Skip to content
Snippets Groups Projects
Commit 28e7a1de authored by Vitali Tatarintev's avatar Vitali Tatarintev Committed by Mayra Cabrera
Browse files

Add missing feature flag definition files

Add missing definition files for some feature flags
parent 8816566a
No related branches found
No related tags found
1 merge request!56746Ignore `default_enabled` value in `Feature.enabled?` [RUN ALL RSPEC] [RUN AS-IF-FOSS]
---
name: prometheus_metrics_method_instrumentation
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4304
rollout_issue_url:
milestone: '10.5'
type: ops
group:
default_enabled: false
---
name: prometheus_metrics_view_instrumentation
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4304
rollout_issue_url:
milestone: '10.5'
type: ops
group:
default_enabled: false
......@@ -57,7 +57,7 @@ def persisted_name?(feature_name)
# use `default_enabled: true` to default the flag to being `enabled`
# unless set explicitly. The default is `disabled`
# TODO: remove the `default_enabled:` and read it from the `defintion_yaml`
# check: https://gitlab.com/gitlab-org/gitlab/-/issues/30228
# check: https://gitlab.com/gitlab-org/gitlab/-/issues/271275
def enabled?(key, thing = nil, type: :development, default_enabled: false)
if check_feature_flags_definition?
if thing && !thing.respond_to?(:flipper_id)
......@@ -65,11 +65,11 @@ def enabled?(key, thing = nil, type: :development, default_enabled: false)
"The thing '#{thing.class.name}' for feature flag '#{key}' needs to include `FeatureGate` or implement `flipper_id`"
end
Feature::Definition.valid_usage!(key, type: type, default_enabled: default_enabled)
Feature::Definition.valid_usage!(key, type: type, default_enabled: :yaml)
end
# If `default_enabled: :yaml` we fetch the value from the YAML definition instead.
default_enabled = Feature::Definition.default_enabled?(key) if default_enabled == :yaml
# TODO: Remove rubocop disable comment once `default_enabled` argument is removed https://gitlab.com/gitlab-org/gitlab/-/issues/271275
default_enabled = Feature::Definition.default_enabled?(key) # rubocop:disable Lint/ShadowedArgument
# During setup the database does not exist yet. So we haven't stored a value
# for the feature yet and return the default.
......
......@@ -15,6 +15,7 @@
before do
skip_feature_flags_yaml_validation
skip_default_enabled_yaml_check
end
subject { result }
......
......@@ -128,6 +128,7 @@ def self.complexity_multiplier(args)
before do
skip_feature_flags_yaml_validation
skip_default_enabled_yaml_check
end
it 'returns false if the feature is not enabled' do
......
......@@ -183,6 +183,7 @@
before do
skip_feature_flags_yaml_validation
skip_default_enabled_yaml_check
end
context 'with feature enabled' do
......
......@@ -8,6 +8,7 @@
before do
skip_feature_flags_yaml_validation
skip_default_enabled_yaml_check
end
describe ".enabled?" do
......
......@@ -123,12 +123,35 @@
end
describe '.enabled?' do
it 'returns false for undefined feature' do
expect(described_class.enabled?(:some_random_feature_flag)).to be_falsey
let(:disabled_ff_definition) do
Feature::Definition.new(
'development/disabled_feature_flag.yml',
name: 'disabled_feature_flag',
type: 'development',
default_enabled: false
)
end
it 'returns true for undefined feature with default_enabled' do
expect(described_class.enabled?(:some_random_feature_flag, default_enabled: true)).to be_truthy
let(:enabled_ff_definition) do
Feature::Definition.new(
'development/enabled_feature_flag.yml',
name: 'enabled_feature_flag',
type: 'development',
default_enabled: true
)
end
before do
allow(Feature::Definition).to receive(:definitions) do
{
disabled_ff_definition.key => disabled_ff_definition,
enabled_ff_definition.key => enabled_ff_definition
}
end
end
it 'raises an exception for undefined feature' do
expect { described_class.enabled?(:some_random_feature_flag) }.to raise_error Feature::InvalidFeatureFlagError
end
it 'returns false for existing disabled feature in the database' do
......@@ -146,39 +169,58 @@
it { expect(described_class.send(:l1_cache_backend)).to eq(Gitlab::ProcessMemoryCache.cache_backend) }
it { expect(described_class.send(:l2_cache_backend)).to eq(Rails.cache) }
it 'caches the status in L1 and L2 caches',
:request_store, :use_clean_rails_memory_store_caching do
it 'caches the status in L1 and L2 caches', :request_store, :use_clean_rails_memory_store_caching, :aggregate_failures do
described_class.enable(:enabled_feature_flag)
flipper_key = "flipper/v1/feature/enabled_feature_flag"
flipper_features_key = 'flipper/v1/features'
flipper_feature_key = 'flipper/v1/feature/enabled_feature_flag'
expect(described_class.send(:l2_cache_backend))
.to receive(:fetch)
.once
.with(flipper_key, expires_in: 1.hour)
.and_call_original
expect(described_class.send(:l1_cache_backend))
.to receive(:fetch)
.once
.with(flipper_key, expires_in: 1.minute)
.and_call_original
allow(described_class.send(:l2_cache_backend)).to receive(:fetch).twice.and_call_original
allow(described_class.send(:l1_cache_backend)).to receive(:fetch).twice.and_call_original
2.times do
expect(described_class.enabled?(:enabled_feature_flag)).to be_truthy
end
expect(described_class.send(:l2_cache_backend)).to have_received(:fetch).with(flipper_features_key, expires_in: 1.hour)
expect(described_class.send(:l2_cache_backend)).to have_received(:fetch).with(flipper_feature_key, expires_in: 1.hour)
expect(described_class.send(:l1_cache_backend)).to have_received(:fetch).with(flipper_features_key, expires_in: 1.minute)
expect(described_class.send(:l1_cache_backend)).to have_received(:fetch).with(flipper_feature_key, expires_in: 1.minute)
end
it 'returns the default value when the database does not exist' do
fake_default = double('fake default')
it 'returns the default value when the database does not exist', :aggregate_falures do
a_feature = Feature::Definition.new(
'development/a_feature.yml',
name: 'a_feature',
type: 'development',
default_enabled: true
)
allow(Feature::Definition).to receive(:definitions) do
{ a_feature.key => a_feature }
end
expect(ActiveRecord::Base).to receive(:connection) { raise ActiveRecord::NoDatabaseError, "No database" }
expect(described_class.enabled?(:a_feature, default_enabled: fake_default)).to eq(fake_default)
expect(described_class.enabled?(:a_feature)).to eq(true)
end
context 'cached feature flag', :request_store do
context 'cached feature flag', :request_store, :use_clean_rails_memory_store_caching, :aggregate_failures do
let(:flag) { :some_feature_flag }
let(:some_feature_flag) do
Feature::Definition.new(
"development/#{flag}.yml",
name: flag.to_s,
type: 'development',
default_enabled: true
)
end
before do
allow(Feature::Definition).to receive(:definitions) do
{ some_feature_flag.key => some_feature_flag }
end
described_class.send(:flipper).memoize = false
described_class.enabled?(flag)
end
......@@ -273,63 +315,48 @@
.to raise_error(/The `type:` of/)
end
it 'when invalid default_enabled is used' do
expect { described_class.enabled?(:my_feature_flag, default_enabled: true) }
.to raise_error(/The `default_enabled:` of/)
it 'reads the default from the YAML definition' do
expect(described_class.enabled?(:my_feature_flag)).to eq(false)
end
context 'when `default_enabled: :yaml` is used in code' do
it 'reads the default from the YAML definition' do
expect(described_class.enabled?(:my_feature_flag, default_enabled: :yaml)).to eq(false)
end
context 'when YAML definition does not exist for an optional type' do
let(:optional_type) { described_class::Shared::TYPES.find { |name, attrs| attrs[:optional] }.first }
context 'when default_enabled is true in the YAML definition' do
let(:default_enabled) { true }
it 'reads the default from the YAML definition' do
expect(described_class.enabled?(:my_feature_flag, default_enabled: :yaml)).to eq(true)
context 'when in dev or test environment' do
it 'raises an error for dev' do
expect { described_class.enabled?(:non_existent_flag, type: optional_type) }
.to raise_error(
Feature::InvalidFeatureFlagError,
"The feature flag YAML definition for 'non_existent_flag' does not exist")
end
end
context 'when YAML definition does not exist for an optional type' do
let(:optional_type) { described_class::Shared::TYPES.find { |name, attrs| attrs[:optional] }.first }
context 'when in dev or test environment' do
it 'raises an error for dev' do
expect { described_class.enabled?(:non_existent_flag, type: optional_type, default_enabled: :yaml) }
.to raise_error(
Feature::InvalidFeatureFlagError,
"The feature flag YAML definition for 'non_existent_flag' does not exist")
end
context 'when in production' do
before do
allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false)
end
context 'when in production' do
context 'when database exists' do
before do
allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false)
allow(Gitlab::Database).to receive(:exists?).and_return(true)
end
context 'when database exists' do
before do
allow(Gitlab::Database).to receive(:exists?).and_return(true)
end
it 'checks the persisted status and returns false' do
expect(described_class).to receive(:get).with(:non_existent_flag).and_call_original
it 'checks the persisted status and returns false' do
expect(described_class).to receive(:get).with(:non_existent_flag).and_call_original
expect(described_class.enabled?(:non_existent_flag, type: optional_type, default_enabled: :yaml)).to eq(false)
end
expect(described_class.enabled?(:non_existent_flag, type: optional_type)).to eq(false)
end
end
context 'when database does not exist' do
before do
allow(Gitlab::Database).to receive(:exists?).and_return(false)
end
context 'when database does not exist' do
before do
allow(Gitlab::Database).to receive(:exists?).and_return(false)
end
it 'returns false without checking the status in the database' do
expect(described_class).not_to receive(:get)
it 'returns false without checking the status in the database' do
expect(described_class).not_to receive(:get)
expect(described_class.enabled?(:non_existent_flag, type: optional_type, default_enabled: :yaml)).to eq(false)
end
expect(described_class.enabled?(:non_existent_flag, type: optional_type)).to eq(false)
end
end
end
......@@ -337,13 +364,36 @@
end
end
describe '.disable?' do
it 'returns true for undefined feature' do
expect(described_class.disabled?(:some_random_feature_flag)).to be_truthy
describe '.disabled?' do
let(:disabled_ff_definition) do
Feature::Definition.new(
'development/disabled_feature_flag.yml',
name: 'disabled_feature_flag',
type: 'development',
default_enabled: false
)
end
let(:enabled_ff_definition) do
Feature::Definition.new(
'development/enabled_feature_flag.yml',
name: 'enabled_feature_flag',
type: 'development',
default_enabled: true
)
end
before do
allow(Feature::Definition).to receive(:definitions) do
{
disabled_ff_definition.key => disabled_ff_definition,
enabled_ff_definition.key => enabled_ff_definition
}
end
end
it 'returns false for undefined feature with default_enabled' do
expect(described_class.disabled?(:some_random_feature_flag, default_enabled: true)).to be_falsey
it 'raises an exception for undefined feature' do
expect { described_class.disabled?(:some_random_feature_flag) }.to raise_error Feature::InvalidFeatureFlagError
end
it 'returns true for existing disabled feature in the database' do
......
......@@ -12,6 +12,7 @@
describe '#push_frontend_feature_flag' do
before do
skip_feature_flags_yaml_validation
skip_default_enabled_yaml_check
end
it 'pushes a feature flag to the frontend' do
......
......@@ -102,6 +102,11 @@
let(:feature_name) { :some_metric_feature }
let(:metric) { call_fetch_metric_method(docstring: docstring, with_feature: feature_name) }
before do
skip_feature_flags_yaml_validation
skip_default_enabled_yaml_check
end
context 'when feature is enabled' do
before do
stub_feature_flags(feature_name => true)
......
......@@ -54,6 +54,11 @@
end
describe '.measure' do
before do
skip_feature_flags_yaml_validation
skip_default_enabled_yaml_check
end
context 'without a transaction' do
it 'returns the return value of the block' do
val = described_class.measure(:foo) { 10 }
......
......@@ -438,6 +438,8 @@
context 'when the gate value was set' do
before do
skip_feature_flags_yaml_validation
skip_default_enabled_yaml_check
Feature.enable(feature_name)
end
......
......@@ -73,6 +73,7 @@
context 'with unknown event' do
before do
skip_feature_flags_yaml_validation
skip_default_enabled_yaml_check
end
it 'returns status ok' do
......@@ -149,6 +150,7 @@
context 'with unknown event' do
before do
skip_feature_flags_yaml_validation
skip_default_enabled_yaml_check
end
it 'returns status ok' do
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment