diff options
Diffstat (limited to 'lib/gitlab/experiment/rollout/feature.rb')
-rw-r--r-- | lib/gitlab/experiment/rollout/feature.rb | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/lib/gitlab/experiment/rollout/feature.rb b/lib/gitlab/experiment/rollout/feature.rb new file mode 100644 index 00000000000..5a14e3c272e --- /dev/null +++ b/lib/gitlab/experiment/rollout/feature.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module Gitlab + class Experiment + module Rollout + class Feature < Percent + # For this rollout strategy to consider an experiment as enabled, we + # must: + # + # - have a feature flag yaml file that declares it. + # - be in an environment that permits it. + # - not have rolled out the feature flag at all (no percent of actors, + # no inclusions, etc.) + def enabled? + return false if ::Feature::Definition.get(feature_flag_name).nil? + return false unless Gitlab.dev_env_or_com? + + ::Feature.get(feature_flag_name).state != :off # rubocop:disable Gitlab/AvoidFeatureGet + end + + # For assignment we first check to see if our feature flag is enabled + # for "self". This is done by calling `#flipper_id` (used behind the + # scenes by `Feature`). By default this is our `experiment.id` (or more + # specifically, the context key, which is an anonymous SHA generated + # using the details of an experiment. + # + # If the `Feature.enabled?` check is false, we return nil implicitly, + # which will assign the control. Otherwise we call super, which will + # assign a variant evenly, or based on our provided distribution rules. + def execute_assigment + super if ::Feature.enabled?(feature_flag_name, self, type: :experiment, default_enabled: :yaml) + end + + # NOTE: There's a typo in the name of this method that we'll fix up. + alias_method :execute_assignment, :execute_assigment + + # This is what's provided to the `Feature.enabled?` call that will be + # used to determine experiment inclusion. An experiment may provide an + # override for this method to make the experiment work on user, group, + # or projects. + # + # For example, when running an experiment on a project, you could make + # the experiment assignable by project (using chatops) by implementing + # a `flipper_id` method in the experiment: + # + # def flipper_id + # context.project.flipper_id + # end + # + # Or even cleaner, simply delegate it: + # + # delegate :flipper_id, to: -> { context.project } + def flipper_id + return experiment.flipper_id if experiment.respond_to?(:flipper_id) + + "Experiment;#{id}" + end + + private + + def feature_flag_name + experiment.name.tr('/', '_') + end + end + end + end +end |