Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConfigurationFeatureDefinitionProvider ignores False when there are exists RequirementType #521

Open
DenomikoN opened this issue Dec 5, 2024 · 1 comment

Comments

@DenomikoN
Copy link

Faced with the issue when due to the overriding configuration ConfigurationFeatureDefinitionProvider will falsely enable a feature flag.
Example setup:
appsettings.json

{
  "FeatureManagement":{
    "FeatureA": {
       "RequirementType": "All",
       "EnabledFor": [{
          "Name":"AlwaysOn"
       }]
    }
  }
}

Overriden by production configuration:
appsettings.production.json

{
  "FeatureManagement:FeatureA": "False" // Disable feature
}

That produces the following configuration key-values pairs:

["FeatureManagement:FeatureA"] = "False",
["FeatureManagement:FeatureA:RequirementType"] = "All",
["FeatureManagement:FeatureA:EnabledFor:0:Name"] = "AlwaysOn"

Expected result: feature manager returns False for IsEnabledAsync("FeatureA")
Test example:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.FeatureManagement;

namespace TestProject
{
    public class Tests
    {
        [Test]
        public async Task FeatureWithDirectValueMustBeDisabled()
        {
            var configKeys = new Dictionary<string, string?>
            {
                ["FeatureManagement:FeatureA"] = "False",
                ["FeatureManagement:FeatureA:RequirementType"] = "All",
                ["FeatureManagement:FeatureA:EnabledFor:0:Name"] = "AlwaysOn"
            };
            var cfg = new ConfigurationBuilder()
                            .AddInMemoryCollection(configKeys)
                            .Build();

            var sc = new ServiceCollection();
            sc.AddFeatureManagement();
            sc.AddSingleton<IConfiguration>(cfg);

            var serviceProvider = sc.BuildServiceProvider();

            var featureManager = serviceProvider.GetRequiredService<IFeatureManager>();

            var featureState = await featureManager.IsEnabledAsync("FeatureA");

            Assert.IsFalse(featureState, "FeatureA must be disabled as it's Value is False");
        }
    }
}

Actual result:: featureManager.IsEnabledAsync("FeatureA") returns True

Root cause: ConfigurationFeatureDefinitionProvider doesn't distinguish if there is an actual False value set or value can't be parsed in the method ParseDotnetSchemaFeatureDefinition:

 61: if (!string.IsNullOrEmpty(val) && bool.TryParse(val, out bool result) && result)
@zhiyuanliang-ms
Copy link
Contributor

Hi, @DenomikoN Sorry for the inconvenience. I think the key issue here is that when merging different configuration source, the IConfiguration system will produce the below configuration

["FeatureManagement:FeatureA"] = "False",
["FeatureManagement:FeatureA:RequirementType"] = "All",
["FeatureManagement:FeatureA:EnabledFor:0:Name"] = "AlwaysOn"

This should not be a valid feature flag declaration.

To work around with the current behavior of IConfiguration, I suggest you switch to our new Microsoft feature management schema (json schema is here)
Using this schema can kind of mitigate the merging configuration issue.

From our side, the root cause you mentioned is correct. Do you think when ConfigurationFeatureDefinitionProvider meets some feature flag declaration which is invalid or cannot be parsed, it should throw exception or log warning?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants