Merge pull request #3755 from rishinair11/add_multiple_args

Allow multiple arguments for "flux suspend/resume"
pull/4023/head
Stefan Prodan 2 years ago committed by GitHub
commit 25af5d2968
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -61,11 +61,26 @@ func TestKustomizationFromGit(t *testing.T) {
"testdata/kustomization/suspend_kustomization_from_git.golden", "testdata/kustomization/suspend_kustomization_from_git.golden",
tmpl, tmpl,
}, },
{
"suspend kustomization tkfg foo tkfg bar",
"testdata/kustomization/suspend_kustomization_from_git_multiple_args.golden",
tmpl,
},
{
"resume kustomization tkfg foo --wait",
"testdata/kustomization/resume_kustomization_from_git_multiple_args_wait.golden",
tmpl,
},
{ {
"resume kustomization tkfg", "resume kustomization tkfg",
"testdata/kustomization/resume_kustomization_from_git.golden", "testdata/kustomization/resume_kustomization_from_git.golden",
tmpl, tmpl,
}, },
{
"resume kustomization tkfg tkfg",
"testdata/kustomization/resume_kustomization_from_git_multiple_args.golden",
tmpl,
},
{ {
"delete kustomization tkfg --silent", "delete kustomization tkfg --silent",
"testdata/kustomization/delete_kustomization_from_git.golden", "testdata/kustomization/delete_kustomization_from_git.golden",

@ -365,6 +365,12 @@ func executeTemplate(content string, templateValues map[string]string) (string,
// Run the command and return the captured output. // Run the command and return the captured output.
func executeCommand(cmd string) (string, error) { func executeCommand(cmd string) (string, error) {
defer resetCmdArgs() defer resetCmdArgs()
defer func() {
// need to set this explicitly because apparently its value isn't changed
// in subsequent executions which causes tests to fail that rely on the value
// of "Changed".
resumeCmd.PersistentFlags().Lookup("wait").Changed = false
}()
args, err := shellwords.Parse(cmd) args, err := shellwords.Parse(cmd)
if err != nil { if err != nil {
return "", err return "", err

@ -47,7 +47,7 @@ type copyable interface {
deepCopyClientObject() client.Object deepCopyClientObject() client.Object
} }
// listAdapater is the analogue to adapter, but for lists; the // listAdapter is the analogue to adapter, but for lists; the
// controller runtime distinguishes between methods dealing with // controller runtime distinguishes between methods dealing with
// objects and lists. // objects and lists.
type listAdapter interface { type listAdapter interface {

@ -19,6 +19,8 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"sort"
"sync"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@ -59,8 +61,10 @@ type resumable interface {
type resumeCommand struct { type resumeCommand struct {
apiType apiType
object resumable client client.WithWatch
list listResumable list listResumable
namespace string
shouldReconcile bool
} }
type listResumable interface { type listResumable interface {
@ -68,6 +72,11 @@ type listResumable interface {
resumeItem(i int) resumable resumeItem(i int) resumable
} }
type reconcileResponse struct {
resumable
err error
}
func (resume resumeCommand) run(cmd *cobra.Command, args []string) error { func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
if len(args) < 1 && !resumeArgs.all { if len(args) < 1 && !resumeArgs.all {
return fmt.Errorf("%s name is required", resume.humanKind) return fmt.Errorf("%s name is required", resume.humanKind)
@ -80,52 +89,162 @@ func (resume resumeCommand) run(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return err return err
} }
resume.client = kubeClient
resume.namespace = *kubeconfigArgs.Namespace
var listOpts []client.ListOption // require waiting for the object(s) if the user has not provided the --wait flag and gave exactly
listOpts = append(listOpts, client.InNamespace(*kubeconfigArgs.Namespace)) // one object to resume. This is necessary to maintain backwards compatibility with prior versions
if len(args) > 0 { // of this command. Otherwise just follow the value of the --wait flag (including its default).
listOpts = append(listOpts, client.MatchingFields{ resume.shouldReconcile = !resumeCmd.PersistentFlags().Changed("wait") && len(args) == 1 || resumeArgs.wait
"metadata.name": args[0],
})
}
err = kubeClient.List(ctx, resume.list.asClientList(), listOpts...) resumables, err := resume.getPatchedResumables(ctx, args)
if err != nil { if err != nil {
return err return err
} }
var wg sync.WaitGroup
wg.Add(len(resumables))
resultChan := make(chan reconcileResponse, len(resumables))
for _, r := range resumables {
go func(res resumable) {
defer wg.Done()
resultChan <- resume.reconcile(ctx, res)
}(r)
}
go func() {
defer close(resultChan)
wg.Wait()
}()
reconcileResps := make([]reconcileResponse, 0, len(resumables))
for c := range resultChan {
reconcileResps = append(reconcileResps, c)
}
resume.printMessage(reconcileResps)
return nil
}
// getPatchedResumables returns a list of the given resumable objects that have been patched to be resumed.
// If the args slice is empty, it patches all resumable objects in the given namespace.
func (resume *resumeCommand) getPatchedResumables(ctx context.Context, args []string) ([]resumable, error) {
if len(args) < 1 {
objs, err := resume.patch(ctx, []client.ListOption{
client.InNamespace(resume.namespace),
})
if err != nil {
return nil, fmt.Errorf("failed patching objects: %w", err)
}
return objs, nil
}
var resumables []resumable
processed := make(map[string]struct{}, len(args))
for _, arg := range args {
if _, has := processed[arg]; has {
continue // skip object that user might have provided more than once
}
processed[arg] = struct{}{}
objs, err := resume.patch(ctx, []client.ListOption{
client.InNamespace(resume.namespace),
client.MatchingFields{
"metadata.name": arg,
},
})
if err != nil {
return nil, err
}
resumables = append(resumables, objs...)
}
return resumables, nil
}
// Patches resumable objects by setting their status to unsuspended.
// Returns a slice of resumables that have been patched and any error encountered during patching.
func (resume resumeCommand) patch(ctx context.Context, listOpts []client.ListOption) ([]resumable, error) {
if err := resume.client.List(ctx, resume.list.asClientList(), listOpts...); err != nil {
return nil, err
}
if resume.list.len() == 0 { if resume.list.len() == 0 {
logger.Failuref("no %s objects found in %s namespace", resume.kind, *kubeconfigArgs.Namespace) logger.Failuref("no %s objects found in %s namespace", resume.kind, resume.namespace)
return nil return nil, nil
} }
var resumables []resumable
for i := 0; i < resume.list.len(); i++ { for i := 0; i < resume.list.len(); i++ {
logger.Actionf("resuming %s %s in %s namespace", resume.humanKind, resume.list.resumeItem(i).asClientObject().GetName(), *kubeconfigArgs.Namespace)
obj := resume.list.resumeItem(i) obj := resume.list.resumeItem(i)
logger.Actionf("resuming %s %s in %s namespace", resume.humanKind, obj.asClientObject().GetName(), resume.namespace)
patch := client.MergeFrom(obj.deepCopyClientObject()) patch := client.MergeFrom(obj.deepCopyClientObject())
obj.setUnsuspended() obj.setUnsuspended()
if err := kubeClient.Patch(ctx, obj.asClientObject(), patch); err != nil { if err := resume.client.Patch(ctx, obj.asClientObject(), patch); err != nil {
return err return nil, err
} }
resumables = append(resumables, obj)
logger.Successf("%s resumed", resume.humanKind) logger.Successf("%s resumed", resume.humanKind)
}
return resumables, nil
}
// Waits for resumable object to be reconciled and returns the object and any error encountered while waiting.
// Returns an empty reconcileResponse, if shouldReconcile is false.
func (resume resumeCommand) reconcile(ctx context.Context, res resumable) reconcileResponse {
if !resume.shouldReconcile {
return reconcileResponse{}
}
if resumeArgs.wait || !resumeArgs.all { namespacedName := types.NamespacedName{
namespacedName := types.NamespacedName{ Name: res.asClientObject().GetName(),
Name: resume.list.resumeItem(i).asClientObject().GetName(), Namespace: resume.namespace,
Namespace: *kubeconfigArgs.Namespace, }
}
logger.Waitingf("waiting for %s reconciliation", resume.kind)
logger.Waitingf("waiting for %s reconciliation", resume.kind)
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout, if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isReady(ctx, kubeClient, namespacedName, resume.list.resumeItem(i))); err != nil { isReady(ctx, resume.client, namespacedName, res)); err != nil {
logger.Failuref(err.Error()) return reconcileResponse{
continue resumable: res,
} err: err,
logger.Successf("%s reconciliation completed", resume.kind)
logger.Successf(resume.list.resumeItem(i).successMessage())
} }
} }
return nil return reconcileResponse{
resumable: res,
err: nil,
}
}
// Sorts the given reconcileResponses by resumable name and prints the success/error message for each response.
func (resume resumeCommand) printMessage(responses []reconcileResponse) {
sort.Slice(responses, func(i, j int) bool {
r1, r2 := responses[i], responses[j]
if r1.resumable == nil || r2.resumable == nil {
return false
}
return r1.asClientObject().GetName() <= r2.asClientObject().GetName()
})
// Print success/error message.
for _, r := range responses {
if r.resumable == nil {
continue
}
if r.err != nil {
logger.Failuref(r.err.Error())
}
logger.Successf("%s %s reconciliation completed", resume.kind, r.asClientObject().GetName())
logger.Successf(r.successMessage())
}
} }

@ -28,11 +28,13 @@ var resumeAlertCmd = &cobra.Command{
Long: `The resume command marks a previously suspended Alert resource for reconciliation and waits for it to Long: `The resume command marks a previously suspended Alert resource for reconciliation and waits for it to
finish the apply.`, finish the apply.`,
Example: ` # Resume reconciliation for an existing Alert Example: ` # Resume reconciliation for an existing Alert
flux resume alert main`, flux resume alert main
# Resume reconciliation for multiple Alerts
flux resume alert main-1 main-2`,
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.AlertKind)), ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.AlertKind)),
RunE: resumeCommand{ RunE: resumeCommand{
apiType: alertType, apiType: alertType,
object: alertAdapter{&notificationv1.Alert{}},
list: &alertListAdapter{&notificationv1.AlertList{}}, list: &alertListAdapter{&notificationv1.AlertList{}},
}.run, }.run,
} }

@ -31,11 +31,13 @@ var resumeHrCmd = &cobra.Command{
Long: `The resume command marks a previously suspended HelmRelease resource for reconciliation and waits for it to Long: `The resume command marks a previously suspended HelmRelease resource for reconciliation and waits for it to
finish the apply.`, finish the apply.`,
Example: ` # Resume reconciliation for an existing Helm release Example: ` # Resume reconciliation for an existing Helm release
flux resume hr podinfo`, flux resume hr podinfo
# Resume reconciliation for multiple Helm releases
flux resume hr podinfo-1 podinfo-2`,
ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)), ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)),
RunE: resumeCommand{ RunE: resumeCommand{
apiType: helmReleaseType, apiType: helmReleaseType,
object: helmReleaseAdapter{&helmv2.HelmRelease{}},
list: helmReleaseListAdapter{&helmv2.HelmReleaseList{}}, list: helmReleaseListAdapter{&helmv2.HelmReleaseList{}},
}.run, }.run,
} }

@ -27,11 +27,13 @@ var resumeImageRepositoryCmd = &cobra.Command{
Short: "Resume a suspended ImageRepository", Short: "Resume a suspended ImageRepository",
Long: `The resume command marks a previously suspended ImageRepository resource for reconciliation and waits for it to finish.`, Long: `The resume command marks a previously suspended ImageRepository resource for reconciliation and waits for it to finish.`,
Example: ` # Resume reconciliation for an existing ImageRepository Example: ` # Resume reconciliation for an existing ImageRepository
flux resume image repository alpine`, flux resume image repository alpine
# Resume reconciliation for multiple ImageRepositories
flux resume image repository alpine-1 alpine-2`,
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)), ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)),
RunE: resumeCommand{ RunE: resumeCommand{
apiType: imageRepositoryType, apiType: imageRepositoryType,
object: imageRepositoryAdapter{&imagev1.ImageRepository{}},
list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}}, list: imageRepositoryListAdapter{&imagev1.ImageRepositoryList{}},
}.run, }.run,
} }

@ -27,11 +27,13 @@ var resumeImageUpdateCmd = &cobra.Command{
Short: "Resume a suspended ImageUpdateAutomation", Short: "Resume a suspended ImageUpdateAutomation",
Long: `The resume command marks a previously suspended ImageUpdateAutomation resource for reconciliation and waits for it to finish.`, 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 Example: ` # Resume reconciliation for an existing ImageUpdateAutomation
flux resume image update latest-images`, flux resume image update latest-images
# Resume reconciliation for multiple ImageUpdateAutomations
flux resume image update latest-images-1 latest-images-2`,
ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)), ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)),
RunE: resumeCommand{ RunE: resumeCommand{
apiType: imageUpdateAutomationType, apiType: imageUpdateAutomationType,
object: imageUpdateAutomationAdapter{&autov1.ImageUpdateAutomation{}},
list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}}, list: imageUpdateAutomationListAdapter{&autov1.ImageUpdateAutomationList{}},
}.run, }.run,
} }

@ -31,11 +31,13 @@ var resumeKsCmd = &cobra.Command{
Long: `The resume command marks a previously suspended Kustomization resource for reconciliation and waits for it to Long: `The resume command marks a previously suspended Kustomization resource for reconciliation and waits for it to
finish the apply.`, finish the apply.`,
Example: ` # Resume reconciliation for an existing Kustomization Example: ` # Resume reconciliation for an existing Kustomization
flux resume ks podinfo`, flux resume ks podinfo
# Resume reconciliation for multiple Kustomizations
flux resume ks podinfo-1 podinfo-2`,
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)), ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
RunE: resumeCommand{ RunE: resumeCommand{
apiType: kustomizationType, apiType: kustomizationType,
object: kustomizationAdapter{&kustomizev1.Kustomization{}},
list: kustomizationListAdapter{&kustomizev1.KustomizationList{}}, list: kustomizationListAdapter{&kustomizev1.KustomizationList{}},
}.run, }.run,
} }

@ -28,11 +28,13 @@ var resumeReceiverCmd = &cobra.Command{
Long: `The resume command marks a previously suspended Receiver resource for reconciliation and waits for it to Long: `The resume command marks a previously suspended Receiver resource for reconciliation and waits for it to
finish the apply.`, finish the apply.`,
Example: ` # Resume reconciliation for an existing Receiver Example: ` # Resume reconciliation for an existing Receiver
flux resume receiver main`, flux resume receiver main
# Resume reconciliation for multiple Receivers
flux resume receiver main-1 main-2`,
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)), ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)),
RunE: resumeCommand{ RunE: resumeCommand{
apiType: receiverType, apiType: receiverType,
object: receiverAdapter{&notificationv1.Receiver{}},
list: receiverListAdapter{&notificationv1.ReceiverList{}}, list: receiverListAdapter{&notificationv1.ReceiverList{}},
}.run, }.run,
} }

@ -27,11 +27,13 @@ var resumeSourceBucketCmd = &cobra.Command{
Short: "Resume a suspended Bucket", Short: "Resume a suspended Bucket",
Long: `The resume command marks a previously suspended Bucket resource for reconciliation and waits for it to finish.`, Long: `The resume command marks a previously suspended Bucket resource for reconciliation and waits for it to finish.`,
Example: ` # Resume reconciliation for an existing Bucket Example: ` # Resume reconciliation for an existing Bucket
flux resume source bucket podinfo`, flux resume source bucket podinfo
# Resume reconciliation for multiple Buckets
flux resume source bucket podinfo-1 podinfo-2`,
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)), ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)),
RunE: resumeCommand{ RunE: resumeCommand{
apiType: bucketType, apiType: bucketType,
object: bucketAdapter{&sourcev1.Bucket{}},
list: bucketListAdapter{&sourcev1.BucketList{}}, list: bucketListAdapter{&sourcev1.BucketList{}},
}.run, }.run,
} }

@ -29,11 +29,13 @@ var resumeSourceHelmChartCmd = &cobra.Command{
Short: "Resume a suspended HelmChart", Short: "Resume a suspended HelmChart",
Long: `The resume command marks a previously suspended HelmChart resource for reconciliation and waits for it to finish.`, Long: `The resume command marks a previously suspended HelmChart resource for reconciliation and waits for it to finish.`,
Example: ` # Resume reconciliation for an existing HelmChart Example: ` # Resume reconciliation for an existing HelmChart
flux resume source chart podinfo`, flux resume source chart podinfo
# Resume reconciliation for multiple HelmCharts
flux resume source chart podinfo-1 podinfo-2`,
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmChartKind)), ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmChartKind)),
RunE: resumeCommand{ RunE: resumeCommand{
apiType: helmChartType, apiType: helmChartType,
object: &helmChartAdapter{&sourcev1.HelmChart{}},
list: &helmChartListAdapter{&sourcev1.HelmChartList{}}, list: &helmChartListAdapter{&sourcev1.HelmChartList{}},
}.run, }.run,
} }

@ -27,11 +27,13 @@ var resumeSourceGitCmd = &cobra.Command{
Short: "Resume a suspended GitRepository", Short: "Resume a suspended GitRepository",
Long: `The resume command marks a previously suspended GitRepository resource for reconciliation and waits for it to finish.`, Long: `The resume command marks a previously suspended GitRepository resource for reconciliation and waits for it to finish.`,
Example: ` # Resume reconciliation for an existing GitRepository Example: ` # Resume reconciliation for an existing GitRepository
flux resume source git podinfo`, flux resume source git podinfo
# Resume reconciliation for multiple GitRepositories
flux resume source git podinfo-1 podinfo-2`,
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)), ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)),
RunE: resumeCommand{ RunE: resumeCommand{
apiType: gitRepositoryType, apiType: gitRepositoryType,
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
list: gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}}, list: gitRepositoryListAdapter{&sourcev1.GitRepositoryList{}},
}.run, }.run,
} }

@ -27,11 +27,13 @@ var resumeSourceHelmCmd = &cobra.Command{
Short: "Resume a suspended HelmRepository", Short: "Resume a suspended HelmRepository",
Long: `The resume command marks a previously suspended HelmRepository resource for reconciliation and waits for it to finish.`, Long: `The resume command marks a previously suspended HelmRepository resource for reconciliation and waits for it to finish.`,
Example: ` # Resume reconciliation for an existing HelmRepository Example: ` # Resume reconciliation for an existing HelmRepository
flux resume source helm bitnami`, flux resume source helm bitnami
# Resume reconciliation for multiple HelmRepositories
flux resume source helm bitnami-1 bitnami-2`,
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)), ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)),
RunE: resumeCommand{ RunE: resumeCommand{
apiType: helmRepositoryType, apiType: helmRepositoryType,
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
list: helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}}, list: helmRepositoryListAdapter{&sourcev1.HelmRepositoryList{}},
}.run, }.run,
} }

@ -27,11 +27,13 @@ var resumeSourceOCIRepositoryCmd = &cobra.Command{
Short: "Resume a suspended OCIRepository", Short: "Resume a suspended OCIRepository",
Long: `The resume command marks a previously suspended OCIRepository resource for reconciliation and waits for it to finish.`, Long: `The resume command marks a previously suspended OCIRepository resource for reconciliation and waits for it to finish.`,
Example: ` # Resume reconciliation for an existing OCIRepository Example: ` # Resume reconciliation for an existing OCIRepository
flux resume source oci podinfo`, flux resume source oci podinfo
# Resume reconciliation for multiple OCIRepositories
flux resume source oci podinfo-1 podinfo-2`,
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.OCIRepositoryKind)), ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.OCIRepositoryKind)),
RunE: resumeCommand{ RunE: resumeCommand{
apiType: ociRepositoryType, apiType: ociRepositoryType,
object: ociRepositoryAdapter{&sourcev1.OCIRepository{}},
list: ociRepositoryListAdapter{&sourcev1.OCIRepositoryList{}}, list: ociRepositoryListAdapter{&sourcev1.OCIRepositoryList{}},
}.run, }.run,
} }

@ -18,6 +18,7 @@ package main
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -75,22 +76,53 @@ func (suspend suspendCommand) run(cmd *cobra.Command, args []string) error {
return err return err
} }
var listOpts []client.ListOption if len(args) < 1 && suspendArgs.all {
listOpts = append(listOpts, client.InNamespace(*kubeconfigArgs.Namespace)) listOpts := []client.ListOption{
if len(args) > 0 { client.InNamespace(*kubeconfigArgs.Namespace),
listOpts = append(listOpts, client.MatchingFields{ }
"metadata.name": args[0],
}) if err := suspend.patch(ctx, kubeClient, listOpts); err != nil {
return err
}
return nil
} }
err = kubeClient.List(ctx, suspend.list.asClientList(), listOpts...) processed := make(map[string]struct{}, len(args))
if err != nil { for _, arg := range args {
if _, has := processed[arg]; has {
continue // skip object that user might have provided more than once
}
processed[arg] = struct{}{}
listOpts := []client.ListOption{
client.InNamespace(*kubeconfigArgs.Namespace),
client.MatchingFields{
"metadata.name": arg,
},
}
if err := suspend.patch(ctx, kubeClient, listOpts); err != nil {
if err == ErrNoObjectsFound {
logger.Failuref("%s %s not found in %s namespace", suspend.kind, arg, *kubeconfigArgs.Namespace)
} else {
logger.Failuref("failed suspending %s %s in %s namespace: %s", suspend.kind, arg, *kubeconfigArgs.Namespace, err.Error())
}
}
}
return nil
}
var ErrNoObjectsFound = errors.New("no objects found")
func (suspend suspendCommand) patch(ctx context.Context, kubeClient client.WithWatch, listOpts []client.ListOption) error {
if err := kubeClient.List(ctx, suspend.list.asClientList(), listOpts...); err != nil {
return err return err
} }
if suspend.list.len() == 0 { if suspend.list.len() == 0 {
logger.Failuref("no %s objects found in %s namespace", suspend.kind, *kubeconfigArgs.Namespace) return ErrNoObjectsFound
return nil
} }
for i := 0; i < suspend.list.len(); i++ { for i := 0; i < suspend.list.len(); i++ {
@ -102,8 +134,8 @@ func (suspend suspendCommand) run(cmd *cobra.Command, args []string) error {
if err := kubeClient.Patch(ctx, obj.asClientObject(), patch); err != nil { if err := kubeClient.Patch(ctx, obj.asClientObject(), patch); err != nil {
return err return err
} }
logger.Successf("%s suspended", suspend.humanKind)
logger.Successf("%s suspended", suspend.humanKind)
} }
return nil return nil

@ -27,7 +27,10 @@ var suspendAlertCmd = &cobra.Command{
Short: "Suspend reconciliation of Alert", Short: "Suspend reconciliation of Alert",
Long: `The suspend command disables the reconciliation of a Alert resource.`, Long: `The suspend command disables the reconciliation of a Alert resource.`,
Example: ` # Suspend reconciliation for an existing Alert Example: ` # Suspend reconciliation for an existing Alert
flux suspend alert main`, flux suspend alert main
# Suspend reconciliation for multiple Alerts
flux suspend alert main-1 main-2`,
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.AlertKind)), ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.AlertKind)),
RunE: suspendCommand{ RunE: suspendCommand{
apiType: alertType, apiType: alertType,

@ -28,7 +28,10 @@ var suspendHrCmd = &cobra.Command{
Short: "Suspend reconciliation of HelmRelease", Short: "Suspend reconciliation of HelmRelease",
Long: `The suspend command disables the reconciliation of a HelmRelease resource.`, Long: `The suspend command disables the reconciliation of a HelmRelease resource.`,
Example: ` # Suspend reconciliation for an existing Helm release Example: ` # Suspend reconciliation for an existing Helm release
flux suspend hr podinfo`, flux suspend hr podinfo
# Suspend reconciliation for multiple Helm releases
flux suspend hr podinfo-1 podinfo-2 `,
ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)), ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)),
RunE: suspendCommand{ RunE: suspendCommand{
apiType: helmReleaseType, apiType: helmReleaseType,

@ -27,7 +27,10 @@ var suspendImageRepositoryCmd = &cobra.Command{
Short: "Suspend reconciliation of an ImageRepository", Short: "Suspend reconciliation of an ImageRepository",
Long: `The suspend image repository command disables the reconciliation of a ImageRepository resource.`, Long: `The suspend image repository command disables the reconciliation of a ImageRepository resource.`,
Example: ` # Suspend reconciliation for an existing ImageRepository Example: ` # Suspend reconciliation for an existing ImageRepository
flux suspend image repository alpine`, flux suspend image repository alpine
# Suspend reconciliation for multiple ImageRepositories
flux suspend image repository alpine-1 alpine-2`,
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)), ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)),
RunE: suspendCommand{ RunE: suspendCommand{
apiType: imageRepositoryType, apiType: imageRepositoryType,

@ -27,7 +27,10 @@ var suspendImageUpdateCmd = &cobra.Command{
Short: "Suspend reconciliation of an ImageUpdateAutomation", Short: "Suspend reconciliation of an ImageUpdateAutomation",
Long: `The suspend image update command disables the reconciliation of a ImageUpdateAutomation resource.`, Long: `The suspend image update command disables the reconciliation of a ImageUpdateAutomation resource.`,
Example: ` # Suspend reconciliation for an existing ImageUpdateAutomation Example: ` # Suspend reconciliation for an existing ImageUpdateAutomation
flux suspend image update latest-images`, flux suspend image update latest-images
# Suspend reconciliation for multiple ImageUpdateAutomations
flux suspend image update latest-images-1 latest-images-2`,
ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)), ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)),
RunE: suspendCommand{ RunE: suspendCommand{
apiType: imageUpdateAutomationType, apiType: imageUpdateAutomationType,

@ -28,7 +28,10 @@ var suspendKsCmd = &cobra.Command{
Short: "Suspend reconciliation of Kustomization", Short: "Suspend reconciliation of Kustomization",
Long: `The suspend command disables the reconciliation of a Kustomization resource.`, Long: `The suspend command disables the reconciliation of a Kustomization resource.`,
Example: ` # Suspend reconciliation for an existing Kustomization Example: ` # Suspend reconciliation for an existing Kustomization
flux suspend ks podinfo`, flux suspend ks podinfo
# Suspend reconciliation for multiple Kustomizations
flux suspend ks podinfo-1 podinfo-2`,
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)), ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
RunE: suspendCommand{ RunE: suspendCommand{
apiType: kustomizationType, apiType: kustomizationType,

@ -27,7 +27,10 @@ var suspendReceiverCmd = &cobra.Command{
Short: "Suspend reconciliation of Receiver", Short: "Suspend reconciliation of Receiver",
Long: `The suspend command disables the reconciliation of a Receiver resource.`, Long: `The suspend command disables the reconciliation of a Receiver resource.`,
Example: ` # Suspend reconciliation for an existing Receiver Example: ` # Suspend reconciliation for an existing Receiver
flux suspend receiver main`, flux suspend receiver main
# Suspend reconciliation for multiple Receivers
flux suspend receiver main-1 main-2`,
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)), ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)),
RunE: suspendCommand{ RunE: suspendCommand{
apiType: receiverType, apiType: receiverType,

@ -27,7 +27,10 @@ var suspendSourceBucketCmd = &cobra.Command{
Short: "Suspend reconciliation of a Bucket", Short: "Suspend reconciliation of a Bucket",
Long: `The suspend command disables the reconciliation of a Bucket resource.`, Long: `The suspend command disables the reconciliation of a Bucket resource.`,
Example: ` # Suspend reconciliation for an existing Bucket Example: ` # Suspend reconciliation for an existing Bucket
flux suspend source bucket podinfo`, flux suspend source bucket podinfo
# Suspend reconciliation for multiple Buckets
flux suspend source bucket podinfo-1 podinfo-2`,
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)), ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)),
RunE: suspendCommand{ RunE: suspendCommand{
apiType: bucketType, apiType: bucketType,

@ -27,7 +27,10 @@ var suspendSourceHelmChartCmd = &cobra.Command{
Short: "Suspend reconciliation of a HelmChart", Short: "Suspend reconciliation of a HelmChart",
Long: `The suspend command disables the reconciliation of a HelmChart resource.`, Long: `The suspend command disables the reconciliation of a HelmChart resource.`,
Example: ` # Suspend reconciliation for an existing HelmChart Example: ` # Suspend reconciliation for an existing HelmChart
flux suspend source chart podinfo`, flux suspend source chart podinfo
# Suspend reconciliation for multiple HelmCharts
flux suspend source chart podinfo-1 podinfo-2`,
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmChartKind)), ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmChartKind)),
RunE: suspendCommand{ RunE: suspendCommand{
apiType: helmChartType, apiType: helmChartType,

@ -27,7 +27,10 @@ var suspendSourceGitCmd = &cobra.Command{
Short: "Suspend reconciliation of a GitRepository", Short: "Suspend reconciliation of a GitRepository",
Long: `The suspend command disables the reconciliation of a GitRepository resource.`, Long: `The suspend command disables the reconciliation of a GitRepository resource.`,
Example: ` # Suspend reconciliation for an existing GitRepository Example: ` # Suspend reconciliation for an existing GitRepository
flux suspend source git podinfo`, flux suspend source git podinfo
# Suspend reconciliation for multiple GitRepositories
flux suspend source git podinfo-1 podinfo-2`,
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)), ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)),
RunE: suspendCommand{ RunE: suspendCommand{
apiType: gitRepositoryType, apiType: gitRepositoryType,

@ -27,7 +27,10 @@ var suspendSourceHelmCmd = &cobra.Command{
Short: "Suspend reconciliation of a HelmRepository", Short: "Suspend reconciliation of a HelmRepository",
Long: `The suspend command disables the reconciliation of a HelmRepository resource.`, Long: `The suspend command disables the reconciliation of a HelmRepository resource.`,
Example: ` # Suspend reconciliation for an existing HelmRepository Example: ` # Suspend reconciliation for an existing HelmRepository
flux suspend source helm bitnami`, flux suspend source helm bitnami
# Suspend reconciliation for multiple HelmRepositories
flux suspend source helm bitnami-1 bitnami-2 `,
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)), ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)),
RunE: suspendCommand{ RunE: suspendCommand{
apiType: helmRepositoryType, apiType: helmRepositoryType,

@ -27,7 +27,10 @@ var suspendSourceOCIRepositoryCmd = &cobra.Command{
Short: "Suspend reconciliation of an OCIRepository", Short: "Suspend reconciliation of an OCIRepository",
Long: `The suspend command disables the reconciliation of an OCIRepository resource.`, Long: `The suspend command disables the reconciliation of an OCIRepository resource.`,
Example: ` # Suspend reconciliation for an existing OCIRepository Example: ` # Suspend reconciliation for an existing OCIRepository
flux suspend source oci podinfo`, flux suspend source oci podinfo
# Suspend reconciliation for multiple OCIRepositories
flux suspend source oci podinfo-1 podinfo-2`,
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.OCIRepositoryKind)), ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.OCIRepositoryKind)),
RunE: suspendCommand{ RunE: suspendCommand{
apiType: ociRepositoryType, apiType: ociRepositoryType,

@ -1,5 +1,5 @@
► resuming helmrelease thrfg in {{ .ns }} namespace ► resuming helmrelease thrfg in {{ .ns }} namespace
✔ helmrelease resumed ✔ helmrelease resumed
◎ waiting for HelmRelease reconciliation ◎ waiting for HelmRelease reconciliation
✔ HelmRelease reconciliation completed ✔ HelmRelease thrfg reconciliation completed
✔ applied revision 6.3.5 ✔ applied revision 6.3.5

@ -1,5 +1,5 @@
► resuming kustomization tkfg in {{ .ns }} namespace ► resuming kustomization tkfg in {{ .ns }} namespace
✔ kustomization resumed ✔ kustomization resumed
◎ waiting for Kustomization reconciliation ◎ waiting for Kustomization reconciliation
✔ Kustomization reconciliation completed ✔ Kustomization tkfg reconciliation completed
✔ applied revision 6.3.5@sha1:67e2c98a60dc92283531412a9e604dd4bae005a9 ✔ applied revision 6.3.5@sha1:67e2c98a60dc92283531412a9e604dd4bae005a9

@ -0,0 +1,2 @@
► resuming kustomization tkfg in {{ .ns }} namespace
✔ kustomization resumed

@ -0,0 +1,6 @@
► resuming kustomization tkfg in {{ .ns }} namespace
✔ kustomization resumed
✗ no Kustomization objects found in {{ .ns }} namespace
◎ waiting for Kustomization reconciliation
✔ Kustomization tkfg reconciliation completed
✔ applied revision 6.3.5@sha1:67e2c98a60dc92283531412a9e604dd4bae005a9

@ -0,0 +1,4 @@
► suspending kustomization tkfg in {{ .ns }} namespace
✔ kustomization suspended
✗ Kustomization foo not found in {{ .ns }} namespace
✗ Kustomization bar not found in {{ .ns }} namespace

@ -1,5 +1,5 @@
► resuming source oci thrfg in {{ .ns }} namespace ► resuming source oci thrfg in {{ .ns }} namespace
✔ source oci resumed ✔ source oci resumed
◎ waiting for OCIRepository reconciliation ◎ waiting for OCIRepository reconciliation
✔ OCIRepository reconciliation completed ✔ OCIRepository thrfg reconciliation completed
✔ fetched revision 6.3.5@sha256:6c959c51ccbb952e5fe4737563338a0aaf975675dcf812912cf09e5463181871 ✔ fetched revision 6.3.5@sha256:6c959c51ccbb952e5fe4737563338a0aaf975675dcf812912cf09e5463181871

Loading…
Cancel
Save