From 293e2f471f8c92db1a86d16ba553c95774b66360 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 10 Dec 2025 18:25:47 +1000 Subject: [PATCH 1/2] add logSuccessAsInfo --- .../Kubernetes/Integration/CommandLineTool.cs | 26 ++++++++++++++----- .../Kubernetes/Integration/HelmCli.cs | 12 ++++++++- .../Calamari/Kubernetes/SpecialVariables.cs | 1 + 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/source/Calamari/Kubernetes/Integration/CommandLineTool.cs b/source/Calamari/Kubernetes/Integration/CommandLineTool.cs index ec14a76bcd..377f0d2859 100644 --- a/source/Calamari/Kubernetes/Integration/CommandLineTool.cs +++ b/source/Calamari/Kubernetes/Integration/CommandLineTool.cs @@ -28,7 +28,7 @@ protected CommandLineTool( public string ExecutableLocation { get; protected set; } protected CommandResult ExecuteCommandAndLogOutput(CommandLineInvocation invocation) - => ExecuteCommandAndLogOutput(invocation, false); + => ExecuteCommandAndLogOutput(invocation, false, false); /// /// This is a special case for when the invocation results in an error @@ -36,9 +36,15 @@ protected CommandResult ExecuteCommandAndLogOutput(CommandLineInvocation invocat /// 2) we don't want to inform this at an error level when this happens. /// protected CommandResult ExecuteCommandAndLogOutputAsVerbose(CommandLineInvocation invocation) - => ExecuteCommandAndLogOutput(invocation, true); + => ExecuteCommandAndLogOutput(invocation, true, false); - CommandResult ExecuteCommandAndLogOutput(CommandLineInvocation invocation, bool logOutputAsVerbose) + /// + /// Execute command and log successful output at Info level instead of Verbose + /// + protected CommandResult ExecuteCommandAndLogOutputAsInfo(CommandLineInvocation invocation) + => ExecuteCommandAndLogOutput(invocation, false, true); + + CommandResult ExecuteCommandAndLogOutput(CommandLineInvocation invocation, bool logOutputAsVerbose, bool logSuccessAsInfo) { invocation.EnvironmentVars = environmentVars; invocation.WorkingDirectory = workingDirectory; @@ -52,7 +58,7 @@ CommandResult ExecuteCommandAndLogOutput(CommandLineInvocation invocation, bool var result = commandLineRunner.Execute(invocation); - LogCapturedOutput(result, captureCommandOutput, logOutputAsVerbose); + LogCapturedOutput(result, captureCommandOutput, logOutputAsVerbose, logSuccessAsInfo); return result; } @@ -62,13 +68,21 @@ void LogCommandText(CommandLineInvocation invocation) log.Verbose(invocation.ToString()); } - void LogCapturedOutput(CommandResult result, CaptureCommandOutput captureCommandOutput, bool logOutputAsVerbose) + void LogCapturedOutput(CommandResult result, CaptureCommandOutput captureCommandOutput, bool logOutputAsVerbose, bool logSuccessAsInfo) { foreach (var message in captureCommandOutput.Messages) { if (result.ExitCode == 0) { - log.Verbose(message.Text); + // When logSuccessAsInfo is true, log successful output at Info level + if (logSuccessAsInfo) + { + log.Info(message.Text); + } + else + { + log.Verbose(message.Text); + } continue; } diff --git a/source/Calamari/Kubernetes/Integration/HelmCli.cs b/source/Calamari/Kubernetes/Integration/HelmCli.cs index 740de5f9b8..f0c78a149a 100644 --- a/source/Calamari/Kubernetes/Integration/HelmCli.cs +++ b/source/Calamari/Kubernetes/Integration/HelmCli.cs @@ -131,7 +131,11 @@ public CommandResult Upgrade(string releaseName, string packagePath, IEnumerable buildArgs.Add($"\"{releaseName}\""); buildArgs.Add($"\"{packagePath}\""); - var result = ExecuteCommandAndLogOutput(buildArgs); + // Check if we should log output at Info level + var logOutputAsInfo = variables.GetFlag(SpecialVariables.Helm.LogOutputAsInfo); + var result = logOutputAsInfo + ? ExecuteCommandAndLogOutputAsInfo(buildArgs) + : ExecuteCommandAndLogOutput(buildArgs); return result; } @@ -147,6 +151,12 @@ CommandResult ExecuteCommandAndLogOutput(IEnumerable arguments) return base.ExecuteCommandAndLogOutput(new CommandLineInvocation(ExecutableLocation, SanitiseCommandLineArgs(arguments))); } + CommandResult ExecuteCommandAndLogOutputAsInfo(IEnumerable arguments) + { + ChmodExecutable(); + return base.ExecuteCommandAndLogOutputAsInfo(new CommandLineInvocation(ExecutableLocation, SanitiseCommandLineArgs(arguments))); + } + static string[] SanitiseCommandLineArgs(IEnumerable arguments) => arguments.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); void ChmodExecutable() diff --git a/source/Calamari/Kubernetes/SpecialVariables.cs b/source/Calamari/Kubernetes/SpecialVariables.cs index c589bcb98a..d25159c0cb 100644 --- a/source/Calamari/Kubernetes/SpecialVariables.cs +++ b/source/Calamari/Kubernetes/SpecialVariables.cs @@ -51,6 +51,7 @@ public static class Helm public const string CustomHelmExecutable = "Octopus.Action.Helm.CustomHelmExecutable"; public const string Timeout = "Octopus.Action.Helm.Timeout"; public const string ChartDirectory = "Octopus.Action.Helm.ChartDirectory"; + public const string LogOutputAsInfo = "Octopus.Action.Helm.LogOutputAsInfo"; public static class Packages { From 664144f40c0f838eb2dc6e18aa313444845682fa Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 15 Dec 2025 10:57:46 +1000 Subject: [PATCH 2/2] feat: Add Octopus.Action.Kubernetes.VerboseOutput variable to log CLI tool output at Info level for Kubernetes steps --- .../Calamari/Kubernetes/Integration/AwsCli.cs | 6 +++-- .../Kubernetes/Integration/AzureCli.cs | 5 ++-- .../Kubernetes/Integration/CommandLineTool.cs | 24 +++++++++++-------- .../Calamari/Kubernetes/Integration/GCloud.cs | 7 +++--- .../Integration/GkeGcloudAuthPlugin.cs | 5 ++-- .../Kubernetes/Integration/HelmCli.cs | 18 +++----------- .../Kubernetes/Integration/Kubectl.cs | 2 +- .../Kubernetes/Integration/Kubelogin.cs | 5 ++-- .../Kubernetes/SetupKubectlAuthentication.cs | 11 +++++---- .../Calamari/Kubernetes/SpecialVariables.cs | 2 +- 10 files changed, 42 insertions(+), 43 deletions(-) diff --git a/source/Calamari/Kubernetes/Integration/AwsCli.cs b/source/Calamari/Kubernetes/Integration/AwsCli.cs index d15b679f3a..29f9cf516c 100644 --- a/source/Calamari/Kubernetes/Integration/AwsCli.cs +++ b/source/Calamari/Kubernetes/Integration/AwsCli.cs @@ -4,6 +4,7 @@ using Calamari.Common.Features.Processes; using Calamari.Common.Plumbing; using Calamari.Common.Plumbing.Logging; +using Calamari.Common.Plumbing.Variables; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Octopus.Versioning.Semver; @@ -16,8 +17,9 @@ public AwsCli( ILog log, ICommandLineRunner commandLineRunner, string workingDirectory, - Dictionary environmentVars) - : base(log, commandLineRunner, workingDirectory, environmentVars) + Dictionary environmentVars, + IVariables variables) + : base(log, commandLineRunner, workingDirectory, environmentVars, variables) { } diff --git a/source/Calamari/Kubernetes/Integration/AzureCli.cs b/source/Calamari/Kubernetes/Integration/AzureCli.cs index b3c28b8621..6485f2140e 100644 --- a/source/Calamari/Kubernetes/Integration/AzureCli.cs +++ b/source/Calamari/Kubernetes/Integration/AzureCli.cs @@ -5,13 +5,14 @@ using Calamari.Common.Plumbing; using Calamari.Common.Plumbing.FileSystem; using Calamari.Common.Plumbing.Logging; +using Calamari.Common.Plumbing.Variables; namespace Calamari.Kubernetes.Integration { public class AzureCli : CommandLineTool { - public AzureCli(ILog log, ICommandLineRunner commandLineRunner, string workingDirectory, Dictionary environmentVars) - : base(log, commandLineRunner, workingDirectory, environmentVars) + public AzureCli(ILog log, ICommandLineRunner commandLineRunner, string workingDirectory, Dictionary environmentVars, IVariables variables) + : base(log, commandLineRunner, workingDirectory, environmentVars, variables) { } diff --git a/source/Calamari/Kubernetes/Integration/CommandLineTool.cs b/source/Calamari/Kubernetes/Integration/CommandLineTool.cs index 377f0d2859..16d04f1b55 100644 --- a/source/Calamari/Kubernetes/Integration/CommandLineTool.cs +++ b/source/Calamari/Kubernetes/Integration/CommandLineTool.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Calamari.Common.Features.Processes; using Calamari.Common.Plumbing.Logging; +using Calamari.Common.Plumbing.Variables; namespace Calamari.Kubernetes.Integration { @@ -10,6 +11,7 @@ public class CommandLineTool protected readonly ILog log; protected string workingDirectory; protected Dictionary environmentVars; + protected readonly IVariables variables; readonly ICommandLineRunner commandLineRunner; @@ -17,18 +19,25 @@ protected CommandLineTool( ILog log, ICommandLineRunner commandLineRunner, string workingDirectory, - Dictionary environmentVars) + Dictionary environmentVars, + IVariables variables) { this.log = log; this.commandLineRunner = commandLineRunner; this.workingDirectory = workingDirectory; this.environmentVars = environmentVars; + this.variables = variables; } public string ExecutableLocation { get; protected set; } + protected virtual bool ShouldLogSuccessfulOutputAsInfo() + { + return variables.GetFlag(SpecialVariables.VerboseOutput); + } + protected CommandResult ExecuteCommandAndLogOutput(CommandLineInvocation invocation) - => ExecuteCommandAndLogOutput(invocation, false, false); + => ExecuteCommandAndLogOutput(invocation, false); /// /// This is a special case for when the invocation results in an error @@ -36,15 +45,9 @@ protected CommandResult ExecuteCommandAndLogOutput(CommandLineInvocation invocat /// 2) we don't want to inform this at an error level when this happens. /// protected CommandResult ExecuteCommandAndLogOutputAsVerbose(CommandLineInvocation invocation) - => ExecuteCommandAndLogOutput(invocation, true, false); - - /// - /// Execute command and log successful output at Info level instead of Verbose - /// - protected CommandResult ExecuteCommandAndLogOutputAsInfo(CommandLineInvocation invocation) - => ExecuteCommandAndLogOutput(invocation, false, true); + => ExecuteCommandAndLogOutput(invocation, true); - CommandResult ExecuteCommandAndLogOutput(CommandLineInvocation invocation, bool logOutputAsVerbose, bool logSuccessAsInfo) + CommandResult ExecuteCommandAndLogOutput(CommandLineInvocation invocation, bool logOutputAsVerbose) { invocation.EnvironmentVars = environmentVars; invocation.WorkingDirectory = workingDirectory; @@ -58,6 +61,7 @@ CommandResult ExecuteCommandAndLogOutput(CommandLineInvocation invocation, bool var result = commandLineRunner.Execute(invocation); + var logSuccessAsInfo = ShouldLogSuccessfulOutputAsInfo(); LogCapturedOutput(result, captureCommandOutput, logOutputAsVerbose, logSuccessAsInfo); return result; diff --git a/source/Calamari/Kubernetes/Integration/GCloud.cs b/source/Calamari/Kubernetes/Integration/GCloud.cs index eba19dfde5..725e293787 100644 --- a/source/Calamari/Kubernetes/Integration/GCloud.cs +++ b/source/Calamari/Kubernetes/Integration/GCloud.cs @@ -6,15 +6,16 @@ using Calamari.Common.Plumbing; using Calamari.Common.Plumbing.FileSystem; using Calamari.Common.Plumbing.Logging; +using Calamari.Common.Plumbing.Variables; namespace Calamari.Kubernetes.Integration { public class GCloud : CommandLineTool { - private readonly ICalamariFileSystem fileSystem; + readonly ICalamariFileSystem fileSystem; - public GCloud(ILog log, ICommandLineRunner commandLineRunner, ICalamariFileSystem fileSystem, string workingDirectory, Dictionary environmentVars) - : base(log, commandLineRunner, workingDirectory, environmentVars) + public GCloud(ILog log, ICommandLineRunner commandLineRunner, ICalamariFileSystem fileSystem, string workingDirectory, Dictionary environmentVars, IVariables variables) + : base(log, commandLineRunner, workingDirectory, environmentVars, variables) { this.fileSystem = fileSystem; } diff --git a/source/Calamari/Kubernetes/Integration/GkeGcloudAuthPlugin.cs b/source/Calamari/Kubernetes/Integration/GkeGcloudAuthPlugin.cs index 3f812c5dd3..60df85727b 100644 --- a/source/Calamari/Kubernetes/Integration/GkeGcloudAuthPlugin.cs +++ b/source/Calamari/Kubernetes/Integration/GkeGcloudAuthPlugin.cs @@ -3,13 +3,14 @@ using Calamari.Common.Features.Processes; using Calamari.Common.Plumbing; using Calamari.Common.Plumbing.Logging; +using Calamari.Common.Plumbing.Variables; namespace Calamari.Kubernetes.Integration { public class GkeGcloudAuthPlugin : CommandLineTool { - public GkeGcloudAuthPlugin(ILog log, ICommandLineRunner commandLineRunner, string workingDirectory, Dictionary environmentVars) - : base(log, commandLineRunner, workingDirectory, environmentVars) + public GkeGcloudAuthPlugin(ILog log, ICommandLineRunner commandLineRunner, string workingDirectory, Dictionary environmentVars, IVariables variables) + : base(log, commandLineRunner, workingDirectory, environmentVars, variables) { } diff --git a/source/Calamari/Kubernetes/Integration/HelmCli.cs b/source/Calamari/Kubernetes/Integration/HelmCli.cs index f0c78a149a..ed66704f48 100644 --- a/source/Calamari/Kubernetes/Integration/HelmCli.cs +++ b/source/Calamari/Kubernetes/Integration/HelmCli.cs @@ -23,15 +23,13 @@ public class HelmCli : CommandLineTool { readonly ICommandLineRunner commandLineRunner; readonly ICalamariFileSystem fileSystem; - readonly IVariables variables; bool isCustomExecutable; public HelmCli(ILog log, ICommandLineRunner commandLineRunner, RunningDeployment runningDeployment, ICalamariFileSystem fileSystem) - : base(log, commandLineRunner, runningDeployment.CurrentDirectory, runningDeployment.EnvironmentVariables) + : base(log, commandLineRunner, runningDeployment.CurrentDirectory, runningDeployment.EnvironmentVariables, runningDeployment.Variables) { this.commandLineRunner = commandLineRunner; this.fileSystem = fileSystem; - variables = runningDeployment.Variables; ExecutableLocation = SetExecutable(); } @@ -116,7 +114,7 @@ public string GetManifest(string releaseName, int revisionNumber) return result.Output.MergeInfoLogs(); } - + public CommandResult Upgrade(string releaseName, string packagePath, IEnumerable upgradeArgs) { var buildArgs = new List @@ -131,11 +129,7 @@ public CommandResult Upgrade(string releaseName, string packagePath, IEnumerable buildArgs.Add($"\"{releaseName}\""); buildArgs.Add($"\"{packagePath}\""); - // Check if we should log output at Info level - var logOutputAsInfo = variables.GetFlag(SpecialVariables.Helm.LogOutputAsInfo); - var result = logOutputAsInfo - ? ExecuteCommandAndLogOutputAsInfo(buildArgs) - : ExecuteCommandAndLogOutput(buildArgs); + var result = ExecuteCommandAndLogOutput(buildArgs); return result; } @@ -151,12 +145,6 @@ CommandResult ExecuteCommandAndLogOutput(IEnumerable arguments) return base.ExecuteCommandAndLogOutput(new CommandLineInvocation(ExecutableLocation, SanitiseCommandLineArgs(arguments))); } - CommandResult ExecuteCommandAndLogOutputAsInfo(IEnumerable arguments) - { - ChmodExecutable(); - return base.ExecuteCommandAndLogOutputAsInfo(new CommandLineInvocation(ExecutableLocation, SanitiseCommandLineArgs(arguments))); - } - static string[] SanitiseCommandLineArgs(IEnumerable arguments) => arguments.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); void ChmodExecutable() diff --git a/source/Calamari/Kubernetes/Integration/Kubectl.cs b/source/Calamari/Kubernetes/Integration/Kubectl.cs index a6e11ce3d4..3f12c97c6e 100644 --- a/source/Calamari/Kubernetes/Integration/Kubectl.cs +++ b/source/Calamari/Kubernetes/Integration/Kubectl.cs @@ -31,7 +31,7 @@ public Kubectl(IVariables variables, ILog log, ICommandLineRunner commandLineRunner, string workingDirectory, - Dictionary environmentVariables) : base(log, commandLineRunner, workingDirectory, environmentVariables) + Dictionary environmentVariables) : base(log, commandLineRunner, workingDirectory, environmentVariables, variables) { customKubectlExecutable = variables.Get("Octopus.Action.Kubernetes.CustomKubectlExecutable"); } diff --git a/source/Calamari/Kubernetes/Integration/Kubelogin.cs b/source/Calamari/Kubernetes/Integration/Kubelogin.cs index 5da2cc2f19..149b873d9f 100644 --- a/source/Calamari/Kubernetes/Integration/Kubelogin.cs +++ b/source/Calamari/Kubernetes/Integration/Kubelogin.cs @@ -4,13 +4,14 @@ using Calamari.Common.Features.Processes; using Calamari.Common.Plumbing; using Calamari.Common.Plumbing.Logging; +using Calamari.Common.Plumbing.Variables; namespace Calamari.Kubernetes.Integration { public class KubeLogin : CommandLineTool { - public KubeLogin(ILog log, ICommandLineRunner commandLineRunner, string workingDirectory, Dictionary environmentVars) - : base(log, commandLineRunner, workingDirectory, environmentVars) + public KubeLogin(ILog log, ICommandLineRunner commandLineRunner, string workingDirectory, Dictionary environmentVars, IVariables variables) + : base(log, commandLineRunner, workingDirectory, environmentVars, variables) { } diff --git a/source/Calamari/Kubernetes/SetupKubectlAuthentication.cs b/source/Calamari/Kubernetes/SetupKubectlAuthentication.cs index b5605c0189..907fd757d6 100644 --- a/source/Calamari/Kubernetes/SetupKubectlAuthentication.cs +++ b/source/Calamari/Kubernetes/SetupKubectlAuthentication.cs @@ -174,8 +174,9 @@ void SetupGCloudContext(string @namespace) commandLineRunner, fileSystem, workingDirectory, - environmentVars); - var gkeGcloudAuthPlugin = new GkeGcloudAuthPlugin(log, commandLineRunner, workingDirectory, environmentVars); + environmentVars, + variables); + var gkeGcloudAuthPlugin = new GkeGcloudAuthPlugin(log, commandLineRunner, workingDirectory, environmentVars, variables); var gcloudAuth = new GoogleKubernetesEngineAuth(gcloudCli, gkeGcloudAuthPlugin, kubectl, @@ -186,8 +187,8 @@ void SetupGCloudContext(string @namespace) void SetupAzureContext(string @namespace, string kubeConfig) { - var azureCli = new AzureCli(log, commandLineRunner, workingDirectory, environmentVars); - var kubeloginCli = new KubeLogin(log, commandLineRunner, workingDirectory, environmentVars); + var azureCli = new AzureCli(log, commandLineRunner, workingDirectory, environmentVars, variables); + var kubeloginCli = new KubeLogin(log, commandLineRunner, workingDirectory, environmentVars, variables); var azureAuth = new AzureKubernetesServicesAuth(azureCli, kubectl, kubeloginCli, variables); azureAuth.Configure(@namespace, kubeConfig); } @@ -317,7 +318,7 @@ void SetupContextForUsernamePassword(string user) void SetupAwsContext(string @namespace, string clusterUrl, string user) { - var awsCli = new AwsCli(log, commandLineRunner, workingDirectory, environmentVars); + var awsCli = new AwsCli(log, commandLineRunner, workingDirectory, environmentVars, variables); var awsAuth = new AwsCliAuth(awsCli, kubectl, variables, environmentVars, log); awsAuth.Configure(@namespace, clusterUrl, user); diff --git a/source/Calamari/Kubernetes/SpecialVariables.cs b/source/Calamari/Kubernetes/SpecialVariables.cs index d25159c0cb..a1fbd0da70 100644 --- a/source/Calamari/Kubernetes/SpecialVariables.cs +++ b/source/Calamari/Kubernetes/SpecialVariables.cs @@ -17,6 +17,7 @@ public static class SpecialVariables public const string OutputKubeConfig = "Octopus.Action.Kubernetes.OutputKubeConfig"; public const string CustomKubectlExecutable = "Octopus.Action.Kubernetes.CustomKubectlExecutable"; public const string ResourceStatusCheck = "Octopus.Action.Kubernetes.ResourceStatusCheck"; + public const string VerboseOutput = "Octopus.Action.Kubernetes.VerboseOutput"; public const string DeploymentStyle = "Octopus.Action.KubernetesContainers.DeploymentStyle"; public const string DeploymentWait = "Octopus.Action.KubernetesContainers.DeploymentWait"; public const string CustomResourceYamlFileName = "Octopus.Action.KubernetesContainers.CustomResourceYamlFileName"; @@ -51,7 +52,6 @@ public static class Helm public const string CustomHelmExecutable = "Octopus.Action.Helm.CustomHelmExecutable"; public const string Timeout = "Octopus.Action.Helm.Timeout"; public const string ChartDirectory = "Octopus.Action.Helm.ChartDirectory"; - public const string LogOutputAsInfo = "Octopus.Action.Helm.LogOutputAsInfo"; public static class Packages {