Refactor reconcile and resume cmd for alert and receiver

Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
pull/1224/head
Somtochi Onyekwere 4 years ago
parent 6d4e37ccb2
commit 4204ec1d43

@ -19,6 +19,7 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/notification-controller/api/v1beta1"
"time" "time"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
@ -97,12 +98,23 @@ func (reconcile reconcileCommand) run(cmd *cobra.Command, args []string) error {
} }
logger.Successf("%s annotated", reconcile.kind) logger.Successf("%s annotated", reconcile.kind)
if reconcile.kind == v1beta1.AlertKind || reconcile.kind == v1beta1.ReceiverKind {
if err = wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isReconcileReady(ctx, kubeClient, namespacedName, reconcile.object)); err != nil {
return err
}
logger.Successf(reconcile.object.successMessage())
return nil
}
lastHandledReconcileAt := reconcile.object.lastHandledReconcileRequest() lastHandledReconcileAt := reconcile.object.lastHandledReconcileRequest()
logger.Waitingf("waiting for %s reconciliation", reconcile.kind) logger.Waitingf("waiting for %s reconciliation", reconcile.kind)
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout, if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil { reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil {
return err return err
} }
logger.Successf("%s reconciliation completed", reconcile.kind) logger.Successf("%s reconciliation completed", reconcile.kind)
if apimeta.IsStatusConditionFalse(*reconcile.object.GetStatusConditions(), meta.ReadyCondition) { if apimeta.IsStatusConditionFalse(*reconcile.object.GetStatusConditions(), meta.ReadyCondition) {
@ -140,3 +152,23 @@ func requestReconciliation(ctx context.Context, kubeClient client.Client,
return kubeClient.Update(ctx, obj.asClientObject()) return kubeClient.Update(ctx, obj.asClientObject())
}) })
} }
func isReconcileReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, obj reconcilable) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, obj.asClientObject())
if err != nil {
return false, err
}
if c := apimeta.FindStatusCondition(*obj.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,18 +17,8 @@ limitations under the License.
package main package main
import ( import (
"context"
"fmt"
"time"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/spf13/cobra"
) )
var reconcileAlertCmd = &cobra.Command{ var reconcileAlertCmd = &cobra.Command{
@ -37,61 +27,16 @@ var reconcileAlertCmd = &cobra.Command{
Long: `The reconcile alert command triggers a reconciliation of an Alert resource and waits for it to finish.`, Long: `The reconcile alert command triggers a reconciliation of an Alert resource and waits for it to finish.`,
Example: ` # Trigger a reconciliation for an existing alert Example: ` # Trigger a reconciliation for an existing alert
flux reconcile alert main`, flux reconcile alert main`,
RunE: reconcileAlertCmdRun, RunE: reconcileCommand{
apiType: alertType,
object: alertAdapter{&notificationv1.Alert{}},
}.run,
} }
func init() { func init() {
reconcileCmd.AddCommand(reconcileAlertCmd) reconcileCmd.AddCommand(reconcileAlertCmd)
} }
func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error { func (obj alertAdapter) lastHandledReconcileRequest() string {
if len(args) < 1 { return ""
return fmt.Errorf("Alert name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
if alert.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
logger.Actionf("annotating Alert %s in %s namespace", name, rootArgs.namespace)
if alert.Annotations == nil {
alert.Annotations = map[string]string{
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
alert.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
}
if err := kubeClient.Update(ctx, &alert); err != nil {
return err
}
logger.Successf("Alert annotated")
logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("Alert reconciliation completed")
return nil
} }

@ -17,23 +17,9 @@ limitations under the License.
package main package main
import ( import (
"context"
"fmt"
"time"
"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"
"github.com/fluxcd/pkg/apis/meta"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
) )
var reconcileHrCmd = &cobra.Command{ var reconcileHrCmd = &cobra.Command{
@ -47,7 +33,10 @@ The reconcile kustomization command triggers a reconciliation of a HelmRelease r
# Trigger a reconciliation of the HelmRelease's source and apply changes # Trigger a reconciliation of the HelmRelease's source and apply changes
flux reconcile hr podinfo --with-source`, flux reconcile hr podinfo --with-source`,
RunE: reconcileHrCmdRun, RunE: reconcileWithSourceCommand{
apiType: helmReleaseType,
object: helmReleaseAdapter{&helmv2.HelmRelease{}},
}.run,
} }
type reconcileHelmReleaseFlags struct { type reconcileHelmReleaseFlags struct {
@ -62,117 +51,33 @@ func init() {
reconcileCmd.AddCommand(reconcileHrCmd) reconcileCmd.AddCommand(reconcileHrCmd)
} }
func reconcileHrCmdRun(cmd *cobra.Command, args []string) error { func (obj helmReleaseAdapter) lastHandledReconcileRequest() string {
if len(args) < 1 { return obj.Status.GetLastHandledReconcileRequest()
return fmt.Errorf("HelmRelease name is required") }
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var helmRelease helmv2.HelmRelease
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return err
}
if helmRelease.Spec.Suspend { func (obj helmReleaseAdapter) reconcileSource() bool {
return fmt.Errorf("resource is suspended") return rhrArgs.syncHrWithSource
} }
if rhrArgs.syncHrWithSource { func (obj helmReleaseAdapter) getSource() (reconcileCommand, string) {
nsCopy := rootArgs.namespace var cmd reconcileCommand
if helmRelease.Spec.Chart.Spec.SourceRef.Namespace != "" { switch obj.Spec.Chart.Spec.SourceRef.Kind {
rootArgs.namespace = helmRelease.Spec.Chart.Spec.SourceRef.Namespace case sourcev1.HelmRepositoryKind:
cmd = reconcileCommand{
apiType: helmRepositoryType,
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
} }
switch helmRelease.Spec.Chart.Spec.SourceRef.Kind { case sourcev1.GitRepositoryKind:
case sourcev1.HelmRepositoryKind: cmd = reconcileCommand{
err = reconcileCommand{ apiType: gitRepositoryType,
apiType: helmRepositoryType, object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
object: helmRepositoryAdapter{&sourcev1.HelmRepository{}},
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
case sourcev1.GitRepositoryKind:
err = reconcileCommand{
apiType: gitRepositoryType,
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
case sourcev1.BucketKind:
err = reconcileCommand{
apiType: bucketType,
object: bucketAdapter{&sourcev1.Bucket{}},
}.run(nil, []string{helmRelease.Spec.Chart.Spec.SourceRef.Name})
} }
if err != nil { case sourcev1.BucketKind:
return err cmd = reconcileCommand{
apiType: bucketType,
object: bucketAdapter{&sourcev1.Bucket{}},
} }
rootArgs.namespace = nsCopy
} }
lastHandledReconcileAt := helmRelease.Status.LastHandledReconcileAt return cmd, obj.Spec.Chart.Spec.SourceRef.Name
logger.Actionf("annotating HelmRelease %s in %s namespace", name, rootArgs.namespace)
if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName, &helmRelease); err != nil {
return err
}
logger.Successf("HelmRelease annotated")
logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
helmReleaseReconciliationHandled(ctx, kubeClient, namespacedName, &helmRelease, lastHandledReconcileAt),
); err != nil {
return err
}
logger.Successf("HelmRelease reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return err
}
if c := apimeta.FindStatusCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case metav1.ConditionFalse:
return fmt.Errorf("HelmRelease reconciliation failed: %s", c.Message)
default:
logger.Successf("reconciled revision %s", helmRelease.Status.LastAppliedRevision)
}
}
return nil
}
func helmReleaseReconciliationHandled(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, helmRelease)
if err != nil {
return false, err
}
return helmRelease.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
}
}
func requestHelmReleaseReconciliation(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, helmRelease); err != nil {
return err
}
if helmRelease.Annotations == nil {
helmRelease.Annotations = map[string]string{
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano),
}
} else {
helmRelease.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
}
return kubeClient.Update(ctx, helmRelease)
})
} }

@ -17,22 +17,9 @@ limitations under the License.
package main package main
import ( import (
"context"
"fmt"
"time"
apimeta "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
"github.com/spf13/cobra"
) )
var reconcileKsCmd = &cobra.Command{ var reconcileKsCmd = &cobra.Command{
@ -46,7 +33,10 @@ The reconcile kustomization command triggers a reconciliation of a Kustomization
# Trigger a sync of the Kustomization's source and apply changes # Trigger a sync of the Kustomization's source and apply changes
flux reconcile kustomization podinfo --with-source`, flux reconcile kustomization podinfo --with-source`,
RunE: reconcileKsCmdRun, RunE: reconcileWithSourceCommand{
apiType: kustomizationType,
object: kustomizationAdapter{&kustomizev1.Kustomization{}},
}.run,
} }
type reconcileKsFlags struct { type reconcileKsFlags struct {
@ -61,104 +51,28 @@ func init() {
reconcileCmd.AddCommand(reconcileKsCmd) reconcileCmd.AddCommand(reconcileKsCmd)
} }
func reconcileKsCmdRun(cmd *cobra.Command, args []string) error { func (obj kustomizationAdapter) lastHandledReconcileRequest() string {
if len(args) < 1 { return obj.Status.GetLastHandledReconcileRequest()
return fmt.Errorf("Kustomization name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var kustomization kustomizev1.Kustomization
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return err
}
if kustomization.Spec.Suspend {
return fmt.Errorf("resource is suspended")
}
if rksArgs.syncKsWithSource {
nsCopy := rootArgs.namespace
if kustomization.Spec.SourceRef.Namespace != "" {
rootArgs.namespace = kustomization.Spec.SourceRef.Namespace
}
switch kustomization.Spec.SourceRef.Kind {
case sourcev1.GitRepositoryKind:
err = reconcileCommand{
apiType: gitRepositoryType,
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
}.run(nil, []string{kustomization.Spec.SourceRef.Name})
case sourcev1.BucketKind:
err = reconcileCommand{
apiType: bucketType,
object: bucketAdapter{&sourcev1.Bucket{}},
}.run(nil, []string{kustomization.Spec.SourceRef.Name})
}
if err != nil {
return err
}
rootArgs.namespace = nsCopy
}
lastHandledReconcileAt := kustomization.Status.LastHandledReconcileAt
logger.Actionf("annotating Kustomization %s in %s namespace", name, rootArgs.namespace)
if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName, &kustomization); err != nil {
return err
}
logger.Successf("Kustomization annotated")
logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate(
rootArgs.pollInterval, rootArgs.timeout,
kustomizeReconciliationHandled(ctx, kubeClient, namespacedName, &kustomization, lastHandledReconcileAt),
); err != nil {
return err
}
logger.Successf("Kustomization reconciliation completed")
if apimeta.IsStatusConditionFalse(kustomization.Status.Conditions, meta.ReadyCondition) {
return fmt.Errorf("Kustomization reconciliation failed")
}
logger.Successf("reconciled revision %s", kustomization.Status.LastAppliedRevision)
return nil
} }
func kustomizeReconciliationHandled(ctx context.Context, kubeClient client.Client, func (obj kustomizationAdapter) reconcileSource() bool {
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization, lastHandledReconcileAt string) wait.ConditionFunc { return rksArgs.syncKsWithSource
return func() (bool, error) {
err := kubeClient.Get(ctx, namespacedName, kustomization)
if err != nil {
return false, err
}
return kustomization.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
}
} }
func requestKustomizeReconciliation(ctx context.Context, kubeClient client.Client, func (obj kustomizationAdapter) getSource() (reconcileCommand, string) {
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) error { var cmd reconcileCommand
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { switch obj.Spec.SourceRef.Kind {
if err := kubeClient.Get(ctx, namespacedName, kustomization); err != nil { case sourcev1.GitRepositoryKind:
return err cmd = reconcileCommand{
apiType: gitRepositoryType,
object: gitRepositoryAdapter{&sourcev1.GitRepository{}},
} }
if kustomization.Annotations == nil { case sourcev1.BucketKind:
kustomization.Annotations = map[string]string{ cmd = reconcileCommand{
meta.ReconcileRequestAnnotation: time.Now().Format(time.RFC3339Nano), apiType: bucketType,
} object: bucketAdapter{&sourcev1.Bucket{}},
} else {
kustomization.Annotations[meta.ReconcileRequestAnnotation] = time.Now().Format(time.RFC3339Nano)
} }
return kubeClient.Update(ctx, kustomization) }
})
return cmd, obj.Spec.SourceRef.Name
} }

@ -0,0 +1,88 @@
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"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
)
type reconcileWithSource interface {
adapter
reconcilable
reconcileSource() bool
getSource() (reconcileCommand, string)
}
type reconcileWithSourceCommand struct {
apiType
object reconcileWithSource
}
func (reconcile reconcileWithSourceCommand) run(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("%s name is required", reconcile.kind)
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, reconcile.object.asClientObject())
if err != nil {
return err
}
if reconcile.object.isSuspended() {
return fmt.Errorf("resource is suspended")
}
if reconcile.object.reconcileSource() {
nsCopy := rootArgs.namespace
objectNs := reconcile.object.asClientObject().GetNamespace()
if objectNs != "" {
rootArgs.namespace = reconcile.object.asClientObject().GetNamespace()
}
reconcileCmd, sourceName := reconcile.object.getSource()
err := reconcileCmd.run(nil, []string{sourceName})
if err != nil {
return err
}
rootArgs.namespace = nsCopy
}
logger.Actionf("annotating %s %s in %s namespace", reconcile.kind, name, rootArgs.namespace)
if err := requestReconciliation(ctx, kubeClient, namespacedName, reconcile.object); err != nil {
return err
}
logger.Successf("%s annotated", reconcile.kind)
lastHandledReconcileAt := reconcile.object.lastHandledReconcileRequest()
logger.Waitingf("waiting for %s reconciliation", reconcile.kind)
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
reconciliationHandled(ctx, kubeClient, namespacedName, reconcile.object, lastHandledReconcileAt)); err != nil {
return err
}
logger.Successf("%s reconciliation completed", reconcile.kind)
if apimeta.IsStatusConditionFalse(*reconcile.object.GetStatusConditions(), meta.ReadyCondition) {
return fmt.Errorf("%s reconciliation failed", reconcile.kind)
}
logger.Successf(reconcile.object.successMessage())
return nil
}

@ -17,20 +17,9 @@ limitations under the License.
package main package main
import ( import (
"context" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"fmt"
"github.com/fluxcd/flux2/internal/utils"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra" "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"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
) )
var resumeAlertCmd = &cobra.Command{ var resumeAlertCmd = &cobra.Command{
@ -40,72 +29,24 @@ var resumeAlertCmd = &cobra.Command{
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`,
RunE: resumeAlertCmdRun, RunE: resumeCommand{
apiType: alertType,
object: alertAdapter{&notificationv1.Alert{}},
}.run,
} }
func init() { func init() {
resumeCmd.AddCommand(resumeAlertCmd) resumeCmd.AddCommand(resumeAlertCmd)
} }
func resumeAlertCmdRun(cmd *cobra.Command, args []string) error { func (obj alertAdapter) getObservedGeneration() int64 {
if len(args) < 1 { return obj.Alert.Status.ObservedGeneration
return fmt.Errorf("Alert name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
logger.Actionf("resuming Alert %s in %s namespace", name, rootArgs.namespace)
alert.Spec.Suspend = false
if err := kubeClient.Update(ctx, &alert); err != nil {
return err
}
logger.Successf("Alert resumed")
logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isAlertResumed(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err
}
logger.Successf("Alert reconciliation completed")
return nil
} }
func isAlertResumed(ctx context.Context, kubeClient client.Client, func (obj alertAdapter) setUnsuspended() {
namespacedName types.NamespacedName, alert *notificationv1.Alert) wait.ConditionFunc { obj.Alert.Spec.Suspend = false
return func() (bool, error) { }
err := kubeClient.Get(ctx, namespacedName, alert)
if err != nil {
return false, err
}
if c := apimeta.FindStatusCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil { func (obj alertAdapter) successMessage() string {
switch c.Status { return "Alert reconciliation completed"
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
if c.Reason == meta.SuspendedReason {
return false, nil
}
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
} }

@ -17,20 +17,8 @@ limitations under the License.
package main package main
import ( 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"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/spf13/cobra"
) )
var resumeReceiverCmd = &cobra.Command{ var resumeReceiverCmd = &cobra.Command{
@ -40,73 +28,24 @@ var resumeReceiverCmd = &cobra.Command{
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`,
RunE: resumeReceiverCmdRun, RunE: resumeCommand{
apiType: receiverType,
object: receiverAdapter{&notificationv1.Receiver{}},
}.run,
} }
func init() { func init() {
resumeCmd.AddCommand(resumeReceiverCmd) resumeCmd.AddCommand(resumeReceiverCmd)
} }
func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error { func (obj receiverAdapter) getObservedGeneration() int64 {
if len(args) < 1 { return obj.Receiver.Status.ObservedGeneration
return fmt.Errorf("Receiver name is required")
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
logger.Actionf("resuming Receiver %s in %s namespace", name, rootArgs.namespace)
receiver.Spec.Suspend = false
if err := kubeClient.Update(ctx, &receiver); err != nil {
return err
}
logger.Successf("Receiver resumed")
logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
isReceiverResumed(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err
}
logger.Successf("Receiver reconciliation completed")
return nil
} }
func isReceiverResumed(ctx context.Context, kubeClient client.Client, func (obj receiverAdapter) setUnsuspended() {
namespacedName types.NamespacedName, receiver *notificationv1.Receiver) wait.ConditionFunc { obj.Receiver.Spec.Suspend = false
return func() (bool, error) { }
err := kubeClient.Get(ctx, namespacedName, receiver)
if err != nil {
return false, err
}
if c := apimeta.FindStatusCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil { func (obj receiverAdapter) successMessage() string {
switch c.Status { return "Receiver reconciliation completed"
case metav1.ConditionTrue:
return true, nil
case metav1.ConditionFalse:
if c.Reason == meta.SuspendedReason {
return false, nil
}
return false, fmt.Errorf(c.Message)
}
}
return false, nil
}
} }

@ -17,14 +17,8 @@ limitations under the License.
package main package main
import ( import (
"context"
"fmt"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/spf13/cobra"
) )
var suspendAlertCmd = &cobra.Command{ var suspendAlertCmd = &cobra.Command{
@ -33,43 +27,20 @@ var suspendAlertCmd = &cobra.Command{
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`,
RunE: suspendAlertCmdRun, RunE: suspendCommand{
apiType: alertType,
object: &alertAdapter{&notificationv1.Alert{}},
}.run,
} }
func init() { func init() {
suspendCmd.AddCommand(suspendAlertCmd) suspendCmd.AddCommand(suspendAlertCmd)
} }
func suspendAlertCmdRun(cmd *cobra.Command, args []string) error { func (obj alertAdapter) isSuspended() bool {
if len(args) < 1 { return obj.Alert.Spec.Suspend
return fmt.Errorf("Alert name is required") }
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil {
return err
}
logger.Actionf("suspending Alert %s in %s namespace", name, rootArgs.namespace)
alert.Spec.Suspend = true
if err := kubeClient.Update(ctx, &alert); err != nil {
return err
}
logger.Successf("Alert suspended")
return nil func (obj alertAdapter) setSuspended() {
obj.Alert.Spec.Suspend = true
} }

@ -17,14 +17,8 @@ limitations under the License.
package main package main
import ( import (
"context"
"fmt"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/flux2/internal/utils"
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1" notificationv1 "github.com/fluxcd/notification-controller/api/v1beta1"
"github.com/spf13/cobra"
) )
var suspendReceiverCmd = &cobra.Command{ var suspendReceiverCmd = &cobra.Command{
@ -33,43 +27,20 @@ var suspendReceiverCmd = &cobra.Command{
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`,
RunE: suspendReceiverCmdRun, RunE: suspendCommand{
apiType: receiverType,
object: &receiverAdapter{&notificationv1.Receiver{}},
}.run,
} }
func init() { func init() {
suspendCmd.AddCommand(suspendReceiverCmd) suspendCmd.AddCommand(suspendReceiverCmd)
} }
func suspendReceiverCmdRun(cmd *cobra.Command, args []string) error { func (obj receiverAdapter) isSuspended() bool {
if len(args) < 1 { return obj.Receiver.Spec.Suspend
return fmt.Errorf("Receiver name is required") }
}
name := args[0]
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
}
namespacedName := types.NamespacedName{
Namespace: rootArgs.namespace,
Name: name,
}
var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return err
}
logger.Actionf("suspending Receiver %s in %s namespace", name, rootArgs.namespace)
receiver.Spec.Suspend = true
if err := kubeClient.Update(ctx, &receiver); err != nil {
return err
}
logger.Successf("Receiver suspended")
return nil func (obj receiverAdapter) setSuspended() {
obj.Receiver.Spec.Suspend = true
} }

Loading…
Cancel
Save