Skip to content

Commit da6e7ed

Browse files
committed
First class FeatureFlags pattern
1 parent 3106046 commit da6e7ed

File tree

8 files changed

+73
-19
lines changed

8 files changed

+73
-19
lines changed

src/WebJobs.Script.WebHost/Security/DefaultKeyValueConverterFactory.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Linq;
88
using System.Text;
99
using System.Threading.Tasks;
10+
using Microsoft.Azure.WebJobs.Script.Config;
1011
using static Microsoft.Azure.Web.DataProtection.Constants;
1112

1213
namespace Microsoft.Azure.WebJobs.Script.WebHost
@@ -28,7 +29,7 @@ private static bool IsEncryptionSupported()
2829
// We're temporarily placing encryption behind a feature toggle until
2930
// other consumers (e.g. portal) are updated to work with it.
3031
// TODO: Remove this
31-
return string.Equals(Environment.GetEnvironmentVariable("AzureWebJobsEncryptionEnabled"), "true", StringComparison.OrdinalIgnoreCase);
32+
return FeatureFlags.IsEnabled("SecretEncryption");
3233
}
3334

3435
return Environment.GetEnvironmentVariable(AzureWebsiteLocalEncryptionKey) != null;

src/WebJobs.Script.WebHost/Security/Serialization/ScriptSecretSerializer.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Linq;
77
using System.Text;
88
using System.Threading.Tasks;
9+
using Microsoft.Azure.WebJobs.Script.Config;
910
using Newtonsoft.Json;
1011
using Newtonsoft.Json.Linq;
1112

@@ -23,8 +24,9 @@ internal static IScriptSecretSerializer DefaultSerializer
2324
{
2425
get
2526
{
26-
// This is temporarily behind a feature flag. Once other clients are able to work with the new version, this should be removed.
27-
if (string.Equals(Environment.GetEnvironmentVariable("AzureWebJobsEnableMultiKey"), "true", StringComparison.OrdinalIgnoreCase))
27+
// This is temporarily behind a feature flag. Once other clients are able to
28+
// work with the new version, this should be removed.
29+
if (FeatureFlags.IsEnabled("MultiKey"))
2830
{
2931
return _secretFormatters.Last();
3032
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Linq;
6+
7+
namespace Microsoft.Azure.WebJobs.Script.Config
8+
{
9+
/// <summary>
10+
/// Provides a way to determine whether certain runtime features are enabled or disabled
11+
/// based on a feature flags app setting.
12+
/// </summary>
13+
public static class FeatureFlags
14+
{
15+
public static bool IsEnabled(string name)
16+
{
17+
string featureFlags = Environment.GetEnvironmentVariable("AzureWebJobsFeatureFlags");
18+
if (!string.IsNullOrEmpty(featureFlags))
19+
{
20+
string[] flags = featureFlags.Split(',');
21+
return flags.Contains(name, StringComparer.OrdinalIgnoreCase);
22+
}
23+
24+
return false;
25+
}
26+
}
27+
}

src/WebJobs.Script/WebJobs.Script.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@
336336
<Compile Include="Binding\TableBinding.cs" />
337337
<Compile Include="Binding\WebJobsCoreScriptBindingProvider.cs" />
338338
<Compile Include="Binding\SimpleValueProvider.cs" />
339+
<Compile Include="Config\FeatureFlags.cs" />
339340
<Compile Include="Description\Binding\Cardinality.cs" />
340341
<Compile Include="Description\DotNet\DiagnosticExtensions.cs" />
341342
<Compile Include="Description\DotNet\CSharp\Analyzers\InvalidFileMetadataReferenceAnalyzer.cs" />
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.Azure.WebJobs.Script.Config;
6+
using Xunit;
7+
8+
namespace Microsoft.Azure.WebJobs.Script.Tests
9+
{
10+
public class FeatureFlagsTests
11+
{
12+
public FeatureFlagsTests()
13+
{
14+
Environment.SetEnvironmentVariable("AzureWebJobsFeatureFlags", "AwesomeFeature,RadFeature");
15+
}
16+
17+
[Theory]
18+
[InlineData("AwesomeFeature", true)]
19+
[InlineData("AWESOMEFEATURE", true)]
20+
[InlineData("radfeature", true)]
21+
[InlineData("brokenfeature", false)]
22+
public void IsEnabled_ReturnsExpectedValue(string name, bool expected)
23+
{
24+
Assert.Equal(FeatureFlags.IsEnabled(name), expected);
25+
}
26+
}
27+
}

test/WebJobs.Script.Tests/Security/ScriptSecretSerializerTests.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

4-
using System;
5-
using System.Collections.Generic;
6-
using System.Linq;
7-
using System.Text;
8-
using System.Threading.Tasks;
94
using Microsoft.Azure.WebJobs.Script.WebHost;
105
using Xunit;
116

@@ -16,7 +11,7 @@ public class ScriptSecretSerializerTests
1611
[Fact]
1712
public void DefaultSerializer_WhenMultiKeyFeatureIsEnabled_ReturnsV1Serializer()
1813
{
19-
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsEnableMultiKey", "true"))
14+
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
2015
{
2116
Assert.Equal(typeof(ScriptSecretSerializerV1), ScriptSecretSerializer.DefaultSerializer?.GetType());
2217
}
@@ -25,7 +20,7 @@ public void DefaultSerializer_WhenMultiKeyFeatureIsEnabled_ReturnsV1Serializer()
2520
[Fact]
2621
public void DefaultSerializer_WhenMultiKeyFeatureIsDisabled_ReturnsV0Serializer()
2722
{
28-
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsEnableMultiKey", "false"))
23+
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", string.Empty))
2924
{
3025
Assert.Equal(typeof(ScriptSecretSerializerV0), ScriptSecretSerializer.DefaultSerializer?.GetType());
3126
}

test/WebJobs.Script.Tests/Security/SecretManagerTests.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public void GetFunctionSecrets_UpdatesStaleSecrets()
8787
var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
8888
try
8989
{
90-
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsEnableMultiKey", "true"))
90+
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
9191
{
9292
Directory.CreateDirectory(secretsPath);
9393
string functionSecretsJson =
@@ -134,7 +134,7 @@ public void GetHostSecrets_UpdatesStaleSecrets()
134134
var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
135135
try
136136
{
137-
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsEnableMultiKey", "true"))
137+
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
138138
{
139139
Directory.CreateDirectory(secretsPath);
140140
string hostSecretsJson =
@@ -188,7 +188,7 @@ public void GetHostSecrets_WhenNoHostSecretFileExists_GeneratesSecretsAndPersist
188188
var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
189189
try
190190
{
191-
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsEnableMultiKey", "true"))
191+
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
192192
{
193193
Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false);
194194

@@ -221,7 +221,7 @@ public void GetFunctionSecrets_WhenNoSecretFileExists_ReturnsEmptySecretsAndDoes
221221
var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
222222
try
223223
{
224-
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsEnableMultiKey", "true"))
224+
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
225225
{
226226
Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false);
227227

@@ -250,7 +250,7 @@ public void AddOrUpdateFunctionSecrets_WithFunctionNameAndNoSecret_GeneratesFunc
250250
var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
251251
try
252252
{
253-
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsEnableMultiKey", "true"))
253+
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
254254
{
255255
Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false);
256256

@@ -282,7 +282,7 @@ public void AddOrUpdateFunctionSecrets_WithFunctionNameAndProvidedSecret_UsesSec
282282
var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
283283
try
284284
{
285-
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsEnableMultiKey", "true"))
285+
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
286286
{
287287
Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false);
288288

@@ -314,7 +314,7 @@ public void AddOrUpdateFunctionSecrets_WithNoFunctionNameAndProvidedSecret_UsesS
314314
var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
315315
try
316316
{
317-
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsEnableMultiKey", "true"))
317+
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
318318
{
319319
Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false);
320320

@@ -350,7 +350,7 @@ public void SetMasterKey_WithProvidedKey_UsesProvidedKeyAndPersistsFile()
350350
string testSecret = "abcde0123456789abcde0123456789abcde0123456789";
351351
try
352352
{
353-
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsEnableMultiKey", "true"))
353+
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
354354
{
355355
Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false);
356356

@@ -383,7 +383,7 @@ public void SetMasterKey_WithoutProvidedKey_GeneratesKeyAndPersistsFile()
383383
var secretsPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
384384
try
385385
{
386-
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsEnableMultiKey", "true"))
386+
using (var variables = new TestScopedEnvironmentVariables("AzureWebJobsFeatureFlags", "MultiKey"))
387387
{
388388
Mock<IKeyValueConverterFactory> mockValueConverterFactory = GetConverterFactoryMock(false);
389389

test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@
421421
<Compile Include="EndToEndTestsBase.cs" />
422422
<Compile Include="ApiHub\FakeTabularConnectorAdapter.cs" />
423423
<Compile Include="ExtensionSharedAssemblyProviderTests.cs" />
424+
<Compile Include="FeatureFlagsTests.cs" />
424425
<Compile Include="FileTraceWriterTests.cs" />
425426
<Compile Include="FSharpEndToEndTests.cs" />
426427
<Compile Include="FunctionBindingTests.cs" />

0 commit comments

Comments
 (0)