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 <michael@weave.works>
pull/538/head
Michael Bridgen 4 years ago
parent 2bb09697ce
commit d55d185044

@ -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())
})
}

@ -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")
func (obj imageRepositoryAdapter) lastHandledReconcileRequest() string {
return obj.Status.GetLastHandledReconcileRequest()
}
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 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)
}

@ -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"
}

@ -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
}

@ -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
func (obj imageRepositoryAdapter) getObservedGeneration() int64 {
return obj.ImageRepository.Status.ObservedGeneration
}
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 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
}

@ -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
}

@ -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
}
}

@ -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
}

@ -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
func (obj imageRepositoryAdapter) isSuspended() bool {
return obj.ImageRepository.Spec.Suspend
}
logger.Successf("image repository suspended")
return nil
func (obj imageRepositoryAdapter) setSuspended() {
obj.ImageRepository.Spec.Suspend = true
}

@ -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
}

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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
Loading…
Cancel
Save