Skip to content

Commit 8b6fc25

Browse files
authored
Adds support to create TableServiceClient from JobHost configuration (#10891)
1 parent b0f9836 commit 8b6fc25

File tree

4 files changed

+138
-7
lines changed

4 files changed

+138
-7
lines changed

src/WebJobs.Script/StorageProvider/HostAzureTableStorageProvider.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
using System;
55
using Azure.Data.Tables;
6+
using Microsoft.Azure.WebJobs.Script.Config;
7+
using Microsoft.Azure.WebJobs.Script.Configuration;
68
using Microsoft.Extensions.Azure;
79
using Microsoft.Extensions.Configuration;
810
using Microsoft.Extensions.Logging;
@@ -15,11 +17,32 @@ internal sealed class HostAzureTableStorageProvider : IAzureTableStorageProvider
1517
private readonly TableServiceClientProvider _tableServiceClientProvider;
1618
private readonly IConfiguration _configuration;
1719

18-
public HostAzureTableStorageProvider(IConfiguration configuration, ILogger<HostAzureTableStorageProvider> logger, AzureComponentFactory componentFactory, AzureEventSourceLogForwarder logForwarder)
20+
public HostAzureTableStorageProvider(IScriptHostManager scriptHostManager, IConfiguration configuration, ILogger<HostAzureTableStorageProvider> logger, AzureComponentFactory componentFactory, AzureEventSourceLogForwarder logForwarder)
1921
{
2022
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
2123
_tableServiceClientProvider = new TableServiceClientProvider(componentFactory, logForwarder);
22-
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
24+
25+
if (configuration is null)
26+
{
27+
throw new ArgumentNullException(nameof(configuration));
28+
}
29+
30+
if (FeatureFlags.IsEnabled(ScriptConstants.FeatureFlagDisableMergedWebHostScriptHostConfiguration))
31+
{
32+
_configuration = configuration;
33+
}
34+
else
35+
{
36+
if (scriptHostManager == null)
37+
{
38+
throw new ArgumentNullException(nameof(scriptHostManager));
39+
}
40+
41+
_configuration = new ConfigurationBuilder()
42+
.Add(new ActiveHostConfigurationSource(scriptHostManager))
43+
.AddConfiguration(configuration)
44+
.Build();
45+
}
2346
}
2447

2548
public bool TryCreateTableServiceClient(string connection, out TableServiceClient client)

test/WebJobs.Script.Tests.Integration/Scale/TableStorageScaleMetricsRepositoryTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public TableStorageScaleMetricsRepositoryTests()
4343
_loggerProvider = new TestLoggerProvider();
4444
ILoggerFactory loggerFactory = new LoggerFactory();
4545
loggerFactory.AddProvider(_loggerProvider);
46-
_azureTableStorageProvider = TestHelpers.GetAzureTableStorageProvider(configuration, new TestEnvironment());
46+
_azureTableStorageProvider = TestHelpers.GetAzureTableStorageProvider(configuration, environment: new TestEnvironment());
4747
// Allow for up to 30 seconds of creation retries for tests due to slow table deletes
4848
_repository = new TableStorageScaleMetricsRepository(_hostIdProviderMock.Object, new OptionsWrapper<ScaleOptions>(_scaleOptions), loggerFactory, _azureTableStorageProvider, 60);
4949

test/WebJobs.Script.Tests.Integration/Storage/AzureTableStorageProviderTests.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Text;
67
using System.Threading.Tasks;
78
using Azure.Data.Tables;
89
using Microsoft.Extensions.Configuration;
@@ -33,6 +34,48 @@ public async Task TryCreateHostingTableServiceClient_ConnectionInWebHostConfigur
3334
await VerifyTableServiceClientAvailable(client);
3435
}
3536

37+
[Fact]
38+
public async Task TryCreateHostingTableServiceClient_ConnectionInJobHostConfiguration()
39+
{
40+
var testConfiguration = TestHelpers.GetTestConfiguration();
41+
var testData = new Dictionary<string, string>(StringComparer.Ordinal)
42+
{
43+
{ "ConnectionStrings:AzureWebJobsStorage", testConfiguration.GetWebJobsConnectionString(StorageConnection) },
44+
{ "AzureWebJobsStorage", "" }
45+
};
46+
47+
var webHostConfiguration = new ConfigurationBuilder().Build();
48+
var jobHostConfiguration = new ConfigurationBuilder()
49+
.AddInMemoryCollection(testData)
50+
.Build();
51+
52+
var azureTableStorageProvider = TestHelpers.GetAzureTableStorageProvider(webHostConfiguration, jobHostConfiguration);
53+
azureTableStorageProvider.TryCreateHostingTableServiceClient(out TableServiceClient client);
54+
await VerifyTableServiceClientAvailable(client);
55+
}
56+
57+
[Theory]
58+
[InlineData("AzureWebJobsStorage:accountName", "storage")]
59+
[InlineData("AzureWebJobsStorage:AccountName", "storage")]
60+
[InlineData("AzureWebJobsStorage:tableServiceUri", "https://mytable.functions")]
61+
[InlineData("AzureWebJobsStorage:TableServiceUri", "https://mytable.functions")]
62+
public void TryCreateHostingTableServiceClient_IdentityBasedConnections(string key, string value)
63+
{
64+
var testData = new Dictionary<string, string>(StringComparer.Ordinal)
65+
{
66+
{ key, value }
67+
};
68+
69+
// Using a case sensitive data source to match the ScriptEnvironmentVariablesConfigurationSource behavior on Linux (case-sensitive env vars)
70+
var webHostConfiguration = new ConfigurationBuilder().Build();
71+
var jobHostConfiguration = new ConfigurationBuilder()
72+
.Add(new CaseSensitiveConfigurationSource(testData))
73+
.Build();
74+
75+
var azureTableStorageProvider = TestHelpers.GetAzureTableStorageProvider(webHostConfiguration, jobHostConfiguration);
76+
Assert.True(azureTableStorageProvider.TryCreateHostingTableServiceClient(out _));
77+
}
78+
3679
[Fact]
3780
public void TryCreateHostingTableServiceClient_NoConnectionThrowsException()
3881
{
@@ -45,6 +88,36 @@ public void TryCreateHostingTableServiceClient_NoConnectionThrowsException()
4588
Assert.False(azureTableStorageProvider.TryCreateTableServiceClient(ConnectionStringNames.Storage, out TableServiceClient blobServiceClient));
4689
}
4790

91+
[Theory]
92+
[InlineData("ConnectionStrings:AzureWebJobsStorage1")]
93+
[InlineData("AzureWebJobsStorage1")]
94+
[InlineData("Storage1")]
95+
public void TestAzureBlobStorageProvider_JobHostConfigurationWinsConflict(string connectionName)
96+
{
97+
var bytes = Encoding.UTF8.GetBytes("someKey");
98+
var encodedString = Convert.ToBase64String(bytes);
99+
100+
var webHostConfigData = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
101+
{
102+
{ connectionName, $"DefaultEndpointsProtocol=https;AccountName=webHostAccount;AccountKey={encodedString};EndpointSuffix=core.windows.net" },
103+
};
104+
var webHostConfiguration = new ConfigurationBuilder()
105+
.AddInMemoryCollection(webHostConfigData)
106+
.Build();
107+
108+
var jobHostConfigData = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
109+
{
110+
{ connectionName, $"DefaultEndpointsProtocol=https;AccountName=jobHostAccount;AccountKey={encodedString};EndpointSuffix=core.windows.net" },
111+
};
112+
var jobHostConfiguration = new ConfigurationBuilder()
113+
.AddInMemoryCollection(jobHostConfigData)
114+
.Build();
115+
116+
var azureTableStorageProvider = TestHelpers.GetAzureTableStorageProvider(webHostConfiguration, jobHostConfiguration);
117+
Assert.True(azureTableStorageProvider.TryCreateTableServiceClient("Storage1", out TableServiceClient client));
118+
Assert.Equal("webHostAccount", client.AccountName, ignoreCase: true);
119+
}
120+
48121
private async Task VerifyTableServiceClientAvailable(TableServiceClient client)
49122
{
50123
try
@@ -57,5 +130,32 @@ private async Task VerifyTableServiceClientAvailable(TableServiceClient client)
57130
Assert.False(true, $"Could not establish connection to TableService. {e}");
58131
}
59132
}
133+
134+
private class CaseSensitiveConfigurationSource : IConfigurationSource
135+
{
136+
private readonly Dictionary<string, string> _data;
137+
public CaseSensitiveConfigurationSource(Dictionary<string, string> data)
138+
{
139+
_data = data;
140+
}
141+
public IConfigurationProvider Build(IConfigurationBuilder builder)
142+
{
143+
return new CaseSensitiveConfigurationProvider(_data);
144+
}
145+
}
146+
147+
private class CaseSensitiveConfigurationProvider : ConfigurationProvider
148+
{
149+
private readonly Dictionary<string, string> _data;
150+
public CaseSensitiveConfigurationProvider(Dictionary<string, string> data)
151+
{
152+
_data = data;
153+
}
154+
public override void Load()
155+
{
156+
// _data might be already case-insensitive but we want to ensure it's case-sensitive
157+
Data = new Dictionary<string, string>(_data, StringComparer.Ordinal);
158+
}
159+
}
60160
}
61161
}

test/WebJobs.Script.Tests.Shared/TestHelpers.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -504,29 +504,37 @@ public static IServiceCollection AddTestAzureBlobStorageProvider(IServiceCollect
504504
return services;
505505
}
506506

507-
public static IAzureTableStorageProvider GetAzureTableStorageProvider(IConfiguration configuration, IEnvironment environment = default)
507+
public static IAzureTableStorageProvider GetAzureTableStorageProvider(IConfiguration webHostConfiguration, IConfiguration jobHostConfiguration = null, IEnvironment environment = default)
508508
{
509509
environment ??= new TestEnvironment();
510510

511511
IHost tempHost = new HostBuilder()
512512
.ConfigureServices(services =>
513513
{
514-
AddTestAzureTableStorageProvider(services, configuration, environment);
514+
AddTestAzureTableStorageProvider(services, jobHostConfiguration ?? webHostConfiguration, environment);
515515
})
516516
.ConfigureAppConfiguration(c =>
517517
{
518-
c.AddConfiguration(configuration);
518+
c.AddConfiguration(webHostConfiguration);
519519
})
520520
.Build();
521521

522522
var azureTableStorageProvider = tempHost.Services.GetRequiredService<IAzureTableStorageProvider>();
523523
return azureTableStorageProvider;
524524
}
525525

526-
public static IServiceCollection AddTestAzureTableStorageProvider(IServiceCollection services, IConfiguration configuration, IEnvironment environment)
526+
public static IServiceCollection AddTestAzureTableStorageProvider(IServiceCollection services, IConfiguration configuration, IEnvironment environment, IScriptHostManager scriptHostManager = null)
527527
{
528528
// Adds necessary Azure services to create clients
529529
services.AddAzureClientsCore();
530+
531+
if (scriptHostManager == null)
532+
{
533+
scriptHostManager = new TestScriptHostService(configuration);
534+
}
535+
536+
services.AddSingleton<IScriptHostManager>(scriptHostManager);
537+
530538
services.AddSingleton<IAzureTableStorageProvider, HostAzureTableStorageProvider>();
531539

532540
return services;

0 commit comments

Comments
 (0)