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

Commit 6c451f8

Browse files
author
Christy Henriksson
authored
Jobs.Common helpers for NuGet.Services.Sql (#505)
1 parent 32182ee commit 6c451f8

19 files changed

Lines changed: 429 additions & 36 deletions

File tree

NuGet.Jobs.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Validation.Symbols.Tests",
151151
EndProject
152152
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Validation.Symbols.Core.Tests", "tests\Validation.Symbols.Core.Tests\Validation.Symbols.Core.Tests.csproj", "{9ED642DF-4623-4EB2-8B72-52C6489BF9D6}"
153153
EndProject
154+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGet.Jobs.Common.Tests", "tests\NuGet.Jobs.Common.Tests\NuGet.Jobs.Common.Tests.csproj", "{CE96428B-8138-4914-9999-2B391797FFF8}"
155+
EndProject
154156
Global
155157
GlobalSection(SolutionConfigurationPlatforms) = preSolution
156158
Debug|Any CPU = Debug|Any CPU
@@ -399,6 +401,10 @@ Global
399401
{9ED642DF-4623-4EB2-8B72-52C6489BF9D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
400402
{9ED642DF-4623-4EB2-8B72-52C6489BF9D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
401403
{9ED642DF-4623-4EB2-8B72-52C6489BF9D6}.Release|Any CPU.Build.0 = Release|Any CPU
404+
{CE96428B-8138-4914-9999-2B391797FFF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
405+
{CE96428B-8138-4914-9999-2B391797FFF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
406+
{CE96428B-8138-4914-9999-2B391797FFF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
407+
{CE96428B-8138-4914-9999-2B391797FFF8}.Release|Any CPU.Build.0 = Release|Any CPU
402408
EndGlobalSection
403409
GlobalSection(SolutionProperties) = preSolution
404410
HideSolutionNode = FALSE
@@ -464,6 +470,7 @@ Global
464470
{2DD07A73-8C88-4429-BB24-C2813586EF92} = {678D7B14-F8BC-4193-99AF-2EE8AA390A02}
465471
{640D29AB-4D1B-4FC7-AE67-AD12EE5AC503} = {6A776396-02B1-475D-A104-26940ADB04AB}
466472
{9ED642DF-4623-4EB2-8B72-52C6489BF9D6} = {6A776396-02B1-475D-A104-26940ADB04AB}
473+
{CE96428B-8138-4914-9999-2B391797FFF8} = {6A776396-02B1-475D-A104-26940ADB04AB}
467474
EndGlobalSection
468475
GlobalSection(ExtensibilityGlobals) = postSolution
469476
SolutionGuid = {284A7AC3-FB43-4F1F-9C9C-2AF0E1F46C2B}

src/ArchivePackages/ArchivePackages.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
<Version>9.0.1</Version>
7777
</PackageReference>
7878
<PackageReference Include="NuGet.Services.Sql">
79-
<Version>2.25.0-master-30453</Version>
79+
<Version>2.27.0</Version>
8080
</PackageReference>
8181
<PackageReference Include="System.Net.Http">
8282
<Version>4.3.3</Version>

src/Gallery.CredentialExpiration/Gallery.CredentialExpiration.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
<Version>9.0.1</Version>
9494
</PackageReference>
9595
<PackageReference Include="NuGet.Services.Sql">
96-
<Version>2.25.0-master-30263</Version>
96+
<Version>2.27.0</Version>
9797
</PackageReference>
9898
<PackageReference Include="NuGet.Services.Storage">
9999
<Version>2.1.3</Version>

src/Gallery.Maintenance/Gallery.Maintenance.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
<Version>9.0.1</Version>
6969
</PackageReference>
7070
<PackageReference Include="NuGet.Services.Sql">
71-
<Version>2.25.0-master-30263</Version>
71+
<Version>2.27.0</Version>
7272
</PackageReference>
7373
<PackageReference Include="System.Net.Http">
7474
<Version>4.3.3</Version>

src/NuGet.Jobs.Common/Extensions/SqlConnectionStringBuilderExtensions.cs

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/NuGet.Jobs.Common/JobBase.cs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.ComponentModel.Design;
7+
using System.Data.SqlClient;
68
using System.Diagnostics.Tracing;
79
using System.Threading.Tasks;
10+
using Microsoft.Extensions.DependencyInjection;
811
using Microsoft.Extensions.Logging;
12+
using Microsoft.Extensions.Options;
13+
using NuGet.Jobs.Configuration;
14+
using NuGet.Services.KeyVault;
15+
using NuGet.Services.Sql;
916

1017
namespace NuGet.Jobs
1118
{
@@ -22,6 +29,7 @@ protected JobBase(EventSource jobEventSource)
2229
{
2330
JobName = GetType().ToString();
2431
_jobEventSource = jobEventSource;
32+
SqlConnectionFactories = new Dictionary<string, ISqlConnectionFactory>();
2533
}
2634

2735
public string JobName { get; private set; }
@@ -30,14 +38,180 @@ protected JobBase(EventSource jobEventSource)
3038

3139
protected ILogger Logger { get; private set; }
3240

41+
private Dictionary<string, ISqlConnectionFactory> SqlConnectionFactories { get; }
42+
3343
public void SetLogger(ILoggerFactory loggerFactory, ILogger logger)
3444
{
3545
LoggerFactory = loggerFactory;
3646
Logger = logger;
3747
}
3848

49+
/// <summary>
50+
/// Initialize the job, provided the service container and configuration.
51+
/// </summary>
3952
public abstract void Init(IServiceContainer serviceContainer, IDictionary<string, string> jobArgsDictionary);
4053

54+
/// <summary>
55+
/// Run the job.
56+
/// </summary>
4157
public abstract Task Run();
58+
59+
60+
/// <summary>
61+
/// Test connection early to fail fast, and log connection diagnostics.
62+
/// </summary>
63+
private async Task TestConnection(string name, ISqlConnectionFactory connectionFactory)
64+
{
65+
try
66+
{
67+
using (var connection = await connectionFactory.OpenAsync())
68+
using (var cmd = new SqlCommand("SELECT CONCAT(CURRENT_USER, '/', SYSTEM_USER)", connection))
69+
{
70+
var result = cmd.ExecuteScalar();
71+
var user = result.ToString();
72+
Logger.LogInformation("Verified CreateSqlConnectionAsync({name}) connects to database {DataSource}/{InitialCatalog} as {User}",
73+
name, connectionFactory.DataSource, connectionFactory.InitialCatalog, user);
74+
}
75+
}
76+
catch (Exception e)
77+
{
78+
Logger.LogError(0, e, "Failed to connect to database {DataSource}/{InitialCatalog}",
79+
connectionFactory.DataSource, connectionFactory.InitialCatalog);
80+
81+
throw;
82+
}
83+
}
84+
85+
public SqlConnectionStringBuilder GetDatabaseRegistration<T>()
86+
where T : IDbConfiguration
87+
{
88+
if (SqlConnectionFactories.TryGetValue(GetDatabaseKey<T>(), out var connectionFactory))
89+
{
90+
return ((AzureSqlConnectionFactory)connectionFactory).SqlConnectionStringBuilder;
91+
}
92+
93+
return null;
94+
}
95+
96+
/// <summary>
97+
/// Initializes an <see cref="ISqlConnectionFactory"/>, for use by validation jobs.
98+
/// </summary>
99+
/// <returns>ConnectionStringBuilder, used for diagnostics.</returns>
100+
public SqlConnectionStringBuilder RegisterDatabase<T>(
101+
IServiceProvider serviceProvider,
102+
bool testConnection = true)
103+
where T : IDbConfiguration
104+
{
105+
if (serviceProvider == null)
106+
{
107+
throw new ArgumentNullException(nameof(serviceProvider));
108+
}
109+
110+
var secretInjector = serviceProvider.GetRequiredService<ISecretInjector>();
111+
var connectionString = serviceProvider.GetRequiredService<IOptionsSnapshot<T>>().Value.ConnectionString;
112+
var connectionFactory = new AzureSqlConnectionFactory(connectionString, secretInjector);
113+
114+
return RegisterDatabase(GetDatabaseKey<T>(), connectionString, testConnection, secretInjector);
115+
}
116+
117+
/// <summary>
118+
/// Initializes an <see cref="ISqlConnectionFactory"/>, for use by non-validation jobs.
119+
/// </summary>
120+
/// <returns>ConnectionStringBuilder, used for diagnostics.</returns>
121+
public SqlConnectionStringBuilder RegisterDatabase(
122+
IServiceContainer serviceContainer,
123+
IDictionary<string, string> jobArgsDictionary,
124+
string connectionStringArgName,
125+
bool testConnection = true)
126+
{
127+
if (serviceContainer == null)
128+
{
129+
throw new ArgumentNullException(nameof(serviceContainer));
130+
}
131+
132+
if (jobArgsDictionary == null)
133+
{
134+
throw new ArgumentNullException(nameof(jobArgsDictionary));
135+
}
136+
137+
if (string.IsNullOrEmpty(connectionStringArgName))
138+
{
139+
throw new ArgumentException("Argument cannot be null or empty.", nameof(connectionStringArgName));
140+
}
141+
142+
var secretInjector = (ISecretInjector)serviceContainer.GetService(typeof(ISecretInjector));
143+
var connectionString = JobConfigurationManager.GetArgument(jobArgsDictionary, connectionStringArgName);
144+
145+
return RegisterDatabase(connectionStringArgName, connectionString, testConnection, secretInjector);
146+
}
147+
148+
/// <summary>
149+
/// Register a job database at initialization time. Each call should overwrite any existing
150+
/// registration because <see cref="JobRunner"/> calls <see cref="Init"/> on every iteration.
151+
/// </summary>
152+
/// <returns>ConnectionStringBuilder, used for diagnostics.</returns>
153+
private SqlConnectionStringBuilder RegisterDatabase(
154+
string name,
155+
string connectionString,
156+
bool testConnection,
157+
ISecretInjector secretInjector)
158+
{
159+
var connectionFactory = new AzureSqlConnectionFactory(connectionString, secretInjector, Logger);
160+
SqlConnectionFactories[name] = connectionFactory;
161+
162+
if (testConnection)
163+
{
164+
Task.Run(() => TestConnection(name, connectionFactory)).Wait();
165+
}
166+
167+
return connectionFactory.SqlConnectionStringBuilder;
168+
}
169+
170+
private static string GetDatabaseKey<T>()
171+
{
172+
return typeof(T).Name;
173+
}
174+
175+
/// <summary>
176+
/// Create a SqlConnection, for use by validation jobs.
177+
/// </summary>
178+
public Task<SqlConnection> CreateSqlConnectionAsync<T>()
179+
where T : IDbConfiguration
180+
{
181+
var name = GetDatabaseKey<T>();
182+
if (!SqlConnectionFactories.ContainsKey(name))
183+
{
184+
throw new InvalidOperationException($"Database {name} has not been registered.");
185+
}
186+
187+
return SqlConnectionFactories[name].CreateAsync();
188+
}
189+
190+
/// <summary>
191+
/// Synchronous creation of a SqlConnection, for use by validation jobs.
192+
/// </summary>
193+
public SqlConnection CreateSqlConnection<T>()
194+
where T : IDbConfiguration
195+
{
196+
return Task.Run(() => CreateSqlConnectionAsync<T>()).Result;
197+
}
198+
199+
/// <summary>
200+
/// Creates and opens a SqlConnection, for use by non-validation jobs.
201+
/// </summary>
202+
public Task<SqlConnection> OpenSqlConnectionAsync(string connectionStringArgName)
203+
{
204+
if (string.IsNullOrEmpty(connectionStringArgName))
205+
{
206+
throw new ArgumentException("Argument cannot be null or empty.", nameof(connectionStringArgName));
207+
}
208+
209+
if (!SqlConnectionFactories.ContainsKey(connectionStringArgName))
210+
{
211+
throw new InvalidOperationException($"Database {connectionStringArgName} has not been registered.");
212+
}
213+
214+
return SqlConnectionFactories[connectionStringArgName].OpenAsync();
215+
}
42216
}
43217
}

src/NuGet.Jobs.Common/NuGet.Jobs.Common.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
<Compile Include="Configuration\ValidationDbConfiguration.cs" />
5151
<Compile Include="Configuration\ValidationStorageConfiguration.cs" />
5252
<Compile Include="Extensions\LoggerExtensions.cs" />
53-
<Compile Include="Extensions\SqlConnectionStringBuilderExtensions.cs" />
5453
<Compile Include="Extensions\XElementExtensions.cs" />
5554
<Compile Include="SecretReader\ISecretReaderFactory.cs" />
5655
<Compile Include="SecretReader\SecretReaderFactory.cs" />
@@ -83,6 +82,9 @@
8382
<PackageReference Include="NuGet.Services.Logging">
8483
<Version>2.27.0</Version>
8584
</PackageReference>
85+
<PackageReference Include="NuGet.Services.Sql">
86+
<Version>2.27.0</Version>
87+
</PackageReference>
8688
<PackageReference Include="System.Net.Http">
8789
<Version>4.3.3</Version>
8890
</PackageReference>

src/NuGet.Services.Validation.Orchestrator/NuGet.Services.Validation.Orchestrator.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@
127127
<Version>1.2.0</Version>
128128
</PackageReference>
129129
<PackageReference Include="NuGet.Services.Sql">
130-
<Version>2.26.0-master-34394</Version>
130+
<Version>2.27.0</Version>
131131
</PackageReference>
132132
<PackageReference Include="NuGet.Services.Validation.Issues">
133133
<Version>2.27.0-master-35351</Version>

src/NuGet.SupportRequests.Notifications/NuGet.SupportRequests.Notifications.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
<Version>9.0.1</Version>
108108
</PackageReference>
109109
<PackageReference Include="NuGet.Services.Sql">
110-
<Version>2.25.0-master-30453</Version>
110+
<Version>2.27.0</Version>
111111
</PackageReference>
112112
</ItemGroup>
113113
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

src/Search.GenerateAuxiliaryData/Search.GenerateAuxiliaryData.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
<Version>9.0.1</Version>
9494
</PackageReference>
9595
<PackageReference Include="NuGet.Services.Sql">
96-
<Version>2.25.0-master-30453</Version>
96+
<Version>2.27.0</Version>
9797
</PackageReference>
9898
<PackageReference Include="WindowsAzure.Storage">
9999
<Version>7.1.2</Version>

0 commit comments

Comments
 (0)