diff --git a/Makefile b/Makefile
index aaec6cef..718aacb7 100644
--- a/Makefile
+++ b/Makefile
@@ -27,7 +27,11 @@ SOURCE_VER ?= $(shell go list -m github.com/fluxcd/source-controller/api | awk '
# Version of the image-reflector-controller from which to get the ImagePolicy CRD.
# Pulls image-reflector-controller/api's version set in go.mod.
-REFLECTOR_VER ?= $(shell go list -m github.com/fluxcd/image-reflector-controller/api | awk '{print $$2}')
+# If the version has hyphens, it's assumed to be a non-released version
+# and the part after the last hyphen is assumed to be a commit hash,
+# which is used to fetch the CRD from the repository in this case.
+# Otherwise the whole version is used.
+REFLECTOR_VER ?= $(shell go list -m github.com/fluxcd/image-reflector-controller/api | awk '{ n = split($$2, a, "-"); print a[n] }')
# Repository root based on Git metadata.
REPOSITORY_ROOT := $(shell git rev-parse --show-toplevel)
diff --git a/api/go.mod b/api/go.mod
index b579c93b..39253b3b 100644
--- a/api/go.mod
+++ b/api/go.mod
@@ -18,6 +18,7 @@ require (
github.com/kr/text v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/text v0.24.0 // indirect
diff --git a/api/go.sum b/api/go.sum
index b1e5374f..98dc17fe 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -40,8 +40,9 @@ github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
diff --git a/api/v1beta2/reference.go b/api/v1beta2/reference.go
index 917189ee..c0d9e719 100644
--- a/api/v1beta2/reference.go
+++ b/api/v1beta2/reference.go
@@ -16,7 +16,9 @@ limitations under the License.
package v1beta2
-import "fmt"
+import (
+ "fmt"
+)
// CrossNamespaceSourceReference contains enough information to let you locate the
// typed Kubernetes resource object at cluster level.
@@ -55,10 +57,15 @@ type ImageRef struct {
// Tag is the image's tag.
// +required
Tag string `json:"tag"`
+ // Digest is the image's digest.
+ // +optional
+ Digest string `json:"digest,omitempty"`
}
-// String combines the components of ImageRef to construct a string
-// representation of the image reference.
-func (r ImageRef) String() string {
- return r.Name + ":" + r.Tag
+func (in *ImageRef) String() string {
+ res := in.Name + ":" + in.Tag
+ if in.Digest != "" {
+ res += "@" + in.Digest
+ }
+ return res
}
diff --git a/config/crd/bases/image.toolkit.fluxcd.io_imageupdateautomations.yaml b/config/crd/bases/image.toolkit.fluxcd.io_imageupdateautomations.yaml
index 1ff8c40c..2973df70 100644
--- a/config/crd/bases/image.toolkit.fluxcd.io_imageupdateautomations.yaml
+++ b/config/crd/bases/image.toolkit.fluxcd.io_imageupdateautomations.yaml
@@ -691,6 +691,9 @@ spec:
additionalProperties:
description: ImageRef represents an image reference.
properties:
+ digest:
+ description: Digest is the image's digest.
+ type: string
name:
description: Name is the bare image's name.
type: string
diff --git a/docs/api/v1beta2/image-automation.md b/docs/api/v1beta2/image-automation.md
index 9ebf3ba9..ab12d64b 100644
--- a/docs/api/v1beta2/image-automation.md
+++ b/docs/api/v1beta2/image-automation.md
@@ -332,6 +332,18 @@ string
Tag is the image’s tag.
+
+
+digest
+
+string
+
+ |
+
+(Optional)
+ Digest is the image’s digest.
+ |
+
diff --git a/go.mod b/go.mod
index 2dfe314b..7956d319 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +16,7 @@ require (
github.com/ProtonMail/go-crypto v1.2.0
github.com/cyphar/filepath-securejoin v0.4.1
github.com/fluxcd/image-automation-controller/api v0.40.0
- github.com/fluxcd/image-reflector-controller/api v0.34.0
+ github.com/fluxcd/image-reflector-controller/api v0.34.1-0.20250512083550-1c69ffe07af4
github.com/fluxcd/pkg/apis/acl v0.7.0
github.com/fluxcd/pkg/apis/event v0.17.0
github.com/fluxcd/pkg/apis/meta v1.12.0
diff --git a/go.sum b/go.sum
index 93efae2d..37088371 100644
--- a/go.sum
+++ b/go.sum
@@ -110,8 +110,8 @@ github.com/fluxcd/cli-utils v0.36.0-flux.13 h1:2X5yjz/rk9mg7+bMFBDZKGKzeZpAmY2s6
github.com/fluxcd/cli-utils v0.36.0-flux.13/go.mod h1:b2iSoIeDTtjfCB0IKtGgqlhhvWa1oux3e90CjOf81oA=
github.com/fluxcd/gitkit v0.6.0 h1:iNg5LTx6ePo+Pl0ZwqHTAkhbUHxGVSY3YCxCdw7VIFg=
github.com/fluxcd/gitkit v0.6.0/go.mod h1:svOHuKi0fO9HoawdK4HfHAJJseZDHHjk7I3ihnCIqNo=
-github.com/fluxcd/image-reflector-controller/api v0.34.0 h1:+0AGoaYzHYXzVDQO9xq2eGZKkPl81Bfz6xFI7rElBzs=
-github.com/fluxcd/image-reflector-controller/api v0.34.0/go.mod h1:C6742RYyZVt2KIyJv16lb4gYbsK+P1RGQeaQ8C8huec=
+github.com/fluxcd/image-reflector-controller/api v0.34.1-0.20250512083550-1c69ffe07af4 h1:4wm/cMYP/8bGXvFdxdTvftheXZWDmcnu6XXnif7JG7A=
+github.com/fluxcd/image-reflector-controller/api v0.34.1-0.20250512083550-1c69ffe07af4/go.mod h1:kH1hTdo3h08J2ZDnw7w+5D+xcK2DJOY0fwipOG4e8fE=
github.com/fluxcd/pkg/apis/acl v0.7.0 h1:dMhZJH+g6ZRPjs4zVOAN9vHBd1DcavFgcIFkg5ooOE0=
github.com/fluxcd/pkg/apis/acl v0.7.0/go.mod h1:uv7pXXR/gydiX4MUwlQa7vS8JONEDztynnjTvY3JxKQ=
github.com/fluxcd/pkg/apis/event v0.17.0 h1:foEINE++pCJlWVhWjYDXfkVmGKu8mQ4BDBlbYi5NU7M=
diff --git a/internal/constants/constants.go b/internal/constants/constants.go
new file mode 100644
index 00000000..4dc0be0d
--- /dev/null
+++ b/internal/constants/constants.go
@@ -0,0 +1,24 @@
+/*
+Copyright 2025 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package constants
+
+const (
+ // SetterShortHand is a shorthand that can be used to mark
+ // setters; instead of
+ // # { "$ref": "#/definitions/
+ SetterShortHand = "$imagepolicy"
+)
diff --git a/internal/controller/imageupdateautomation_controller.go b/internal/controller/imageupdateautomation_controller.go
index 7132b3c8..c44c8104 100644
--- a/internal/controller/imageupdateautomation_controller.go
+++ b/internal/controller/imageupdateautomation_controller.go
@@ -20,7 +20,6 @@ import (
"context"
"errors"
"fmt"
- "strings"
"time"
corev1 "k8s.io/api/core/v1"
@@ -331,10 +330,14 @@ func (r *ImageUpdateAutomationReconciler) reconcile(ctx context.Context, sp *pat
conditions.MarkUnknown(obj, meta.ReadyCondition, meta.ProgressingReason, "reconciliation in progress")
}
- observedPolicies, err := observedPolicies(policies)
- if err != nil {
- result, retErr = ctrl.Result{}, err
- return
+ // Index the policies by their name.
+ observedPolicies := imagev1.ObservedPolicies{}
+ for _, policy := range policies {
+ observedPolicies[policy.Name] = imagev1.ImageRef{
+ Name: policy.Status.LatestRef.Name,
+ Tag: policy.Status.LatestRef.Tag,
+ Digest: policy.Status.LatestRef.Digest,
+ }
}
// If the policies have changed, require a full sync.
@@ -547,7 +550,7 @@ func getPolicies(ctx context.Context, kclient client.Client, namespace string, s
readyPolicies := []imagev1_reflect.ImagePolicy{}
for _, policy := range policies.Items {
// Ignore the policies that don't have a latest image.
- if policy.Status.LatestImage == "" {
+ if policy.Status.LatestRef == nil {
continue
}
readyPolicies = append(readyPolicies, policy)
@@ -556,31 +559,6 @@ func getPolicies(ctx context.Context, kclient client.Client, namespace string, s
return readyPolicies, nil
}
-// observedPolicies takes a list of ImagePolicies and returns an
-// ObservedPolicies with all the policies in it.
-func observedPolicies(policies []imagev1_reflect.ImagePolicy) (imagev1.ObservedPolicies, error) {
- observedPolicies := imagev1.ObservedPolicies{}
- for _, policy := range policies {
- name, tag := splitByLastColon(policy.Status.LatestImage)
- if name == "" || tag == "" {
- return nil, fmt.Errorf("failed parsing image: %s", policy.Status.LatestImage)
- }
- observedPolicies[policy.Name] = imagev1.ImageRef{
- Name: name,
- Tag: tag,
- }
- }
- return observedPolicies, nil
-}
-
-func splitByLastColon(latestImage string) (string, string) {
- idx := strings.LastIndex(latestImage, ":")
- if idx == -1 {
- return latestImage, ""
- }
- return latestImage[:idx], latestImage[idx+1:]
-}
-
// observedPoliciesChanged returns if the previous and current observedPolicies
// have changed.
func observedPoliciesChanged(previous, current imagev1.ObservedPolicies) bool {
diff --git a/internal/controller/imageupdateautomation_controller_test.go b/internal/controller/imageupdateautomation_controller_test.go
index 52d7d296..f5705908 100644
--- a/internal/controller/imageupdateautomation_controller_test.go
+++ b/internal/controller/imageupdateautomation_controller_test.go
@@ -1532,7 +1532,7 @@ func Test_getPolicies(t *testing.T) {
aPolicy.Name = p.name
aPolicy.Namespace = p.namespace
aPolicy.Status = imagev1_reflect.ImagePolicyStatus{
- LatestImage: p.latestImage,
+ LatestRef: testutil.ImageToRef(p.latestImage),
}
aPolicy.Labels = p.labels
testObjects = append(testObjects, aPolicy)
@@ -1555,65 +1555,6 @@ func Test_getPolicies(t *testing.T) {
}
}
-func Test_observedPolicies(t *testing.T) {
- tests := []struct {
- name string
- policyWithImage map[string]string
- want imagev1.ObservedPolicies
- wantErr bool
- }{
- {
- name: "good policies",
- policyWithImage: map[string]string{
- "p1": "aaa:bbb",
- "p2": "ccc:ddd",
- "p3": "eee:latest",
- "p4": "registry.localhost:5000/sample-web:0.1.0",
- },
- want: imagev1.ObservedPolicies{
- "p1": imagev1.ImageRef{Name: "aaa", Tag: "bbb"},
- "p2": imagev1.ImageRef{Name: "ccc", Tag: "ddd"},
- "p3": imagev1.ImageRef{Name: "eee", Tag: "latest"},
- "p4": imagev1.ImageRef{Name: "registry.localhost:5000/sample-web", Tag: "0.1.0"},
- },
- },
- {
- name: "bad policy image with no tag",
- policyWithImage: map[string]string{
- "p1": "aaa",
- },
- wantErr: true,
- },
- {
- name: "no policy",
- want: imagev1.ObservedPolicies{},
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- g := NewWithT(t)
-
- policies := []imagev1_reflect.ImagePolicy{}
- for name, image := range tt.policyWithImage {
- aPolicy := imagev1_reflect.ImagePolicy{}
- aPolicy.Name = name
- aPolicy.Status = imagev1_reflect.ImagePolicyStatus{
- LatestImage: image,
- }
- policies = append(policies, aPolicy)
- }
-
- result, err := observedPolicies(policies)
- if (err != nil) != tt.wantErr {
- g.Fail(fmt.Sprintf("unexpected error: %v", err))
- }
- if err == nil {
- g.Expect(result).To(Equal(tt.want))
- }
- })
- }
-}
-
func Test_observedPoliciesChanged(t *testing.T) {
tests := []struct {
name string
@@ -1902,7 +1843,7 @@ func createImagePolicyWithLatestImageForSpec(ctx context.Context, kClient client
return err
}
patch := client.MergeFrom(policy.DeepCopy())
- policy.Status.LatestImage = latest
+ policy.Status.LatestRef = testutil.ImageToRef(latest)
return kClient.Status().Patch(ctx, policy, patch)
}
@@ -1916,7 +1857,7 @@ func updateImagePolicyWithLatestImage(ctx context.Context, kClient client.Client
return err
}
patch := client.MergeFrom(policy.DeepCopy())
- policy.Status.LatestImage = latest
+ policy.Status.LatestRef = testutil.ImageToRef(latest)
return kClient.Status().Patch(ctx, policy, patch)
}
diff --git a/internal/controller/predicate.go b/internal/controller/predicate.go
index 03893d3b..380128bf 100644
--- a/internal/controller/predicate.go
+++ b/internal/controller/predicate.go
@@ -53,7 +53,11 @@ func (latestImageChangePredicate) Update(e event.UpdateEvent) bool {
return false
}
- if oldSource.Status.LatestImage != newSource.Status.LatestImage {
+ if newSource.Status.LatestRef == nil {
+ return false
+ }
+
+ if oldSource.Status.LatestRef == nil || *oldSource.Status.LatestRef != *newSource.Status.LatestRef {
return true
}
diff --git a/internal/controller/predicate_test.go b/internal/controller/predicate_test.go
index e6479b25..ae5819dc 100644
--- a/internal/controller/predicate_test.go
+++ b/internal/controller/predicate_test.go
@@ -35,24 +35,24 @@ func Test_latestImageChangePredicate_Update(t *testing.T) {
{
name: "no latest image",
beforeFunc: func(oldObj, newObj *imagev1_reflect.ImagePolicy) {
- oldObj.Status.LatestImage = ""
- newObj.Status.LatestImage = ""
+ oldObj.Status.LatestRef = nil
+ newObj.Status.LatestRef = nil
},
want: false,
},
{
name: "new image, no old image",
beforeFunc: func(oldObj, newObj *imagev1_reflect.ImagePolicy) {
- oldObj.Status.LatestImage = ""
- newObj.Status.LatestImage = "foo"
+ oldObj.Status.LatestRef = nil
+ newObj.Status.LatestRef = &imagev1_reflect.ImageRef{Name: "foo"}
},
want: true,
},
{
name: "different old and new image",
beforeFunc: func(oldObj, newObj *imagev1_reflect.ImagePolicy) {
- oldObj.Status.LatestImage = "bar"
- newObj.Status.LatestImage = "foo"
+ oldObj.Status.LatestRef = &imagev1_reflect.ImageRef{Name: "bar"}
+ newObj.Status.LatestRef = &imagev1_reflect.ImageRef{Name: "foo"}
},
want: true,
},
diff --git a/internal/policy/applier_test.go b/internal/policy/applier_test.go
index e183c483..7fc1bf59 100644
--- a/internal/policy/applier_test.go
+++ b/internal/policy/applier_test.go
@@ -124,7 +124,7 @@ func Test_applyPolicies(t *testing.T) {
policy.Name = name
policy.Namespace = testNS
policy.Status = imagev1_reflect.ImagePolicyStatus{
- LatestImage: image,
+ LatestRef: testutil.ImageToRef(image),
}
policyList = append(policyList, *policy)
}
diff --git a/internal/source/source_test.go b/internal/source/source_test.go
index 00c6e575..a62a9b21 100644
--- a/internal/source/source_test.go
+++ b/internal/source/source_test.go
@@ -640,7 +640,7 @@ Testing: value
imgPolicy.Name = "policy1"
imgPolicy.Namespace = testNS
imgPolicy.Status = imagev1_reflect.ImagePolicyStatus{
- LatestImage: tt.latestImage,
+ LatestRef: testutil.ImageToRef(tt.latestImage),
}
testObjects = append(testObjects, imgPolicy)
policyKey := client.ObjectKeyFromObject(imgPolicy)
@@ -865,7 +865,7 @@ func test_pushBranchUpdateScenarios(t *testing.T, proto string, srcOpts []Source
imgPolicy.Name = "policy1"
imgPolicy.Namespace = testNS
imgPolicy.Status = imagev1_reflect.ImagePolicyStatus{
- LatestImage: latestImage,
+ LatestRef: testutil.ImageToRef(latestImage),
}
testObjects = append(testObjects, imgPolicy)
// Take the policyKey to update the setter marker with.
@@ -966,7 +966,7 @@ func test_pushBranchUpdateScenarios(t *testing.T, proto string, srcOpts []Source
// Update latest image.
latestImage = "helloworld:v1.3.0"
- imgPolicy.Status.LatestImage = latestImage
+ imgPolicy.Status.LatestRef = testutil.ImageToRef(latestImage)
g.Expect(kClient.Update(ctx, imgPolicy)).To(Succeed())
policies = []imagev1_reflect.ImagePolicy{*imgPolicy}
@@ -1014,7 +1014,7 @@ func test_pushBranchUpdateScenarios(t *testing.T, proto string, srcOpts []Source
// Update latest image.
latestImage = "helloworld:v1.3.1"
- imgPolicy.Status.LatestImage = latestImage
+ imgPolicy.Status.LatestRef = testutil.ImageToRef(latestImage)
g.Expect(kClient.Update(ctx, imgPolicy)).To(Succeed())
policies = []imagev1_reflect.ImagePolicy{*imgPolicy}
diff --git a/internal/testutil/util.go b/internal/testutil/util.go
index 53324df6..dc6baba1 100644
--- a/internal/testutil/util.go
+++ b/internal/testutil/util.go
@@ -24,6 +24,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strings"
"time"
"github.com/ProtonMail/go-crypto/openpgp"
@@ -43,7 +44,8 @@ import (
"github.com/fluxcd/pkg/gittestserver"
- "github.com/fluxcd/image-automation-controller/pkg/update"
+ "github.com/fluxcd/image-automation-controller/internal/constants"
+ imagev1_reflect "github.com/fluxcd/image-reflector-controller/api/v1beta2"
)
const (
@@ -80,7 +82,7 @@ func ReplaceMarkerWithMarker(path string, policyKey types.NamespacedName, marker
}
func setterRef(name types.NamespacedName) string {
- return fmt.Sprintf(`{"%s": "%s:%s"}`, update.SetterShortHand, name.Namespace, name.Name)
+ return fmt.Sprintf(`{"%s": "%s:%s"}`, constants.SetterShortHand, name.Namespace, name.Name)
}
func CommitInRepo(ctx context.Context, g *WithT, repoURL, branch, remote, msg string, changeFiles func(path string)) plumbing.Hash {
@@ -456,3 +458,23 @@ func GetSigningKeyPair(g *WithT, passphrase string) (*openpgp.Entity, []byte) {
return pgpEntity, b.Bytes()
}
+
+func ImageToRef(image string) *imagev1_reflect.ImageRef {
+ var digest string
+
+ if idx := strings.LastIndex(image, "@"); idx != -1 {
+ image, digest = image[:idx], image[idx+1:]
+ }
+
+ var tag string
+
+ if idx := strings.LastIndex(image, ":"); idx != -1 {
+ image, tag = image[:idx], image[idx+1:]
+ }
+
+ return &imagev1_reflect.ImageRef{
+ Name: image,
+ Tag: tag,
+ Digest: digest,
+ }
+}
diff --git a/pkg/update/setters.go b/pkg/update/setters.go
index d8123c92..c5566fde 100644
--- a/pkg/update/setters.go
+++ b/pkg/update/setters.go
@@ -18,7 +18,6 @@ package update
import (
"fmt"
- "strings"
"github.com/go-logr/logr"
"github.com/google/go-containerregistry/pkg/name"
@@ -31,21 +30,17 @@ import (
"sigs.k8s.io/kustomize/kyaml/sets"
"sigs.k8s.io/kustomize/kyaml/yaml"
+ "github.com/fluxcd/image-automation-controller/internal/constants"
imagev1_reflect "github.com/fluxcd/image-reflector-controller/api/v1beta2"
)
const (
// This is preserved from setters2
K8sCliExtensionKey = "x-k8s-cli"
-
- // SetterShortHand is a shorthand that can be used to mark
- // setters; instead of
- // # { "$ref": "#/definitions/
- SetterShortHand = "$imagepolicy"
)
func init() {
- fieldmeta.SetShortHandRef(SetterShortHand)
+ fieldmeta.SetShortHandRef(constants.SetterShortHand)
// this prevents the global schema, should it be initialised, from
// parsing all the Kubernetes openAPI definitions, which is not
// necessary.
@@ -144,7 +139,7 @@ func UpdateV2WithSetters(tracelog logr.Logger, inpath, outpath string, policies
}
result.Files[file] = fileres
}
- objres, ok := fileres.Objects[oid]
+ objres := fileres.Objects[oid]
for _, n := range objres {
if n == ref {
return
@@ -156,7 +151,7 @@ func UpdateV2WithSetters(tracelog logr.Logger, inpath, outpath string, policies
defs := map[string]spec.Schema{}
for _, policy := range policies {
- if policy.Status.LatestImage == "" {
+ if policy.Status.LatestRef == nil {
continue
}
// Using strict validation would mean any image that omits the
@@ -165,10 +160,10 @@ func UpdateV2WithSetters(tracelog logr.Logger, inpath, outpath string, policies
// filled in. Usually this would mean the tag would end up
// being `latest` if empty in the input; but I'm assuming here
// that the policy won't have a tagless ref.
- image := policy.Status.LatestImage
+ image := policy.Status.LatestRef.String()
r, err := name.ParseReference(image, name.WeakValidation)
if err != nil {
- return ResultV2{}, fmt.Errorf("encountered invalid image ref %q: %w", policy.Status.LatestImage, err)
+ return ResultV2{}, fmt.Errorf("encountered invalid image ref %q: %w", image, err)
}
ref := imageRef{
Reference: r,
@@ -178,15 +173,13 @@ func UpdateV2WithSetters(tracelog logr.Logger, inpath, outpath string, policies
},
}
- tag := ref.Identifier()
- // annoyingly, neither the library imported above, nor an
- // alternative I found, will yield the original image name;
- // this is an easy way to get it
- name := strings.TrimSuffix(image, ":"+tag)
+ tag := policy.Status.LatestRef.Tag
+ name := policy.Status.LatestRef.Name
+ digest := policy.Status.LatestRef.Digest
imageSetter := fmt.Sprintf("%s:%s", policy.GetNamespace(), policy.GetName())
tracelog.Info("adding setter", "name", imageSetter)
- defs[fieldmeta.SetterDefinitionPrefix+imageSetter] = setterSchema(imageSetter, policy.Status.LatestImage)
+ defs[fieldmeta.SetterDefinitionPrefix+imageSetter] = setterSchema(imageSetter, image)
imageRefs[imageSetter] = ref
tagSetter := imageSetter + ":tag"
@@ -194,11 +187,15 @@ func UpdateV2WithSetters(tracelog logr.Logger, inpath, outpath string, policies
defs[fieldmeta.SetterDefinitionPrefix+tagSetter] = setterSchema(tagSetter, tag)
imageRefs[tagSetter] = ref
- // Context().Name() gives the image repository _as supplied_
nameSetter := imageSetter + ":name"
tracelog.Info("adding setter", "name", nameSetter)
defs[fieldmeta.SetterDefinitionPrefix+nameSetter] = setterSchema(nameSetter, name)
imageRefs[nameSetter] = ref
+
+ digestSetter := imageSetter + ":digest"
+ tracelog.Info("adding setter", "name", digestSetter)
+ defs[fieldmeta.SetterDefinitionPrefix+digestSetter] = setterSchema(digestSetter, digest)
+ imageRefs[digestSetter] = ref
}
settersSchema.Definitions = defs
@@ -206,7 +203,7 @@ func UpdateV2WithSetters(tracelog logr.Logger, inpath, outpath string, policies
// get ready with the reader and writer
reader := &ScreeningLocalReader{
Path: inpath,
- Token: fmt.Sprintf("%q", SetterShortHand),
+ Token: fmt.Sprintf("%q", constants.SetterShortHand),
Trace: tracelog,
}
writer := &kio.LocalPackageWriter{
diff --git a/pkg/update/testdata/setters/expected/kustomization.yml b/pkg/update/testdata/setters/expected/kustomization.yml
index 739a25dd..cf492cb6 100644
--- a/pkg/update/testdata/setters/expected/kustomization.yml
+++ b/pkg/update/testdata/setters/expected/kustomization.yml
@@ -7,6 +7,7 @@ images:
- name: container
newName: index.repo.fake/updated # {"$imagepolicy": "automation-ns:policy:name"}
newTag: v1.0.1 # {"$imagepolicy": "automation-ns:policy:tag"}
+ newDigest: sha256:6745aaad46d795c9836632e1fb62f24b7e7f4c843144da8e47a5465c411a14be # {"$imagepolicy": "automation-ns:digest:digest"}
# Prove fix for https://github.com/fluxcd/flux2/issues/3284
patches:
- patch: |
@@ -24,3 +25,4 @@ patches:
version: v1
kind: Deployment
name: sxxxxdadminservice
+ image: image:v1.0.0@sha256:6745aaad46d795c9836632e1fb62f24b7e7f4c843144da8e47a5465c411a14be # {"$imagepolicy": "automation-ns:digest"}
diff --git a/pkg/update/testdata/setters/original/kustomization.yml b/pkg/update/testdata/setters/original/kustomization.yml
index d1cb6a22..9a7d5eaf 100644
--- a/pkg/update/testdata/setters/original/kustomization.yml
+++ b/pkg/update/testdata/setters/original/kustomization.yml
@@ -7,6 +7,7 @@ images:
- name: container
newName: replaced # {"$imagepolicy": "automation-ns:policy:name"}
newTag: v1 # {"$imagepolicy": "automation-ns:policy:tag"}
+ newDigest: sha256:1234567890abcdef # {"$imagepolicy": "automation-ns:digest:digest"}
# Prove fix for https://github.com/fluxcd/flux2/issues/3284
patches:
- patch: |
@@ -24,3 +25,4 @@ patches:
version: v1
kind: Deployment
name: sxxxxdadminservice
+ image: image # {"$imagepolicy": "automation-ns:digest"}
diff --git a/pkg/update/update_test.go b/pkg/update/update_test.go
index 36f428b2..c28d783f 100644
--- a/pkg/update/update_test.go
+++ b/pkg/update/update_test.go
@@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/kustomize/kyaml/yaml"
+ "github.com/fluxcd/image-automation-controller/internal/testutil"
"github.com/fluxcd/image-automation-controller/pkg/test"
imagev1_reflect "github.com/fluxcd/image-reflector-controller/api/v1beta2"
)
@@ -35,21 +36,30 @@ func TestUpdateWithSetters(t *testing.T) {
policies := []imagev1_reflect.ImagePolicy{
{
- ObjectMeta: metav1.ObjectMeta{ // name matches marker used in testdata/setters/{original,expected}
+ ObjectMeta: metav1.ObjectMeta{
Namespace: "automation-ns",
Name: "policy",
},
Status: imagev1_reflect.ImagePolicyStatus{
- LatestImage: "index.repo.fake/updated:v1.0.1",
+ LatestRef: testutil.ImageToRef("index.repo.fake/updated:v1.0.1"),
},
},
{
- ObjectMeta: metav1.ObjectMeta{ // name matches marker used in testdata/setters/{original,expected}
+ ObjectMeta: metav1.ObjectMeta{
Namespace: "automation-ns",
Name: "unchanged",
},
Status: imagev1_reflect.ImagePolicyStatus{
- LatestImage: "image:v1.0.0",
+ LatestRef: testutil.ImageToRef("image:v1.0.0"),
+ },
+ },
+ {
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "automation-ns",
+ Name: "digest",
+ },
+ Status: imagev1_reflect.ImagePolicyStatus{
+ LatestRef: testutil.ImageToRef("image:v1.0.0@sha256:6745aaad46d795c9836632e1fb62f24b7e7f4c843144da8e47a5465c411a14be"),
},
},
}
@@ -75,18 +85,26 @@ func TestUpdateWithSetters(t *testing.T) {
Name: "foo",
},
}}
+
r, _ := name.ParseReference("index.repo.fake/updated:v1.0.1")
expectedImageRef := imageRef{r, types.NamespacedName{
Name: "policy",
Namespace: "automation-ns",
}}
+ r, _ = name.ParseReference("image:v1.0.0@sha256:6745aaad46d795c9836632e1fb62f24b7e7f4c843144da8e47a5465c411a14be")
+ expectedImageRefDigest := imageRef{r, types.NamespacedName{
+ Name: "digest",
+ Namespace: "automation-ns",
+ }}
+
expectedResult := Result{
Files: map[string]FileResult{
"kustomization.yml": {
Objects: map[ObjectIdentifier][]ImageRef{
kustomizeResourceID: {
expectedImageRef,
+ expectedImageRefDigest,
},
},
},
@@ -130,6 +148,16 @@ func TestUpdateWithSetters(t *testing.T) {
NewValue: "v1.0.1",
Setter: "automation-ns:policy:tag",
},
+ {
+ OldValue: "sha256:1234567890abcdef",
+ NewValue: "sha256:6745aaad46d795c9836632e1fb62f24b7e7f4c843144da8e47a5465c411a14be",
+ Setter: "automation-ns:digest:digest",
+ },
+ {
+ OldValue: "image",
+ NewValue: "image:v1.0.0@sha256:6745aaad46d795c9836632e1fb62f24b7e7f4c843144da8e47a5465c411a14be",
+ Setter: "automation-ns:digest",
+ },
},
},
"Kustomization": {
diff --git a/tests/fuzz/oss_fuzz_prebuild.sh b/tests/fuzz/oss_fuzz_prebuild.sh
index 3c971873..daf343ee 100755
--- a/tests/fuzz/oss_fuzz_prebuild.sh
+++ b/tests/fuzz/oss_fuzz_prebuild.sh
@@ -30,7 +30,11 @@ SOURCE_VER=$(go list -m github.com/fluxcd/source-controller/api | awk '{print $2
# Version of the image-reflector-controller from which to get the ImagePolicy CRD.
# Pulls image-reflector-controller/api's version set in go.mod.
-REFLECTOR_VER=$(go list -m github.com/fluxcd/image-reflector-controller/api | awk '{print $2}')
+# If the version has hyphens, it's assumed to be a non-released version
+# and the part after the last hyphen is assumed to be a commit hash,
+# which is used to fetch the CRD from the repository in this case.
+# Otherwise the whole version is used.
+REFLECTOR_VER=$(go list -m github.com/fluxcd/image-reflector-controller/api | awk '{ n = split($2, a, "-"); print a[n] }')
if [ -d "../../internal/controller/testdata/crds" ]; then
cp ../../internal/controller/testdata/crds/*.yaml testdata/crds