Skip to content

Commit 7975a00

Browse files
committed
Add deprecation handling for .Updated template field
Implement error handling for deprecated .Updated template field usage in ImageUpdateAutomation commit message templates. When users attempt to use deprecated .Updated fields, the controller now sets a Stalled condition with a clear error message directing them to use .Changed fields instead, preventing infinite reconciliation loops. This prepares for the eventual removal of the .Updated field from the ImageUpdateAutomation API as part of GA preparation. Signed-off-by: cappyzawa <[email protected]>
1 parent 7bd947c commit 7975a00

File tree

6 files changed

+126
-27
lines changed

6 files changed

+126
-27
lines changed

api/v1beta2/condition_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,7 @@ const (
3636

3737
// InvalidPolicySelectorReason represents an invalid policy selector.
3838
InvalidPolicySelectorReason string = "InvalidPolicySelector"
39+
40+
// RemovedTemplateFieldReason represents usage of removed template field.
41+
RemovedTemplateFieldReason string = "RemovedTemplateField"
3942
)

docs/spec/v1beta2/imageupdateautomations.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -374,12 +374,11 @@ spec:
374374
Automated image update by Flux
375375
```
376376

377-
**Deprecation Note:** The `Updated` template data available in v1beta1 API is
378-
deprecated. `Changed` template data is recommended for template data, as it
379-
accommodates for all the updates, including partial updates to just the image
380-
name or the tag, not just full image with name and tag update. The old templates
381-
will continue to work in v1beta2 API as `Updated` has not been removed yet. In
382-
the next API version, `Updated` may be removed.
377+
**Removal Note:** The `Updated` template data has been removed from the API.
378+
Use `Changed` template data instead, as it accommodates for all the updates,
379+
including partial updates to just the image name or the tag, not just full image
380+
with name and tag update. Templates using `Updated` will result in an error and
381+
the ImageUpdateAutomation will be marked as Stalled.
383382

384383
The message template also has access to the data related to the changes made by
385384
the automation. The template is a [Go text template][go-text-template]. The data

internal/controller/imageupdateautomation_controller.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,14 @@ func (r *ImageUpdateAutomationReconciler) reconcile(ctx context.Context, sp *pat
481481

482482
pushResult, err = sm.CommitAndPush(ctx, obj, policyResult, pushCfg...)
483483
if err != nil {
484+
// Check if error is due to removed template field usage.
485+
// Set Stalled condition and return nil error to prevent requeue, allowing user to fix template.
486+
if errors.Is(err, source.ErrRemovedTemplateField) {
487+
conditions.MarkStalled(obj, imagev1.RemovedTemplateFieldReason, "%s", err)
488+
result, retErr = ctrl.Result{}, nil
489+
return
490+
}
491+
484492
e := fmt.Errorf("failed to update source: %w", err)
485493
conditions.MarkFalse(obj, meta.ReadyCondition, imagev1.GitOperationFailedReason, "%s", e)
486494
result, retErr = ctrl.Result{}, e

internal/controller/imageupdateautomation_controller_test.go

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,12 @@ const (
7171
Automation: {{ .AutomationObject }}
7272
7373
Files:
74-
{{ range $filename, $_ := .Updated.Files -}}
74+
{{ range $filename, $_ := .Changed.ImageResult.Files -}}
7575
- {{ $filename }}
7676
{{ end -}}
7777
7878
Objects:
79-
{{ range $resource, $_ := .Updated.Objects -}}
79+
{{ range $resource, $_ := .Changed.ImageResult.Objects -}}
8080
{{ if eq $resource.Kind "Deployment" -}}
8181
- {{ $resource.Kind | lower }} {{ $resource.Name | lower }}
8282
{{ else -}}
@@ -85,7 +85,7 @@ Objects:
8585
{{ end -}}
8686
8787
Images:
88-
{{ range .Updated.Images -}}
88+
{{ range .Changed.ImageResult.Images -}}
8989
- {{.}} ({{.Policy.Name}})
9090
{{ end -}}
9191
`
@@ -841,6 +841,97 @@ Automation: %s/update-test
841841
}
842842
}
843843

844+
// TestImageUpdateAutomationReconciler_removedTemplateField tests removed .Updated template field usage.
845+
func TestImageUpdateAutomationReconciler_removedTemplateField(t *testing.T) {
846+
g := NewWithT(t)
847+
ctx := context.TODO()
848+
849+
policySpec := imagev1_reflect.ImagePolicySpec{
850+
ImageRepositoryRef: meta.NamespacedObjectReference{
851+
Name: "not-expected-to-exist",
852+
},
853+
Policy: imagev1_reflect.ImagePolicyChoice{
854+
SemVer: &imagev1_reflect.SemVerPolicy{
855+
Range: "1.x",
856+
},
857+
},
858+
}
859+
fixture := "testdata/appconfig"
860+
latest := "helloworld:v1.0.0"
861+
862+
removedTemplate := `Commit summary
863+
864+
Automation: {{ .AutomationObject }}
865+
866+
Files:
867+
{{ range $filename, $_ := .Updated.Files -}}
868+
- {{ $filename }}
869+
{{ end -}}
870+
871+
Objects:
872+
{{ range $resource, $_ := .Updated.Objects -}}
873+
{{ if eq $resource.Kind "Deployment" -}}
874+
- {{ $resource.Kind | lower }} {{ $resource.Name | lower }}
875+
{{ else -}}
876+
- {{ $resource.Kind }} {{ $resource.Name }}
877+
{{ end -}}
878+
{{ end -}}
879+
880+
Images:
881+
{{ range .Updated.Images -}}
882+
- {{.}} ({{.Policy.Name}})
883+
{{ end -}}
884+
`
885+
886+
namespace, err := testEnv.CreateNamespace(ctx, "image-auto-test")
887+
g.Expect(err).ToNot(HaveOccurred())
888+
defer func() { g.Expect(testEnv.Delete(ctx, namespace)).To(Succeed()) }()
889+
890+
testWithRepoAndImagePolicy(
891+
ctx, g, testEnv, namespace.Name, fixture, policySpec, latest,
892+
func(g *WithT, s repoAndPolicyArgs, repoURL string, localRepo *extgogit.Repository) {
893+
policyKey := types.NamespacedName{
894+
Name: s.imagePolicyName,
895+
Namespace: s.namespace,
896+
}
897+
_ = testutil.CommitInRepo(ctx, g, repoURL, s.branch, originRemote, "Install setter marker", func(tmp string) {
898+
g.Expect(testutil.ReplaceMarker(filepath.Join(tmp, "deploy.yaml"), policyKey)).To(Succeed())
899+
})
900+
901+
preChangeCommitId := testutil.CommitIdFromBranch(localRepo, s.branch)
902+
waitForNewHead(g, localRepo, s.branch, preChangeCommitId)
903+
preChangeCommitId = testutil.CommitIdFromBranch(localRepo, s.branch)
904+
905+
updateStrategy := &imagev1.UpdateStrategy{
906+
Strategy: imagev1.UpdateStrategySetters,
907+
}
908+
err := createImageUpdateAutomation(ctx, testEnv, "update-test", s.namespace, s.gitRepoName, s.gitRepoNamespace, s.branch, s.branch, "", removedTemplate, "", updateStrategy)
909+
g.Expect(err).ToNot(HaveOccurred())
910+
defer func() {
911+
g.Expect(deleteImageUpdateAutomation(ctx, testEnv, "update-test", s.namespace)).To(Succeed())
912+
}()
913+
914+
imageUpdateKey := types.NamespacedName{
915+
Namespace: s.namespace,
916+
Name: "update-test",
917+
}
918+
919+
g.Eventually(func() bool {
920+
var imageUpdate imagev1.ImageUpdateAutomation
921+
_ = testEnv.Get(context.TODO(), imageUpdateKey, &imageUpdate)
922+
stalledCondition := apimeta.FindStatusCondition(imageUpdate.Status.Conditions, meta.StalledCondition)
923+
return stalledCondition != nil &&
924+
stalledCondition.Status == metav1.ConditionTrue &&
925+
stalledCondition.Reason == imagev1.RemovedTemplateFieldReason &&
926+
strings.Contains(stalledCondition.Message, "template uses removed '.Updated' field")
927+
}, timeout).Should(BeTrue())
928+
929+
head, _ := localRepo.Head()
930+
g.Expect(head.Hash().String()).To(Equal(preChangeCommitId))
931+
},
932+
)
933+
}
934+
844935
func TestImageUpdateAutomationReconciler_crossNamespaceRef(t *testing.T) {
845936
g := NewWithT(t)
846937
policySpec := imagev1_reflect.ImagePolicySpec{

internal/source/source.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,15 @@ import (
4848
// ErrInvalidSourceConfiguration is an error for invalid source configuration.
4949
var ErrInvalidSourceConfiguration = errors.New("invalid source configuration")
5050

51+
// ErrRemovedTemplateField is an error for removed template field usage.
52+
var ErrRemovedTemplateField = errors.New("template uses removed '.Updated' field. Please use '.Changed' instead. See: https://fluxcd.io/flux/components/image/imageupdateautomations/#message-template")
53+
5154
const defaultMessageTemplate = `Update from image update automation`
5255

5356
// TemplateData is the type of the value given to the commit message
5457
// template.
5558
type TemplateData struct {
5659
AutomationObject types.NamespacedName
57-
Updated update.Result
5860
Changed update.ResultV2
5961
Values map[string]string
6062
}
@@ -279,7 +281,6 @@ func (sm SourceManager) CommitAndPush(ctx context.Context, obj *imagev1.ImageUpd
279281
// Perform a Git commit.
280282
templateValues := &TemplateData{
281283
AutomationObject: sm.automationObjKey,
282-
Updated: policyResult.ImageResult,
283284
Changed: policyResult,
284285
Values: obj.Spec.GitSpec.Commit.MessageTemplateValues,
285286
}
@@ -356,6 +357,9 @@ func templateMsg(messageTemplate string, templateValues *TemplateData) (string,
356357

357358
b := &strings.Builder{}
358359
if err := t.Execute(b, *templateValues); err != nil {
360+
if strings.Contains(err.Error(), "can't evaluate field Updated in type source.TemplateData") {
361+
return "", ErrRemovedTemplateField
362+
}
359363
return "", fmt.Errorf("failed to run template from spec: %w", err)
360364
}
361365
return b.String(), nil

internal/source/source_test.go

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ import (
5555
)
5656

5757
const (
58-
originRemote = "origin"
59-
testCommitTemplate = `Commit summary
58+
originRemote = "origin"
59+
testCommitTemplateRemoved = `Commit summary
6060
6161
Automation: {{ .AutomationObject }}
6262
@@ -469,31 +469,20 @@ func test_sourceManager_CommitAndPush(t *testing.T, proto string) {
469469
checkRefSpecBranch string
470470
}{
471471
{
472-
name: "push to cloned branch with custom template",
472+
name: "push to cloned branch with removed template field",
473473
gitSpec: &imagev1.GitSpec{
474474
Push: &imagev1.PushSpec{
475475
Branch: "main",
476476
},
477477
Commit: imagev1.CommitSpec{
478-
MessageTemplate: testCommitTemplate,
478+
MessageTemplate: testCommitTemplateRemoved,
479479
},
480480
},
481481
gitRepoReference: &sourcev1.GitRepositoryRef{
482482
Branch: "main",
483483
},
484484
latestImage: "helloworld:1.0.1",
485-
wantErr: false,
486-
wantCommitMsg: `Commit summary
487-
488-
Automation: test-ns/test-update
489-
490-
Files:
491-
- deploy.yaml
492-
Objects:
493-
- deployment test
494-
Images:
495-
- helloworld:1.0.1 (policy1)
496-
`,
485+
wantErr: true,
497486
},
498487
{
499488
name: "commit with update ResultV2 template",
@@ -771,6 +760,11 @@ Testing: value
771760

772761
pushResult, err := sm.CommitAndPush(ctx, updateAuto, result)
773762
g.Expect(err != nil).To(Equal(tt.wantErr))
763+
if tt.wantErr {
764+
g.Expect(pushResult).To(BeNil())
765+
g.Expect(err).To(MatchError(ErrRemovedTemplateField))
766+
return
767+
}
774768
if tt.noChange {
775769
g.Expect(pushResult).To(BeNil())
776770
return

0 commit comments

Comments
 (0)