From 906d7d1e51c22f5051087bc7cd1bdc3d86bdecd7 Mon Sep 17 00:00:00 2001 From: Antonstockmarr <43210147+Antonstockmarr@users.noreply.github.com> Date: Fri, 1 Oct 2021 06:10:20 -0700 Subject: [PATCH 1/5] Enable single execution deployment Allows the user to run the tool with argument "--deploy", which runs all steps required for setting up hub and spoke. Also allows "--clean-storage", which will clean all Azure storage accounts. --- src/CLI/ArgumentParser.cs | 44 +++++++++++++++ src/CLI/Deployer.cs | 115 ++++++++++++++++++++++++++++++++++++++ src/CLI/Program.cs | 31 +++++++++- 3 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 src/CLI/ArgumentParser.cs create mode 100644 src/CLI/Deployer.cs diff --git a/src/CLI/ArgumentParser.cs b/src/CLI/ArgumentParser.cs new file mode 100644 index 0000000..b504aa1 --- /dev/null +++ b/src/CLI/ArgumentParser.cs @@ -0,0 +1,44 @@ +using System; + +namespace CLI +{ + class ArgumentParser + { + public bool deploy = false; + public bool cleanStorage = false; + private bool[] matched; + private string[] arguments; + + public void Parse(string[] args) + { + arguments = args; + matched = new bool[args.Length]; + for (int i = 0; i < matched.Length; i++) + { + matched[i] = false; + } + + deploy = ParseArgument("--deploy"); + cleanStorage = ParseArgument("--clean-storage"); + + for (int i = 0; i < matched.Length; i++) + { + if (matched[i] == false) + { + throw new Exception($"Did not recognize argument {args[i]}. Allowed arguments are \"--deploy\" and \"--clean-storage\""); + } + } + } + + private bool ParseArgument(string argument) + { + var cliOption = Array.FindIndex(arguments, option => option.Equals(argument)); + if (cliOption != -1) + { + matched[cliOption] = true; + return true; + } + return false; + } + } +} diff --git a/src/CLI/Deployer.cs b/src/CLI/Deployer.cs new file mode 100644 index 0000000..255e73e --- /dev/null +++ b/src/CLI/Deployer.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using ScaleUnitManagement.ScaleUnitFeatureManager.Common; +using ScaleUnitManagement.ScaleUnitFeatureManager.Hub; +using ScaleUnitManagement.ScaleUnitFeatureManager.ScaleUnit; +using ScaleUnitManagement.ScaleUnitFeatureManager.Utilities; +using ScaleUnitManagement.Utilities; +using ScaleUnitManagement.WorkloadSetupOrchestrator; + +namespace CLI +{ + internal class Deployer + { + public async Task Deploy() + { + await InitializeEnvironments(); + await PrepareEnvironments(); + await InstallWorkloads(); + + Console.WriteLine("\nHub and spoke environments have been deployed successfully!\n"); + } + + public async Task CleanStorage() + { + foreach (ScaleUnitInstance scaleUnit in Config.ScaleUnitInstances()) + { + Console.WriteLine($"\nCleaning environment on {scaleUnit.PrintableName()}"); + using var context = ScaleUnitContext.CreateContext(scaleUnit.ScaleUnitId); + var storageAccountManager = new StorageAccountManager(); + await storageAccountManager.CleanStorageAccount(); + } + } + + private async Task InitializeEnvironments() + { + foreach (ScaleUnitInstance scaleUnit in Config.ScaleUnitInstances()) + { + Console.WriteLine($"\nInitializing environment on {scaleUnit.PrintableName()}"); + using var context = ScaleUnitContext.CreateContext(scaleUnit.ScaleUnitId); + List steps = GetSteps(); + await RunSteps(steps); + + Console.WriteLine($"\nAll initialization steps completed for {scaleUnit.PrintableName()}"); + } + } + + private List GetSteps() + { + var sf = new StepFactory(); + List steps = sf.GetStepsOfType(); + if (ScaleUnitContext.GetScaleUnitId() == "@@") + { + steps.AddRange(sf.GetStepsOfType()); + } + else + { + steps.AddRange(sf.GetStepsOfType()); + } + steps.Sort((x, y) => x.Priority().CompareTo(y.Priority())); + return steps; + } + + private async Task RunSteps(List steps) + { + for (int i = 0; i < steps.Count; i++) + { + try + { + Console.WriteLine("\nExecuting step: " + steps[i].Label()); + await steps[i].Run(); + } + catch (Exception ex) + { + Console.Error.WriteLine($"Error occurred while enabling scale unit feature:\n{ex}"); + } + } + } + + private async Task PrepareEnvironments() + { + foreach (ScaleUnitInstance scaleUnit in Config.ScaleUnitInstances()) + { + using var context = ScaleUnitContext.CreateContext(scaleUnit.ScaleUnitId); + Console.WriteLine($"\nPreparing {scaleUnit.PrintableName()} for installation"); + if (scaleUnit.ScaleUnitId.Equals("@@")) + { + await new HubConfigurationManager().Configure(); + } + else + { + await new ScaleUnitConfigurationManager().Configure(); + } + } + } + + private async Task InstallWorkloads() + { + Console.WriteLine("\nInstalling workloads on hub"); + using (var context = ScaleUnitContext.CreateContext("@@")) + { + await new HubWorkloadInstaller().Install(); + } + + foreach (ScaleUnitInstance scaleUnit in Config.NonHubScaleUnitInstances()) + { + using var context = ScaleUnitContext.CreateContext(scaleUnit.ScaleUnitId); + Console.WriteLine($"\nInstalling workloads on {scaleUnit.PrintableName()}"); + await new ScaleUnitWorkloadInstaller().Install(); + } + } + } +} + + diff --git a/src/CLI/Program.cs b/src/CLI/Program.cs index 430d224..ec9f952 100644 --- a/src/CLI/Program.cs +++ b/src/CLI/Program.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using CLIFramework; using ScaleUnitManagement.ScaleUnitFeatureManager.Utilities; @@ -6,12 +7,38 @@ namespace CLI { public class Program { - public static async Task Main(string[] args) { CheckForAdminAccess.ValidateCurrentUserIsProcessAdmin(); - await CLIController.Run(new RootMenu()); + if (args.Length == 0) + { + await CLIController.Run(new RootMenu()); + return; + } + + var arguments = new ArgumentParser(); + try + { + arguments.Parse(args); + } + catch (Exception ex) + { + Console.WriteLine(ex); + return; + } + + var deployer = new Deployer(); + + if (arguments.cleanStorage) + { + await deployer.CleanStorage(); + } + + if (arguments.deploy) + { + await deployer.Deploy(); + } } } } From ad85f26f749eb0f2c37271e38c37fee9ed162ccf Mon Sep 17 00:00:00 2001 From: Antonstockmarr <43210147+Antonstockmarr@users.noreply.github.com> Date: Fri, 1 Oct 2021 06:52:07 -0700 Subject: [PATCH 2/5] Update ArgumentParser.cs --- src/CLI/ArgumentParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CLI/ArgumentParser.cs b/src/CLI/ArgumentParser.cs index b504aa1..e4016e3 100644 --- a/src/CLI/ArgumentParser.cs +++ b/src/CLI/ArgumentParser.cs @@ -23,7 +23,7 @@ public void Parse(string[] args) for (int i = 0; i < matched.Length; i++) { - if (matched[i] == false) + if (!matched[i]) { throw new Exception($"Did not recognize argument {args[i]}. Allowed arguments are \"--deploy\" and \"--clean-storage\""); } @@ -32,7 +32,7 @@ public void Parse(string[] args) private bool ParseArgument(string argument) { - var cliOption = Array.FindIndex(arguments, option => option.Equals(argument)); + int cliOption = Array.FindIndex(arguments, option => option.Equals(argument)); if (cliOption != -1) { matched[cliOption] = true; From 4e9999dbc792646631b881d6beaaf78fd85c4b1a Mon Sep 17 00:00:00 2001 From: Antonstockmarr <43210147+Antonstockmarr@users.noreply.github.com> Date: Mon, 18 Oct 2021 06:12:09 -0700 Subject: [PATCH 3/5] Checking that the environment is SingleOneBox Deployment only works in single one box. Adds error handling to ensure this. --- src/CLI/ArgumentParser.cs | 4 ++-- src/CLI/Deployer.cs | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/CLI/ArgumentParser.cs b/src/CLI/ArgumentParser.cs index e4016e3..55c7b93 100644 --- a/src/CLI/ArgumentParser.cs +++ b/src/CLI/ArgumentParser.cs @@ -18,14 +18,14 @@ public void Parse(string[] args) matched[i] = false; } - deploy = ParseArgument("--deploy"); + deploy = ParseArgument("--single-box-deploy"); cleanStorage = ParseArgument("--clean-storage"); for (int i = 0; i < matched.Length; i++) { if (!matched[i]) { - throw new Exception($"Did not recognize argument {args[i]}. Allowed arguments are \"--deploy\" and \"--clean-storage\""); + throw new Exception($"Did not recognize argument {args[i]}. Allowed arguments are \"--single-box-deploy\" and \"--clean-storage\""); } } } diff --git a/src/CLI/Deployer.cs b/src/CLI/Deployer.cs index 255e73e..1244086 100644 --- a/src/CLI/Deployer.cs +++ b/src/CLI/Deployer.cs @@ -14,6 +14,11 @@ internal class Deployer { public async Task Deploy() { + if (!Config.UseSingleOneBox()) + { + throw new Exception("You can only use automatic deployment in a SingleOneBox environment"); + } + await InitializeEnvironments(); await PrepareEnvironments(); await InstallWorkloads(); From 13e7442e1a15dee1c3e1f9707985eee8904baf03 Mon Sep 17 00:00:00 2001 From: Antonstockmarr <43210147+Antonstockmarr@users.noreply.github.com> Date: Mon, 18 Oct 2021 07:32:45 -0700 Subject: [PATCH 4/5] Sort scale units before deploying Initialize hub first and other scale units after. --- src/CLI/ArgumentParser.cs | 12 ++++-------- src/CLI/Deployer.cs | 31 ++++++++++++++++++++----------- src/CLI/Program.cs | 8 ++++---- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/CLI/ArgumentParser.cs b/src/CLI/ArgumentParser.cs index 55c7b93..585393f 100644 --- a/src/CLI/ArgumentParser.cs +++ b/src/CLI/ArgumentParser.cs @@ -4,8 +4,8 @@ namespace CLI { class ArgumentParser { - public bool deploy = false; - public bool cleanStorage = false; + public bool Deploy { get; set; } = false; + public bool CleanStorage { get; set; } = false; private bool[] matched; private string[] arguments; @@ -13,13 +13,9 @@ public void Parse(string[] args) { arguments = args; matched = new bool[args.Length]; - for (int i = 0; i < matched.Length; i++) - { - matched[i] = false; - } - deploy = ParseArgument("--single-box-deploy"); - cleanStorage = ParseArgument("--clean-storage"); + Deploy = ParseArgument("--single-box-deploy"); + CleanStorage = ParseArgument("--clean-storage"); for (int i = 0; i < matched.Length; i++) { diff --git a/src/CLI/Deployer.cs b/src/CLI/Deployer.cs index 1244086..26e6e2a 100644 --- a/src/CLI/Deployer.cs +++ b/src/CLI/Deployer.cs @@ -12,6 +12,14 @@ namespace CLI { internal class Deployer { + private readonly List sortedScaleUnitInstances; + + public Deployer() + { + sortedScaleUnitInstances = Config.ScaleUnitInstances(); + sortedScaleUnitInstances.Sort((s1, s2) => s1.ScaleUnitId.CompareTo(s2.ScaleUnitId)); + } + public async Task Deploy() { if (!Config.UseSingleOneBox()) @@ -28,7 +36,7 @@ public async Task Deploy() public async Task CleanStorage() { - foreach (ScaleUnitInstance scaleUnit in Config.ScaleUnitInstances()) + foreach (ScaleUnitInstance scaleUnit in sortedScaleUnitInstances) { Console.WriteLine($"\nCleaning environment on {scaleUnit.PrintableName()}"); using var context = ScaleUnitContext.CreateContext(scaleUnit.ScaleUnitId); @@ -39,7 +47,7 @@ public async Task CleanStorage() private async Task InitializeEnvironments() { - foreach (ScaleUnitInstance scaleUnit in Config.ScaleUnitInstances()) + foreach (ScaleUnitInstance scaleUnit in sortedScaleUnitInstances) { Console.WriteLine($"\nInitializing environment on {scaleUnit.PrintableName()}"); using var context = ScaleUnitContext.CreateContext(scaleUnit.ScaleUnitId); @@ -84,7 +92,7 @@ private async Task RunSteps(List steps) private async Task PrepareEnvironments() { - foreach (ScaleUnitInstance scaleUnit in Config.ScaleUnitInstances()) + foreach (ScaleUnitInstance scaleUnit in sortedScaleUnitInstances) { using var context = ScaleUnitContext.CreateContext(scaleUnit.ScaleUnitId); Console.WriteLine($"\nPreparing {scaleUnit.PrintableName()} for installation"); @@ -101,17 +109,18 @@ private async Task PrepareEnvironments() private async Task InstallWorkloads() { - Console.WriteLine("\nInstalling workloads on hub"); - using (var context = ScaleUnitContext.CreateContext("@@")) - { - await new HubWorkloadInstaller().Install(); - } - - foreach (ScaleUnitInstance scaleUnit in Config.NonHubScaleUnitInstances()) + foreach (ScaleUnitInstance scaleUnit in sortedScaleUnitInstances) { using var context = ScaleUnitContext.CreateContext(scaleUnit.ScaleUnitId); Console.WriteLine($"\nInstalling workloads on {scaleUnit.PrintableName()}"); - await new ScaleUnitWorkloadInstaller().Install(); + if (scaleUnit.IsHub()) + { + await new HubWorkloadInstaller().Install(); + } + else + { + await new ScaleUnitWorkloadInstaller().Install(); + } } } } diff --git a/src/CLI/Program.cs b/src/CLI/Program.cs index ec9f952..f59cbb4 100644 --- a/src/CLI/Program.cs +++ b/src/CLI/Program.cs @@ -17,10 +17,10 @@ public static async Task Main(string[] args) return; } - var arguments = new ArgumentParser(); + var argumentParser = new ArgumentParser(); try { - arguments.Parse(args); + argumentParser.Parse(args); } catch (Exception ex) { @@ -30,12 +30,12 @@ public static async Task Main(string[] args) var deployer = new Deployer(); - if (arguments.cleanStorage) + if (argumentParser.CleanStorage) { await deployer.CleanStorage(); } - if (arguments.deploy) + if (argumentParser.Deploy) { await deployer.Deploy(); } From 3725e2fde92e5371b4f41d50fa4428587a16705c Mon Sep 17 00:00:00 2001 From: Antonstockmarr <43210147+Antonstockmarr@users.noreply.github.com> Date: Tue, 19 Oct 2021 01:35:46 -0700 Subject: [PATCH 5/5] Add testing Unit testing of the ArgumentParser. Removing redundant Comparison. --- src/CLI/ArgumentParser.cs | 2 +- src/CLI/Deployer.cs | 2 +- .../ArgumentParserTest.cs | 52 +++++++++++++++++++ .../ScaleUnitManagementTests.csproj | 3 +- 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 src/ScaleUnitManagementTests/ArgumentParserTest.cs diff --git a/src/CLI/ArgumentParser.cs b/src/CLI/ArgumentParser.cs index 585393f..34d26f0 100644 --- a/src/CLI/ArgumentParser.cs +++ b/src/CLI/ArgumentParser.cs @@ -2,7 +2,7 @@ namespace CLI { - class ArgumentParser + public class ArgumentParser { public bool Deploy { get; set; } = false; public bool CleanStorage { get; set; } = false; diff --git a/src/CLI/Deployer.cs b/src/CLI/Deployer.cs index 26e6e2a..4759860 100644 --- a/src/CLI/Deployer.cs +++ b/src/CLI/Deployer.cs @@ -17,7 +17,7 @@ internal class Deployer public Deployer() { sortedScaleUnitInstances = Config.ScaleUnitInstances(); - sortedScaleUnitInstances.Sort((s1, s2) => s1.ScaleUnitId.CompareTo(s2.ScaleUnitId)); + sortedScaleUnitInstances.Sort(); } public async Task Deploy() diff --git a/src/ScaleUnitManagementTests/ArgumentParserTest.cs b/src/ScaleUnitManagementTests/ArgumentParserTest.cs new file mode 100644 index 0000000..54f8a44 --- /dev/null +++ b/src/ScaleUnitManagementTests/ArgumentParserTest.cs @@ -0,0 +1,52 @@ +using System; +using CLI; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace ScaleUnitManagementTests +{ + [TestClass] + public sealed class ArgumentParserTest + { + private ArgumentParser argumentParser; + + [TestInitialize] + public void Setup() + { + argumentParser = new ArgumentParser(); + } + + [TestMethod] + public void BeforeParsing_HasInitialValues() + { + // Arrange + Act + Assert + argumentParser.Deploy.Should().BeFalse(); + argumentParser.CleanStorage.Should().BeFalse(); + } + + [TestMethod] + public void Parse_WithValidArguments_SetsValues() + { + // Arrange + Act + string[] arguments = { "--single-box-deploy", "--clean-storage" }; + argumentParser.Parse(arguments); + + // Assert + argumentParser.Deploy.Should().BeTrue(); + argumentParser.CleanStorage.Should().BeTrue(); + } + + [TestMethod] + public void Parse_WithInvalidArgument_ThrowsError() + { + // Arrange + string[] arguments = { "--single-box-deploy --unknown-argument" }; + + // Act + Action act = () => argumentParser.Parse(arguments); + + // Assert + act.Should().Throw(); + } + } +} diff --git a/src/ScaleUnitManagementTests/ScaleUnitManagementTests.csproj b/src/ScaleUnitManagementTests/ScaleUnitManagementTests.csproj index f50e2ac..fcb1af9 100644 --- a/src/ScaleUnitManagementTests/ScaleUnitManagementTests.csproj +++ b/src/ScaleUnitManagementTests/ScaleUnitManagementTests.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -15,6 +15,7 @@ +