From f5e149c967c18eb172dbf1e2060f2bb5d9bb807f Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Wed, 31 Aug 2022 11:46:56 +0200 Subject: [PATCH] feat(miniooni): make CLI much more user friendly Part of https://github.com/ooni/probe/issues/2184, because I wanted to allow swapping commands and options more freely. AFAICT, every usage that was legal before is still legal. What has changed seems the freedom to swap commands and options and a much better help that lists the available options. --- go.mod | 6 + go.sum | 6 + internal/cmd/miniooni/consent.go | 2 +- internal/cmd/miniooni/libminiooni.go | 275 ++++++++++++++++-------- internal/cmd/miniooni/main_test.go | 2 +- internal/cmd/miniooni/oonirun.go | 2 +- internal/cmd/miniooni/runx.go | 2 +- internal/cmd/miniooni/session.go | 2 +- internal/registry/allexperiments.go | 4 +- internal/registry/dash.go | 2 +- internal/registry/dnscheck.go | 2 +- internal/registry/dnsping.go | 2 +- internal/registry/example.go | 2 +- internal/registry/factory.go | 2 +- internal/registry/fbmessenger.go | 2 +- internal/registry/hhfm.go | 2 +- internal/registry/hirl.go | 2 +- internal/registry/httphostheader.go | 2 +- internal/registry/ndt.go | 2 +- internal/registry/psiphon.go | 2 +- internal/registry/quicping.go | 2 +- internal/registry/riseupvpn.go | 2 +- internal/registry/run.go | 2 +- internal/registry/signal.go | 2 +- internal/registry/simplequicping.go | 2 +- internal/registry/sniblocking.go | 2 +- internal/registry/stunreachability.go | 2 +- internal/registry/tcpping.go | 2 +- internal/registry/telegram.go | 2 +- internal/registry/tlsping.go | 2 +- internal/registry/tlstool.go | 2 +- internal/registry/tor.go | 2 +- internal/registry/torsf.go | 2 +- internal/registry/urlgetter.go | 2 +- internal/registry/vanillator.go | 2 +- internal/registry/webconnectivity.go | 2 +- internal/registry/webconnectivityv05.go | 2 +- internal/registry/whatsapp.go | 2 +- 38 files changed, 235 insertions(+), 124 deletions(-) diff --git a/go.mod b/go.mod index fe8e6e5241..e6408f4895 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,11 @@ require ( golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 ) +require ( + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect +) + require ( filippo.io/edwards25519 v1.0.0 // indirect github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect @@ -109,6 +114,7 @@ require ( github.com/refraction-networking/utls v1.0.0 // indirect github.com/sergeyfrolov/bsbuffer v0.0.0-20180903213811-94e85abb8507 // indirect github.com/sirupsen/logrus v1.9.0 // indirect + github.com/spf13/cobra v1.5.0 github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect github.com/templexxx/cpu v0.0.9 // indirect github.com/templexxx/xorsimd v0.4.1 // indirect diff --git a/go.sum b/go.sum index 0de46231cb..04b082d178 100644 --- a/go.sum +++ b/go.sum @@ -156,6 +156,7 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -398,6 +399,7 @@ github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= @@ -778,6 +780,7 @@ github.com/rubenv/sql-migrate v1.1.2 h1:9M6oj4e//owVVHYrFISmY9LBRw6gzkCNmD9MV36t github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= @@ -839,10 +842,13 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= diff --git a/internal/cmd/miniooni/consent.go b/internal/cmd/miniooni/consent.go index 7939e05b03..c8862c50d1 100644 --- a/internal/cmd/miniooni/consent.go +++ b/internal/cmd/miniooni/consent.go @@ -13,7 +13,7 @@ import ( // acquireUserConsent ensures the user is okay with using miniooni. This function // panics if we do not have acquired the user consent. -func acquireUserConsent(miniooniDir string, currentOptions Options) { +func acquireUserConsent(miniooniDir string, currentOptions *Options) { consentFile := path.Join(miniooniDir, "informed") err := maybeWriteConsentFile(currentOptions.Yes, consentFile) runtimex.PanicOnError(err, "cannot write informed consent file") diff --git a/internal/cmd/miniooni/libminiooni.go b/internal/cmd/miniooni/libminiooni.go index 72d7102dd4..3ce7929d55 100644 --- a/internal/cmd/miniooni/libminiooni.go +++ b/internal/cmd/miniooni/libminiooni.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "path" + "strings" "time" "github.com/apex/log" @@ -14,9 +15,10 @@ import ( "github.com/ooni/probe-cli/v3/internal/humanize" "github.com/ooni/probe-cli/v3/internal/legacy/assetsdir" "github.com/ooni/probe-cli/v3/internal/model" + "github.com/ooni/probe-cli/v3/internal/registry" "github.com/ooni/probe-cli/v3/internal/runtimex" "github.com/ooni/probe-cli/v3/internal/version" - "github.com/pborman/getopt/v2" + "github.com/spf13/cobra" ) // Options contains the options you can set from the CLI. @@ -38,118 +40,197 @@ type Options struct { TorBinary string Tunnel string Verbose bool - Version bool Yes bool } -var globalOptions Options +// main is the main function of miniooni. This function parses the command line +// options and uses a global state. Use MainWithConfiguration if you want to avoid +// using any global state and relying on command line options. +func main() { + var globalOptions Options + rootCmd := &cobra.Command{ + Use: "miniooni", + Short: "miniooni is OONI's research client", + Args: cobra.NoArgs, + Version: version.Version, + } + rootCmd.SetVersionTemplate("{{ .Version }}\n") + flags := rootCmd.PersistentFlags() -func init() { - getopt.FlagLong( - &globalOptions.Annotations, "annotation", 'A', "Add annotaton", "KEY=VALUE", - ) - getopt.FlagLong( - &globalOptions.ExtraOptions, "option", 'O', - "Pass an option to the experiment", "KEY=VALUE", - ) - getopt.FlagLong( - &globalOptions.InputFilePaths, "input-file", 'f', - "Path to input file to supply test-dependent input. File must contain one input per line.", "PATH", + flags.StringSliceVarP( + &globalOptions.Annotations, + "annotation", + "A", + []string{}, + "add KEY=VALUE annotation to the report", ) - getopt.FlagLong( - &globalOptions.HomeDir, "home", 0, - "Force specific home directory", "PATH", - ) - getopt.FlagLong( - &globalOptions.Inputs, "input", 'i', - "Add test-dependent input to the test input", "INPUT", - ) - getopt.FlagLong( - &globalOptions.MaxRuntime, "max-runtime", 0, - "Maximum runtime in seconds when looping over a list of inputs (zero means infinite)", "N", - ) - getopt.FlagLong( - &globalOptions.NoJSON, "no-json", 'N', "Disable writing to disk", - ) - getopt.FlagLong( - &globalOptions.NoCollector, "no-collector", 'n', "Don't use a collector", + + flags.StringVar( + &globalOptions.HomeDir, + "home", + "", + "force specific home directory", ) - getopt.FlagLong( - &globalOptions.ProbeServicesURL, "probe-services", 0, - "Set the URL of the probe-services instance you want to use", "URL", + + flags.BoolVarP( + &globalOptions.NoJSON, + "no-json", + "N", + false, + "disable writing to disk", ) - getopt.FlagLong( - &globalOptions.Proxy, "proxy", 0, "Set the proxy URL", "URL", + + flags.BoolVarP( + &globalOptions.NoCollector, + "no-collector", + "n", + false, + "do not submit measurements to the OONI collector", ) - getopt.FlagLong( - &globalOptions.Random, "random", 0, "Randomize inputs", + + flags.StringVar( + &globalOptions.ProbeServicesURL, + "probe-services", + "", + "URL of the OONI backend instance you want to use", ) - getopt.FlagLong( - &globalOptions.RepeatEvery, "repeat-every", 0, - "Repeat the measurement every INTERVAL number of seconds", "INTERVAL", + + flags.StringVar( + &globalOptions.Proxy, + "proxy", + "", + "set proxy URL to communicate with the OONI backend", ) - getopt.FlagLong( - &globalOptions.ReportFile, "reportfile", 'o', - "Set the report file path", "PATH", + + flags.Int64Var( + &globalOptions.RepeatEvery, + "repeat-every", + 0, + "wait the given number of seconds and then measure again", ) - getopt.FlagLong( - &globalOptions.TorArgs, "tor-args", 0, - "Extra args for tor binary (may be specified multiple times)", + + flags.StringVarP( + &globalOptions.ReportFile, + "reportfile", + "o", + "", + "set the output report file path (default report.jsonl)", ) - getopt.FlagLong( - &globalOptions.TorBinary, "tor-binary", 0, - "Specify path to a specific tor binary", + + flags.StringSliceVar( + &globalOptions.TorArgs, + "tor-args", + []string{}, + "extra arguments for the tor binary (may be specified multiple times)", ) - getopt.FlagLong( - &globalOptions.Tunnel, "tunnel", 0, - "Name of the tunnel to use (one of `tor`, `psiphon`)", + + flags.StringVar( + &globalOptions.TorBinary, + "tor-binary", + "", + "execute a specific tor binary", ) - getopt.FlagLong( - &globalOptions.Verbose, "verbose", 'v', "Increase verbosity", + + flags.StringVar( + &globalOptions.Tunnel, + "tunnel", + "", + "tunnel to use to communicate with the OONI backend (one of: tor, psiphon)", ) - getopt.FlagLong( - &globalOptions.Version, "version", 0, "Print version and exit", + + flags.BoolVarP( + &globalOptions.Verbose, + "verbose", + "v", + false, + "increase verbosity level", ) - getopt.FlagLong( - &globalOptions.Yes, "yes", 'y', - "Assume yes as the answer to all questions", + + flags.BoolVarP( + &globalOptions.Yes, + "yes", + "y", + false, + "assume yes as the answer to all questions", ) -} -// main is the main function of miniooni. This function parses the command line -// options and uses a global state. Use MainWithConfiguration if you want to avoid -// using any global state and relying on command line options. -// -// This function will panic in case of a fatal error. It is up to you that -// integrate this function to either handle the panic of ignore it. -func main() { - getopt.Parse() - if globalOptions.Version { - fmt.Printf("%s\n", version.Version) - os.Exit(0) + rootCmd.MarkFlagsMutuallyExclusive("proxy", "tunnel") + + for name, factory := range registry.AllExperiments { + subCmd := &cobra.Command{ + Use: name, + Short: fmt.Sprintf("Runs the %s experiment", name), + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + MainWithConfiguration(cmd.Use, &globalOptions) + }, + } + rootCmd.AddCommand(subCmd) + flags := subCmd.Flags() + + switch factory.InputPolicy() { + case model.InputOrQueryBackend, + model.InputStrictlyRequired, + model.InputOptional, + model.InputOrStaticDefault: + + flags.StringSliceVarP( + &globalOptions.InputFilePaths, + "input-file", + "f", + []string{}, + "path to input file to supply test dependent input.", + ) + + flags.StringSliceVarP( + &globalOptions.Inputs, + "input", + "i", + []string{}, + "add test-dependent input", + ) + + flags.Int64Var( + &globalOptions.MaxRuntime, + "max-runtime", + 0, + "maximum runtime in seconds (zero means infinite)", + ) + + flags.BoolVar( + &globalOptions.Random, + "random", + false, + "randomize inputs", + ) + + default: + // nothing + } + + if doc := documentationForOptions(name, factory); doc != "" { + flags.StringSliceVarP( + &globalOptions.ExtraOptions, + "option", + "O", + []string{}, + doc, + ) + } } - runtimex.PanicIfFalse(len(getopt.Args()) == 1, "Missing experiment name") - runtimex.PanicOnError(engine.CheckEmbeddedPsiphonConfig(), "Invalid embedded psiphon config") - MainWithConfiguration(getopt.Arg(0), globalOptions) -} -// tunnelAndProxy is the text printed when the user specifies -// both the --tunnel and the --proxy options -const tunnelAndProxy = `USAGE ERROR: The --tunnel option and the --proxy -option cannot be specified at the same time. The --tunnel option is actually -just syntactic sugar for --proxy. Setting --tunnel=psiphon is currently the -equivalent of setting --proxy=psiphon:///. This MAY change in a future version -of miniooni, when we will allow a tunnel to use a proxy. -` + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} // MainWithConfiguration is the miniooni main with a specific configuration // represented by the experiment name and the current options. // // This function will panic in case of a fatal error. It is up to you that // integrate this function to either handle the panic of ignore it. -func MainWithConfiguration(experimentName string, currentOptions Options) { - runtimex.PanicIfTrue(currentOptions.Proxy != "" && currentOptions.Tunnel != "", - tunnelAndProxy) +func MainWithConfiguration(experimentName string, currentOptions *Options) { + runtimex.PanicOnError(engine.CheckEmbeddedPsiphonConfig(), "Invalid embedded psiphon config") if currentOptions.Tunnel != "" { currentOptions.Proxy = fmt.Sprintf("%s:///", currentOptions.Tunnel) } @@ -175,7 +256,7 @@ func MainWithConfiguration(experimentName string, currentOptions Options) { // mainSingleIteration runs a single iteration. There may be multiple iterations // when the user specifies the --repeat-every command line flag. -func mainSingleIteration(logger model.Logger, experimentName string, currentOptions Options) { +func mainSingleIteration(logger model.Logger, experimentName string, currentOptions *Options) { extraOptions := mustMakeMapStringAny(currentOptions.ExtraOptions) annotations := mustMakeMapStringString(currentOptions.Annotations) @@ -225,3 +306,21 @@ func mainSingleIteration(logger model.Logger, experimentName string, currentOpti // Otherwise just run OONI experiments as we normally do. runx(ctx, sess, experimentName, annotations, extraOptions, currentOptions) } + +func documentationForOptions(name string, factory *registry.Factory) string { + var sb strings.Builder + options, err := factory.Options() + if err != nil || len(options) < 1 { + return "" + } + fmt.Fprint(&sb, "Pass KEY=VALUE options to the experiment. Available options:\n") + for name, info := range options { + if info.Doc == "" { + continue + } + fmt.Fprintf(&sb, "\n") + fmt.Fprintf(&sb, " -O, --option %s=<%s>\n", name, info.Type) + fmt.Fprintf(&sb, " %s\n", info.Doc) + } + return sb.String() +} diff --git a/internal/cmd/miniooni/main_test.go b/internal/cmd/miniooni/main_test.go index 01ec1a5ef5..8199c2fb25 100644 --- a/internal/cmd/miniooni/main_test.go +++ b/internal/cmd/miniooni/main_test.go @@ -6,7 +6,7 @@ func TestSimple(t *testing.T) { if testing.Short() { t.Skip("skip test in short mode") } - MainWithConfiguration("example", Options{ + MainWithConfiguration("example", &Options{ Yes: true, }) } diff --git a/internal/cmd/miniooni/oonirun.go b/internal/cmd/miniooni/oonirun.go index 3aa0421765..eb5946d12f 100644 --- a/internal/cmd/miniooni/oonirun.go +++ b/internal/cmd/miniooni/oonirun.go @@ -16,7 +16,7 @@ import ( // ooniRunMain runs the experiments described by the given OONI Run URLs. This // function works with both v1 and v2 OONI Run URLs. func ooniRunMain(ctx context.Context, - sess *engine.Session, currentOptions Options, annotations map[string]string) { + sess *engine.Session, currentOptions *Options, annotations map[string]string) { runtimex.PanicIfTrue( len(currentOptions.Inputs) <= 0, "in oonirun mode you need to specify at least one URL using `-i URL`", diff --git a/internal/cmd/miniooni/runx.go b/internal/cmd/miniooni/runx.go index 31b8f2b59e..12bb18518b 100644 --- a/internal/cmd/miniooni/runx.go +++ b/internal/cmd/miniooni/runx.go @@ -13,7 +13,7 @@ import ( // runx runs the given experiment by name func runx(ctx context.Context, sess oonirun.Session, experimentName string, - annotations map[string]string, extraOptions map[string]any, currentOptions Options) { + annotations map[string]string, extraOptions map[string]any, currentOptions *Options) { desc := &oonirun.Experiment{ Annotations: annotations, ExtraOptions: extraOptions, diff --git a/internal/cmd/miniooni/session.go b/internal/cmd/miniooni/session.go index 37cb3fc2b9..fd573c6e62 100644 --- a/internal/cmd/miniooni/session.go +++ b/internal/cmd/miniooni/session.go @@ -20,7 +20,7 @@ const ( ) // newSessionOrPanic creates and starts a new session or panics on failure -func newSessionOrPanic(ctx context.Context, currentOptions Options, +func newSessionOrPanic(ctx context.Context, currentOptions *Options, miniooniDir string, logger model.Logger) *engine.Session { var proxyURL *url.URL if currentOptions.Proxy != "" { diff --git a/internal/registry/allexperiments.go b/internal/registry/allexperiments.go index 4cb28ac17e..d191b6c515 100644 --- a/internal/registry/allexperiments.go +++ b/internal/registry/allexperiments.go @@ -1,11 +1,11 @@ package registry // Where we register all the available experiments. -var allexperiments = map[string]*Factory{} +var AllExperiments = map[string]*Factory{} // ExperimentNames returns the name of all experiments func ExperimentNames() (names []string) { - for key := range allexperiments { + for key := range AllExperiments { names = append(names, key) } return diff --git a/internal/registry/dash.go b/internal/registry/dash.go index f56bced240..92f0338761 100644 --- a/internal/registry/dash.go +++ b/internal/registry/dash.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["dash"] = &Factory{ + AllExperiments["dash"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return dash.NewExperimentMeasurer( *config.(*dash.Config), diff --git a/internal/registry/dnscheck.go b/internal/registry/dnscheck.go index b4c264df17..acd0a376d4 100644 --- a/internal/registry/dnscheck.go +++ b/internal/registry/dnscheck.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["dnscheck"] = &Factory{ + AllExperiments["dnscheck"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return dnscheck.NewExperimentMeasurer( *config.(*dnscheck.Config), diff --git a/internal/registry/dnsping.go b/internal/registry/dnsping.go index 6ced01b75c..c4e4fc274b 100644 --- a/internal/registry/dnsping.go +++ b/internal/registry/dnsping.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["dnsping"] = &Factory{ + AllExperiments["dnsping"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return dnsping.NewExperimentMeasurer( *config.(*dnsping.Config), diff --git a/internal/registry/example.go b/internal/registry/example.go index 947715cfe9..b0c40b32e5 100644 --- a/internal/registry/example.go +++ b/internal/registry/example.go @@ -12,7 +12,7 @@ import ( ) func init() { - allexperiments["example"] = &Factory{ + AllExperiments["example"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return example.NewExperimentMeasurer( *config.(*example.Config), "example", diff --git a/internal/registry/factory.go b/internal/registry/factory.go index e8be338d0d..e28f0c01bb 100644 --- a/internal/registry/factory.go +++ b/internal/registry/factory.go @@ -216,7 +216,7 @@ func CanonicalizeExperimentName(name string) string { // NewFactory creates a new Factory instance. func NewFactory(name string) (*Factory, error) { name = CanonicalizeExperimentName(name) - factory := allexperiments[name] + factory := AllExperiments[name] if factory == nil { return nil, fmt.Errorf("no such experiment: %s", name) } diff --git a/internal/registry/fbmessenger.go b/internal/registry/fbmessenger.go index 6d2cda4c1d..2bde0f4546 100644 --- a/internal/registry/fbmessenger.go +++ b/internal/registry/fbmessenger.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["facebook_messenger"] = &Factory{ + AllExperiments["facebook_messenger"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return fbmessenger.NewExperimentMeasurer( *config.(*fbmessenger.Config), diff --git a/internal/registry/hhfm.go b/internal/registry/hhfm.go index 9a2bdafe58..80cde4e806 100644 --- a/internal/registry/hhfm.go +++ b/internal/registry/hhfm.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["http_header_field_manipulation"] = &Factory{ + AllExperiments["http_header_field_manipulation"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return hhfm.NewExperimentMeasurer( *config.(*hhfm.Config), diff --git a/internal/registry/hirl.go b/internal/registry/hirl.go index 63cb6339bd..d266902845 100644 --- a/internal/registry/hirl.go +++ b/internal/registry/hirl.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["http_invalid_request_line"] = &Factory{ + AllExperiments["http_invalid_request_line"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return hirl.NewExperimentMeasurer( *config.(*hirl.Config), diff --git a/internal/registry/httphostheader.go b/internal/registry/httphostheader.go index 7f346145cd..a5de1e811c 100644 --- a/internal/registry/httphostheader.go +++ b/internal/registry/httphostheader.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["http_host_header"] = &Factory{ + AllExperiments["http_host_header"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return httphostheader.NewExperimentMeasurer( *config.(*httphostheader.Config), diff --git a/internal/registry/ndt.go b/internal/registry/ndt.go index 2a1a2c5beb..e48e15c2b8 100644 --- a/internal/registry/ndt.go +++ b/internal/registry/ndt.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["ndt"] = &Factory{ + AllExperiments["ndt"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return ndt7.NewExperimentMeasurer( *config.(*ndt7.Config), diff --git a/internal/registry/psiphon.go b/internal/registry/psiphon.go index 7702b31270..7c2553c6ef 100644 --- a/internal/registry/psiphon.go +++ b/internal/registry/psiphon.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["psiphon"] = &Factory{ + AllExperiments["psiphon"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return psiphon.NewExperimentMeasurer( *config.(*psiphon.Config), diff --git a/internal/registry/quicping.go b/internal/registry/quicping.go index cc59ff008e..793f52896b 100644 --- a/internal/registry/quicping.go +++ b/internal/registry/quicping.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["quicping"] = &Factory{ + AllExperiments["quicping"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return quicping.NewExperimentMeasurer( *config.(*quicping.Config), diff --git a/internal/registry/riseupvpn.go b/internal/registry/riseupvpn.go index de2c88d768..ec2c7d97b1 100644 --- a/internal/registry/riseupvpn.go +++ b/internal/registry/riseupvpn.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["riseupvpn"] = &Factory{ + AllExperiments["riseupvpn"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return riseupvpn.NewExperimentMeasurer( *config.(*riseupvpn.Config), diff --git a/internal/registry/run.go b/internal/registry/run.go index 120578ca81..18e0f6866e 100644 --- a/internal/registry/run.go +++ b/internal/registry/run.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["run"] = &Factory{ + AllExperiments["run"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return run.NewExperimentMeasurer( *config.(*run.Config), diff --git a/internal/registry/signal.go b/internal/registry/signal.go index 71a919b3b1..5d6f4e6bd4 100644 --- a/internal/registry/signal.go +++ b/internal/registry/signal.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["signal"] = &Factory{ + AllExperiments["signal"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return signal.NewExperimentMeasurer( *config.(*signal.Config), diff --git a/internal/registry/simplequicping.go b/internal/registry/simplequicping.go index 22230b3d4a..5b002dda16 100644 --- a/internal/registry/simplequicping.go +++ b/internal/registry/simplequicping.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["simplequicping"] = &Factory{ + AllExperiments["simplequicping"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return simplequicping.NewExperimentMeasurer( *config.(*simplequicping.Config), diff --git a/internal/registry/sniblocking.go b/internal/registry/sniblocking.go index cd3409f17f..d3e08043f7 100644 --- a/internal/registry/sniblocking.go +++ b/internal/registry/sniblocking.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["sni_blocking"] = &Factory{ + AllExperiments["sni_blocking"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return sniblocking.NewExperimentMeasurer( *config.(*sniblocking.Config), diff --git a/internal/registry/stunreachability.go b/internal/registry/stunreachability.go index bf6f6a4c50..76181882c3 100644 --- a/internal/registry/stunreachability.go +++ b/internal/registry/stunreachability.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["stunreachability"] = &Factory{ + AllExperiments["stunreachability"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return stunreachability.NewExperimentMeasurer( *config.(*stunreachability.Config), diff --git a/internal/registry/tcpping.go b/internal/registry/tcpping.go index 5ff79ea417..3769e32173 100644 --- a/internal/registry/tcpping.go +++ b/internal/registry/tcpping.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["tcpping"] = &Factory{ + AllExperiments["tcpping"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return tcpping.NewExperimentMeasurer( *config.(*tcpping.Config), diff --git a/internal/registry/telegram.go b/internal/registry/telegram.go index 3c20980aba..b31fdfb5dc 100644 --- a/internal/registry/telegram.go +++ b/internal/registry/telegram.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["telegram"] = &Factory{ + AllExperiments["telegram"] = &Factory{ build: func(config any) model.ExperimentMeasurer { return telegram.NewExperimentMeasurer( config.(telegram.Config), diff --git a/internal/registry/tlsping.go b/internal/registry/tlsping.go index 0f944809ec..1273d0012d 100644 --- a/internal/registry/tlsping.go +++ b/internal/registry/tlsping.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["tlsping"] = &Factory{ + AllExperiments["tlsping"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return tlsping.NewExperimentMeasurer( *config.(*tlsping.Config), diff --git a/internal/registry/tlstool.go b/internal/registry/tlstool.go index 434351601b..6d7fb56883 100644 --- a/internal/registry/tlstool.go +++ b/internal/registry/tlstool.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["tlstool"] = &Factory{ + AllExperiments["tlstool"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return tlstool.NewExperimentMeasurer( *config.(*tlstool.Config), diff --git a/internal/registry/tor.go b/internal/registry/tor.go index 5a2a2ba08f..408404a57f 100644 --- a/internal/registry/tor.go +++ b/internal/registry/tor.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["tor"] = &Factory{ + AllExperiments["tor"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return tor.NewExperimentMeasurer( *config.(*tor.Config), diff --git a/internal/registry/torsf.go b/internal/registry/torsf.go index b31888fda2..45dbaf16a4 100644 --- a/internal/registry/torsf.go +++ b/internal/registry/torsf.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["torsf"] = &Factory{ + AllExperiments["torsf"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return torsf.NewExperimentMeasurer( *config.(*torsf.Config), diff --git a/internal/registry/urlgetter.go b/internal/registry/urlgetter.go index 84762e052d..afe441e409 100644 --- a/internal/registry/urlgetter.go +++ b/internal/registry/urlgetter.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["urlgetter"] = &Factory{ + AllExperiments["urlgetter"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return urlgetter.NewExperimentMeasurer( *config.(*urlgetter.Config), diff --git a/internal/registry/vanillator.go b/internal/registry/vanillator.go index 042ae549ed..3dd44a6741 100644 --- a/internal/registry/vanillator.go +++ b/internal/registry/vanillator.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["vanilla_tor"] = &Factory{ + AllExperiments["vanilla_tor"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return vanillator.NewExperimentMeasurer( *config.(*vanillator.Config), diff --git a/internal/registry/webconnectivity.go b/internal/registry/webconnectivity.go index 69e27aa2d4..b286d8335b 100644 --- a/internal/registry/webconnectivity.go +++ b/internal/registry/webconnectivity.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["web_connectivity"] = &Factory{ + AllExperiments["web_connectivity"] = &Factory{ build: func(config any) model.ExperimentMeasurer { return webconnectivity.NewExperimentMeasurer( config.(webconnectivity.Config), diff --git a/internal/registry/webconnectivityv05.go b/internal/registry/webconnectivityv05.go index 2697c1e08c..1bbafdff13 100644 --- a/internal/registry/webconnectivityv05.go +++ b/internal/registry/webconnectivityv05.go @@ -14,7 +14,7 @@ import ( func init() { // Note: the name inserted into the table is the canonicalized experiment // name though we advertise using `web_connectivity@v0.5`. - allexperiments["web_connectivity@v_0_5"] = &Factory{ + AllExperiments["web_connectivity@v_0_5"] = &Factory{ build: func(config any) model.ExperimentMeasurer { return webconnectivity.NewExperimentMeasurer( config.(*webconnectivity.Config), diff --git a/internal/registry/whatsapp.go b/internal/registry/whatsapp.go index 9fd8d0f96d..b6ae9bed32 100644 --- a/internal/registry/whatsapp.go +++ b/internal/registry/whatsapp.go @@ -10,7 +10,7 @@ import ( ) func init() { - allexperiments["whatsapp"] = &Factory{ + AllExperiments["whatsapp"] = &Factory{ build: func(config interface{}) model.ExperimentMeasurer { return whatsapp.NewExperimentMeasurer( *config.(*whatsapp.Config),