From 48d509d8388944da8a7c026240cccc35909cc763 Mon Sep 17 00:00:00 2001 From: lukas8219 Date: Tue, 26 Aug 2025 09:10:29 -0300 Subject: [PATCH] Implement flux [reconcile|suspend|resume] image policy commands Signed-off-by: lukas8219 --- cmd/flux/create_image_policy.go | 6 --- cmd/flux/image.go | 42 +++++++++++++++++++ cmd/flux/image_test.go | 12 ++++++ cmd/flux/reconcile_image_policy.go | 40 ++++++++++++++++++ cmd/flux/resume_image_policy.go | 40 ++++++++++++++++++ cmd/flux/suspend_image_policy.go | 37 ++++++++++++++++ .../image/reconcile_image_policy.golden | 4 ++ .../testdata/image/resume_image_policy.golden | 5 +++ .../image/suspend_image_policy.golden | 2 + 9 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 cmd/flux/reconcile_image_policy.go create mode 100644 cmd/flux/resume_image_policy.go create mode 100644 cmd/flux/suspend_image_policy.go create mode 100644 cmd/flux/testdata/image/reconcile_image_policy.golden create mode 100644 cmd/flux/testdata/image/resume_image_policy.golden create mode 100644 cmd/flux/testdata/image/suspend_image_policy.golden diff --git a/cmd/flux/create_image_policy.go b/cmd/flux/create_image_policy.go index 20ce1cf6..72d52163 100644 --- a/cmd/flux/create_image_policy.go +++ b/cmd/flux/create_image_policy.go @@ -81,12 +81,6 @@ func init() { createImageCmd.AddCommand(createImagePolicyCmd) } -// getObservedGeneration is implemented here, since it's not -// (presently) needed elsewhere. -func (obj imagePolicyAdapter) getObservedGeneration() int64 { - return obj.ImagePolicy.Status.ObservedGeneration -} - func createImagePolicyRun(cmd *cobra.Command, args []string) error { objectName := args[0] diff --git a/cmd/flux/image.go b/cmd/flux/image.go index 0b67e5a3..d7559ad5 100644 --- a/cmd/flux/image.go +++ b/cmd/flux/image.go @@ -17,6 +17,8 @@ limitations under the License. package main import ( + "fmt" + "sigs.k8s.io/controller-runtime/pkg/client" autov1 "github.com/fluxcd/image-automation-controller/api/v1" @@ -77,6 +79,34 @@ func (a imagePolicyAdapter) asClientObject() client.Object { return a.ImagePolicy } +func (a imagePolicyAdapter) deepCopyClientObject() client.Object { + return a.ImagePolicy.DeepCopy() +} + +func (a imagePolicyAdapter) isStatic() bool { + return false +} + +func (a imagePolicyAdapter) lastHandledReconcileRequest() string { + return a.Status.GetLastHandledReconcileRequest() +} + +func (a imagePolicyAdapter) isSuspended() bool { + return a.Spec.Suspend +} + +func (a imagePolicyAdapter) setSuspended() { + a.Spec.Suspend = true +} + +func (a imagePolicyAdapter) successMessage() string { + return fmt.Sprintf("selected ref %s", a.Status.LatestRef.String()) +} + +func (a imagePolicyAdapter) setUnsuspended() { + a.Spec.Suspend = false +} + // imagev1.ImagePolicyList type imagePolicyListAdapter struct { @@ -91,6 +121,18 @@ func (a imagePolicyListAdapter) len() int { return len(a.ImagePolicyList.Items) } +func (a imagePolicyListAdapter) resumeItem(i int) resumable { + return &imagePolicyAdapter{&a.ImagePolicyList.Items[i]} +} + +func (obj imagePolicyAdapter) getObservedGeneration() int64 { + return obj.ImagePolicy.Status.ObservedGeneration +} + +func (a imagePolicyListAdapter) item(i int) suspendable { + return &imagePolicyAdapter{&a.ImagePolicyList.Items[i]} +} + // autov1.ImageUpdateAutomation var imageUpdateAutomationType = apiType{ diff --git a/cmd/flux/image_test.go b/cmd/flux/image_test.go index ce13dcb4..81a8acb3 100644 --- a/cmd/flux/image_test.go +++ b/cmd/flux/image_test.go @@ -53,6 +53,18 @@ func TestImageScanning(t *testing.T) { "get image policy podinfo-regex", "testdata/image/get_image_policy_regex.golden", }, + { + "suspend image policy podinfo-semver", + "testdata/image/suspend_image_policy.golden", + }, + { + "resume image policy podinfo-semver", + "testdata/image/resume_image_policy.golden", + }, + { + "reconcile image policy podinfo-semver", + "testdata/image/reconcile_image_policy.golden", + }, } for _, tc := range cases { diff --git a/cmd/flux/reconcile_image_policy.go b/cmd/flux/reconcile_image_policy.go new file mode 100644 index 00000000..33e27730 --- /dev/null +++ b/cmd/flux/reconcile_image_policy.go @@ -0,0 +1,40 @@ +/* +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 main + +import ( + imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" + "github.com/spf13/cobra" +) + +var reconcileImagePolicyCmd = &cobra.Command{ + Use: "policy [name]", + Short: "Reconcile an ImagePolicy", + Long: `The reconcile image policy command triggers a reconciliation of an ImagePolicy resource and waits for it to finish.`, + Example: ` + # Trigger a reconciliation for an existing image policy called 'alpine' + flux reconcile image policy alpine`, + ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)), + RunE: reconcileCommand{ + apiType: imagePolicyType, + object: imagePolicyAdapter{&imagev1.ImagePolicy{}}, + }.run, +} + +func init() { + reconcileImageCmd.AddCommand(reconcileImagePolicyCmd) +} diff --git a/cmd/flux/resume_image_policy.go b/cmd/flux/resume_image_policy.go new file mode 100644 index 00000000..042ff7cd --- /dev/null +++ b/cmd/flux/resume_image_policy.go @@ -0,0 +1,40 @@ +/* +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 main + +import ( + imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" + "github.com/spf13/cobra" +) + +var resumeImagePolicyCmd = &cobra.Command{ + Use: "policy [name]", + Short: "Resume an ImagePolicy", + Long: `The resume image policy command resumes a suspended ImagePolicy resource.`, + Example: ` + # Resume a suspended image policy called 'alpine' + flux resume image policy alpine`, + ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)), + RunE: resumeCommand{ + apiType: imagePolicyType, + list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}}, + }.run, +} + +func init() { + resumeImageCmd.AddCommand(resumeImagePolicyCmd) +} diff --git a/cmd/flux/suspend_image_policy.go b/cmd/flux/suspend_image_policy.go new file mode 100644 index 00000000..c1362411 --- /dev/null +++ b/cmd/flux/suspend_image_policy.go @@ -0,0 +1,37 @@ +/* +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 main + +import ( + imagev1 "github.com/fluxcd/image-reflector-controller/api/v1" + "github.com/spf13/cobra" +) + +var suspendImagePolicyCmd = &cobra.Command{ + Use: "policy [name]", + Short: "Suspend an ImagePolicy", + Long: `The suspend image policy command suspends the reconciliation of an ImagePolicy resource.`, + ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)), + RunE: suspendCommand{ + apiType: imagePolicyType, + list: imagePolicyListAdapter{&imagev1.ImagePolicyList{}}, + }.run, +} + +func init() { + suspendImageCmd.AddCommand(suspendImagePolicyCmd) +} diff --git a/cmd/flux/testdata/image/reconcile_image_policy.golden b/cmd/flux/testdata/image/reconcile_image_policy.golden new file mode 100644 index 00000000..183945b9 --- /dev/null +++ b/cmd/flux/testdata/image/reconcile_image_policy.golden @@ -0,0 +1,4 @@ +► annotating ImagePolicy podinfo-semver in tis-2 namespace +✔ ImagePolicy annotated +◎ waiting for ImagePolicy reconciliation +✔ selected ref ghcr.io/stefanprodan/podinfo:5.0.3@sha256:8704da90172710d422af855049175c1a8295731cbe2ad3b9a1c1074feecf8c10 diff --git a/cmd/flux/testdata/image/resume_image_policy.golden b/cmd/flux/testdata/image/resume_image_policy.golden new file mode 100644 index 00000000..6c7f202b --- /dev/null +++ b/cmd/flux/testdata/image/resume_image_policy.golden @@ -0,0 +1,5 @@ +► resuming image policy podinfo-semver in tis-2 namespace +✔ image policy resumed +◎ waiting for ImagePolicy reconciliation +✔ ImagePolicy podinfo-semver reconciliation completed +✔ selected ref ghcr.io/stefanprodan/podinfo:5.0.3@sha256:8704da90172710d422af855049175c1a8295731cbe2ad3b9a1c1074feecf8c10 diff --git a/cmd/flux/testdata/image/suspend_image_policy.golden b/cmd/flux/testdata/image/suspend_image_policy.golden new file mode 100644 index 00000000..c73305af --- /dev/null +++ b/cmd/flux/testdata/image/suspend_image_policy.golden @@ -0,0 +1,2 @@ +► suspending image policy podinfo-semver in tis-2 namespace +✔ image policy suspended