-
Notifications
You must be signed in to change notification settings - Fork 644
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
Conversation
Note that the meat of the work is done in |
|
||
public async Task<bool> IsAvailableAsync() | ||
{ | ||
return await _auditContainer.ExistsAsync(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you just return here without await
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general I prefer await
since it eases refactoring, but I don't care much. I'll remove the await
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
public async Task<bool> IsAvailableAsync() | ||
{ | ||
var container = GetCloudBlobContainer(); | ||
return await container.ExistsAsync(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing here, is await
actually needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
@@ -49,5 +45,18 @@ public CloudReportService(string connectionString, bool readAccessGeoRedundant) | |||
|
|||
return new StatisticsReport(content, blob.Properties.LastModified?.UtcDateTime); | |||
} | |||
|
|||
private CloudBlobContainer GetCloudBlobContainer() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the CloudBlobContainer
be cached? Or do we want to fetch a fresh container reference each time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to keep this out of scope -- we would need to analyze whether we would be regressing anything here by caching.
|
||
var connectionStringToBindingKey = dependents | ||
.GroupBy(d => d.AzureStorageConnectionString ?? DefaultBindingKey) | ||
.ToDictionary(g => g.Key, g => string.Join(" ", g.Select(d => d.ImplementationType))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should you use d.ImplementationType.FullName
here instead to be consistent with StorageDependent.Create
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should actually be g.Select(d => d.BindingKey)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
var completedBindingKeys = new HashSet<string>(); | ||
foreach (var dependent in GetStorageDependents(configuration.Current)) | ||
{ | ||
if (!completedBindingKeys.Contains(dependent.BindingKey)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could just call Add(..) without ! here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
public Type ImplementationType { get; } | ||
|
||
/// <summary> | ||
/// The storage dependent's interface tyep. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, a tyepo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
public string AzureStorage_Statistics_ConnectionString { get; set; } | ||
|
||
[DisplayName("AzureStorage.Uploads.ConnectionString")] | ||
public string AzureStorage_Uploads_ConnectionString { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use underscores in the property names?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the underscores in the property names? Today, the code that populates the
AppConfiguration
instance does not work on complex objects -- just simple properties. The underscore is basically a way to mimic complex objects in a flat list of properties likeIAppConfiguration
.
@@ -19,7 +20,7 @@ public class StatusService : IStatusService | |||
{ | |||
private readonly HttpClient _httpClient = new HttpClient(); | |||
private readonly IEntitiesContext _entities; | |||
private readonly IFileStorageService _fileStorageService; | |||
private readonly IEnumerable<ICloudStorageAvailabilityCheck> _cloudStorageAvailabilityChecks; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use ICollection
instead of IEnumerable
. The code potentially reads it multiple times and generally, IEnumerable does not support multiple iterations over it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, good point. Who knows how Autofac implements this (I don't care to know)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpicks, looks awesome
string AzureStorage_Errors_ConnectionString { get; set; } | ||
|
||
/// <summary> | ||
/// The Azure Storage connection string used downloading nuget.exe. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
used downloading
-> used for downloading
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
public class DefaultDependenciesModuleFacts | ||
{ | ||
[Fact] | ||
public void AllTypesDependingOnIFileStorageServiceAreReturnedByGetStorageDependents() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests are amazing and absolutely get rid of any concerns I had about the DI approach.
Great job.
/// 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 ICloudStorageAvailabilityCheck |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: semantically, ICloudStorageAvailabilityCheck
doesn't make a whole lot of sense--this is an object that can be checked for availability, not an availability check itself. Something along the lines of ICloudStorageResource
or IStatusDependency
would make more sense to me (there's nothing about this resource that implies that we must be checking a cloud storage object for availability).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
||
private static void ConfigureForAzureStorage(ContainerBuilder builder, IGalleryConfigurationService configuration) | ||
{ | ||
var completedBindingKeys = new HashSet<string>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given the complexity of this code, it may be beneficial to add some comments here, much like you did in the summary of this PR, describing what this code is and what it's accomplishing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
/// <summary> | ||
/// Represents a type that depends on <see cref="IFileStorageService"/>. | ||
/// </summary> | ||
internal class StorageDependent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is already massive, it may be better to move this as well as GetStorageDependents
to its own file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
{ | ||
const string DefaultBindingKey = "Default"; | ||
|
||
var dependents = new[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May want to add a comment here detailing that this array must be added to when a new StorageDependent
is added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
||
namespace NuGetGallery | ||
{ | ||
public class DefaultDependenciesModuleFacts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: These are really just StorageDependentFacts
. As suggested above, you should move StorageDependent
to its own file and then rename this class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Replaced configuration
Gallery.AzureStorageConnectionString
with new variables:Gallery.AzureStorage.Auditing.ConnectionString
Gallery.AzureStorage.Content.ConnectionString
Gallery.AzureStorage.Errors.ConnectionString
Gallery.AzureStorage.NuGetExe.ConnectionString
Gallery.AzureStorage.Packages.ConnectionString
Gallery.AzureStorage.Statistics.ConnectionString
Gallery.AzureStorage.Uploads.ConnectionString
Why the dots in the configuration name? I think the proper mental model here is that each storage purpose (e.g.
Errors
) may eventually have additional properties. For example, theGallery.AzureStorageReadAccessGeoRedundant
setting could become account-specific or we could configure the table/container name thatUploads
go into (for example). For that reason, I imagined the future whereGallery.AzureStorage.Statistics
would refer to an object with a set of properties. Today there is only one property:ConnectionString
.Why the underscores in the property names? Today, the code that populates the
AppConfiguration
instance does not work on complex objects -- just simple properties. The underscore is basically a way to mimic complex objects in a flat list of properties likeIAppConfiguration
.The basis of this implementation is telling our DI container (Autofac) which connection string to use for different services. We now need N instances of
CloudBlobFileStorageService
, instead of just one. Since this means we will have multiple implementations ofIFileStorageService
available in the DI container, we need a way to differentiate between them. We do this with keyed services. For more information about this general DI project, see here:http://docs.autofac.org/en/latest/faq/select-by-context.html
After this PR is approved and before I merge the PR, I will add the new configuration values to our DEV, INT, and PROD configuration files (.cscfg).
Fix #4554.