From 2653a3f67f46e20b2744320029941a7479ae377d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 22 Jun 2018 10:29:47 +0200 Subject: [PATCH 01/32] Consolidate config related functionality into the config package --- config/advanced.go | 7 -- config/automated_testing.go | 8 -- config/notifications.go | 8 -- config/parser.go | 119 ++++++++++++++++++++ config/{nettest_groups.go => settings.go} | 38 ++++++- config/sharing.go | 10 -- data/default-config.json | 8 +- ooni.go | 125 ++-------------------- 8 files changed, 165 insertions(+), 158 deletions(-) delete mode 100644 config/advanced.go delete mode 100644 config/automated_testing.go delete mode 100644 config/notifications.go create mode 100644 config/parser.go rename config/{nettest_groups.go => settings.go} (65%) delete mode 100644 config/sharing.go diff --git a/config/advanced.go b/config/advanced.go deleted file mode 100644 index 2add03c..0000000 --- a/config/advanced.go +++ /dev/null @@ -1,7 +0,0 @@ -package config - -// Advanced settings -type Advanced struct { - IncludeCountry bool `json:"include_country"` - UseDomainFronting bool `json:"use_domain_fronting"` -} diff --git a/config/automated_testing.go b/config/automated_testing.go deleted file mode 100644 index 56dc660..0000000 --- a/config/automated_testing.go +++ /dev/null @@ -1,8 +0,0 @@ -package config - -// AutomatedTesting settings -type AutomatedTesting struct { - Enabled bool `json:"enabled"` - EnabledTests []string `json:"enabled_tests"` - MonthlyAllowance string `json:"monthly_allowance"` -} diff --git a/config/notifications.go b/config/notifications.go deleted file mode 100644 index 7341ac3..0000000 --- a/config/notifications.go +++ /dev/null @@ -1,8 +0,0 @@ -package config - -// Notifications settings -type Notifications struct { - Enabled bool `json:"enabled"` - NotifyOnTestCompletion bool `json:"notify_on_test_completion"` - NotifyOnNews bool `json:"notify_on_news"` -} diff --git a/config/parser.go b/config/parser.go new file mode 100644 index 0000000..f013900 --- /dev/null +++ b/config/parser.go @@ -0,0 +1,119 @@ +package config + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "sync" + + "github.com/ooni/probe-cli/utils" + "github.com/pkg/errors" +) + +// ReadConfig reads the configuration from the path +func ReadConfig(path string) (*Config, error) { + b, err := ioutil.ReadFile(path) + + if os.IsNotExist(err) { + c := &Config{} + + if err = c.Default(); err != nil { + return nil, errors.Wrap(err, "defaulting") + } + + if err = c.Validate(); err != nil { + return nil, errors.Wrap(err, "validating") + } + + return c, nil + } + + if err != nil { + return nil, errors.Wrap(err, "reading file") + } + + c, err := ParseConfig(b) + if err != nil { + return nil, errors.Wrap(err, "parsing config") + } + c.path = path + return c, err +} + +// ParseConfig returns config from JSON bytes. +func ParseConfig(b []byte) (*Config, error) { + c := &Config{} + + if err := json.Unmarshal(b, c); err != nil { + return nil, errors.Wrap(err, "parsing json") + } + + if err := c.Default(); err != nil { + return nil, errors.Wrap(err, "defaulting") + } + + if err := c.Validate(); err != nil { + return nil, errors.Wrap(err, "validating") + } + + return c, nil +} + +// Config for the OONI Probe installation +type Config struct { + // Private settings + Comment string `json:"_"` + Version int64 `json:"_version"` + InformedConsent bool `json:"_informed_consent"` + + AutoUpdate bool `json:"auto_update"` + Sharing Sharing `json:"sharing"` + Notifications Notifications `json:"notifications"` + AutomatedTesting AutomatedTesting `json:"automated_testing"` + NettestGroups NettestGroups `json:"test_settings"` + Advanced Advanced `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 +func (c *Config) Default() error { + home, err := utils.GetOONIHome() + if err != nil { + return err + } + + c.path = filepath.Join(home, "config.json") + return nil +} + +// Validate the config file +func (c *Config) Validate() error { + return nil +} diff --git a/config/nettest_groups.go b/config/settings.go similarity index 65% rename from config/nettest_groups.go rename to config/settings.go index 9be7a6c..0935772 100644 --- a/config/nettest_groups.go +++ b/config/settings.go @@ -82,11 +82,10 @@ func (s *InstantMessaging) NettestConfigs() []NettestConfig { // Performance nettest group type Performance struct { - EnabledTests []string `json:"enabled_tests"` - NDTServer string `json:"ndt_server"` - NDTServerPort string `json:"ndt_server_port"` - DashServer string `json:"dash_server"` - DashServerPort string `json:"dash_server_port"` + NDTServer string `json:"ndt_server"` + NDTServerPort string `json:"ndt_server_port"` + DashServer string `json:"dash_server"` + DashServerPort string `json:"dash_server_port"` } // Middlebox nettest group @@ -101,3 +100,32 @@ type NettestGroups struct { Performance Performance `json:"performance"` Middlebox Middlebox `json:"middlebox"` } + +// Notifications settings +type Notifications struct { + Enabled bool `json:"enabled"` + NotifyOnTestCompletion bool `json:"notify_on_test_completion"` + NotifyOnNews bool `json:"notify_on_news"` +} + +// Sharing settings +type Sharing struct { + IncludeIP bool `json:"include_ip"` + IncludeASN bool `json:"include_asn"` + IncludeGPS bool `json:"include_gps"` + UploadResults bool `json:"upload_results"` + SendCrashReports bool `json:"send_crash_reports"` +} + +// Advanced settings +type Advanced struct { + IncludeCountry bool `json:"include_country"` + UseDomainFronting bool `json:"use_domain_fronting"` +} + +// AutomatedTesting settings +type AutomatedTesting struct { + Enabled bool `json:"enabled"` + EnabledTests []string `json:"enabled_tests"` + MonthlyAllowance string `json:"monthly_allowance"` +} diff --git a/config/sharing.go b/config/sharing.go deleted file mode 100644 index 6aebf8d..0000000 --- a/config/sharing.go +++ /dev/null @@ -1,10 +0,0 @@ -package config - -// Sharing settings -type Sharing struct { - IncludeIP bool `json:"include_ip"` - IncludeASN bool `json:"include_asn"` - IncludeGPS bool `json:"include_gps"` - UploadResults bool `json:"upload_results"` - SendCrashReports bool `json:"send_crash_reports"` -} diff --git a/data/default-config.json b/data/default-config.json index 1068d91..79438b1 100644 --- a/data/default-config.json +++ b/data/default-config.json @@ -1,5 +1,7 @@ { - "_": "This is your OONI Probe config file. See https://ooni.io/help/ooniprobe-cli for help", + "_": "This is your OONI Probe config file. See https://ooni.io/help/probe-cli for help", + "_version": 0, + "_informed_consent": false, "auto_update": true, "sharing": { "include_ip": false, @@ -56,8 +58,6 @@ }, "advanced": { "include_country": true, - "use_domain_fronting": true + "use_domain_fronting": false }, - "_config_version": "0.0.1", - "_informed_consent": true } diff --git a/ooni.go b/ooni.go index 051e051..1c794e9 100644 --- a/ooni.go +++ b/ooni.go @@ -1,11 +1,9 @@ package ooni import ( - "encoding/json" "io/ioutil" "os" "path/filepath" - "sync" "github.com/apex/log" "github.com/jmoiron/sqlx" @@ -17,7 +15,7 @@ import ( ) // Onboarding process -func Onboarding(c *Config) error { +func Onboarding(c *config.Config) error { log.Info("Onboarding starting") // To prevent races we always must acquire the config file lock before @@ -35,7 +33,7 @@ func Onboarding(c *Config) error { // Context for OONI Probe type Context struct { - Config *Config + Config *config.Config DB *sqlx.DB Location *utils.LocationInfo @@ -82,7 +80,7 @@ func (c *Context) Init() error { if c.configPath != "" { log.Debugf("Reading config file from %s", c.configPath) - c.Config, err = ReadConfig(c.configPath) + c.Config, err = config.ReadConfig(c.configPath) } else { log.Debug("Reading default config file") c.Config, err = ReadDefaultConfigPaths(c.Home) @@ -118,88 +116,11 @@ func (c *Context) Init() error { func NewContext(configPath string, homePath string) *Context { return &Context{ Home: homePath, - Config: &Config{}, + Config: &config.Config{}, configPath: configPath, } } -// Config for the OONI Probe installation -type Config struct { - // Private settings - Comment string `json:"_"` - ConfigVersion string `json:"_config_version"` - InformedConsent bool `json:"_informed_consent"` - - AutoUpdate bool `json:"auto_update"` - Sharing config.Sharing `json:"sharing"` - Notifications config.Notifications `json:"notifications"` - AutomatedTesting config.AutomatedTesting `json:"automated_testing"` - NettestGroups config.NettestGroups `json:"test_settings"` - Advanced config.Advanced `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 -func (c *Config) Default() error { - home, err := utils.GetOONIHome() - if err != nil { - return err - } - - c.path = filepath.Join(home, "config.json") - return nil -} - -// Validate the config file -func (c *Config) Validate() error { - return nil -} - -// ParseConfig returns config from JSON bytes. -func ParseConfig(b []byte) (*Config, error) { - c := &Config{} - - if err := json.Unmarshal(b, c); err != nil { - return nil, errors.Wrap(err, "parsing json") - } - - if err := c.Default(); err != nil { - return nil, errors.Wrap(err, "defaulting") - } - - if err := c.Validate(); err != nil { - return nil, errors.Wrap(err, "validating") - } - - return c, nil -} - // MaybeInitializeHome does the setup for a new OONI Home func MaybeInitializeHome(home string) error { firstRun := false @@ -214,9 +135,11 @@ func MaybeInitializeHome(home string) error { if firstRun == true { log.Info("This is the first time you are running OONI Probe. Downloading some files.") geoipDir := utils.GeoIPDir(home) + log.Debugf("Downloading GeoIP database files") if err := utils.DownloadGeoIPDatabaseFiles(geoipDir); err != nil { return err } + log.Debugf("Downloading legacy GeoIP database Files") if err := utils.DownloadLegacyGeoIPDatabaseFiles(geoipDir); err != nil { return err } @@ -225,44 +148,14 @@ func MaybeInitializeHome(home string) error { return nil } -// ReadConfig reads the configuration from the path -func ReadConfig(path string) (*Config, error) { - b, err := ioutil.ReadFile(path) - - if os.IsNotExist(err) { - c := &Config{} - - if err = c.Default(); err != nil { - return nil, errors.Wrap(err, "defaulting") - } - - if err = c.Validate(); err != nil { - return nil, errors.Wrap(err, "validating") - } - - return c, nil - } - - if err != nil { - return nil, errors.Wrap(err, "reading file") - } - - 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(home string) (*Config, error) { +func ReadDefaultConfigPaths(home string) (*config.Config, error) { var paths = []string{ filepath.Join(home, "config.json"), } for _, path := range paths { if _, err := os.Stat(path); err == nil { - c, err := ReadConfig(path) + c, err := config.ReadConfig(path) if err != nil { return nil, err } @@ -271,5 +164,5 @@ func ReadDefaultConfigPaths(home string) (*Config, error) { } // Run from the default config - return ReadConfig(paths[0]) + return config.ReadConfig(paths[0]) } From 1bba0c789922dcdef7dbd2f05f37dd40304933b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 22 Jun 2018 11:01:15 +0200 Subject: [PATCH 02/32] Add basic unittests for the config related functionality --- config/parser.go | 22 ++--------- config/parser_test.go | 19 ++++++++++ config/testdata/valid-config.json | 63 +++++++++++++++++++++++++++++++ internal/cli/run/run.go | 6 +++ ooni.go | 17 ++++++--- 5 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 config/parser_test.go create mode 100644 config/testdata/valid-config.json diff --git a/config/parser.go b/config/parser.go index f013900..ea79923 100644 --- a/config/parser.go +++ b/config/parser.go @@ -3,7 +3,6 @@ package config import ( "encoding/json" "io/ioutil" - "os" "path/filepath" "sync" @@ -14,21 +13,6 @@ import ( // ReadConfig reads the configuration from the path func ReadConfig(path string) (*Config, error) { b, err := ioutil.ReadFile(path) - - if os.IsNotExist(err) { - c := &Config{} - - if err = c.Default(); err != nil { - return nil, errors.Wrap(err, "defaulting") - } - - if err = c.Validate(); err != nil { - return nil, errors.Wrap(err, "validating") - } - - return c, nil - } - if err != nil { return nil, errors.Wrap(err, "reading file") } @@ -43,9 +27,9 @@ func ReadConfig(path string) (*Config, error) { // ParseConfig returns config from JSON bytes. func ParseConfig(b []byte) (*Config, error) { - c := &Config{} + var c Config - if err := json.Unmarshal(b, c); err != nil { + if err := json.Unmarshal(b, &c); err != nil { return nil, errors.Wrap(err, "parsing json") } @@ -57,7 +41,7 @@ func ParseConfig(b []byte) (*Config, error) { return nil, errors.Wrap(err, "validating") } - return c, nil + return &c, nil } // Config for the OONI Probe installation diff --git a/config/parser_test.go b/config/parser_test.go new file mode 100644 index 0000000..010dbe6 --- /dev/null +++ b/config/parser_test.go @@ -0,0 +1,19 @@ +package config + +import ( + "testing" +) + +func TestParseConfig(t *testing.T) { + config, err := ReadConfig("testdata/valid-config.json") + if err != nil { + t.Error(err) + } + + if len(config.NettestGroups.Middlebox.EnabledTests) < 0 { + t.Error("at least one middlebox test should be enabled") + } + if config.Advanced.IncludeCountry == false { + t.Error("country should be included") + } +} diff --git a/config/testdata/valid-config.json b/config/testdata/valid-config.json new file mode 100644 index 0000000..3171bdc --- /dev/null +++ b/config/testdata/valid-config.json @@ -0,0 +1,63 @@ +{ + "_": "This is your OONI Probe config file. See https://ooni.io/help/probe-cli for help", + "_version": 0, + "_informed_consent": false, + "auto_update": true, + "sharing": { + "include_ip": false, + "include_asn": true, + "include_gps": true, + "upload_results": true, + "send_crash_reports": true + }, + "notifications": { + "enabled": true, + "notify_on_test_completion": true, + "notify_on_news": false + }, + "automated_testing": { + "enabled": false, + "enabled_tests": [ + "web-connectivity", + "facebook-messenger", + "whatsapp", + "telegram", + "dash", + "ndt", + "http-invalid-request-line", + "http-header-field-manipulation" + ], + "monthly_allowance": "300MB" + }, + "test_settings": { + "websites": { + "enabled_categories": [] + }, + "instant_messaging": { + "enabled_tests": [ + "facebook-messenger", + "whatsapp", + "telegram" + ] + }, + "performance": { + "enabled_tests": [ + "ndt" + ], + "ndt_server": "auto", + "ndt_server_port": "auto", + "dash_server": "auto", + "dash_server_port": "auto" + }, + "middlebox": { + "enabled_tests": [ + "http-invalid-request-line", + "http-header-field-manipulation" + ] + } + }, + "advanced": { + "include_country": true, + "use_domain_fronting": false + } +} diff --git a/internal/cli/run/run.go b/internal/cli/run/run.go index b6125bc..dacd23d 100644 --- a/internal/cli/run/run.go +++ b/internal/cli/run/run.go @@ -27,6 +27,12 @@ func init() { log.Errorf("%s", err) return err } + + if err = ctx.MaybeOnboarding(); err != nil { + log.WithError(err).Error("failed to perform onboarding") + return err + } + group, ok := groups.NettestGroups[*nettestGroup] if !ok { log.Errorf("No test group named %s", *nettestGroup) diff --git a/ooni.go b/ooni.go index 1c794e9..db1e391 100644 --- a/ooni.go +++ b/ooni.go @@ -66,6 +66,17 @@ func (c *Context) LocationLookup() error { return nil } +// MaybeOnboarding will run the onboarding process only if the informed consent +// config option is set to false +func (c *Context) MaybeOnboarding() error { + if c.Config.InformedConsent == false { + if err := Onboarding(c.Config); err != nil { + return errors.Wrap(err, "onboarding") + } + } + return nil +} + // Init the OONI manager func (c *Context) Init() error { var err error @@ -90,12 +101,6 @@ func (c *Context) Init() error { } c.dbPath = utils.DBDir(c.Home, "main") - if c.Config.InformedConsent == false { - if err = Onboarding(c.Config); err != nil { - return errors.Wrap(err, "onboarding") - } - } - log.Debugf("Connecting to database sqlite3://%s", c.dbPath) db, err := database.Connect(c.dbPath) if err != nil { From 15c901ed682ed931a516b3c37f7f755268a58a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 22 Jun 2018 11:17:51 +0200 Subject: [PATCH 03/32] Make the download of geoip data files conditional --- internal/cli/geoip/geoip.go | 4 ++++ ooni.go | 39 ++++++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/internal/cli/geoip/geoip.go b/internal/cli/geoip/geoip.go index 82a1945..d4fb28f 100644 --- a/internal/cli/geoip/geoip.go +++ b/internal/cli/geoip/geoip.go @@ -19,6 +19,10 @@ func init() { return err } + if err = ctx.MaybeDownloadDataFiles(); err != nil { + log.WithError(err).Error("failed to download data files") + } + geoipPath := utils.GeoIPDir(ctx.Home) if *shouldUpdate { utils.DownloadGeoIPDatabaseFiles(geoipPath) diff --git a/ooni.go b/ooni.go index db1e391..399accc 100644 --- a/ooni.go +++ b/ooni.go @@ -3,6 +3,7 @@ package ooni import ( "io/ioutil" "os" + "path" "path/filepath" "github.com/apex/log" @@ -56,8 +57,11 @@ func (c *Context) MaybeLocationLookup() error { func (c *Context) LocationLookup() error { var err error - geoipDir := utils.GeoIPDir(c.Home) + if err = c.MaybeDownloadDataFiles(); err != nil { + log.WithError(err).Error("failed to download data files") + } + geoipDir := utils.GeoIPDir(c.Home) c.Location, err = utils.GeoIPLookup(geoipDir) if err != nil { return err @@ -77,6 +81,24 @@ func (c *Context) MaybeOnboarding() error { return nil } +// MaybeDownloadDataFiles will download geoip data files if they are not present +func (c *Context) MaybeDownloadDataFiles() error { + geoipDir := utils.GeoIPDir(c.Home) + if _, err := os.Stat(path.Join(geoipDir, "GeoLite2-Country.mmdb")); os.IsNotExist(err) { + log.Debugf("Downloading GeoIP database files") + if err := utils.DownloadGeoIPDatabaseFiles(geoipDir); err != nil { + return err + } + } + if _, err := os.Stat(path.Join(geoipDir, "GeoIP.dat")); os.IsNotExist(err) { + log.Debugf("Downloading legacy GeoIP database Files") + if err := utils.DownloadLegacyGeoIPDatabaseFiles(geoipDir); err != nil { + return err + } + } + return nil +} + // Init the OONI manager func (c *Context) Init() error { var err error @@ -128,28 +150,13 @@ func NewContext(configPath string, homePath string) *Context { // MaybeInitializeHome does the setup for a new OONI Home func MaybeInitializeHome(home string) error { - firstRun := false for _, d := range utils.RequiredDirs(home) { if _, e := os.Stat(d); e != nil { - firstRun = true if err := os.MkdirAll(d, 0700); err != nil { return err } } } - if firstRun == true { - log.Info("This is the first time you are running OONI Probe. Downloading some files.") - geoipDir := utils.GeoIPDir(home) - log.Debugf("Downloading GeoIP database files") - if err := utils.DownloadGeoIPDatabaseFiles(geoipDir); err != nil { - return err - } - log.Debugf("Downloading legacy GeoIP database Files") - if err := utils.DownloadLegacyGeoIPDatabaseFiles(geoipDir); err != nil { - return err - } - } - return nil } From 703b260903ba2cb23fb01c721a173027fc1e3c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 22 Jun 2018 11:53:10 +0200 Subject: [PATCH 04/32] Implement missing bits in result listing --- internal/cli/list/list.go | 47 +++++++++++++++--------- internal/log/handlers/cli/cli.go | 15 ++++++++ internal/log/handlers/cli/result_item.go | 21 +++++++++-- internal/log/handlers/cli/util.go | 10 +++++ internal/output/output.go | 25 +++++++++++++ 5 files changed, 97 insertions(+), 21 deletions(-) create mode 100644 internal/log/handlers/cli/util.go diff --git a/internal/cli/list/list.go b/internal/cli/list/list.go index 80e1395..d62d293 100644 --- a/internal/cli/list/list.go +++ b/internal/cli/list/list.go @@ -23,24 +23,9 @@ func init() { return err } - log.Info("Results") - for idx, result := range doneResults { - output.ResultItem(output.ResultItemData{ - ID: result.ID, - Index: idx, - TotalCount: len(doneResults), - Name: result.Name, - StartTime: result.StartTime, - NetworkName: result.NetworkName, - Country: result.Country, - ASN: result.ASN, - Summary: result.Summary, - Done: result.Done, - DataUsageUp: result.DataUsageUp, - DataUsageDown: result.DataUsageDown, - }) + if len(incompleteResults) > 0 { + output.SectionTitle("Incomplete results") } - log.Info("Incomplete results") for idx, result := range incompleteResults { output.ResultItem(output.ResultItemData{ ID: result.ID, @@ -57,6 +42,34 @@ func init() { DataUsageDown: result.DataUsageDown, }) } + + resultSummary := output.ResultSummaryData{} + netCount := make(map[string]int) + output.SectionTitle("Results") + for idx, result := range doneResults { + output.ResultItem(output.ResultItemData{ + ID: result.ID, + Index: idx, + TotalCount: len(doneResults), + Name: result.Name, + StartTime: result.StartTime, + NetworkName: result.NetworkName, + Country: result.Country, + ASN: result.ASN, + Summary: result.Summary, + Done: result.Done, + DataUsageUp: result.DataUsageUp, + DataUsageDown: result.DataUsageDown, + }) + resultSummary.TotalTests++ + netCount[result.ASN]++ + resultSummary.TotalDataUsageUp += result.DataUsageUp + resultSummary.TotalDataUsageDown += result.DataUsageDown + } + resultSummary.TotalNetworks = int64(len(netCount)) + + output.ResultSummary(resultSummary) + return nil }) } diff --git a/internal/log/handlers/cli/cli.go b/internal/log/handlers/cli/cli.go index fbb04cd..1b87655 100644 --- a/internal/log/handlers/cli/cli.go +++ b/internal/log/handlers/cli/cli.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "os" + "strings" "sync" "time" @@ -60,6 +61,16 @@ func New(w io.Writer) *Handler { } } +func logSectionTitle(w io.Writer, f log.Fields) error { + colWidth := 24 + + title := f.Get("title").(string) + fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth+2)+"┓\n") + fmt.Fprintf(w, "┃ %s ┃\n", RightPad(title, colWidth)) + fmt.Fprintf(w, "┗"+strings.Repeat("━", colWidth+2)+"┛\n") + return nil +} + // TypedLog is used for handling special "typed" logs to the CLI func (h *Handler) TypedLog(t string, e *log.Entry) error { switch t { @@ -70,6 +81,10 @@ func (h *Handler) TypedLog(t string, e *log.Entry) error { return nil case "result_item": return logResultItem(h.Writer, e.Fields) + case "result_summary": + return logResultSummary(h.Writer, e.Fields) + case "section_title": + return logSectionTitle(h.Writer, e.Fields) default: return h.DefaultLog(e) } diff --git a/internal/log/handlers/cli/result_item.go b/internal/log/handlers/cli/result_item.go index 1f3ec8e..391fa46 100644 --- a/internal/log/handlers/cli/result_item.go +++ b/internal/log/handlers/cli/result_item.go @@ -10,10 +10,6 @@ import ( "github.com/apex/log" ) -func RightPad(str string, length int) string { - return str + strings.Repeat(" ", length-len(str)) -} - // XXX Copy-pasta from nettest/groups // PerformanceSummary is the result summary for a performance test type PerformanceSummary struct { @@ -145,3 +141,20 @@ func logResultItem(w io.Writer, f log.Fields) error { } return nil } + +func logResultSummary(w io.Writer, f log.Fields) error { + + networks := f.Get("total_networks").(int64) + tests := f.Get("total_tests").(int64) + dataUp := f.Get("total_data_usage_up").(int64) + dataDown := f.Get("total_data_usage_down").(int64) + + // └┬──────────────┬──────────────┬──────────────┬ + fmt.Fprintf(w, " │ %s │ %s │ %s │\n", + RightPad(fmt.Sprintf("%d tests", tests), 12), + RightPad(fmt.Sprintf("%d nets", networks), 12), + RightPad(fmt.Sprintf("%d ⬆ %d ⬇", dataUp, dataDown), 12)) + fmt.Fprintf(w, " └──────────────┴──────────────┴──────────────┘\n") + + return nil +} diff --git a/internal/log/handlers/cli/util.go b/internal/log/handlers/cli/util.go new file mode 100644 index 0000000..41a42e2 --- /dev/null +++ b/internal/log/handlers/cli/util.go @@ -0,0 +1,10 @@ +package cli + +import ( + "strings" + "unicode/utf8" +) + +func RightPad(str string, length int) string { + return str + strings.Repeat(" ", length-utf8.RuneCountInString(str)) +} diff --git a/internal/output/output.go b/internal/output/output.go index f658765..8bb1cd2 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -51,3 +51,28 @@ func ResultItem(result ResultItemData) { "total_count": result.TotalCount, }).Info("result item") } + +type ResultSummaryData struct { + TotalTests int64 + TotalDataUsageUp int64 + TotalDataUsageDown int64 + TotalNetworks int64 +} + +func ResultSummary(result ResultSummaryData) { + log.WithFields(log.Fields{ + "type": "result_summary", + "total_tests": result.TotalTests, + "total_data_usage_up": result.TotalDataUsageUp, + "total_data_usage_down": result.TotalDataUsageDown, + "total_networks": result.TotalNetworks, + }).Info("result summary") +} + +// SectionTitle is the title of a section +func SectionTitle(text string) { + log.WithFields(log.Fields{ + "type": "section_title", + "title": text, + }).Info(text) +} From 6ff00eeed16924b09470b716f6c61e3791ac70cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 22 Jun 2018 11:53:30 +0200 Subject: [PATCH 05/32] Rename result_item to results --- internal/log/handlers/cli/{result_item.go => results.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/log/handlers/cli/{result_item.go => results.go} (100%) diff --git a/internal/log/handlers/cli/result_item.go b/internal/log/handlers/cli/results.go similarity index 100% rename from internal/log/handlers/cli/result_item.go rename to internal/log/handlers/cli/results.go From 04eb07624cc42300acb741e9d5f96e05bd791ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 22 Jun 2018 11:57:25 +0200 Subject: [PATCH 06/32] Add zero state --- internal/log/handlers/cli/results.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/log/handlers/cli/results.go b/internal/log/handlers/cli/results.go index 391fa46..50ca767 100644 --- a/internal/log/handlers/cli/results.go +++ b/internal/log/handlers/cli/results.go @@ -148,7 +148,12 @@ func logResultSummary(w io.Writer, f log.Fields) error { tests := f.Get("total_tests").(int64) dataUp := f.Get("total_data_usage_up").(int64) dataDown := f.Get("total_data_usage_down").(int64) - + if tests == 0 { + fmt.Fprintf(w, "No results\n") + fmt.Fprintf(w, "Try running:\n") + fmt.Fprintf(w, " ooni run websites\n") + return nil + } // └┬──────────────┬──────────────┬──────────────┬ fmt.Fprintf(w, " │ %s │ %s │ %s │\n", RightPad(fmt.Sprintf("%d tests", tests), 12), From eb4e6988b3e0574a37203c9c0dbb5917dc585491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 22 Jun 2018 12:12:35 +0200 Subject: [PATCH 07/32] Implement quick and dirty measurement listing --- internal/cli/list/list.go | 113 ++++++++++++++++++++---------------- internal/cli/show/show.go | 7 ++- internal/database/models.go | 36 ++++++++++++ 3 files changed, 106 insertions(+), 50 deletions(-) diff --git a/internal/cli/list/list.go b/internal/cli/list/list.go index d62d293..b52882b 100644 --- a/internal/cli/list/list.go +++ b/internal/cli/list/list.go @@ -1,6 +1,8 @@ package list import ( + "fmt" + "github.com/alecthomas/kingpin" "github.com/apex/log" "github.com/ooni/probe-cli/internal/cli/root" @@ -11,64 +13,77 @@ import ( func init() { cmd := root.Command("list", "List results") + resultID := cmd.Arg("id", "the id of the result to list measurements for").Int64() + cmd.Action(func(_ *kingpin.ParseContext) error { ctx, err := root.Init() if err != nil { log.WithError(err).Error("failed to initialize root context") return err } - doneResults, incompleteResults, err := database.ListResults(ctx.DB) - if err != nil { - log.WithError(err).Error("failed to list results") - return err - } + if *resultID > 0 { + measurements, err := database.ListMeasurements(ctx.DB, *resultID) + if err != nil { + log.WithError(err).Error("failed to list measurements") + return err + } + for idx, msmt := range measurements { + fmt.Printf("%d: %v\n", idx, msmt) + } + } else { + doneResults, incompleteResults, err := database.ListResults(ctx.DB) + if err != nil { + log.WithError(err).Error("failed to list results") + return err + } - if len(incompleteResults) > 0 { - output.SectionTitle("Incomplete results") - } - for idx, result := range incompleteResults { - output.ResultItem(output.ResultItemData{ - ID: result.ID, - Index: idx, - TotalCount: len(incompleteResults), - Name: result.Name, - StartTime: result.StartTime, - NetworkName: result.NetworkName, - Country: result.Country, - ASN: result.ASN, - Summary: result.Summary, - Done: result.Done, - DataUsageUp: result.DataUsageUp, - DataUsageDown: result.DataUsageDown, - }) - } + if len(incompleteResults) > 0 { + output.SectionTitle("Incomplete results") + } + for idx, result := range incompleteResults { + output.ResultItem(output.ResultItemData{ + ID: result.ID, + Index: idx, + TotalCount: len(incompleteResults), + Name: result.Name, + StartTime: result.StartTime, + NetworkName: result.NetworkName, + Country: result.Country, + ASN: result.ASN, + Summary: result.Summary, + Done: result.Done, + DataUsageUp: result.DataUsageUp, + DataUsageDown: result.DataUsageDown, + }) + } - resultSummary := output.ResultSummaryData{} - netCount := make(map[string]int) - output.SectionTitle("Results") - for idx, result := range doneResults { - output.ResultItem(output.ResultItemData{ - ID: result.ID, - Index: idx, - TotalCount: len(doneResults), - Name: result.Name, - StartTime: result.StartTime, - NetworkName: result.NetworkName, - Country: result.Country, - ASN: result.ASN, - Summary: result.Summary, - Done: result.Done, - DataUsageUp: result.DataUsageUp, - DataUsageDown: result.DataUsageDown, - }) - resultSummary.TotalTests++ - netCount[result.ASN]++ - resultSummary.TotalDataUsageUp += result.DataUsageUp - resultSummary.TotalDataUsageDown += result.DataUsageDown - } - resultSummary.TotalNetworks = int64(len(netCount)) + resultSummary := output.ResultSummaryData{} + netCount := make(map[string]int) + output.SectionTitle("Results") + for idx, result := range doneResults { + output.ResultItem(output.ResultItemData{ + ID: result.ID, + Index: idx, + TotalCount: len(doneResults), + Name: result.Name, + StartTime: result.StartTime, + NetworkName: result.NetworkName, + Country: result.Country, + ASN: result.ASN, + Summary: result.Summary, + Done: result.Done, + DataUsageUp: result.DataUsageUp, + DataUsageDown: result.DataUsageDown, + }) + resultSummary.TotalTests++ + netCount[result.ASN]++ + resultSummary.TotalDataUsageUp += result.DataUsageUp + resultSummary.TotalDataUsageDown += result.DataUsageDown + } + resultSummary.TotalNetworks = int64(len(netCount)) - output.ResultSummary(resultSummary) + output.ResultSummary(resultSummary) + } return nil }) diff --git a/internal/cli/show/show.go b/internal/cli/show/show.go index de919e3..ff79442 100644 --- a/internal/cli/show/show.go +++ b/internal/cli/show/show.go @@ -10,7 +10,12 @@ func init() { cmd := root.Command("show", "Show a specific measurement") cmd.Action(func(_ *kingpin.ParseContext) error { - log.Info("Show") + _, err := root.Init() + if err != nil { + log.WithError(err).Error("failed to initialize root context") + return err + } + return nil }) } diff --git a/internal/database/models.go b/internal/database/models.go index daff59b..3bc2539 100644 --- a/internal/database/models.go +++ b/internal/database/models.go @@ -34,6 +34,42 @@ func UpdateOne(db *sqlx.DB, query string, arg interface{}) error { return nil } +// ListMeasurements given a result ID +func ListMeasurements(db *sqlx.DB, resultID int64) ([]*Measurement, error) { + measurements := []*Measurement{} + + rows, err := db.Query(`SELECT id, name, + start_time, runtime, + country, + asn, + summary, + input + FROM measurements + WHERE result_id = ? + ORDER BY start_time;`, resultID) + if err != nil { + return measurements, errors.Wrap(err, "failed to get measurement list") + } + + for rows.Next() { + msmt := Measurement{} + err = rows.Scan(&msmt.ID, &msmt.Name, + &msmt.StartTime, &msmt.Runtime, + &msmt.CountryCode, + &msmt.ASN, + &msmt.Summary, &msmt.Input, + //&result.DataUsageUp, &result.DataUsageDown) + ) + if err != nil { + log.WithError(err).Error("failed to fetch a row") + continue + } + measurements = append(measurements, &msmt) + } + + return measurements, nil +} + // Measurement model type Measurement struct { ID int64 `db:"id"` From 5b31403061aee84871621472bd78704210a5a219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 22 Jun 2018 13:56:42 +0200 Subject: [PATCH 08/32] Implement progressbar for web_connectivity test --- internal/log/handlers/cli/cli.go | 37 +++-- .../log/handlers/cli/progress/progress.go | 126 ++++++++++++++++++ 2 files changed, 155 insertions(+), 8 deletions(-) create mode 100644 internal/log/handlers/cli/progress/progress.go diff --git a/internal/log/handlers/cli/cli.go b/internal/log/handlers/cli/cli.go index 1b87655..7985fec 100644 --- a/internal/log/handlers/cli/cli.go +++ b/internal/log/handlers/cli/cli.go @@ -7,10 +7,12 @@ import ( "strings" "sync" "time" + "unicode/utf8" "github.com/apex/log" "github.com/fatih/color" colorable "github.com/mattn/go-colorable" + "github.com/ooni/probe-cli/internal/log/handlers/cli/progress" ) // Default handler outputting to stderr. @@ -71,14 +73,21 @@ func logSectionTitle(w io.Writer, f log.Fields) error { return nil } +var bar *progress.Bar +var lastBarChars int64 + // TypedLog is used for handling special "typed" logs to the CLI func (h *Handler) TypedLog(t string, e *log.Entry) error { switch t { case "progress": - // XXX replace this with something more fancy like https://github.com/tj/go-progress - fmt.Fprintf(h.Writer, "%.1f%% [%s]: %s", e.Fields.Get("percentage").(float64)*100, e.Fields.Get("key"), e.Message) - fmt.Fprintln(h.Writer) - return nil + var err error + if bar == nil { + bar = progress.New(1.0) + } + bar.Value(e.Fields.Get("percentage").(float64)) + bar.Text(e.Message) + lastBarChars, err = bar.WriteTo(h.Writer) + return err case "result_item": return logResultItem(h.Writer, e.Fields) case "result_summary": @@ -96,16 +105,28 @@ func (h *Handler) DefaultLog(e *log.Entry) error { level := Strings[e.Level] names := e.Fields.Names() - color.Fprintf(h.Writer, "%s %-25s", bold.Sprintf("%*s", h.Padding+1, level), e.Message) - + s := color.Sprintf("%s %-25s", bold.Sprintf("%*s", h.Padding+1, level), e.Message) for _, name := range names { if name == "source" { continue } - fmt.Fprintf(h.Writer, " %s=%s", color.Sprint(name), e.Fields.Get(name)) + s += fmt.Sprintf(" %s=%s", color.Sprint(name), e.Fields.Get(name)) } - fmt.Fprintln(h.Writer) + if bar != nil { + // We need to move the cursor back to the begging of the line and add some + // padding to the end of the string to delete the previous line written to + // the console. + sChars := int64(utf8.RuneCountInString(s)) + fmt.Fprintf(h.Writer, + fmt.Sprintf("\r%s%s", s, strings.Repeat(" ", int(lastBarChars-sChars))), + ) + fmt.Fprintln(h.Writer) + bar.WriteTo(h.Writer) + } else { + fmt.Fprintf(h.Writer, s) + fmt.Fprintln(h.Writer) + } return nil } diff --git a/internal/log/handlers/cli/progress/progress.go b/internal/log/handlers/cli/progress/progress.go new file mode 100644 index 0000000..50887a5 --- /dev/null +++ b/internal/log/handlers/cli/progress/progress.go @@ -0,0 +1,126 @@ +// Package progress provides a simple terminal progress bar. +package progress + +import ( + "bytes" + "fmt" + "html/template" + "io" + "math" + "strings" +) + +// Bar is a progress bar. +type Bar struct { + StartDelimiter string // StartDelimiter for the bar ("|"). + EndDelimiter string // EndDelimiter for the bar ("|"). + Filled string // Filled section representation ("█"). + Empty string // Empty section representation ("░") + Total float64 // Total value. + Width int // Width of the bar. + + value float64 + tmpl *template.Template + text string +} + +// New returns a new bar with the given total. +func New(total float64) *Bar { + b := &Bar{ + StartDelimiter: "|", + EndDelimiter: "|", + Filled: "█", + Empty: "░", + Total: total, + Width: 60, + } + + b.Template(`{{.Percent | printf "%3.0f"}}% {{.Bar}} {{.Text}}`) + + return b +} + +// NewInt returns a new bar with the given total. +func NewInt(total int) *Bar { + return New(float64(total)) +} + +// Text sets the text value. +func (b *Bar) Text(s string) { + b.text = s +} + +// Value sets the value. +func (b *Bar) Value(n float64) { + if n > b.Total { + panic("Bar update value cannot be greater than the total") + } + b.value = n +} + +// ValueInt sets the value. +func (b *Bar) ValueInt(n int) { + b.Value(float64(n)) +} + +// Percent returns the percentage +func (b *Bar) percent() float64 { + return (b.value / b.Total) * 100 +} + +// Bar returns the progress bar string. +func (b *Bar) bar() string { + p := b.value / b.Total + filled := math.Ceil(float64(b.Width) * p) + empty := math.Floor(float64(b.Width) - filled) + s := b.StartDelimiter + s += strings.Repeat(b.Filled, int(filled)) + s += strings.Repeat(b.Empty, int(empty)) + s += b.EndDelimiter + return s +} + +// String returns the progress bar. +func (b *Bar) String() string { + var buf bytes.Buffer + + data := struct { + Value float64 + Total float64 + Percent float64 + StartDelimiter string + EndDelimiter string + Bar string + Text string + }{ + Value: b.value, + Text: b.text, + StartDelimiter: b.StartDelimiter, + EndDelimiter: b.EndDelimiter, + Percent: b.percent(), + Bar: b.bar(), + } + + if err := b.tmpl.Execute(&buf, data); err != nil { + panic(err) + } + + return buf.String() +} + +// WriteTo writes the progress bar to w. +func (b *Bar) WriteTo(w io.Writer) (int64, error) { + s := fmt.Sprintf("\r %s ", b.String()) + _, err := io.WriteString(w, s) + return int64(len(s)), err +} + +// Template for rendering. This method will panic if the template fails to parse. +func (b *Bar) Template(s string) { + t, err := template.New("").Parse(s) + if err != nil { + panic(err) + } + + b.tmpl = t +} From 65b5feef0daad5220303ababeb7cfe4aec2125a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 22 Jun 2018 14:25:30 +0200 Subject: [PATCH 09/32] Make the geoip lookup output prettier --- internal/cli/geoip/geoip.go | 8 ++++++-- internal/log/handlers/cli/cli.go | 31 +++++++++++++++++++++++++++++++ internal/log/handlers/cli/util.go | 16 ++++++++++++++-- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/internal/cli/geoip/geoip.go b/internal/cli/geoip/geoip.go index d4fb28f..0de3620 100644 --- a/internal/cli/geoip/geoip.go +++ b/internal/cli/geoip/geoip.go @@ -1,9 +1,12 @@ package geoip import ( + "fmt" + "github.com/alecthomas/kingpin" "github.com/apex/log" "github.com/ooni/probe-cli/internal/cli/root" + "github.com/ooni/probe-cli/internal/output" "github.com/ooni/probe-cli/utils" ) @@ -13,7 +16,7 @@ func init() { shouldUpdate := cmd.Flag("update", "Update the geoip database").Bool() cmd.Action(func(_ *kingpin.ParseContext) error { - log.Info("geoip") + output.SectionTitle("GeoIP lookup") ctx, err := root.Init() if err != nil { return err @@ -35,7 +38,8 @@ func init() { } log.WithFields(log.Fields{ - "asn": loc.ASN, + "type": "table", + "asn": fmt.Sprintf("AS%d", loc.ASN), "network_name": loc.NetworkName, "country_code": loc.CountryCode, "ip": loc.IP, diff --git a/internal/log/handlers/cli/cli.go b/internal/log/handlers/cli/cli.go index 7985fec..07ca09b 100644 --- a/internal/log/handlers/cli/cli.go +++ b/internal/log/handlers/cli/cli.go @@ -73,6 +73,35 @@ func logSectionTitle(w io.Writer, f log.Fields) error { return nil } +func logTable(w io.Writer, f log.Fields) error { + color := color.New(color.FgBlue) + + names := f.Names() + + var lines []string + colWidth := 0 + for _, name := range names { + if name == "type" { + continue + } + line := fmt.Sprintf("%s: %s", color.Sprint(name), f.Get(name)) + lineLength := escapeAwareRuneCountInString(line) + lines = append(lines, line) + if colWidth < lineLength { + colWidth = lineLength + } + } + + fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth+2)+"┓\n") + for _, line := range lines { + fmt.Fprintf(w, "┃ %s ┃\n", + RightPad(line, colWidth), + ) + } + fmt.Fprintf(w, "┗"+strings.Repeat("━", colWidth+2)+"┛\n") + return nil +} + var bar *progress.Bar var lastBarChars int64 @@ -88,6 +117,8 @@ func (h *Handler) TypedLog(t string, e *log.Entry) error { bar.Text(e.Message) lastBarChars, err = bar.WriteTo(h.Writer) return err + case "table": + return logTable(h.Writer, e.Fields) case "result_item": return logResultItem(h.Writer, e.Fields) case "result_summary": diff --git a/internal/log/handlers/cli/util.go b/internal/log/handlers/cli/util.go index 41a42e2..07de510 100644 --- a/internal/log/handlers/cli/util.go +++ b/internal/log/handlers/cli/util.go @@ -1,10 +1,22 @@ package cli import ( + "regexp" "strings" "unicode/utf8" ) -func RightPad(str string, length int) string { - return str + strings.Repeat(" ", length-utf8.RuneCountInString(str)) +// Finds the control character sequences (like colors) +var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d") + +func escapeAwareRuneCountInString(s string) int { + n := utf8.RuneCountInString(s) + for _, sm := range ctrlFinder.FindAllString(s, -1) { + n -= utf8.RuneCountInString(sm) + } + return n +} + +func RightPad(str string, length int) string { + return str + strings.Repeat(" ", length-escapeAwareRuneCountInString(str)) } From 030ecacf8080e2ddb9ba2874e331de69af6b5b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 22 Jun 2018 14:55:00 +0200 Subject: [PATCH 10/32] Improve the help output of run --- internal/cli/run/run.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/cli/run/run.go b/internal/cli/run/run.go index dacd23d..103d499 100644 --- a/internal/cli/run/run.go +++ b/internal/cli/run/run.go @@ -4,11 +4,13 @@ import ( "errors" "fmt" "path/filepath" + "strings" "time" "github.com/alecthomas/kingpin" "github.com/apex/log" "github.com/ooni/probe-cli/internal/cli/root" + "github.com/ooni/probe-cli/internal/colors" "github.com/ooni/probe-cli/internal/database" "github.com/ooni/probe-cli/nettests" "github.com/ooni/probe-cli/nettests/groups" @@ -18,7 +20,14 @@ import ( func init() { cmd := root.Command("run", "Run a test group or OONI Run link") - nettestGroup := cmd.Arg("name", "the nettest group to run").Required().String() + var nettestGroupNames []string + for name := range groups.NettestGroups { + nettestGroupNames = append(nettestGroupNames, colors.Blue(name)) + } + + nettestGroup := cmd.Arg("name", + fmt.Sprintf("the nettest group to run. Supported tests are: %s", + strings.Join(nettestGroupNames, ", "))).Required().String() cmd.Action(func(_ *kingpin.ParseContext) error { log.Infof("Starting %s", *nettestGroup) From 38a5637cf5e8c0c7ff90c685e1ba7af0c8c0d171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Mon, 25 Jun 2018 13:19:54 +0200 Subject: [PATCH 11/32] Write to output when a function is not implemented --- internal/cli/info/info.go | 1 + internal/cli/nettest/nettest.go | 1 + internal/cli/show/show.go | 1 + internal/cli/upload/upload.go | 1 + 4 files changed, 4 insertions(+) diff --git a/internal/cli/info/info.go b/internal/cli/info/info.go index ef80127..5743dff 100644 --- a/internal/cli/info/info.go +++ b/internal/cli/info/info.go @@ -11,6 +11,7 @@ func init() { cmd.Action(func(_ *kingpin.ParseContext) error { log.Info("Info") + log.Error("this function is not implemented") return nil }) } diff --git a/internal/cli/nettest/nettest.go b/internal/cli/nettest/nettest.go index 39fc053..3517a66 100644 --- a/internal/cli/nettest/nettest.go +++ b/internal/cli/nettest/nettest.go @@ -11,6 +11,7 @@ func init() { cmd.Action(func(_ *kingpin.ParseContext) error { log.Info("Nettest") + log.Error("this function is not implemented") return nil }) } diff --git a/internal/cli/show/show.go b/internal/cli/show/show.go index ff79442..ca75897 100644 --- a/internal/cli/show/show.go +++ b/internal/cli/show/show.go @@ -15,6 +15,7 @@ func init() { log.WithError(err).Error("failed to initialize root context") return err } + log.Error("this function is not implemented") return nil }) diff --git a/internal/cli/upload/upload.go b/internal/cli/upload/upload.go index 208af91..42a70f4 100644 --- a/internal/cli/upload/upload.go +++ b/internal/cli/upload/upload.go @@ -11,6 +11,7 @@ func init() { cmd.Action(func(_ *kingpin.ParseContext) error { log.Info("Uploading") + log.Error("this function is not implemented") return nil }) } From 7951ee6bb5b8293951ea7bd7438b2d1ddc29e126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Mon, 25 Jun 2018 16:31:44 +0200 Subject: [PATCH 12/32] Add onboard command --- cmd/ooni/main.go | 1 + internal/cli/onboard/onboard.go | 134 +++++++++++++++++++++++++++ internal/log/handlers/cli/cli.go | 7 +- internal/log/handlers/cli/results.go | 21 +++-- internal/log/handlers/cli/util.go | 22 ----- internal/output/output.go | 19 ++++ internal/util/util.go | 88 ++++++++++++++++++ 7 files changed, 257 insertions(+), 35 deletions(-) create mode 100644 internal/cli/onboard/onboard.go delete mode 100644 internal/log/handlers/cli/util.go diff --git a/cmd/ooni/main.go b/cmd/ooni/main.go index e48b9b3..df0473e 100644 --- a/cmd/ooni/main.go +++ b/cmd/ooni/main.go @@ -8,6 +8,7 @@ import ( _ "github.com/ooni/probe-cli/internal/cli/info" _ "github.com/ooni/probe-cli/internal/cli/list" _ "github.com/ooni/probe-cli/internal/cli/nettest" + _ "github.com/ooni/probe-cli/internal/cli/onboard" _ "github.com/ooni/probe-cli/internal/cli/run" _ "github.com/ooni/probe-cli/internal/cli/show" _ "github.com/ooni/probe-cli/internal/cli/upload" diff --git a/internal/cli/onboard/onboard.go b/internal/cli/onboard/onboard.go new file mode 100644 index 0000000..b130a89 --- /dev/null +++ b/internal/cli/onboard/onboard.go @@ -0,0 +1,134 @@ +package onboard + +import ( + "fmt" + + "github.com/alecthomas/kingpin" + "github.com/apex/log" + "github.com/ooni/probe-cli/internal/cli/root" + "github.com/ooni/probe-cli/internal/colors" + "github.com/ooni/probe-cli/internal/output" + "gopkg.in/AlecAivazis/survey.v1" +) + +func init() { + cmd := root.Command("onboard", "Starts the onboarding process") + + cmd.Action(func(_ *kingpin.ParseContext) error { + output.SectionTitle("What is OONI Probe?") + + fmt.Println() + output.Paragraph("Your tool for detecting internet censorship!") + fmt.Println() + output.Paragraph("OONI Probe checks whether your provider blocks access to sites and services. Run OONI Probe to collect evidence of internet censorship and to measure your network performance.") + fmt.Println() + output.PressEnterToContinue("Press 'Enter' to continue...") + + output.SectionTitle("Heads Up") + fmt.Println() + output.Bullet("Anyone monitoring your internet activity (such as your government or ISP) may be able to see that you are running OONI Probe.") + fmt.Println() + output.Bullet("The network data you will collect will automatically be published (unless you opt-out in the settings).") + fmt.Println() + output.Bullet("You may test objectionable sites.") + fmt.Println() + output.Bullet("Read the documentation to learn more.") + fmt.Println() + output.PressEnterToContinue("Press 'Enter' to continue...") + + output.SectionTitle("Pop Quiz!") + output.Paragraph("") + answer := "" + quiz1 := &survey.Select{ + Message: "Anyone monitoring my internet activity may be able to see that I am running OONI Probe.", + Options: []string{"true", "false"}, + Default: "true", + } + survey.AskOne(quiz1, &answer, nil) + if answer != "true" { + output.Paragraph(colors.Red("Actually...")) + output.Paragraph("OONI Probe is not a privacy tool. Therefore, anyone monitoring your internet activity may be able to see which software you are running.") + } else { + output.Paragraph(colors.Blue("Good job!")) + } + answer = "" + quiz2 := &survey.Select{ + Message: "The network data I will collect will automatically be published (unless I opt-out in the settings).", + Options: []string{"true", "false"}, + Default: "true", + } + survey.AskOne(quiz2, &answer, nil) + if answer != "true" { + output.Paragraph(colors.Red("Actually...")) + output.Paragraph("The network data you will collect will automatically be published to increase transparency of internet censorship (unless you opt-out in the settings).") + } else { + output.Paragraph(colors.Blue("Well done!")) + } + + changeDefaults := false + prompt := &survey.Confirm{ + Message: "Do you want to change the default settings?", + Default: false, + } + survey.AskOne(prompt, &changeDefaults, nil) + + settings := struct { + IncludeIP bool + IncludeNetwork bool + IncludeCountry bool + UploadResults bool + SendCrashReports bool + }{} + settings.IncludeIP = false + settings.IncludeNetwork = true + settings.IncludeCountry = true + settings.UploadResults = true + settings.SendCrashReports = true + + if changeDefaults == true { + var qs = []*survey.Question{ + { + Name: "IncludeIP", + Prompt: &survey.Confirm{Message: "Should we include your IP?"}, + }, + { + Name: "IncludeNetwork", + Prompt: &survey.Confirm{ + Message: "Can we include your network name?", + Default: true, + }, + }, + { + Name: "IncludeCountry", + Prompt: &survey.Confirm{ + Message: "Can we include your country name?", + Default: true, + }, + }, + { + Name: "UploadResults", + Prompt: &survey.Confirm{ + Message: "Can we upload your results?", + Default: true, + }, + }, + { + Name: "SendCrashReports", + Prompt: &survey.Confirm{ + Message: "Can we send crash reports to OONI?", + Default: true, + }, + }, + } + + err := survey.Ask(qs, &settings) + if err != nil { + log.WithError(err).Error("there was an error in parsing your responses") + return err + } + } + + log.Error("this function is not implemented") + return nil + }) +} diff --git a/internal/log/handlers/cli/cli.go b/internal/log/handlers/cli/cli.go index 07ca09b..d6a6b15 100644 --- a/internal/log/handlers/cli/cli.go +++ b/internal/log/handlers/cli/cli.go @@ -13,6 +13,7 @@ import ( "github.com/fatih/color" colorable "github.com/mattn/go-colorable" "github.com/ooni/probe-cli/internal/log/handlers/cli/progress" + "github.com/ooni/probe-cli/internal/util" ) // Default handler outputting to stderr. @@ -68,7 +69,7 @@ func logSectionTitle(w io.Writer, f log.Fields) error { title := f.Get("title").(string) fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth+2)+"┓\n") - fmt.Fprintf(w, "┃ %s ┃\n", RightPad(title, colWidth)) + fmt.Fprintf(w, "┃ %s ┃\n", util.RightPad(title, colWidth)) fmt.Fprintf(w, "┗"+strings.Repeat("━", colWidth+2)+"┛\n") return nil } @@ -85,7 +86,7 @@ func logTable(w io.Writer, f log.Fields) error { continue } line := fmt.Sprintf("%s: %s", color.Sprint(name), f.Get(name)) - lineLength := escapeAwareRuneCountInString(line) + lineLength := util.EscapeAwareRuneCountInString(line) lines = append(lines, line) if colWidth < lineLength { colWidth = lineLength @@ -95,7 +96,7 @@ func logTable(w io.Writer, f log.Fields) error { fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth+2)+"┓\n") for _, line := range lines { fmt.Fprintf(w, "┃ %s ┃\n", - RightPad(line, colWidth), + util.RightPad(line, colWidth), ) } fmt.Fprintf(w, "┗"+strings.Repeat("━", colWidth+2)+"┛\n") diff --git a/internal/log/handlers/cli/results.go b/internal/log/handlers/cli/results.go index 50ca767..5c11720 100644 --- a/internal/log/handlers/cli/results.go +++ b/internal/log/handlers/cli/results.go @@ -8,6 +8,7 @@ import ( "time" "github.com/apex/log" + "github.com/ooni/probe-cli/internal/util" ) // XXX Copy-pasta from nettest/groups @@ -118,21 +119,21 @@ func logResultItem(w io.Writer, f log.Fields) error { fmt.Fprintf(w, "┢"+strings.Repeat("━", colWidth*2+2)+"┪\n") } - firstRow := RightPad(fmt.Sprintf("#%d - %s", rID, startTime.Format(time.RFC822)), colWidth*2) + firstRow := util.RightPad(fmt.Sprintf("#%d - %s", rID, startTime.Format(time.RFC822)), colWidth*2) fmt.Fprintf(w, "┃ "+firstRow+" ┃\n") fmt.Fprintf(w, "┡"+strings.Repeat("━", colWidth*2+2)+"┩\n") summary := makeSummary(name, f.Get("summary").(string)) fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n", - RightPad(name, colWidth), - RightPad(summary[0], colWidth))) + util.RightPad(name, colWidth), + util.RightPad(summary[0], colWidth))) fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n", - RightPad(networkName, colWidth), - RightPad(summary[1], colWidth))) + util.RightPad(networkName, colWidth), + util.RightPad(summary[1], colWidth))) fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n", - RightPad(asn, colWidth), - RightPad(summary[2], colWidth))) + util.RightPad(asn, colWidth), + util.RightPad(summary[2], colWidth))) if index == totalCount-1 { fmt.Fprintf(w, "└┬──────────────┬──────────────┬──────────────┬") @@ -156,9 +157,9 @@ func logResultSummary(w io.Writer, f log.Fields) error { } // └┬──────────────┬──────────────┬──────────────┬ fmt.Fprintf(w, " │ %s │ %s │ %s │\n", - RightPad(fmt.Sprintf("%d tests", tests), 12), - RightPad(fmt.Sprintf("%d nets", networks), 12), - RightPad(fmt.Sprintf("%d ⬆ %d ⬇", dataUp, dataDown), 12)) + util.RightPad(fmt.Sprintf("%d tests", tests), 12), + util.RightPad(fmt.Sprintf("%d nets", networks), 12), + util.RightPad(fmt.Sprintf("%d ⬆ %d ⬇", dataUp, dataDown), 12)) fmt.Fprintf(w, " └──────────────┴──────────────┴──────────────┘\n") return nil diff --git a/internal/log/handlers/cli/util.go b/internal/log/handlers/cli/util.go deleted file mode 100644 index 07de510..0000000 --- a/internal/log/handlers/cli/util.go +++ /dev/null @@ -1,22 +0,0 @@ -package cli - -import ( - "regexp" - "strings" - "unicode/utf8" -) - -// Finds the control character sequences (like colors) -var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d") - -func escapeAwareRuneCountInString(s string) int { - n := utf8.RuneCountInString(s) - for _, sm := range ctrlFinder.FindAllString(s, -1) { - n -= utf8.RuneCountInString(sm) - } - return n -} - -func RightPad(str string, length int) string { - return str + strings.Repeat(" ", length-escapeAwareRuneCountInString(str)) -} diff --git a/internal/output/output.go b/internal/output/output.go index 8bb1cd2..2758fd8 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -1,9 +1,13 @@ package output import ( + "bufio" + "fmt" + "os" "time" "github.com/apex/log" + "github.com/ooni/probe-cli/internal/util" ) // Progress logs a progress type event @@ -76,3 +80,18 @@ func SectionTitle(text string) { "title": text, }).Info(text) } + +func Paragraph(text string) { + const width = 80 + fmt.Println(util.WrapString(text, width)) +} + +func Bullet(text string) { + const width = 80 + fmt.Printf("• %s\n", util.WrapString(text, width)) +} + +func PressEnterToContinue(text string) { + fmt.Print(text) + bufio.NewReader(os.Stdin).ReadBytes('\n') +} diff --git a/internal/util/util.go b/internal/util/util.go index 45358bb..5deb009 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -1,8 +1,13 @@ package util import ( + "bytes" "fmt" "os" + "regexp" + "strings" + "unicode" + "unicode/utf8" "github.com/ooni/probe-cli/internal/colors" ) @@ -17,3 +22,86 @@ func Fatal(err error) { fmt.Fprintf(os.Stderr, "\n %s %s\n\n", colors.Red("Error:"), err) os.Exit(1) } + +// Finds the control character sequences (like colors) +var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d") + +func EscapeAwareRuneCountInString(s string) int { + n := utf8.RuneCountInString(s) + for _, sm := range ctrlFinder.FindAllString(s, -1) { + n -= utf8.RuneCountInString(sm) + } + return n +} + +func RightPad(str string, length int) string { + return str + strings.Repeat(" ", length-EscapeAwareRuneCountInString(str)) +} + +// WrapString wraps the given string within lim width in characters. +// +// Wrapping is currently naive and only happens at white-space. A future +// version of the library will implement smarter wrapping. This means that +// pathological cases can dramatically reach past the limit, such as a very +// long word. +// This is taken from: https://github.com/mitchellh/go-wordwrap/tree/f253961a26562056904822f2a52d4692347db1bd +func WrapString(s string, lim uint) string { + // Initialize a buffer with a slightly larger size to account for breaks + init := make([]byte, 0, len(s)) + buf := bytes.NewBuffer(init) + + var current uint + var wordBuf, spaceBuf bytes.Buffer + + for _, char := range s { + if char == '\n' { + if wordBuf.Len() == 0 { + if current+uint(spaceBuf.Len()) > lim { + current = 0 + } else { + current += uint(spaceBuf.Len()) + spaceBuf.WriteTo(buf) + } + spaceBuf.Reset() + } else { + current += uint(spaceBuf.Len() + wordBuf.Len()) + spaceBuf.WriteTo(buf) + spaceBuf.Reset() + wordBuf.WriteTo(buf) + wordBuf.Reset() + } + buf.WriteRune(char) + current = 0 + } else if unicode.IsSpace(char) { + if spaceBuf.Len() == 0 || wordBuf.Len() > 0 { + current += uint(spaceBuf.Len() + wordBuf.Len()) + spaceBuf.WriteTo(buf) + spaceBuf.Reset() + wordBuf.WriteTo(buf) + wordBuf.Reset() + } + + spaceBuf.WriteRune(char) + } else { + + wordBuf.WriteRune(char) + + if current+uint(spaceBuf.Len()+wordBuf.Len()) > lim && uint(wordBuf.Len()) < lim { + buf.WriteRune('\n') + current = 0 + spaceBuf.Reset() + } + } + } + + if wordBuf.Len() == 0 { + if current+uint(spaceBuf.Len()) <= lim { + spaceBuf.WriteTo(buf) + } + } else { + spaceBuf.WriteTo(buf) + wordBuf.WriteTo(buf) + } + + return buf.String() +} From aae2805786b0a8afaedc413227001b95989a5064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Mon, 25 Jun 2018 17:09:10 +0200 Subject: [PATCH 13/32] Write the config file when onboarding is run --- internal/cli/onboard/onboard.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/internal/cli/onboard/onboard.go b/internal/cli/onboard/onboard.go index b130a89..aea1000 100644 --- a/internal/cli/onboard/onboard.go +++ b/internal/cli/onboard/onboard.go @@ -15,6 +15,11 @@ func init() { cmd := root.Command("onboard", "Starts the onboarding process") cmd.Action(func(_ *kingpin.ParseContext) error { + ctx, err := root.Init() + if err != nil { + return err + } + output.SectionTitle("What is OONI Probe?") fmt.Println() @@ -128,7 +133,20 @@ func init() { } } - log.Error("this function is not implemented") + ctx.Config.Lock() + ctx.Config.InformedConsent = true + ctx.Config.Advanced.IncludeCountry = settings.IncludeCountry + ctx.Config.Sharing.IncludeIP = settings.IncludeIP + ctx.Config.Sharing.IncludeASN = settings.IncludeNetwork + ctx.Config.Sharing.UploadResults = settings.UploadResults + ctx.Config.Sharing.SendCrashReports = settings.SendCrashReports + ctx.Config.Unlock() + + if err := ctx.Config.Write(); err != nil { + log.WithError(err).Error("failed to write config file") + return err + } + return nil }) } From a6b95f50c96deebaba8f11881af865da3bcc7c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Mon, 25 Jun 2018 17:14:29 +0200 Subject: [PATCH 14/32] Move onboard into a separate package --- internal/cli/onboard/onboard.go | 136 +----------------------------- internal/onboard/onboard.go | 141 ++++++++++++++++++++++++++++++++ ooni.go | 20 +---- 3 files changed, 145 insertions(+), 152 deletions(-) create mode 100644 internal/onboard/onboard.go diff --git a/internal/cli/onboard/onboard.go b/internal/cli/onboard/onboard.go index aea1000..7970976 100644 --- a/internal/cli/onboard/onboard.go +++ b/internal/cli/onboard/onboard.go @@ -1,14 +1,9 @@ package onboard import ( - "fmt" - "github.com/alecthomas/kingpin" - "github.com/apex/log" "github.com/ooni/probe-cli/internal/cli/root" - "github.com/ooni/probe-cli/internal/colors" - "github.com/ooni/probe-cli/internal/output" - "gopkg.in/AlecAivazis/survey.v1" + "github.com/ooni/probe-cli/internal/onboard" ) func init() { @@ -20,133 +15,6 @@ func init() { return err } - output.SectionTitle("What is OONI Probe?") - - fmt.Println() - output.Paragraph("Your tool for detecting internet censorship!") - fmt.Println() - output.Paragraph("OONI Probe checks whether your provider blocks access to sites and services. Run OONI Probe to collect evidence of internet censorship and to measure your network performance.") - fmt.Println() - output.PressEnterToContinue("Press 'Enter' to continue...") - - output.SectionTitle("Heads Up") - fmt.Println() - output.Bullet("Anyone monitoring your internet activity (such as your government or ISP) may be able to see that you are running OONI Probe.") - fmt.Println() - output.Bullet("The network data you will collect will automatically be published (unless you opt-out in the settings).") - fmt.Println() - output.Bullet("You may test objectionable sites.") - fmt.Println() - output.Bullet("Read the documentation to learn more.") - fmt.Println() - output.PressEnterToContinue("Press 'Enter' to continue...") - - output.SectionTitle("Pop Quiz!") - output.Paragraph("") - answer := "" - quiz1 := &survey.Select{ - Message: "Anyone monitoring my internet activity may be able to see that I am running OONI Probe.", - Options: []string{"true", "false"}, - Default: "true", - } - survey.AskOne(quiz1, &answer, nil) - if answer != "true" { - output.Paragraph(colors.Red("Actually...")) - output.Paragraph("OONI Probe is not a privacy tool. Therefore, anyone monitoring your internet activity may be able to see which software you are running.") - } else { - output.Paragraph(colors.Blue("Good job!")) - } - answer = "" - quiz2 := &survey.Select{ - Message: "The network data I will collect will automatically be published (unless I opt-out in the settings).", - Options: []string{"true", "false"}, - Default: "true", - } - survey.AskOne(quiz2, &answer, nil) - if answer != "true" { - output.Paragraph(colors.Red("Actually...")) - output.Paragraph("The network data you will collect will automatically be published to increase transparency of internet censorship (unless you opt-out in the settings).") - } else { - output.Paragraph(colors.Blue("Well done!")) - } - - changeDefaults := false - prompt := &survey.Confirm{ - Message: "Do you want to change the default settings?", - Default: false, - } - survey.AskOne(prompt, &changeDefaults, nil) - - settings := struct { - IncludeIP bool - IncludeNetwork bool - IncludeCountry bool - UploadResults bool - SendCrashReports bool - }{} - settings.IncludeIP = false - settings.IncludeNetwork = true - settings.IncludeCountry = true - settings.UploadResults = true - settings.SendCrashReports = true - - if changeDefaults == true { - var qs = []*survey.Question{ - { - Name: "IncludeIP", - Prompt: &survey.Confirm{Message: "Should we include your IP?"}, - }, - { - Name: "IncludeNetwork", - Prompt: &survey.Confirm{ - Message: "Can we include your network name?", - Default: true, - }, - }, - { - Name: "IncludeCountry", - Prompt: &survey.Confirm{ - Message: "Can we include your country name?", - Default: true, - }, - }, - { - Name: "UploadResults", - Prompt: &survey.Confirm{ - Message: "Can we upload your results?", - Default: true, - }, - }, - { - Name: "SendCrashReports", - Prompt: &survey.Confirm{ - Message: "Can we send crash reports to OONI?", - Default: true, - }, - }, - } - - err := survey.Ask(qs, &settings) - if err != nil { - log.WithError(err).Error("there was an error in parsing your responses") - return err - } - } - - ctx.Config.Lock() - ctx.Config.InformedConsent = true - ctx.Config.Advanced.IncludeCountry = settings.IncludeCountry - ctx.Config.Sharing.IncludeIP = settings.IncludeIP - ctx.Config.Sharing.IncludeASN = settings.IncludeNetwork - ctx.Config.Sharing.UploadResults = settings.UploadResults - ctx.Config.Sharing.SendCrashReports = settings.SendCrashReports - ctx.Config.Unlock() - - if err := ctx.Config.Write(); err != nil { - log.WithError(err).Error("failed to write config file") - return err - } - - return nil + return onboard.Onboarding(ctx.Config) }) } diff --git a/internal/onboard/onboard.go b/internal/onboard/onboard.go new file mode 100644 index 0000000..b112f09 --- /dev/null +++ b/internal/onboard/onboard.go @@ -0,0 +1,141 @@ +package onboard + +import ( + "fmt" + + "github.com/apex/log" + "github.com/ooni/probe-cli/config" + "github.com/ooni/probe-cli/internal/colors" + "github.com/ooni/probe-cli/internal/output" + survey "gopkg.in/AlecAivazis/survey.v1" +) + +func Onboarding(config *config.Config) error { + output.SectionTitle("What is OONI Probe?") + + fmt.Println() + output.Paragraph("Your tool for detecting internet censorship!") + fmt.Println() + output.Paragraph("OONI Probe checks whether your provider blocks access to sites and services. Run OONI Probe to collect evidence of internet censorship and to measure your network performance.") + fmt.Println() + output.PressEnterToContinue("Press 'Enter' to continue...") + + output.SectionTitle("Heads Up") + fmt.Println() + output.Bullet("Anyone monitoring your internet activity (such as your government or ISP) may be able to see that you are running OONI Probe.") + fmt.Println() + output.Bullet("The network data you will collect will automatically be published (unless you opt-out in the settings).") + fmt.Println() + output.Bullet("You may test objectionable sites.") + fmt.Println() + output.Bullet("Read the documentation to learn more.") + fmt.Println() + output.PressEnterToContinue("Press 'Enter' to continue...") + + output.SectionTitle("Pop Quiz!") + output.Paragraph("") + answer := "" + quiz1 := &survey.Select{ + Message: "Anyone monitoring my internet activity may be able to see that I am running OONI Probe.", + Options: []string{"true", "false"}, + Default: "true", + } + survey.AskOne(quiz1, &answer, nil) + if answer != "true" { + output.Paragraph(colors.Red("Actually...")) + output.Paragraph("OONI Probe is not a privacy tool. Therefore, anyone monitoring your internet activity may be able to see which software you are running.") + } else { + output.Paragraph(colors.Blue("Good job!")) + } + answer = "" + quiz2 := &survey.Select{ + Message: "The network data I will collect will automatically be published (unless I opt-out in the settings).", + Options: []string{"true", "false"}, + Default: "true", + } + survey.AskOne(quiz2, &answer, nil) + if answer != "true" { + output.Paragraph(colors.Red("Actually...")) + output.Paragraph("The network data you will collect will automatically be published to increase transparency of internet censorship (unless you opt-out in the settings).") + } else { + output.Paragraph(colors.Blue("Well done!")) + } + + changeDefaults := false + prompt := &survey.Confirm{ + Message: "Do you want to change the default settings?", + Default: false, + } + survey.AskOne(prompt, &changeDefaults, nil) + + settings := struct { + IncludeIP bool + IncludeNetwork bool + IncludeCountry bool + UploadResults bool + SendCrashReports bool + }{} + settings.IncludeIP = false + settings.IncludeNetwork = true + settings.IncludeCountry = true + settings.UploadResults = true + settings.SendCrashReports = true + + if changeDefaults == true { + var qs = []*survey.Question{ + { + Name: "IncludeIP", + Prompt: &survey.Confirm{Message: "Should we include your IP?"}, + }, + { + Name: "IncludeNetwork", + Prompt: &survey.Confirm{ + Message: "Can we include your network name?", + Default: true, + }, + }, + { + Name: "IncludeCountry", + Prompt: &survey.Confirm{ + Message: "Can we include your country name?", + Default: true, + }, + }, + { + Name: "UploadResults", + Prompt: &survey.Confirm{ + Message: "Can we upload your results?", + Default: true, + }, + }, + { + Name: "SendCrashReports", + Prompt: &survey.Confirm{ + Message: "Can we send crash reports to OONI?", + Default: true, + }, + }, + } + + err := survey.Ask(qs, &settings) + if err != nil { + log.WithError(err).Error("there was an error in parsing your responses") + return err + } + } + + config.Lock() + config.InformedConsent = true + config.Advanced.IncludeCountry = settings.IncludeCountry + config.Sharing.IncludeIP = settings.IncludeIP + config.Sharing.IncludeASN = settings.IncludeNetwork + config.Sharing.UploadResults = settings.UploadResults + config.Sharing.SendCrashReports = settings.SendCrashReports + config.Unlock() + + if err := config.Write(); err != nil { + log.WithError(err).Error("failed to write config file") + return err + } + return nil +} diff --git a/ooni.go b/ooni.go index 399accc..df2c7a9 100644 --- a/ooni.go +++ b/ooni.go @@ -11,27 +11,11 @@ import ( "github.com/ooni/probe-cli/config" "github.com/ooni/probe-cli/internal/database" "github.com/ooni/probe-cli/internal/legacy" + "github.com/ooni/probe-cli/internal/onboard" "github.com/ooni/probe-cli/utils" "github.com/pkg/errors" ) -// Onboarding process -func Onboarding(c *config.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 { - log.Warnf("Failed to save informed consent: %v", err) - return err - } - return nil -} - // Context for OONI Probe type Context struct { Config *config.Config @@ -74,7 +58,7 @@ func (c *Context) LocationLookup() error { // config option is set to false func (c *Context) MaybeOnboarding() error { if c.Config.InformedConsent == false { - if err := Onboarding(c.Config); err != nil { + if err := onboard.Onboarding(c.Config); err != nil { return errors.Wrap(err, "onboarding") } } From 78cf8d6ca2dfd6c545f9214a8949c207032db927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Mon, 25 Jun 2018 17:49:17 +0200 Subject: [PATCH 15/32] Create config file when it's missing --- config/parser.go | 5 ++--- ooni.go | 41 ++++++++++++++++++++++++++++------------- utils/paths.go | 5 +++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/config/parser.go b/config/parser.go index ea79923..8ebbe0b 100644 --- a/config/parser.go +++ b/config/parser.go @@ -3,7 +3,6 @@ package config import ( "encoding/json" "io/ioutil" - "path/filepath" "sync" "github.com/ooni/probe-cli/utils" @@ -14,7 +13,7 @@ import ( func ReadConfig(path string) (*Config, error) { b, err := ioutil.ReadFile(path) if err != nil { - return nil, errors.Wrap(err, "reading file") + return nil, err } c, err := ParseConfig(b) @@ -93,7 +92,7 @@ func (c *Config) Default() error { return err } - c.path = filepath.Join(home, "config.json") + c.path = utils.ConfigPath(home) return nil } diff --git a/ooni.go b/ooni.go index df2c7a9..c7a7125 100644 --- a/ooni.go +++ b/ooni.go @@ -4,11 +4,11 @@ import ( "io/ioutil" "os" "path" - "path/filepath" "github.com/apex/log" "github.com/jmoiron/sqlx" "github.com/ooni/probe-cli/config" + "github.com/ooni/probe-cli/internal/bindata" "github.com/ooni/probe-cli/internal/database" "github.com/ooni/probe-cli/internal/legacy" "github.com/ooni/probe-cli/internal/onboard" @@ -100,7 +100,7 @@ func (c *Context) Init() error { c.Config, err = config.ReadConfig(c.configPath) } else { log.Debug("Reading default config file") - c.Config, err = ReadDefaultConfigPaths(c.Home) + c.Config, err = InitDefaultConfig(c.Home) } if err != nil { return err @@ -144,21 +144,36 @@ func MaybeInitializeHome(home string) error { return nil } -// ReadDefaultConfigPaths from common locations. -func ReadDefaultConfigPaths(home string) (*config.Config, error) { - var paths = []string{ - filepath.Join(home, "config.json"), - } - for _, path := range paths { - if _, err := os.Stat(path); err == nil { - c, err := config.ReadConfig(path) +// InitDefaultConfig reads the config from common locations or creates it if +// missing. +func InitDefaultConfig(home string) (*config.Config, error) { + var ( + err error + c *config.Config + configPath = utils.ConfigPath(home) + ) + + c, err = config.ReadConfig(configPath) + if err != nil { + if os.IsNotExist(err) { + log.Debugf("writing default config to %s", configPath) + var data []byte + data, err = bindata.Asset("data/default-config.json") if err != nil { return nil, err } - return c, nil + err = ioutil.WriteFile( + configPath, + data, + 0644, + ) + if err != nil { + return nil, err + } + return InitDefaultConfig(home) } + return nil, err } - // Run from the default config - return config.ReadConfig(paths[0]) + return c, nil } diff --git a/utils/paths.go b/utils/paths.go index 63ce079..c4be216 100644 --- a/utils/paths.go +++ b/utils/paths.go @@ -20,6 +20,11 @@ func RequiredDirs(home string) []string { return requiredDirs } +// ConfigPath returns the default path to the config file +func ConfigPath(home string) string { + return filepath.Join(home, "config.json") +} + // GeoIPDir returns the geoip data dir for the given OONI Home func GeoIPDir(home string) string { return filepath.Join(home, "geoip") From d23f30d1598bfa4f6ea6058eb1e8d6432893b4e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Wed, 27 Jun 2018 15:45:27 +0200 Subject: [PATCH 16/32] Update bindata --- internal/bindata/bindata.go | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/internal/bindata/bindata.go b/internal/bindata/bindata.go index 1935962..3f4d952 100644 --- a/internal/bindata/bindata.go +++ b/internal/bindata/bindata.go @@ -80,26 +80,26 @@ func (fi bindataFileInfo) Sys() interface{} { } var _bindataDataDefaultconfigjson = []byte( - "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x94\xcd\x6e\xe3\x38\x0c\xc7\xef\x7d\x0a\x41\xe7\x3a\xcd\x62\x6f\x39" + - "\xee\x6d\x0f\xbb\x1d\x60\xe6\x56\x14\x82\x62\xd1\x36\x31\x32\xa9\x11\xe9\x64\x82\x41\xdf\x7d\x20\x35\x89\xed\x7e" + - "\x4d\x8f\xe2\x9f\xa2\xc8\x1f\x29\xfe\xba\x31\xc6\x3a\xbb\x33\xf6\xdb\x80\x62\x50\xcc\x89\xa7\x6c\xee\xef\xff\xff" + - "\xd7\x7c\xc9\xbc\x07\xd3\x32\x75\xd8\x9b\x0e\x23\x6c\xcc\x57\x00\x33\xa8\x26\xd9\xdd\xdd\x31\x13\x6e\x90\xef\x06" + - "\x88\xa9\x1e\x52\xf1\x6f\xda\x88\xa6\xe3\x6c\x8a\xd9\xde\x96\xf0\x7e\x52\x76\x53\x0a\x5e\xc1\xee\x8c\xe6\x09\xaa" + - "\x59\x06\x9f\x91\x7a\xbb\x33\x25\x09\x63\x2c\x52\x1b\xa7\x00\x0e\x93\xdd\x99\xce\x47\xa9\x7e\x0b\xc1\x0b\x2d\x02" + - "\x2c\x84\x3e\xc9\x5a\x98\x52\x64\x1f\x5c\x06\x99\xa2\xbe\xd0\x04\x28\xb8\x36\x7b\x19\x5c\x86\xc4\xf9\xaa\xdf\x18" + - "\xf3\x54\x33\x23\x56\xec\xb0\xf5\x8a\x4c\x32\xe7\x07\xe4\xf7\x11\xc2\x3a\x5a\xf5\x3d\x39\x26\xa7\x20\xea\x5a\x1e" + - "\x53\x84\x72\xf1\x3d\x37\x82\xa3\x5c\xea\xbb\xbe\x58\x10\x8d\x5e\x21\xd4\x28\x2b\x2a\xf3\xab\x4b\x24\x67\x6b\x75" + - "\x2f\xe1\x1e\xaa\xd9\x18\x7b\x84\x7d\xd3\x32\x11\xb4\x8a\x07\xd4\x93\xbd\xbd\x28\x9d\x6f\x61\xcf\xfc\xbd\x19\x41" + - "\x04\xa8\x87\x3c\x6b\xc7\xc1\xab\xf8\x94\x66\x8b\x42\x84\x3e\xfb\x71\xb6\x04\x2f\xc3\x7c\xa2\xa0\xf3\xa1\x8c\x44" + - "\x83\x74\xf0\x11\x43\x93\xe1\xc7\x04\xa2\x4d\x44\x82\x17\x2e\x03\xf8\x00\xb9\xe9\x10\x62\x68\x46\x4f\x98\xa6\x58" + - "\x29\xdb\xea\xf6\x78\x2e\x6e\x64\xd2\x21\x9e\x9c\x8f\x91\x8f\x9e\xda\x32\x36\xf6\xef\xed\xf6\xbf\x7f\xec\x95\x58" + - "\xa5\x2d\xa0\x05\xd6\xa2\x47\x47\xd8\x0b\x2a\xcc\x96\x05\xab\xd6\x2b\xf4\x9c\xb1\xaa\x0f\x8f\x55\x7e\xba\x4e\x92" + - "\xa8\x27\x75\x85\x8d\xef\x97\x0d\xf8\x00\xf6\xc7\x50\xdf\xc2\xba\x04\x7b\x36\xad\xf3\x48\x90\x3b\xce\xe3\xb9\xe8" + - "\xcf\x64\x50\x1a\x71\x09\xb5\xec\x8e\x13\xc8\x07\xc8\x05\x5d\x99\x2e\xfb\x86\xe6\xca\xf4\xbf\x76\x28\x8d\x7e\xf7" + - "\xf6\x42\x5c\x5f\x5f\x95\x31\x62\x08\x11\xf6\xfc\xf3\x93\x45\xfc\x79\x80\x3e\x39\x42\x57\x9e\xf3\xd7\x0a\x87\x42" + - "\x33\xbc\xde\x33\x2d\x4f\xa4\xf9\xf4\x62\x73\x08\xb8\xc0\xa3\x47\x72\x5d\x66\x3a\xff\xc5\xd5\x7a\x70\xcf\x2b\xd1" + - "\x1d\x20\xcb\xf3\x47\xb7\xdb\xcd\x76\xf3\xd7\xf3\xb6\x73\x48\xa5\x83\x65\xde\x98\x04\x48\x2f\xd7\x9f\x6e\x7e\x07" + - "\x00\x00\xff\xff\x0f\x7e\x15\xb3\x6d\x05\x00\x00") + "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x94\x41\x6f\xdb\x3c\x0c\x86\xef\xfd\x15\x82\xce\x75\x53\xe0\xbb\xe5" + + "\xf8\xdd\x76\xd8\x3a\x60\xbb\x15\x85\xa0\x48\xb4\x4d\x4c\x26\x35\x91\x4e\x16\x0c\xfd\xef\x83\xdc\x34\x56\xda\xae" + + "\xeb\xd1\xef\x4b\xd1\xe4\x43\x4a\xbf\xaf\x8c\xb1\xce\x6e\x8d\xfd\x3e\xa2\x18\x14\x73\xe4\xb9\x98\xbb\xbb\x2f\x9f" + + "\xcc\xd7\xc2\x3b\x30\x81\xa9\xc7\xc1\xf4\x98\xe0\xc6\x7c\x03\x30\xa3\x6a\x96\xed\x66\xc3\x4c\x78\x83\xbc\x19\x21" + + "\xe5\x4d\xae\xb1\x5d\x48\x68\x7a\x2e\xa6\x4a\xf6\x7a\x49\xbd\x87\x22\xc8\x64\xb7\xe6\xf6\x49\x40\xea\xb9\x4c\x10" + + "\x5d\x60\x12\x20\xb5\x5b\xd3\xfb\x24\xb0\xb8\x7e\x56\x76\x73\x8e\x5e\xc1\x6e\x8d\x96\xf9\x49\x96\xd1\x17\xa4\xc1" + + "\x6e\x4d\xad\xd7\x18\x8b\x14\xd2\x1c\xc1\x61\x6e\x8f\x37\x86\x17\x6a\x12\x34\xc6\x90\xe5\xd2\x98\x73\x62\x1f\x5d" + + "\x01\x99\x93\xbe\xf0\x04\x28\xba\x50\xbc\x8c\xae\x40\xe6\x72\xf6\xaf\x8c\x79\x5c\x2a\x23\x56\xec\x31\x78\x45\x26" + + "\x59\xeb\x03\xf2\xbb\x04\xf1\x32\xdb\x12\x7b\x74\x4c\x4e\x41\xd4\x05\x9e\x72\x02\x7d\x82\xf3\x66\x18\xc1\x41\x9e" + + "\xfb\x3b\xff\xb1\x22\x9a\xbc\x42\x5c\xb2\x5c\x50\x59\xff\xda\x22\x39\xa9\x4b\x78\x4d\x77\xbf\xc8\xc6\xd8\x03\xec" + + "\xba\xc0\x44\x10\x14\xf7\xa8\x47\x7b\xfd\xec\xf4\x3e\xc0\x8e\xf9\x47\x37\x81\x08\xd0\x00\x65\xf5\x0e\xa3\x57\xf1" + + "\x39\xaf\x8a\x42\x82\xa1\xf8\x69\x55\xa2\x97\x71\xfd\xa2\xa8\xeb\x47\xdd\x9e\x0e\x69\xef\x13\xc6\xae\xc0\xcf\x19" + + "\x44\xbb\x84\x04\x2f\x42\x46\xf0\x11\x4a\xd7\x23\xa4\xd8\x4d\x9e\x30\xcf\x69\xa1\x6c\x97\xb0\x87\x53\x73\x13\x93" + + "\x8e\xe9\xe8\x7c\x4a\x7c\xf0\x14\xea\xda\xd8\xff\x6e\x6f\x3f\xff\x6f\xcf\xc4\x16\xda\x02\x5a\x61\x35\x33\x3a\xc0" + + "\x4e\x50\x61\x55\x1a\x56\xc1\x2b\x0c\x5c\x70\x71\xef\x1f\x16\xfb\xf1\xbc\x49\xa2\x9e\xd4\x55\x36\x7e\x68\x07\xf0" + + "\x0e\xec\xf7\xa1\xbe\x85\xb5\x05\x7b\x92\x2e\xeb\xc8\x50\xea\x4d\x3a\x35\xfd\x91\x0a\xea\x20\x9e\x53\xb5\xd3\x71" + + "\x02\x65\x0f\xa5\xa2\xab\xdb\x65\xdf\xf0\x5c\xdd\xfe\xd7\x01\x75\xd0\x7f\x3d\xdd\x98\x97\xc7\x2f\xda\x98\x30\xc6" + + "\x04\x3b\xfe\xf5\xc1\x26\xfe\xbd\x40\x1f\x5c\xa1\x33\xcf\xf5\x6a\xc5\x7d\xa5\x19\x5f\xbf\x33\x81\x67\xd2\x72\x7c" + + "\xf1\x72\x08\xb8\xc8\x93\x47\x72\x7d\x61\x3a\xdd\xc5\xe6\xb6\x3e\x5e\xfd\x09\x00\x00\xff\xff\xdf\xd2\xaa\xf7\x5f" + + "\x05\x00\x00") func bindataDataDefaultconfigjsonBytes() ([]byte, error) { return bindataRead( From 533ea77aa2ee927a57c720afba701d3ceaefaa79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Wed, 27 Jun 2018 15:45:35 +0200 Subject: [PATCH 17/32] Add some XXX --- nettests/websites/web_connectivity.go | 1 + 1 file changed, 1 insertion(+) diff --git a/nettests/websites/web_connectivity.go b/nettests/websites/web_connectivity.go index ed81742..d149207 100644 --- a/nettests/websites/web_connectivity.go +++ b/nettests/websites/web_connectivity.go @@ -29,6 +29,7 @@ func lookupURLs(ctl *nettests.Controller) ([]string, error) { parsed = new(URLResponse) urls []string ) + // XXX pass in the configuration for category codes reqURL := fmt.Sprintf("%s/api/v1/urls?probe_cc=%s", orchestrateBaseURL, ctl.Ctx.Location.CountryCode) From cf588c846656664df7ca59247b41b13c4fdaa087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Thu, 28 Jun 2018 13:51:43 +0200 Subject: [PATCH 18/32] Move summary related code into it's own package --- internal/database/models.go | 13 ++----- internal/log/handlers/cli/results.go | 35 +++-------------- nettests/groups/groups.go | 56 +++++----------------------- nettests/summary/summary.go | 44 ++++++++++++++++++++++ 4 files changed, 63 insertions(+), 85 deletions(-) create mode 100644 nettests/summary/summary.go diff --git a/internal/database/models.go b/internal/database/models.go index 3bc2539..689d806 100644 --- a/internal/database/models.go +++ b/internal/database/models.go @@ -7,16 +7,11 @@ import ( "github.com/apex/log" "github.com/jmoiron/sqlx" + "github.com/ooni/probe-cli/nettests/summary" "github.com/ooni/probe-cli/utils" "github.com/pkg/errors" ) -// ResultSummaryFunc is the function used to generate result summaries -type ResultSummaryFunc func(SummaryMap) (string, error) - -// SummaryMap contains a mapping from test name to serialized summary for it -type SummaryMap map[string][]string - // UpdateOne will run the specified update query and check that it only affected one row func UpdateOne(db *sqlx.DB, query string, arg interface{}) error { res, err := db.NamedExec(query, arg) @@ -298,8 +293,8 @@ func ListResults(db *sqlx.DB) ([]*Result, []*Result, error) { // MakeSummaryMap return a mapping of test names to summaries for the given // result -func MakeSummaryMap(db *sqlx.DB, r *Result) (SummaryMap, error) { - summaryMap := SummaryMap{} +func MakeSummaryMap(db *sqlx.DB, r *Result) (summary.SummaryMap, error) { + summaryMap := summary.SummaryMap{} msmts := []Measurement{} // XXX maybe we only want to select some of the columns @@ -319,7 +314,7 @@ func MakeSummaryMap(db *sqlx.DB, r *Result) (SummaryMap, error) { } // Finished marks the result as done and sets the runtime -func (r *Result) Finished(db *sqlx.DB, makeSummary ResultSummaryFunc) error { +func (r *Result) Finished(db *sqlx.DB, makeSummary summary.ResultSummaryFunc) error { if r.Done == true || r.Runtime != 0 { return errors.New("Result is already finished") } diff --git a/internal/log/handlers/cli/results.go b/internal/log/handlers/cli/results.go index 5c11720..630145b 100644 --- a/internal/log/handlers/cli/results.go +++ b/internal/log/handlers/cli/results.go @@ -9,34 +9,9 @@ import ( "github.com/apex/log" "github.com/ooni/probe-cli/internal/util" + "github.com/ooni/probe-cli/nettests/summary" ) -// XXX Copy-pasta from nettest/groups -// PerformanceSummary is the result summary for a performance test -type PerformanceSummary struct { - Upload int64 - Download int64 - Ping float64 - Bitrate int64 -} - -// MiddleboxSummary is the summary for the middlebox tests -type MiddleboxSummary struct { - Detected bool -} - -// IMSummary is the summary for the im tests -type IMSummary struct { - Tested uint - Blocked uint -} - -// WebsitesSummary is the summary for the websites test -type WebsitesSummary struct { - Tested uint - Blocked uint -} - func formatSpeed(speed int64) string { if speed < 1000 { return fmt.Sprintf("%d Kbit/s", speed) @@ -51,7 +26,7 @@ func formatSpeed(speed int64) string { var summarizers = map[string]func(string) []string{ "websites": func(ss string) []string { - var summary WebsitesSummary + var summary summary.WebsitesSummary if err := json.Unmarshal([]byte(ss), &summary); err != nil { return nil } @@ -62,7 +37,7 @@ var summarizers = map[string]func(string) []string{ } }, "performance": func(ss string) []string { - var summary PerformanceSummary + var summary summary.PerformanceSummary if err := json.Unmarshal([]byte(ss), &summary); err != nil { return nil } @@ -73,7 +48,7 @@ var summarizers = map[string]func(string) []string{ } }, "im": func(ss string) []string { - var summary IMSummary + var summary summary.IMSummary if err := json.Unmarshal([]byte(ss), &summary); err != nil { return nil } @@ -84,7 +59,7 @@ var summarizers = map[string]func(string) []string{ } }, "middlebox": func(ss string) []string { - var summary MiddleboxSummary + var summary summary.MiddleboxSummary if err := json.Unmarshal([]byte(ss), &summary); err != nil { return nil } diff --git a/nettests/groups/groups.go b/nettests/groups/groups.go index b35e04d..06d6e67 100644 --- a/nettests/groups/groups.go +++ b/nettests/groups/groups.go @@ -2,14 +2,13 @@ package groups import ( "encoding/json" - "fmt" "github.com/apex/log" - "github.com/ooni/probe-cli/internal/database" "github.com/ooni/probe-cli/nettests" "github.com/ooni/probe-cli/nettests/im" "github.com/ooni/probe-cli/nettests/middlebox" "github.com/ooni/probe-cli/nettests/performance" + "github.com/ooni/probe-cli/nettests/summary" "github.com/ooni/probe-cli/nettests/websites" ) @@ -17,42 +16,7 @@ import ( type NettestGroup struct { Label string Nettests []nettests.Nettest - Summary database.ResultSummaryFunc -} - -// PerformanceSummary is the result summary for a performance test -type PerformanceSummary struct { - Upload int64 - Download int64 - Ping float64 - Bitrate int64 -} - -// MiddleboxSummary is the summary for the middlebox tests -type MiddleboxSummary struct { - Detected bool -} - -// IMSummary is the summary for the im tests -type IMSummary struct { - Tested uint - Blocked uint -} - -// WebsitesSummary is the summary for the websites test -type WebsitesSummary struct { - Tested uint - Blocked uint -} - -func checkRequiredKeys(rk []string, m database.SummaryMap) error { - for _, key := range rk { - if _, ok := m[key]; ok { - continue - } - return fmt.Errorf("missing SummaryMap key '%s'", key) - } - return nil + Summary summary.ResultSummaryFunc } // NettestGroups that can be run by the user @@ -62,14 +26,14 @@ var NettestGroups = map[string]NettestGroup{ Nettests: []nettests.Nettest{ websites.WebConnectivity{}, }, - Summary: func(m database.SummaryMap) (string, error) { + Summary: func(m summary.SummaryMap) (string, error) { if err := checkRequiredKeys([]string{"WebConnectivity"}, m); err != nil { log.WithError(err).Error("missing keys") return "", err } // XXX to generate this I need to create the summary map as a list - var summary WebsitesSummary + var summary summary.WebsitesSummary summary.Tested = 0 summary.Blocked = 0 for _, msmtSummaryStr := range m["WebConnectivity"] { @@ -98,7 +62,7 @@ var NettestGroups = map[string]NettestGroup{ performance.Dash{}, performance.NDT{}, }, - Summary: func(m database.SummaryMap) (string, error) { + Summary: func(m summary.SummaryMap) (string, error) { if err := checkRequiredKeys([]string{"Dash", "Ndt"}, m); err != nil { log.WithError(err).Error("missing keys") return "", err @@ -108,7 +72,7 @@ var NettestGroups = map[string]NettestGroup{ err error ndtSummary performance.NDTSummary dashSummary performance.DashSummary - summary PerformanceSummary + summary summary.PerformanceSummary ) err = json.Unmarshal([]byte(m["Dash"][0]), &dashSummary) if err != nil { @@ -137,7 +101,7 @@ var NettestGroups = map[string]NettestGroup{ middlebox.HTTPInvalidRequestLine{}, middlebox.HTTPHeaderFieldManipulation{}, }, - Summary: func(m database.SummaryMap) (string, error) { + Summary: func(m summary.SummaryMap) (string, error) { if err := checkRequiredKeys([]string{"WebConnectivity"}, m); err != nil { log.WithError(err).Error("missing keys") return "", err @@ -147,7 +111,7 @@ var NettestGroups = map[string]NettestGroup{ err error hhfmSummary middlebox.HTTPHeaderFieldManipulationSummary hirlSummary middlebox.HTTPInvalidRequestLineSummary - summary MiddleboxSummary + summary summary.MiddleboxSummary ) err = json.Unmarshal([]byte(m["HttpHeaderFieldManipulation"][0]), &hhfmSummary) if err != nil { @@ -174,7 +138,7 @@ var NettestGroups = map[string]NettestGroup{ im.Telegram{}, im.WhatsApp{}, }, - Summary: func(m database.SummaryMap) (string, error) { + Summary: func(m summary.SummaryMap) (string, error) { if err := checkRequiredKeys([]string{"Whatsapp", "Telegram", "FacebookMessenger"}, m); err != nil { log.WithError(err).Error("missing keys") return "", err @@ -184,7 +148,7 @@ var NettestGroups = map[string]NettestGroup{ waSummary im.WhatsAppSummary tgSummary im.TelegramSummary fbSummary im.FacebookMessengerSummary - summary IMSummary + summary summary.IMSummary ) err = json.Unmarshal([]byte(m["Whatsapp"][0]), &waSummary) if err != nil { diff --git a/nettests/summary/summary.go b/nettests/summary/summary.go new file mode 100644 index 0000000..a7dabfe --- /dev/null +++ b/nettests/summary/summary.go @@ -0,0 +1,44 @@ +package summary + +import "fmt" + +// ResultSummaryFunc is the function used to generate result summaries +type ResultSummaryFunc func(SummaryMap) (string, error) + +// SummaryMap contains a mapping from test name to serialized summary for it +type SummaryMap map[string][]string + +// PerformanceSummary is the result summary for a performance test +type PerformanceSummary struct { + Upload int64 + Download int64 + Ping float64 + Bitrate int64 +} + +// MiddleboxSummary is the summary for the middlebox tests +type MiddleboxSummary struct { + Detected bool +} + +// IMSummary is the summary for the im tests +type IMSummary struct { + Tested uint + Blocked uint +} + +// WebsitesSummary is the summary for the websites test +type WebsitesSummary struct { + Tested uint + Blocked uint +} + +func checkRequiredKeys(rk []string, m SummaryMap) error { + for _, key := range rk { + if _, ok := m[key]; ok { + continue + } + return fmt.Errorf("missing SummaryMap key '%s'", key) + } + return nil +} From f75c53d4044127f5f9dccecd966bfd641a0c03cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 29 Jun 2018 15:10:12 +0200 Subject: [PATCH 19/32] Fix summary package --- nettests/groups/groups.go | 8 ++++---- nettests/summary/summary.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nettests/groups/groups.go b/nettests/groups/groups.go index 06d6e67..b5e559c 100644 --- a/nettests/groups/groups.go +++ b/nettests/groups/groups.go @@ -27,7 +27,7 @@ var NettestGroups = map[string]NettestGroup{ websites.WebConnectivity{}, }, Summary: func(m summary.SummaryMap) (string, error) { - if err := checkRequiredKeys([]string{"WebConnectivity"}, m); err != nil { + if err := summary.CheckRequiredKeys([]string{"WebConnectivity"}, m); err != nil { log.WithError(err).Error("missing keys") return "", err } @@ -63,7 +63,7 @@ var NettestGroups = map[string]NettestGroup{ performance.NDT{}, }, Summary: func(m summary.SummaryMap) (string, error) { - if err := checkRequiredKeys([]string{"Dash", "Ndt"}, m); err != nil { + if err := summary.CheckRequiredKeys([]string{"Dash", "Ndt"}, m); err != nil { log.WithError(err).Error("missing keys") return "", err } @@ -102,7 +102,7 @@ var NettestGroups = map[string]NettestGroup{ middlebox.HTTPHeaderFieldManipulation{}, }, Summary: func(m summary.SummaryMap) (string, error) { - if err := checkRequiredKeys([]string{"WebConnectivity"}, m); err != nil { + if err := summary.CheckRequiredKeys([]string{"WebConnectivity"}, m); err != nil { log.WithError(err).Error("missing keys") return "", err } @@ -139,7 +139,7 @@ var NettestGroups = map[string]NettestGroup{ im.WhatsApp{}, }, Summary: func(m summary.SummaryMap) (string, error) { - if err := checkRequiredKeys([]string{"Whatsapp", "Telegram", "FacebookMessenger"}, m); err != nil { + if err := summary.CheckRequiredKeys([]string{"Whatsapp", "Telegram", "FacebookMessenger"}, m); err != nil { log.WithError(err).Error("missing keys") return "", err } diff --git a/nettests/summary/summary.go b/nettests/summary/summary.go index a7dabfe..f225927 100644 --- a/nettests/summary/summary.go +++ b/nettests/summary/summary.go @@ -33,7 +33,7 @@ type WebsitesSummary struct { Blocked uint } -func checkRequiredKeys(rk []string, m SummaryMap) error { +func CheckRequiredKeys(rk []string, m SummaryMap) error { for _, key := range rk { if _, ok := m[key]; ok { continue From b8dff783e1f9883bf090b1e69ffca86f0de957cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 29 Jun 2018 15:10:19 +0200 Subject: [PATCH 20/32] Add sentry for sending crash logs --- Gopkg.lock | 14 +++++++++++++- Gopkg.toml | 4 ++++ cmd/ooni/main.go | 3 +++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Gopkg.lock b/Gopkg.lock index f4938e4..78a20b7 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -40,12 +40,24 @@ packages = ["quantile"] revision = "3a771d992973f24aa725d07868b467d1ddfceafb" +[[projects]] + name = "github.com/certifi/gocertifi" + packages = ["."] + revision = "deb3ae2ef2610fde3330947281941c562861188b" + version = "2018.01.18" + [[projects]] name = "github.com/fatih/color" packages = ["."] revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4" version = "v1.7.0" +[[projects]] + branch = "master" + name = "github.com/getsentry/raven-go" + packages = ["."] + revision = "ed7bcb39ff10f39ab08e317ce16df282845852fa" + [[projects]] name = "github.com/golang/protobuf" packages = ["proto"] @@ -201,6 +213,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "95c3e971d63b97b0dc531f67d98401cfa9968b99aacf1eed73ce801bbaadb0cd" + inputs-digest = "5aa544c8f252bc366d223acc4156a6203287d5eb72d46c10c937810d79adc864" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 7a941ae..7bc1e5b 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -61,3 +61,7 @@ required = ["github.com/shuLhan/go-bindata/go-bindata"] [[constraint]] name = "github.com/oschwald/geoip2-golang" version = "1.2.1" + +[[constraint]] + branch = "master" + name = "github.com/getsentry/raven-go" diff --git a/cmd/ooni/main.go b/cmd/ooni/main.go index df0473e..601c6ff 100644 --- a/cmd/ooni/main.go +++ b/cmd/ooni/main.go @@ -3,6 +3,7 @@ package main import ( // commands "github.com/apex/log" + "github.com/getsentry/raven-go" _ "github.com/ooni/probe-cli/internal/cli/geoip" _ "github.com/ooni/probe-cli/internal/cli/info" @@ -18,6 +19,8 @@ import ( ) func main() { + raven.SetDSN("https://cb4510e090f64382ac371040c19b2258:8448daeebfa643c289ef398f8645980b@sentry.io/1234954") + err := app.Run() if err == nil { return From d88764aa3419857ec239b57c3519cc8d49c5e228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 29 Jun 2018 15:29:05 +0200 Subject: [PATCH 21/32] Add support for crash reporting via sentry and change the send_crash_reports key --- cmd/ooni/main.go | 16 +++++----- config/parser.go | 6 ++++ config/settings.go | 10 +++--- config/testdata/valid-config.json | 6 ++-- data/default-config.json | 4 +-- internal/crashreport/crashreport.go | 47 +++++++++++++++++++++++++++++ internal/onboard/onboard.go | 2 +- 7 files changed, 72 insertions(+), 19 deletions(-) create mode 100644 internal/crashreport/crashreport.go diff --git a/cmd/ooni/main.go b/cmd/ooni/main.go index 601c6ff..2b56450 100644 --- a/cmd/ooni/main.go +++ b/cmd/ooni/main.go @@ -3,7 +3,6 @@ package main import ( // commands "github.com/apex/log" - "github.com/getsentry/raven-go" _ "github.com/ooni/probe-cli/internal/cli/geoip" _ "github.com/ooni/probe-cli/internal/cli/info" @@ -16,14 +15,15 @@ import ( _ "github.com/ooni/probe-cli/internal/cli/version" "github.com/ooni/probe-cli/internal/cli/app" + "github.com/ooni/probe-cli/internal/crashreport" ) func main() { - raven.SetDSN("https://cb4510e090f64382ac371040c19b2258:8448daeebfa643c289ef398f8645980b@sentry.io/1234954") - - err := app.Run() - if err == nil { - return - } - log.WithError(err).Fatal("main exit") + crashreport.CapturePanicAndWait(func() { + err := app.Run() + if err == nil { + return + } + log.WithError(err).Fatal("main exit") + }, nil) } diff --git a/config/parser.go b/config/parser.go index 8ebbe0b..a312fd0 100644 --- a/config/parser.go +++ b/config/parser.go @@ -5,6 +5,8 @@ import ( "io/ioutil" "sync" + "github.com/apex/log" + "github.com/ooni/probe-cli/internal/crashreport" "github.com/ooni/probe-cli/utils" "github.com/pkg/errors" ) @@ -39,6 +41,10 @@ func ParseConfig(b []byte) (*Config, error) { if err := c.Validate(); err != nil { return nil, errors.Wrap(err, "validating") } + if c.Advanced.SendCrashReports == false { + log.Info("Disabling crash reporting.") + crashreport.Disabled = true + } return &c, nil } diff --git a/config/settings.go b/config/settings.go index 0935772..c8ddf06 100644 --- a/config/settings.go +++ b/config/settings.go @@ -110,17 +110,17 @@ type Notifications struct { // Sharing settings type Sharing struct { - IncludeIP bool `json:"include_ip"` - IncludeASN bool `json:"include_asn"` - IncludeGPS bool `json:"include_gps"` - UploadResults bool `json:"upload_results"` - SendCrashReports bool `json:"send_crash_reports"` + IncludeIP bool `json:"include_ip"` + IncludeASN bool `json:"include_asn"` + IncludeGPS bool `json:"include_gps"` + UploadResults bool `json:"upload_results"` } // Advanced settings type Advanced struct { IncludeCountry bool `json:"include_country"` UseDomainFronting bool `json:"use_domain_fronting"` + SendCrashReports bool `json:"send_crash_reports"` } // AutomatedTesting settings diff --git a/config/testdata/valid-config.json b/config/testdata/valid-config.json index 3171bdc..c6c3875 100644 --- a/config/testdata/valid-config.json +++ b/config/testdata/valid-config.json @@ -7,8 +7,7 @@ "include_ip": false, "include_asn": true, "include_gps": true, - "upload_results": true, - "send_crash_reports": true + "upload_results": true }, "notifications": { "enabled": true, @@ -58,6 +57,7 @@ }, "advanced": { "include_country": true, - "use_domain_fronting": false + "use_domain_fronting": false, + "send_crash_reports": true } } diff --git a/data/default-config.json b/data/default-config.json index 79438b1..83e6a1a 100644 --- a/data/default-config.json +++ b/data/default-config.json @@ -8,7 +8,6 @@ "include_asn": true, "include_gps": true, "upload_results": true, - "send_crash_reports": true }, "notifications": { "enabled": true, @@ -58,6 +57,7 @@ }, "advanced": { "include_country": true, - "use_domain_fronting": false + "use_domain_fronting": false, + "send_crash_reports": true }, } diff --git a/internal/crashreport/crashreport.go b/internal/crashreport/crashreport.go new file mode 100644 index 0000000..21e2c1c --- /dev/null +++ b/internal/crashreport/crashreport.go @@ -0,0 +1,47 @@ +package crashreport + +import ( + "github.com/getsentry/raven-go" +) + +// Disabled flag is used to globally disable crash reporting and make all the +// crash reporting logic a no-op. +var Disabled = false + +// CapturePanic is a wrapper around raven.CapturePanic that becomes a noop if +// `Disabled` is set to true. +func CapturePanic(f func(), tags map[string]string) (interface{}, string) { + if Disabled == true { + return nil, "" + } + return raven.CapturePanic(f, tags) +} + +// CapturePanicAndWait is a wrapper around raven.CapturePanicAndWait that becomes a noop if +// `Disabled` is set to true. +func CapturePanicAndWait(f func(), tags map[string]string) (interface{}, string) { + if Disabled == true { + return nil, "" + } + return raven.CapturePanicAndWait(f, tags) +} + +// CaptureError is a wrapper around raven.CaptureError +func CaptureError(err error, tags map[string]string) string { + if Disabled == true { + return "" + } + return raven.CaptureError(err, tags) +} + +// CaptureErrorAndWait is a wrapper around raven.CaptureErrorAndWait +func CaptureErrorAndWait(err error, tags map[string]string) string { + if Disabled == true { + return "" + } + return raven.CaptureErrorAndWait(err, tags) +} + +func init() { + raven.SetDSN("https://cb4510e090f64382ac371040c19b2258:8448daeebfa643c289ef398f8645980b@sentry.io/1234954") +} diff --git a/internal/onboard/onboard.go b/internal/onboard/onboard.go index b112f09..fa8a133 100644 --- a/internal/onboard/onboard.go +++ b/internal/onboard/onboard.go @@ -127,10 +127,10 @@ func Onboarding(config *config.Config) error { config.Lock() config.InformedConsent = true config.Advanced.IncludeCountry = settings.IncludeCountry + config.Advanced.SendCrashReports = settings.SendCrashReports config.Sharing.IncludeIP = settings.IncludeIP config.Sharing.IncludeASN = settings.IncludeNetwork config.Sharing.UploadResults = settings.UploadResults - config.Sharing.SendCrashReports = settings.SendCrashReports config.Unlock() if err := config.Write(); err != nil { From 5aa0ae6b8eb658cc2822677ffcb0e428e14f8724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 29 Jun 2018 15:41:12 +0200 Subject: [PATCH 22/32] Handle more failures events for mk --- nettests/nettests.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/nettests/nettests.go b/nettests/nettests.go index a944c44..c32de5d 100644 --- a/nettests/nettests.go +++ b/nettests/nettests.go @@ -132,6 +132,8 @@ func (c *Controller) Init(nt *mk.Nettest) error { log.Debugf("GeoIPCountryPath: %s", nt.Options.GeoIPCountryPath) nt.On("log", func(e mk.Event) { + log.Debugf(colors.Red(e.Key)) + level := e.Value.LogLevel msg := e.Value.Message @@ -189,6 +191,41 @@ func (c *Controller) Init(nt *mk.Nettest) error { log.Debugf(colors.Red(e.Key)) }) + // XXX should these be made into permanent failures? + nt.On("failure.asn_lookup", func(e mk.Event) { + log.Debugf(colors.Red(e.Key)) + log.Debugf("%v", e.Value) + }) + nt.On("failure.cc_lookup", func(e mk.Event) { + log.Debugf(colors.Red(e.Key)) + log.Debugf("%v", e.Value) + }) + nt.On("failure.ip_lookup", func(e mk.Event) { + log.Debugf(colors.Red(e.Key)) + log.Debugf("%v", e.Value) + }) + + nt.On("failure.resolver_lookup", func(e mk.Event) { + log.Debugf(colors.Red(e.Key)) + log.Debugf("%v", e.Value) + }) + + nt.On("failure.report_create", func(e mk.Event) { + log.Debugf(colors.Red(e.Key)) + log.Debugf("%v", e.Value) + }) + + nt.On("failure.report_close", func(e mk.Event) { + log.Debugf(colors.Red(e.Key)) + log.Debugf("%v", e.Value) + }) + + nt.On("failure.startup", func(e mk.Event) { + log.Debugf(colors.Red(e.Key)) + + c.msmts[e.Value.Idx].Failed(c.Ctx.DB, e.Value.Failure) + }) + nt.On("failure.measurement", func(e mk.Event) { log.Debugf(colors.Red(e.Key)) @@ -219,6 +256,8 @@ func (c *Controller) Init(nt *mk.Nettest) error { }) nt.On("measurement", func(e mk.Event) { + log.Debugf("status.end") + c.OnEntry(e.Value.Idx, e.Value.JSONStr) }) From c4c13cb2797cd11a738bddf5956af333b817df28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 29 Jun 2018 16:38:13 +0200 Subject: [PATCH 23/32] Properly compute the padding by fixing the ansi escape regexp --- internal/log/handlers/cli/cli.go | 12 +++++++----- internal/util/util.go | 9 ++++++--- internal/util/util_test.go | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 internal/util/util_test.go diff --git a/internal/log/handlers/cli/cli.go b/internal/log/handlers/cli/cli.go index d6a6b15..0eaaf68 100644 --- a/internal/log/handlers/cli/cli.go +++ b/internal/log/handlers/cli/cli.go @@ -7,7 +7,6 @@ import ( "strings" "sync" "time" - "unicode/utf8" "github.com/apex/log" "github.com/fatih/color" @@ -104,7 +103,7 @@ func logTable(w io.Writer, f log.Fields) error { } var bar *progress.Bar -var lastBarChars int64 +var lastBarChars int // TypedLog is used for handling special "typed" logs to the CLI func (h *Handler) TypedLog(t string, e *log.Entry) error { @@ -116,7 +115,9 @@ func (h *Handler) TypedLog(t string, e *log.Entry) error { } bar.Value(e.Fields.Get("percentage").(float64)) bar.Text(e.Message) - lastBarChars, err = bar.WriteTo(h.Writer) + barStr := bar.String() + fmt.Fprintf(h.Writer, "\r %s", barStr) + lastBarChars = util.EscapeAwareRuneCountInString(barStr) + 3 return err case "table": return logTable(h.Writer, e.Fields) @@ -149,10 +150,11 @@ func (h *Handler) DefaultLog(e *log.Entry) error { // We need to move the cursor back to the begging of the line and add some // padding to the end of the string to delete the previous line written to // the console. - sChars := int64(utf8.RuneCountInString(s)) + sChars := util.EscapeAwareRuneCountInString(s) fmt.Fprintf(h.Writer, - fmt.Sprintf("\r%s%s", s, strings.Repeat(" ", int(lastBarChars-sChars))), + fmt.Sprintf("\r%s%s", s, strings.Repeat(" ", lastBarChars-sChars)), ) + //fmt.Fprintf(h.Writer, "\n(%d,%d)\n", lastBarChars, sChars) fmt.Fprintln(h.Writer) bar.WriteTo(h.Writer) } else { diff --git a/internal/util/util.go b/internal/util/util.go index 5deb009..d647138 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -23,12 +23,15 @@ func Fatal(err error) { os.Exit(1) } -// Finds the control character sequences (like colors) -var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d") +// Finds the ansi escape sequences (like colors) +// Taken from: https://github.com/chalk/ansi-regex/blob/d9d806ecb45d899cf43408906a4440060c5c50e5/index.js +var ansiEscapes = regexp.MustCompile(`[\x1B\x9B][[\]()#;?]*` + + `(?:(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\\d]*)*)?\x07)` + + `|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PRZcf-ntqry=><~]))`) func EscapeAwareRuneCountInString(s string) int { n := utf8.RuneCountInString(s) - for _, sm := range ctrlFinder.FindAllString(s, -1) { + for _, sm := range ansiEscapes.FindAllString(s, -1) { n -= utf8.RuneCountInString(sm) } return n diff --git a/internal/util/util_test.go b/internal/util/util_test.go new file mode 100644 index 0000000..4a08074 --- /dev/null +++ b/internal/util/util_test.go @@ -0,0 +1,19 @@ +package util + +import ( + "testing" + + "github.com/fatih/color" + ocolor "github.com/ooni/probe-cli/internal/colors" +) + +func TestEscapeAwareRuneCountInString(t *testing.T) { + var bold = color.New(color.Bold) + var myColor = color.New(color.FgBlue) + + s := myColor.Sprintf("•ABC%s%s", bold.Sprintf("DEF"), ocolor.Red("GHI")) + count := EscapeAwareRuneCountInString(s) + if count != 10 { + t.Errorf("Count was incorrect, got: %d, want: %d.", count, 10) + } +} From d8cbfd1b90858512b5ecaa4e13734f56593fc5fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Fri, 29 Jun 2018 16:50:05 +0200 Subject: [PATCH 24/32] One color package to rule them all. --- internal/cli/run/run.go | 4 ++-- internal/colors/colors.go | 40 ------------------------------------- internal/onboard/onboard.go | 10 +++++----- internal/util/util.go | 6 +++--- internal/util/util_test.go | 3 +-- nettests/nettests.go | 34 +++++++++++++++---------------- 6 files changed, 28 insertions(+), 69 deletions(-) delete mode 100644 internal/colors/colors.go diff --git a/internal/cli/run/run.go b/internal/cli/run/run.go index 103d499..7c8a9aa 100644 --- a/internal/cli/run/run.go +++ b/internal/cli/run/run.go @@ -9,8 +9,8 @@ import ( "github.com/alecthomas/kingpin" "github.com/apex/log" + "github.com/fatih/color" "github.com/ooni/probe-cli/internal/cli/root" - "github.com/ooni/probe-cli/internal/colors" "github.com/ooni/probe-cli/internal/database" "github.com/ooni/probe-cli/nettests" "github.com/ooni/probe-cli/nettests/groups" @@ -22,7 +22,7 @@ func init() { var nettestGroupNames []string for name := range groups.NettestGroups { - nettestGroupNames = append(nettestGroupNames, colors.Blue(name)) + nettestGroupNames = append(nettestGroupNames, color.BlueString(name)) } nettestGroup := cmd.Arg("name", diff --git a/internal/colors/colors.go b/internal/colors/colors.go deleted file mode 100644 index 09b0c58..0000000 --- a/internal/colors/colors.go +++ /dev/null @@ -1,40 +0,0 @@ -package colors - -import ( - color "github.com/aybabtme/rgbterm" -) - -// Gray string. -func Gray(s string) string { - return color.FgString(s, 150, 150, 150) -} - -// Blue string. -func Blue(s string) string { - return color.FgString(s, 77, 173, 247) -} - -// Cyan string. -func Cyan(s string) string { - return color.FgString(s, 34, 184, 207) -} - -// Green string. -func Green(s string) string { - return color.FgString(s, 0, 200, 255) -} - -// Red string. -func Red(s string) string { - return color.FgString(s, 194, 37, 92) -} - -// Yellow string. -func Yellow(s string) string { - return color.FgString(s, 252, 196, 25) -} - -// Purple string. -func Purple(s string) string { - return color.FgString(s, 96, 97, 190) -} diff --git a/internal/onboard/onboard.go b/internal/onboard/onboard.go index fa8a133..f34a9ff 100644 --- a/internal/onboard/onboard.go +++ b/internal/onboard/onboard.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/apex/log" + "github.com/fatih/color" "github.com/ooni/probe-cli/config" - "github.com/ooni/probe-cli/internal/colors" "github.com/ooni/probe-cli/internal/output" survey "gopkg.in/AlecAivazis/survey.v1" ) @@ -42,10 +42,10 @@ func Onboarding(config *config.Config) error { } survey.AskOne(quiz1, &answer, nil) if answer != "true" { - output.Paragraph(colors.Red("Actually...")) + output.Paragraph(color.RedString("Actually...")) output.Paragraph("OONI Probe is not a privacy tool. Therefore, anyone monitoring your internet activity may be able to see which software you are running.") } else { - output.Paragraph(colors.Blue("Good job!")) + output.Paragraph(color.BlueString("Good job!")) } answer = "" quiz2 := &survey.Select{ @@ -55,10 +55,10 @@ func Onboarding(config *config.Config) error { } survey.AskOne(quiz2, &answer, nil) if answer != "true" { - output.Paragraph(colors.Red("Actually...")) + output.Paragraph(color.RedString("Actually...")) output.Paragraph("The network data you will collect will automatically be published to increase transparency of internet censorship (unless you opt-out in the settings).") } else { - output.Paragraph(colors.Blue("Well done!")) + output.Paragraph(color.BlueString("Well done!")) } changeDefaults := false diff --git a/internal/util/util.go b/internal/util/util.go index d647138..c47d301 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -9,17 +9,17 @@ import ( "unicode" "unicode/utf8" - "github.com/ooni/probe-cli/internal/colors" + "github.com/fatih/color" ) // Log outputs a log message. func Log(msg string, v ...interface{}) { - fmt.Printf(" %s\n", colors.Purple(fmt.Sprintf(msg, v...))) + fmt.Printf(" %s\n", color.CyanString(msg, v...)) } // Fatal error func Fatal(err error) { - fmt.Fprintf(os.Stderr, "\n %s %s\n\n", colors.Red("Error:"), err) + fmt.Fprintf(os.Stderr, "\n %s %s\n\n", color.RedString("Error:"), err) os.Exit(1) } diff --git a/internal/util/util_test.go b/internal/util/util_test.go index 4a08074..3a567c7 100644 --- a/internal/util/util_test.go +++ b/internal/util/util_test.go @@ -4,14 +4,13 @@ import ( "testing" "github.com/fatih/color" - ocolor "github.com/ooni/probe-cli/internal/colors" ) func TestEscapeAwareRuneCountInString(t *testing.T) { var bold = color.New(color.Bold) var myColor = color.New(color.FgBlue) - s := myColor.Sprintf("•ABC%s%s", bold.Sprintf("DEF"), ocolor.Red("GHI")) + s := myColor.Sprintf("•ABC%s%s", bold.Sprintf("DEF"), "\x1B[00;38;5;244m\x1B[m\x1B[00;38;5;33mGHI\x1B[0m") count := EscapeAwareRuneCountInString(s) if count != 10 { t.Errorf("Count was incorrect, got: %d, want: %d.", count, 10) diff --git a/nettests/nettests.go b/nettests/nettests.go index c32de5d..b77bb56 100644 --- a/nettests/nettests.go +++ b/nettests/nettests.go @@ -7,11 +7,11 @@ import ( "path/filepath" "github.com/apex/log" + "github.com/fatih/color" "github.com/measurement-kit/go-measurement-kit" homedir "github.com/mitchellh/go-homedir" ooni "github.com/ooni/probe-cli" "github.com/ooni/probe-cli/internal/cli/version" - "github.com/ooni/probe-cli/internal/colors" "github.com/ooni/probe-cli/internal/database" "github.com/ooni/probe-cli/internal/output" "github.com/ooni/probe-cli/utils" @@ -132,7 +132,7 @@ func (c *Controller) Init(nt *mk.Nettest) error { log.Debugf("GeoIPCountryPath: %s", nt.Options.GeoIPCountryPath) nt.On("log", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) level := e.Value.LogLevel msg := e.Value.Message @@ -163,7 +163,7 @@ func (c *Controller) Init(nt *mk.Nettest) error { }) nt.On("status.geoip_lookup", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) msmtTemplate.ASN = e.Value.ProbeASN msmtTemplate.IP = e.Value.ProbeIP @@ -171,7 +171,7 @@ func (c *Controller) Init(nt *mk.Nettest) error { }) nt.On("status.measurement_start", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) idx := e.Value.Idx msmt, err := database.CreateMeasurement(c.Ctx.DB, msmtTemplate, e.Value.Input) @@ -183,64 +183,64 @@ func (c *Controller) Init(nt *mk.Nettest) error { }) nt.On("status.progress", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) c.OnProgress(e.Value.Percentage, e.Value.Message) }) nt.On("status.update.*", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) }) // XXX should these be made into permanent failures? nt.On("failure.asn_lookup", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) log.Debugf("%v", e.Value) }) nt.On("failure.cc_lookup", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) log.Debugf("%v", e.Value) }) nt.On("failure.ip_lookup", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) log.Debugf("%v", e.Value) }) nt.On("failure.resolver_lookup", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) log.Debugf("%v", e.Value) }) nt.On("failure.report_create", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) log.Debugf("%v", e.Value) }) nt.On("failure.report_close", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) log.Debugf("%v", e.Value) }) nt.On("failure.startup", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) c.msmts[e.Value.Idx].Failed(c.Ctx.DB, e.Value.Failure) }) nt.On("failure.measurement", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) c.msmts[e.Value.Idx].Failed(c.Ctx.DB, e.Value.Failure) }) nt.On("failure.measurement_submission", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) failure := e.Value.Failure c.msmts[e.Value.Idx].UploadFailed(c.Ctx.DB, failure) }) nt.On("status.measurement_submission", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) if err := c.msmts[e.Value.Idx].UploadSucceeded(c.Ctx.DB); err != nil { log.WithError(err).Error("failed to mark msmt as uploaded") @@ -248,7 +248,7 @@ func (c *Controller) Init(nt *mk.Nettest) error { }) nt.On("status.measurement_done", func(e mk.Event) { - log.Debugf(colors.Red(e.Key)) + log.Debugf(color.RedString(e.Key)) if err := c.msmts[e.Value.Idx].Done(c.Ctx.DB); err != nil { log.WithError(err).Error("failed to mark msmt as done") From b2faf3fd34090671b67969fe798e04b2419e0110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Mon, 9 Jul 2018 19:17:59 +0200 Subject: [PATCH 25/32] Make some improvements to the homedir ghetto hax This avoid breakage when inside a dev environment --- nettests/nettests.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nettests/nettests.go b/nettests/nettests.go index b77bb56..50dfb35 100644 --- a/nettests/nettests.go +++ b/nettests/nettests.go @@ -9,7 +9,6 @@ import ( "github.com/apex/log" "github.com/fatih/color" "github.com/measurement-kit/go-measurement-kit" - homedir "github.com/mitchellh/go-homedir" ooni "github.com/ooni/probe-cli" "github.com/ooni/probe-cli/internal/cli/version" "github.com/ooni/probe-cli/internal/database" @@ -76,11 +75,13 @@ func (c *Controller) Init(nt *mk.Nettest) error { caBundlePath := getCaBundlePath() msmtPath := c.msmtPath - userHome, err := homedir.Dir() + userHome, err := utils.GetOONIHome() if err != nil { log.WithError(err).Error("failed to figure out the homedir") return err } + // Get the parent of it + userHome = filepath.Dir(userHome) relPath, err := filepath.Rel(userHome, caBundlePath) if err != nil { From 83ed5fa7673be7020191c8122f4850b60fc2b24f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Wed, 11 Jul 2018 18:06:13 +0200 Subject: [PATCH 26/32] Fix bug in version info --- Gopkg.lock | 66 +-------------------------------------- internal/cli/root/root.go | 2 +- 2 files changed, 2 insertions(+), 66 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 78a20b7..3114935 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -28,18 +28,6 @@ revision = "0296d6eb16bb28f8a0c55668affcf4876dc269be" version = "v1.0.0" -[[projects]] - branch = "master" - name = "github.com/aybabtme/rgbterm" - packages = ["."] - revision = "cc83f3b3ce5911279513a46d6d3316d67bedaa54" - -[[projects]] - branch = "master" - name = "github.com/beorn7/perks" - packages = ["quantile"] - revision = "3a771d992973f24aa725d07868b467d1ddfceafb" - [[projects]] name = "github.com/certifi/gocertifi" packages = ["."] @@ -58,12 +46,6 @@ packages = ["."] revision = "ed7bcb39ff10f39ab08e317ce16df282845852fa" -[[projects]] - name = "github.com/golang/protobuf" - packages = ["proto"] - revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" - version = "v1.1.0" - [[projects]] branch = "master" name = "github.com/jmoiron/sqlx" @@ -91,12 +73,6 @@ revision = "323a32be5a2421b8c7087225079c6c900ec397cd" version = "v1.7.0" -[[projects]] - name = "github.com/matttproud/golang_protobuf_extensions" - packages = ["pbutil"] - revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" - version = "v1.0.1" - [[projects]] branch = "master" name = "github.com/measurement-kit/go-measurement-kit" @@ -109,12 +85,6 @@ packages = ["."] revision = "9520e82c474b0a04dd04f8a40959027271bab992" -[[projects]] - branch = "master" - name = "github.com/mitchellh/go-homedir" - packages = ["."] - revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66" - [[projects]] name = "github.com/oschwald/geoip2-golang" packages = ["."] @@ -133,40 +103,6 @@ revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" -[[projects]] - name = "github.com/prometheus/client_golang" - packages = ["prometheus"] - revision = "c5b7fccd204277076155f10851dad72b76a49317" - version = "v0.8.0" - -[[projects]] - branch = "master" - name = "github.com/prometheus/client_model" - packages = ["go"] - revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c" - -[[projects]] - branch = "master" - name = "github.com/prometheus/common" - packages = [ - "expfmt", - "internal/bitbucket.org/ww/goautoneg", - "model", - "version" - ] - revision = "7600349dcfe1abd18d72d3a1770870d9800a7801" - -[[projects]] - branch = "master" - name = "github.com/prometheus/procfs" - packages = [ - ".", - "internal/util", - "nfs", - "xfs" - ] - revision = "fe93d378a6b03758a2c1b65e86cf630bf78681c0" - [[projects]] branch = "master" name = "github.com/rubenv/sql-migrate" @@ -213,6 +149,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "5aa544c8f252bc366d223acc4156a6203287d5eb72d46c10c937810d79adc864" + inputs-digest = "b2f5c39222a1fb405e3f48d2ae3b4758757fe708e12dbd23743c19135e225579" solver-name = "gps-cdcl" solver-version = 1 diff --git a/internal/cli/root/root.go b/internal/cli/root/root.go index 5777f10..ef71c14 100644 --- a/internal/cli/root/root.go +++ b/internal/cli/root/root.go @@ -7,7 +7,7 @@ import ( "github.com/ooni/probe-cli/internal/log/handlers/batch" "github.com/ooni/probe-cli/internal/log/handlers/cli" "github.com/ooni/probe-cli/utils" - "github.com/prometheus/common/version" + "github.com/ooni/probe-cli/internal/cli/version/version.go" ) // Cmd is the root command From f1018dce476f4cd7a86ed51c5cc98a190ed52e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Wed, 11 Jul 2018 18:06:27 +0200 Subject: [PATCH 27/32] Add LICENSE.md --- LICENSE.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..4cb6d13 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,26 @@ +Copyright 2018 Open Observatory of Network Interference (OONI), The Tor Project + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 45aea93e6bed5ff844cf53d724a7adca2f04239c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Sat, 14 Jul 2018 16:58:08 +0200 Subject: [PATCH 28/32] Fix default config and version --- data/default-config.json | 4 ++-- internal/bindata/bindata.go | 40 ++++++++++++++++----------------- internal/cli/app/app.go | 4 ++-- internal/cli/root/root.go | 3 +-- internal/cli/version/version.go | 4 ++-- nettests/nettests.go | 3 +-- ooni.go | 2 ++ 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/data/default-config.json b/data/default-config.json index 83e6a1a..c6c3875 100644 --- a/data/default-config.json +++ b/data/default-config.json @@ -7,7 +7,7 @@ "include_ip": false, "include_asn": true, "include_gps": true, - "upload_results": true, + "upload_results": true }, "notifications": { "enabled": true, @@ -59,5 +59,5 @@ "include_country": true, "use_domain_fronting": false, "send_crash_reports": true - }, + } } diff --git a/internal/bindata/bindata.go b/internal/bindata/bindata.go index 3f4d952..b257289 100644 --- a/internal/bindata/bindata.go +++ b/internal/bindata/bindata.go @@ -80,26 +80,26 @@ func (fi bindataFileInfo) Sys() interface{} { } var _bindataDataDefaultconfigjson = []byte( - "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x94\x41\x6f\xdb\x3c\x0c\x86\xef\xfd\x15\x82\xce\x75\x53\xe0\xbb\xe5" + - "\xf8\xdd\x76\xd8\x3a\x60\xbb\x15\x85\xa0\x48\xb4\x4d\x4c\x26\x35\x91\x4e\x16\x0c\xfd\xef\x83\xdc\x34\x56\xda\xae" + - "\xeb\xd1\xef\x4b\xd1\xe4\x43\x4a\xbf\xaf\x8c\xb1\xce\x6e\x8d\xfd\x3e\xa2\x18\x14\x73\xe4\xb9\x98\xbb\xbb\x2f\x9f" + - "\xcc\xd7\xc2\x3b\x30\x81\xa9\xc7\xc1\xf4\x98\xe0\xc6\x7c\x03\x30\xa3\x6a\x96\xed\x66\xc3\x4c\x78\x83\xbc\x19\x21" + - "\xe5\x4d\xae\xb1\x5d\x48\x68\x7a\x2e\xa6\x4a\xf6\x7a\x49\xbd\x87\x22\xc8\x64\xb7\xe6\xf6\x49\x40\xea\xb9\x4c\x10" + - "\x5d\x60\x12\x20\xb5\x5b\xd3\xfb\x24\xb0\xb8\x7e\x56\x76\x73\x8e\x5e\xc1\x6e\x8d\x96\xf9\x49\x96\xd1\x17\xa4\xc1" + - "\x6e\x4d\xad\xd7\x18\x8b\x14\xd2\x1c\xc1\x61\x6e\x8f\x37\x86\x17\x6a\x12\x34\xc6\x90\xe5\xd2\x98\x73\x62\x1f\x5d" + - "\x01\x99\x93\xbe\xf0\x04\x28\xba\x50\xbc\x8c\xae\x40\xe6\x72\xf6\xaf\x8c\x79\x5c\x2a\x23\x56\xec\x31\x78\x45\x26" + - "\x59\xeb\x03\xf2\xbb\x04\xf1\x32\xdb\x12\x7b\x74\x4c\x4e\x41\xd4\x05\x9e\x72\x02\x7d\x82\xf3\x66\x18\xc1\x41\x9e" + - "\xfb\x3b\xff\xb1\x22\x9a\xbc\x42\x5c\xb2\x5c\x50\x59\xff\xda\x22\x39\xa9\x4b\x78\x4d\x77\xbf\xc8\xc6\xd8\x03\xec" + - "\xba\xc0\x44\x10\x14\xf7\xa8\x47\x7b\xfd\xec\xf4\x3e\xc0\x8e\xf9\x47\x37\x81\x08\xd0\x00\x65\xf5\x0e\xa3\x57\xf1" + - "\x39\xaf\x8a\x42\x82\xa1\xf8\x69\x55\xa2\x97\x71\xfd\xa2\xa8\xeb\x47\xdd\x9e\x0e\x69\xef\x13\xc6\xae\xc0\xcf\x19" + - "\x44\xbb\x84\x04\x2f\x42\x46\xf0\x11\x4a\xd7\x23\xa4\xd8\x4d\x9e\x30\xcf\x69\xa1\x6c\x97\xb0\x87\x53\x73\x13\x93" + - "\x8e\xe9\xe8\x7c\x4a\x7c\xf0\x14\xea\xda\xd8\xff\x6e\x6f\x3f\xff\x6f\xcf\xc4\x16\xda\x02\x5a\x61\x35\x33\x3a\xc0" + - "\x4e\x50\x61\x55\x1a\x56\xc1\x2b\x0c\x5c\x70\x71\xef\x1f\x16\xfb\xf1\xbc\x49\xa2\x9e\xd4\x55\x36\x7e\x68\x07\xf0" + - "\x0e\xec\xf7\xa1\xbe\x85\xb5\x05\x7b\x92\x2e\xeb\xc8\x50\xea\x4d\x3a\x35\xfd\x91\x0a\xea\x20\x9e\x53\xb5\xd3\x71" + - "\x02\x65\x0f\xa5\xa2\xab\xdb\x65\xdf\xf0\x5c\xdd\xfe\xd7\x01\x75\xd0\x7f\x3d\xdd\x98\x97\xc7\x2f\xda\x98\x30\xc6" + - "\x04\x3b\xfe\xf5\xc1\x26\xfe\xbd\x40\x1f\x5c\xa1\x33\xcf\xf5\x6a\xc5\x7d\xa5\x19\x5f\xbf\x33\x81\x67\xd2\x72\x7c" + - "\xf1\x72\x08\xb8\xc8\x93\x47\x72\x7d\x61\x3a\xdd\xc5\xe6\xb6\x3e\x5e\xfd\x09\x00\x00\xff\xff\xdf\xd2\xaa\xf7\x5f" + - "\x05\x00\x00") + "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x94\x41\x6f\x1b\x2d\x10\x86\xef\xfe\x15\x88\x73\x36\x8e\xf4\xdd\x7c" + + "\xfc\x6e\x3d\xb4\xa9\xd4\xde\xa2\x08\x61\x98\xdd\x1d\x95\x9d\xa1\x30\x6b\xd7\xaa\xf2\xdf\x2b\x88\xed\x65\x93\x34" + + "\xcd\x91\xf7\x1d\x06\xe6\x99\x81\xdf\x1b\xa5\xb4\xd1\x3b\xa5\xbf\x8f\x98\x15\x66\x75\xe2\x39\xa9\xfb\xfb\x2f\x9f" + + "\xd4\xd7\xc4\x7b\x50\x8e\xa9\xc7\x41\xf5\x18\xe0\x56\x7d\x03\x50\xa3\x48\xcc\xbb\xed\x96\x99\xf0\x16\x79\x3b\x42" + + "\x88\xdb\x58\x62\x3b\x17\x50\xf5\x9c\x54\x91\xf4\x4d\x4d\x7d\x80\x94\x91\x49\xef\xd4\xdd\xb3\x80\xd4\x73\x9a\xc0" + + "\x1b\xc7\x94\x81\x44\xef\x54\x6f\x43\x86\xea\xda\x59\xd8\xcc\xd1\x5b\x01\xbd\x53\x92\xe6\x67\x39\x8f\x36\x21\x0d" + + "\x7a\xa7\xca\x7d\x95\xd2\x48\x2e\xcc\x1e\x0c\xc6\x76\x7b\x63\xd8\x4c\x4d\x82\xc6\x18\x62\x5e\x1b\x73\x0c\x6c\xbd" + + "\x49\x90\xe7\x20\x17\x6f\xa3\xd4\x53\x3d\x99\x58\xb0\x47\x67\x05\x99\xf2\x72\x3e\x90\xdd\x07\xf0\xeb\x4c\x35\xf6" + + "\x64\x98\x8c\x40\x16\xe3\x78\x8a\x01\xe4\xb9\xf8\x37\xc3\x08\x8e\xf9\x72\xff\xeb\x89\x05\xc1\x64\x05\x7c\xcd\xb2" + + "\xaa\x7a\x39\xb5\x2d\xf9\xac\xd6\xf0\x92\xee\xa1\xca\x4a\xe9\x23\xec\x3b\xc7\x44\xe0\x04\x0f\x28\x27\x7d\x73\x71" + + "\x7a\xeb\x60\xcf\xfc\xa3\x9b\x20\x67\xa0\x01\xd2\xe2\x1d\x47\x2b\xd9\xc6\xb8\x28\x02\x01\x86\x64\xa7\x45\xf1\x36" + + "\x8f\xcb\x8a\xbc\x2c\x8b\x32\x1d\x1d\xd2\xc1\x06\xf4\x5d\x82\x9f\x33\x64\xe9\x02\x12\xbc\x08\x19\xc1\x7a\x48\x5d" + + "\x8f\x10\x7c\x37\x59\xc2\x38\x87\x4a\x59\xd7\xb0\xc7\x73\x71\x13\x93\x8c\xe1\x64\x6c\x08\x7c\xb4\xe4\xca\x58\xe8" + + "\xff\xee\xee\x3e\xff\xaf\xaf\xc4\x2a\xed\x0c\x52\x60\x35\x3d\x3a\xc2\x3e\xa3\xc0\xa2\x34\xac\x9c\x15\x18\x38\x61" + + "\x75\x1f\x1e\xab\xfd\x74\x9d\x94\x2c\x96\xc4\x14\x36\x76\x68\x1b\xf0\x0e\xec\xf7\xa1\xbe\x85\xb5\x05\x7b\x96\xd6" + + "\xf7\x88\x90\xca\x4b\x39\x17\xfd\x91\x1b\x94\x46\x5c\x52\xb5\xdd\x31\x19\xd2\x01\x52\x41\x57\xa6\x4b\xbf\xe1\x99" + + "\xc8\x49\x5e\x07\x94\x46\xff\x75\x77\x63\xae\xb7\xaf\xca\x98\xd0\xfb\x00\x7b\xfe\xf5\xc1\x22\xfe\x3d\x40\x1f\x1c" + + "\xa1\x2b\xcf\xe5\x69\xf9\x43\xa1\xe9\x5f\xff\x23\x8e\x67\x92\x74\x7a\xf1\x33\x64\x30\x9e\x27\x8b\x64\xfa\xc4\x74" + + "\x7e\x8b\xed\xd3\xcb\x40\xde\xb8\x54\x38\x24\x28\x08\xda\xff\x63\xf3\xb4\xf9\x13\x00\x00\xff\xff\xe0\xc5\x07\x53" + + "\x5e\x05\x00\x00") func bindataDataDefaultconfigjsonBytes() ([]byte, error) { return bindataRead( diff --git a/internal/cli/app/app.go b/internal/cli/app/app.go index f84bfcc..539874c 100644 --- a/internal/cli/app/app.go +++ b/internal/cli/app/app.go @@ -3,13 +3,13 @@ package app import ( "os" + ooni "github.com/ooni/probe-cli" "github.com/ooni/probe-cli/internal/cli/root" - "github.com/ooni/probe-cli/internal/cli/version" ) // Run the app. This is the main app entry point func Run() error { - root.Cmd.Version(version.Version) + root.Cmd.Version(ooni.Version) _, err := root.Cmd.Parse(os.Args[1:]) return err } diff --git a/internal/cli/root/root.go b/internal/cli/root/root.go index ef71c14..c7749da 100644 --- a/internal/cli/root/root.go +++ b/internal/cli/root/root.go @@ -7,7 +7,6 @@ import ( "github.com/ooni/probe-cli/internal/log/handlers/batch" "github.com/ooni/probe-cli/internal/log/handlers/cli" "github.com/ooni/probe-cli/utils" - "github.com/ooni/probe-cli/internal/cli/version/version.go" ) // Cmd is the root command @@ -33,7 +32,7 @@ func init() { } if *isVerbose { log.SetLevel(log.DebugLevel) - log.Debugf("ooni version %s", version.Version) + log.Debugf("ooni version %s", ooni.Version) } Init = func() (*ooni.Context, error) { diff --git a/internal/cli/version/version.go b/internal/cli/version/version.go index 3deceda..5e55be1 100644 --- a/internal/cli/version/version.go +++ b/internal/cli/version/version.go @@ -4,15 +4,15 @@ import ( "fmt" "github.com/alecthomas/kingpin" + ooni "github.com/ooni/probe-cli" "github.com/ooni/probe-cli/internal/cli/root" ) -const Version = "3.0.0-dev.0" func init() { cmd := root.Command("version", "Show version.") cmd.Action(func(_ *kingpin.ParseContext) error { - fmt.Println(Version) + fmt.Println(ooni.Version) return nil }) } diff --git a/nettests/nettests.go b/nettests/nettests.go index 50dfb35..0243515 100644 --- a/nettests/nettests.go +++ b/nettests/nettests.go @@ -10,7 +10,6 @@ import ( "github.com/fatih/color" "github.com/measurement-kit/go-measurement-kit" ooni "github.com/ooni/probe-cli" - "github.com/ooni/probe-cli/internal/cli/version" "github.com/ooni/probe-cli/internal/database" "github.com/ooni/probe-cli/internal/output" "github.com/ooni/probe-cli/utils" @@ -122,7 +121,7 @@ func (c *Controller) Init(nt *mk.Nettest) error { DisableReportFile: false, DisableCollector: false, SoftwareName: "ooniprobe", - SoftwareVersion: version.Version, + SoftwareVersion: ooni.Version, OutputPath: msmtPath, GeoIPCountryPath: geoIPCountryPath, diff --git a/ooni.go b/ooni.go index c7a7125..b17c57c 100644 --- a/ooni.go +++ b/ooni.go @@ -16,6 +16,8 @@ import ( "github.com/pkg/errors" ) +const Version = "3.0.0-dev.0" + // Context for OONI Probe type Context struct { Config *config.Config From 69ec294ad44e707316174e2cbe94644d9b91aa67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Sat, 14 Jul 2018 17:19:06 +0200 Subject: [PATCH 29/32] Go for a much more minimal progress update --- internal/log/handlers/cli/cli.go | 32 +++++--------------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/internal/log/handlers/cli/cli.go b/internal/log/handlers/cli/cli.go index 0eaaf68..5335c51 100644 --- a/internal/log/handlers/cli/cli.go +++ b/internal/log/handlers/cli/cli.go @@ -11,7 +11,6 @@ import ( "github.com/apex/log" "github.com/fatih/color" colorable "github.com/mattn/go-colorable" - "github.com/ooni/probe-cli/internal/log/handlers/cli/progress" "github.com/ooni/probe-cli/internal/util" ) @@ -102,22 +101,14 @@ func logTable(w io.Writer, f log.Fields) error { return nil } -var bar *progress.Bar -var lastBarChars int - // TypedLog is used for handling special "typed" logs to the CLI func (h *Handler) TypedLog(t string, e *log.Entry) error { switch t { case "progress": var err error - if bar == nil { - bar = progress.New(1.0) - } - bar.Value(e.Fields.Get("percentage").(float64)) - bar.Text(e.Message) - barStr := bar.String() - fmt.Fprintf(h.Writer, "\r %s", barStr) - lastBarChars = util.EscapeAwareRuneCountInString(barStr) + 3 + s := fmt.Sprintf("%.2f%%: %s", e.Fields.Get("percentage").(float64), e.Message) + fmt.Fprintf(h.Writer, s) + fmt.Fprintln(h.Writer) return err case "table": return logTable(h.Writer, e.Fields) @@ -146,21 +137,8 @@ func (h *Handler) DefaultLog(e *log.Entry) error { s += fmt.Sprintf(" %s=%s", color.Sprint(name), e.Fields.Get(name)) } - if bar != nil { - // We need to move the cursor back to the begging of the line and add some - // padding to the end of the string to delete the previous line written to - // the console. - sChars := util.EscapeAwareRuneCountInString(s) - fmt.Fprintf(h.Writer, - fmt.Sprintf("\r%s%s", s, strings.Repeat(" ", lastBarChars-sChars)), - ) - //fmt.Fprintf(h.Writer, "\n(%d,%d)\n", lastBarChars, sChars) - fmt.Fprintln(h.Writer) - bar.WriteTo(h.Writer) - } else { - fmt.Fprintf(h.Writer, s) - fmt.Fprintln(h.Writer) - } + fmt.Fprintf(h.Writer, s) + fmt.Fprintln(h.Writer) return nil } From 8fb5e397d713ef401bce2c05010349e64017fda8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Mon, 16 Jul 2018 13:15:01 +0200 Subject: [PATCH 30/32] Cleanup the output of the progress handler --- internal/log/handlers/cli/cli.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/log/handlers/cli/cli.go b/internal/log/handlers/cli/cli.go index 5335c51..ca716d5 100644 --- a/internal/log/handlers/cli/cli.go +++ b/internal/log/handlers/cli/cli.go @@ -106,7 +106,7 @@ func (h *Handler) TypedLog(t string, e *log.Entry) error { switch t { case "progress": var err error - s := fmt.Sprintf("%.2f%%: %s", e.Fields.Get("percentage").(float64), e.Message) + s := fmt.Sprintf("%.2f%%: %-25s", e.Fields.Get("percentage").(float64)*100, e.Message) fmt.Fprintf(h.Writer, s) fmt.Fprintln(h.Writer) return err From f343a2a1bd6bd4934592a866d4fea295f3a79a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Wed, 18 Jul 2018 18:42:54 +0200 Subject: [PATCH 31/32] Fix bug in checks for keys in middlebox tests --- nettests/groups/groups.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nettests/groups/groups.go b/nettests/groups/groups.go index b5e559c..8b68dac 100644 --- a/nettests/groups/groups.go +++ b/nettests/groups/groups.go @@ -102,7 +102,7 @@ var NettestGroups = map[string]NettestGroup{ middlebox.HTTPHeaderFieldManipulation{}, }, Summary: func(m summary.SummaryMap) (string, error) { - if err := summary.CheckRequiredKeys([]string{"WebConnectivity"}, m); err != nil { + if err := summary.CheckRequiredKeys([]string{"HttpInvalidRequestLine", "HttpHeaderFieldManipulation"}, m); err != nil { log.WithError(err).Error("missing keys") return "", err } From 247d128b196155ce6c5faba76d1fce6756bbcfaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Mon, 30 Jul 2018 18:51:44 +0200 Subject: [PATCH 32/32] Add _is_beta flag to config file --- config/parser.go | 1 + data/default-config.json | 1 + internal/bindata/bindata.go | 40 ++++++++++++++++----------------- internal/cli/onboard/onboard.go | 15 +++++++++++++ 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/config/parser.go b/config/parser.go index a312fd0..5b5e15e 100644 --- a/config/parser.go +++ b/config/parser.go @@ -55,6 +55,7 @@ type Config struct { Comment string `json:"_"` Version int64 `json:"_version"` InformedConsent bool `json:"_informed_consent"` + IsBeta bool `json:"_is_beta"` // This is a boolean flag used to indicate this installation of OONI Probe was a beta install. These installations will have their data deleted across releases. AutoUpdate bool `json:"auto_update"` Sharing Sharing `json:"sharing"` diff --git a/data/default-config.json b/data/default-config.json index c6c3875..cd72e53 100644 --- a/data/default-config.json +++ b/data/default-config.json @@ -2,6 +2,7 @@ "_": "This is your OONI Probe config file. See https://ooni.io/help/probe-cli for help", "_version": 0, "_informed_consent": false, + "_is_beta": true, "auto_update": true, "sharing": { "include_ip": false, diff --git a/internal/bindata/bindata.go b/internal/bindata/bindata.go index b257289..812dc69 100644 --- a/internal/bindata/bindata.go +++ b/internal/bindata/bindata.go @@ -80,26 +80,26 @@ func (fi bindataFileInfo) Sys() interface{} { } var _bindataDataDefaultconfigjson = []byte( - "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x94\x41\x6f\x1b\x2d\x10\x86\xef\xfe\x15\x88\x73\x36\x8e\xf4\xdd\x7c" + - "\xfc\x6e\x3d\xb4\xa9\xd4\xde\xa2\x08\x61\x98\xdd\x1d\x95\x9d\xa1\x30\x6b\xd7\xaa\xf2\xdf\x2b\x88\xed\x65\x93\x34" + - "\xcd\x91\xf7\x1d\x06\xe6\x99\x81\xdf\x1b\xa5\xb4\xd1\x3b\xa5\xbf\x8f\x98\x15\x66\x75\xe2\x39\xa9\xfb\xfb\x2f\x9f" + - "\xd4\xd7\xc4\x7b\x50\x8e\xa9\xc7\x41\xf5\x18\xe0\x56\x7d\x03\x50\xa3\x48\xcc\xbb\xed\x96\x99\xf0\x16\x79\x3b\x42" + - "\x88\xdb\x58\x62\x3b\x17\x50\xf5\x9c\x54\x91\xf4\x4d\x4d\x7d\x80\x94\x91\x49\xef\xd4\xdd\xb3\x80\xd4\x73\x9a\xc0" + - "\x1b\xc7\x94\x81\x44\xef\x54\x6f\x43\x86\xea\xda\x59\xd8\xcc\xd1\x5b\x01\xbd\x53\x92\xe6\x67\x39\x8f\x36\x21\x0d" + - "\x7a\xa7\xca\x7d\x95\xd2\x48\x2e\xcc\x1e\x0c\xc6\x76\x7b\x63\xd8\x4c\x4d\x82\xc6\x18\x62\x5e\x1b\x73\x0c\x6c\xbd" + - "\x49\x90\xe7\x20\x17\x6f\xa3\xd4\x53\x3d\x99\x58\xb0\x47\x67\x05\x99\xf2\x72\x3e\x90\xdd\x07\xf0\xeb\x4c\x35\xf6" + - "\x64\x98\x8c\x40\x16\xe3\x78\x8a\x01\xe4\xb9\xf8\x37\xc3\x08\x8e\xf9\x72\xff\xeb\x89\x05\xc1\x64\x05\x7c\xcd\xb2" + - "\xaa\x7a\x39\xb5\x2d\xf9\xac\xd6\xf0\x92\xee\xa1\xca\x4a\xe9\x23\xec\x3b\xc7\x44\xe0\x04\x0f\x28\x27\x7d\x73\x71" + - "\x7a\xeb\x60\xcf\xfc\xa3\x9b\x20\x67\xa0\x01\xd2\xe2\x1d\x47\x2b\xd9\xc6\xb8\x28\x02\x01\x86\x64\xa7\x45\xf1\x36" + - "\x8f\xcb\x8a\xbc\x2c\x8b\x32\x1d\x1d\xd2\xc1\x06\xf4\x5d\x82\x9f\x33\x64\xe9\x02\x12\xbc\x08\x19\xc1\x7a\x48\x5d" + - "\x8f\x10\x7c\x37\x59\xc2\x38\x87\x4a\x59\xd7\xb0\xc7\x73\x71\x13\x93\x8c\xe1\x64\x6c\x08\x7c\xb4\xe4\xca\x58\xe8" + - "\xff\xee\xee\x3e\xff\xaf\xaf\xc4\x2a\xed\x0c\x52\x60\x35\x3d\x3a\xc2\x3e\xa3\xc0\xa2\x34\xac\x9c\x15\x18\x38\x61" + - "\x75\x1f\x1e\xab\xfd\x74\x9d\x94\x2c\x96\xc4\x14\x36\x76\x68\x1b\xf0\x0e\xec\xf7\xa1\xbe\x85\xb5\x05\x7b\x96\xd6" + - "\xf7\x88\x90\xca\x4b\x39\x17\xfd\x91\x1b\x94\x46\x5c\x52\xb5\xdd\x31\x19\xd2\x01\x52\x41\x57\xa6\x4b\xbf\xe1\x99" + - "\xc8\x49\x5e\x07\x94\x46\xff\x75\x77\x63\xae\xb7\xaf\xca\x98\xd0\xfb\x00\x7b\xfe\xf5\xc1\x22\xfe\x3d\x40\x1f\x1c" + - "\xa1\x2b\xcf\xe5\x69\xf9\x43\xa1\xe9\x5f\xff\x23\x8e\x67\x92\x74\x7a\xf1\x33\x64\x30\x9e\x27\x8b\x64\xfa\xc4\x74" + - "\x7e\x8b\xed\xd3\xcb\x40\xde\xb8\x54\x38\x24\x28\x08\xda\xff\x63\xf3\xb4\xf9\x13\x00\x00\xff\xff\xe0\xc5\x07\x53" + - "\x5e\x05\x00\x00") + "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x94\x41\x6f\xdb\x3c\x0c\x86\xef\xf9\x15\x82\xce\x75\x53\xe0\xbb\xe5" + + "\xf8\xdd\x76\xd8\x3a\x60\xbb\x15\x85\x20\x5b\xb4\x4d\x4c\x26\x35\x91\x4e\x16\x0c\xfd\xef\x83\xdc\x24\x56\xda\xae" + + "\xeb\xd1\xef\x4b\x53\xe2\x43\x52\xbf\x37\xc6\x58\x67\x77\xc6\x7e\x1f\x51\x0c\x8a\x39\xf2\x9c\xcd\xfd\xfd\x97\x4f" + + "\xe6\x6b\xe6\x16\x4c\xc7\xd4\xe3\x60\x7a\x8c\x70\x6b\xbe\x01\x98\x51\x35\xc9\x6e\xbb\x65\x26\xbc\x45\xde\x8e\x10" + + "\xd3\x36\x95\xd8\xa6\x8b\x68\x7a\xce\xa6\x48\xf6\x66\x49\xbd\x87\x2c\xc8\x64\x77\xe6\xee\x59\x40\xea\x39\x4f\x10" + + "\x5c\xc7\x24\x40\x6a\x77\xa6\xf7\x51\xe0\xe4\x8a\x6b\x41\xbd\xdd\x19\xcd\xf3\xb3\xe6\x67\x65\x37\xa7\xe0\x15\x6a" + + "\x59\x46\x9f\x91\x06\xbb\x33\xa5\x06\x63\x2c\x52\x17\xe7\x00\x0e\x53\x9d\xb2\x32\xbc\x50\x95\xa0\x32\x86\x24\xd7" + + "\xc6\x9c\x22\xfb\xe0\x32\xc8\x1c\xf5\xec\x6d\x8c\x79\x5a\x4e\x26\x56\xec\xb1\xf3\x8a\x4c\xb2\x9e\x0f\xe4\xdb\x08" + + "\xe1\x3a\xd3\x12\x7b\x74\x4c\x4e\x41\xd4\x75\x3c\xa5\x08\xfa\x0c\xe4\xcd\x30\x82\x83\x9c\xef\x7f\x39\xb1\x20\x98" + + "\xbc\x42\x58\xb2\x5c\x55\xbd\x9e\x5a\x97\x7c\x52\x97\xf0\x92\xee\x61\x91\x8d\xb1\x07\x68\x9b\x8e\x89\xa0\x53\xdc" + + "\xa3\x1e\xed\xcd\xd9\xe9\x7d\x07\x2d\xf3\x8f\x66\x02\x11\xa0\x01\xf2\xea\x1d\x46\xaf\xe2\x53\x5a\x15\x85\x08\x43" + + "\xf6\xd3\xaa\x04\x2f\xe3\xfa\x45\x41\xd7\x8f\x32\x31\x0d\xd2\xde\x47\x0c\x4d\x86\x9f\x33\x88\x36\x11\x09\x5e\x84" + + "\x8c\xe0\x03\xe4\xa6\x47\x88\xa1\x99\x3c\x61\x9a\xe3\x42\xd9\x2e\x61\x8f\xa7\xe2\x26\x26\x1d\xe3\xd1\xf9\x18\xf9" + + "\xe0\xa9\x2b\x63\x61\xff\xbb\xbb\xfb\xfc\xbf\xbd\x10\x5b\x68\x0b\x68\x81\x55\xf5\xe8\x00\xad\xa0\xc2\xaa\x54\xac" + + "\x3a\xaf\x30\x70\xc6\xc5\x7d\x78\x5c\xec\xa7\xcb\xa4\x88\x7a\x52\x57\xd8\xf8\xa1\x6e\xc0\x3b\xb0\xdf\x87\xfa\x16" + + "\xd6\x1a\xec\x49\xba\xbe\x47\x82\x5c\xb6\xe7\x54\xf4\x47\x6e\x50\x1a\x71\x4e\x55\x77\xc7\x09\xe4\x3d\xe4\x82\xae" + + "\x4c\x97\x7d\xc3\x73\x89\xb3\xbe\x0e\x28\x8d\xfe\xeb\xdf\x95\x79\xfd\xfb\x55\x19\x13\x86\x10\xa1\xe5\x5f\x1f\x2c" + + "\xe2\xdf\x03\xf4\xc1\x11\xba\xf0\x5c\x57\x2b\xec\x0b\xcd\xf0\xfa\x1d\xe9\x78\x26\xcd\xc7\x17\x2f\x83\x80\x0b\x3c" + + "\x79\x24\xd7\x67\xa6\xd3\x2e\xd6\xab\x27\x40\xc1\x75\xb9\x70\xc8\x50\x10\xd4\xef\xc7\xe6\x69\xf3\x27\x00\x00\xff" + + "\xff\x42\x02\xc0\xed\x72\x05\x00\x00") func bindataDataDefaultconfigjsonBytes() ([]byte, error) { return bindataRead( diff --git a/internal/cli/onboard/onboard.go b/internal/cli/onboard/onboard.go index 7970976..108672a 100644 --- a/internal/cli/onboard/onboard.go +++ b/internal/cli/onboard/onboard.go @@ -2,6 +2,7 @@ package onboard import ( "github.com/alecthomas/kingpin" + "github.com/apex/log" "github.com/ooni/probe-cli/internal/cli/root" "github.com/ooni/probe-cli/internal/onboard" ) @@ -9,12 +10,26 @@ import ( func init() { cmd := root.Command("onboard", "Starts the onboarding process") + yes := cmd.Flag("yes", "Answer yes to all the onboarding questions.").Bool() + cmd.Action(func(_ *kingpin.ParseContext) error { ctx, err := root.Init() if err != nil { return err } + if *yes == true { + ctx.Config.Lock() + ctx.Config.InformedConsent = true + ctx.Config.Unlock() + + if err := ctx.Config.Write(); err != nil { + log.WithError(err).Error("failed to write config file") + return err + } + return nil + } + return onboard.Onboarding(ctx.Config) }) }