Skip to content

Commit e4f24b8

Browse files
authored
Use Marain.TenantManagement v3 (#404)
1 parent 5c4b52b commit e4f24b8

39 files changed

Lines changed: 3644 additions & 5936 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,4 +337,6 @@ coverage.cobertura.xml
337337
/.vscode
338338

339339
appsettings.json
340-
*.feature.cs
340+
*.feature.cs
341+
*.ncrunchproject
342+
*.ncrunchsolution

GitVersion.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ branches:
2020
- feature
2121
- support
2222
- hotfix
23-
next-version: "2.0"
23+
next-version: "3.0"
2424

Solutions/Marain.Operations.Abstractions/Marain.Operations.Abstractions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<PrivateAssets>all</PrivateAssets>
2121
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2222
</PackageReference>
23-
<PackageReference Include="Menes.Abstractions" Version="3.1.4" />
23+
<PackageReference Include="Menes.Abstractions" Version="4.0.0" />
2424
</ItemGroup>
2525

2626
</Project>

Solutions/Marain.Operations.Abstractions/packages.lock.json

Lines changed: 117 additions & 115 deletions
Large diffs are not rendered by default.

Solutions/Marain.Operations.Api.Specs/Bindings/OperationsContainerBindings.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ public static void PerFeatureContainerSetup(FeatureContext featureContext)
4545
services.AddJsonNetDateTimeOffsetToIso8601AndUnixTimeConverter();
4646
services.AddSingleton<JsonConverter>(new StringEnumConverter(new CamelCaseNamingStrategy()));
4747

48+
services.AddAzureBlobStorageClientSourceFromDynamicConfiguration();
49+
4850
// Tenancy service client.
4951
TenancyClientOptions tenancyConfiguration = config.GetSection("TenancyClient").Get<TenancyClientOptions>();
5052

@@ -64,7 +66,7 @@ public static void PerFeatureContainerSetup(FeatureContext featureContext)
6466
services.AddMicrosoftRestAdapterForServiceIdentityAccessTokenSource();
6567

6668
// Marain tenancy management, required to create transient client/service tenants.
67-
services.AddMarainTenantManagement();
69+
services.AddMarainTenantManagementForBlobStorage();
6870
});
6971
}
7072

Solutions/Marain.Operations.Api.Specs/Bindings/TransientTenantBindings.cs

Lines changed: 13 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,19 @@
44

55
namespace Marain.Operations.Api.Specs.Bindings
66
{
7-
using System;
87
using System.Threading.Tasks;
98

10-
using Azure.Storage.Blobs;
9+
using BoDi;
1110

12-
using Corvus.Azure.Cosmos.Tenancy;
13-
using Corvus.Azure.Storage.Tenancy;
14-
using Corvus.Storage.Azure.BlobStorage.Tenancy;
11+
using Corvus.Storage.Azure.BlobStorage;
1512
using Corvus.Tenancy;
1613
using Corvus.Testing.AzureFunctions;
1714
using Corvus.Testing.AzureFunctions.SpecFlow;
1815
using Corvus.Testing.SpecFlow;
1916

20-
using Marain.Operations.Storage.Blob;
21-
using Marain.TenantManagement.EnrollmentConfiguration;
17+
using Marain.Operations.Specs;
2218
using Marain.TenantManagement.Testing;
2319

24-
using Microsoft.Extensions.Configuration;
25-
using Microsoft.Extensions.DependencyInjection;
26-
using Microsoft.Extensions.Logging;
27-
2820
using TechTalk.SpecFlow;
2921

3022
/// <summary>
@@ -36,85 +28,36 @@ public static class TransientTenantBindings
3628
public const string TransientServiceTenantIdFeatureContextKey = "TransientServiceTenantId";
3729

3830
/// <summary>
39-
/// Creates a new <see cref="ITenant"/> for the current feature, adding a test <see cref="CosmosConfiguration"/>
31+
/// Creates a new <see cref="ITenant"/> for the current feature, adding a test <see cref="BlobContainerConfiguration"/>
4032
/// to the tenant data.
4133
/// </summary>
4234
/// <param name="featureContext">The current <see cref="FeatureContext"/>.</param>
35+
/// <param name="objectContainer">The SpecFlow DI container.</param>
4336
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
4437
/// <remarks>
45-
/// The newly created tenant is added to the <see cref="FeatureContext"/>. Access it via the helper methods
46-
/// <see cref="GetTransientTenant(FeatureContext)"/> or <see cref="GetTransientTenantId(FeatureContext)"/>.
38+
/// The newly created client and service tenants are available to tests via DI through the
39+
/// <see cref="OperationsServiceTestTenants"/> type.
4740
/// </remarks>
4841
[BeforeFeature(Order = BindingSequence.TransientTenantSetup)]
49-
public static async Task SetupTransientTenant(FeatureContext featureContext)
42+
public static async Task SetupTransientTenant(FeatureContext featureContext, IObjectContainer objectContainer)
5043
{
51-
ITenantProvider tenantProvider = ContainerBindings.GetServiceProvider(featureContext).GetRequiredService<ITenantProvider>();
52-
var transientTenantManager = TransientTenantManager.GetInstance(featureContext);
53-
await transientTenantManager.EnsureInitialised().ConfigureAwait(false);
54-
55-
// Create a transient service tenant for testing purposes.
56-
ITenant transientServiceTenant = await transientTenantManager.CreateTransientServiceTenantFromEmbeddedResourceAsync(
57-
typeof(TransientTenantBindings).Assembly,
58-
"Marain.Operations.Api.Specs.ServiceManifests.OperationsServiceManifest.jsonc").ConfigureAwait(false);
44+
OperationsServiceTestTenants transientTenants = await OperationsTestTenantSetup.CreateTestTenantAndEnrollInClaimsAsync(featureContext);
5945

6046
// Now update the service Id in our configuration and in the function configuration
61-
UpdateServiceConfigurationWithTransientTenantId(featureContext, transientServiceTenant);
62-
63-
// Now we need to construct a transient client tenant for the test, and enroll it in the new
64-
// transient service.
65-
ITenant transientClientTenant = await transientTenantManager.CreateTransientClientTenantAsync().ConfigureAwait(false);
47+
UpdateServiceConfigurationWithTransientTenantId(featureContext, transientTenants.TransientServiceTenant);
6648

67-
await transientTenantManager.AddEnrollmentAsync(
68-
transientClientTenant.Id,
69-
transientServiceTenant.Id,
70-
GetOperationsConfig(featureContext)).ConfigureAwait(false);
71-
72-
// TODO: Temporary hack to work around the fact that the transient tenant manager no longer holds the latest
73-
// version of the tenants it's tracking; see https://github.com/marain-dotnet/Marain.TenantManagement/issues/28
74-
transientTenantManager.PrimaryTransientClient = await tenantProvider.GetTenantAsync(transientClientTenant.Id).ConfigureAwait(false);
49+
objectContainer.RegisterInstanceAs(transientTenants);
7550
}
7651

7752
[AfterFeature]
7853
public static async Task TearDownTenants(FeatureContext featureContext)
7954
{
80-
var tenantManager = TransientTenantManager.GetInstance(featureContext);
81-
82-
await featureContext.RunAndStoreExceptionsAsync(async () =>
83-
{
84-
IBlobContainerSourceWithTenantLegacyTransition cloudBlobContainerFactory = ContainerBindings.GetServiceProvider(featureContext).GetRequiredService<IBlobContainerSourceWithTenantLegacyTransition>();
85-
BlobContainerClient testContainer = await cloudBlobContainerFactory.GetBlobContainerClientFromTenantAsync(
86-
tenantManager.PrimaryTransientClient,
87-
OperationsRepository.OperationsV2ConfigKey,
88-
OperationsRepository.OperationsV3ConfigKey).ConfigureAwait(false);
89-
await testContainer.DeleteIfExistsAsync().ConfigureAwait(false);
90-
}).ConfigureAwait(false);
55+
await OperationsTestStorageSetup.TearDownBlobContainersAsync(featureContext);
9156

57+
var tenantManager = TransientTenantManager.GetInstance(featureContext);
9258
await featureContext.RunAndStoreExceptionsAsync(() => tenantManager.CleanupAsync()).ConfigureAwait(false);
9359
}
9460

95-
/// <summary>
96-
/// Retrieves the transient tenant created for the current feature from the supplied <see cref="FeatureContext"/>,
97-
/// or null if there is none.
98-
/// </summary>
99-
/// <param name="context">The current <see cref="FeatureContext"/>.</param>
100-
/// <returns>The <see cref="ITenant"/>.</returns>
101-
public static ITenant GetTransientTenant(this FeatureContext context)
102-
{
103-
return TransientTenantManager.GetInstance(context).PrimaryTransientClient;
104-
}
105-
106-
/// <summary>
107-
/// Retrieves the Id of the transient tenant created for the current feature from the supplied feature context.
108-
/// <see cref="FeatureContext"/>.
109-
/// </summary>
110-
/// <param name="context">The current <see cref="FeatureContext"/>.</param>
111-
/// <returns>The Id of the <see cref="ITenant"/>.</returns>
112-
/// <exception cref="ArgumentNullException">There is no current tenant.</exception>
113-
public static string GetTransientTenantId(this FeatureContext context)
114-
{
115-
return context.GetTransientTenant().Id;
116-
}
117-
11861
private static void UpdateServiceConfigurationWithTransientTenantId(
11962
FeatureContext featureContext,
12063
ITenant transientServiceTenant)
@@ -129,36 +72,5 @@ private static void UpdateServiceConfigurationWithTransientTenantId(
12972
"MarainServiceConfiguration:ServiceDisplayName",
13073
transientServiceTenant.Name);
13174
}
132-
133-
private static EnrollmentConfigurationItem[] GetOperationsConfig(FeatureContext featureContext)
134-
{
135-
IConfiguration configuration = ContainerBindings
136-
.GetServiceProvider(featureContext)
137-
.GetRequiredService<IConfiguration>();
138-
139-
// Can't create a logger using the generic type of this class because it's static, so we'll do it using
140-
// the feature context instead.
141-
ILogger<FeatureContext> logger = ContainerBindings
142-
.GetServiceProvider(featureContext)
143-
.GetRequiredService<ILogger<FeatureContext>>();
144-
145-
BlobStorageConfiguration blobStorageConfiguration =
146-
configuration.GetSection("TestBlobStorageConfiguration").Get<BlobStorageConfiguration>()
147-
?? new BlobStorageConfiguration();
148-
149-
if (string.IsNullOrEmpty(blobStorageConfiguration.AccountName))
150-
{
151-
logger.LogDebug("No configuration value 'TestBlobStorageConfiguration:AccountName' provided; using local storage emulator.");
152-
}
153-
154-
return new EnrollmentConfigurationItem[]
155-
{
156-
new EnrollmentBlobStorageConfigurationItem
157-
{
158-
Key = "operationsStore",
159-
Configuration = blobStorageConfiguration,
160-
},
161-
};
162-
}
16375
}
16476
}

Solutions/Marain.Operations.Api.Specs/Features/OperationsUsingClient.feature

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Scenario: Change an operation state to Running and retrieve it
4040
And I use the operations control client to create an operation
4141
| IdName | ResourceLocation | ExpireAfter | Body |
4242
| NewOperationId | http://www.google.com/ | 300 | This is some text for the body |
43+
And the request succeeds
4344
And I use the operations control client to set the status of the operation with Id called 'NewOperationId' to Running
4445
And the request succeeds
4546
When I use the operations status client to get the operation with the Id called 'NewOperationId'
@@ -52,6 +53,7 @@ Scenario: Update the percentage complete of a Running operation and retrieve it
5253
And I use the operations control client to create an operation
5354
| IdName | ResourceLocation | ExpireAfter | Body |
5455
| NewOperationId | http://www.google.com/ | 300 | This is some text for the body |
56+
And the request succeeds
5557
And I use the operations control client to set the status of the operation with Id called 'NewOperationId' to Running
5658
And the request succeeds
5759
And I use the operations control client to set the status of the operation with Id called 'NewOperationId' to Running and the percentage complete to 45
@@ -66,6 +68,7 @@ Scenario: Change an operation state to Succeeded and retrieve it
6668
And I use the operations control client to create an operation
6769
| IdName | ResourceLocation | ExpireAfter | Body |
6870
| NewOperationId | http://www.google.com/ | 300 | This is some text for the body |
71+
And the request succeeds
6972
And I use the operations control client to set the status of the operation with Id called 'NewOperationId' to Succeeded
7073
And the request succeeds
7174
When I use the operations status client to get the operation with the Id called 'NewOperationId'
@@ -78,6 +81,7 @@ Scenario: Change an operation state to Failed and retrieve it
7881
And I use the operations control client to create an operation
7982
| IdName | ResourceLocation | ExpireAfter | Body |
8083
| NewOperationId | http://www.google.com/ | 300 | This is some text for the body |
84+
And the request succeeds
8185
And I use the operations control client to set the status of the operation with Id called 'NewOperationId' to Failed
8286
And the request succeeds
8387
When I use the operations status client to get the operation with the Id called 'NewOperationId'

Solutions/Marain.Operations.Api.Specs/Marain.Operations.Api.Specs.csproj

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,11 @@
4747
<PrivateAssets>all</PrivateAssets>
4848
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
4949
</PackageReference>
50-
<PackageReference Include="Marain.Services.Tenancy.Testing" Version="2.3.3" />
51-
</ItemGroup>
52-
<ItemGroup>
53-
<Folder Include="ServiceManifests\" />
54-
</ItemGroup>
55-
<ItemGroup>
56-
<EmbeddedResource Include="..\Marain.Operations.Deployment\ServiceManifests\OperationsServiceManifest.jsonc" Link="ServiceManifests\OperationsServiceManifest.jsonc">
57-
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
58-
</EmbeddedResource>
5950
</ItemGroup>
6051
<ItemGroup>
6152
<ProjectReference Include="..\Marain.Operations.ControlClient\Marain.Operations.ControlClient.csproj" />
6253
<ProjectReference Include="..\Marain.Operations.ControlHost.Functions\Marain.Operations.ControlHost.Functions.csproj" />
54+
<ProjectReference Include="..\Marain.Operations.Specs.Common\Marain.Operations.Specs.Common.csproj" />
6355
<ProjectReference Include="..\Marain.Operations.StatusClient\Marain.Operations.StatusClient.csproj" />
6456
<ProjectReference Include="..\Marain.Operations.StatusHost.Functions\Marain.Operations.StatusHost.Functions.csproj" />
6557
</ItemGroup>

Solutions/Marain.Operations.Api.Specs/Steps/Exceptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public static void AssertNoLastException(SpecFlowContext context)
5454
{
5555
if (context.TryGetValue(LastExceptionContextKey, out Exception ex))
5656
{
57-
Assert.Fail($"Expected no last exception, but an exception of type '{ex.GetType().Name}' was thrown: '{ex.Message}'");
57+
Assert.Fail($"Expected no last exception, but an exception of type '{ex.GetType().Name}' was thrown: '{ex.Message}' - {ex}");
5858
}
5959
}
6060

Solutions/Marain.Operations.Api.Specs/appsettings.template.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
"APPINSIGHTS_INSTRUMENTATIONKEY": "",
66
"AI:DeveloperMode": "true",
77

8+
// This uses the emulator:
9+
"TestBlobStorageConfiguration:ConnectionStringPlainText": "UseDevelopmentStorage=true",
10+
// If using a real blob storage account, provide the details using these settings.
11+
//"TestBlobStorageConfiguration:AccountName": ""
12+
//"TestBlobStorageConfiguration:AccessKeyInKeyVault:SecretName": "",
13+
//"TestBlobStorageConfiguration:AccessKeyInKeyVault:VaultName": ""
14+
815
// If running with a local tenancy service, point TenancyClient:TenancyServiceBaseUri at the localhost address for that
916
// and set the ResourceIdForMsiAuthentication to an empty string.
1017
"TenancyClient:TenancyServiceBaseUri": "http://localhost:7071/"

0 commit comments

Comments
 (0)