Skip to content

Commit

Permalink
feat(Storage): Add Azure Blob Storage provider (#128)
Browse files Browse the repository at this point in the history
Closes #127
  • Loading branch information
Regenhardt authored Mar 25, 2024
1 parent 7adb8cb commit 94462ac
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 104 deletions.
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<ItemGroup>
<PackageVersion Include="Azure.Search.Documents" Version="11.5.1" />
<PackageVersion Include="Azure.Storage.Blobs" Version="12.19.1" />
<PackageVersion Include="Azure.Storage.Common" Version="12.18.1" />
<PackageVersion Include="McMaster.Extensions.CommandLineUtils" Version="4.1.1" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.3" />
<PackageVersion Include="Microsoft.Azure.Cosmos" Version="3.38.1" />
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/Index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ BaGetter (pronounced "ba getter") is a lightweight NuGet and symbol server. It i
<img width="100%" src="https://user-images.githubusercontent.com/737941/50140219-d8409700-0258-11e9-94c9-dad24d2b48bb.png"/>
</CenterImg>

BaGetter supports Filesystem, GCP and AWS S3 buckets for package storage, and MySQL, Sqlite, SqlServer and PostgreSQL as database. The current per-package size limit is ~8GB. It can be hosted on IIS, and is also available in a linux [docker image](https://hub.docker.com/r/bagetter/bagetter).
BaGetter supports Filesystem, GCP and AWS S3 buckets, and Azure Blob Storage for package storage, and MySQL, Sqlite, SqlServer and PostgreSQL as database. The current per-package size limit is ~8GB. It can be hosted on IIS, and is also available in a linux [docker image](https://hub.docker.com/r/bagetter/bagetter).

## Run BaGetter

Expand Down
42 changes: 28 additions & 14 deletions docs/docs/Installation/azure.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Run BaGetter on Azure

:::warning
Expand All @@ -6,7 +9,7 @@ This page is a work in progress!

:::

Use Azure to scale BaGetter. You can store metadata on [Azure SQL Database](https://azure.microsoft.com/en-us/services/sql-database/), upload packages to [Azure Blob Storage](https://azure.microsoft.com/en-us/services/storage/blobs/), and provide powerful search using [Azure Search](https://azure.microsoft.com/en-us/services/search/).
Use Azure to scale BaGetter. You can store metadata on [Azure SQL Database](https://azure.microsoft.com/products/azure-sql/database/), upload packages to [Azure Blob Storage](https://azure.microsoft.com/products/storage/blobs/), and soon provide powerful search using [Azure Search](https://azure.microsoft.com/en-us/services/search/).

## TODO

Expand All @@ -16,11 +19,11 @@ Use Azure to scale BaGetter. You can store metadata on [Azure SQL Database](http

## Configure BaGetter

You can modify BaGetter's configurations by editing the `appsettings.json` file. For the full list of configurations, please refer to [BaGetter's configuration](../configuration.md) guide.
You can modify BaGetter's configurations by editing the `appsettings.json` file or through [environment variables](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-8.0#non-prefixed-environment-variables). For the full list of configurations, please refer to [BaGetter's configuration](../configuration.md) guide.

### Azure SQL database

Update the `appsettings.json` file:
Set the database type to `SqlServer` and provide a [connection string](https://learn.microsoft.com/ef/core/miscellaneous/connection-strings):

```json
{
Expand All @@ -37,49 +40,60 @@ Update the `appsettings.json` file:

### Azure Blob Storage

Update the `appsettings.json` file:
Set the storage type to `AzureBlobStorage` and provide a container name to use and credentials:

<Tabs groupId="blob-storage">
<TabItem value="accessKey" label="Access Key" default>

To use account name and access key, add them like this:
```json
{
...

"Storage": {
"Type": "AzureBlobStorage",
"Container": "my-container",
"AccountName": "my-account",
"AccessKey": "abcd1234",
"Container": "my-container"
"AccessKey": "abcd1234"
},

...
}
```

Alternatively, you can use a full Azure Storage connection string:
</TabItem>

<TabItem value="connectionString" label="Connection string">

To use a connection string, add it like this:

```json
{
...

"Storage": {
"Type": "AzureBlobStorage",
"ConnectionString": "AccountName=my-account;AccountKey=abcd1234;...",
"Container": "my-container"
"Container": "my-container",
"ConnectionString": "AccountName=my-account;AccountKey=abcd1234;..."
},

...
}
```

</TabItem>
</Tabs>

### Azure Search

Update the `appsettings.json` file:
Azure Search is currently not available due to significant API changes BaGetter has to make. Once it's available, you can set the search type to `AzureSearch` and provide an account name and API key:

```json
{
...

"Search": {
"Type": "Azure",
"Type": "AzureSearch",
"AccountName": "my-account",
"ApiKey": "ABCD1234"
},
Expand Down Expand Up @@ -116,13 +130,13 @@ You can restore packages by using the following package source:

Some helpful guides:

- [Visual Studio](https://docs.microsoft.com/en-us/nuget/consume-packages/install-use-packages-visual-studio#package-sources)
- [NuGet.config](https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file#package-source-sections)
- [Visual Studio](https://docs.microsoft.com/nuget/consume-packages/install-use-packages-visual-studio#package-sources)
- [NuGet.config](https://docs.microsoft.com/nuget/reference/nuget-config-file#package-source-sections)

## Symbol server

You can load symbols by using the following symbol location:

`http://localhost:5000/api/download/symbols`

For Visual Studio, please refer to the [Configure Debugging](https://docs.microsoft.com/en-us/visualstudio/debugger/specify-symbol-dot-pdb-and-source-files-in-the-visual-studio-debugger?view=vs-2017#configure-symbol-locations-and-loading-options) guide.
For Visual Studio, please refer to the [Configure Debugging](https://docs.microsoft.com/visualstudio/debugger/specify-symbol-dot-pdb-and-source-files-in-the-visual-studio-debugger?view=vs-2017#configure-symbol-locations-and-loading-options) guide.
2 changes: 1 addition & 1 deletion docs/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const config: Config = {
// Please change this to your repo.
// Remove this to remove the "edit this page" links.
editUrl:
'https://github.com/bagetter/BaGetter/tree/main/',
'https://github.com/bagetter/BaGetter/tree/main/docs',
},
theme: {
customCss: './src/css/custom.css',
Expand Down
59 changes: 27 additions & 32 deletions src/BaGetter.Azure/AzureApplicationExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using Azure.Storage;
using Azure.Storage.Blobs;
using BaGetter.Azure;
using BaGetter.Core;
//using Azure.Cosmos.Table;
Expand Down Expand Up @@ -81,46 +83,39 @@ public static BaGetterApplication AddAzureTableDatabase(

public static BaGetterApplication AddAzureBlobStorage(this BaGetterApplication app)
{
throw new NotImplementedException();
app.Services.AddBaGetterOptions<AzureBlobStorageOptions>(nameof(BaGetterOptions.Storage));
app.Services.AddTransient<BlobStorageService>();
app.Services.TryAddTransient<IStorageService>(provider => provider.GetRequiredService<BlobStorageService>());

//app.Services.AddBaGetterOptions<AzureBlobStorageOptions>(nameof(BaGetterOptions.Storage));
//app.Services.AddTransient<BlobStorageService>();
//app.Services.TryAddTransient<IStorageService>(provider => provider.GetRequiredService<BlobStorageService>());
app.Services.AddSingleton(provider =>
{
var options = provider.GetRequiredService<IOptions<AzureBlobStorageOptions>>().Value;
//app.Services.AddSingleton(provider =>
//{
// var options = provider.GetRequiredService<IOptions<AzureBlobStorageOptions>>().Value;

// if (!string.IsNullOrEmpty(options.ConnectionString))
// {
// return CloudStorageAccount.Parse(options.ConnectionString);
// }

// return new CloudStorageAccount(
// new StorageCredentials(
// options.AccountName,
// options.AccessKey),
// useHttps: true);
//});
// TODO: Add BlobClientOptions with customer-provided key.
if (!string.IsNullOrEmpty(options.ConnectionString))
{
return new BlobServiceClient(options.ConnectionString);
}
//app.Services.AddTransient(provider =>
//{
// var options = provider.GetRequiredService<IOptionsSnapshot<AzureBlobStorageOptions>>().Value;
// var account = provider.GetRequiredService<CloudStorageAccount>();
return new BlobServiceClient(new Uri($"https://{options.AccountName}.blob.core.windows.net"), new StorageSharedKeyCredential(options.AccountName, options.AccessKey));
});

// var client = account.CreateCloudBlobClient();
app.Services.AddTransient(provider =>
{
var options = provider.GetRequiredService<IOptionsSnapshot<AzureBlobStorageOptions>>().Value;
var account = provider.GetRequiredService<BlobServiceClient>();
// return client.GetContainerReference(options.Container);
//});
return account.GetBlobContainerClient(options.Container);
});

//app.Services.AddProvider<IStorageService>((provider, config) =>
//{
// if (!config.HasStorageType("AzureBlobStorage")) return null;
app.Services.AddProvider<IStorageService>((provider, config) =>
{
if (!config.HasStorageType("AzureBlobStorage")) return null;
// return provider.GetRequiredService<BlobStorageService>();
//});
return provider.GetRequiredService<BlobStorageService>();
});

//return app;
return app;
}

public static BaGetterApplication AddAzureBlobStorage(
Expand Down
4 changes: 3 additions & 1 deletion src/BaGetter.Azure/BaGetter.Azure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="Azure.Search.Documents" />
<PackageReference Include="Azure.Storage.Blobs" />
<PackageReference Include="Azure.Storage.Common" />
<PackageReference Include="Microsoft.Azure.Cosmos" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
</ItemGroup>
Expand All @@ -19,6 +20,7 @@
</ItemGroup>

<ItemGroup>
<Compile Remove="**/*.cs" />
<Compile Remove="Search/*.cs" />
<Compile Remove="Table/*.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace BaGetter.Azure
{
/// <summary>
/// BaGetter's configurations to use Azure Blob Storage to store packages.
/// See: https://loic-sharma.github.io/BaGet/quickstart/azure/#azure-blob-storage
/// See: https://www.bagetter.com/docs/Installation/azure#azure-blob-storage
/// </summary>
public class AzureBlobStorageOptions : IValidatableObject
{
Expand Down
31 changes: 15 additions & 16 deletions src/BaGetter.Azure/Extensions/StorageExceptionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
using Azure;
using System.Net;

namespace BaGetter.Azure
{
using StorageException = Microsoft.WindowsAzure.Storage.StorageException;
using TableStorageException = Microsoft.Azure.Cosmos.Table.StorageException;

internal static class StorageExceptionExtensions
{
public static bool IsAlreadyExistsException(this StorageException e)
public static bool IsAlreadyExistsException(this RequestFailedException e)
{
return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.Conflict;
return e?.Status == (int?)HttpStatusCode.Conflict;
}

public static bool IsNotFoundException(this TableStorageException e)
{
return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.NotFound;
}
//public static bool IsNotFoundException(this TableStorageException e)
//{
// return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.NotFound;
//}

public static bool IsAlreadyExistsException(this TableStorageException e)
{
return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.Conflict;
}
//public static bool IsAlreadyExistsException(this TableStorageException e)
//{
// return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.Conflict;
//}

public static bool IsPreconditionFailedException(this TableStorageException e)
{
return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.PreconditionFailed;
}
//public static bool IsPreconditionFailedException(this TableStorageException e)
//{
// return e?.RequestInformation?.HttpStatusCode == (int?)HttpStatusCode.PreconditionFailed;
//}
}
}
Loading

0 comments on commit 94462ac

Please sign in to comment.