From 12a2100fcf0970fe27163f6b72c80d945a0f51b7 Mon Sep 17 00:00:00 2001
From: Somtochi Onyekwere <somtochionyekwere@gmail.com>
Date: Tue, 27 Apr 2021 09:03:37 +0100
Subject: [PATCH] Adds suspend and resume all cmd

Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
---
 cmd/flux/resume.go                        | 65 +++++++++++++++++------
 cmd/flux/resume_alert.go                  |  5 ++
 cmd/flux/resume_helmrelease.go            |  5 ++
 cmd/flux/resume_image_repository.go       |  5 ++
 cmd/flux/resume_image_updateauto.go       |  5 ++
 cmd/flux/resume_kustomization.go          |  5 ++
 cmd/flux/resume_receiver.go               |  5 ++
 cmd/flux/resume_source_bucket.go          |  4 ++
 cmd/flux/resume_source_chart.go           |  5 ++
 cmd/flux/resume_source_git.go             |  5 ++
 cmd/flux/resume_source_helm.go            |  5 ++
 cmd/flux/suspend.go                       | 49 ++++++++++++-----
 cmd/flux/suspend_alert.go                 |  5 ++
 cmd/flux/suspend_helmrelease.go           |  5 ++
 cmd/flux/suspend_image_repository.go      |  5 ++
 cmd/flux/suspend_image_updateauto.go      |  5 ++
 cmd/flux/suspend_kustomization.go         |  5 ++
 cmd/flux/suspend_receiver.go              |  5 ++
 cmd/flux/suspend_source_bucket.go         |  5 ++
 cmd/flux/suspend_source_chart.go          |  5 ++
 cmd/flux/suspend_source_git.go            |  5 ++
 cmd/flux/suspend_source_helm.go           |  5 ++
 docs/cmd/flux_resume.md                   |  1 +
 docs/cmd/flux_resume_alert.md             |  1 +
 docs/cmd/flux_resume_helmrelease.md       |  1 +
 docs/cmd/flux_resume_image.md             |  1 +
 docs/cmd/flux_resume_image_repository.md  |  1 +
 docs/cmd/flux_resume_image_update.md      |  1 +
 docs/cmd/flux_resume_kustomization.md     |  1 +
 docs/cmd/flux_resume_receiver.md          |  1 +
 docs/cmd/flux_resume_source.md            |  1 +
 docs/cmd/flux_resume_source_bucket.md     |  1 +
 docs/cmd/flux_resume_source_chart.md      |  1 +
 docs/cmd/flux_resume_source_git.md        |  1 +
 docs/cmd/flux_resume_source_helm.md       |  1 +
 docs/cmd/flux_suspend.md                  |  1 +
 docs/cmd/flux_suspend_alert.md            |  1 +
 docs/cmd/flux_suspend_helmrelease.md      |  1 +
 docs/cmd/flux_suspend_image.md            |  1 +
 docs/cmd/flux_suspend_image_repository.md |  1 +
 docs/cmd/flux_suspend_image_update.md     |  1 +
 docs/cmd/flux_suspend_kustomization.md    |  1 +
 docs/cmd/flux_suspend_receiver.md         |  1 +
 docs/cmd/flux_suspend_source.md           |  1 +
 docs/cmd/flux_suspend_source_bucket.md    |  1 +
 docs/cmd/flux_suspend_source_chart.md     |  1 +
 docs/cmd/flux_suspend_source_git.md       |  1 +
 docs/cmd/flux_suspend_source_helm.md      |  1 +
 48 files changed, 210 insertions(+), 29 deletions(-)

diff --git a/cmd/flux/resume.go b/cmd/flux/resume.go
index f7bc74af..f25e99bf 100644
--- a/cmd/flux/resume.go
+++ b/cmd/flux/resume.go
@@ -23,6 +23,7 @@ import (
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/wait"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	"github.com/fluxcd/flux2/internal/utils"
 )
@@ -33,7 +34,15 @@ var resumeCmd = &cobra.Command{
 	Long:  "The resume sub-commands resume a suspended resource.",
 }
 
+type ResumeFlags struct {
+	all bool
+}
+
+var resumeArgs ResumeFlags
+
 func init() {
+	resumeCmd.PersistentFlags().BoolVarP(&resumeArgs.all, "all", "", false,
+		"suspend all resources in that namespace")
 	rootCmd.AddCommand(resumeCmd)
 }
 
@@ -47,13 +56,18 @@ type resumable interface {
 type resumeCommand struct {
 	apiType
 	object resumable
+	list   listResumable
+}
+
+type listResumable interface {
+	listAdapter
+	resumeItem(i int) resumable
 }
 
 func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
-	if len(args) < 1 {
+	if len(args) < 1 && !resumeArgs.all {
 		return fmt.Errorf("%s name is required", resume.humanKind)
 	}
-	name := args[0]
 
 	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
 	defer cancel()
@@ -63,29 +77,46 @@ func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	namespacedName := types.NamespacedName{
-		Namespace: rootArgs.namespace,
-		Name:      name,
+	var listOpts []client.ListOption
+	listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
+	if len(args) > 0 {
+		listOpts = append(listOpts, client.MatchingFields{
+			"metadata.name": args[0],
+		})
 	}
 
-	err = kubeClient.Get(ctx, namespacedName, resume.object.asClientObject())
+	err = kubeClient.List(ctx, resume.list.asClientList(), listOpts...)
 	if err != nil {
 		return err
 	}
 
-	logger.Actionf("resuming %s %s in %s namespace", resume.humanKind, name, rootArgs.namespace)
-	resume.object.setUnsuspended()
-	if err := kubeClient.Update(ctx, resume.object.asClientObject()); err != nil {
-		return err
+	if resume.list.len() == 0 {
+		logger.Failuref("no %s objects found in %s namespace", resume.kind, rootArgs.namespace)
+		return nil
 	}
-	logger.Successf("%s resumed", resume.humanKind)
 
-	logger.Waitingf("waiting for %s reconciliation", resume.kind)
-	if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
-		isReady(ctx, kubeClient, namespacedName, resume.object)); err != nil {
-		return err
+	for i := 0; i < resume.list.len(); i++ {
+		logger.Actionf("resuming %s %s in %s namespace", resume.humanKind, resume.list.resumeItem(i).asClientObject().GetName(), rootArgs.namespace)
+		resume.list.resumeItem(i).setUnsuspended()
+		if err := kubeClient.Update(ctx, resume.list.resumeItem(i).asClientObject()); err != nil {
+			return err
+		}
+		logger.Successf("%s resumed", resume.humanKind)
+
+		namespacedName := types.NamespacedName{
+			Name:      resume.list.resumeItem(i).asClientObject().GetName(),
+			Namespace: rootArgs.namespace,
+		}
+
+		logger.Waitingf("waiting for %s reconciliation", resume.kind)
+		if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
+			isReady(ctx, kubeClient, namespacedName, resume.list.resumeItem(i))); err != nil {
+			logger.Failuref(err.Error())
+			continue
+		}
+		logger.Successf("%s reconciliation completed", resume.kind)
+		logger.Successf(resume.list.resumeItem(i).successMessage())
 	}
-	logger.Successf("%s reconciliation completed", resume.kind)
-	logger.Successf(resume.object.successMessage())
+
 	return nil
 }
diff --git a/cmd/flux/resume_alert.go b/cmd/flux/resume_alert.go
index b221ca0e..de5a645a 100644
--- a/cmd/flux/resume_alert.go
+++ b/cmd/flux/resume_alert.go
@@ -32,6 +32,7 @@ finish the apply.`,
 	RunE: resumeCommand{
 		apiType: alertType,
 		object:  alertAdapter{&notificationv1.Alert{}},
+		list:    &alertListAdapter{&notificationv1.AlertList{}},
 	}.run,
 }
 
@@ -50,3 +51,7 @@ func (obj alertAdapter) setUnsuspended() {
 func (obj alertAdapter) successMessage() string {
 	return "Alert reconciliation completed"
 }
+
+func (a alertListAdapter) resumeItem(i int) resumable {
+	return &alertAdapter{&a.AlertList.Items[i]}
+}
diff --git a/cmd/flux/resume_helmrelease.go b/cmd/flux/resume_helmrelease.go
index e2cff0fb..24ee8010 100644
--- a/cmd/flux/resume_helmrelease.go
+++ b/cmd/flux/resume_helmrelease.go
@@ -35,6 +35,7 @@ finish the apply.`,
 	RunE: resumeCommand{
 		apiType: helmReleaseType,
 		object:  helmReleaseAdapter{&helmv2.HelmRelease{}},
+		list:    helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
 	}.run,
 }
 
@@ -53,3 +54,7 @@ func (obj helmReleaseAdapter) setUnsuspended() {
 func (obj helmReleaseAdapter) successMessage() string {
 	return fmt.Sprintf("applied revision %s", obj.Status.LastAppliedRevision)
 }
+
+func (a helmReleaseListAdapter) resumeItem(i int) resumable {
+	return &helmReleaseAdapter{&a.HelmReleaseList.Items[i]}
+}
diff --git a/cmd/flux/resume_image_repository.go b/cmd/flux/resume_image_repository.go
index 3153a174..3538a150 100644
--- a/cmd/flux/resume_image_repository.go
+++ b/cmd/flux/resume_image_repository.go
@@ -31,6 +31,7 @@ var resumeImageRepositoryCmd = &cobra.Command{
 	RunE: resumeCommand{
 		apiType: imageRepositoryType,
 		object:  imageRepositoryAdapter{&imagev1.ImageRepository{}},
+		list:    imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (obj imageRepositoryAdapter) getObservedGeneration() int64 {
 func (obj imageRepositoryAdapter) setUnsuspended() {
 	obj.ImageRepository.Spec.Suspend = false
 }
+
+func (a imageRepositoryListAdapter) resumeItem(i int) resumable {
+	return &imageRepositoryAdapter{&a.ImageRepositoryList.Items[i]}
+}
diff --git a/cmd/flux/resume_image_updateauto.go b/cmd/flux/resume_image_updateauto.go
index 2d92d54e..79368ddc 100644
--- a/cmd/flux/resume_image_updateauto.go
+++ b/cmd/flux/resume_image_updateauto.go
@@ -31,6 +31,7 @@ var resumeImageUpdateCmd = &cobra.Command{
 	RunE: resumeCommand{
 		apiType: imageUpdateAutomationType,
 		object:  imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
+		list:    imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (obj imageUpdateAutomationAdapter) setUnsuspended() {
 func (obj imageUpdateAutomationAdapter) getObservedGeneration() int64 {
 	return obj.ImageUpdateAutomation.Status.ObservedGeneration
 }
+
+func (a imageUpdateAutomationListAdapter) resumeItem(i int) resumable {
+	return &imageUpdateAutomationAdapter{&a.ImageUpdateAutomationList.Items[i]}
+}
diff --git a/cmd/flux/resume_kustomization.go b/cmd/flux/resume_kustomization.go
index ac2def27..1555cc9a 100644
--- a/cmd/flux/resume_kustomization.go
+++ b/cmd/flux/resume_kustomization.go
@@ -35,6 +35,7 @@ finish the apply.`,
 	RunE: resumeCommand{
 		apiType: kustomizationType,
 		object:  kustomizationAdapter{&kustomizev1.Kustomization{}},
+		list:    kustomizationListAdapter{&kustomizev1.KustomizationList{}},
 	}.run,
 }
 
@@ -53,3 +54,7 @@ func (obj kustomizationAdapter) setUnsuspended() {
 func (obj kustomizationAdapter) successMessage() string {
 	return fmt.Sprintf("applied revision %s", obj.Status.LastAppliedRevision)
 }
+
+func (a kustomizationListAdapter) resumeItem(i int) resumable {
+	return &kustomizationAdapter{&a.KustomizationList.Items[i]}
+}
diff --git a/cmd/flux/resume_receiver.go b/cmd/flux/resume_receiver.go
index 7bf37577..eecfe63d 100644
--- a/cmd/flux/resume_receiver.go
+++ b/cmd/flux/resume_receiver.go
@@ -32,6 +32,7 @@ finish the apply.`,
 	RunE: resumeCommand{
 		apiType: receiverType,
 		object:  receiverAdapter{&notificationv1.Receiver{}},
+		list:    receiverListAdapter{&notificationv1.ReceiverList{}},
 	}.run,
 }
 
@@ -50,3 +51,7 @@ func (obj receiverAdapter) setUnsuspended() {
 func (obj receiverAdapter) successMessage() string {
 	return "Receiver reconciliation completed"
 }
+
+func (a receiverListAdapter) resumeItem(i int) resumable {
+	return &receiverAdapter{&a.ReceiverList.Items[i]}
+}
diff --git a/cmd/flux/resume_source_bucket.go b/cmd/flux/resume_source_bucket.go
index a7a81124..e40cef34 100644
--- a/cmd/flux/resume_source_bucket.go
+++ b/cmd/flux/resume_source_bucket.go
@@ -45,3 +45,7 @@ func (obj bucketAdapter) getObservedGeneration() int64 {
 func (obj bucketAdapter) setUnsuspended() {
 	obj.Bucket.Spec.Suspend = false
 }
+
+func (a bucketListAdapter) resumeItem(i int) resumable {
+	return &bucketAdapter{&a.BucketList.Items[i]}
+}
diff --git a/cmd/flux/resume_source_chart.go b/cmd/flux/resume_source_chart.go
index f2cf59a0..d3525eb3 100644
--- a/cmd/flux/resume_source_chart.go
+++ b/cmd/flux/resume_source_chart.go
@@ -33,6 +33,7 @@ var resumeSourceHelmChartCmd = &cobra.Command{
 	RunE: resumeCommand{
 		apiType: helmChartType,
 		object:  &helmChartAdapter{&sourcev1.HelmChart{}},
+		list:    &helmChartListAdapter{&sourcev1.HelmChartList{}},
 	}.run,
 }
 
@@ -51,3 +52,7 @@ func (obj helmChartAdapter) setUnsuspended() {
 func (obj helmChartAdapter) successMessage() string {
 	return fmt.Sprintf("fetched revision %s", obj.Status.Artifact.Revision)
 }
+
+func (a helmChartListAdapter) resumeItem(i int) resumable {
+	return &helmChartAdapter{&a.HelmChartList.Items[i]}
+}
diff --git a/cmd/flux/resume_source_git.go b/cmd/flux/resume_source_git.go
index e67614d6..f4aa22ed 100644
--- a/cmd/flux/resume_source_git.go
+++ b/cmd/flux/resume_source_git.go
@@ -31,6 +31,7 @@ var resumeSourceGitCmd = &cobra.Command{
 	RunE: resumeCommand{
 		apiType: gitRepositoryType,
 		object:  gitRepositoryAdapter{&sourcev1.GitRepository{}},
+		list:    gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (obj gitRepositoryAdapter) getObservedGeneration() int64 {
 func (obj gitRepositoryAdapter) setUnsuspended() {
 	obj.GitRepository.Spec.Suspend = false
 }
+
+func (a gitRepositoryListAdapter) resumeItem(i int) resumable {
+	return &gitRepositoryAdapter{&a.GitRepositoryList.Items[i]}
+}
diff --git a/cmd/flux/resume_source_helm.go b/cmd/flux/resume_source_helm.go
index 1a9a10b6..a22d7327 100644
--- a/cmd/flux/resume_source_helm.go
+++ b/cmd/flux/resume_source_helm.go
@@ -31,6 +31,7 @@ var resumeSourceHelmCmd = &cobra.Command{
 	RunE: resumeCommand{
 		apiType: helmRepositoryType,
 		object:  helmRepositoryAdapter{&sourcev1.HelmRepository{}},
+		list:    helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (obj helmRepositoryAdapter) getObservedGeneration() int64 {
 func (obj helmRepositoryAdapter) setUnsuspended() {
 	obj.HelmRepository.Spec.Suspend = false
 }
+
+func (a helmRepositoryListAdapter) resumeItem(i int) resumable {
+	return &helmRepositoryAdapter{&a.HelmRepositoryList.Items[i]}
+}
diff --git a/cmd/flux/suspend.go b/cmd/flux/suspend.go
index 6f2eac88..101f29bb 100644
--- a/cmd/flux/suspend.go
+++ b/cmd/flux/suspend.go
@@ -21,7 +21,7 @@ import (
 	"fmt"
 
 	"github.com/spf13/cobra"
-	"k8s.io/apimachinery/pkg/types"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	"github.com/fluxcd/flux2/internal/utils"
 )
@@ -32,7 +32,15 @@ var suspendCmd = &cobra.Command{
 	Long:  "The suspend sub-commands suspend the reconciliation of a resource.",
 }
 
+type SuspendFlags struct {
+	all bool
+}
+
+var suspendArgs SuspendFlags
+
 func init() {
+	suspendCmd.PersistentFlags().BoolVarP(&suspendArgs.all, "all", "", false,
+		"suspend all resources in that namespace")
 	rootCmd.AddCommand(suspendCmd)
 }
 
@@ -44,14 +52,19 @@ type suspendable interface {
 
 type suspendCommand struct {
 	apiType
+	list   listSuspendable
 	object suspendable
 }
 
+type listSuspendable interface {
+	listAdapter
+	item(i int) suspendable
+}
+
 func (suspend suspendCommand) run(cmd *cobra.Command, args []string) error {
-	if len(args) < 1 {
+	if len(args) < 1 && !suspendArgs.all {
 		return fmt.Errorf("%s name is required", suspend.humanKind)
 	}
-	name := args[0]
 
 	ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
 	defer cancel()
@@ -61,21 +74,33 @@ func (suspend suspendCommand) run(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	namespacedName := types.NamespacedName{
-		Namespace: rootArgs.namespace,
-		Name:      name,
+	var listOpts []client.ListOption
+	listOpts = append(listOpts, client.InNamespace(rootArgs.namespace))
+	if len(args) > 0 {
+		listOpts = append(listOpts, client.MatchingFields{
+			"metadata.name": args[0],
+		})
 	}
-	err = kubeClient.Get(ctx, namespacedName, suspend.object.asClientObject())
+
+	err = kubeClient.List(ctx, suspend.list.asClientList(), listOpts...)
 	if err != nil {
 		return err
 	}
 
-	logger.Actionf("suspending %s %s in %s namespace", suspend.humanKind, name, rootArgs.namespace)
-	suspend.object.setSuspended()
-	if err := kubeClient.Update(ctx, suspend.object.asClientObject()); err != nil {
-		return err
+	if suspend.list.len() == 0 {
+		logger.Failuref("no %s objects found in %s namespace", suspend.kind, rootArgs.namespace)
+		return nil
+	}
+
+	for i := 0; i < suspend.list.len(); i++ {
+		logger.Actionf("suspending %s %s in %s namespace", suspend.humanKind, suspend.list.item(i).asClientObject().GetName(), rootArgs.namespace)
+		suspend.list.item(i).setSuspended()
+		if err := kubeClient.Update(ctx, suspend.list.item(i).asClientObject()); err != nil {
+			return err
+		}
+		logger.Successf("%s suspended", suspend.humanKind)
+
 	}
-	logger.Successf("%s suspended", suspend.humanKind)
 
 	return nil
 }
diff --git a/cmd/flux/suspend_alert.go b/cmd/flux/suspend_alert.go
index f963bb88..eb3c184f 100644
--- a/cmd/flux/suspend_alert.go
+++ b/cmd/flux/suspend_alert.go
@@ -31,6 +31,7 @@ var suspendAlertCmd = &cobra.Command{
 	RunE: suspendCommand{
 		apiType: alertType,
 		object:  &alertAdapter{&notificationv1.Alert{}},
+		list:    &alertListAdapter{&notificationv1.AlertList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (obj alertAdapter) isSuspended() bool {
 func (obj alertAdapter) setSuspended() {
 	obj.Alert.Spec.Suspend = true
 }
+
+func (a alertListAdapter) item(i int) suspendable {
+	return &alertAdapter{&a.AlertList.Items[i]}
+}
diff --git a/cmd/flux/suspend_helmrelease.go b/cmd/flux/suspend_helmrelease.go
index 11f88b82..d222916a 100644
--- a/cmd/flux/suspend_helmrelease.go
+++ b/cmd/flux/suspend_helmrelease.go
@@ -32,6 +32,7 @@ var suspendHrCmd = &cobra.Command{
 	RunE: suspendCommand{
 		apiType: helmReleaseType,
 		object:  &helmReleaseAdapter{&helmv2.HelmRelease{}},
+		list:    &helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
 	}.run,
 }
 
@@ -46,3 +47,7 @@ func (obj helmReleaseAdapter) isSuspended() bool {
 func (obj helmReleaseAdapter) setSuspended() {
 	obj.HelmRelease.Spec.Suspend = true
 }
+
+func (a helmReleaseListAdapter) item(i int) suspendable {
+	return &helmReleaseAdapter{&a.HelmReleaseList.Items[i]}
+}
diff --git a/cmd/flux/suspend_image_repository.go b/cmd/flux/suspend_image_repository.go
index 6ea3f45f..768299e8 100644
--- a/cmd/flux/suspend_image_repository.go
+++ b/cmd/flux/suspend_image_repository.go
@@ -31,6 +31,7 @@ var suspendImageRepositoryCmd = &cobra.Command{
 	RunE: suspendCommand{
 		apiType: imageRepositoryType,
 		object:  imageRepositoryAdapter{&imagev1.ImageRepository{}},
+		list:    &imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (obj imageRepositoryAdapter) isSuspended() bool {
 func (obj imageRepositoryAdapter) setSuspended() {
 	obj.ImageRepository.Spec.Suspend = true
 }
+
+func (a imageRepositoryListAdapter) item(i int) suspendable {
+	return &imageRepositoryAdapter{&a.ImageRepositoryList.Items[i]}
+}
diff --git a/cmd/flux/suspend_image_updateauto.go b/cmd/flux/suspend_image_updateauto.go
index c8bd8aca..3d4f81b4 100644
--- a/cmd/flux/suspend_image_updateauto.go
+++ b/cmd/flux/suspend_image_updateauto.go
@@ -31,6 +31,7 @@ var suspendImageUpdateCmd = &cobra.Command{
 	RunE: suspendCommand{
 		apiType: imageUpdateAutomationType,
 		object:  imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
+		list:    &imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (update imageUpdateAutomationAdapter) isSuspended() bool {
 func (update imageUpdateAutomationAdapter) setSuspended() {
 	update.ImageUpdateAutomation.Spec.Suspend = true
 }
+
+func (a imageUpdateAutomationListAdapter) item(i int) suspendable {
+	return &imageUpdateAutomationAdapter{&a.ImageUpdateAutomationList.Items[i]}
+}
diff --git a/cmd/flux/suspend_kustomization.go b/cmd/flux/suspend_kustomization.go
index 272c5d65..1d1f208b 100644
--- a/cmd/flux/suspend_kustomization.go
+++ b/cmd/flux/suspend_kustomization.go
@@ -32,6 +32,7 @@ var suspendKsCmd = &cobra.Command{
 	RunE: suspendCommand{
 		apiType: kustomizationType,
 		object:  kustomizationAdapter{&kustomizev1.Kustomization{}},
+		list:    &kustomizationListAdapter{&kustomizev1.KustomizationList{}},
 	}.run,
 }
 
@@ -46,3 +47,7 @@ func (obj kustomizationAdapter) isSuspended() bool {
 func (obj kustomizationAdapter) setSuspended() {
 	obj.Kustomization.Spec.Suspend = true
 }
+
+func (a kustomizationListAdapter) item(i int) suspendable {
+	return &kustomizationAdapter{&a.KustomizationList.Items[i]}
+}
diff --git a/cmd/flux/suspend_receiver.go b/cmd/flux/suspend_receiver.go
index 7223fd49..90ca5cf0 100644
--- a/cmd/flux/suspend_receiver.go
+++ b/cmd/flux/suspend_receiver.go
@@ -31,6 +31,7 @@ var suspendReceiverCmd = &cobra.Command{
 	RunE: suspendCommand{
 		apiType: receiverType,
 		object:  &receiverAdapter{&notificationv1.Receiver{}},
+		list:    &receiverListAdapter{&notificationv1.ReceiverList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (obj receiverAdapter) isSuspended() bool {
 func (obj receiverAdapter) setSuspended() {
 	obj.Receiver.Spec.Suspend = true
 }
+
+func (a receiverListAdapter) item(i int) suspendable {
+	return &receiverAdapter{&a.ReceiverList.Items[i]}
+}
diff --git a/cmd/flux/suspend_source_bucket.go b/cmd/flux/suspend_source_bucket.go
index 523d41a8..72f1d0e3 100644
--- a/cmd/flux/suspend_source_bucket.go
+++ b/cmd/flux/suspend_source_bucket.go
@@ -31,6 +31,7 @@ var suspendSourceBucketCmd = &cobra.Command{
 	RunE: suspendCommand{
 		apiType: bucketType,
 		object:  bucketAdapter{&sourcev1.Bucket{}},
+		list:    bucketListAdapter{&sourcev1.BucketList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (obj bucketAdapter) isSuspended() bool {
 func (obj bucketAdapter) setSuspended() {
 	obj.Bucket.Spec.Suspend = true
 }
+
+func (a bucketListAdapter) item(i int) suspendable {
+	return &bucketAdapter{&a.BucketList.Items[i]}
+}
diff --git a/cmd/flux/suspend_source_chart.go b/cmd/flux/suspend_source_chart.go
index 8741a032..703e820e 100644
--- a/cmd/flux/suspend_source_chart.go
+++ b/cmd/flux/suspend_source_chart.go
@@ -31,6 +31,7 @@ var suspendSourceHelmChartCmd = &cobra.Command{
 	RunE: suspendCommand{
 		apiType: helmChartType,
 		object:  helmChartAdapter{&sourcev1.HelmChart{}},
+		list:    helmChartListAdapter{&sourcev1.HelmChartList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (obj helmChartAdapter) isSuspended() bool {
 func (obj helmChartAdapter) setSuspended() {
 	obj.HelmChart.Spec.Suspend = true
 }
+
+func (a helmChartListAdapter) item(i int) suspendable {
+	return &helmChartAdapter{&a.HelmChartList.Items[i]}
+}
diff --git a/cmd/flux/suspend_source_git.go b/cmd/flux/suspend_source_git.go
index 9cc08fa2..bc058303 100644
--- a/cmd/flux/suspend_source_git.go
+++ b/cmd/flux/suspend_source_git.go
@@ -31,6 +31,7 @@ var suspendSourceGitCmd = &cobra.Command{
 	RunE: suspendCommand{
 		apiType: gitRepositoryType,
 		object:  gitRepositoryAdapter{&sourcev1.GitRepository{}},
+		list:    gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (obj gitRepositoryAdapter) isSuspended() bool {
 func (obj gitRepositoryAdapter) setSuspended() {
 	obj.GitRepository.Spec.Suspend = true
 }
+
+func (a gitRepositoryListAdapter) item(i int) suspendable {
+	return &gitRepositoryAdapter{&a.GitRepositoryList.Items[i]}
+}
diff --git a/cmd/flux/suspend_source_helm.go b/cmd/flux/suspend_source_helm.go
index 5cd483f6..a4a5787c 100644
--- a/cmd/flux/suspend_source_helm.go
+++ b/cmd/flux/suspend_source_helm.go
@@ -31,6 +31,7 @@ var suspendSourceHelmCmd = &cobra.Command{
 	RunE: suspendCommand{
 		apiType: helmRepositoryType,
 		object:  helmRepositoryAdapter{&sourcev1.HelmRepository{}},
+		list:    helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
 	}.run,
 }
 
@@ -45,3 +46,7 @@ func (obj helmRepositoryAdapter) isSuspended() bool {
 func (obj helmRepositoryAdapter) setSuspended() {
 	obj.HelmRepository.Spec.Suspend = true
 }
+
+func (a helmRepositoryListAdapter) item(i int) suspendable {
+	return &helmRepositoryAdapter{&a.HelmRepositoryList.Items[i]}
+}
diff --git a/docs/cmd/flux_resume.md b/docs/cmd/flux_resume.md
index afeea539..7a5fe604 100644
--- a/docs/cmd/flux_resume.md
+++ b/docs/cmd/flux_resume.md
@@ -12,6 +12,7 @@ The resume sub-commands resume a suspended resource.
 ### Options
 
 ```
+      --all    suspend all resources in that namespace
   -h, --help   help for resume
 ```
 
diff --git a/docs/cmd/flux_resume_alert.md b/docs/cmd/flux_resume_alert.md
index cd8cc060..a60af6a2 100644
--- a/docs/cmd/flux_resume_alert.md
+++ b/docs/cmd/flux_resume_alert.md
@@ -30,6 +30,7 @@ flux resume alert [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_resume_helmrelease.md b/docs/cmd/flux_resume_helmrelease.md
index 5d4f26ff..a7befe76 100644
--- a/docs/cmd/flux_resume_helmrelease.md
+++ b/docs/cmd/flux_resume_helmrelease.md
@@ -30,6 +30,7 @@ flux resume helmrelease [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_resume_image.md b/docs/cmd/flux_resume_image.md
index 7a43a2a4..754042dd 100644
--- a/docs/cmd/flux_resume_image.md
+++ b/docs/cmd/flux_resume_image.md
@@ -18,6 +18,7 @@ The resume image sub-commands resume suspended image automation objects.
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_resume_image_repository.md b/docs/cmd/flux_resume_image_repository.md
index 4649de8d..0217c465 100644
--- a/docs/cmd/flux_resume_image_repository.md
+++ b/docs/cmd/flux_resume_image_repository.md
@@ -29,6 +29,7 @@ flux resume image repository [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_resume_image_update.md b/docs/cmd/flux_resume_image_update.md
index 3ec77ab2..29d4a36f 100644
--- a/docs/cmd/flux_resume_image_update.md
+++ b/docs/cmd/flux_resume_image_update.md
@@ -29,6 +29,7 @@ flux resume image update [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_resume_kustomization.md b/docs/cmd/flux_resume_kustomization.md
index 5d6a6bdc..9322c870 100644
--- a/docs/cmd/flux_resume_kustomization.md
+++ b/docs/cmd/flux_resume_kustomization.md
@@ -30,6 +30,7 @@ flux resume kustomization [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_resume_receiver.md b/docs/cmd/flux_resume_receiver.md
index 7f598b68..c4487583 100644
--- a/docs/cmd/flux_resume_receiver.md
+++ b/docs/cmd/flux_resume_receiver.md
@@ -30,6 +30,7 @@ flux resume receiver [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_resume_source.md b/docs/cmd/flux_resume_source.md
index 3e2257e3..2ccde0b3 100644
--- a/docs/cmd/flux_resume_source.md
+++ b/docs/cmd/flux_resume_source.md
@@ -18,6 +18,7 @@ The resume sub-commands resume a suspended source.
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_resume_source_bucket.md b/docs/cmd/flux_resume_source_bucket.md
index c9fd6eb8..2ce39e0a 100644
--- a/docs/cmd/flux_resume_source_bucket.md
+++ b/docs/cmd/flux_resume_source_bucket.md
@@ -29,6 +29,7 @@ flux resume source bucket [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_resume_source_chart.md b/docs/cmd/flux_resume_source_chart.md
index ef68931a..f356329f 100644
--- a/docs/cmd/flux_resume_source_chart.md
+++ b/docs/cmd/flux_resume_source_chart.md
@@ -29,6 +29,7 @@ flux resume source chart [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_resume_source_git.md b/docs/cmd/flux_resume_source_git.md
index 1e32e35e..bf02fbb5 100644
--- a/docs/cmd/flux_resume_source_git.md
+++ b/docs/cmd/flux_resume_source_git.md
@@ -29,6 +29,7 @@ flux resume source git [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_resume_source_helm.md b/docs/cmd/flux_resume_source_helm.md
index cc7292af..23c404d8 100644
--- a/docs/cmd/flux_resume_source_helm.md
+++ b/docs/cmd/flux_resume_source_helm.md
@@ -29,6 +29,7 @@ flux resume source helm [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend.md b/docs/cmd/flux_suspend.md
index 56eb8be4..8d2da1d5 100644
--- a/docs/cmd/flux_suspend.md
+++ b/docs/cmd/flux_suspend.md
@@ -12,6 +12,7 @@ The suspend sub-commands suspend the reconciliation of a resource.
 ### Options
 
 ```
+      --all    suspend all resources in that namespace
   -h, --help   help for suspend
 ```
 
diff --git a/docs/cmd/flux_suspend_alert.md b/docs/cmd/flux_suspend_alert.md
index e1ba99f2..3e55db73 100644
--- a/docs/cmd/flux_suspend_alert.md
+++ b/docs/cmd/flux_suspend_alert.md
@@ -29,6 +29,7 @@ flux suspend alert [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend_helmrelease.md b/docs/cmd/flux_suspend_helmrelease.md
index b55d335f..37a41e4a 100644
--- a/docs/cmd/flux_suspend_helmrelease.md
+++ b/docs/cmd/flux_suspend_helmrelease.md
@@ -29,6 +29,7 @@ flux suspend helmrelease [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend_image.md b/docs/cmd/flux_suspend_image.md
index 9a2659e2..603c98ef 100644
--- a/docs/cmd/flux_suspend_image.md
+++ b/docs/cmd/flux_suspend_image.md
@@ -18,6 +18,7 @@ The suspend image sub-commands suspend the reconciliation of an image automation
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend_image_repository.md b/docs/cmd/flux_suspend_image_repository.md
index 153c8a01..34b82f76 100644
--- a/docs/cmd/flux_suspend_image_repository.md
+++ b/docs/cmd/flux_suspend_image_repository.md
@@ -29,6 +29,7 @@ flux suspend image repository [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend_image_update.md b/docs/cmd/flux_suspend_image_update.md
index 5970f77a..7d0f0d80 100644
--- a/docs/cmd/flux_suspend_image_update.md
+++ b/docs/cmd/flux_suspend_image_update.md
@@ -29,6 +29,7 @@ flux suspend image update [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend_kustomization.md b/docs/cmd/flux_suspend_kustomization.md
index edfbb530..394fdc19 100644
--- a/docs/cmd/flux_suspend_kustomization.md
+++ b/docs/cmd/flux_suspend_kustomization.md
@@ -29,6 +29,7 @@ flux suspend kustomization [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend_receiver.md b/docs/cmd/flux_suspend_receiver.md
index bc9bb259..aebfc33e 100644
--- a/docs/cmd/flux_suspend_receiver.md
+++ b/docs/cmd/flux_suspend_receiver.md
@@ -29,6 +29,7 @@ flux suspend receiver [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend_source.md b/docs/cmd/flux_suspend_source.md
index 87150e97..123dc0e9 100644
--- a/docs/cmd/flux_suspend_source.md
+++ b/docs/cmd/flux_suspend_source.md
@@ -18,6 +18,7 @@ The suspend sub-commands suspend the reconciliation of a source.
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend_source_bucket.md b/docs/cmd/flux_suspend_source_bucket.md
index ca41385f..3d83c432 100644
--- a/docs/cmd/flux_suspend_source_bucket.md
+++ b/docs/cmd/flux_suspend_source_bucket.md
@@ -29,6 +29,7 @@ flux suspend source bucket [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend_source_chart.md b/docs/cmd/flux_suspend_source_chart.md
index aa3c1078..857fad99 100644
--- a/docs/cmd/flux_suspend_source_chart.md
+++ b/docs/cmd/flux_suspend_source_chart.md
@@ -29,6 +29,7 @@ flux suspend source chart [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend_source_git.md b/docs/cmd/flux_suspend_source_git.md
index 50c372f1..865affc3 100644
--- a/docs/cmd/flux_suspend_source_git.md
+++ b/docs/cmd/flux_suspend_source_git.md
@@ -29,6 +29,7 @@ flux suspend source git [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
diff --git a/docs/cmd/flux_suspend_source_helm.md b/docs/cmd/flux_suspend_source_helm.md
index 1c8003cf..acee9f15 100644
--- a/docs/cmd/flux_suspend_source_helm.md
+++ b/docs/cmd/flux_suspend_source_helm.md
@@ -29,6 +29,7 @@ flux suspend source helm [name] [flags]
 ### Options inherited from parent commands
 
 ```
+      --all                 suspend all resources in that namespace
       --context string      kubernetes context to use
       --kubeconfig string   absolute path to the kubeconfig file
   -n, --namespace string    the namespace scope for this operation (default "flux-system")
-- 
GitLab