Skip to content
This repository was archived by the owner on Jul 30, 2024. It is now read-only.

Commit 37a0839

Browse files
authored
Merge pull request #522 from NuGet/dev
[ReleasePrep][2018.08.06] RI of dev into master
2 parents bf51759 + 32182ee commit 37a0839

21 files changed

Lines changed: 452 additions & 59 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace NuGet.Services.Revalidate
5+
{
6+
/// <summary>
7+
/// The configuration needed to query an Application Insights account using
8+
/// the REST endpoints.
9+
/// </summary>
10+
public class ApplicationInsightsConfiguration
11+
{
12+
/// <summary>
13+
/// The Application Insights account identifier.
14+
/// </summary>
15+
public string AppId { get; set; }
16+
17+
/// <summary>
18+
/// The API Key used to access the Application Insights account.
19+
/// </summary>
20+
public string ApiKey { get; set; }
21+
}
22+
}

src/NuGet.Services.Revalidate/Configuration/RevalidationConfiguration.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ public class RevalidationConfiguration
4040
/// </summary>
4141
public HealthConfiguration Health { get; set; }
4242

43+
/// <summary>
44+
/// The configurations to authenticate to Application Insight's REST endpoints.
45+
/// </summary>
46+
public ApplicationInsightsConfiguration AppInsights { get; set; }
47+
4348
/// <summary>
4449
/// The configurations used by the in-memory queue of revalidations to start.
4550
/// </summary>

src/NuGet.Services.Revalidate/Initialization/PackageFinder.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Linq.Expressions;
99
using System.Threading;
1010
using Microsoft.Extensions.Logging;
11+
using Newtonsoft.Json;
1112
using NuGet.Versioning;
1213
using NuGetGallery;
1314

@@ -22,6 +23,8 @@ public class PackageFinder : IPackageFinder
2223
public const string DependencySetName = "Dependency";
2324
public const string RemainingSetName = "Remaining";
2425

26+
private const string PreinstalledPackagesResource = "NuGet.Services.Revalidate.Initialization.PreinstalledPackages.json";
27+
2528
private static int BatchSize = 1000;
2629
private static string MicrosoftAccountName = "Microsoft";
2730

@@ -46,16 +49,13 @@ public HashSet<int> FindMicrosoftPackages()
4649

4750
public HashSet<int> FindPreinstalledPackages(HashSet<int> except)
4851
{
49-
var preinstalledPackagesNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
52+
List<string> preinstalledPackagesNames;
53+
var assembly = typeof(PackageFinder).Assembly;
5054

51-
foreach (var path in _config.PreinstalledPaths)
55+
using (var resource = assembly.GetManifestResourceStream(PreinstalledPackagesResource))
56+
using (var reader = new StreamReader(resource))
5257
{
53-
var expandedPath = Environment.ExpandEnvironmentVariables(path);
54-
var packagesInPath = Directory.GetDirectories(expandedPath)
55-
.Select(d => d.Replace(expandedPath, "").Trim('\\').ToLowerInvariant())
56-
.Where(d => !d.StartsWith("."));
57-
58-
preinstalledPackagesNames.UnionWith(packagesInPath);
58+
preinstalledPackagesNames = JsonConvert.DeserializeObject<List<string>>(reader.ReadToEnd());
5959
}
6060

6161
var preinstalledPackages = FindRegistrationKeys(PreinstalledSetName, r => preinstalledPackagesNames.Contains(r.Id));

src/NuGet.Services.Revalidate/Initialization/PreinstalledPackages.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

src/NuGet.Services.Revalidate/Job.cs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
using System;
55
using System.Collections.Generic;
66
using System.ComponentModel.Design;
7+
using System.IO;
8+
using System.Linq;
79
using System.Threading.Tasks;
810
using Autofac;
911
using Microsoft.Extensions.Configuration;
1012
using Microsoft.Extensions.DependencyInjection;
1113
using Microsoft.Extensions.Logging;
1214
using Microsoft.Extensions.Options;
15+
using Newtonsoft.Json;
1316
using NuGet.Jobs;
1417
using NuGet.Jobs.Configuration;
1518
using NuGet.Jobs.Validation;
@@ -25,19 +28,22 @@ namespace NuGet.Services.Revalidate
2528

2629
public class Job : JsonConfigurationJob
2730
{
31+
private const string RebuildPreinstalledSetArgumentName = "RebuildPreinstalledSet";
2832
private const string InitializeArgumentName = "Initialize";
2933
private const string VerifyInitializationArgumentName = "VerifyInitialization";
3034
private const string JobConfigurationSectionName = "RevalidateJob";
3135

3236
private static readonly TimeSpan RetryLaterSleepDuration = TimeSpan.FromMinutes(5);
3337

38+
private string _preinstalledSetPath;
3439
private bool _initialize;
3540
private bool _verifyInitialization;
3641

3742
public override void Init(IServiceContainer serviceContainer, IDictionary<string, string> jobArgsDictionary)
3843
{
3944
base.Init(serviceContainer, jobArgsDictionary);
4045

46+
_preinstalledSetPath = JobConfigurationManager.TryGetArgument(jobArgsDictionary, RebuildPreinstalledSetArgumentName);
4147
_initialize = JobConfigurationManager.TryGetBoolArgument(jobArgsDictionary, InitializeArgumentName);
4248
_verifyInitialization = JobConfigurationManager.TryGetBoolArgument(jobArgsDictionary, VerifyInitializationArgumentName);
4349

@@ -56,7 +62,28 @@ public override async Task Run()
5662
{
5763
using (var scope = _serviceProvider.CreateScope())
5864
{
59-
if (_initialize || _verifyInitialization)
65+
if (!string.IsNullOrEmpty(_preinstalledSetPath))
66+
{
67+
Logger.LogInformation("Rebuilding the preinstalled packages set...");
68+
69+
var config = scope.ServiceProvider.GetRequiredService<InitializationConfiguration>();
70+
var preinstalledPackagesNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
71+
72+
foreach (var path in config.PreinstalledPaths)
73+
{
74+
var expandedPath = Environment.ExpandEnvironmentVariables(path);
75+
var packagesInPath = Directory.GetDirectories(expandedPath)
76+
.Select(d => d.Replace(expandedPath, "").Trim('\\').ToLowerInvariant())
77+
.Where(d => !d.StartsWith("."));
78+
79+
preinstalledPackagesNames.UnionWith(packagesInPath);
80+
}
81+
82+
File.WriteAllText(_preinstalledSetPath, JsonConvert.SerializeObject(preinstalledPackagesNames));
83+
84+
Logger.LogInformation("Rebuilt the preinstalled package set. Found {PreinstalledPackages} package ids", preinstalledPackagesNames.Count);
85+
}
86+
else if (_initialize || _verifyInitialization)
6087
{
6188
var initializer = scope.ServiceProvider.GetRequiredService<InitializationManager>();
6289

@@ -97,6 +124,7 @@ protected override void ConfigureJobServices(IServiceCollection services, IConfi
97124
services.AddSingleton(provider => provider.GetRequiredService<IOptionsSnapshot<RevalidationConfiguration>>().Value);
98125
services.AddSingleton(provider => provider.GetRequiredService<IOptionsSnapshot<RevalidationConfiguration>>().Value.Initialization);
99126
services.AddSingleton(provider => provider.GetRequiredService<IOptionsSnapshot<RevalidationConfiguration>>().Value.Health);
127+
services.AddSingleton(provider => provider.GetRequiredService<IOptionsSnapshot<RevalidationConfiguration>>().Value.AppInsights);
100128
services.AddSingleton(provider => provider.GetRequiredService<IOptionsSnapshot<RevalidationConfiguration>>().Value.Queue);
101129

102130
services.AddScoped<IGalleryContext>(provider =>
@@ -112,13 +140,14 @@ protected override void ConfigureJobServices(IServiceCollection services, IConfi
112140

113141
services.AddTransient<IPackageRevalidationStateService, PackageRevalidationStateService>();
114142
services.AddTransient<IRevalidationJobStateService, RevalidationJobStateService>();
115-
services.AddTransient<NuGetGallery.IRevalidationStateService, NuGetGallery.RevalidationStateService>();
143+
services.AddTransient<IRevalidationStateService, RevalidationStateService>();
116144

117145
// Initialization
118146
services.AddTransient<IPackageFinder, PackageFinder>();
119147
services.AddTransient<InitializationManager>();
120148

121149
// Revalidation
150+
services.AddTransient<IGalleryService, GalleryService>();
122151
services.AddTransient<IHealthService, HealthService>();
123152
services.AddTransient<IRevalidationQueue, RevalidationQueue>();
124153
services.AddTransient<IRevalidationService, RevalidationService>();

src/NuGet.Services.Revalidate/NuGet.Services.Revalidate.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<ItemGroup>
3737
<Reference Include="System" />
3838
<Reference Include="System.Core" />
39+
<Reference Include="System.Web" />
3940
<Reference Include="System.Xml.Linq" />
4041
<Reference Include="System.Data.DataSetExtensions" />
4142
<Reference Include="Microsoft.CSharp" />
@@ -44,6 +45,7 @@
4445
</ItemGroup>
4546
<ItemGroup>
4647
<Compile Include="Configuration\HealthConfiguration.cs" />
48+
<Compile Include="Configuration\ApplicationInsightsConfiguration.cs" />
4749
<Compile Include="Configuration\InitializationConfiguration.cs" />
4850
<Compile Include="Configuration\RevalidationQueueConfiguration.cs" />
4951
<Compile Include="Extensions\IEnumerableExtensions.cs" />
@@ -52,7 +54,9 @@
5254
<Compile Include="Initialization\IPackageFinder.cs" />
5355
<Compile Include="Initialization\PackageFinder.cs" />
5456
<Compile Include="Initialization\PackageRegistrationInformation.cs" />
57+
<Compile Include="Services\GalleryService.cs" />
5558
<Compile Include="Services\HealthService.cs" />
59+
<Compile Include="Services\IGalleryService.cs" />
5660
<Compile Include="Services\IHealthService.cs" />
5761
<Compile Include="Services\IRevalidationQueue.cs" />
5862
<Compile Include="Services\IRevalidationJobStateService.cs" />
@@ -89,6 +93,9 @@
8993
<ItemGroup>
9094
<Content Include="Scripts\nssm.exe" />
9195
</ItemGroup>
96+
<ItemGroup>
97+
<EmbeddedResource Include="Initialization\PreinstalledPackages.json" />
98+
</ItemGroup>
9299
<ItemGroup>
93100
<ProjectReference Include="..\NuGet.Jobs.Common\NuGet.Jobs.Common.csproj">
94101
<Project>{4B4B1EFB-8F33-42E6-B79F-54E7F3293D31}</Project>

src/NuGet.Services.Revalidate/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Program
1010
static void Main(string[] args)
1111
{
1212
var job = new Job();
13-
JobRunner.Run(job, args).GetAwaiter().GetResult();
13+
JobRunner.RunOnce(job, args).GetAwaiter().GetResult();
1414
}
1515
}
1616
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Net.Http;
6+
using System.Threading.Tasks;
7+
using System.Web;
8+
using Microsoft.Extensions.Logging;
9+
using Newtonsoft.Json;
10+
11+
namespace NuGet.Services.Revalidate
12+
{
13+
public class GalleryService : IGalleryService
14+
{
15+
private static readonly string GalleryEventsQuery = HttpUtility.UrlPathEncode(
16+
"customMetrics | " +
17+
"where name == \"PackagePush\" or name == \"PackageUnlisted\" or name == \"PackageListed\" | " +
18+
"summarize sum(value)");
19+
20+
private readonly HttpClient _httpClient;
21+
private readonly ApplicationInsightsConfiguration _appInsightsConfig;
22+
private readonly ILogger<GalleryService> _logger;
23+
24+
public GalleryService(
25+
HttpClient httpClient,
26+
ApplicationInsightsConfiguration appInsightsConfig,
27+
ILogger<GalleryService> logger)
28+
{
29+
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
30+
_appInsightsConfig = appInsightsConfig ?? throw new ArgumentNullException(nameof(appInsightsConfig));
31+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
32+
}
33+
34+
public async Task<int> CountEventsInPastHourAsync()
35+
{
36+
try
37+
{
38+
using (var request = new HttpRequestMessage())
39+
{
40+
request.RequestUri = new Uri($"https://api.applicationinsights.io/v1/apps/{_appInsightsConfig.AppId}/query?timespan=PT1H&query={GalleryEventsQuery}");
41+
request.Method = HttpMethod.Get;
42+
43+
request.Headers.Add("x-api-key", _appInsightsConfig.ApiKey);
44+
45+
using (var response = await _httpClient.SendAsync(request))
46+
{
47+
response.EnsureSuccessStatusCode();
48+
49+
var json = await response.Content.ReadAsStringAsync();
50+
var data = JsonConvert.DeserializeObject<QueryResult>(json);
51+
52+
if (data?.Tables?.Length != 1 ||
53+
data.Tables[0]?.Rows?.Length != 1 ||
54+
data.Tables[0].Rows[0]?.Length != 1)
55+
{
56+
throw new InvalidOperationException("Malformed response content");
57+
}
58+
59+
// Get the first row's first column's value.
60+
return data.Tables[0].Rows[0][0];
61+
}
62+
}
63+
}
64+
catch (Exception e)
65+
{
66+
_logger.LogError(0, e, "Exception thrown when getting the Gallery's package event rate.");
67+
68+
throw new InvalidOperationException("Exception thrown when getting the Gallery's package event rate.", e);
69+
}
70+
}
71+
72+
private class QueryResult
73+
{
74+
public QueryTable[] Tables { get; set; }
75+
}
76+
77+
private class QueryTable
78+
{
79+
public int[][] Rows { get; set; }
80+
}
81+
}
82+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Threading.Tasks;
5+
6+
namespace NuGet.Services.Revalidate
7+
{
8+
public interface IGalleryService
9+
{
10+
/// <summary>
11+
/// Count the number of gallery events (package pushes, listing, and unlisting) in the past hour.
12+
/// </summary>
13+
/// <returns>The number of gallery events in the past hour.</returns>
14+
Task<int> CountEventsInPastHourAsync();
15+
}
16+
}

src/NuGet.Services.Revalidate/Services/RevalidationThrottler.cs

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,52 @@ public class RevalidationThrottler : IRevalidationThrottler
1111
{
1212
private readonly IRevalidationJobStateService _jobState;
1313
private readonly IPackageRevalidationStateService _packageState;
14+
private readonly IGalleryService _gallery;
1415
private readonly RevalidationConfiguration _config;
1516
private readonly ILogger<RevalidationThrottler> _logger;
1617

1718
public RevalidationThrottler(
1819
IRevalidationJobStateService jobState,
1920
IPackageRevalidationStateService packageState,
21+
IGalleryService gallery,
2022
RevalidationConfiguration config,
2123
ILogger<RevalidationThrottler> logger)
2224
{
2325
_jobState = jobState ?? throw new ArgumentNullException(nameof(jobState));
2426
_packageState = packageState ?? throw new ArgumentNullException(nameof(packageState));
27+
_gallery = gallery ?? throw new ArgumentNullException(nameof(gallery));
2528
_config = config ?? throw new ArgumentNullException(nameof(config));
2629
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
2730
}
2831

2932
public async Task<bool> IsThrottledAsync()
3033
{
3134
var desiredRate = await _jobState.GetDesiredPackageEventRateAsync();
32-
var recentGalleryEvents = await CountGalleryEventsInPastHourAsync();
35+
var recentGalleryEvents = await _gallery.CountEventsInPastHourAsync();
3336
var recentRevalidations = await _packageState.CountRevalidationsEnqueuedInPastHourAsync();
3437

3538
var revalidationQuota = desiredRate - recentRevalidations - recentGalleryEvents;
3639

37-
return (revalidationQuota <= 0);
40+
if (revalidationQuota <= 0)
41+
{
42+
_logger.LogInformation(
43+
"Throttling revalidations. Desired rate: {DesiredRate}, gallery events: {GalleryEvents}, recent revalidations: {RecentRevalidations}",
44+
desiredRate,
45+
recentGalleryEvents,
46+
recentRevalidations);
47+
48+
return true;
49+
}
50+
else
51+
{
52+
_logger.LogInformation(
53+
"Allowing revalidations. Desired rate: {DesiredRate}, gallery events: {GalleryEvents}, recent revalidations: {RecentRevalidations}",
54+
desiredRate,
55+
recentGalleryEvents,
56+
recentRevalidations);
57+
58+
return false;
59+
}
3860
}
3961

4062
public async Task DelayUntilNextRevalidationAsync()
@@ -55,22 +77,5 @@ public async Task DelayUntilRevalidationRetryAsync()
5577

5678
await Task.Delay(_config.RetryLaterSleep);
5779
}
58-
59-
private Task<int> CountGalleryEventsInPastHourAsync()
60-
{
61-
// TODO: Count the number of package pushes, lists, and unlists.
62-
// Run this AI query:
63-
//
64-
// customMetrics | where name == "PackagePush" or name == "PackageUnlisted" or name == "PackageListed" | summarize sum(value)
65-
//
66-
// Using this HTTP request:
67-
//
68-
// GET /v1/apps/46f13c7d-635f-42c3-8120-593edeaad426/query?timespan=P1D&query=customMetrics%20%7C%20where%20name%20%3D%3D%20%22PackagePush%22%20or%20name%20%3D%3D%20%22PackageUnlisted%22%20or%20name%20%3D%3D%20%22PackageListed%22%20%7C%20summarize%20sum(value)%20 HTTP/1.1
69-
// Host: api.applicationinsights.io
70-
// x-api-key: my-super-secret-api-key
71-
//
72-
// See: https://dev.applicationinsights.io/quickstart
73-
return Task.FromResult(0);
74-
}
7580
}
7681
}

0 commit comments

Comments
 (0)