Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@ public void MultiSource_OneSourceUpdated_OtherAlreadyAtTarget_BothTracked()
updater.Install(runningDeployment);

// Assert
using var scope = new AssertionScope();
//using var scope = new AssertionScope();
Comment thread
oleksandr-codefresh marked this conversation as resolved.
Outdated
var results = getResults();
results.Should().NotBeNull();
var actual = results.Single();
Expand Down
35 changes: 35 additions & 0 deletions source/Calamari.Tests/ArgoCD/ContainerImageReplacerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,41 @@ public void UpdateImages_WithQuotedReference_PreservesQuotes()
result.UpdatedImageReferences.Should().ContainSingle(r => r == "nginx:1.25");
}

[Test]
public void UpdateImages_WithImageOnNonDefaultRegistry_UpdatesTagAndReportsImage()
{
// Images on a non-default registry (e.g. GAR/GCR/ECR) must be matched and updated. The
// first path segment (us-docker.pkg.dev) is recognised as a registry, and the rest is the
// image name; only the tag should change.
const string inputYaml = @"apiVersion: v1
kind: Pod
spec:
containers:
- name: helloworld
image: us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v1";
const string expectedYaml = @"apiVersion: v1
kind: Pod
spec:
containers:
- name: helloworld
image: us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v2";

var imageReplacer = new ContainerImageReplacer(inputYaml, DefaultContainerRegistry);

var updatedImage = new List<ContainerImageReferenceAndHelmReference>
{
new(ContainerImageReference.FromReferenceString(
"us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v2",
DefaultContainerRegistry))
};

var result = imageReplacer.UpdateImages(updatedImage);

result.UpdatedContents.Should().Be(expectedYaml);
result.UpdatedImageReferences.Should().ContainSingle()
.Which.Should().Be("us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v2");
}

[Test]
public void DoesNotUpdateComments()
{
Expand Down
87 changes: 87 additions & 0 deletions source/Calamari.Tests/ArgoCD/DirectoryUpdaterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.IO;
using Calamari.ArgoCD;
using Calamari.ArgoCD.Conventions;
using Calamari.ArgoCD.Conventions.UpdateImageTag;
using Calamari.ArgoCD.Domain;
using Calamari.ArgoCD.Models;
using Calamari.Common.Plumbing.FileSystem;
using Calamari.Common.Plumbing.Logging;
using Calamari.Testing.Helpers;
using Calamari.Tests.Fixtures.Integration.FileSystem;
using FluentAssertions;
using NUnit.Framework;

namespace Calamari.Tests.ArgoCD
{
/// <summary>
/// Integration tests for DirectoryUpdater.Process() workflow.
/// Mirrors KustomizeUpdaterTests, focusing on non-default container registries.
/// </summary>
[TestFixture]
public class DirectoryUpdaterTests
{
ILog log;
ICalamariFileSystem fileSystem;
string tempDir;

[SetUp]
public void SetUp()
{
log = new InMemoryLog();
fileSystem = TestCalamariPhysicalFileSystem.GetPhysicalFileSystem();
tempDir = fileSystem.CreateTemporaryDirectory();
}

[TearDown]
public void TearDown()
{
fileSystem?.DeleteDirectory(tempDir);
}

[Test]
public void Process_WithImageOnNonDefaultRegistry_ProducesPatchSoChangesAreCommitted()
{
// Regression: an image on a non-default registry (e.g. GAR/GCR/ECR) must update the
// manifest AND produce a JSON patch, so HasChanges() is true and the commit/push isn't
// silently skipped.
const string deployment = @"apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld
spec:
template:
spec:
containers:
- name: helloworld
image: us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v1
";
fileSystem.OverwriteFile(Path.Combine(tempDir, "deployment.yaml"), deployment);

var garImages = new List<ContainerImageReferenceAndHelmReference>
{
new(ContainerImageReference.FromReferenceString(
"us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v2",
ArgoCDConstants.DefaultContainerRegistry)),
};

var sourceWithMetadata = new ApplicationSourceWithMetadata(
new ApplicationSource { Path = "." },
SourceType.Directory,
0);

var updater = new DirectoryUpdater(garImages, ArgoCDConstants.DefaultContainerRegistry, log, fileSystem);

var result = updater.Process(sourceWithMetadata, tempDir);

result.UpdatedImages.Should().NotBeEmpty();
result.HasChanges().Should().BeTrue("a JSON patch must be produced so the commit/push is not skipped");
result.PatchedFiles.Should().NotBeEmpty();

var updatedContent = fileSystem.ReadFile(Path.Combine(tempDir, "deployment.yaml"));
updatedContent.Should().Contain("us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v2");
updatedContent.Should().NotContain("helloworld:v1");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,30 @@ public void StructuredValue_AlreadyAtTarget_TracksWithFriendlyName()
result.AlreadyUpToDateImages.Should().BeEquivalentTo(["nginx:1.27.1"]);
}

[Test]
public void StructuredValue_ImageOnNonDefaultRegistry_UpdatesFullRefAndTracksWithRegistry()
{
// Helm/Ref report updates using the registry-qualified FriendlyName, so an image on a
// non-default registry (e.g. GAR/GCR/ECR) round-trips with its registry intact.
const string yaml = @"
image:
name: us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v1
";
var replacer = new HelmValuesImageReplaceStepVariables(yaml, DefaultRegistry, log);
var images = new List<ContainerImageReferenceAndHelmReference>
{
new(ContainerImageReference.FromReferenceString(
"us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v2",
DefaultRegistry), "image.name")
};

var result = replacer.UpdateImages(images);

using var scope = new AssertionScope();
result.UpdatedImageReferences.Should().BeEquivalentTo(["us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v2"]);
result.UpdatedContents.Should().Contain("name: us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v2");
}

[Test]
public void TwoImagesWithSameTag_OnlyUpdatesConfiguredPath()
{
Expand Down
8 changes: 4 additions & 4 deletions source/Calamari.Tests/ArgoCD/InlineJsonPatchReplacerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public void UpdateImages_WithMultiplePatchesAndImages_UpdatesAllMatchingImages()
result.UpdatedContents.Should().Contain("my-registry.com/busybox:stable");
result.UpdatedImageReferences.Count.Should().Be(2);
result.UpdatedImageReferences.Should().Contain("nginx:1.25");
result.UpdatedImageReferences.Should().Contain("busybox:stable");
result.UpdatedImageReferences.Should().Contain("my-registry.com/busybox:stable");
}

[Test]
Expand Down Expand Up @@ -146,7 +146,7 @@ public void UpdateImages_WithNestedContainerStructures_UpdatesNestedImages()
result.UpdatedContents.Should().NotBeNull();
result.UpdatedImageReferences.Count.Should().Be(2);
result.UpdatedImageReferences.Should().Contain("nginx:1.25");
result.UpdatedImageReferences.Should().Contain("busybox:stable");
result.UpdatedImageReferences.Should().Contain("my-registry.com/busybox:stable");
}

[Test]
Expand Down Expand Up @@ -388,7 +388,7 @@ public void UpdateImages_WithJson6902PatchAddOperation_UpdatesImageReferences()
result.UpdatedContents.Should().Contain("my-registry.com/busybox:stable");
result.UpdatedImageReferences.Count.Should().Be(2);
result.UpdatedImageReferences.Should().Contain("nginx:1.25");
result.UpdatedImageReferences.Should().Contain("busybox:stable");
result.UpdatedImageReferences.Should().Contain("my-registry.com/busybox:stable");
}

[Test]
Expand Down Expand Up @@ -428,7 +428,7 @@ public void UpdateImages_WithMixedStrategicMergeAndJson6902Patches_UpdatesBothTy
result.UpdatedContents.Should().Contain("my-registry.com/busybox:stable");
result.UpdatedImageReferences.Count.Should().Be(2);
result.UpdatedImageReferences.Should().Contain("nginx:1.25");
result.UpdatedImageReferences.Should().Contain("busybox:stable");
result.UpdatedImageReferences.Should().Contain("my-registry.com/busybox:stable");
}

[Test]
Expand Down
8 changes: 4 additions & 4 deletions source/Calamari.Tests/ArgoCD/JsonPatchImageReplacerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void UpdateImages_WithAddOperation_UpdatesImageReference()

result.UpdatedContents.Should().NotBeNull();
result.UpdatedImageReferences.Count.Should().Be(1);
result.UpdatedImageReferences.Should().ContainSingle(r => r == "busybox:stable");
result.UpdatedImageReferences.Should().ContainSingle(r => r == "my-registry.com/busybox:stable");
result.UpdatedContents.Should().Contain("my-registry.com/busybox:stable");
}

Expand Down Expand Up @@ -100,7 +100,7 @@ public void UpdateImages_WithObjectValue_UpdatesNestedImageReferences()
result.UpdatedContents.Should().NotBeNull();
result.UpdatedImageReferences.Count.Should().Be(2);
result.UpdatedImageReferences.Should().Contain("nginx:1.25");
result.UpdatedImageReferences.Should().Contain("busybox:stable");
result.UpdatedImageReferences.Should().Contain("my-registry.com/busybox:stable");
result.UpdatedContents.Should().Contain("nginx:1.25");
result.UpdatedContents.Should().Contain("my-registry.com/busybox:stable");
}
Expand Down Expand Up @@ -166,7 +166,7 @@ public void UpdateImages_WithComplexNestedStructure_UpdatesAllMatchingImages()
result.UpdatedContents.Should().NotBeNull();
result.UpdatedImageReferences.Count.Should().Be(2);
result.UpdatedImageReferences.Should().Contain("nginx:1.25");
result.UpdatedImageReferences.Should().Contain("busybox:stable");
result.UpdatedImageReferences.Should().Contain("my-registry.com/busybox:stable");
}

[Test]
Expand Down Expand Up @@ -197,7 +197,7 @@ public void UpdateImages_WithMultiplePatchOperations_UpdatesAllMatchingImages()
result.UpdatedContents.Should().NotBeNull();
result.UpdatedImageReferences.Count.Should().Be(2);
result.UpdatedImageReferences.Should().Contain("nginx:1.25");
result.UpdatedImageReferences.Should().Contain("busybox:stable");
result.UpdatedImageReferences.Should().Contain("my-registry.com/busybox:stable");
}

[Test]
Expand Down
10 changes: 5 additions & 5 deletions source/Calamari.Tests/ArgoCD/KustomizeImageReplacerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void UpdateImages_WithQualifiedNameOnly_AddsNewTagNode()
result.UpdatedContents.Should().NotBeNull();
result.UpdatedContents.Should().Be(expectedYaml);
result.UpdatedImageReferences.Count.Should().Be(1);
result.UpdatedImageReferences.Should().ContainSingle(r => r == "nginx:1.25");
result.UpdatedImageReferences.Should().ContainSingle(r => r == "docker.io/nginx:1.25");
}

[Test]
Expand Down Expand Up @@ -240,7 +240,7 @@ public void UpdateImages_ExistingContainerHasDigest_StripsDigestAndAddsNewTagNod
result.UpdatedContents.Should().NotBeNull();
result.UpdatedContents.Should().Be(expectedYaml);
result.UpdatedImageReferences.Count.Should().Be(1);
result.UpdatedImageReferences.Should().ContainSingle(r => r == "busybox:stable");
result.UpdatedImageReferences.Should().ContainSingle(r => r == "my-registry.com/busybox:stable");
}

[Test]
Expand All @@ -266,7 +266,7 @@ public void UpdateImages_HasNewNameNode_PreferencesNewNameNodeWhenMatching()
result.UpdatedContents.Should().NotBeNull();
result.UpdatedContents.Should().Be(expectedYaml);
result.UpdatedImageReferences.Count.Should().Be(1);
result.UpdatedImageReferences.Should().ContainSingle(r => r == "busybox:stable");
result.UpdatedImageReferences.Should().ContainSingle(r => r == "my-registry.com/busybox:stable");
}

[Test]
Expand Down Expand Up @@ -300,7 +300,7 @@ public void UpdateImages_MultipleImages_AllCorrectlyUpdated()
result.UpdatedContents.Should().NotBeNull();
result.UpdatedContents.Should().Be(expectedYaml);
result.UpdatedImageReferences.Count.Should().Be(2);
result.UpdatedImageReferences.Should().ContainSingle(r => r == "busybox:stable");
result.UpdatedImageReferences.Should().ContainSingle(r => r == "my-registry.com/busybox:stable");
result.UpdatedImageReferences.Should().ContainSingle(r => r == "nginx:1.25");
}

Expand Down Expand Up @@ -454,7 +454,7 @@ public void UpdateImages_FullKustomizationFile_ShouldOnlyChangeTheImagesNode()
result.UpdatedContents.Should().Be(expectedYaml);
result.UpdatedImageReferences.Count.Should().Be(2);
result.UpdatedImageReferences.Should().ContainSingle(r => r == "monopole:100");
result.UpdatedImageReferences.Should().ContainSingle(r => r == "nginx:1.25");
result.UpdatedImageReferences.Should().ContainSingle(r => r == "docker.io/nginx:1.25");
}
}
}
48 changes: 42 additions & 6 deletions source/Calamari.Tests/ArgoCD/KustomizeUpdaterTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Calamari.ArgoCD;
using Calamari.ArgoCD.Conventions;
using Calamari.ArgoCD.Conventions.UpdateImageTag;
Expand All @@ -13,7 +12,6 @@
using Calamari.Testing.Helpers;
using Calamari.Tests.Fixtures.Integration.FileSystem;
using FluentAssertions;
using NSubstitute;
using NUnit.Framework;

namespace Calamari.Tests.ArgoCD
Expand All @@ -27,8 +25,8 @@ public class KustomizeUpdaterTests
{
readonly List<ContainerImageReferenceAndHelmReference> imagesToUpdate = new()
{
new(ContainerImageReference.FromReferenceString("nginx:1.25", ArgoCDConstants.DefaultContainerRegistry)),
new(ContainerImageReference.FromReferenceString("busybox:stable", "my-registry.com")),
new(ContainerImageReference.FromReferenceString("docker.io/nginx:1.25")),
new(ContainerImageReference.FromReferenceString("my-registry.com/busybox:stable"))
};

ILog log;
Expand Down Expand Up @@ -279,7 +277,7 @@ public void Process_WithMixedPatchTypes_UpdatesAllFiles()
var result = updater.Process(sourceWithMetadata, tempDir);

result.UpdatedImages.Should().Contain("nginx:1.25");
result.UpdatedImages.Should().Contain("busybox:stable");
result.UpdatedImages.Should().Contain("my-registry.com/busybox:stable");
result.UpdatedImages.Count.Should().Be(2);

var updatedDeploymentContent = fileSystem.ReadFile(Path.Combine(tempDir,"deployment-patch.yaml"));
Expand All @@ -289,7 +287,45 @@ public void Process_WithMixedPatchTypes_UpdatesAllFiles()
updatedServiceContent.Should().Contain("busybox:stable");

var updatedKustomizationContent = fileSystem.ReadFile(Path.Combine(tempDir, "kustomization.yaml"));
updatedKustomizationContent.Should().Contain("busybox:stable");

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this intentional deletion of tag?

@oleksandr-codefresh oleksandr-codefresh Jun 16, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checked this, we can add it back

updatedKustomizationContent.Should().Contain("my-registry.com/busybox:");
}

[Test]
public void Process_WithImageOnNonDefaultRegistry_ProducesPatchSoChangesAreCommitted()
{
// Regression test: an image whose registry differs from the default (e.g. GAR/GCR/ECR)
// must still produce a JSON patch. Previously the recorded image reference had its
// registry stripped, so CreateJsonPatch re-parsed it, defaulted the registry to
// docker.io, failed to match, and returned no patch — meaning HasChanges() was false
// and the working-copy edit was silently discarded (never committed).
const string kustomizationContent = @"apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
- name: us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld
newTag: ""latest""
";

var garImages = new List<ContainerImageReferenceAndHelmReference>
{
new(ContainerImageReference.FromReferenceString(
"us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v2",
ArgoCDConstants.DefaultContainerRegistry)),
};

var sourceWithMetadata = new ApplicationSourceWithMetadata(
new ApplicationSource { Path = "." },
SourceType.Kustomize,
0);

CreateKustomizationFile(kustomizationContent);

var updater = new KustomizeUpdater(CreateMockDeploymentConfig(garImages), ArgoCDConstants.DefaultContainerRegistry, log, fileSystem);

var result = updater.Process(sourceWithMetadata, tempDir);

result.UpdatedImages.Should().Contain("us-docker.pkg.dev/shared-gke-dev-gqtrxy/argo-test/helloworld:v2");
result.HasChanges().Should().BeTrue("a JSON patch must be produced so the commit/push is not skipped");
result.PatchedFiles.Should().NotBeEmpty();
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public void WithTag_WithNoRepository_ReturnsImageWithTag()
var image = ContainerImageReference.FromReferenceString("nginx:latest");
var result = image.WithTag("1.27");

result.Should().Be("nginx:1.27");
result.FriendlyName().Should().Be("nginx:1.27");
}

[Test]
Expand All @@ -133,7 +133,7 @@ public void WithTag_WithRepository_ReturnsRepositoryImageWithTag()

var result = image.WithTag("1.27");

result.Should().Be("docker.io/nginx:1.27");
result.FriendlyName().Should().Be("docker.io/nginx:1.27");
}

[Test]
Expand All @@ -143,7 +143,7 @@ public void WithTag_WithDefaultRegistry_ReturnsImageWithTag()

var result = image.WithTag("1.27");

result.Should().Be("nginx:1.27");
result.FriendlyName().Should().Be("nginx:1.27");
}

[Theory]
Expand Down
Loading