diff --git a/source/Octopus.Cli/Commands/Runbooks/RunRunbookCommand.cs b/source/Octopus.Cli/Commands/Runbooks/RunRunbookCommand.cs index 9e80d3fe19..b86205c737 100644 --- a/source/Octopus.Cli/Commands/Runbooks/RunRunbookCommand.cs +++ b/source/Octopus.Cli/Commands/Runbooks/RunRunbookCommand.cs @@ -7,21 +7,17 @@ using Octopus.Cli.Repositories; using Octopus.Cli.Util; using Octopus.Client; -using Octopus.Client.Exceptions; using Octopus.Client.Model; -using Octostache; namespace Octopus.Cli.Commands.Runbooks { - - [Command("run-runbook", Description = "Runs a Runbook.")] public class RunRunbookCommand : ApiCommand, ISupportFormattedOutput { private readonly ExecutionResourceWaiter.Factory executionResourceWaiterFactory; - private readonly Dictionary variables = new Dictionary(); + private readonly Dictionary Variables = new Dictionary(); - private RunbookRunResource[] runbookRuns { get; set; } + private RunbookRunResource[] RunbookRuns { get; set; } private string ProjectNameOrId { get; set; } // required for tenant filtering with *, runbook filtering private string RunbookNameOrId { get; set; } @@ -33,7 +29,7 @@ public class RunRunbookCommand : ApiCommand, ISupportFormattedOutput private List ExcludedMachineIds { get; } = new List(); private List StepNamesToSkip { get; } = new List(); private bool UseDefaultSnapshot { get; } = false; - private List TenantIds { get; } = new List(); + private List TenantNamesOrIds { get; } = new List(); private List TenantTagNames { get; } = new List(); private DateTimeOffset? RunAt { get; set; } private DateTimeOffset? NoRunAfter { get; set; } @@ -93,7 +89,7 @@ public RunRunbookCommand( options.Add("tenant=", "[Optional] Run a runbook on the tenant with this name or ID; specify this argument multiple times to add multiple tenants or use `*` wildcard to deploy to all tenants who are ready for this release (according to lifecycle).", - v => TenantIds.Add(v)); + v => TenantNamesOrIds.Add(v)); options.Add("tenantTag=", "[Optional] Run a runbook on the tenants matching this tag; specify this argument multiple times to build a query/filter with multiple tags, just like you can in the user interface.", @@ -116,7 +112,12 @@ public RunRunbookCommand( options.Add("waitForRun", "[Optional] Whether to wait synchronously for deployment to finish.", v => WaitForRun = true); - options.Add("progress", "[Optional] Show progress of the runbook run", v => { Progress = true; WaitForRun = true; NoRawLog = true; }); + options.Add("progress", "[Optional] Show progress of the runbook run", v => + { + Progress = true; + WaitForRun = true; + NoRawLog = true; + }); options.Add("runTimeout=", "[Optional] Specifies maximum time (timespan format) that the console session will wait for the runbook run to finish (default 00:10:00). This will not stop the run. Requires --waitForRun parameter to be set.", @@ -141,6 +142,7 @@ public async Task Request() var project = await Repository.Projects.FindByNameOrIdOrFail(ProjectNameOrId).ConfigureAwait(false); var runbook = await Repository.Runbooks.FindByNameOrIdOrFail(RunbookNameOrId, project).ConfigureAwait(false); var environments = await Repository.Environments.FindByNamesOrIdsOrFail(EnvironmentNamesOrIds).ConfigureAwait(false); + var tenants = await RetrieveTenants(); LogScheduledDeployment(); var payload = new RunbookRunParameters() @@ -155,20 +157,20 @@ public async Task Request() ExcludedMachineIds = ExcludedMachineIds.ToArray(), SkipActions = StepNamesToSkip.ToArray(), UseGuidedFailure = GuidedFailure, - TenantIds = TenantIds.ToArray(), + TenantIds = tenants, TenantTagNames = TenantTagNames.ToArray(), QueueTime = RunAt, QueueTimeExpiry = NoRunAfter, - FormValues = variables + FormValues = Variables }; - runbookRuns = await Repository.Runbooks.Run(runbook, payload); + RunbookRuns = await Repository.Runbooks.Run(runbook, payload); - if (runbookRuns.Any() && WaitForRun) + if (RunbookRuns.Any() && WaitForRun) { var waiter = executionResourceWaiterFactory(Repository, ServerBaseUrl); await waiter.WaitForRunbookRunToComplete( - runbookRuns, + RunbookRuns, project, Progress, NoRawLog, @@ -179,6 +181,13 @@ await waiter.WaitForRunbookRunToComplete( } } + private async Task RetrieveTenants() + { + return (!TenantNamesOrIds.Contains("*") && TenantNamesOrIds.Any()) + ? (await Repository.Tenants.FindByNamesOrIdsOrFail(TenantNamesOrIds).ConfigureAwait(false)).Select(ten => ten.Id).ToArray() + : TenantNamesOrIds.ToArray(); + } + protected override Task ValidateParameters() { if (ProjectNameOrId == null) @@ -197,13 +206,14 @@ protected override Task ValidateParameters() } if ((RunAt ?? DateTimeOffset.Now) > NoRunAfter) - throw new CommandException("The Run will expire before it has a chance to execute. Please select an expiry time that occurs after the deployment is scheduled to begin"); + throw new CommandException( + "The Run will expire before it has a chance to execute. Please select an expiry time that occurs after the deployment is scheduled to begin"); CheckForIntersection(IncludedMachineIds.ToList(), ExcludedMachineIds.ToList()); - if (TenantIds.Contains("*") && (TenantIds.Count > 1 || TenantTagNames.Count > 0)) + if (TenantNamesOrIds.Contains("*") && (TenantNamesOrIds.Count > 1 || TenantTagNames.Count > 0)) throw new CommandException( - "When running on all tenants using the --tenantIds=* wildcard, no other tenant filters can be provided"); + "When running on all tenants using the --tenant=* wildcard, no other tenant filters can be provided"); return Task.FromResult(0); } @@ -217,14 +227,16 @@ private static void CheckForIntersection(IEnumerable included, IEnumerab $"Cannot specify the same machine as both included and excluded: {intersection.ReadableJoin(", ")}"); } } + private void LogScheduledDeployment() { if (RunAt == null) return; var now = DateTimeOffset.UtcNow; - commandOutputProvider.Information("Runbook run will be scheduled to start in: {Duration:l}", (RunAt.Value - now).FriendlyDuration()); + commandOutputProvider.Information("Runbook run will be scheduled to start in: {Duration:l}", + (RunAt.Value - now).FriendlyDuration()); } - void ParseVariable(string variable) + private void ParseVariable(string variable) { var index = variable.IndexOfAny(new[] {':', '='}); if (index <= 0) @@ -233,7 +245,7 @@ void ParseVariable(string variable) var key = variable.Substring(0, index); var value = (index >= variable.Length - 1) ? string.Empty : variable.Substring(index + 1); - variables.Add(key, value); + Variables.Add(key, value); } public void PrintDefaultOutput() @@ -242,7 +254,7 @@ public void PrintDefaultOutput() public void PrintJsonOutput() { - foreach (var run in runbookRuns) + foreach (var run in RunbookRuns) { commandOutputProvider.Json(new {