forked from ooni/probe-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: mvp of conditionally enabling experiments (ooni#1355)
## Checklist - [x] I have read the [contribution guidelines](https://github.com/ooni/probe-cli/blob/master/CONTRIBUTING.md) - [x] reference issue for this pull request: ooni/probe#2553 - [x] if you changed anything related to how experiments work and you need to reflect these changes in the ooni/spec repository, please link to the related ooni/spec pull request: N/A - [x] if you changed code inside an experiment, make sure you bump its version number ## Description We have a class of experiments that I call experimental experiments. These are experiments that we just added, so we're not 100% sure about them, or experiments that are historically flaky, so we want to be defensive. Currently, riseupvpn and echechk fall into this class. The idea of this diff is to implement the functional specification described by issue ooni/probe#2553. While there, make sure we have more robust testing for the `./internal/registry` package. While there, make sure package creation logic lives in `./internal/registry` rather than in `./internal/engine`.
- Loading branch information
1 parent
5d4435d
commit c395c75
Showing
37 changed files
with
590 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,15 +11,13 @@ import ( | |
"sync/atomic" | ||
|
||
"github.com/ooni/probe-cli/v3/internal/bytecounter" | ||
"github.com/ooni/probe-cli/v3/internal/checkincache" | ||
"github.com/ooni/probe-cli/v3/internal/enginelocate" | ||
"github.com/ooni/probe-cli/v3/internal/enginenetx" | ||
"github.com/ooni/probe-cli/v3/internal/engineresolver" | ||
"github.com/ooni/probe-cli/v3/internal/kvstore" | ||
"github.com/ooni/probe-cli/v3/internal/model" | ||
"github.com/ooni/probe-cli/v3/internal/platform" | ||
"github.com/ooni/probe-cli/v3/internal/probeservices" | ||
"github.com/ooni/probe-cli/v3/internal/registry" | ||
"github.com/ooni/probe-cli/v3/internal/runtimex" | ||
"github.com/ooni/probe-cli/v3/internal/tunnel" | ||
"github.com/ooni/probe-cli/v3/internal/version" | ||
|
@@ -406,16 +404,6 @@ var ErrAlreadyUsingProxy = errors.New( | |
// for the experiment with the given name, or an error if | ||
// there's no such experiment with the given name | ||
func (s *Session) NewExperimentBuilder(name string) (model.ExperimentBuilder, error) { | ||
name = registry.CanonicalizeExperimentName(name) | ||
switch { | ||
case name == "web_connectivity" && checkincache.GetFeatureFlag(s.kvStore, "webconnectivity_0.5"): | ||
// use LTE rather than the normal webconnectivity when the | ||
// feature flag has been set through the check-in API | ||
s.Logger().Infof("using webconnectivity LTE") | ||
name = "[email protected]" | ||
default: | ||
// nothing | ||
} | ||
eb, err := newExperimentBuilder(s, name) | ||
if err != nil { | ||
return nil, err | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,9 +7,11 @@ package registry | |
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"reflect" | ||
"strconv" | ||
|
||
"github.com/ooni/probe-cli/v3/internal/checkincache" | ||
"github.com/ooni/probe-cli/v3/internal/model" | ||
"github.com/ooni/probe-cli/v3/internal/strcasex" | ||
) | ||
|
@@ -22,6 +24,9 @@ type Factory struct { | |
// config contains the experiment's config. | ||
config any | ||
|
||
// enabledByDefault indicates whether this experiment is enabled by default. | ||
enabledByDefault bool | ||
|
||
// inputPolicy contains the experiment's InputPolicy. | ||
inputPolicy model.InputPolicy | ||
|
||
|
@@ -218,12 +223,65 @@ func CanonicalizeExperimentName(name string) string { | |
// ErrNoSuchExperiment indicates a given experiment does not exist. | ||
var ErrNoSuchExperiment = errors.New("no such experiment") | ||
|
||
// ErrRequiresForceEnable is returned for experiments that are not enabled by default and are also | ||
// not enabled by the most recent check-in API call. | ||
var ErrRequiresForceEnable = errors.New("experiment not enabled by check-in API") | ||
|
||
const experimentDisabledByCheckInWarning = `experiment '%s' is not enabled by default and the | ||
most recent check-in API call did not enable this experiment as well. You can bypass this restriction | ||
by setting the OONI_FORCE_ENABLE_EXPERIMENT environment variable to the string "1". On Unix like | ||
systems, you can use 'export OONI_FORCE_ENABLE_EXPERIMENT=1' to set this environment variable.` | ||
|
||
// OONI_FORCE_ENABLE_EXPERIMENT is the name of the environment variable you should set to "1" | ||
// to bypass the algorithm preventing disabled by default experiments to be instantiated. | ||
const OONI_FORCE_ENABLE_EXPERIMENT = "OONI_FORCE_ENABLE_EXPERIMENT" | ||
|
||
// NewFactory creates a new Factory instance. | ||
func NewFactory(name string) (*Factory, error) { | ||
func NewFactory(name string, kvStore model.KeyValueStore, logger model.Logger) (*Factory, error) { | ||
// Make sure we are deadling with the canonical experiment name. Historically MK used | ||
// names such as WebConnectivity and we want to continue supporting this use case. | ||
name = CanonicalizeExperimentName(name) | ||
|
||
// Handle A/B testing where we dynamically choose LTE for some users. The current policy | ||
// only relates to a few users to collect data. | ||
// | ||
// TODO(https://github.com/ooni/probe/issues/2555): perform the actual comparison | ||
// and improve the LTE implementation so that we can always use it. See the actual | ||
// issue test for additional details on this planned A/B test. | ||
switch { | ||
case name == "web_connectivity" && checkincache.GetFeatureFlag(kvStore, "webconnectivity_0.5"): | ||
// use LTE rather than the normal webconnectivity when the | ||
// feature flag has been set through the check-in API | ||
logger.Infof("using webconnectivity LTE") | ||
name = "[email protected]" | ||
|
||
default: | ||
// nothing | ||
} | ||
|
||
// Obtain the factory for the canonical name. | ||
factory := AllExperiments[name] | ||
if factory == nil { | ||
return nil, fmt.Errorf("%w: %s", ErrNoSuchExperiment, name) | ||
} | ||
return factory, nil | ||
|
||
// Some experiments are not enabled by default. To enable them we use | ||
// the cached check-in response or an environment variable. | ||
// | ||
// Note: check-in flags expire after 24h. | ||
// | ||
// TODO(https://github.com/ooni/probe/issues/2554): we need to restructure | ||
// how we run experiments to make sure check-in flags are always fresh. | ||
if factory.enabledByDefault { | ||
return factory, nil // enabled by default | ||
} | ||
if os.Getenv(OONI_FORCE_ENABLE_EXPERIMENT) == "1" { | ||
return factory, nil // enabled by environment variable | ||
} | ||
if checkincache.ExperimentEnabled(kvStore, name) { | ||
return factory, nil // enabled by check-in | ||
} | ||
|
||
logger.Warnf(experimentDisabledByCheckInWarning, name) | ||
return nil, fmt.Errorf("%s: %w", name, ErrRequiresForceEnable) | ||
} |
Oops, something went wrong.