public static void SetupBlobStorageRepository(FeatureContext featureContext) { IServiceProvider serviceProvider = ContainerBindings.GetServiceProvider(featureContext); IBlobContainerSourceWithTenantLegacyTransition factory = serviceProvider.GetRequiredService <IBlobContainerSourceWithTenantLegacyTransition>(); ITenantProvider tenantProvider = serviceProvider.GetRequiredService <ITenantProvider>(); IConfiguration configuration = serviceProvider.GetRequiredService <IConfiguration>(); ITenant rootTenant = tenantProvider.Root; LegacyV2BlobStorageConfiguration storageConfig = configuration.GetSection("TestStorageConfiguration").Get <LegacyV2BlobStorageConfiguration>() ?? new LegacyV2BlobStorageConfiguration(); // Generate a container name just for this test, otherwise you can end up with collisions // on subsequent tests runs while containers are still being deleted. storageConfig.Container = Guid.NewGuid().ToString(); tenantProvider.Root.UpdateProperties(data => data.Append(new KeyValuePair <string, object>( "StorageConfiguration__workflowdefinitions", storageConfig))); }
private static BlobContainerConfiguration V3ConfigurationFromLegacy( ITenant tenant, string?containerName, LegacyV2BlobStorageConfiguration legacyConfiguration) { BlobContainerConfiguration v3Configuration = LegacyConfigurationConverter.FromV2ToV3(legacyConfiguration); if (legacyConfiguration.Container is not null) { v3Configuration = v3Configuration with { Container = string.IsNullOrWhiteSpace(legacyConfiguration.Container) ? containerName is null ? null : AzureStorageBlobContainerNaming.HashAndEncodeBlobContainerName(containerName) : AzureStorageBlobContainerNaming.HashAndEncodeBlobContainerName( legacyConfiguration.DisableTenantIdPrefix ? legacyConfiguration.Container : AzureStorageBlobTenantedContainerNaming.GetTenantedLogicalBlobContainerNameFor(tenant, legacyConfiguration.Container)), }; } return(AddContainerNameIfNotInConfig(v3Configuration, containerName)); }
/// <inheritdoc/> public async Task <ITenant> CreateWellKnownChildTenantAsync( string parentTenantId, Guid wellKnownChildTenantGuid, string name) { (ITenant parentTenant, BlobContainerClient container) = await this.GetContainerAndTenantForChildTenantsOfAsync(parentTenantId).ConfigureAwait(false); // We need to copy blob storage settings for the Tenancy container definition from the parent to the new child // to support the tenant blob store provider. We would expect this to be overridden by clients that wanted to // establish their own settings. bool configIsInV3 = true; LegacyV2BlobStorageConfiguration?v2TenancyStorageConfiguration = null; if (!parentTenant.Properties.TryGet(TenancyV3ConfigKey, out BlobContainerConfiguration tenancyStorageConfiguration)) { configIsInV3 = false; if (!parentTenant.Properties.TryGet(TenancyV2ConfigKey, out v2TenancyStorageConfiguration)) { throw new InvalidOperationException($"No configuration found for ${TenancyV3ConfigKey} or ${TenancyV2ConfigKey}"); } } IPropertyBag childProperties; if (parentTenantId == this.Root.Id && this.propagateRootStorageConfigAsV2) { configIsInV3 = false; v2TenancyStorageConfiguration = new LegacyV2BlobStorageConfiguration { Container = tenancyStorageConfiguration.Container, }; if (tenancyStorageConfiguration.ConnectionStringPlainText != null) { v2TenancyStorageConfiguration.AccountName = tenancyStorageConfiguration.ConnectionStringPlainText; } else if (tenancyStorageConfiguration.AccountName != null) { v2TenancyStorageConfiguration.AccountName = tenancyStorageConfiguration.AccountName; v2TenancyStorageConfiguration.KeyVaultName = tenancyStorageConfiguration.AccessKeyInKeyVault?.VaultName; v2TenancyStorageConfiguration.AccountKeySecretName = tenancyStorageConfiguration.AccessKeyInKeyVault?.SecretName; } } if (configIsInV3) { childProperties = this.propertyBagFactory.Create(values => values.Append(new KeyValuePair <string, object>(TenancyV3ConfigKey, tenancyStorageConfiguration))); } else { childProperties = this.propertyBagFactory.Create(values => values.Append(new KeyValuePair <string, object>(TenancyV2ConfigKey, v2TenancyStorageConfiguration !))); } var child = new Tenant( parentTenantId.CreateChildId(wellKnownChildTenantGuid), name, childProperties); // TODO: this needs thinking through. BlobContainerClient newTenantBlobContainer = await this.GetBlobContainer(child).ConfigureAwait(false); await newTenantBlobContainer.CreateIfNotExistsAsync().ConfigureAwait(false); // As we create the new blob, we need to ensure there isn't already a tenant with the same Id. We do this by // providing an If-None-Match header passing a "*", which will cause a storage exception with a 409 status // code if a blob with the same Id already exists. BlockBlobClient blob = GetLiveTenantBlockBlobReference(child.Id, container); var content = new MemoryStream(); using (var sw = new StreamWriter(content, UTF8WithoutBom, leaveOpen: true)) using (JsonWriter writer = new JsonTextWriter(sw)) { this.jsonSerializer.Serialize(writer, child); } content.Position = 0; try { Response <BlobContentInfo> response = await blob.UploadAsync( content, new BlobUploadOptions { Conditions = new BlobRequestConditions { IfNoneMatch = ETag.All } }) .ConfigureAwait(false); child.ETag = response.Value.ETag.ToString("H"); } catch (global::Azure.RequestFailedException x) when(x.ErrorCode == "BlobAlreadyExists") { // This exception is thrown because there's already a tenant with the same Id. This should never happen when // this method has been called from CreateChildTenantAsync as the Guid will have been generated and the // chances of it matching one previously generated are miniscule. However, it could happen when calling this // method directly with a wellKnownChildTenantGuid that's already in use. In this case, the fault is with // the client code - creating tenants with well known Ids is something one would expect to happen under // controlled conditions, so it's only likely that a conflict will occur when either the client code has made // a mistake or someone is actively trying to cause problems. throw new ArgumentException( $"A child tenant of '{parentTenantId}' with a well known Guid of '{wellKnownChildTenantGuid}' already exists.", nameof(wellKnownChildTenantGuid)); } return(child); }