Skip to content

Commit 8219a5b

Browse files
drewgilliesadvay26
andauthored
Migrate StatusAggregator job to MSI storage authentication (#10328)
Co-authored-by: Advay Tandon <[email protected]>
1 parent daa1375 commit 8219a5b

8 files changed

Lines changed: 73 additions & 50 deletions

File tree

src/NuGet.Jobs.Common/StorageAccountExtensions.cs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,7 @@ public static TableServiceClient CreateTableServiceClient(
141141
}
142142

143143
StorageMsiConfiguration msiConfiguration = serviceProvider.GetRequiredService<IOptions<StorageMsiConfiguration>>().Value;
144-
return CreateTableServiceClientClient(
145-
msiConfiguration,
146-
storageConnectionString);
144+
return CreateTableServiceClient(msiConfiguration, storageConnectionString);
147145
}
148146

149147
public static IRegistrationBuilder<TableServiceClient, SimpleActivatorData, SingleRegistrationStyle> RegisterTableServiceClient<TConfiguration>(
@@ -165,9 +163,7 @@ public static IRegistrationBuilder<TableServiceClient, SimpleActivatorData, Sing
165163
IOptionsSnapshot<TConfiguration> options = c.Resolve<IOptionsSnapshot<TConfiguration>>();
166164
string storageConnectionString = getConnectionString(options.Value);
167165
StorageMsiConfiguration msiConfiguration = c.Resolve<IOptions<StorageMsiConfiguration>>().Value;
168-
return CreateTableServiceClientClient(
169-
msiConfiguration,
170-
storageConnectionString);
166+
return CreateTableServiceClient(msiConfiguration, storageConnectionString);
171167
});
172168
}
173169

@@ -277,27 +273,27 @@ public static BlobServiceClientFactory CreateBlobServiceClientFactory(
277273
}
278274
}
279275

280-
private static TableServiceClient CreateTableServiceClientClient(
276+
public static TableServiceClient CreateTableServiceClient(
281277
StorageMsiConfiguration msiConfiguration,
282278
string tableStorageConnectionString)
283279
{
284280
if (msiConfiguration.UseManagedIdentity)
285281
{
282+
Uri tableEndpointUri = new Uri(tableStorageConnectionString);
283+
286284
if (string.IsNullOrWhiteSpace(msiConfiguration.ManagedIdentityClientId))
287285
{
288-
return new TableServiceClient(new Uri(tableStorageConnectionString),
289-
new DefaultAzureCredential());
286+
return new TableServiceClient(tableEndpointUri, new DefaultAzureCredential());
290287
}
291288
else
292289
{
293-
return new TableServiceClient(new Uri(tableStorageConnectionString),
294-
new ManagedIdentityCredential(msiConfiguration.ManagedIdentityClientId));
290+
return new TableServiceClient(tableEndpointUri, new ManagedIdentityCredential(msiConfiguration.ManagedIdentityClientId));
295291
}
296292
}
297293

298294
// workaround for https://github.com/Azure/azure-sdk-for-net/issues/44373
299-
tableStorageConnectionString.Replace("SharedAccessSignature=?", "SharedAccessSignature=");
300-
return new TableServiceClient(tableStorageConnectionString);
295+
var connectionString = tableStorageConnectionString.Replace("SharedAccessSignature=?", "SharedAccessSignature=");
296+
return new TableServiceClient(connectionString);
301297
}
302298
}
303299
}

src/NuGet.Services.Storage/AzureStorage.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Threading;
1010
using System.Threading.Tasks;
1111
using Azure;
12+
using Azure.Data.Tables;
1213
using Azure.Storage.Blobs;
1314
using Azure.Storage.Blobs.Models;
1415
using Azure.Storage.Blobs.Specialized;
@@ -436,10 +437,11 @@ private Uri ResolvePathedUri(string filename)
436437
return ResolveUri(Path.Combine(_path, filename));
437438
}
438439

440+
// Returns the primary blob service URI from the connection string
439441
public static Uri GetPrimaryServiceUri(string storageConnectionString)
440442
{
441443
var tempClient = new BlobServiceClient(storageConnectionString);
442-
// if _storageConnectionString has SAS token, Uri will contain SAS signature, we need to strip it
444+
// if _storageConnectionString has SAS token, Uri will contain SAS signature, we need to strip it
443445
return new Uri(tempClient.Uri.GetLeftPart(UriPartial.Path));
444446
}
445447
}

src/NuGet.Services.Storage/NuGet.Services.Storage.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10+
<PackageReference Include="Azure.Data.Tables" />
1011
<PackageReference Include="Azure.Identity" />
1112
<PackageReference Include="Azure.Storage.Blobs" />
1213
<PackageReference Include="Azure.Storage.Queues" />

src/StatusAggregator/Container/ContainerWrapper.cs

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

4+
using System.IO;
5+
using System.Text;
46
using System.Threading.Tasks;
57
using Azure.Storage.Blobs;
68

@@ -20,10 +22,13 @@ public Task CreateIfNotExistsAsync()
2022
return _container.CreateIfNotExistsAsync();
2123
}
2224

23-
public Task SaveBlobAsync(string name, string contents)
25+
public async Task SaveBlobAsync(string name, string contents)
2426
{
2527
var blob = _container.GetBlobClient(name);
26-
return blob.UploadAsync(contents);
28+
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
29+
{
30+
await blob.UploadAsync(stream, overwrite: true);
31+
}
2732
}
2833
}
29-
}
34+
}

src/StatusAggregator/Job.cs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,14 @@ private static void AddStorage(ContainerBuilder containerBuilder)
115115
{
116116
var statusStorageConnectionBuilders = new StatusStorageConnectionBuilder[]
117117
{
118-
new StatusStorageConnectionBuilder(PrimaryStorageAccountName, configuration => configuration.StorageAccount),
119-
new StatusStorageConnectionBuilder(SecondaryStorageAccountName, configuration => configuration.StorageAccountSecondary)
118+
new StatusStorageConnectionBuilder(
119+
PrimaryStorageAccountName,
120+
configuration => configuration.PrimaryStorageBlobEndpoint,
121+
configuration => configuration.PrimaryStorageTableEndpoint),
122+
new StatusStorageConnectionBuilder(
123+
SecondaryStorageAccountName,
124+
configuration => configuration.SecondaryStorageBlobEndpoint,
125+
configuration => configuration.SecondaryStorageTableEndpoint)
120126
};
121127

122128
// Add all storages to the container by name.
@@ -165,35 +171,31 @@ private static void AddStorage(ContainerBuilder containerBuilder)
165171
}
166172
}
167173

168-
private static string GetConnectionString(IComponentContext ctx, StatusStorageConnectionBuilder statusStorageConnectionBuilder)
169-
{
170-
var configuration = ctx.Resolve<StatusAggregatorConfiguration>();
171-
var connectionString = statusStorageConnectionBuilder.GetConnectionString(configuration);
172-
173-
// workaround for https://github.com/Azure/azure-sdk-for-net/issues/44373
174-
connectionString = connectionString.Replace("SharedAccessSignature=?", "SharedAccessSignature=");
175-
return connectionString;
176-
}
177-
178174
private static TableServiceClient GetTableServiceClient(IComponentContext ctx, StatusStorageConnectionBuilder statusStorageConnectionBuilder)
179175
{
180-
string connectionString = GetConnectionString(ctx, statusStorageConnectionBuilder);
181-
return new TableServiceClient(connectionString);
176+
StorageMsiConfiguration storageMsiConfiguration = ctx.Resolve<IOptionsSnapshot<StorageMsiConfiguration>>().Value;
177+
StatusAggregatorConfiguration configuration = ctx.Resolve<IOptionsSnapshot<StatusAggregatorConfiguration>>().Value;
178+
string connectionString = statusStorageConnectionBuilder.GetTableConnectionString(configuration);
179+
180+
return StorageAccountHelper.CreateTableServiceClient(storageMsiConfiguration, connectionString);
182181
}
183182

184-
private static ITableWrapper GetTableWrapper(IComponentContext ctx, TableServiceClient tableServiceClient)
183+
private static TableWrapper GetTableWrapper(IComponentContext ctx, TableServiceClient tableServiceClient)
185184
{
186185
var configuration = ctx.Resolve<StatusAggregatorConfiguration>();
187186
return new TableWrapper(tableServiceClient, configuration.TableName);
188187
}
189188

190189
private static BlobServiceClient GetBlobServiceClient(IComponentContext ctx, StatusStorageConnectionBuilder statusStorageConnectionBuilder)
191190
{
192-
string connectionString = GetConnectionString(ctx, statusStorageConnectionBuilder);
193-
return new BlobServiceClient(connectionString);
191+
StorageMsiConfiguration storageMsiConfiguration = ctx.Resolve<IOptionsSnapshot<StorageMsiConfiguration>>().Value;
192+
StatusAggregatorConfiguration configuration = ctx.Resolve<IOptionsSnapshot<StatusAggregatorConfiguration>>().Value;
193+
string connectionString = statusStorageConnectionBuilder.GetBlobConnectionString(configuration);
194+
195+
return StorageAccountHelper.CreateBlobServiceClient(storageMsiConfiguration, connectionString);
194196
}
195197

196-
private static IContainerWrapper GetContainerWrapper(IComponentContext ctx, BlobServiceClient blobServiceClient)
198+
private static ContainerWrapper GetContainerWrapper(IComponentContext ctx, BlobServiceClient blobServiceClient)
197199
{
198200
var configuration = ctx.Resolve<StatusAggregatorConfiguration>();
199201
var container = blobServiceClient.GetBlobContainerClient(configuration.ContainerName);

src/StatusAggregator/StatusAggregatorConfiguration.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System.Collections.Generic;
@@ -8,9 +8,24 @@ namespace StatusAggregator
88
public class StatusAggregatorConfiguration
99
{
1010
/// <summary>
11-
/// A connection string for the storage account to use.
11+
/// A connection string for the primary storage account's blob endpoint.
1212
/// </summary>
13-
public string StorageAccount { get; set; }
13+
public string PrimaryStorageBlobEndpoint { get; set; }
14+
15+
/// <summary>
16+
/// A connection string for the primary storage account's table endpoint.
17+
/// </summary>
18+
public string PrimaryStorageTableEndpoint { get; set; }
19+
20+
/// <summary>
21+
/// A connection string for the secondary storage account's blob endpoint.
22+
/// </summary>
23+
public string SecondaryStorageBlobEndpoint { get; set; }
24+
25+
/// <summary>
26+
/// A connection string for the secondary storage account's table endpoint.
27+
/// </summary>
28+
public string SecondaryStorageTableEndpoint { get; set; }
1429

1530
/// <summary>
1631
/// The container name to export the <see cref="ServiceStatus"/> to.
@@ -22,11 +37,6 @@ public class StatusAggregatorConfiguration
2237
/// </summary>
2338
public string TableName { get; set; }
2439

25-
/// <summary>
26-
/// A connection string for the secondary storage account to use.
27-
/// </summary>
28-
public string StorageAccountSecondary { get; set; }
29-
3040
/// <summary>
3141
/// A list of environments to filter incidents by.
3242
/// See <see cref="EnvironmentFilter"/>.

src/StatusAggregator/StatusStorageConnectionBuilder.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
@@ -17,16 +17,23 @@ public class StatusStorageConnectionBuilder
1717
public string Name { get; }
1818

1919
/// <summary>
20-
/// Describes how to access this storage account's connection string using the job's configuration.
20+
/// Describes how to access this storage account's blob connection string using the job's configuration.
2121
/// </summary>
22-
public Func<StatusAggregatorConfiguration, string> GetConnectionString { get; }
22+
public Func<StatusAggregatorConfiguration, string> GetBlobConnectionString { get; }
23+
24+
/// <summary>
25+
/// Describes how to access this storage account's table connection string using the job's configuration.
26+
/// </summary>
27+
public Func<StatusAggregatorConfiguration, string> GetTableConnectionString { get; }
2328

2429
public StatusStorageConnectionBuilder(
2530
string name,
26-
Func<StatusAggregatorConfiguration, string> getConnectionString)
31+
Func<StatusAggregatorConfiguration, string> getBlobConnectionString,
32+
Func<StatusAggregatorConfiguration, string> getTableConnectionString)
2733
{
2834
Name = name ?? throw new ArgumentNullException(nameof(name));
29-
GetConnectionString = getConnectionString ?? throw new ArgumentNullException(nameof(getConnectionString));
35+
GetBlobConnectionString = getBlobConnectionString ?? throw new ArgumentNullException(nameof(getBlobConnectionString));
36+
GetTableConnectionString = getTableConnectionString ?? throw new ArgumentNullException(nameof(getTableConnectionString));
3037
}
3138
}
3239
}

src/StatusAggregator/Table/TableWrapper.cs

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

44
using System;
@@ -35,7 +35,7 @@ public Task CreateIfNotExistsAsync()
3535
public async Task<T> RetrieveAsync<T>(string rowKey)
3636
where T : class, ITableEntity
3737
{
38-
return (await _table.GetEntityAsync<T>(TablePartitionKeys.Get<T>(), rowKey)) as T;
38+
return (await _table.GetEntityAsync<T>(TablePartitionKeys.Get<T>(), rowKey))?.Value as T;
3939
}
4040

4141
public Task InsertAsync(ITableEntity tableEntity)

0 commit comments

Comments
 (0)