From 6008b5c7c55775e4b88878ebff1f892c89275847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Mon, 12 Feb 2018 17:45:13 +0200 Subject: [PATCH] Config file workflow management --- Gopkg.lock | 11 ++++- Gopkg.toml | 4 ++ internal/cli/root/root.go | 35 +++++++++++++- internal/cli/run/run.go | 10 +++- internal/legacy/legacy.go | 1 + ooni.go | 99 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 155 insertions(+), 5 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index cd22cd0..9edafb4 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -22,6 +22,15 @@ packages = ["."] revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" +[[projects]] + name = "github.com/apex/log" + packages = [ + ".", + "handlers/cli" + ] + revision = "0296d6eb16bb28f8a0c55668affcf4876dc269be" + version = "v1.0.0" + [[projects]] branch = "master" name = "github.com/aybabtme/rgbterm" @@ -77,6 +86,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "8ca4591e7aeb614acd312b2f95a425fe4def8100bbfc56f9c9147ae8e8b3575a" + inputs-digest = "b014c3dfbccece13286bfddc98c8ec74007629647cd54df8d9af9d94985cdb17" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 7c4f843..b4dbc52 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -48,3 +48,7 @@ [[constraint]] name = "gopkg.in/AlecAivazis/survey.v1" version = "1.4.1" + +[[constraint]] + name = "github.com/apex/log" + version = "1.0.0" diff --git a/internal/cli/root/root.go b/internal/cli/root/root.go index 102dcb0..47d0aaa 100644 --- a/internal/cli/root/root.go +++ b/internal/cli/root/root.go @@ -2,7 +2,10 @@ package root import ( "github.com/alecthomas/kingpin" - "github.com/openobservatory/gooni/internal/util" + "github.com/apex/log" + "github.com/apex/log/handlers/cli" + ooni "github.com/openobservatory/gooni" + "github.com/prometheus/common/version" ) // Cmd is the root command @@ -11,9 +14,37 @@ var Cmd = kingpin.New("ooni", "") // Command is syntax sugar for defining sub-commands var Command = Cmd.Command +// Init should be called by all subcommand that care to have a ooni.OONI instance +var Init func() (*ooni.Config, *ooni.OONI, error) + func init() { + configPath := Cmd.Flag("config", "Set a custom config file path").Short('c').String() + verbose := Cmd.Flag("verbose", "Enable verbose log output.").Short('v').Bool() + Cmd.PreAction(func(ctx *kingpin.ParseContext) error { - util.Log("Running pre-action") + log.SetHandler(cli.Default) + if *verbose { + log.SetLevel(log.DebugLevel) + log.Debugf("ooni version %s", version.Version) + } + + Init = func() (*ooni.Config, *ooni.OONI, error) { + var c *ooni.Config + var err error + + if *configPath != "" { + c, err = ooni.ReadConfig(*configPath) + } else { + c, err = ooni.ReadDefaultConfigPaths() + } + if err != nil { + return nil, nil, err + } + o := ooni.New(c) + o.Init() + return c, o, nil + } + return nil }) } diff --git a/internal/cli/run/run.go b/internal/cli/run/run.go index bbc2d91..1c44672 100644 --- a/internal/cli/run/run.go +++ b/internal/cli/run/run.go @@ -2,6 +2,7 @@ package run import ( "github.com/alecthomas/kingpin" + "github.com/apex/log" "github.com/openobservatory/gooni/internal/cli/root" "github.com/openobservatory/gooni/internal/util" ) @@ -12,7 +13,14 @@ func init() { nettestGroup := cmd.Arg("name", "the nettest group to run").Required().String() cmd.Action(func(_ *kingpin.ParseContext) error { - util.Log("Starting %s", nettestGroup) + util.Log("Starting %s", *nettestGroup) + config, ooni, err := root.Init() + if err != nil { + log.Errorf("%s", err) + return err + } + log.Infof("%s", config) + log.Infof("%s", ooni) return nil }) } diff --git a/internal/legacy/legacy.go b/internal/legacy/legacy.go index 4d9c194..1877ddc 100644 --- a/internal/legacy/legacy.go +++ b/internal/legacy/legacy.go @@ -10,6 +10,7 @@ import ( "gopkg.in/AlecAivazis/survey.v1" ) +// HomePath returns the path to the OONI Home func HomePath() (string, error) { home, err := homedir.Dir() if err != nil { diff --git a/ooni.go b/ooni.go index 60c7a09..8d434fd 100644 --- a/ooni.go +++ b/ooni.go @@ -5,12 +5,56 @@ import ( "io/ioutil" "os" "path/filepath" + "sync" + "github.com/apex/log" homedir "github.com/mitchellh/go-homedir" "github.com/openobservatory/gooni/config" + "github.com/openobservatory/gooni/internal/legacy" "github.com/pkg/errors" ) +// Onboarding process +func Onboarding(c *Config) error { + log.Info("Onboarding starting") + + // To prevent races we always must acquire the config file lock before + // changing it. + c.Lock() + c.InformedConsent = true + c.Unlock() + + if err := c.Write(); err != nil { + return err + } + return nil +} + +// OONI manager. +type OONI struct { + config *Config +} + +// Init the OONI manager +func (o *OONI) Init() error { + if err := legacy.MaybeMigrateHome(); err != nil { + return errors.Wrap(err, "migrating home") + } + if o.config.InformedConsent == false { + if err := Onboarding(o.config); err != nil { + return errors.Wrap(err, "onboarding") + } + } + return nil +} + +// New OONI manager instance. +func New(c *Config) *OONI { + return &OONI{ + config: c, + } +} + // GetOONIHome returns the path to the OONI Home func GetOONIHome() (string, error) { home, err := homedir.Dir() @@ -35,6 +79,33 @@ type Config struct { AutomatedTesting config.AutomatedTesting `json:"automated_testing"` NettestGroups config.NettestGroups `json:"test_settings"` Advanced config.Sharing `json:"advanced"` + + mutex sync.Mutex + path string +} + +// Write the config file in json to the path +func (c *Config) Write() error { + c.Lock() + configJSON, _ := json.MarshalIndent(c, "", " ") + if c.path == "" { + return errors.New("config file path is empty") + } + if err := ioutil.WriteFile(c.path, configJSON, 0644); err != nil { + return errors.Wrap(err, "writing config JSON") + } + c.Unlock() + return nil +} + +// Lock acquires the write mutex +func (c *Config) Lock() { + c.mutex.Lock() +} + +// Unlock releases the write mutex +func (c *Config) Unlock() { + c.mutex.Unlock() } // Default config settings @@ -86,5 +157,31 @@ func ReadConfig(path string) (*Config, error) { return nil, errors.Wrap(err, "reading file") } - return ParseConfig(b) + c, err := ParseConfig(b) + if err != nil { + return nil, errors.Wrap(err, "parsing config") + } + c.path = path + return c, err +} + +// ReadDefaultConfigPaths from common locations. +func ReadDefaultConfigPaths() (*Config, error) { + home, err := GetOONIHome() + if err != nil { + return nil, errors.Wrap(err, "reading default config paths") + } + var paths = []string{ + filepath.Join(home, "config.json"), + } + for _, path := range paths { + if _, err := os.Stat(path); err == nil { + c, err := ReadConfig(path) + if err != nil { + return nil, err + } + return c, nil + } + } + return nil, errors.New("failed to find a config") }