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

[ReleasePrep][2021.04.08]RI dev into main #8514

Merged
merged 13 commits into from
Apr 9, 2021
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
4 changes: 4 additions & 0 deletions src/Bootstrap/dist/css/bootstrap-theme.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 10 additions & 5 deletions src/Bootstrap/less/theme/page-add-organization.less
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
.page-add-organization {
.owner-image {
margin-top: 6px;
margin-bottom: 6px;
}
}
.owner-image {
margin-top: 6px;
margin-bottom: 6px;
}

.required:after {
color: red;
content: " *";
}
}
6 changes: 3 additions & 3 deletions src/Bootstrap/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/DatabaseMigrationTools/DatabaseMigrationTools.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="NuGet.Services.Validation">
<Version>2.84.0</Version>
<Version>2.86.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
<Version>4.3.0-dev-3612825</Version>
</PackageReference>
<PackageReference Include="NuGet.Services.Cursor">
<Version>2.84.0</Version>
<Version>2.86.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
Expand Down
13 changes: 12 additions & 1 deletion src/NuGet.Services.Entities/Package.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace NuGet.Services.Entities
public class Package
: IPackageEntity
{
private string _id;

#pragma warning disable 618 // TODO: remove Package.Authors completely once production services definitely no longer need it
public Package()
Expand Down Expand Up @@ -264,7 +265,17 @@ public bool HasEmbeddedReadme

public virtual ICollection<SymbolPackage> SymbolPackages { get; set; }

public string Id => PackageRegistration.Id;
/// <summary>
/// The package ID with casing specific to this version if available, otherwise it will fallback to the ID on
/// the package registration. WARNING: this property should not be used for comparisons in LINQ to SQL because
/// it may be null sometimes. Use <see cref="PackageRegistration.Id"/> instead.
/// </summary>
[StringLength(Constants.MaxPackageIdLength)]
public string Id
{
get => _id ?? PackageRegistration?.Id;
set => _id = value;
}

public EmbeddedLicenseFileType EmbeddedLicenseType { get; set; }

Expand Down
41 changes: 14 additions & 27 deletions src/NuGetGallery.Core/Auditing/CloudAuditingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ public class CloudAuditingService : AuditingService, ICloudStorageStatusDependen
{
public static readonly string DefaultContainerName = "auditing";

private CloudBlobContainer _auditContainer;
private Func<ICloudBlobContainer> _auditContainerFactory;
private Func<Task<AuditActor>> _getOnBehalfOf;

public CloudAuditingService(string storageConnectionString, bool readAccessGeoRedundant, Func<Task<AuditActor>> getOnBehalfOf)
: this(GetContainer(storageConnectionString, readAccessGeoRedundant), getOnBehalfOf)
public CloudAuditingService(Func<ICloudBlobClient> cloudBlobClientFactory, Func<Task<AuditActor>> getOnBehalfOf)
: this(() => GetContainer(cloudBlobClientFactory), getOnBehalfOf)
{
}

public CloudAuditingService(CloudBlobContainer auditContainer, Func<Task<AuditActor>> getOnBehalfOf)
public CloudAuditingService(Func<ICloudBlobContainer> auditContainerFactory, Func<Task<AuditActor>> getOnBehalfOf)
{
_auditContainer = auditContainer;
_auditContainerFactory = auditContainerFactory;
_getOnBehalfOf = getOnBehalfOf;
}

Expand All @@ -52,7 +52,8 @@ protected override async Task SaveAuditRecordAsync(string auditData, string reso
$"{filePath.Replace(Path.DirectorySeparatorChar, '/')}/" +
$"{Guid.NewGuid().ToString("N")}-{action.ToLowerInvariant()}.audit.v1.json";

var blob = _auditContainer.GetBlockBlobReference(fullPath);
var container = _auditContainerFactory();
var blob = container.GetBlobReference(fullPath);
bool retry = false;
try
{
Expand All @@ -74,37 +75,23 @@ protected override async Task SaveAuditRecordAsync(string auditData, string reso
{
// Create the container and try again,
// this time we let exceptions bubble out
await Task.Factory.FromAsync(
(cb, s) => _auditContainer.BeginCreateIfNotExists(cb, s),
ar => _auditContainer.EndCreateIfNotExists(ar),
null);
await container.CreateIfNotExistAsync(permissions: null);
await WriteBlob(auditData, fullPath, blob);
}
}

private static CloudBlobContainer GetContainer(string storageConnectionString, bool readAccessGeoRedundant)
private static ICloudBlobContainer GetContainer(Func<ICloudBlobClient> cloudBlobClientFactory)
{
var cloudBlobClient = CloudStorageAccount.Parse(storageConnectionString).CreateCloudBlobClient();
if (readAccessGeoRedundant)
{
cloudBlobClient.DefaultRequestOptions.LocationMode = LocationMode.PrimaryThenSecondary;
}
var cloudBlobClient = cloudBlobClientFactory();
return cloudBlobClient.GetContainerReference(DefaultContainerName);
}

private static async Task WriteBlob(string auditData, string fullPath, CloudBlockBlob blob)
private static async Task WriteBlob(string auditData, string fullPath, ISimpleCloudBlob blob)
{
try
{
var strm = await Task.Factory.FromAsync(
(cb, s) => blob.BeginOpenWrite(
AccessCondition.GenerateIfNoneMatchCondition("*"),
new BlobRequestOptions(),
new OperationContext(),
cb, s),
ar => blob.EndOpenWrite(ar),
null);
using (var writer = new StreamWriter(strm))
using (var stream = await blob.OpenWriteAsync(AccessCondition.GenerateIfNoneMatchCondition("*")))
using (var writer = new StreamWriter(stream))
{
await writer.WriteAsync(auditData);
}
Expand All @@ -125,7 +112,7 @@ private static async Task WriteBlob(string auditData, string fullPath, CloudBloc

public Task<bool> IsAvailableAsync(BlobRequestOptions options, OperationContext operationContext)
{
return _auditContainer.ExistsAsync(options, operationContext);
return _auditContainerFactory().ExistsAsync(options, operationContext);
}

public override string RenderAuditEntry(AuditEntry entry)
Expand Down
59 changes: 39 additions & 20 deletions src/NuGetGallery.Core/Infrastructure/AzureEntityList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,25 @@ namespace NuGetGallery.Infrastructure
private const string IndexPartitionKey = "INDEX";
private const string IndexRowKey = "0";

private CloudTable _tableRef;
private readonly string _tableName;
private readonly bool _readAccessGeoRedundant;
private readonly Func<string> _connectionStringFactory;

public AzureEntityList(string connStr, string tableName, bool readAccessGeoRedundant)
public AzureEntityList(Func<string> connectionStringFactory, string tableName, bool readAccessGeoRedundant)
{
var tableClient = CloudStorageAccount.Parse(connStr).CreateCloudTableClient();
if (readAccessGeoRedundant)
{
tableClient.DefaultRequestOptions.LocationMode = LocationMode.PrimaryThenSecondary;
}
_tableRef = tableClient.GetTableReference(tableName);
_tableName = tableName ?? throw new ArgumentNullException(nameof(tableName));
_readAccessGeoRedundant = readAccessGeoRedundant;
_connectionStringFactory = connectionStringFactory ?? throw new ArgumentNullException(nameof(connectionStringFactory));

var tableRef = GetTableReference();
// Create the actual Azure Table, if it doesn't yet exist.
bool newTable = _tableRef.CreateIfNotExists();
bool newTable = tableRef.CreateIfNotExists();

// Create the Index if it doesn't yet exist.
bool needsIndex = newTable;
if (!newTable)
{
var indexResult = _tableRef.Execute(
var indexResult = tableRef.Execute(
TableOperation.Retrieve<Index>(IndexPartitionKey, IndexRowKey));

needsIndex = (indexResult.HttpStatusCode == 404);
Expand All @@ -49,7 +49,7 @@ public AzureEntityList(string connStr, string tableName, bool readAccessGeoRedun
if (needsIndex)
{
// Create the index
var result = _tableRef.Execute(
var result = tableRef.Execute(
TableOperation.Insert(new Index
{
Count = 0,
Expand Down Expand Up @@ -102,7 +102,8 @@ public T this[long index]
string partitionKey = FormatPartitionKey(page);
string rowKey = FormatRowKey(row);

var response = _tableRef.Execute(TableOperation.Retrieve<T>(partitionKey, rowKey));
var tableRef = GetTableReference();
var response = tableRef.Execute(TableOperation.Retrieve<T>(partitionKey, rowKey));
if (response.HttpStatusCode == 404)
{
throw new ArgumentOutOfRangeException(nameof(index), index, CoreStrings.Http404NotFound);
Expand All @@ -129,8 +130,10 @@ public T this[long index]
value.PartitionKey = FormatPartitionKey(page);
value.RowKey = FormatRowKey(row);

var tableRef = GetTableReference();

// Just do an unconditional update - if you wanted any *real* benefit of atomic update then you would need a more complex method signature that calls you back when optimistic updates fail ETAG checks!
_tableRef.Execute(TableOperation.Replace(value));
tableRef.Execute(TableOperation.Replace(value));
}
}

Expand Down Expand Up @@ -161,13 +164,14 @@ public long Add(T entity)
/// </summary>
public IEnumerator<T> GetEnumerator()
{
var tableRef = GetTableReference();
for (long page = 0;; page++)
{
string partitionKey = FormatPartitionKey(page);
var chunkQuery = new TableQuery<T>().Where(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));

var chunk = _tableRef.ExecuteQuery(chunkQuery).ToArray();
var chunk = tableRef.ExecuteQuery(chunkQuery).ToArray();

foreach (var item in chunk)
{
Expand All @@ -191,6 +195,7 @@ public IEnumerable<T> GetRange(long pos, int n)
int done = 0;
long page = pos / 1000;
long offset = pos % 1000;
var tableRef = GetTableReference();
while (done < n)
{
string partitionKey = FormatPartitionKey(page);
Expand All @@ -201,7 +206,7 @@ public IEnumerable<T> GetRange(long pos, int n)
TableOperators.And,
TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThanOrEqual, rowKey)));

var chunk = _tableRef.ExecuteQuery(chunkQuery).ToArray();
var chunk = tableRef.ExecuteQuery(chunkQuery).ToArray();
if (chunk.Length == 0)
{
break; // Reached the end of the list
Expand Down Expand Up @@ -235,9 +240,10 @@ private long AtomicIncrementCount()
// 2) use ETAG to do a conditional +1 update
// 3) retry if that optimistic concurrency attempt failed
long pos = -1; // To avoid compiler warnings, grr - should never be returned
var tableRef = GetTableReference();
DoReplaceWithRetry(() =>
{
var result1 = _tableRef.Execute(
var result1 = tableRef.Execute(
TableOperation.Retrieve<Index>(IndexPartitionKey, IndexRowKey));

ThrowIfErrorStatus(result1);
Expand All @@ -260,6 +266,7 @@ private long AtomicIncrementCount()
private void InsertIfNotExistsWithRetry<T2>(Func<T2> valueGenerator) where T2 : ITableEntity
{
TableResult storeResult;
var tableRef = GetTableReference();
do
{
var entity = valueGenerator();
Expand All @@ -268,7 +275,7 @@ private void InsertIfNotExistsWithRetry<T2>(Func<T2> valueGenerator) where T2 :
// - the dummy MERGES with existing data instead of overwriting it, so no data loss.
// 2) Use its ETAG to conditionally replace the item
// 3) return true if success, false to allow retry on failure
var dummyResult = _tableRef.Execute(
var dummyResult = tableRef.Execute(
TableOperation.InsertOrMerge(new HazardEntry
{
PartitionKey = entity.PartitionKey,
Expand All @@ -281,7 +288,7 @@ private void InsertIfNotExistsWithRetry<T2>(Func<T2> valueGenerator) where T2 :
}

entity.ETag = dummyResult.Etag;
storeResult = _tableRef.Execute(TableOperation.Replace(entity));
storeResult = tableRef.Execute(TableOperation.Replace(entity));
}
while (storeResult.HttpStatusCode == 412);
ThrowIfErrorStatus(storeResult);
Expand All @@ -290,17 +297,19 @@ private void InsertIfNotExistsWithRetry<T2>(Func<T2> valueGenerator) where T2 :
private void DoReplaceWithRetry<T2>(Func<T2> valueGenerator) where T2 : ITableEntity
{
TableResult storeResult;
var tableRef = GetTableReference();
do
{
storeResult = _tableRef.Execute(TableOperation.Replace(valueGenerator.Invoke()));
storeResult = tableRef.Execute(TableOperation.Replace(valueGenerator.Invoke()));
}
while (storeResult.HttpStatusCode == 412);
ThrowIfErrorStatus(storeResult);
}

private Index ReadIndex()
{
var response = _tableRef.Execute(TableOperation.Retrieve<Index>(IndexPartitionKey, IndexRowKey));
var tableRef = GetTableReference();
var response = tableRef.Execute(TableOperation.Retrieve<Index>(IndexPartitionKey, IndexRowKey));
return (Index)response.Result;
}

Expand Down Expand Up @@ -333,6 +342,16 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
return GetEnumerator();
}

private CloudTable GetTableReference()
{
var tableClient = CloudStorageAccount.Parse(_connectionStringFactory()).CreateCloudTableClient();
if (_readAccessGeoRedundant)
{
tableClient.DefaultRequestOptions.LocationMode = LocationMode.PrimaryThenSecondary;
}
return tableClient.GetTableReference(_tableName);
}

class HazardEntry : ITableEntity
{
private const string PlaceHolderPropertyName = "Place_Held";
Expand Down
6 changes: 2 additions & 4 deletions src/NuGetGallery.Core/Infrastructure/TableErrorLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,13 +141,11 @@ public class TableErrorLog : ErrorLog
{
public const string TableName = "ElmahErrors";

private readonly string _connectionString;
private readonly AzureEntityList<ErrorEntity> _entityList;

public TableErrorLog(string connectionString, bool readAccessGeoRedundant)
public TableErrorLog(Func<string> connectionStringFactory, bool readAccessGeoRedundant)
{
_connectionString = connectionString;
_entityList = new AzureEntityList<ErrorEntity>(connectionString, TableName, readAccessGeoRedundant);
_entityList = new AzureEntityList<ErrorEntity>(connectionStringFactory, TableName, readAccessGeoRedundant);
}

public override ErrorLogEntry GetError(string id)
Expand Down
8 changes: 4 additions & 4 deletions src/NuGetGallery.Core/NuGetGallery.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<Version>5.9.0</Version>
</PackageReference>
<PackageReference Include="NuGet.Services.FeatureFlags">
<Version>2.84.0</Version>
<Version>2.86.0</Version>
</PackageReference>
<PackageReference Include="WindowsAzure.Storage">
<Version>9.3.3</Version>
Expand All @@ -56,13 +56,13 @@

<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
<PackageReference Include="NuGet.Services.Messaging.Email">
<Version>2.84.0</Version>
<Version>2.86.0</Version>
</PackageReference>
<PackageReference Include="NuGet.Services.Validation">
<Version>2.84.0</Version>
<Version>2.86.0</Version>
</PackageReference>
<PackageReference Include="NuGet.Services.Validation.Issues">
<Version>2.84.0</Version>
<Version>2.86.0</Version>
</PackageReference>
<PackageReference Include="NuGet.StrongName.elmah.corelibrary">
<Version>1.2.2</Version>
Expand Down
Loading