From d55d185044b2afb81f2e4941d68ac1b44f8c95c0 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 8 Dec 2020 11:26:31 +0000 Subject: [PATCH] Implement suspend, resume, reconcile image-update .. and refactor. These are all amenable to the adapter refactoring that has served well so far. Signed-off-by: Michael Bridgen --- cmd/flux/reconcile.go | 111 +++++++++++++++++++ cmd/flux/reconcile_auto_imagerepository.go | 92 ++------------- cmd/flux/reconcile_auto_imageupdateauto.go | 62 +++++++++++ cmd/flux/resume.go | 60 ++++++++++ cmd/flux/resume_auto_imagerepository.go | 83 ++------------ cmd/flux/resume_auto_imageupdateauto.go | 49 ++++++++ cmd/flux/status.go | 67 +++++++++++ cmd/flux/suspend.go | 50 +++++++++ cmd/flux/suspend_auto_imagerepository.go | 46 ++------ cmd/flux/suspend_auto_imageupdateauto.go | 48 ++++++++ docs/cmd/flux_reconcile_auto.md | 1 + docs/cmd/flux_reconcile_auto_image-update.md | 40 +++++++ docs/cmd/flux_resume_auto.md | 1 + docs/cmd/flux_resume_auto_image-update.md | 40 +++++++ docs/cmd/flux_suspend_auto.md | 1 + docs/cmd/flux_suspend_auto_image-update.md | 40 +++++++ 16 files changed, 596 insertions(+), 195 deletions(-) create mode 100644 cmd/flux/reconcile_auto_imageupdateauto.go create mode 100644 cmd/flux/resume_auto_imageupdateauto.go create mode 100644 cmd/flux/status.go create mode 100644 cmd/flux/suspend_auto_imageupdateauto.go create mode 100644 docs/cmd/flux_reconcile_auto_image-update.md create mode 100644 docs/cmd/flux_resume_auto_image-update.md create mode 100644 docs/cmd/flux_suspend_auto_image-update.md diff --git a/cmd/flux/reconcile.go b/cmd/flux/reconcile.go index e718c8a2..e103cbcb 100644 --- a/cmd/flux/reconcile.go +++ b/cmd/flux/reconcile.go @@ -17,7 +17,20 @@ limitations under the License. package main import ( + "context" + "fmt" + "time" + + "github.com/fluxcd/pkg/apis/meta" "github.com/spf13/cobra" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/util/retry" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/fluxcd/flux2/internal/utils" ) var reconcileCmd = &cobra.Command{ @@ -29,3 +42,101 @@ var reconcileCmd = &cobra.Command{ func init() { rootCmd.AddCommand(reconcileCmd) } + +type reconcileCommand struct { + humanKind string + adapter reconcilable +} + +type reconcilable interface { + adapter // to be able to load from the cluster + suspendable // to tell if it's suspended + + // these are implemented by anything embedding metav1.ObjectMeta + GetAnnotations() map[string]string + SetAnnotations(map[string]string) + + // this is usually implemented by GOTK types, since it's used for meta.SetResourceCondition + GetStatusConditions() *[]metav1.Condition + + lastHandledReconcileRequest() string // what was the last handled reconcile request? + successMessage() string // what do you want to tell people when successfully reconciled? +} + +func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return fmt.Errorf("%s name is required", reconcile.humanKind) + } + name := args[0] + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) + if err != nil { + return err + } + + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + + err = kubeClient.Get(ctx, namespacedName, reconcile.adapter.asRuntimeObject()) + if err != nil { + return err + } + + if reconcile.adapter.isSuspended() { + return fmt.Errorf("resource is suspended") + } + + logger.Actionf("annotating %s %s in %s namespace", reconcile.humanKind, name, namespace) + if err := requestReconciliation(ctx, kubeClient, namespacedName, reconcile.adapter); err != nil { + return err + } + logger.Successf("%s annotated", reconcile.humanKind) + + lastHandledReconcileAt := reconcile.adapter.lastHandledReconcileRequest() + logger.Waitingf("waiting for %s reconciliation", reconcile.humanKind) + if err := wait.PollImmediate(pollInterval, timeout, + reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.adapter, lastHandledReconcileAt)); err != nil { + return err + } + logger.Successf("%s reconciliation completed", reconcile.humanKind) + + if apimeta.IsStatusConditionFalse(*reconcile.adapter.GetStatusConditions(), meta.ReadyCondition) { + return fmt.Errorf("%s reconciliation failed", reconcile.humanKind) + } + logger.Successf(reconcile.adapter.successMessage()) + return nil +} + +func reconciliationHandled(ctx context.Context, kubeClient client.Client, + namespacedName types.NamespacedName, obj reconcilable, lastHandledReconcileAt string) wait.ConditionFunc { + return func() (bool, error) { + err := kubeClient.Get(ctx, namespacedName, obj.asRuntimeObject()) + if err != nil { + return false, err + } + return obj.lastHandledReconcileRequest() != lastHandledReconcileAt, nil + } +} + +func requestReconciliation(ctx context.Context, kubeClient client.Client, + namespacedName types.NamespacedName, obj reconcilable) error { + return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { + if err := kubeClient.Get(ctx, namespacedName, obj.asRuntimeObject()); err != nil { + return err + } + if ann := obj.GetAnnotations(); ann == nil { + obj.SetAnnotations(map[string]string{ + meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), + }) + } else { + ann[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) + obj.SetAnnotations(ann) + } + return kubeClient.Update(ctx, obj.asRuntimeObject()) + }) +} diff --git a/cmd/flux/reconcile_auto_imagerepository.go b/cmd/flux/reconcile_auto_imagerepository.go index 7cfa80cc..b487e68a 100644 --- a/cmd/flux/reconcile_auto_imagerepository.go +++ b/cmd/flux/reconcile_auto_imagerepository.go @@ -17,19 +17,9 @@ limitations under the License. package main import ( - "context" "fmt" - "time" - - "github.com/fluxcd/flux2/internal/utils" - "github.com/fluxcd/pkg/apis/meta" - apimeta "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/client-go/util/retry" - "sigs.k8s.io/controller-runtime/pkg/client" "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1" ) @@ -41,86 +31,20 @@ var reconcileImageRepositoryCmd = &cobra.Command{ Example: ` # Trigger an scan for an existing image repository flux reconcile auto image-repository alpine `, - RunE: reconcileImageRepositoryRun, + RunE: reconcileCommand{ + humanKind: imagev1.ImageRepositoryKind, + adapter: imageRepositoryAdapter{&imagev1.ImageRepository{}}, + }.run, } func init() { reconcileAutoCmd.AddCommand(reconcileImageRepositoryCmd) } -func reconcileImageRepositoryRun(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("source name is required") - } - name := args[0] - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) - if err != nil { - return err - } - - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: name, - } - var repo imagev1.ImageRepository - err = kubeClient.Get(ctx, namespacedName, &repo) - if err != nil { - return err - } - - if repo.Spec.Suspend { - return fmt.Errorf("resource is suspended") - } - - logger.Actionf("annotating ImageRepository %s in %s namespace", name, namespace) - if err := requestImageRepositoryReconciliation(ctx, kubeClient, namespacedName, &repo); err != nil { - return err - } - logger.Successf("ImageRepository annotated") - - lastHandledReconcileAt := repo.Status.LastHandledReconcileAt - logger.Waitingf("waiting for ImageRepository reconciliation") - if err := wait.PollImmediate(pollInterval, timeout, - imageRepositoryReconciliationHandled(ctx, kubeClient, namespacedName, &repo, lastHandledReconcileAt)); err != nil { - return err - } - logger.Successf("ImageRepository reconciliation completed") - - if apimeta.IsStatusConditionFalse(repo.Status.Conditions, meta.ReadyCondition) { - return fmt.Errorf("ImageRepository reconciliation failed") - } - logger.Successf("scan fetched %d tags", repo.Status.LastScanResult.TagCount) - return nil -} - -func imageRepositoryReconciliationHandled(ctx context.Context, kubeClient client.Client, - namespacedName types.NamespacedName, repo *imagev1.ImageRepository, lastHandledReconcileAt string) wait.ConditionFunc { - return func() (bool, error) { - err := kubeClient.Get(ctx, namespacedName, repo) - if err != nil { - return false, err - } - return repo.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil - } +func (obj imageRepositoryAdapter) lastHandledReconcileRequest() string { + return obj.Status.GetLastHandledReconcileRequest() } -func requestImageRepositoryReconciliation(ctx context.Context, kubeClient client.Client, - namespacedName types.NamespacedName, repo *imagev1.ImageRepository) error { - return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { - if err := kubeClient.Get(ctx, namespacedName, repo); err != nil { - return err - } - if repo.Annotations == nil { - repo.Annotations = map[string]string{ - meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), - } - } else { - repo.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) - } - return kubeClient.Update(ctx, repo) - }) +func (obj imageRepositoryAdapter) successMessage() string { + return fmt.Sprintf("scan fetched %d tags", obj.Status.LastScanResult.TagCount) } diff --git a/cmd/flux/reconcile_auto_imageupdateauto.go b/cmd/flux/reconcile_auto_imageupdateauto.go new file mode 100644 index 00000000..4cc3ab62 --- /dev/null +++ b/cmd/flux/reconcile_auto_imageupdateauto.go @@ -0,0 +1,62 @@ +/* +Copyright 2020 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 ( + "time" + + "github.com/spf13/cobra" + apimeta "k8s.io/apimachinery/pkg/api/meta" + + autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1" + meta "github.com/fluxcd/pkg/apis/meta" +) + +var reconcileImageUpdateCmd = &cobra.Command{ + Use: "image-update [name]", + Short: "Reconcile an ImageUpdateAutomation", + Long: `The reconcile auto image-update command triggers a reconciliation of an ImageUpdateAutomation resource and waits for it to finish.`, + Example: ` # Trigger an automation run for an existing image update automation + flux reconcile auto image-update latest-images +`, + RunE: reconcileCommand{ + humanKind: autov1.ImageUpdateAutomationKind, + adapter: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}}, + }.run, +} + +func init() { + reconcileAutoCmd.AddCommand(reconcileImageUpdateCmd) +} + +func (obj imageUpdateAutomationAdapter) suspended() bool { + return obj.ImageUpdateAutomation.Spec.Suspend +} + +func (obj imageUpdateAutomationAdapter) lastHandledReconcileRequest() string { + return obj.Status.GetLastHandledReconcileRequest() +} + +func (obj imageUpdateAutomationAdapter) successMessage() string { + if rc := apimeta.FindStatusCondition(obj.Status.Conditions, meta.ReadyCondition); rc != nil { + return rc.Message + } + if obj.Status.LastAutomationRunTime != nil { + return "last run " + obj.Status.LastAutomationRunTime.Time.Format(time.RFC3339) + } + return "automation not yet run" +} diff --git a/cmd/flux/resume.go b/cmd/flux/resume.go index 9d9c8421..8fd53674 100644 --- a/cmd/flux/resume.go +++ b/cmd/flux/resume.go @@ -17,7 +17,14 @@ limitations under the License. package main import ( + "context" + "fmt" + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + + "github.com/fluxcd/flux2/internal/utils" ) var resumeCmd = &cobra.Command{ @@ -29,3 +36,56 @@ var resumeCmd = &cobra.Command{ func init() { rootCmd.AddCommand(resumeCmd) } + +type resumable interface { + adapter + statusable + setUnsuspended() +} + +type resumeCommand struct { + kind string + humanKind string + object resumable +} + +func (resume resumeCommand) run(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return fmt.Errorf("%s name is required", resume.humanKind) + } + name := args[0] + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) + if err != nil { + return err + } + + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + + err = kubeClient.Get(ctx, namespacedName, resume.object.asRuntimeObject()) + if err != nil { + return err + } + + logger.Actionf("resuming %s %s in %s namespace", resume.humanKind, name, namespace) + resume.object.setUnsuspended() + if err := kubeClient.Update(ctx, resume.object.asRuntimeObject()); err != nil { + return err + } + logger.Successf("%s resumed", resume.humanKind) + + logger.Waitingf("waiting for %s reconciliation", resume.kind) + if err := wait.PollImmediate(pollInterval, timeout, + isReady(ctx, kubeClient, namespacedName, resume.object)); err != nil { + return err + } + logger.Successf("%s reconciliation completed", resume.kind) + logger.Successf(resume.object.successMessage()) + return nil +} diff --git a/cmd/flux/resume_auto_imagerepository.go b/cmd/flux/resume_auto_imagerepository.go index 7b5d15a9..c8be1271 100644 --- a/cmd/flux/resume_auto_imagerepository.go +++ b/cmd/flux/resume_auto_imagerepository.go @@ -17,18 +17,7 @@ limitations under the License. package main import ( - "context" - "fmt" - - "github.com/fluxcd/flux2/internal/utils" - "github.com/fluxcd/pkg/apis/meta" - "github.com/spf13/cobra" - apimeta "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/wait" - "sigs.k8s.io/controller-runtime/pkg/client" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1" ) @@ -40,75 +29,21 @@ var resumeImageRepositoryCmd = &cobra.Command{ Example: ` # Resume reconciliation for an existing ImageRepository flux resume auto image-repository alpine `, - RunE: resumeImageRepositoryRun, + RunE: resumeCommand{ + kind: imagev1.ImageRepositoryKind, + humanKind: "image repository", + object: imageRepositoryAdapter{&imagev1.ImageRepository{}}, + }.run, } func init() { resumeAutoCmd.AddCommand(resumeImageRepositoryCmd) } -func resumeImageRepositoryRun(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("source name is required") - } - name := args[0] - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) - if err != nil { - return err - } - - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: name, - } - var repo imagev1.ImageRepository - err = kubeClient.Get(ctx, namespacedName, &repo) - if err != nil { - return err - } - - logger.Actionf("resuming image repository %s in %s namespace", name, namespace) - repo.Spec.Suspend = false - if err := kubeClient.Update(ctx, &repo); err != nil { - return err - } - logger.Successf("image repository resumed") - - logger.Waitingf("waiting for ImageRepository reconciliation") - if err := wait.PollImmediate(pollInterval, timeout, - isImageRepositoryResumed(ctx, kubeClient, namespacedName, &repo)); err != nil { - return err - } - logger.Successf("ImageRepository reconciliation completed") - logger.Successf("scanned %d tags", repo.Status.LastScanResult.TagCount) - return nil +func (obj imageRepositoryAdapter) getObservedGeneration() int64 { + return obj.ImageRepository.Status.ObservedGeneration } -func isImageRepositoryResumed(ctx context.Context, kubeClient client.Client, - namespacedName types.NamespacedName, repo *imagev1.ImageRepository) wait.ConditionFunc { - return func() (bool, error) { - err := kubeClient.Get(ctx, namespacedName, repo) - if err != nil { - return false, err - } - - // Confirm the state we are observing is for the current generation - if repo.Generation != repo.Status.ObservedGeneration { - return false, nil - } - - if c := apimeta.FindStatusCondition(repo.Status.Conditions, meta.ReadyCondition); c != nil { - switch c.Status { - case metav1.ConditionTrue: - return true, nil - case metav1.ConditionFalse: - return false, fmt.Errorf(c.Message) - } - } - return false, nil - } +func (obj imageRepositoryAdapter) setUnsuspended() { + obj.ImageRepository.Spec.Suspend = false } diff --git a/cmd/flux/resume_auto_imageupdateauto.go b/cmd/flux/resume_auto_imageupdateauto.go new file mode 100644 index 00000000..8836d74f --- /dev/null +++ b/cmd/flux/resume_auto_imageupdateauto.go @@ -0,0 +1,49 @@ +/* +Copyright 2020 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 ( + "github.com/spf13/cobra" + + autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1" +) + +var resumeImageUpdateCmd = &cobra.Command{ + Use: "image-update [name]", + Short: "Resume a suspended ImageUpdateAutomation", + Long: `The resume command marks a previously suspended ImageUpdateAutomation resource for reconciliation and waits for it to finish.`, + Example: ` # Resume reconciliation for an existing ImageUpdateAutomation + flux resume auto image-update latest-images +`, + RunE: resumeCommand{ + kind: autov1.ImageUpdateAutomationKind, + humanKind: "image update automation", + object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}}, + }.run, +} + +func init() { + resumeAutoCmd.AddCommand(resumeImageUpdateCmd) +} + +func (obj imageUpdateAutomationAdapter) setUnsuspended() { + obj.ImageUpdateAutomation.Spec.Suspend = false +} + +func (obj imageUpdateAutomationAdapter) getObservedGeneration() int64 { + return obj.ImageUpdateAutomation.Status.ObservedGeneration +} diff --git a/cmd/flux/status.go b/cmd/flux/status.go new file mode 100644 index 00000000..2c2a2352 --- /dev/null +++ b/cmd/flux/status.go @@ -0,0 +1,67 @@ +/* +Copyright 2020 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 ( + "context" + "fmt" + + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/fluxcd/pkg/apis/meta" +) + +// statusable is used to see if a resource is considered ready in the usual way +type statusable interface { + adapter + // this is implemented by ObjectMeta + GetGeneration() int64 + getObservedGeneration() int64 + // this is usually implemented by GOTK API objects because it's used by pkg/apis/meta + GetStatusConditions() *[]metav1.Condition + // successMessage gives a short summary of the successful reconciliation + successMessage() string +} + +func isReady(ctx context.Context, kubeClient client.Client, + namespacedName types.NamespacedName, object statusable) wait.ConditionFunc { + return func() (bool, error) { + err := kubeClient.Get(ctx, namespacedName, object.asRuntimeObject()) + if err != nil { + return false, err + } + + // Confirm the state we are observing is for the current generation + if object.GetGeneration() != object.getObservedGeneration() { + return false, nil + } + + if c := apimeta.FindStatusCondition(*object.GetStatusConditions(), meta.ReadyCondition); c != nil { + switch c.Status { + case metav1.ConditionTrue: + return true, nil + case metav1.ConditionFalse: + return false, fmt.Errorf(c.Message) + } + } + return false, nil + } +} diff --git a/cmd/flux/suspend.go b/cmd/flux/suspend.go index 389bfcdb..e9d46b24 100644 --- a/cmd/flux/suspend.go +++ b/cmd/flux/suspend.go @@ -17,7 +17,13 @@ limitations under the License. package main import ( + "context" + "fmt" + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/types" + + "github.com/fluxcd/flux2/internal/utils" ) var suspendCmd = &cobra.Command{ @@ -29,3 +35,47 @@ var suspendCmd = &cobra.Command{ func init() { rootCmd.AddCommand(suspendCmd) } + +type suspendable interface { + adapter + isSuspended() bool + setSuspended() +} + +type suspendCommand struct { + object suspendable + humanKind string +} + +func (suspend suspendCommand) run(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return fmt.Errorf("%s name is required", suspend.humanKind) + } + name := args[0] + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) + if err != nil { + return err + } + + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + err = kubeClient.Get(ctx, namespacedName, suspend.object.asRuntimeObject()) + if err != nil { + return err + } + + logger.Actionf("suspending %s %s in %s namespace", suspend.humanKind, name, namespace) + suspend.object.setSuspended() + if err := kubeClient.Update(ctx, suspend.object.asRuntimeObject()); err != nil { + return err + } + logger.Successf("%s suspended", suspend.humanKind) + + return nil +} diff --git a/cmd/flux/suspend_auto_imagerepository.go b/cmd/flux/suspend_auto_imagerepository.go index 450dd6a6..c5a45830 100644 --- a/cmd/flux/suspend_auto_imagerepository.go +++ b/cmd/flux/suspend_auto_imagerepository.go @@ -17,13 +17,8 @@ limitations under the License. package main import ( - "context" - "fmt" - "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/types" - "github.com/fluxcd/flux2/internal/utils" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1" ) @@ -34,43 +29,20 @@ var suspendImageRepositoryCmd = &cobra.Command{ Example: ` # Suspend reconciliation for an existing ImageRepository flux suspend auto image-repository alpine `, - RunE: suspendImageRepositoryRun, + RunE: suspendCommand{ + humanKind: "image repository", + object: imageRepositoryAdapter{&imagev1.ImageRepository{}}, + }.run, } func init() { suspendAutoCmd.AddCommand(suspendImageRepositoryCmd) } -func suspendImageRepositoryRun(cmd *cobra.Command, args []string) error { - if len(args) < 1 { - return fmt.Errorf("image repository name is required") - } - name := args[0] - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) - if err != nil { - return err - } - - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: name, - } - var repository imagev1.ImageRepository - err = kubeClient.Get(ctx, namespacedName, &repository) - if err != nil { - return err - } - - logger.Actionf("suspending image repository %s in %s namespace", name, namespace) - repository.Spec.Suspend = true - if err := kubeClient.Update(ctx, &repository); err != nil { - return err - } - logger.Successf("image repository suspended") +func (obj imageRepositoryAdapter) isSuspended() bool { + return obj.ImageRepository.Spec.Suspend +} - return nil +func (obj imageRepositoryAdapter) setSuspended() { + obj.ImageRepository.Spec.Suspend = true } diff --git a/cmd/flux/suspend_auto_imageupdateauto.go b/cmd/flux/suspend_auto_imageupdateauto.go new file mode 100644 index 00000000..474227d7 --- /dev/null +++ b/cmd/flux/suspend_auto_imageupdateauto.go @@ -0,0 +1,48 @@ +/* +Copyright 2020 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 ( + "github.com/spf13/cobra" + + autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1" +) + +var suspendImageUpdateCmd = &cobra.Command{ + Use: "image-update [name]", + Short: "Suspend reconciliation of an ImageUpdateAutomation", + Long: "The suspend command disables the reconciliation of a ImageUpdateAutomation resource.", + Example: ` # Suspend reconciliation for an existing ImageUpdateAutomation + flux suspend auto image-update latest-images +`, + RunE: suspendCommand{ + humanKind: "image update automation", + object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}}, + }.run, +} + +func init() { + suspendAutoCmd.AddCommand(suspendImageUpdateCmd) +} + +func (update imageUpdateAutomationAdapter) isSuspended() bool { + return update.ImageUpdateAutomation.Spec.Suspend +} + +func (update imageUpdateAutomationAdapter) setSuspended() { + update.ImageUpdateAutomation.Spec.Suspend = true +} diff --git a/docs/cmd/flux_reconcile_auto.md b/docs/cmd/flux_reconcile_auto.md index b474f46b..ff844092 100644 --- a/docs/cmd/flux_reconcile_auto.md +++ b/docs/cmd/flux_reconcile_auto.md @@ -26,4 +26,5 @@ The reconcile auto sub-commands trigger a reconciliation of automation objects. * [flux reconcile](flux_reconcile.md) - Reconcile sources and resources * [flux reconcile auto image-repository](flux_reconcile_auto_image-repository.md) - Reconcile an ImageRepository +* [flux reconcile auto image-update](flux_reconcile_auto_image-update.md) - Reconcile an ImageUpdateAutomation diff --git a/docs/cmd/flux_reconcile_auto_image-update.md b/docs/cmd/flux_reconcile_auto_image-update.md new file mode 100644 index 00000000..424105ee --- /dev/null +++ b/docs/cmd/flux_reconcile_auto_image-update.md @@ -0,0 +1,40 @@ +## flux reconcile auto image-update + +Reconcile an ImageUpdateAutomation + +### Synopsis + +The reconcile auto image-update command triggers a reconciliation of an ImageUpdateAutomation resource and waits for it to finish. + +``` +flux reconcile auto image-update [name] [flags] +``` + +### Examples + +``` + # Trigger an automation run for an existing image update automation + flux reconcile auto image-update latest-images + +``` + +### Options + +``` + -h, --help help for image-update +``` + +### Options inherited from parent commands + +``` + --context string kubernetes context to use + --kubeconfig string path to the kubeconfig file (default "~/.kube/config") + -n, --namespace string the namespace scope for this operation (default "flux-system") + --timeout duration timeout for this operation (default 5m0s) + --verbose print generated objects +``` + +### SEE ALSO + +* [flux reconcile auto](flux_reconcile_auto.md) - Reconcile automation objects + diff --git a/docs/cmd/flux_resume_auto.md b/docs/cmd/flux_resume_auto.md index 3568e560..f46bb944 100644 --- a/docs/cmd/flux_resume_auto.md +++ b/docs/cmd/flux_resume_auto.md @@ -26,4 +26,5 @@ The resume auto sub-commands resume a suspended automation object. * [flux resume](flux_resume.md) - Resume suspended resources * [flux resume auto image-repository](flux_resume_auto_image-repository.md) - Resume a suspended ImageRepository +* [flux resume auto image-update](flux_resume_auto_image-update.md) - Resume a suspended ImageUpdateAutomation diff --git a/docs/cmd/flux_resume_auto_image-update.md b/docs/cmd/flux_resume_auto_image-update.md new file mode 100644 index 00000000..01196ba6 --- /dev/null +++ b/docs/cmd/flux_resume_auto_image-update.md @@ -0,0 +1,40 @@ +## flux resume auto image-update + +Resume a suspended ImageUpdateAutomation + +### Synopsis + +The resume command marks a previously suspended ImageUpdateAutomation resource for reconciliation and waits for it to finish. + +``` +flux resume auto image-update [name] [flags] +``` + +### Examples + +``` + # Resume reconciliation for an existing ImageUpdateAutomation + flux resume auto image-update latest-images + +``` + +### Options + +``` + -h, --help help for image-update +``` + +### Options inherited from parent commands + +``` + --context string kubernetes context to use + --kubeconfig string path to the kubeconfig file (default "~/.kube/config") + -n, --namespace string the namespace scope for this operation (default "flux-system") + --timeout duration timeout for this operation (default 5m0s) + --verbose print generated objects +``` + +### SEE ALSO + +* [flux resume auto](flux_resume_auto.md) - Resume automation objects + diff --git a/docs/cmd/flux_suspend_auto.md b/docs/cmd/flux_suspend_auto.md index ee57cea0..a0484e7c 100644 --- a/docs/cmd/flux_suspend_auto.md +++ b/docs/cmd/flux_suspend_auto.md @@ -26,4 +26,5 @@ The suspend auto sub-commands suspend the reconciliation of an automation object * [flux suspend](flux_suspend.md) - Suspend resources * [flux suspend auto image-repository](flux_suspend_auto_image-repository.md) - Suspend reconciliation of an ImageRepository +* [flux suspend auto image-update](flux_suspend_auto_image-update.md) - Suspend reconciliation of an ImageUpdateAutomation diff --git a/docs/cmd/flux_suspend_auto_image-update.md b/docs/cmd/flux_suspend_auto_image-update.md new file mode 100644 index 00000000..2638d61b --- /dev/null +++ b/docs/cmd/flux_suspend_auto_image-update.md @@ -0,0 +1,40 @@ +## flux suspend auto image-update + +Suspend reconciliation of an ImageUpdateAutomation + +### Synopsis + +The suspend command disables the reconciliation of a ImageUpdateAutomation resource. + +``` +flux suspend auto image-update [name] [flags] +``` + +### Examples + +``` + # Suspend reconciliation for an existing ImageUpdateAutomation + flux suspend auto image-update latest-images + +``` + +### Options + +``` + -h, --help help for image-update +``` + +### Options inherited from parent commands + +``` + --context string kubernetes context to use + --kubeconfig string path to the kubeconfig file (default "~/.kube/config") + -n, --namespace string the namespace scope for this operation (default "flux-system") + --timeout duration timeout for this operation (default 5m0s) + --verbose print generated objects +``` + +### SEE ALSO + +* [flux suspend auto](flux_suspend_auto.md) - Suspend automation objects +