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

Add storage connection strings for each distinct purpose in the gallery #4568

Merged
merged 1 commit into from
Aug 18, 2017
Merged
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
8 changes: 6 additions & 2 deletions src/NuGetGallery.Core/Auditing/CloudAuditingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace NuGetGallery.Auditing
/// <summary>
/// Writes audit records to a specific container in the Cloud Storage Account provided
/// </summary>
public class CloudAuditingService : AuditingService
public class CloudAuditingService : AuditingService, ICloudStorageStatusDependency
{
public static readonly string DefaultContainerName = "auditing";

Expand All @@ -26,7 +26,6 @@ public class CloudAuditingService : AuditingService
public CloudAuditingService(string instanceId, string localIP, string storageConnectionString, Func<Task<AuditActor>> getOnBehalfOf)
: this(instanceId, localIP, GetContainer(storageConnectionString), getOnBehalfOf)
{

}

public CloudAuditingService(string instanceId, string localIP, CloudBlobContainer auditContainer, Func<Task<AuditActor>> getOnBehalfOf)
Expand Down Expand Up @@ -121,5 +120,10 @@ private static async Task WriteBlob(string auditData, string fullPath, CloudBloc
throw;
}
}

public Task<bool> IsAvailableAsync()
{
return _auditContainer.ExistsAsync();
}
}
}
16 changes: 16 additions & 0 deletions src/NuGetGallery.Core/ICloudStorageStatusDependency.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading.Tasks;

namespace NuGetGallery
{
/// <summary>
/// Determines whether a cloud storage object (e.g. a blob container) is available. The purpose of this interface
/// is to assess a gallery instance's connection to Azure Storage.
/// </summary>
public interface ICloudStorageStatusDependency
{
Task<bool> IsAvailableAsync();
}
}
1 change: 1 addition & 0 deletions src/NuGetGallery.Core/NuGetGallery.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
<Compile Include="Entities\Scope.cs" />
<Compile Include="Entities\UserSecurityPolicy.cs" />
<Compile Include="Entities\User.cs" />
<Compile Include="ICloudStorageStatusDependency.cs" />
<Compile Include="Infrastructure\AzureEntityList.cs" />
<Compile Include="Infrastructure\TableErrorLog.cs" />
<Compile Include="PackageReaderCoreExtensions.cs" />
Expand Down
145 changes: 81 additions & 64 deletions src/NuGetGallery/App_Start/DefaultDependenciesModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
using System.Net;
using System.Net.Mail;
using System.Security.Principal;
using System.Threading;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using AnglicanGeek.MarkdownMailer;
using Autofac;
using Autofac.Core;
using Elmah;
using Microsoft.WindowsAzure.ServiceRuntime;
using NuGetGallery.Areas.Admin;
Expand Down Expand Up @@ -71,31 +71,13 @@ protected override void Load(ContainerBuilder builder)

ConfigureSearch(builder, configuration);

if (!string.IsNullOrEmpty(configuration.Current.AzureStorageConnectionString))
{
builder.RegisterInstance(new TableErrorLog(configuration.Current.AzureStorageConnectionString))
.As<ErrorLog>()
.SingleInstance();
}
else
{
builder.RegisterInstance(new SqlErrorLog(configuration.Current.SqlConnectionString))
.As<ErrorLog>()
.SingleInstance();
}

builder.RegisterType<DateTimeProvider>().AsSelf().As<IDateTimeProvider>().SingleInstance();

builder.RegisterType<HttpContextCacheService>()
.AsSelf()
.As<ICacheService>()
.InstancePerLifetimeScope();

builder.RegisterType<ContentService>()
.AsSelf()
.As<IContentService>()
.SingleInstance();

builder.Register(c => new EntitiesContext(configuration.Current.SqlConnectionString, readOnly: configuration.Current.ReadOnlyMode))
.AsSelf()
.As<IEntitiesContext>()
Expand Down Expand Up @@ -194,11 +176,6 @@ protected override void Load(ContainerBuilder builder)
.As<ITempDataProvider>()
.InstancePerLifetimeScope();

builder.RegisterType<NuGetExeDownloaderService>()
.AsSelf()
.As<INuGetExeDownloaderService>()
.InstancePerLifetimeScope();

builder.RegisterType<StatusService>()
.AsSelf()
.As<IStatusService>()
Expand Down Expand Up @@ -279,29 +256,14 @@ protected override void Load(ContainerBuilder builder)
break;
case StorageType.AzureStorage:
ConfigureForAzureStorage(builder, configuration);
defaultAuditingService = GetAuditingServiceForAzureStorage(configuration);
defaultAuditingService = GetAuditingServiceForAzureStorage(builder, configuration);
break;
}

RegisterAuditingServices(builder, defaultAuditingService);

RegisterCookieComplianceService(builder, configuration, diagnosticsService);

builder.RegisterType<FileSystemService>()
.AsSelf()
.As<IFileSystemService>()
.SingleInstance();

builder.RegisterType<PackageFileService>()
.AsSelf()
.As<IPackageFileService>()
.InstancePerLifetimeScope();

builder.RegisterType<UploadFileService>()
.AsSelf()
.As<IUploadFileService>()
.InstancePerLifetimeScope();

// todo: bind all package curators by convention
builder.RegisterType<WebMatrixPackageCurator>()
.AsSelf()
Expand Down Expand Up @@ -389,11 +351,24 @@ private static void ConfigureAutocomplete(ContainerBuilder builder, IGalleryConf

private static void ConfigureForLocalFileSystem(ContainerBuilder builder, IGalleryConfigurationService configuration)
{
builder.RegisterType<FileSystemService>()
.AsSelf()
.As<IFileSystemService>()
.SingleInstance();

builder.RegisterType<FileSystemFileStorageService>()
.AsSelf()
.As<IFileStorageService>()
.SingleInstance();

foreach (var dependent in StorageDependent.GetAll(configuration.Current))
{
builder.RegisterType(dependent.ImplementationType)
.AsSelf()
.As(dependent.InterfaceType)
.InstancePerLifetimeScope();
}

builder.RegisterInstance(NullReportService.Instance)
.AsSelf()
.As<IReportService>()
Expand All @@ -409,34 +384,75 @@ private static void ConfigureForLocalFileSystem(ContainerBuilder builder, IGalle
.AsSelf()
.As<IAggregateStatsService>()
.InstancePerLifetimeScope();

builder.RegisterInstance(new SqlErrorLog(configuration.Current.SqlConnectionString))
.As<ErrorLog>()
.SingleInstance();
}

private static void ConfigureForAzureStorage(ContainerBuilder builder, IGalleryConfigurationService configuration)
private static IAuditingService GetAuditingServiceForLocalFileSystem(IGalleryConfigurationService configuration)
{
builder.RegisterInstance(new CloudBlobClientWrapper(configuration.Current.AzureStorageConnectionString, configuration.Current.AzureStorageReadAccessGeoRedundant))
.AsSelf()
.As<ICloudBlobClient>()
.SingleInstance();
var auditingPath = Path.Combine(
FileSystemFileStorageService.ResolvePath(configuration.Current.FileStorageDirectory),
FileSystemAuditingService.DefaultContainerName);

builder.RegisterType<CloudBlobFileStorageService>()
.AsSelf()
.As<IFileStorageService>()
.SingleInstance();
return new FileSystemAuditingService(auditingPath, AuditActor.GetAspNetOnBehalfOfAsync);
}

private static void ConfigureForAzureStorage(ContainerBuilder builder, IGalleryConfigurationService configuration)
{
/// The goal here is to initialize a <see cref="ICloudBlobClient"/> and <see cref="IFileStorageService"/>
/// instance for each unique connection string. Each dependent of <see cref="IFileStorageService"/> (that
/// is, each service that has a <see cref="IFileStorageService"/> constructor parameter) is registered in
/// <see cref="StorageDependent.GetAll(IAppConfiguration)"/> and is grouped by the respective storage
/// connection string. Each group is given a binding key which refers to the appropriate instance of the
/// <see cref="IFileStorageService"/>.
var completedBindingKeys = new HashSet<string>();
foreach (var dependent in StorageDependent.GetAll(configuration.Current))
{
if (completedBindingKeys.Add(dependent.BindingKey))
{
builder.RegisterInstance(new CloudBlobClientWrapper(dependent.AzureStorageConnectionString, configuration.Current.AzureStorageReadAccessGeoRedundant))
.AsSelf()
.As<ICloudBlobClient>()
.SingleInstance()
.Keyed<ICloudBlobClient>(dependent.BindingKey);

builder.RegisterType<CloudBlobFileStorageService>()
.WithParameter(new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(ICloudBlobClient),
(pi, ctx) => ctx.ResolveKeyed<ICloudBlobClient>(dependent.BindingKey)))
.AsSelf()
.As<IFileStorageService>()
.As<ICloudStorageStatusDependency>()
.SingleInstance()
.Keyed<IFileStorageService>(dependent.BindingKey);
}

builder.RegisterType(dependent.ImplementationType)
.WithParameter(new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(IFileStorageService),
(pi, ctx) => ctx.ResolveKeyed<IFileStorageService>(dependent.BindingKey)))
.AsSelf()
.As(dependent.InterfaceType)
.InstancePerLifetimeScope();
}

// when running on Windows Azure, we use a back-end job to calculate stats totals and store in the blobs
builder.RegisterInstance(new JsonAggregateStatsService(configuration.Current.AzureStorageConnectionString, configuration.Current.AzureStorageReadAccessGeoRedundant))
builder.RegisterInstance(new JsonAggregateStatsService(configuration.Current.AzureStorage_Statistics_ConnectionString, configuration.Current.AzureStorageReadAccessGeoRedundant))
.AsSelf()
.As<IAggregateStatsService>()
.SingleInstance();

// when running on Windows Azure, pull the statistics from the warehouse via storage
builder.RegisterInstance(new CloudReportService(configuration.Current.AzureStorageConnectionString, configuration.Current.AzureStorageReadAccessGeoRedundant))
builder.RegisterInstance(new CloudReportService(configuration.Current.AzureStorage_Statistics_ConnectionString, configuration.Current.AzureStorageReadAccessGeoRedundant))
.AsSelf()
.As<IReportService>()
.As<ICloudStorageStatusDependency>()
.SingleInstance();

// when running on Windows Azure, download counts come from the downloads.v1.json blob
var downloadCountService = new CloudDownloadCountService(configuration.Current.AzureStorageConnectionString, configuration.Current.AzureStorageReadAccessGeoRedundant);
var downloadCountService = new CloudDownloadCountService(configuration.Current.AzureStorage_Statistics_ConnectionString, configuration.Current.AzureStorageReadAccessGeoRedundant);
builder.RegisterInstance(downloadCountService)
.AsSelf()
.As<IDownloadCountService>()
Expand All @@ -447,18 +463,13 @@ private static void ConfigureForAzureStorage(ContainerBuilder builder, IGalleryC
.AsSelf()
.As<IStatisticsService>()
.SingleInstance();
}

private static IAuditingService GetAuditingServiceForLocalFileSystem(IGalleryConfigurationService configuration)
{
var auditingPath = Path.Combine(
FileSystemFileStorageService.ResolvePath(configuration.Current.FileStorageDirectory),
FileSystemAuditingService.DefaultContainerName);

return new FileSystemAuditingService(auditingPath, AuditActor.GetAspNetOnBehalfOfAsync);
builder.RegisterInstance(new TableErrorLog(configuration.Current.AzureStorage_Errors_ConnectionString))
.As<ErrorLog>()
.SingleInstance();
}

private static IAuditingService GetAuditingServiceForAzureStorage(IGalleryConfigurationService configuration)
private static IAuditingService GetAuditingServiceForAzureStorage(ContainerBuilder builder, IGalleryConfigurationService configuration)
{
string instanceId;
try
Expand All @@ -472,10 +483,16 @@ private static IAuditingService GetAuditingServiceForAzureStorage(IGalleryConfig

var localIp = AuditActor.GetLocalIpAddressAsync().Result;

return new CloudAuditingService(instanceId, localIp, configuration.Current.AzureStorageConnectionString, AuditActor.GetAspNetOnBehalfOfAsync);
var service = new CloudAuditingService(instanceId, localIp, configuration.Current.AzureStorage_Auditing_ConnectionString, AuditActor.GetAspNetOnBehalfOfAsync);

builder.RegisterInstance(service)
.As<ICloudStorageStatusDependency>()
.SingleInstance();

return service;
}

private static IAuditingService CombineServices(IEnumerable<IAuditingService> services)
private static IAuditingService CombineAuditingServices(IEnumerable<IAuditingService> services)
{
if (!services.Any())
{
Expand Down Expand Up @@ -510,7 +527,7 @@ private static void RegisterAuditingServices(ContainerBuilder builder, IAuditing
services.Add(defaultAuditingService);
}

var service = CombineServices(services);
var service = CombineAuditingServices(services);

builder.RegisterInstance(service)
.AsSelf()
Expand Down
Loading