diff --git a/source/Calamari.Common/FeatureToggles/OctopusFeatureToggle.cs b/source/Calamari.Common/FeatureToggles/OctopusFeatureToggle.cs index 31f6968dce..8467862ef6 100644 --- a/source/Calamari.Common/FeatureToggles/OctopusFeatureToggle.cs +++ b/source/Calamari.Common/FeatureToggles/OctopusFeatureToggle.cs @@ -9,11 +9,13 @@ public static class KnownSlugs public const string AnsiColorsInTaskLogFeatureToggle = "ansi-colors"; public const string ArgoCDHelmReplacePathFromContainerReferenceFeatureToggle = "argo-cd-helm-replace-path-from-container-reference"; public const string KustomizePatchImageUpdatesFeatureToggle = "kustomize-patch-image-updates"; + public const string ArgoRolloutsSupportFeatureToggle = "argo-rollouts-support"; }; public static readonly OctopusFeatureToggle AnsiColorsInTaskLogFeatureToggle = new(KnownSlugs.AnsiColorsInTaskLogFeatureToggle); public static readonly OctopusFeatureToggle ArgoCDHelmReplacePathFromContainerReferenceFeatureToggle = new(KnownSlugs.ArgoCDHelmReplacePathFromContainerReferenceFeatureToggle); public static readonly OctopusFeatureToggle KustomizePatchImageUpdatesFeatureToggle = new(KnownSlugs.KustomizePatchImageUpdatesFeatureToggle); + public static readonly OctopusFeatureToggle ArgoRolloutsSupportFeatureToggle = new(KnownSlugs.ArgoRolloutsSupportFeatureToggle); public class OctopusFeatureToggle { diff --git a/source/Calamari.Tests/KubernetesFixtures/Commands/Executors/AppliedResourcesOutputHelperTests.cs b/source/Calamari.Tests/KubernetesFixtures/Commands/Executors/AppliedResourcesOutputHelperTests.cs new file mode 100644 index 0000000000..04f7199bee --- /dev/null +++ b/source/Calamari.Tests/KubernetesFixtures/Commands/Executors/AppliedResourcesOutputHelperTests.cs @@ -0,0 +1,147 @@ +using System.Collections.Generic; +using System.Linq; +using Calamari.Common.Commands; +using Calamari.Common.FeatureToggles; +using Calamari.Common.Plumbing.Variables; +using Calamari.Kubernetes; +using Calamari.Kubernetes.Commands.Executors; +using Calamari.Kubernetes.ResourceStatus.Resources; +using Calamari.Testing.Helpers; +using FluentAssertions; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Calamari.Tests.KubernetesFixtures.Commands.Executors +{ + [TestFixture] + public class AppliedResourcesOutputHelperTests + { + InMemoryLog log; + + [SetUp] + public void SetUp() + { + log = new InMemoryLog(); + } + + [Test] + public void SetsAppliedResourcesOutputVariable_WhenFeatureToggleIsEnabled() + { + // Arrange + var variables = new CalamariVariables + { + [KnownVariables.EnabledFeatureToggles] = OctopusFeatureToggles.KnownSlugs.ArgoRolloutsSupportFeatureToggle + }; + var deployment = new RunningDeployment(variables); + var resources = new[] + { + new ResourceIdentifier(SupportedResourceGroupVersionKinds.DeploymentV1, "my-deployment", "default"), + new ResourceIdentifier(SupportedResourceGroupVersionKinds.ServiceV1, "my-service", "default") + }; + + // Act + AppliedResourcesOutputHelper.SetAppliedResourcesOutputVariable(log, deployment, resources); + + // Assert + var outputVariable = variables.Get(SpecialVariables.AppliedResources); + outputVariable.Should().NotBeNullOrEmpty(); + + var deserializedResources = JsonConvert.DeserializeAnonymousType(outputVariable, new[] + { + new { Group = "", Version = "", Kind = "", Name = "", Namespace = "" } + }); + + deserializedResources.Should().HaveCount(2); + deserializedResources[0].Should().BeEquivalentTo(new + { + Group = "apps", + Version = "v1", + Kind = "Deployment", + Name = "my-deployment", + Namespace = "default" + }); + deserializedResources[1].Should().BeEquivalentTo(new + { + Group = "", + Version = "v1", + Kind = "Service", + Name = "my-service", + Namespace = "default" + }); + } + + [Test] + public void DoesNotSetOutputVariable_WhenFeatureToggleIsDisabled() + { + // Arrange + var variables = new CalamariVariables(); + var deployment = new RunningDeployment(variables); + var resources = new[] + { + new ResourceIdentifier(SupportedResourceGroupVersionKinds.DeploymentV1, "my-deployment", "default") + }; + + // Act + AppliedResourcesOutputHelper.SetAppliedResourcesOutputVariable(log, deployment, resources); + + // Assert + var outputVariable = variables.Get(SpecialVariables.AppliedResources); + outputVariable.Should().BeNull(); + } + + [Test] + public void HandlesEmptyResourceCollection_WhenFeatureToggleIsEnabled() + { + // Arrange + var variables = new CalamariVariables + { + [KnownVariables.EnabledFeatureToggles] = OctopusFeatureToggles.KnownSlugs.ArgoRolloutsSupportFeatureToggle + }; + var deployment = new RunningDeployment(variables); + var resources = Enumerable.Empty(); + + // Act + AppliedResourcesOutputHelper.SetAppliedResourcesOutputVariable(log, deployment, resources); + + // Assert + var outputVariable = variables.Get(SpecialVariables.AppliedResources); + outputVariable.Should().NotBeNullOrEmpty(); + outputVariable.Should().Be("[]"); + } + + [Test] + public void SerializesResourcesWithCorrectJsonFormat() + { + // Arrange + var variables = new CalamariVariables + { + [KnownVariables.EnabledFeatureToggles] = OctopusFeatureToggles.KnownSlugs.ArgoRolloutsSupportFeatureToggle + }; + var deployment = new RunningDeployment(variables); + var resources = new[] + { + new ResourceIdentifier(new ResourceGroupVersionKind("argoproj.io", "v1alpha1", "Rollout"), "my-rollout", "production") + }; + + // Act + AppliedResourcesOutputHelper.SetAppliedResourcesOutputVariable(log, deployment, resources); + + // Assert + var outputVariable = variables.Get(SpecialVariables.AppliedResources); + var deserializedResources = JsonConvert.DeserializeAnonymousType(outputVariable, new[] + { + new { Group = "", Version = "", Kind = "", Name = "", Namespace = "" } + }); + + deserializedResources.Should().ContainSingle() + .Which.Should().BeEquivalentTo(new + { + Group = "argoproj.io", + Version = "v1alpha1", + Kind = "Rollout", + Name = "my-rollout", + Namespace = "production" + }); + } + } +} diff --git a/source/Calamari.Tests/KubernetesFixtures/Commands/Executors/KustomizeExecutorTests.cs b/source/Calamari.Tests/KubernetesFixtures/Commands/Executors/KustomizeExecutorTests.cs index 1368fdc8d4..9bc913da9b 100644 --- a/source/Calamari.Tests/KubernetesFixtures/Commands/Executors/KustomizeExecutorTests.cs +++ b/source/Calamari.Tests/KubernetesFixtures/Commands/Executors/KustomizeExecutorTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Calamari.Common.Commands; using Calamari.Common.Features.Processes; +using Calamari.Common.FeatureToggles; using Calamari.Common.Plumbing.FileSystem; using Calamari.Common.Plumbing.Variables; using Calamari.Kubernetes; @@ -14,6 +15,7 @@ using Calamari.Testing.Helpers; using Calamari.Tests.Fixtures.Integration.FileSystem; using FluentAssertions; +using Newtonsoft.Json; using NSubstitute; using NSubstitute.ClearExtensions; using NUnit.Framework; @@ -371,6 +373,59 @@ public async Task CommandLineReturnsNonZeroCode_ReturnsFalseToIndicateFailure() AssertNoManifestsReported(); } + [Test] + public async Task SetsAppliedResourcesOutputVariable_WhenFeatureToggleIsEnabled() + { + // Arrange + SetupCommandLineRunnerMock(); + var variables = new CalamariVariables + { + [KnownVariables.OriginalPackageDirectoryPath] = StagingDirectory, + [SpecialVariables.KustomizeOverlayPath] = OverlayPath, + [KnownVariables.EnabledFeatureToggles] = OctopusFeatureToggles.KnownSlugs.ArgoRolloutsSupportFeatureToggle + }; + var runningDeployment = new RunningDeployment(variables); + var executor = CreateExecutor(variables); + + // Act + var result = await executor.Execute(runningDeployment, RecordingCallback); + + // Assert + result.Should().BeTrue(); + var appliedResourcesJson = variables.Get(SpecialVariables.AppliedResources); + appliedResourcesJson.Should().NotBeNullOrEmpty(); + + var deserializedResources = JsonConvert.DeserializeAnonymousType(appliedResourcesJson, new[] + { + new { Group = "", Version = "", Kind = "", Name = "", Namespace = "" } + }); + deserializedResources.Should().HaveCount(2); + deserializedResources.Should().Contain(r => r.Kind == "Deployment" && r.Name == "basic-deployment" && r.Namespace == "dev"); + deserializedResources.Should().Contain(r => r.Kind == "Service" && r.Name == "basic-service" && r.Namespace == "dev"); + } + + [Test] + public async Task DoesNotSetAppliedResourcesOutputVariable_WhenFeatureToggleIsDisabled() + { + // Arrange + SetupCommandLineRunnerMock(); + var variables = new CalamariVariables + { + [KnownVariables.OriginalPackageDirectoryPath] = StagingDirectory, + [SpecialVariables.KustomizeOverlayPath] = OverlayPath + }; + var runningDeployment = new RunningDeployment(variables); + var executor = CreateExecutor(variables); + + // Act + var result = await executor.Execute(runningDeployment, RecordingCallback); + + // Assert + result.Should().BeTrue(); + var appliedResourcesJson = variables.Get("AppliedResources"); + appliedResourcesJson.Should().BeNull(); + } + void SetupCommandLineRunnerMock(int kubectlMinor = 28) { const string resourceJson = @"{ diff --git a/source/Calamari.Tests/KubernetesFixtures/Conventions/Helm/HelmUpgradeExecutorTests.cs b/source/Calamari.Tests/KubernetesFixtures/Conventions/Helm/HelmUpgradeExecutorTests.cs new file mode 100644 index 0000000000..1605bf7968 --- /dev/null +++ b/source/Calamari.Tests/KubernetesFixtures/Conventions/Helm/HelmUpgradeExecutorTests.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using Calamari.Common.Commands; +using Calamari.Common.Features.Packages; +using Calamari.Common.Features.Processes; +using Calamari.Common.FeatureToggles; +using Calamari.Common.Plumbing.Deployment; +using Calamari.Common.Plumbing.FileSystem; +using Calamari.Common.Plumbing.Variables; +using Calamari.Kubernetes; +using Calamari.Kubernetes.Conventions.Helm; +using Calamari.Kubernetes.Helm; +using Calamari.Kubernetes.Integration; +using Calamari.Testing.Helpers; +using Calamari.Tests.Fixtures.Integration.FileSystem; +using Calamari.Tests.KubernetesFixtures.Builders; +using FluentAssertions; +using Newtonsoft.Json; +using NSubstitute; +using NSubstitute.ClearExtensions; +using NUnit.Framework; + +namespace Calamari.Tests.KubernetesFixtures.Conventions.Helm +{ + [TestFixture] + public class HelmUpgradeExecutorTests + { + const string ReleaseName = "my-release"; + const int RevisionNumber = 5; + + readonly ICalamariFileSystem fileSystem = TestCalamariPhysicalFileSystem.GetPhysicalFileSystem(); + ICommandLineRunner commandLineRunner; + + InMemoryLog log; + string tempDirectory; + CancellationTokenSource installCompletedCts; + CancellationTokenSource installErrorCts; + + string ChartDirectory => Path.Combine(tempDirectory, "chart"); + + [SetUp] + public void SetUp() + { + log = new InMemoryLog(); + commandLineRunner = Substitute.For(); + tempDirectory = fileSystem.CreateTemporaryDirectory(); + installCompletedCts = new CancellationTokenSource(); + installErrorCts = new CancellationTokenSource(); + + SetupChartDirectory(); + SetupHelmVersionMock(); + } + + [TearDown] + public void TearDown() + { + fileSystem.DeleteDirectory(tempDirectory, FailureOptions.IgnoreFailure); + commandLineRunner.ClearSubstitute(); + installCompletedCts.Dispose(); + installErrorCts.Dispose(); + } + + [Test] + public void SetsAppliedResourcesOutputVariable_WhenFeatureToggleIsEnabled() + { + // Arrange + var variables = CreateVariables(); + variables.Set(KnownVariables.EnabledFeatureToggles, OctopusFeatureToggles.KnownSlugs.ArgoRolloutsSupportFeatureToggle); + + SetupHelmUpgradeMock(); + SetupHelmGetManifestMock(GetSampleManifest()); + + var deployment = CreateRunningDeployment(variables); + var executor = CreateExecutor(deployment); + + // Act + executor.ExecuteHelmUpgrade(deployment, ReleaseName, RevisionNumber, installCompletedCts, installErrorCts); + + // Assert + commandLineRunner.Received().Execute(Arg.Is(i => + i.Arguments.Contains("get") && + i.Arguments.Contains("manifest") && + i.Arguments.Contains(ReleaseName) && + i.Arguments.Contains($"--revision {RevisionNumber}"))); + + var appliedResourcesJson = variables.Get(SpecialVariables.AppliedResources); + appliedResourcesJson.Should().NotBeNullOrEmpty(); + + var deserializedResources = JsonConvert.DeserializeAnonymousType(appliedResourcesJson, new[] + { + new { Group = "", Version = "", Kind = "", Name = "", Namespace = "" } + }); + deserializedResources.Should().HaveCount(2); + deserializedResources.Should().Contain(r => r.Kind == "Deployment" && r.Name == "my-app"); + deserializedResources.Should().Contain(r => r.Kind == "Service" && r.Name == "my-app-service"); + } + + [Test] + public void DoesNotSetAppliedResourcesOutputVariable_WhenFeatureToggleIsDisabled() + { + // Arrange + var variables = CreateVariables(); + + SetupHelmUpgradeMock(); + SetupHelmGetManifestMock(GetSampleManifest()); + + var deployment = CreateRunningDeployment(variables); + var executor = CreateExecutor(deployment); + + // Act + executor.ExecuteHelmUpgrade(deployment, ReleaseName, RevisionNumber, installCompletedCts, installErrorCts); + + // Assert + var appliedResourcesJson = variables.Get(SpecialVariables.AppliedResources); + appliedResourcesJson.Should().BeNull(); + + commandLineRunner.DidNotReceive().Execute(Arg.Is(i => i.Arguments.Contains("get manifest"))); + } + + [Test] + public void LogsWarningAndContinues_WhenGetManifestFails() + { + // Arrange + var variables = CreateVariables(); + variables.Set(KnownVariables.EnabledFeatureToggles, OctopusFeatureToggles.KnownSlugs.ArgoRolloutsSupportFeatureToggle); + + SetupHelmUpgradeMock(); + SetupHelmGetManifestMockToFail(); + + var deployment = CreateRunningDeployment(variables); + var executor = CreateExecutor(deployment); + + // Act - should not throw + executor.ExecuteHelmUpgrade(deployment, ReleaseName, RevisionNumber, installCompletedCts, installErrorCts); + + // Assert + log.MessagesWarnFormatted.Should().Contain(msg => msg.Contains($"Failed to get manifest for {ReleaseName} revision {RevisionNumber}")); + var appliedResourcesJson = variables.Get(SpecialVariables.AppliedResources); + appliedResourcesJson.Should().BeNull(); + } + + [Test] + public void SkipsAppliedResourcesVariable_WhenManifestIsEmpty() + { + // Arrange + var variables = CreateVariables(); + variables.Set(KnownVariables.EnabledFeatureToggles, OctopusFeatureToggles.KnownSlugs.ArgoRolloutsSupportFeatureToggle); + + SetupHelmUpgradeMock(); + SetupHelmGetManifestMock(string.Empty); + + var deployment = CreateRunningDeployment(variables); + var executor = CreateExecutor(deployment); + + // Act + executor.ExecuteHelmUpgrade(deployment, ReleaseName, RevisionNumber, installCompletedCts, installErrorCts); + + // Assert + var appliedResourcesJson = variables.Get(SpecialVariables.AppliedResources); + appliedResourcesJson.Should().BeNull(); + log.MessagesVerboseFormatted.Should().Contain(msg => msg.Contains("empty, skipping applied resources")); + } + + void SetupChartDirectory() + { + Directory.CreateDirectory(ChartDirectory); + File.WriteAllText(Path.Combine(ChartDirectory, "Chart.yaml"), "apiVersion: v2\nname: test-chart\nversion: 1.0.0"); + } + + void SetupHelmVersionMock() + { + commandLineRunner.Execute(Arg.Is(i => i.Arguments.Contains("version") && i.Arguments.Contains("--client"))) + .Returns(info => + { + var invocation = (CommandLineInvocation)info[0]; + invocation.AdditionalInvocationOutputSink?.WriteInfo("v3.14.0"); + return new CommandResult("helm version", 0); + }); + } + + void SetupHelmUpgradeMock() + { + commandLineRunner.Execute(Arg.Is(i => i.Arguments.Contains("upgrade"))) + .Returns(new CommandResult("helm upgrade", 0)); + } + + void SetupHelmUpgradeMockToFail() + { + commandLineRunner.Execute(Arg.Is(i => i.Arguments.Contains("upgrade"))) + .Returns(new CommandResult("helm upgrade", 1)); + } + + void SetupHelmGetManifestMock(string manifestContent) + { + commandLineRunner.Execute(Arg.Is(i => i.Arguments.Contains("get") && i.Arguments.Contains("manifest"))) + .Returns(info => + { + var invocation = (CommandLineInvocation)info[0]; + if (!string.IsNullOrEmpty(manifestContent)) + { + invocation.AdditionalInvocationOutputSink?.WriteInfo(manifestContent); + } + return new CommandResult("helm get manifest", 0); + }); + } + + void SetupHelmGetManifestMockToFail() + { + commandLineRunner.Execute(Arg.Is(i => i.Arguments.Contains("get") && i.Arguments.Contains("manifest"))) + .Returns(new CommandResult("helm get manifest", 1)); + } + + CalamariVariables CreateVariables() + { + return new CalamariVariables + { + [PackageVariables.Output.InstallationDirectoryPath] = ChartDirectory, + [KnownVariables.OriginalPackageDirectoryPath] = tempDirectory + }; + } + + RunningDeployment CreateRunningDeployment(CalamariVariables variables) + { + return new RunningDeployment(variables, new Dictionary()) + { + CurrentDirectoryProvider = DeploymentWorkingDirectory.StagingDirectory, + StagingDirectory = tempDirectory + }; + } + + HelmUpgradeExecutor CreateExecutor(RunningDeployment deployment) + { + var helmCli = new HelmCli(log, commandLineRunner, deployment, fileSystem); + var templateValueSourcesParser = new HelmTemplateValueSourcesParser(fileSystem, log); + var namespaceResolver = new KubernetesManifestNamespaceResolver( + new ApiResourcesScopeLookupBuilder().WithDefaults().Build(), + log); + + return new HelmUpgradeExecutor(log, fileSystem, templateValueSourcesParser, helmCli, namespaceResolver); + } + + static string GetSampleManifest() + { + return @"--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-app + namespace: default +spec: + replicas: 1 +--- +apiVersion: v1 +kind: Service +metadata: + name: my-app-service + namespace: default +spec: + type: ClusterIP +"; + } + } +} diff --git a/source/Calamari/Kubernetes/Commands/Executors/AppliedResourcesOutputHelper.cs b/source/Calamari/Kubernetes/Commands/Executors/AppliedResourcesOutputHelper.cs new file mode 100644 index 0000000000..6e5d464d78 --- /dev/null +++ b/source/Calamari/Kubernetes/Commands/Executors/AppliedResourcesOutputHelper.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Linq; +using Calamari.Common.Commands; +using Calamari.Common.FeatureToggles; +using Calamari.Common.Plumbing.Logging; +using Calamari.Kubernetes.ResourceStatus.Resources; +using Newtonsoft.Json; + +namespace Calamari.Kubernetes.Commands.Executors +{ + public static class AppliedResourcesOutputHelper + { + public static void SetAppliedResourcesOutputVariable( + ILog log, + RunningDeployment deployment, + IEnumerable resources) + { + if (!OctopusFeatureToggles.ArgoRolloutsSupportFeatureToggle.IsEnabled(deployment.Variables)) + { + return; + } + + var resourceList = resources.Select(r => new + { + r.GroupVersionKind.Group, + r.GroupVersionKind.Version, + r.GroupVersionKind.Kind, + r.Name, + r.Namespace + }).ToArray(); + + var json = JsonConvert.SerializeObject(resourceList); + + log.SetOutputVariable(SpecialVariables.AppliedResources, json, deployment.Variables); + } + } +} diff --git a/source/Calamari/Kubernetes/Commands/Executors/GatherAndApplyRawYamlExecutor.cs b/source/Calamari/Kubernetes/Commands/Executors/GatherAndApplyRawYamlExecutor.cs index 7ab9f2e017..05c6f6354a 100644 --- a/source/Calamari/Kubernetes/Commands/Executors/GatherAndApplyRawYamlExecutor.cs +++ b/source/Calamari/Kubernetes/Commands/Executors/GatherAndApplyRawYamlExecutor.cs @@ -62,6 +62,8 @@ protected override async Task> ApplyAndGetResour resourcesIdentifiers.UnionWith(res); } + AppliedResourcesOutputHelper.SetAppliedResourcesOutputVariable(log, deployment, resourcesIdentifiers); + return resourcesIdentifiers; } diff --git a/source/Calamari/Kubernetes/Commands/Executors/KustomizeExecutor.cs b/source/Calamari/Kubernetes/Commands/Executors/KustomizeExecutor.cs index 31537386f8..07b84cc74d 100644 --- a/source/Calamari/Kubernetes/Commands/Executors/KustomizeExecutor.cs +++ b/source/Calamari/Kubernetes/Commands/Executors/KustomizeExecutor.cs @@ -55,7 +55,11 @@ protected override async Task> ApplyAndGetResour manifestReporter.ReportManifestFileApplied(HydratedManifestFilepath(deployment.CurrentDirectory)); log.Info("Applying kustomization"); - return await ApplyKustomization(deployment, appliedResourcesCallback, overlayPath); + var resourceIdentifiers = await ApplyKustomization(deployment, appliedResourcesCallback, overlayPath); + + AppliedResourcesOutputHelper.SetAppliedResourcesOutputVariable(log, deployment, resourceIdentifiers); + + return resourceIdentifiers; } async Task ApplyKustomization(RunningDeployment deployment, Func appliedResourcesCallback, string overlayPath) diff --git a/source/Calamari/Kubernetes/Conventions/Helm/HelmUpgradeExecutor.cs b/source/Calamari/Kubernetes/Conventions/Helm/HelmUpgradeExecutor.cs index 0538628b2d..0550dc2c3e 100644 --- a/source/Calamari/Kubernetes/Conventions/Helm/HelmUpgradeExecutor.cs +++ b/source/Calamari/Kubernetes/Conventions/Helm/HelmUpgradeExecutor.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -9,6 +9,7 @@ using Calamari.Common.Plumbing.FileSystem; using Calamari.Common.Plumbing.Logging; using Calamari.Common.Plumbing.Variables; +using Calamari.Kubernetes.Commands.Executors; using Calamari.Kubernetes.Helm; using Calamari.Kubernetes.Integration; using Calamari.Util; @@ -22,20 +23,24 @@ public class HelmUpgradeExecutor readonly ICalamariFileSystem fileSystem; readonly HelmTemplateValueSourcesParser templateValueSourcesParser; readonly HelmCli helmCli; + readonly IKubernetesManifestNamespaceResolver namespaceResolver; public HelmUpgradeExecutor(ILog log, ICalamariFileSystem fileSystem, HelmTemplateValueSourcesParser templateValueSourcesParser, - HelmCli helmCli) + HelmCli helmCli, + IKubernetesManifestNamespaceResolver namespaceResolver) { this.log = log; this.fileSystem = fileSystem; this.templateValueSourcesParser = templateValueSourcesParser; this.helmCli = helmCli; + this.namespaceResolver = namespaceResolver; } public void ExecuteHelmUpgrade(RunningDeployment deployment, string releaseName, + int newRevisionNumber, CancellationTokenSource installCompletedCts, CancellationTokenSource installErrorCts) { @@ -57,9 +62,37 @@ public void ExecuteHelmUpgrade(RunningDeployment deployment, throw new CommandException("Helm Upgrade returned zero exit code but had error output. Deployment terminated."); } + if (OctopusFeatureToggles.ArgoRolloutsSupportFeatureToggle.IsEnabled(deployment.Variables)) + { + SetAppliedResourcesOutputVariable(deployment, releaseName, newRevisionNumber); + } + installCompletedCts.Cancel(); } + void SetAppliedResourcesOutputVariable(RunningDeployment deployment, string releaseName, int revisionNumber) + { + string manifest = null; + try + { + manifest = helmCli.GetManifest(releaseName, revisionNumber); + } + catch (Exception ex) + { + log.Warn($"Failed to get manifest for {releaseName} revision {revisionNumber}: {ex.Message}"); + return; + } + + if (string.IsNullOrWhiteSpace(manifest)) + { + log.Verbose($"Helm manifest for {releaseName} revision {revisionNumber} is empty, skipping applied resources output variable."); + return; + } + + var resources = ManifestParser.GetResourcesFromManifest(manifest, namespaceResolver, deployment.Variables, log); + AppliedResourcesOutputHelper.SetAppliedResourcesOutputVariable(log, deployment, resources); + } + List GetUpgradeCommandArgs(RunningDeployment deployment) { var args = new List(); diff --git a/source/Calamari/Kubernetes/Conventions/HelmUpgradeWithKOSConvention.cs b/source/Calamari/Kubernetes/Conventions/HelmUpgradeWithKOSConvention.cs index 1b5ae8b46a..7ff9ee13ad 100644 --- a/source/Calamari/Kubernetes/Conventions/HelmUpgradeWithKOSConvention.cs +++ b/source/Calamari/Kubernetes/Conventions/HelmUpgradeWithKOSConvention.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -69,9 +69,10 @@ public void Install(RunningDeployment deployment) var executor = new HelmUpgradeExecutor(log, fileSystem, valueSourcesParser, - helmCli); + helmCli, + namespaceResolver); - executor.ExecuteHelmUpgrade(deployment, releaseName, helmInstallCompletedCts, helmInstallErrorCts); + executor.ExecuteHelmUpgrade(deployment, releaseName, newRevisionNumber, helmInstallCompletedCts, helmInstallErrorCts); }); var manifestAndStatusCheckTask = Task.Run(async () => diff --git a/source/Calamari/Kubernetes/SpecialVariables.cs b/source/Calamari/Kubernetes/SpecialVariables.cs index 9f5b7400a8..86cddcf04b 100644 --- a/source/Calamari/Kubernetes/SpecialVariables.cs +++ b/source/Calamari/Kubernetes/SpecialVariables.cs @@ -42,6 +42,8 @@ public static class SpecialVariables public const string ServerSideApplyEnabled = "Octopus.Action.Kubernetes.ServerSideApply.Enabled"; public const string ServerSideApplyForceConflicts = "Octopus.Action.Kubernetes.ServerSideApply.ForceConflicts"; + public const string AppliedResources = "Octopus.Action.Kubernetes.AppliedResources"; + public static class Helm { public const string ReleaseName = "Octopus.Action.Helm.ReleaseName";