feat(miniooni): make CLI much more user friendly (#913)
Part of https://github.com/ooni/probe/issues/2184, because I wanted to allow swapping commands and options more freely. As a side effect, this PR closes https://github.com/ooni/probe/issues/2248. 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.
This commit is contained in:
parent
7daa686c68
commit
0bc6aae601
6
go.mod
6
go.mod
|
@ -43,6 +43,11 @@ require (
|
||||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6
|
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 (
|
require (
|
||||||
filippo.io/edwards25519 v1.0.0 // indirect
|
filippo.io/edwards25519 v1.0.0 // indirect
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // 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/refraction-networking/utls v1.0.0 // indirect
|
||||||
github.com/sergeyfrolov/bsbuffer v0.0.0-20180903213811-94e85abb8507 // indirect
|
github.com/sergeyfrolov/bsbuffer v0.0.0-20180903213811-94e85abb8507 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.0 // 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/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||||
github.com/templexxx/cpu v0.0.9 // indirect
|
github.com/templexxx/cpu v0.0.9 // indirect
|
||||||
github.com/templexxx/xorsimd v0.4.1 // indirect
|
github.com/templexxx/xorsimd v0.4.1 // indirect
|
||||||
|
|
6
go.sum
6
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 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-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.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.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
|
||||||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
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-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/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/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/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/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=
|
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/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 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.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/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/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=
|
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.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
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.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.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
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.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.3/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/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.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
|
|
||||||
// acquireUserConsent ensures the user is okay with using miniooni. This function
|
// acquireUserConsent ensures the user is okay with using miniooni. This function
|
||||||
// panics if we do not have acquired the user consent.
|
// 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")
|
consentFile := path.Join(miniooniDir, "informed")
|
||||||
err := maybeWriteConsentFile(currentOptions.Yes, consentFile)
|
err := maybeWriteConsentFile(currentOptions.Yes, consentFile)
|
||||||
runtimex.PanicOnError(err, "cannot write informed consent file")
|
runtimex.PanicOnError(err, "cannot write informed consent file")
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/apex/log"
|
"github.com/apex/log"
|
||||||
|
@ -14,9 +15,10 @@ import (
|
||||||
"github.com/ooni/probe-cli/v3/internal/humanize"
|
"github.com/ooni/probe-cli/v3/internal/humanize"
|
||||||
"github.com/ooni/probe-cli/v3/internal/legacy/assetsdir"
|
"github.com/ooni/probe-cli/v3/internal/legacy/assetsdir"
|
||||||
"github.com/ooni/probe-cli/v3/internal/model"
|
"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/runtimex"
|
||||||
"github.com/ooni/probe-cli/v3/internal/version"
|
"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.
|
// Options contains the options you can set from the CLI.
|
||||||
|
@ -38,118 +40,227 @@ type Options struct {
|
||||||
TorBinary string
|
TorBinary string
|
||||||
Tunnel string
|
Tunnel string
|
||||||
Verbose bool
|
Verbose bool
|
||||||
Version bool
|
|
||||||
Yes bool
|
Yes bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var globalOptions Options
|
// main is the main function of miniooni.
|
||||||
|
|
||||||
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",
|
|
||||||
)
|
|
||||||
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",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.ProbeServicesURL, "probe-services", 0,
|
|
||||||
"Set the URL of the probe-services instance you want to use", "URL",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.Proxy, "proxy", 0, "Set the proxy URL", "URL",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.Random, "random", 0, "Randomize inputs",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.RepeatEvery, "repeat-every", 0,
|
|
||||||
"Repeat the measurement every INTERVAL number of seconds", "INTERVAL",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.ReportFile, "reportfile", 'o',
|
|
||||||
"Set the report file path", "PATH",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.TorArgs, "tor-args", 0,
|
|
||||||
"Extra args for tor binary (may be specified multiple times)",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.TorBinary, "tor-binary", 0,
|
|
||||||
"Specify path to a specific tor binary",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.Tunnel, "tunnel", 0,
|
|
||||||
"Name of the tunnel to use (one of `tor`, `psiphon`)",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.Verbose, "verbose", 'v', "Increase verbosity",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.Version, "version", 0, "Print version and exit",
|
|
||||||
)
|
|
||||||
getopt.FlagLong(
|
|
||||||
&globalOptions.Yes, "yes", 'y',
|
|
||||||
"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() {
|
func main() {
|
||||||
getopt.Parse()
|
var globalOptions Options
|
||||||
if globalOptions.Version {
|
rootCmd := &cobra.Command{
|
||||||
fmt.Printf("%s\n", version.Version)
|
Use: "miniooni",
|
||||||
os.Exit(0)
|
Short: "miniooni is OONI's research client",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Version: version.Version,
|
||||||
|
}
|
||||||
|
rootCmd.SetVersionTemplate("{{ .Version }}\n")
|
||||||
|
flags := rootCmd.PersistentFlags()
|
||||||
|
|
||||||
|
flags.StringSliceVarP(
|
||||||
|
&globalOptions.Annotations,
|
||||||
|
"annotation",
|
||||||
|
"A",
|
||||||
|
[]string{},
|
||||||
|
"add KEY=VALUE annotation to the report (can be repeated multiple times)",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.StringVar(
|
||||||
|
&globalOptions.HomeDir,
|
||||||
|
"home",
|
||||||
|
"",
|
||||||
|
"force specific home directory",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.BoolVarP(
|
||||||
|
&globalOptions.NoJSON,
|
||||||
|
"no-json",
|
||||||
|
"N",
|
||||||
|
false,
|
||||||
|
"disable writing to disk",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.BoolVarP(
|
||||||
|
&globalOptions.NoCollector,
|
||||||
|
"no-collector",
|
||||||
|
"n",
|
||||||
|
false,
|
||||||
|
"do not submit measurements to the OONI collector",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.StringVar(
|
||||||
|
&globalOptions.ProbeServicesURL,
|
||||||
|
"probe-services",
|
||||||
|
"",
|
||||||
|
"URL of the OONI backend instance you want to use",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.StringVar(
|
||||||
|
&globalOptions.Proxy,
|
||||||
|
"proxy",
|
||||||
|
"",
|
||||||
|
"set proxy URL to communicate with the OONI backend (mutually exclusive with --tunnel)",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.Int64Var(
|
||||||
|
&globalOptions.RepeatEvery,
|
||||||
|
"repeat-every",
|
||||||
|
0,
|
||||||
|
"wait the given number of seconds and then repeat the same measurement",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.StringVarP(
|
||||||
|
&globalOptions.ReportFile,
|
||||||
|
"reportfile",
|
||||||
|
"o",
|
||||||
|
"",
|
||||||
|
"set the output report file path (default: \"report.jsonl\")",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.StringSliceVar(
|
||||||
|
&globalOptions.TorArgs,
|
||||||
|
"tor-args",
|
||||||
|
[]string{},
|
||||||
|
"extra arguments for the tor binary (may be specified multiple times)",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.StringVar(
|
||||||
|
&globalOptions.TorBinary,
|
||||||
|
"tor-binary",
|
||||||
|
"",
|
||||||
|
"execute a specific tor binary",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.StringVar(
|
||||||
|
&globalOptions.Tunnel,
|
||||||
|
"tunnel",
|
||||||
|
"",
|
||||||
|
"tunnel to use to communicate with the OONI backend (one of: tor, psiphon)",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.BoolVarP(
|
||||||
|
&globalOptions.Verbose,
|
||||||
|
"verbose",
|
||||||
|
"v",
|
||||||
|
false,
|
||||||
|
"increase verbosity level",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.BoolVarP(
|
||||||
|
&globalOptions.Yes,
|
||||||
|
"yes",
|
||||||
|
"y",
|
||||||
|
false,
|
||||||
|
"assume yes as the answer to all questions",
|
||||||
|
)
|
||||||
|
|
||||||
|
rootCmd.MarkFlagsMutuallyExclusive("proxy", "tunnel")
|
||||||
|
|
||||||
|
registerAllExperiments(rootCmd, &globalOptions)
|
||||||
|
registerOONIRun(rootCmd, &globalOptions)
|
||||||
|
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
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
|
// TODO(bassosimone): the current implementation is basically a cobra application
|
||||||
// both the --tunnel and the --proxy options
|
// where we hammered the previous miniooni code to make it work. We should
|
||||||
const tunnelAndProxy = `USAGE ERROR: The --tunnel option and the --proxy
|
// obviously strive for more correctness. For example, it's a bit disgusting
|
||||||
option cannot be specified at the same time. The --tunnel option is actually
|
// that MainWithConfiguration is invoked for both oonirun and random experiments.
|
||||||
just syntactic sugar for --proxy. Setting --tunnel=psiphon is currently the
|
|
||||||
equivalent of setting --proxy=psiphon:///. This MAY change in a future version
|
// registerOONIRun registers the oonirun subcommand
|
||||||
of miniooni, when we will allow a tunnel to use a proxy.
|
func registerOONIRun(rootCmd *cobra.Command, globalOptions *Options) {
|
||||||
`
|
subCmd := &cobra.Command{
|
||||||
|
Use: "oonirun",
|
||||||
|
Short: "Runs a given OONI Run v2 link",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
MainWithConfiguration(cmd.Use, globalOptions)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rootCmd.AddCommand(subCmd)
|
||||||
|
flags := subCmd.Flags()
|
||||||
|
flags.StringSliceVarP(
|
||||||
|
&globalOptions.Inputs,
|
||||||
|
"input",
|
||||||
|
"i",
|
||||||
|
[]string{},
|
||||||
|
"URL of the OONI Run v2 descriptor to run (may be specified multiple times)",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerAllExperiments registers a subcommand for each experiment
|
||||||
|
func registerAllExperiments(rootCmd *cobra.Command, globalOptions *Options) {
|
||||||
|
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 file to supply test dependent input (may be specified multiple times)",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.StringSliceVarP(
|
||||||
|
&globalOptions.Inputs,
|
||||||
|
"input",
|
||||||
|
"i",
|
||||||
|
[]string{},
|
||||||
|
"add test-dependent input (may be specified multiple times)",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.Int64Var(
|
||||||
|
&globalOptions.MaxRuntime,
|
||||||
|
"max-runtime",
|
||||||
|
0,
|
||||||
|
"maximum runtime in seconds for the experiment (zero means infinite)",
|
||||||
|
)
|
||||||
|
|
||||||
|
flags.BoolVar(
|
||||||
|
&globalOptions.Random,
|
||||||
|
"random",
|
||||||
|
false,
|
||||||
|
"randomize the inputs list",
|
||||||
|
)
|
||||||
|
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
if doc := documentationForOptions(name, factory); doc != "" {
|
||||||
|
flags.StringSliceVarP(
|
||||||
|
&globalOptions.ExtraOptions,
|
||||||
|
"option",
|
||||||
|
"O",
|
||||||
|
[]string{},
|
||||||
|
doc,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MainWithConfiguration is the miniooni main with a specific configuration
|
// MainWithConfiguration is the miniooni main with a specific configuration
|
||||||
// represented by the experiment name and the current options.
|
// 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
|
// 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.
|
// integrate this function to either handle the panic of ignore it.
|
||||||
func MainWithConfiguration(experimentName string, currentOptions Options) {
|
func MainWithConfiguration(experimentName string, currentOptions *Options) {
|
||||||
runtimex.PanicIfTrue(currentOptions.Proxy != "" && currentOptions.Tunnel != "",
|
runtimex.PanicOnError(engine.CheckEmbeddedPsiphonConfig(), "Invalid embedded psiphon config")
|
||||||
tunnelAndProxy)
|
|
||||||
if currentOptions.Tunnel != "" {
|
if currentOptions.Tunnel != "" {
|
||||||
currentOptions.Proxy = fmt.Sprintf("%s:///", currentOptions.Tunnel)
|
currentOptions.Proxy = fmt.Sprintf("%s:///", currentOptions.Tunnel)
|
||||||
}
|
}
|
||||||
|
@ -175,7 +286,7 @@ func MainWithConfiguration(experimentName string, currentOptions Options) {
|
||||||
|
|
||||||
// mainSingleIteration runs a single iteration. There may be multiple iterations
|
// mainSingleIteration runs a single iteration. There may be multiple iterations
|
||||||
// when the user specifies the --repeat-every command line flag.
|
// 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)
|
extraOptions := mustMakeMapStringAny(currentOptions.ExtraOptions)
|
||||||
annotations := mustMakeMapStringString(currentOptions.Annotations)
|
annotations := mustMakeMapStringString(currentOptions.Annotations)
|
||||||
|
|
||||||
|
@ -225,3 +336,21 @@ func mainSingleIteration(logger model.Logger, experimentName string, currentOpti
|
||||||
// Otherwise just run OONI experiments as we normally do.
|
// Otherwise just run OONI experiments as we normally do.
|
||||||
runx(ctx, sess, experimentName, annotations, extraOptions, currentOptions)
|
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()
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ func TestSimple(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skip test in short mode")
|
t.Skip("skip test in short mode")
|
||||||
}
|
}
|
||||||
MainWithConfiguration("example", Options{
|
MainWithConfiguration("example", &Options{
|
||||||
Yes: true,
|
Yes: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
// ooniRunMain runs the experiments described by the given OONI Run URLs. This
|
// ooniRunMain runs the experiments described by the given OONI Run URLs. This
|
||||||
// function works with both v1 and v2 OONI Run URLs.
|
// function works with both v1 and v2 OONI Run URLs.
|
||||||
func ooniRunMain(ctx context.Context,
|
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(
|
runtimex.PanicIfTrue(
|
||||||
len(currentOptions.Inputs) <= 0,
|
len(currentOptions.Inputs) <= 0,
|
||||||
"in oonirun mode you need to specify at least one URL using `-i URL`",
|
"in oonirun mode you need to specify at least one URL using `-i URL`",
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
|
|
||||||
// runx runs the given experiment by name
|
// runx runs the given experiment by name
|
||||||
func runx(ctx context.Context, sess oonirun.Session, experimentName string,
|
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{
|
desc := &oonirun.Experiment{
|
||||||
Annotations: annotations,
|
Annotations: annotations,
|
||||||
ExtraOptions: extraOptions,
|
ExtraOptions: extraOptions,
|
||||||
|
|
|
@ -20,7 +20,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// newSessionOrPanic creates and starts a new session or panics on failure
|
// 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 {
|
miniooniDir string, logger model.Logger) *engine.Session {
|
||||||
var proxyURL *url.URL
|
var proxyURL *url.URL
|
||||||
if currentOptions.Proxy != "" {
|
if currentOptions.Proxy != "" {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package registry
|
package registry
|
||||||
|
|
||||||
// Where we register all the available experiments.
|
// Where we register all the available experiments.
|
||||||
var allexperiments = map[string]*Factory{}
|
var AllExperiments = map[string]*Factory{}
|
||||||
|
|
||||||
// ExperimentNames returns the name of all experiments
|
// ExperimentNames returns the name of all experiments
|
||||||
func ExperimentNames() (names []string) {
|
func ExperimentNames() (names []string) {
|
||||||
for key := range allexperiments {
|
for key := range AllExperiments {
|
||||||
names = append(names, key)
|
names = append(names, key)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["dash"] = &Factory{
|
AllExperiments["dash"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return dash.NewExperimentMeasurer(
|
return dash.NewExperimentMeasurer(
|
||||||
*config.(*dash.Config),
|
*config.(*dash.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["dnscheck"] = &Factory{
|
AllExperiments["dnscheck"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return dnscheck.NewExperimentMeasurer(
|
return dnscheck.NewExperimentMeasurer(
|
||||||
*config.(*dnscheck.Config),
|
*config.(*dnscheck.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["dnsping"] = &Factory{
|
AllExperiments["dnsping"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return dnsping.NewExperimentMeasurer(
|
return dnsping.NewExperimentMeasurer(
|
||||||
*config.(*dnsping.Config),
|
*config.(*dnsping.Config),
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["example"] = &Factory{
|
AllExperiments["example"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return example.NewExperimentMeasurer(
|
return example.NewExperimentMeasurer(
|
||||||
*config.(*example.Config), "example",
|
*config.(*example.Config), "example",
|
||||||
|
|
|
@ -208,6 +208,8 @@ func CanonicalizeExperimentName(name string) string {
|
||||||
name = "dnscheck"
|
name = "dnscheck"
|
||||||
case "stun_reachability":
|
case "stun_reachability":
|
||||||
name = "stunreachability"
|
name = "stunreachability"
|
||||||
|
case "web_connectivity@v_0_5":
|
||||||
|
name = "web_connectivity@v0.5"
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
return name
|
return name
|
||||||
|
@ -216,7 +218,7 @@ func CanonicalizeExperimentName(name string) string {
|
||||||
// NewFactory creates a new Factory instance.
|
// NewFactory creates a new Factory instance.
|
||||||
func NewFactory(name string) (*Factory, error) {
|
func NewFactory(name string) (*Factory, error) {
|
||||||
name = CanonicalizeExperimentName(name)
|
name = CanonicalizeExperimentName(name)
|
||||||
factory := allexperiments[name]
|
factory := AllExperiments[name]
|
||||||
if factory == nil {
|
if factory == nil {
|
||||||
return nil, fmt.Errorf("no such experiment: %s", name)
|
return nil, fmt.Errorf("no such experiment: %s", name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["facebook_messenger"] = &Factory{
|
AllExperiments["facebook_messenger"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return fbmessenger.NewExperimentMeasurer(
|
return fbmessenger.NewExperimentMeasurer(
|
||||||
*config.(*fbmessenger.Config),
|
*config.(*fbmessenger.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["http_header_field_manipulation"] = &Factory{
|
AllExperiments["http_header_field_manipulation"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return hhfm.NewExperimentMeasurer(
|
return hhfm.NewExperimentMeasurer(
|
||||||
*config.(*hhfm.Config),
|
*config.(*hhfm.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["http_invalid_request_line"] = &Factory{
|
AllExperiments["http_invalid_request_line"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return hirl.NewExperimentMeasurer(
|
return hirl.NewExperimentMeasurer(
|
||||||
*config.(*hirl.Config),
|
*config.(*hirl.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["http_host_header"] = &Factory{
|
AllExperiments["http_host_header"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return httphostheader.NewExperimentMeasurer(
|
return httphostheader.NewExperimentMeasurer(
|
||||||
*config.(*httphostheader.Config),
|
*config.(*httphostheader.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["ndt"] = &Factory{
|
AllExperiments["ndt"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return ndt7.NewExperimentMeasurer(
|
return ndt7.NewExperimentMeasurer(
|
||||||
*config.(*ndt7.Config),
|
*config.(*ndt7.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["psiphon"] = &Factory{
|
AllExperiments["psiphon"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return psiphon.NewExperimentMeasurer(
|
return psiphon.NewExperimentMeasurer(
|
||||||
*config.(*psiphon.Config),
|
*config.(*psiphon.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["quicping"] = &Factory{
|
AllExperiments["quicping"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return quicping.NewExperimentMeasurer(
|
return quicping.NewExperimentMeasurer(
|
||||||
*config.(*quicping.Config),
|
*config.(*quicping.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["riseupvpn"] = &Factory{
|
AllExperiments["riseupvpn"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return riseupvpn.NewExperimentMeasurer(
|
return riseupvpn.NewExperimentMeasurer(
|
||||||
*config.(*riseupvpn.Config),
|
*config.(*riseupvpn.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["run"] = &Factory{
|
AllExperiments["run"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return run.NewExperimentMeasurer(
|
return run.NewExperimentMeasurer(
|
||||||
*config.(*run.Config),
|
*config.(*run.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["signal"] = &Factory{
|
AllExperiments["signal"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return signal.NewExperimentMeasurer(
|
return signal.NewExperimentMeasurer(
|
||||||
*config.(*signal.Config),
|
*config.(*signal.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["simplequicping"] = &Factory{
|
AllExperiments["simplequicping"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return simplequicping.NewExperimentMeasurer(
|
return simplequicping.NewExperimentMeasurer(
|
||||||
*config.(*simplequicping.Config),
|
*config.(*simplequicping.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["sni_blocking"] = &Factory{
|
AllExperiments["sni_blocking"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return sniblocking.NewExperimentMeasurer(
|
return sniblocking.NewExperimentMeasurer(
|
||||||
*config.(*sniblocking.Config),
|
*config.(*sniblocking.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["stunreachability"] = &Factory{
|
AllExperiments["stunreachability"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return stunreachability.NewExperimentMeasurer(
|
return stunreachability.NewExperimentMeasurer(
|
||||||
*config.(*stunreachability.Config),
|
*config.(*stunreachability.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["tcpping"] = &Factory{
|
AllExperiments["tcpping"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return tcpping.NewExperimentMeasurer(
|
return tcpping.NewExperimentMeasurer(
|
||||||
*config.(*tcpping.Config),
|
*config.(*tcpping.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["telegram"] = &Factory{
|
AllExperiments["telegram"] = &Factory{
|
||||||
build: func(config any) model.ExperimentMeasurer {
|
build: func(config any) model.ExperimentMeasurer {
|
||||||
return telegram.NewExperimentMeasurer(
|
return telegram.NewExperimentMeasurer(
|
||||||
config.(telegram.Config),
|
config.(telegram.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["tlsping"] = &Factory{
|
AllExperiments["tlsping"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return tlsping.NewExperimentMeasurer(
|
return tlsping.NewExperimentMeasurer(
|
||||||
*config.(*tlsping.Config),
|
*config.(*tlsping.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["tlstool"] = &Factory{
|
AllExperiments["tlstool"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return tlstool.NewExperimentMeasurer(
|
return tlstool.NewExperimentMeasurer(
|
||||||
*config.(*tlstool.Config),
|
*config.(*tlstool.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["tor"] = &Factory{
|
AllExperiments["tor"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return tor.NewExperimentMeasurer(
|
return tor.NewExperimentMeasurer(
|
||||||
*config.(*tor.Config),
|
*config.(*tor.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["torsf"] = &Factory{
|
AllExperiments["torsf"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return torsf.NewExperimentMeasurer(
|
return torsf.NewExperimentMeasurer(
|
||||||
*config.(*torsf.Config),
|
*config.(*torsf.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["urlgetter"] = &Factory{
|
AllExperiments["urlgetter"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return urlgetter.NewExperimentMeasurer(
|
return urlgetter.NewExperimentMeasurer(
|
||||||
*config.(*urlgetter.Config),
|
*config.(*urlgetter.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["vanilla_tor"] = &Factory{
|
AllExperiments["vanilla_tor"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return vanillator.NewExperimentMeasurer(
|
return vanillator.NewExperimentMeasurer(
|
||||||
*config.(*vanillator.Config),
|
*config.(*vanillator.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["web_connectivity"] = &Factory{
|
AllExperiments["web_connectivity"] = &Factory{
|
||||||
build: func(config any) model.ExperimentMeasurer {
|
build: func(config any) model.ExperimentMeasurer {
|
||||||
return webconnectivity.NewExperimentMeasurer(
|
return webconnectivity.NewExperimentMeasurer(
|
||||||
config.(webconnectivity.Config),
|
config.(webconnectivity.Config),
|
||||||
|
|
|
@ -12,9 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Note: the name inserted into the table is the canonicalized experiment
|
AllExperiments["web_connectivity@v0.5"] = &Factory{
|
||||||
// name though we advertise using `web_connectivity@v0.5`.
|
|
||||||
allexperiments["web_connectivity@v_0_5"] = &Factory{
|
|
||||||
build: func(config any) model.ExperimentMeasurer {
|
build: func(config any) model.ExperimentMeasurer {
|
||||||
return webconnectivity.NewExperimentMeasurer(
|
return webconnectivity.NewExperimentMeasurer(
|
||||||
config.(*webconnectivity.Config),
|
config.(*webconnectivity.Config),
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
allexperiments["whatsapp"] = &Factory{
|
AllExperiments["whatsapp"] = &Factory{
|
||||||
build: func(config interface{}) model.ExperimentMeasurer {
|
build: func(config interface{}) model.ExperimentMeasurer {
|
||||||
return whatsapp.NewExperimentMeasurer(
|
return whatsapp.NewExperimentMeasurer(
|
||||||
*config.(*whatsapp.Config),
|
*config.(*whatsapp.Config),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user