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),