Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move azurite scaffolding #5448

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .devcontainer/internal_dev/postCreateCommand.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@ Proceed? [y/N] " response
Press <Enter> to continue."
remove_comments ./dev/secrets.json
configure_other_vars
echo "Installing Az module. This will take ~a minute..."
pwsh -Command "Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force"
pwsh ./dev/setup_azurite.ps1

dotnet tool install dotnet-certificate-tool -g >/dev/null

Expand Down
47 changes: 0 additions & 47 deletions dev/setup_azurite.ps1

This file was deleted.

11 changes: 11 additions & 0 deletions src/Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
using Bit.Core.Auth.Identity.TokenProviders;
using Bit.Core.Tools.ImportFeatures;
using Bit.Core.Tools.ReportFeatures;
using Bit.Core.Platform.Infrastructure;



#if !OSS
Expand Down Expand Up @@ -66,6 +68,15 @@ public void ConfigureServices(IServiceCollection services)
services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies"));
}

// TODO: Be more selective about adding this scaffolder
if (Environment.IsDevelopment())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We likely want to be much more strict on when this runs. Some additional checks I am considering:

  1. Have this be behind a DEBUG compiler directive
  2. Only run this when ALL azure related connection strings are UseDevelopmentStorage=true.
  3. Only run this when ANY azure related connection strings are UseDevelopmentStorage=true.
  4. Run very selective scaffolding only for the things choosing UseDevelopmentStorage=true.
  5. #1 and #2
  6. #1 and #3

{
// If this scaffolder is going to be registered, we want it registered
// pretty early in case any other hosted services need to use the
// things this hosted service will create.
services.AddHostedService<AzureScaffolder>();
}

// Data Protection
services.AddCustomDataProtectionServices(Environment, globalSettings);

Expand Down
142 changes: 142 additions & 0 deletions src/Core/Platform/Infrastructure/AzureScaffolder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#nullable enable

using Azure;
using Azure.Data.Tables;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Azure.Storage.Queues;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Bit.Core.Platform.Infrastructure;

internal class BrokenDevelepmentEnvironmentException : Exception
{
public BrokenDevelepmentEnvironmentException(string message)
: base(message)
{

Check warning on line 17 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L16-L17

Added lines #L16 - L17 were not covered by tests

}

Check warning on line 19 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L19

Added line #L19 was not covered by tests

public BrokenDevelepmentEnvironmentException(string message, Exception innerException)
: base(message, innerException)
{

Check warning on line 23 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L22-L23

Added lines #L22 - L23 were not covered by tests

}

Check warning on line 25 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L25

Added line #L25 was not covered by tests
}

public class AzureScaffolder : IHostedService
{
private static readonly IEnumerable<string> _containers =
["attachments", "sendfiles", "misc"];

Check warning on line 31 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L30-L31

Added lines #L30 - L31 were not covered by tests

private static readonly IEnumerable<string> _queues =
["event", "notifications", "reference-events", "mail"];

Check warning on line 34 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L33-L34

Added lines #L33 - L34 were not covered by tests

private static readonly IEnumerable<string> _tables =
["event", "metadata", "installationdevice"];

Check warning on line 37 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L36-L37

Added lines #L36 - L37 were not covered by tests

private static readonly BlobCorsRule _corsRule = new()
{
AllowedHeaders = "*",
ExposedHeaders = "*",
AllowedOrigins = "*",
MaxAgeInSeconds = 30,
AllowedMethods = "GET,PUT"
};

Check warning on line 46 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L39-L46

Added lines #L39 - L46 were not covered by tests

private static readonly EqualityComparer<BlobCorsRule> _corsComparer = EqualityComparer<BlobCorsRule>.Create((a, b) =>
{

Check warning on line 49 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L48-L49

Added lines #L48 - L49 were not covered by tests
if (a == null)
{
return b == null;
}

Check warning on line 54 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L51-L54

Added lines #L51 - L54 were not covered by tests
if (b == null)
{
return false;
}

Check warning on line 59 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L56-L59

Added lines #L56 - L59 were not covered by tests
return a.AllowedOrigins == b.AllowedOrigins
&& a.AllowedMethods == b.AllowedMethods
&& a.AllowedHeaders == b.AllowedHeaders
&& a.ExposedHeaders == b.ExposedHeaders
&& a.MaxAgeInSeconds == b.MaxAgeInSeconds;
});

Check warning on line 65 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L61-L65

Added lines #L61 - L65 were not covered by tests

private static readonly string _developmentUri = "UseDevelopmentStorage=true";

Check warning on line 67 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L67

Added line #L67 was not covered by tests
private readonly ILogger<AzureScaffolder> _logger;

public AzureScaffolder(ILogger<AzureScaffolder> logger)
{
_logger = logger;
}

Check warning on line 73 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L70-L73

Added lines #L70 - L73 were not covered by tests

public async Task StartAsync(CancellationToken cancellationToken)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any errors here, will stop the startup of the application. I could instead catch and not-rethrow errors to allow startup to continue but that will generally lead to the error showing up later in the application and likely not rethrowing a more descriptive error.

{

Check warning on line 76 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L76

Added line #L76 was not covered by tests
try
{
_logger.LogInformation("Scaffolding Azurite Infrastrucure.");
await ScaffoldAsync(cancellationToken);
}

Check warning on line 81 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L78-L81

Added lines #L78 - L81 were not covered by tests
// TODO: Handle certain errors with instructions on how to fix, like API version problems
catch (RequestFailedException requestedFailedEx) when (requestedFailedEx.ErrorCode == "InvalidHeaderValue")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically this error could be for a different header than the Api-Version header so I could also compare against the full exception message but I generally don't like that and have only ever seen this error because of that header.

{

Check warning on line 84 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L83-L84

Added lines #L83 - L84 were not covered by tests
// Rethrow with more explicit exception
throw new BrokenDevelepmentEnvironmentException(
"The version of Azurite being ran is incompitable with our Azure packages. Read more https://contributing.bitwarden.com/link-here",
requestedFailedEx
);

Check warning on line 89 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L86-L89

Added lines #L86 - L89 were not covered by tests
}
catch (Exception ex)
{
_logger.LogError(ex, "Unknown error while scaffolding Azure infrastructure in Azurite.");
throw;

Check warning on line 94 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L91-L94

Added lines #L91 - L94 were not covered by tests
}
}

Check warning on line 96 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L96

Added line #L96 was not covered by tests

private static async Task ScaffoldAsync(CancellationToken cancellationToken)
{
var blobServiceClient = new BlobServiceClient(_developmentUri);

Check warning on line 100 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L99-L100

Added lines #L99 - L100 were not covered by tests

foreach (var container in _containers)
{
var blobContainer = blobServiceClient.GetBlobContainerClient(container);

Check warning on line 104 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L103-L104

Added lines #L103 - L104 were not covered by tests
if (!await blobContainer.ExistsAsync(cancellationToken))
{
await blobContainer.CreateAsync(cancellationToken: cancellationToken);
}
}

Check warning on line 109 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L106-L109

Added lines #L106 - L109 were not covered by tests


// Check if our cors rule is already added, if not, add it.
var properties = await blobServiceClient.GetPropertiesAsync(cancellationToken);
var existingCorsRules = properties.Value.Cors;

Check warning on line 114 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L113-L114

Added lines #L113 - L114 were not covered by tests
if (!existingCorsRules.Contains(_corsRule, _corsComparer))
{
properties.Value.Cors.Add(_corsRule);
await blobServiceClient.SetPropertiesAsync(properties.Value, cancellationToken);
}

Check warning on line 119 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L116-L119

Added lines #L116 - L119 were not covered by tests

var queueServiceClient = new QueueServiceClient(_developmentUri);

Check warning on line 121 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L121

Added line #L121 was not covered by tests

foreach (var queue in _queues)
{
var queueClient = queueServiceClient.GetQueueClient(queue);

Check warning on line 125 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L124-L125

Added lines #L124 - L125 were not covered by tests
if (!await queueClient.ExistsAsync(cancellationToken))
{
await queueClient.CreateAsync(cancellationToken: cancellationToken);
}
}

Check warning on line 130 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L127-L130

Added lines #L127 - L130 were not covered by tests

var tableServiceClient = new TableServiceClient(_developmentUri);

Check warning on line 132 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L132

Added line #L132 was not covered by tests

foreach (var table in _tables)
{
var tableClient = tableServiceClient.GetTableClient(table);
await tableClient.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
}
}

Check warning on line 139 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L135-L139

Added lines #L135 - L139 were not covered by tests

public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;

Check warning on line 141 in src/Core/Platform/Infrastructure/AzureScaffolder.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/Platform/Infrastructure/AzureScaffolder.cs#L141

Added line #L141 was not covered by tests
}
13 changes: 12 additions & 1 deletion test/Api.IntegrationTest/Factories/ApiApplicationFactory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Bit.Identity.Models.Request.Accounts;
using Bit.Core.Platform.Infrastructure;
using Bit.Identity.Models.Request.Accounts;
using Bit.IntegrationTestCommon.Factories;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.TestHost;
Expand Down Expand Up @@ -32,6 +33,16 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
var jobService = services.First(sd => sd.ServiceType == typeof(IHostedService) && sd.ImplementationType == typeof(Jobs.JobsHostedService));
services.Remove(jobService);

// Integration tests are not configured to use Azurite yet
var azureScaffolder = services.FirstOrDefault(
sd => sd.ServiceType == typeof(IHostedService) && sd.ImplementationType == typeof(AzureScaffolder)
);

if (azureScaffolder != null)
{
services.Remove(azureScaffolder);
}

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.BackchannelHttpHandler = _identityApplicationFactory.Server.CreateHandler();
Expand Down
Loading