Skip to content

Commit

Permalink
Tool for running SQL AAD connection tests (#181)
Browse files Browse the repository at this point in the history
  • Loading branch information
chenriksson authored Aug 1, 2018
1 parent c6cb66f commit 67f35d6
Show file tree
Hide file tree
Showing 8 changed files with 498 additions and 0 deletions.
9 changes: 9 additions & 0 deletions NuGet.Server.Common.sln
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGet.Services.Status.Table
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGet.Services.Incidents", "src\NuGet.Services.Incidents\NuGet.Services.Incidents.csproj", "{09B15FA8-8B24-4C9A-B2AC-1265ACCB9EB1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{2E74C554-630C-4ED9-BB66-81CFA5DFFBD3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AzureSqlConnectionTest", "tools\AzureSqlConnectionTest\AzureSqlConnectionTest.csproj", "{109E6F0E-CBBC-427A-89CF-8F8AACE479CF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -192,6 +196,10 @@ Global
{09B15FA8-8B24-4C9A-B2AC-1265ACCB9EB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09B15FA8-8B24-4C9A-B2AC-1265ACCB9EB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09B15FA8-8B24-4C9A-B2AC-1265ACCB9EB1}.Release|Any CPU.Build.0 = Release|Any CPU
{109E6F0E-CBBC-427A-89CF-8F8AACE479CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{109E6F0E-CBBC-427A-89CF-8F8AACE479CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{109E6F0E-CBBC-427A-89CF-8F8AACE479CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{109E6F0E-CBBC-427A-89CF-8F8AACE479CF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -226,6 +234,7 @@ Global
{EA54BC71-F9AD-4863-8DC6-8CC41776C881} = {7783A106-0F4C-4055-9AB4-413FB2C7B8F0}
{8D3E82F2-B637-4BA4-BD80-3380E826CA1A} = {8415FED7-1BED-4227-8B4F-BB7C24E041CD}
{09B15FA8-8B24-4C9A-B2AC-1265ACCB9EB1} = {8415FED7-1BED-4227-8B4F-BB7C24E041CD}
{109E6F0E-CBBC-427A-89CF-8F8AACE479CF} = {2E74C554-630C-4ED9-BB66-81CFA5DFFBD3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AA413DB0-5475-4B5D-A3AF-6323DA8D538B}
Expand Down
10 changes: 10 additions & 0 deletions src/NuGet.Services.Sql/AzureSqlConnectionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ public class AzureSqlConnectionFactory : ISqlConnectionFactory

#endregion

public AzureSqlConnectionFactory(
AzureSqlConnectionStringBuilder connectionString,
ISecretInjector secretInjector,
ILogger logger = null)
{
ConnectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
SecretInjector = secretInjector ?? throw new ArgumentNullException(nameof(secretInjector));
Logger = logger;
}

public AzureSqlConnectionFactory(string connectionString, ISecretInjector secretInjector, ILogger logger = null)
{
if (string.IsNullOrEmpty(connectionString))
Expand Down
6 changes: 6 additions & 0 deletions tools/AzureSqlConnectionTest/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
</startup>
</configuration>
70 changes: 70 additions & 0 deletions tools/AzureSqlConnectionTest/AzureSqlConnectionTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{109E6F0E-CBBC-427A-89CF-8F8AACE479CF}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>AzureSqlConnectionTest</RootNamespace>
<AssemblyName>AzureSqlConnectionTest</AssemblyName>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestApplication.cs" />
<Compile Include="TestRunner.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\NuGet.Services.KeyVault\NuGet.Services.KeyVault.csproj">
<Project>{c87d0ef1-54aa-4b0b-89de-cff2dc941d11}</Project>
<Name>NuGet.Services.KeyVault</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\NuGet.Services.Sql\NuGet.Services.Sql.csproj">
<Project>{f5121b0a-669f-48bd-86dc-27c546d1a825}</Project>
<Name>NuGet.Services.Sql</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.CommandLineUtils">
<Version>1.1.1</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
13 changes: 13 additions & 0 deletions tools/AzureSqlConnectionTest/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// 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.

namespace AzureSqlConnectionTest
{
class Program
{
static void Main(string[] args)
{
new TestApplication().Execute(args);
}
}
}
17 changes: 17 additions & 0 deletions tools/AzureSqlConnectionTest/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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.Reflection;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("AzureSqlConnectionTest")]
[assembly: AssemblyDescription("Tool for testing AAD authenticated SQL connections using NuGet.Services.Sql")]
[assembly: AssemblyProduct("AzureSqlConnectionTest")]
[assembly: AssemblyCopyright("Copyright © .NET Foundation 2017")]
[assembly: ComVisible(false)]
[assembly: Guid("109e6f0e-cbbc-427a-89cf-8f8aace479cf")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
170 changes: 170 additions & 0 deletions tools/AzureSqlConnectionTest/TestApplication.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// 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;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Extensions.CommandLineUtils;
using NuGet.Services.KeyVault;

namespace AzureSqlConnectionTest
{
public class TestApplication : CommandLineApplication
{
public CommandOption Help { get; }

public CommandOption KeyVaultName { get; }

public CommandOption KeyVaultClientId { get; }

public CommandOption KeyVaultCertificateThumbprint { get; }

public CommandOption ConnectionString { get; }

public CommandOption Count { get; }

public CommandOption PersistConnections { get; }

public CommandOption DurationInSeconds { get; }

public CommandOption IntervalInSeconds { get; }

public CommandOption SpawnClients { get; }

public CommandOption UseAdalOnly { get; }

public TestApplication()
{
Help = HelpOption("-? | -h | --help");

KeyVaultName = Option("-kv | --keyVaultName", "KeyVault name", CommandOptionType.SingleValue);
KeyVaultClientId = Option("-kvcid | --keyVaultClientId", "KeyVault client id", CommandOptionType.SingleValue);
KeyVaultCertificateThumbprint = Option("-kvct | --keyVaultCertThumbprint", "KeyVault certificate thumbprint", CommandOptionType.SingleValue);

ConnectionString = Option("-cs | --connectionString", "SQL connection string", CommandOptionType.SingleValue);

Count = Option("-c | --count", "Client count", CommandOptionType.SingleValue);
PersistConnections = Option("-p | --persist", "Persist connections", CommandOptionType.NoValue);
DurationInSeconds = Option("-d | --duration", "Duration in seconds", CommandOptionType.SingleValue);
IntervalInSeconds = Option("-i | --interval", "Sleep interval in seconds", CommandOptionType.SingleValue);

SpawnClients = Option("-spawn", "Spawn client processes", CommandOptionType.NoValue);
UseAdalOnly = Option("-adal", "Use ADAL only (default token cache)", CommandOptionType.NoValue);

OnExecute(() => ExecuteAsync().GetAwaiter().GetResult());
}

public async Task<int> ExecuteAsync()
{
if (!Help.HasValue())
{
var count = IntValue(Count, defaultValue: 1);
var durationInSeconds = IntValue(DurationInSeconds, defaultValue: 60);
var intervalInSeconds = IntValue(IntervalInSeconds, defaultValue: 15);

if (count > 1 && SpawnClients.HasValue())
{
return await SpawnTestClients(count, durationInSeconds, intervalInSeconds);
}

var persist = PersistConnections.HasValue();
var useAdalOnly = UseAdalOnly.HasValue();

if (KeyVaultName.HasValue()
&& KeyVaultClientId.HasValue()
&& KeyVaultCertificateThumbprint.HasValue())
{
using (var kvCertificate = GetKeyVaultCertificate(KeyVaultCertificateThumbprint.Value()))
{
var keyVaultConfig = new KeyVaultConfiguration(
KeyVaultName.Value(),
KeyVaultClientId.Value(),
kvCertificate);

var runner = new TestRunner(
ConnectionString.Value(),
keyVaultConfig);

return await runner.TestConnectionsAsync(
count,
durationInSeconds,
intervalInSeconds,
persist,
useAdalOnly);
}
}
}

ShowHelp();

return 1;
}

private async Task<int> SpawnTestClients(int count, int durationInSeconds, int intervalInSeconds)
{
var clients = new Task<int>[count];
for (int i = 0; i < count; i++)
{
clients[i] = Task.Run(() => SpawnTestClient(durationInSeconds, intervalInSeconds));
}

await Task.WhenAll(clients);

return clients.Count(c => c.Result != 0);
}

private int SpawnTestClient(int durationInSeconds, int intervalInSeconds)
{
var commandLine = GetTestClientCommandLine();

var client = Process.Start(commandLine[0], commandLine[1]);
if (!client.WaitForExit((durationInSeconds + (intervalInSeconds * 2)) * 1000))
{
client.Kill();
return 1;
}

return client.ExitCode;
}

private static string[] GetTestClientCommandLine()
{
var commandLine = Environment.CommandLine;

commandLine = Regex.Replace(commandLine,
"-c(ount)? \\d+",
"",
RegexOptions.None,
TimeSpan.FromSeconds(5));

commandLine = Regex.Replace(commandLine,
"-spawn",
"",
RegexOptions.None,
TimeSpan.FromSeconds(5));

var parts = commandLine.Split(' ');
var arguments = parts
.Skip(1)
.Where(p => !String.IsNullOrEmpty(p));

return new[] { parts[0], String.Join(" ", arguments) };
}

private static int IntValue(CommandOption option, int defaultValue = -1)
{
return option.HasValue() && Int32.TryParse(option.Value(), out var result)
? result
: defaultValue;
}

private static X509Certificate2 GetKeyVaultCertificate(string kvCertThumbprint)
{
return CertificateUtility.FindCertificateByThumbprint(
StoreName.My, StoreLocation.LocalMachine, kvCertThumbprint, validationRequired: true);
}
}
}
Loading

0 comments on commit 67f35d6

Please sign in to comment.