From c56ff367902325592626fb40912990eda12a8fec Mon Sep 17 00:00:00 2001 From: Trent Mohay Date: Mon, 6 May 2024 15:51:34 +1000 Subject: [PATCH 1/4] wiring in the secret client --- build/_build.csproj.DotSettings | 5 +- .../Calamari.Testing/Calamari.Testing.csproj | 1 + .../Calamari.Testing/EnvironmentVariables.cs | 52 ++++++++++++++++--- source/Calamari.sln.DotSettings | 3 ++ 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/build/_build.csproj.DotSettings b/build/_build.csproj.DotSettings index 7bc28484c..337271da9 100644 --- a/build/_build.csproj.DotSettings +++ b/build/_build.csproj.DotSettings @@ -16,6 +16,8 @@ False <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> True True True @@ -24,4 +26,5 @@ True True True - True + True + True diff --git a/source/Calamari.Testing/Calamari.Testing.csproj b/source/Calamari.Testing/Calamari.Testing.csproj index cec30973a..c3ef3f960 100644 --- a/source/Calamari.Testing/Calamari.Testing.csproj +++ b/source/Calamari.Testing/Calamari.Testing.csproj @@ -24,6 +24,7 @@ + diff --git a/source/Calamari.Testing/EnvironmentVariables.cs b/source/Calamari.Testing/EnvironmentVariables.cs index d0719f560..0149d159b 100644 --- a/source/Calamari.Testing/EnvironmentVariables.cs +++ b/source/Calamari.Testing/EnvironmentVariables.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; using System.Linq; using Calamari.Common.Plumbing.Logging; +using Microsoft.Extensions.Logging; using NUnit.Framework; +using OnePasswordSdk; namespace Calamari.Testing { @@ -22,25 +25,25 @@ public enum ExternalVariable [EnvironmentVariable("Azure_OctopusAPITester_Certificate", "Azure - OctopusAPITester")] AzureSubscriptionCertificate, - [EnvironmentVariable("GitHub_OctopusAPITester_Username", "GitHub Test Account")] + [EnvironmentVariable("GitHub_OctopusAPITester_Username", "GitHub Test Account", "op://Calamari Secrets for Tests/Github Octopus Api Tester Username")] GitHubUsername, - [EnvironmentVariable("GitHub_OctopusAPITester_Password", "GitHub Test Account")] + [EnvironmentVariable("GitHub_OctopusAPITester_Password", "GitHub Test Account", "op://Calamari Secrets for Tests/Github Octopus Api Tester Password")] GitHubPassword, - [EnvironmentVariable("K8S_OctopusAPITester_Token", "GKS Kubernetes API Test Cluster Token")] + [EnvironmentVariable("K8S_OctopusAPITester_Token", "GKS Kubernetes API Test Cluster Token", "op://Calamari Secrets for Tests/K8s Octopus Api Tester Token")] KubernetesClusterToken, - [EnvironmentVariable("K8S_OctopusAPITester_Server", "GKS Kubernetes API Test Cluster Url")] + [EnvironmentVariable("K8S_OctopusAPITester_Server", "GKS Kubernetes API Test Cluster Url", "op://Calamari Secrets for Tests/K8s Octopus Api Test Server")] KubernetesClusterUrl, [EnvironmentVariable("Helm_OctopusAPITester_Password", "Artifactory Test Account")] HelmPassword, - [EnvironmentVariable("Artifactory_Admin_PAT", "Jfrog Artifactory Admin PAT")] + [EnvironmentVariable("Artifactory_Admin_PAT", "Jfrog Artifactory Admin PAT", "op://Calamari Secrets for Tests/Artifactory_Admin_PAT")] ArtifactoryE2EPassword, - [EnvironmentVariable("DockerHub_TestReaderAccount_Password", "DockerHub Test Reader Account")] + [EnvironmentVariable("DockerHub_TestReaderAccount_Password", "DockerHub Test Reader Account", "op://Calamari Secrets for Tests/DockerHub Test Reader Account Password")] DockerReaderPassword, [EnvironmentVariable("AWS_E2E_AccessKeyId", "AWS E2E Test User Keys")] @@ -70,7 +73,7 @@ public enum ExternalVariable [EnvironmentVariable("CALAMARI_AUTHPASSWORD", "OctopusMyGetTester")] MyGetFeedPassword, - [EnvironmentVariable("GOOGLECLOUD_OCTOPUSAPITESTER_JSONKEY", "GoogleCloud - OctopusAPITester")] + [EnvironmentVariable("GOOGLECLOUD_OCTOPUSAPITESTER_JSONKEY", "GoogleCloud - OctopusAPITester", "op://Calamari Secrets for Tests/Google Cloud Octopus Api Tester JsonKey")] GoogleCloudJsonKeyfile, [EnvironmentVariable("GitHub_RateLimitingPersonalAccessToken", "GitHub test account PAT")] @@ -79,6 +82,19 @@ public enum ExternalVariable public static class ExternalVariables { + static readonly bool SecretManagerIsEnabled = Convert.ToBoolean(Environment.GetEnvironmentVariable("CALAMARI__Tests__SecretManagerEnabled") ?? "True"); + static readonly string SecretManagerAccount = Environment.GetEnvironmentVariable("CALAMARI__Tests__SecretManagerAccount") ?? "octopusdeploy.1password.com"; + + static readonly Lazy SecretManagerClient = new(LoadSecretManagerClient); + + static SecretManagerClient LoadSecretManagerClient() + { + var allVariableAttributes = EnvironmentVariableAttribute.GetAll().Select(x => x.SecretReference).WhereNotNull().ToArray(); + var loggerFactory = new LoggerFactory().AddSerilog(Logger); + var microsoftLogger = loggerFactory.CreateLogger(); + return new SecretManagerClient(SecretManagerAccount, allVariableAttributes, microsoftLogger); + } + public static void LogMissingVariables() { var missingVariables = Enum.GetValues(typeof(ExternalVariable)).Cast() @@ -117,10 +133,12 @@ class EnvironmentVariableAttribute : Attribute { public string Name { get; } public string LastPassName { get; } - public EnvironmentVariableAttribute(string name, string lastPassName) + public string? SecretReference { get; } + public EnvironmentVariableAttribute(string name, string lastPassName, string? secretReference = null) { Name = name; LastPassName = lastPassName; + SecretReference = secretReference; } public static EnvironmentVariableAttribute? Get(object enm) @@ -133,5 +151,23 @@ public EnvironmentVariableAttribute(string name, string lastPassName) return GetCustomAttribute(mi[0], typeof(EnvironmentVariableAttribute)) as EnvironmentVariableAttribute; } + + /// + /// Retrieves a list of from all enum types in the current application domain. + /// + /// A list of . + internal static ICollection GetAll() + { + // var envVarAttributes = AppDomainScanner.OctopusAssemblies + // .Where(a => a.FullName?.Contains("Test", StringComparison.OrdinalIgnoreCase) == true) + // .SelectMany(a => a.GetTypes()) + // .Where(t => t.IsEnum) + // .SelectMany(t => t.GetFields()) + // .Select(field => field.GetCustomAttribute()) + // .WhereNotNull() + // .ToArray(); + + return Array.Empty(); + } } } diff --git a/source/Calamari.sln.DotSettings b/source/Calamari.sln.DotSettings index 40f8ab9ad..355fd40ec 100644 --- a/source/Calamari.sln.DotSettings +++ b/source/Calamari.sln.DotSettings @@ -199,11 +199,14 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> True True True True True + True True True From 086995dde3a6197682ef5250022c315dd954e8eb Mon Sep 17 00:00:00 2001 From: Trent Mohay Date: Mon, 6 May 2024 16:04:32 +1000 Subject: [PATCH 2/4] plumbing seems done --- .../Calamari.Testing/EnvironmentVariables.cs | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/source/Calamari.Testing/EnvironmentVariables.cs b/source/Calamari.Testing/EnvironmentVariables.cs index 0149d159b..a366e2163 100644 --- a/source/Calamari.Testing/EnvironmentVariables.cs +++ b/source/Calamari.Testing/EnvironmentVariables.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Calamari.Common.Plumbing.Logging; using Microsoft.Extensions.Logging; using NUnit.Framework; +using NUnit.Framework.Internal; using OnePasswordSdk; namespace Calamari.Testing @@ -89,8 +92,11 @@ public static class ExternalVariables static SecretManagerClient LoadSecretManagerClient() { - var allVariableAttributes = EnvironmentVariableAttribute.GetAll().Select(x => x.SecretReference).WhereNotNull().ToArray(); - var loggerFactory = new LoggerFactory().AddSerilog(Logger); + var allVariableAttributes = EnvironmentVariableAttribute.GetAll() + .Select(x => x.SecretReference) + .Where(x => x is not null) + .ToArray(); + var loggerFactory = new LoggerFactory(); //.AddSerilog(Logger); var microsoftLogger = loggerFactory.CreateLogger(); return new SecretManagerClient(SecretManagerAccount, allVariableAttributes, microsoftLogger); } @@ -110,7 +116,7 @@ public static void LogMissingVariables() $"\n\nTests that rely on these variables are likely to fail."); } - public static string Get(ExternalVariable property) + public static async Task Get(ExternalVariable property, CancellationToken cancellationToken) { var attr = EnvironmentVariableAttribute.Get(property); if (attr == null) @@ -119,12 +125,24 @@ public static string Get(ExternalVariable property) } var valueFromEnv = Environment.GetEnvironmentVariable(attr.Name); - if (valueFromEnv == null) + if (valueFromEnv != null) { - throw new Exception($"Environment Variable `{attr.Name}` could not be found. The value can be found in the password store under `{attr.LastPassName}`"); + return valueFromEnv; + //throw new Exception($"Environment Variable `{attr.Name}` could not be found. The value can be found in the password store under `{attr.LastPassName}`"); + } + + if (SecretManagerIsEnabled) + { + var valueFromSecretManager = string.IsNullOrEmpty(attr.SecretReference) + ? null + : await SecretManagerClient.Value.GetSecret(attr.SecretReference, cancellationToken, throwOnNotFound: false); + if (!string.IsNullOrEmpty(valueFromSecretManager)) + { + return valueFromSecretManager; + } } - return valueFromEnv; + throw new Exception($"Unable to find `{attr.Name}` as either an Environment Variable, or a SecretReference. The value can be found in the password store under `{attr.LastPassName}`"); } } From 410f54422de52ccc9f622a2580a17f371b6e3ade Mon Sep 17 00:00:00 2001 From: Trent Mohay Date: Mon, 6 May 2024 16:19:00 +1000 Subject: [PATCH 3/4] update the 'GetAll' function to scan for test assemblies' --- .../Calamari.Testing/EnvironmentVariables.cs | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/source/Calamari.Testing/EnvironmentVariables.cs b/source/Calamari.Testing/EnvironmentVariables.cs index a366e2163..529802e61 100644 --- a/source/Calamari.Testing/EnvironmentVariables.cs +++ b/source/Calamari.Testing/EnvironmentVariables.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using Calamari.Common.Plumbing.Logging; @@ -60,13 +61,13 @@ public enum ExternalVariable [EnvironmentVariable("CALAMARI_FEEDZV3URI", "Not LastPass; Calamari TC Config Variables")] FeedzNuGetV3FeedUrl, - + [EnvironmentVariable("CALAMARI_ARTIFACTORYV2URI", "Not LastPass; Calamari TC Config Variables")] ArtifactoryNuGetV2FeedUrl, [EnvironmentVariable("CALAMARI_ARTIFACTORYV3URI", "Not LastPass; Calamari TC Config Variables")] ArtifactoryNuGetV3FeedUrl, - + [EnvironmentVariable("CALAMARI_AUTHURI", "OctopusMyGetTester")] MyGetFeedUrl, @@ -78,7 +79,7 @@ public enum ExternalVariable [EnvironmentVariable("GOOGLECLOUD_OCTOPUSAPITESTER_JSONKEY", "GoogleCloud - OctopusAPITester", "op://Calamari Secrets for Tests/Google Cloud Octopus Api Tester JsonKey")] GoogleCloudJsonKeyfile, - + [EnvironmentVariable("GitHub_RateLimitingPersonalAccessToken", "GitHub test account PAT")] GitHubRateLimitingPersonalAccessToken, } @@ -89,7 +90,7 @@ public static class ExternalVariables static readonly string SecretManagerAccount = Environment.GetEnvironmentVariable("CALAMARI__Tests__SecretManagerAccount") ?? "octopusdeploy.1password.com"; static readonly Lazy SecretManagerClient = new(LoadSecretManagerClient); - + static SecretManagerClient LoadSecretManagerClient() { var allVariableAttributes = EnvironmentVariableAttribute.GetAll() @@ -100,10 +101,11 @@ static SecretManagerClient LoadSecretManagerClient() var microsoftLogger = loggerFactory.CreateLogger(); return new SecretManagerClient(SecretManagerAccount, allVariableAttributes, microsoftLogger); } - + public static void LogMissingVariables() { - var missingVariables = Enum.GetValues(typeof(ExternalVariable)).Cast() + var missingVariables = Enum.GetValues(typeof(ExternalVariable)) + .Cast() .Select(prop => EnvironmentVariableAttribute.Get(prop)) .Where(attr => Environment.GetEnvironmentVariable(attr.Name) == null) .ToList(); @@ -111,9 +113,7 @@ public static void LogMissingVariables() if (!missingVariables.Any()) return; - Log.Warn($"The following environment variables could not be found: " + - $"\n{string.Join("\n", missingVariables.Select(var => $" - {var.Name}\t\tSource: {var.LastPassName}"))}" + - $"\n\nTests that rely on these variables are likely to fail."); + Log.Warn($"The following environment variables could not be found: " + $"\n{string.Join("\n", missingVariables.Select(var => $" - {var.Name}\t\tSource: {var.LastPassName}"))}" + $"\n\nTests that rely on these variables are likely to fail."); } public static async Task Get(ExternalVariable property, CancellationToken cancellationToken) @@ -130,7 +130,7 @@ public static async Task Get(ExternalVariable property, CancellationToke return valueFromEnv; //throw new Exception($"Environment Variable `{attr.Name}` could not be found. The value can be found in the password store under `{attr.LastPassName}`"); } - + if (SecretManagerIsEnabled) { var valueFromSecretManager = string.IsNullOrEmpty(attr.SecretReference) @@ -152,6 +152,7 @@ class EnvironmentVariableAttribute : Attribute public string Name { get; } public string LastPassName { get; } public string? SecretReference { get; } + public EnvironmentVariableAttribute(string name, string lastPassName, string? secretReference = null) { Name = name; @@ -169,23 +170,24 @@ public EnvironmentVariableAttribute(string name, string lastPassName, string? se return GetCustomAttribute(mi[0], typeof(EnvironmentVariableAttribute)) as EnvironmentVariableAttribute; } - + /// /// Retrieves a list of from all enum types in the current application domain. /// /// A list of . internal static ICollection GetAll() { - // var envVarAttributes = AppDomainScanner.OctopusAssemblies - // .Where(a => a.FullName?.Contains("Test", StringComparison.OrdinalIgnoreCase) == true) - // .SelectMany(a => a.GetTypes()) - // .Where(t => t.IsEnum) - // .SelectMany(t => t.GetFields()) - // .Select(field => field.GetCustomAttribute()) - // .WhereNotNull() - // .ToArray(); - - return Array.Empty(); + // inferred from OctopusDeploy.AppDomainScanner + return AppDomain.CurrentDomain.GetAssemblies() + .Where(assembly => !assembly.IsDynamic) + .Where(assembly => assembly.GetName().Name is not null && assembly.GetName().Name.StartsWith("Calamari")) + .Where(a => a.FullName?.Contains("Test", StringComparison.OrdinalIgnoreCase) == true) + .SelectMany(a => a.GetTypes()) + .Where(t => t.IsEnum) + .SelectMany(t => t.GetFields()) + .Select(field => field.GetCustomAttribute()) + .Where(f => f is not null) + .ToArray(); } } -} +} \ No newline at end of file From 990cbc513c4197a022217508c22a82e3e9fd5700 Mon Sep 17 00:00:00 2001 From: Trent Mohay Date: Mon, 6 May 2024 16:36:27 +1000 Subject: [PATCH 4/4] upgrade version of polly --- .../Calamari.AzureAppService.Tests.csproj | 2 +- source/Calamari.Tests/Calamari.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj b/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj index a21001a9c..3cf504966 100644 --- a/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj +++ b/source/Calamari.AzureAppService.Tests/Calamari.AzureAppService.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/source/Calamari.Tests/Calamari.Tests.csproj b/source/Calamari.Tests/Calamari.Tests.csproj index a894fdc81..5d913c6f9 100644 --- a/source/Calamari.Tests/Calamari.Tests.csproj +++ b/source/Calamari.Tests/Calamari.Tests.csproj @@ -28,7 +28,7 @@ - +