From 7327e1ff7f0cfdc5ff0335574b85dc8ceb9465b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Wed, 7 Feb 2018 20:02:18 +0200 Subject: [PATCH] Start laying out the structure of gooni --- .gitignore | 2 + Gopkg.lock | 42 ++++++++++ Gopkg.toml | 42 ++++++++++ Makefile | 6 ++ cmd/ooni/main.go | 23 ++++++ config/advanced.go | 7 ++ config/automated_testing.go | 8 ++ config/nettest_groups.go | 29 +++++++ config/notifications.go | 8 ++ config/sharing.go | 10 +++ internal/cli/app/app.go | 17 ++++ internal/cli/info/info.go | 16 ++++ internal/cli/list/list.go | 16 ++++ internal/cli/nettest/nettest.go | 16 ++++ internal/cli/root/root.go | 19 +++++ internal/cli/run/run.go | 18 ++++ internal/cli/show/show.go | 16 ++++ internal/cli/upload/upload.go | 16 ++++ internal/cli/version/version.go | 18 ++++ internal/colors/colors.go | 40 +++++++++ internal/util/util.go | 19 +++++ ooni.go | 141 ++++++++++++++++++++++++++++++++ 22 files changed, 529 insertions(+) create mode 100644 .gitignore create mode 100644 Gopkg.lock create mode 100644 Gopkg.toml create mode 100644 Makefile create mode 100644 cmd/ooni/main.go create mode 100644 config/advanced.go create mode 100644 config/automated_testing.go create mode 100644 config/nettest_groups.go create mode 100644 config/notifications.go create mode 100644 config/sharing.go create mode 100644 internal/cli/app/app.go create mode 100644 internal/cli/info/info.go create mode 100644 internal/cli/list/list.go create mode 100644 internal/cli/nettest/nettest.go create mode 100644 internal/cli/root/root.go create mode 100644 internal/cli/run/run.go create mode 100644 internal/cli/show/show.go create mode 100644 internal/cli/upload/upload.go create mode 100644 internal/cli/version/version.go create mode 100644 internal/colors/colors.go create mode 100644 internal/util/util.go create mode 100644 ooni.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ed2fa3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +vendor/ +/ooni diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..9efe0bb --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,42 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "github.com/alecthomas/kingpin" + packages = ["."] + revision = "947dcec5ba9c011838740e680966fd7087a71d0d" + version = "v2.2.6" + +[[projects]] + branch = "master" + name = "github.com/alecthomas/template" + packages = [ + ".", + "parse" + ] + revision = "a0175ee3bccc567396460bf5acd36800cb10c49c" + +[[projects]] + branch = "master" + name = "github.com/alecthomas/units" + packages = ["."] + revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a" + +[[projects]] + branch = "master" + name = "github.com/aybabtme/rgbterm" + packages = ["."] + revision = "cc83f3b3ce5911279513a46d6d3316d67bedaa54" + +[[projects]] + name = "github.com/pkg/errors" + packages = ["."] + revision = "645ef00459ed84a119197bfb8d8205042c6df63d" + version = "v0.8.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "4ed0a29997f4d8f197233e4ba7aa66ed7033443a893a795ed2b4a9a5ec4350bd" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..91e2189 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,42 @@ +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + name = "github.com/alecthomas/kingpin" + version = "2.2.6" + +[[constraint]] + branch = "master" + name = "github.com/aybabtme/rgbterm" + +[prune] + go-tests = true + unused-packages = true + +[[constraint]] + name = "github.com/pkg/errors" + version = "0.8.0" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..405c7eb --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +GO ?= go + +build: + @echo "Building ./ooni" + @$(GO) build -o ooni cmd/ooni/main.go +.PHONY: build diff --git a/cmd/ooni/main.go b/cmd/ooni/main.go new file mode 100644 index 0000000..2068be0 --- /dev/null +++ b/cmd/ooni/main.go @@ -0,0 +1,23 @@ +package main + +import ( + // commands + _ "github.com/openobservatory/gooni/internal/cli/info" + _ "github.com/openobservatory/gooni/internal/cli/list" + _ "github.com/openobservatory/gooni/internal/cli/nettest" + _ "github.com/openobservatory/gooni/internal/cli/run" + _ "github.com/openobservatory/gooni/internal/cli/show" + _ "github.com/openobservatory/gooni/internal/cli/upload" + _ "github.com/openobservatory/gooni/internal/cli/version" + "github.com/openobservatory/gooni/internal/util" + + "github.com/openobservatory/gooni/internal/cli/app" +) + +func main() { + err := app.Run() + if err == nil { + return + } + util.Fatal(err) +} diff --git a/config/advanced.go b/config/advanced.go new file mode 100644 index 0000000..2add03c --- /dev/null +++ b/config/advanced.go @@ -0,0 +1,7 @@ +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 new file mode 100644 index 0000000..56dc660 --- /dev/null +++ b/config/automated_testing.go @@ -0,0 +1,8 @@ +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/nettest_groups.go b/config/nettest_groups.go new file mode 100644 index 0000000..9c1c6c5 --- /dev/null +++ b/config/nettest_groups.go @@ -0,0 +1,29 @@ +package config + +type websiteSettings struct { + EnabledCategories []string `json:"enabled_categories"` +} + +type instantMessagingSettings struct { + EnabledTests []string `json:"enabled_tests"` +} + +type performanceSettings 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"` +} + +type middleboxSettings struct { + EnabledTests []string `json:"enabled_tests"` +} + +// NettestGroups related settings +type NettestGroups struct { + Websites websiteSettings `json:"websites"` + InstantMessaging instantMessagingSettings `json:"instant_messaging"` + Performance performanceSettings `json:"performance"` + Middlebox middleboxSettings `json:"middlebox"` +} diff --git a/config/notifications.go b/config/notifications.go new file mode 100644 index 0000000..7341ac3 --- /dev/null +++ b/config/notifications.go @@ -0,0 +1,8 @@ +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/sharing.go b/config/sharing.go new file mode 100644 index 0000000..6aebf8d --- /dev/null +++ b/config/sharing.go @@ -0,0 +1,10 @@ +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/internal/cli/app/app.go b/internal/cli/app/app.go new file mode 100644 index 0000000..db83d1c --- /dev/null +++ b/internal/cli/app/app.go @@ -0,0 +1,17 @@ +package app + +import ( + "os" + + "github.com/openobservatory/gooni/internal/cli/root" + "github.com/openobservatory/gooni/internal/cli/version" + "github.com/openobservatory/gooni/internal/util" +) + +// Run the app. This is the main app entry point +func Run() error { + util.Log("Running") + root.Cmd.Version(version.Version) + _, err := root.Cmd.Parse(os.Args[1:]) + return err +} diff --git a/internal/cli/info/info.go b/internal/cli/info/info.go new file mode 100644 index 0000000..ae0dd7a --- /dev/null +++ b/internal/cli/info/info.go @@ -0,0 +1,16 @@ +package info + +import ( + "github.com/alecthomas/kingpin" + "github.com/openobservatory/gooni/internal/cli/root" + "github.com/openobservatory/gooni/internal/util" +) + +func init() { + cmd := root.Command("info", "Display information about OONI Probe") + + cmd.Action(func(_ *kingpin.ParseContext) error { + util.Log("Info") + return nil + }) +} diff --git a/internal/cli/list/list.go b/internal/cli/list/list.go new file mode 100644 index 0000000..2e7462f --- /dev/null +++ b/internal/cli/list/list.go @@ -0,0 +1,16 @@ +package list + +import ( + "github.com/alecthomas/kingpin" + "github.com/openobservatory/gooni/internal/cli/root" + "github.com/openobservatory/gooni/internal/util" +) + +func init() { + cmd := root.Command("list", "List measurements") + + cmd.Action(func(_ *kingpin.ParseContext) error { + util.Log("Listing") + return nil + }) +} diff --git a/internal/cli/nettest/nettest.go b/internal/cli/nettest/nettest.go new file mode 100644 index 0000000..8210ccf --- /dev/null +++ b/internal/cli/nettest/nettest.go @@ -0,0 +1,16 @@ +package nettest + +import ( + "github.com/alecthomas/kingpin" + "github.com/openobservatory/gooni/internal/cli/root" + "github.com/openobservatory/gooni/internal/util" +) + +func init() { + cmd := root.Command("nettest", "Run a specific nettest") + + cmd.Action(func(_ *kingpin.ParseContext) error { + util.Log("Nettest") + return nil + }) +} diff --git a/internal/cli/root/root.go b/internal/cli/root/root.go new file mode 100644 index 0000000..102dcb0 --- /dev/null +++ b/internal/cli/root/root.go @@ -0,0 +1,19 @@ +package root + +import ( + "github.com/alecthomas/kingpin" + "github.com/openobservatory/gooni/internal/util" +) + +// Cmd is the root command +var Cmd = kingpin.New("ooni", "") + +// Command is syntax sugar for defining sub-commands +var Command = Cmd.Command + +func init() { + Cmd.PreAction(func(ctx *kingpin.ParseContext) error { + util.Log("Running pre-action") + return nil + }) +} diff --git a/internal/cli/run/run.go b/internal/cli/run/run.go new file mode 100644 index 0000000..bbc2d91 --- /dev/null +++ b/internal/cli/run/run.go @@ -0,0 +1,18 @@ +package run + +import ( + "github.com/alecthomas/kingpin" + "github.com/openobservatory/gooni/internal/cli/root" + "github.com/openobservatory/gooni/internal/util" +) + +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() + + cmd.Action(func(_ *kingpin.ParseContext) error { + util.Log("Starting %s", nettestGroup) + return nil + }) +} diff --git a/internal/cli/show/show.go b/internal/cli/show/show.go new file mode 100644 index 0000000..51848fa --- /dev/null +++ b/internal/cli/show/show.go @@ -0,0 +1,16 @@ +package nettest + +import ( + "github.com/alecthomas/kingpin" + "github.com/openobservatory/gooni/internal/cli/root" + "github.com/openobservatory/gooni/internal/util" +) + +func init() { + cmd := root.Command("show", "Show a specific measurement") + + cmd.Action(func(_ *kingpin.ParseContext) error { + util.Log("Show") + return nil + }) +} diff --git a/internal/cli/upload/upload.go b/internal/cli/upload/upload.go new file mode 100644 index 0000000..4a9d75f --- /dev/null +++ b/internal/cli/upload/upload.go @@ -0,0 +1,16 @@ +package upload + +import ( + "github.com/alecthomas/kingpin" + "github.com/openobservatory/gooni/internal/cli/root" + "github.com/openobservatory/gooni/internal/util" +) + +func init() { + cmd := root.Command("upload", "Upload a specific measurement") + + cmd.Action(func(_ *kingpin.ParseContext) error { + util.Log("Uploading") + return nil + }) +} diff --git a/internal/cli/version/version.go b/internal/cli/version/version.go new file mode 100644 index 0000000..f0af341 --- /dev/null +++ b/internal/cli/version/version.go @@ -0,0 +1,18 @@ +package version + +import ( + "fmt" + + "github.com/alecthomas/kingpin" + "github.com/openobservatory/gooni/internal/cli/root" +) + +const Version = "0.0.1" + +func init() { + cmd := root.Command("version", "Show version.") + cmd.Action(func(_ *kingpin.ParseContext) error { + fmt.Println(Version) + return nil + }) +} diff --git a/internal/colors/colors.go b/internal/colors/colors.go new file mode 100644 index 0000000..09b0c58 --- /dev/null +++ b/internal/colors/colors.go @@ -0,0 +1,40 @@ +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/util/util.go b/internal/util/util.go new file mode 100644 index 0000000..a23788c --- /dev/null +++ b/internal/util/util.go @@ -0,0 +1,19 @@ +package util + +import ( + "fmt" + "os" + + "github.com/openobservatory/gooni/internal/colors" +) + +// Log outputs a log message. +func Log(msg string, v ...interface{}) { + fmt.Printf(" %s\n", colors.Purple(fmt.Sprintf(msg, v...))) +} + +// Fatal error +func Fatal(err error) { + fmt.Fprintf(os.Stderr, "\n %s %s\n\n", colors.Red("Error:"), err) + os.Exit(1) +} diff --git a/ooni.go b/ooni.go new file mode 100644 index 0000000..c89013c --- /dev/null +++ b/ooni.go @@ -0,0 +1,141 @@ +package ooni + +import ( + "encoding/json" + "io/ioutil" + "os" + + "github.com/openobservatory/gooni/config" + "github.com/pkg/errors" +) + +// 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.Sharing `json:"advanced"` +} + +// Default config settings +func (c *Config) Default() error { + // This is the default configuration: + /* + _: 'This is your OONI Probe config file. See https://ooni.io/help/ooniprobe-cli for help', + 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: true + }, + _config_version: CONFIG_VERSION, + _informed_consent: false + */ + 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 +} + +// 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") + } + } + + if err != nil { + return nil, errors.Wrap(err, "reading file") + } + + return ParseConfig(b) +}