Refactor create, reconcile and resume cmds

* Take ObservedGeneration into account in readiness checks where
  applicable
* Reduce amount of code (and duplicate GETs) by working with pointers
  where possible
* Improve logged messages to properly take resource names into account
  and better describe processes
pull/352/head
Hidde Beydals 4 years ago
parent 0d1600275d
commit 19918cd342

@ -246,13 +246,15 @@ func applySyncManifests(ctx context.Context, kubeClient client.Client, name, nam
logger.Waitingf("waiting for cluster sync") logger.Waitingf("waiting for cluster sync")
var gitRepository sourcev1.GitRepository
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil { isGitRepositoryReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &gitRepository)); err != nil {
return err return err
} }
var kustomization kustomizev1.Kustomization
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil { isKustomizationReady(ctx, kubeClient, types.NamespacedName{Name: name, Namespace: namespace}, &kustomization)); err != nil {
return err return err
} }

@ -61,7 +61,7 @@ func init() {
func createAlertCmdRun(cmd *cobra.Command, args []string) error { func createAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("alert name is required") return fmt.Errorf("Alert name is required")
} }
name := args[0] name := args[0]
@ -92,7 +92,7 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
} }
if !export { if !export {
logger.Generatef("generating alert") logger.Generatef("generating Alert")
} }
alert := notificationv1.Alert{ alert := notificationv1.Alert{
@ -123,23 +123,23 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
logger.Actionf("applying alert") logger.Actionf("applying Alert")
if err := upsertAlert(ctx, kubeClient, alert); err != nil { namespacedName, err := upsertAlert(ctx, kubeClient, &alert)
if err != nil {
return err return err
} }
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isAlertReady(ctx, kubeClient, name, namespace)); err != nil { isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err return err
} }
logger.Successf("Alert %s is ready", name)
logger.Successf("alert %s is ready", name)
return nil return nil
} }
func upsertAlert(ctx context.Context, kubeClient client.Client, alert notificationv1.Alert) error { func upsertAlert(ctx context.Context, kubeClient client.Client,
alert *notificationv1.Alert) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: alert.GetNamespace(), Namespace: alert.GetNamespace(),
Name: alert.GetName(), Name: alert.GetName(),
@ -149,35 +149,30 @@ func upsertAlert(ctx context.Context, kubeClient client.Client, alert notificati
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &alert); err != nil { if err := kubeClient.Create(ctx, alert); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("alert created") logger.Successf("Alert created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = alert.Labels existing.Labels = alert.Labels
existing.Spec = alert.Spec existing.Spec = alert.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
alert = &existing
logger.Successf("alert updated") logger.Successf("Alert updated")
return nil return namespacedName, nil
} }
func isAlertReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isAlertReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, alert *notificationv1.Alert) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var alert notificationv1.Alert err := kubeClient.Get(ctx, namespacedName, alert)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alert)
if err != nil { if err != nil {
return false, err return false, err
} }

@ -71,12 +71,12 @@ func init() {
func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error { func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("provider name is required") return fmt.Errorf("Provider name is required")
} }
name := args[0] name := args[0]
if apType == "" { if apType == "" {
return fmt.Errorf("type is required") return fmt.Errorf("Provider type is required")
} }
sourceLabels, err := parseLabels() sourceLabels, err := parseLabels()
@ -85,10 +85,10 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
} }
if !export { if !export {
logger.Generatef("generating provider") logger.Generatef("generating Provider")
} }
alertProvider := notificationv1.Provider{ provider := notificationv1.Provider{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
@ -103,13 +103,13 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
} }
if apSecretRef != "" { if apSecretRef != "" {
alertProvider.Spec.SecretRef = &corev1.LocalObjectReference{ provider.Spec.SecretRef = &corev1.LocalObjectReference{
Name: apSecretRef, Name: apSecretRef,
} }
} }
if export { if export {
return exportAlertProvider(alertProvider) return exportAlertProvider(provider)
} }
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
@ -120,66 +120,63 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
logger.Actionf("applying provider") logger.Actionf("applying Provider")
if err := upsertAlertProvider(ctx, kubeClient, alertProvider); err != nil { namespacedName, err := upsertAlertProvider(ctx, kubeClient, &provider)
if err != nil {
return err return err
} }
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for Provider reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isAlertProviderReady(ctx, kubeClient, name, namespace)); err != nil { isAlertProviderReady(ctx, kubeClient, namespacedName, &provider)); err != nil {
return err return err
} }
logger.Successf("provider %s is ready", name) logger.Successf("Provider %s is ready", name)
return nil return nil
} }
func upsertAlertProvider(ctx context.Context, kubeClient client.Client, alertProvider notificationv1.Provider) error { func upsertAlertProvider(ctx context.Context, kubeClient client.Client,
provider *notificationv1.Provider) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: alertProvider.GetNamespace(), Namespace: provider.GetNamespace(),
Name: alertProvider.GetName(), Name: provider.GetName(),
} }
var existing notificationv1.Provider var existing notificationv1.Provider
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &alertProvider); err != nil { if err := kubeClient.Create(ctx, provider); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("provider created") logger.Successf("Provider created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = alertProvider.Labels existing.Labels = provider.Labels
existing.Spec = alertProvider.Spec existing.Spec = provider.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
provider = &existing
logger.Successf("provider updated") logger.Successf("Provider updated")
return nil return namespacedName, nil
} }
func isAlertProviderReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isAlertProviderReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, provider *notificationv1.Provider) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var alertProvider notificationv1.Provider err := kubeClient.Get(ctx, namespacedName, provider)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil { if err != nil {
return false, err return false, err
} }
if c := meta.GetCondition(alertProvider.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionTrue: case corev1.ConditionTrue:
return true, nil return true, nil

@ -19,14 +19,13 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/pkg/apis/meta"
"io/ioutil" "io/ioutil"
"github.com/fluxcd/pkg/apis/meta"
"github.com/spf13/cobra" "github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
@ -34,7 +33,6 @@ import (
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1" helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
var createHelmReleaseCmd = &cobra.Command{ var createHelmReleaseCmd = &cobra.Command{
@ -100,7 +98,7 @@ var (
) )
func init() { func init() {
createHelmReleaseCmd.Flags().StringVar(&hrName, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<hr-name>'") createHelmReleaseCmd.Flags().StringVar(&hrName, "release-name", "", "name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'")
createHelmReleaseCmd.Flags().StringVar(&hrSource, "source", "", "source that contains the chart (<kind>/<name>)") createHelmReleaseCmd.Flags().StringVar(&hrSource, "source", "", "source that contains the chart (<kind>/<name>)")
createHelmReleaseCmd.Flags().StringVar(&hrChart, "chart", "", "Helm chart name or path") createHelmReleaseCmd.Flags().StringVar(&hrChart, "chart", "", "Helm chart name or path")
createHelmReleaseCmd.Flags().StringVar(&hrChartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)") createHelmReleaseCmd.Flags().StringVar(&hrChartVersion, "chart-version", "", "Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)")
@ -112,7 +110,7 @@ func init() {
func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error { func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("release name is required") return fmt.Errorf("HelmRelease name is required")
} }
name := args[0] name := args[0]
@ -137,7 +135,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
} }
if !export { if !export {
logger.Generatef("generating release") logger.Generatef("generating HelmRelease")
} }
helmRelease := helmv2.HelmRelease{ helmRelease := helmv2.HelmRelease{
@ -193,43 +191,25 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
logger.Actionf("applying release") logger.Actionf("applying HelmRelease")
if err := upsertHelmRelease(ctx, kubeClient, helmRelease); err != nil { namespacedName, err := upsertHelmRelease(ctx, kubeClient, &helmRelease)
if err != nil {
return err return err
} }
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for HelmRelease reconciliation")
chartName := fmt.Sprintf("%s-%s", namespace, name)
if err := wait.PollImmediate(pollInterval, timeout,
isHelmChartReady(ctx, kubeClient, chartName, namespace)); err != nil {
return err
}
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isHelmReleaseReady(ctx, kubeClient, name, namespace)); err != nil { isHelmReleaseReady(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
return err return err
} }
logger.Successf("HelmRelease %s is ready", name)
logger.Successf("release %s is ready", name)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return fmt.Errorf("release failed: %w", err)
}
if helmRelease.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision) logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
} else {
return fmt.Errorf("reconciliation failed")
}
return nil return nil
} }
func upsertHelmRelease(ctx context.Context, kubeClient client.Client, helmRelease helmv2.HelmRelease) error { func upsertHelmRelease(ctx context.Context, kubeClient client.Client,
helmRelease *helmv2.HelmRelease) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: helmRelease.GetNamespace(), Namespace: helmRelease.GetNamespace(),
Name: helmRelease.GetName(), Name: helmRelease.GetName(),
@ -239,75 +219,39 @@ func upsertHelmRelease(ctx context.Context, kubeClient client.Client, helmReleas
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &helmRelease); err != nil { if err := kubeClient.Create(ctx, helmRelease); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("release created") logger.Successf("HelmRelease created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = helmRelease.Labels existing.Labels = helmRelease.Labels
existing.Spec = helmRelease.Spec existing.Spec = helmRelease.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
helmRelease = &existing
logger.Successf("release updated") logger.Successf("HelmRelease updated")
return nil return namespacedName, nil
} }
func isHelmChartReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isHelmReleaseReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var helmChart sourcev1.HelmChart err := kubeClient.Get(ctx, namespacedName, helmRelease)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmChart)
if err != nil { if err != nil {
if apierrors.IsNotFound(err) {
return false, nil
}
return false, err return false, err
} }
if c := meta.GetCondition(helmChart.Status.Conditions, meta.ReadyCondition); c != nil { // Confirm the state we are observing is for the current generation
switch c.Status { if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil return false, nil
} }
}
func isHelmReleaseReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { return meta.HasReadyCondition(helmRelease.Status.Conditions), nil
return func() (bool, error) {
var helmRelease helmv2.HelmRelease
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return false, err
}
if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status {
case corev1.ConditionTrue:
return true, nil
case corev1.ConditionFalse:
return false, fmt.Errorf(c.Message)
}
}
return false, nil
} }
} }

@ -102,7 +102,7 @@ func init() {
func createKsCmdRun(cmd *cobra.Command, args []string) error { func createKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("kustomization name is required") return fmt.Errorf("Kustomization name is required")
} }
name := args[0] name := args[0]
@ -127,7 +127,7 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
} }
if !export { if !export {
logger.Generatef("generating kustomization") logger.Generatef("generating Kustomization")
} }
ksLabels, err := parseLabels() ksLabels, err := parseLabels()
@ -232,38 +232,25 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
logger.Actionf("applying kustomization") logger.Actionf("applying Kustomization")
if err := upsertKustomization(ctx, kubeClient, kustomization); err != nil { namespacedName, err := upsertKustomization(ctx, kubeClient, &kustomization)
if err != nil {
return err return err
} }
logger.Waitingf("waiting for kustomization sync") logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil { isKustomizationReady(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
return err return err
} }
logger.Successf("Kustomization %s is ready", name)
logger.Successf("kustomization %s is ready", name)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return fmt.Errorf("kustomization sync failed: %w", err)
}
if kustomization.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision) logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
} else {
return fmt.Errorf("kustomization sync failed")
}
return nil return nil
} }
func upsertKustomization(ctx context.Context, kubeClient client.Client, kustomization kustomizev1.Kustomization) error { func upsertKustomization(ctx context.Context, kubeClient client.Client,
kustomization *kustomizev1.Kustomization) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: kustomization.GetNamespace(), Namespace: kustomization.GetNamespace(),
Name: kustomization.GetName(), Name: kustomization.GetName(),
@ -273,39 +260,39 @@ func upsertKustomization(ctx context.Context, kubeClient client.Client, kustomiz
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &kustomization); err != nil { if err := kubeClient.Create(ctx, kustomization); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("kustomization created") logger.Successf("Kustomization created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = kustomization.Labels existing.Labels = kustomization.Labels
existing.Spec = kustomization.Spec existing.Spec = kustomization.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
kustomization = &existing
logger.Successf("kustomization updated") logger.Successf("Kustomization updated")
return nil return namespacedName, nil
} }
func isKustomizationReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isKustomizationReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var kustomization kustomizev1.Kustomization err := kubeClient.Get(ctx, namespacedName, kustomization)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil { if err != nil {
return false, err return false, err
} }
// Confirm the state we are observing is for the current generation
if kustomization.Generation != kustomization.Status.ObservedGeneration {
return false, nil
}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionTrue: case corev1.ConditionTrue:

@ -65,12 +65,12 @@ func init() {
func createReceiverCmdRun(cmd *cobra.Command, args []string) error { func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("receiver name is required") return fmt.Errorf("Receiver name is required")
} }
name := args[0] name := args[0]
if rcvType == "" { if rcvType == "" {
return fmt.Errorf("type is required") return fmt.Errorf("Receiver type is required")
} }
if rcvSecretRef == "" { if rcvSecretRef == "" {
@ -100,7 +100,7 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
} }
if !export { if !export {
logger.Generatef("generating receiver") logger.Generatef("generating Receiver")
} }
receiver := notificationv1.Receiver{ receiver := notificationv1.Receiver{
@ -132,34 +132,25 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
logger.Actionf("applying receiver") logger.Actionf("applying Receiver")
if err := upsertReceiver(ctx, kubeClient, receiver); err != nil { namespacedName, err := upsertReceiver(ctx, kubeClient, &receiver)
if err != nil {
return err return err
} }
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isReceiverReady(ctx, kubeClient, name, namespace)); err != nil { isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err return err
} }
logger.Successf("Receiver %s is ready", name)
logger.Successf("receiver %s is ready", name)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil {
return fmt.Errorf("receiver sync failed: %w", err)
}
logger.Successf("generated webhook URL %s", receiver.Status.URL) logger.Successf("generated webhook URL %s", receiver.Status.URL)
return nil return nil
} }
func upsertReceiver(ctx context.Context, kubeClient client.Client, receiver notificationv1.Receiver) error { func upsertReceiver(ctx context.Context, kubeClient client.Client,
receiver *notificationv1.Receiver) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: receiver.GetNamespace(), Namespace: receiver.GetNamespace(),
Name: receiver.GetName(), Name: receiver.GetName(),
@ -169,35 +160,30 @@ func upsertReceiver(ctx context.Context, kubeClient client.Client, receiver noti
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &receiver); err != nil { if err := kubeClient.Create(ctx, receiver); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("receiver created") logger.Successf("Receiver created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = receiver.Labels existing.Labels = receiver.Labels
existing.Spec = receiver.Spec existing.Spec = receiver.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
receiver = &existing
logger.Successf("receiver updated") logger.Successf("Receiver updated")
return nil return namespacedName, nil
} }
func isReceiverReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isReceiverReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, receiver *notificationv1.Receiver) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var receiver notificationv1.Receiver err := kubeClient.Get(ctx, namespacedName, receiver)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil { if err != nil {
return false, err return false, err
} }

@ -83,13 +83,13 @@ func init() {
func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error { func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("source name is required") return fmt.Errorf("Bucket source name is required")
} }
name := args[0] name := args[0]
secretName := fmt.Sprintf("bucket-%s", name) secretName := fmt.Sprintf("bucket-%s", name)
if !utils.containsItemString(supportedSourceBucketProviders, sourceBucketProvider) { if !utils.containsItemString(supportedSourceBucketProviders, sourceBucketProvider) {
return fmt.Errorf("bucket provider %s is not supported, can be %v", return fmt.Errorf("Bucket provider %s is not supported, can be %v",
sourceBucketProvider, supportedSourceBucketProviders) sourceBucketProvider, supportedSourceBucketProviders)
} }
@ -112,7 +112,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
bucket := sourcev1.Bucket{ bucket := &sourcev1.Bucket{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
@ -131,7 +131,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
} }
if export { if export {
return exportBucket(bucket) return exportBucket(*bucket)
} }
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
@ -142,7 +142,7 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
logger.Generatef("generating source") logger.Generatef("generating Bucket source")
secret := corev1.Secret{ secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
@ -168,38 +168,28 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("authentication configured") logger.Successf("authentication configured")
} }
logger.Actionf("applying source") logger.Actionf("applying Bucket source")
if err := upsertBucket(ctx, kubeClient, bucket); err != nil { namespacedName, err := upsertBucket(ctx, kubeClient, bucket)
if err != nil {
return err return err
} }
logger.Waitingf("waiting for download") logger.Waitingf("waiting for Bucket source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isBucketReady(ctx, kubeClient, name, namespace)); err != nil { isBucketReady(ctx, kubeClient, namespacedName, bucket)); err != nil {
return err return err
} }
logger.Successf("Bucket source reconciliation completed")
logger.Successf("download completed") if bucket.Status.Artifact == nil {
return fmt.Errorf("Bucket source reconciliation but no artifact was found")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
} }
err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil {
return fmt.Errorf("could not retrieve bucket: %w", err)
}
if bucket.Status.Artifact != nil {
logger.Successf("fetched revision: %s", bucket.Status.Artifact.Revision) logger.Successf("fetched revision: %s", bucket.Status.Artifact.Revision)
} else {
return fmt.Errorf("download failed, artifact not found")
}
return nil return nil
} }
func upsertBucket(ctx context.Context, kubeClient client.Client, bucket sourcev1.Bucket) error { func upsertBucket(ctx context.Context, kubeClient client.Client,
bucket *sourcev1.Bucket) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: bucket.GetNamespace(), Namespace: bucket.GetNamespace(),
Name: bucket.GetName(), Name: bucket.GetName(),
@ -209,22 +199,22 @@ func upsertBucket(ctx context.Context, kubeClient client.Client, bucket sourcev1
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &bucket); err != nil { if err := kubeClient.Create(ctx, bucket); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("source created") logger.Successf("Bucket source created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = bucket.Labels existing.Labels = bucket.Labels
existing.Spec = bucket.Spec existing.Spec = bucket.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
bucket = &existing
logger.Successf("source updated") logger.Successf("Bucket source updated")
return nil return namespacedName, nil
} }

@ -111,7 +111,7 @@ func init() {
func createSourceGitCmdRun(cmd *cobra.Command, args []string) error { func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("source name is required") return fmt.Errorf("GitRepository source name is required")
} }
name := args[0] name := args[0]
@ -234,7 +234,7 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("authentication configured") logger.Successf("authentication configured")
} }
logger.Generatef("generating source") logger.Generatef("generating GitRepository source")
if withAuth { if withAuth {
gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{ gitRepository.Spec.SecretRef = &corev1.LocalObjectReference{
@ -242,34 +242,23 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
} }
} }
logger.Actionf("applying source") logger.Actionf("applying GitRepository source")
if err := upsertGitRepository(ctx, kubeClient, gitRepository); err != nil { namespacedName, err := upsertGitRepository(ctx, kubeClient, &gitRepository)
if err != nil {
return err return err
} }
logger.Waitingf("waiting for git sync") logger.Waitingf("waiting for GitRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil { isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
return err return err
} }
logger.Successf("GitRepository source reconciliation completed")
logger.Successf("git sync completed") if gitRepository.Status.Artifact == nil {
return fmt.Errorf("GitRepository source reconciliation completed but no artifact was found")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
} }
err = kubeClient.Get(ctx, namespacedName, &gitRepository)
if err != nil {
return fmt.Errorf("git sync failed: %w", err)
}
if gitRepository.Status.Artifact != nil {
logger.Successf("fetched revision: %s", gitRepository.Status.Artifact.Revision) logger.Successf("fetched revision: %s", gitRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("git sync failed, artifact not found")
}
return nil return nil
} }
@ -330,7 +319,8 @@ func upsertSecret(ctx context.Context, kubeClient client.Client, secret corev1.S
return nil return nil
} }
func upsertGitRepository(ctx context.Context, kubeClient client.Client, gitRepository sourcev1.GitRepository) error { func upsertGitRepository(ctx context.Context, kubeClient client.Client,
gitRepository *sourcev1.GitRepository) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: gitRepository.GetNamespace(), Namespace: gitRepository.GetNamespace(),
Name: gitRepository.GetName(), Name: gitRepository.GetName(),
@ -340,35 +330,30 @@ func upsertGitRepository(ctx context.Context, kubeClient client.Client, gitRepos
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &gitRepository); err != nil { if err := kubeClient.Create(ctx, gitRepository); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("source created") logger.Successf("GitRepository source created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = gitRepository.Labels existing.Labels = gitRepository.Labels
existing.Spec = gitRepository.Spec existing.Spec = gitRepository.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
gitRepository = &existing
logger.Successf("source updated") logger.Successf("GitRepository source updated")
return nil return namespacedName, nil
} }
func isGitRepositoryReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isGitRepositoryReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, gitRepository *sourcev1.GitRepository) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var gitRepository sourcev1.GitRepository err := kubeClient.Get(ctx, namespacedName, gitRepository)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &gitRepository)
if err != nil { if err != nil {
return false, err return false, err
} }

@ -83,7 +83,7 @@ func init() {
func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error { func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("source name is required") return fmt.Errorf("HelmRepository source name is required")
} }
name := args[0] name := args[0]
secretName := fmt.Sprintf("helm-%s", name) secretName := fmt.Sprintf("helm-%s", name)
@ -107,7 +107,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("url parse failed: %w", err) return fmt.Errorf("url parse failed: %w", err)
} }
helmRepository := sourcev1.HelmRepository{ helmRepository := &sourcev1.HelmRepository{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Namespace: namespace, Namespace: namespace,
@ -122,7 +122,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
} }
if export { if export {
return exportHelmRepository(helmRepository) return exportHelmRepository(*helmRepository)
} }
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
@ -133,7 +133,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
logger.Generatef("generating source") logger.Generatef("generating HelmRepository source")
secret := corev1.Secret{ secret := corev1.Secret{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
@ -181,38 +181,28 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
logger.Successf("authentication configured") logger.Successf("authentication configured")
} }
logger.Actionf("applying source") logger.Actionf("applying HelmRepository source")
if err := upsertHelmRepository(ctx, kubeClient, helmRepository); err != nil { namespacedName, err := upsertHelmRepository(ctx, kubeClient, helmRepository)
if err != nil {
return err return err
} }
logger.Waitingf("waiting for index download") logger.Waitingf("waiting for HelmRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isHelmRepositoryReady(ctx, kubeClient, name, namespace)); err != nil { isHelmRepositoryReady(ctx, kubeClient, namespacedName, helmRepository)); err != nil {
return err return err
} }
logger.Successf("HelmRepository source reconciliation completed")
logger.Successf("index download completed") if helmRepository.Status.Artifact == nil {
return fmt.Errorf("HelmRepository source reconciliation completed but no artifact was found")
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
} }
err = kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil {
return fmt.Errorf("helm index failed: %w", err)
}
if helmRepository.Status.Artifact != nil {
logger.Successf("fetched revision: %s", helmRepository.Status.Artifact.Revision) logger.Successf("fetched revision: %s", helmRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("index download failed, artifact not found")
}
return nil return nil
} }
func upsertHelmRepository(ctx context.Context, kubeClient client.Client, helmRepository sourcev1.HelmRepository) error { func upsertHelmRepository(ctx context.Context, kubeClient client.Client,
helmRepository *sourcev1.HelmRepository) (types.NamespacedName, error) {
namespacedName := types.NamespacedName{ namespacedName := types.NamespacedName{
Namespace: helmRepository.GetNamespace(), Namespace: helmRepository.GetNamespace(),
Name: helmRepository.GetName(), Name: helmRepository.GetName(),
@ -222,22 +212,22 @@ func upsertHelmRepository(ctx context.Context, kubeClient client.Client, helmRep
err := kubeClient.Get(ctx, namespacedName, &existing) err := kubeClient.Get(ctx, namespacedName, &existing)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
if err := kubeClient.Create(ctx, &helmRepository); err != nil { if err := kubeClient.Create(ctx, helmRepository); err != nil {
return err return namespacedName, err
} else { } else {
logger.Successf("source created") logger.Successf("source created")
return nil return namespacedName, nil
} }
} }
return err return namespacedName, err
} }
existing.Labels = helmRepository.Labels existing.Labels = helmRepository.Labels
existing.Spec = helmRepository.Spec existing.Spec = helmRepository.Spec
if err := kubeClient.Update(ctx, &existing); err != nil { if err := kubeClient.Update(ctx, &existing); err != nil {
return err return namespacedName, err
} }
helmRepository = &existing
logger.Successf("source updated") logger.Successf("source updated")
return nil return namespacedName, nil
} }

@ -45,7 +45,7 @@ func init() {
func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error { func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("alert name is required") return fmt.Errorf("Alert name is required")
} }
name := args[0] name := args[0]
@ -62,7 +62,7 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
Name: name, Name: name,
} }
logger.Actionf("annotating alert %s in %s namespace", name, namespace) logger.Actionf("annotating Alert %s in %s namespace", name, namespace)
var alert notificationv1.Alert var alert notificationv1.Alert
err = kubeClient.Get(ctx, namespacedName, &alert) err = kubeClient.Get(ctx, namespacedName, &alert)
if err != nil { if err != nil {
@ -79,15 +79,13 @@ func reconcileAlertCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &alert); err != nil { if err := kubeClient.Update(ctx, &alert); err != nil {
return err return err
} }
logger.Successf("alert annotated") logger.Successf("Alert annotated")
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isAlertReady(ctx, kubeClient, name, namespace)); err != nil { isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err return err
} }
logger.Successf("Alert reconciliation completed")
logger.Successf("alert reconciliation completed")
return nil return nil
} }

@ -45,7 +45,7 @@ func init() {
func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error { func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("provider name is required") return fmt.Errorf("Provider name is required")
} }
name := args[0] name := args[0]
@ -62,7 +62,7 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
Name: name, Name: name,
} }
logger.Actionf("annotating provider %s in %s namespace", name, namespace) logger.Actionf("annotating Provider %s in %s namespace", name, namespace)
var alertProvider notificationv1.Provider var alertProvider notificationv1.Provider
err = kubeClient.Get(ctx, namespacedName, &alertProvider) err = kubeClient.Get(ctx, namespacedName, &alertProvider)
if err != nil { if err != nil {
@ -79,15 +79,13 @@ func reconcileAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &alertProvider); err != nil { if err := kubeClient.Update(ctx, &alertProvider); err != nil {
return err return err
} }
logger.Successf("provider annotated") logger.Successf("Provider annotated")
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isAlertProviderReady(ctx, kubeClient, name, namespace)); err != nil { isAlertProviderReady(ctx, kubeClient, namespacedName, &alertProvider)); err != nil {
return err return err
} }
logger.Successf("Provider reconciliation completed")
logger.Successf("provider reconciliation completed")
return nil return nil
} }

@ -98,19 +98,19 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
} }
} }
lastHandledReconcileAt := helmRelease.Status.LastHandledReconcileAt
logger.Actionf("annotating HelmRelease %s in %s namespace", name, namespace) logger.Actionf("annotating HelmRelease %s in %s namespace", name, namespace)
if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName); err != nil { if err := requestHelmReleaseReconciliation(ctx, kubeClient, namespacedName, &helmRelease); err != nil {
return err return err
} }
logger.Successf("HelmRelease annotated") logger.Successf("HelmRelease annotated")
logger.Waitingf("waiting for HelmRelease reconciliation") logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
helmReleaseReconciliationHandled(ctx, kubeClient, name, namespace, helmRelease.Status.LastHandledReconcileAt), helmReleaseReconciliationHandled(ctx, kubeClient, namespacedName, &helmRelease, lastHandledReconcileAt),
); err != nil { ); err != nil {
return err return err
} }
logger.Successf("HelmRelease reconciliation completed") logger.Successf("HelmRelease reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &helmRelease) err = kubeClient.Get(ctx, namespacedName, &helmRelease)
@ -120,7 +120,7 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(helmRelease.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionFalse: case corev1.ConditionFalse:
return fmt.Errorf("HelmRelease reconciliation failed") return fmt.Errorf("HelmRelease reconciliation failed: %s", c.Message)
default: default:
logger.Successf("reconciled revision %s", helmRelease.Status.LastAppliedRevision) logger.Successf("reconciled revision %s", helmRelease.Status.LastAppliedRevision)
} }
@ -129,39 +129,29 @@ func reconcileHrCmdRun(cmd *cobra.Command, args []string) error {
} }
func helmReleaseReconciliationHandled(ctx context.Context, kubeClient client.Client, func helmReleaseReconciliationHandled(ctx context.Context, kubeClient client.Client,
name, namespace, lastHandledReconcileAt string) wait.ConditionFunc { namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var helmRelease helmv2.HelmRelease err := kubeClient.Get(ctx, namespacedName, helmRelease)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil { if err != nil {
return false, err return false, err
} }
return helmRelease.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil return helmRelease.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
} }
} }
func requestHelmReleaseReconciliation(ctx context.Context, kubeClient client.Client, namespacedName types.NamespacedName) error { func requestHelmReleaseReconciliation(ctx context.Context, kubeClient client.Client,
var release helmv2.HelmRelease namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, &release); err != nil { if err := kubeClient.Get(ctx, namespacedName, helmRelease); err != nil {
return err return err
} }
if helmRelease.Annotations == nil {
if release.Annotations == nil { helmRelease.Annotations = map[string]string{
release.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
} }
} else { } else {
release.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) helmRelease.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
} }
return kubeClient.Update(ctx, helmRelease)
err = kubeClient.Update(ctx, &release)
return
}) })
} }

@ -54,14 +54,14 @@ var (
) )
func init() { func init() {
reconcileKsCmd.Flags().BoolVar(&syncKsWithSource, "with-source", false, "reconcile kustomization source") reconcileKsCmd.Flags().BoolVar(&syncKsWithSource, "with-source", false, "reconcile Kustomization source")
reconcileCmd.AddCommand(reconcileKsCmd) reconcileCmd.AddCommand(reconcileKsCmd)
} }
func reconcileKsCmdRun(cmd *cobra.Command, args []string) error { func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("kustomization name is required") return fmt.Errorf("Kustomization name is required")
} }
name := args[0] name := args[0]
@ -77,7 +77,6 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
Namespace: namespace, Namespace: namespace,
Name: name, Name: name,
} }
var kustomization kustomizev1.Kustomization var kustomization kustomizev1.Kustomization
err = kubeClient.Get(ctx, namespacedName, &kustomization) err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil { if err != nil {
@ -96,30 +95,26 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
} }
} }
logger.Actionf("annotating kustomization %s in %s namespace", name, namespace) lastHandledReconcileAt := kustomization.Status.LastHandledReconcileAt
if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName); err != nil { logger.Actionf("annotating Kustomization %s in %s namespace", name, namespace)
if err := requestKustomizeReconciliation(ctx, kubeClient, namespacedName, &kustomization); err != nil {
return err return err
} }
logger.Successf("kustomization annotated") logger.Successf("Kustomization annotated")
logger.Waitingf("waiting for kustomization reconciliation") logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate( if err := wait.PollImmediate(
pollInterval, timeout, pollInterval, timeout,
kustomizeReconciliationHandled(ctx, kubeClient, name, namespace, kustomization.Status.LastHandledReconcileAt), kustomizeReconciliationHandled(ctx, kubeClient, namespacedName, &kustomization, lastHandledReconcileAt),
); err != nil { ); err != nil {
return err return err
} }
logger.Successf("Kustomization reconciliation completed")
logger.Successf("kustomization reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return err
}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionFalse: case corev1.ConditionFalse:
return fmt.Errorf("kustomization reconciliation failed") return fmt.Errorf("Kustomization reconciliation failed")
default: default:
logger.Successf("reconciled revision %s", kustomization.Status.LastAppliedRevision) logger.Successf("reconciled revision %s", kustomization.Status.LastAppliedRevision)
} }
@ -128,30 +123,22 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error {
} }
func kustomizeReconciliationHandled(ctx context.Context, kubeClient client.Client, func kustomizeReconciliationHandled(ctx context.Context, kubeClient client.Client,
name, namespace, lastHandledReconcileAt string) wait.ConditionFunc { namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization, lastHandledReconcileAt string) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var kustomize kustomizev1.Kustomization err := kubeClient.Get(ctx, namespacedName, kustomization)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomize)
if err != nil { if err != nil {
return false, err return false, err
} }
return kustomization.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
return kustomize.Status.LastHandledReconcileAt != lastHandledReconcileAt, nil
} }
} }
func requestKustomizeReconciliation(ctx context.Context, kubeClient client.Client, namespacedName types.NamespacedName) error { func requestKustomizeReconciliation(ctx context.Context, kubeClient client.Client,
var kustomization kustomizev1.Kustomization namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) error {
return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) { return retry.RetryOnConflict(retry.DefaultBackoff, func() (err error) {
if err := kubeClient.Get(ctx, namespacedName, &kustomization); err != nil { if err := kubeClient.Get(ctx, namespacedName, kustomization); err != nil {
return err return err
} }
if kustomization.Annotations == nil { if kustomization.Annotations == nil {
kustomization.Annotations = map[string]string{ kustomization.Annotations = map[string]string{
meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), meta.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano),
@ -159,8 +146,6 @@ func requestKustomizeReconciliation(ctx context.Context, kubeClient client.Clien
} else { } else {
kustomization.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) kustomization.Annotations[meta.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano)
} }
return kubeClient.Update(ctx, kustomization)
err = kubeClient.Update(ctx, &kustomization)
return
}) })
} }

@ -62,7 +62,7 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
Name: name, Name: name,
} }
logger.Actionf("annotating receiver %s in %s namespace", name, namespace) logger.Actionf("annotating Receiver %s in %s namespace", name, namespace)
var receiver notificationv1.Receiver var receiver notificationv1.Receiver
err = kubeClient.Get(ctx, namespacedName, &receiver) err = kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil { if err != nil {
@ -79,15 +79,15 @@ func reconcileReceiverCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &receiver); err != nil { if err := kubeClient.Update(ctx, &receiver); err != nil {
return err return err
} }
logger.Successf("receiver annotated") logger.Successf("Receiver annotated")
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isReceiverReady(ctx, kubeClient, name, namespace)); err != nil { isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err return err
} }
logger.Successf("receiver reconciliation completed") logger.Successf("Receiver reconciliation completed")
return nil return nil
} }

@ -64,7 +64,7 @@ func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
Name: name, Name: name,
} }
logger.Actionf("annotating source %s in %s namespace", name, namespace) logger.Actionf("annotating Bucket source %s in %s namespace", name, namespace)
var bucket sourcev1.Bucket var bucket sourcev1.Bucket
err = kubeClient.Get(ctx, namespacedName, &bucket) err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil { if err != nil {
@ -81,42 +81,35 @@ func reconcileSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &bucket); err != nil { if err := kubeClient.Update(ctx, &bucket); err != nil {
return err return err
} }
logger.Successf("source annotated") logger.Successf("Bucket source annotated")
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for Bucket source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isBucketReady(ctx, kubeClient, name, namespace)); err != nil { isBucketReady(ctx, kubeClient, namespacedName, &bucket)); err != nil {
return err return err
} }
logger.Successf("Bucket source reconciliation completed")
logger.Successf("bucket reconciliation completed") if bucket.Status.Artifact == nil {
return fmt.Errorf("Bucket source reconciliation completed but no artifact was found")
err = kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil {
return err
} }
if bucket.Status.Artifact != nil {
logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision) logger.Successf("fetched revision %s", bucket.Status.Artifact.Revision)
} else {
return fmt.Errorf("bucket reconciliation failed, artifact not found")
}
return nil return nil
} }
func isBucketReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isBucketReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var bucket sourcev1.Bucket err := kubeClient.Get(ctx, namespacedName, bucket)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &bucket)
if err != nil { if err != nil {
return false, err return false, err
} }
// Confirm the state we are observing is for the current generation
if bucket.Generation != bucket.Status.ObservedGeneration {
return false, nil
}
if c := meta.GetCondition(bucket.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(bucket.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionTrue: case corev1.ConditionTrue:

@ -62,7 +62,7 @@ func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
Name: name, Name: name,
} }
logger.Actionf("annotating source %s in %s namespace", name, namespace) logger.Actionf("annotating GitRepository source %s in %s namespace", name, namespace)
var gitRepository sourcev1.GitRepository var gitRepository sourcev1.GitRepository
err = kubeClient.Get(ctx, namespacedName, &gitRepository) err = kubeClient.Get(ctx, namespacedName, &gitRepository)
if err != nil { if err != nil {
@ -79,25 +79,18 @@ func reconcileSourceGitCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &gitRepository); err != nil { if err := kubeClient.Update(ctx, &gitRepository); err != nil {
return err return err
} }
logger.Successf("source annotated") logger.Successf("GitRepository source annotated")
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for GitRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil { isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
return err return err
} }
logger.Successf("GitRepository source reconciliation completed")
logger.Successf("git reconciliation completed") if gitRepository.Status.Artifact == nil {
return fmt.Errorf("GitRepository source reconciliation completed but no artifact was found")
err = kubeClient.Get(ctx, namespacedName, &gitRepository)
if err != nil {
return err
} }
if gitRepository.Status.Artifact != nil {
logger.Successf("fetched revision %s", gitRepository.Status.Artifact.Revision) logger.Successf("fetched revision %s", gitRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("git reconciliation failed, artifact not found")
}
return nil return nil
} }

@ -47,7 +47,7 @@ func init() {
func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error { func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("source name is required") return fmt.Errorf("HelmRepository source name is required")
} }
name := args[0] name := args[0]
@ -64,7 +64,7 @@ func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
Name: name, Name: name,
} }
logger.Actionf("annotating source %s in %s namespace", name, namespace) logger.Actionf("annotating HelmRepository source %s in %s namespace", name, namespace)
var helmRepository sourcev1.HelmRepository var helmRepository sourcev1.HelmRepository
err = kubeClient.Get(ctx, namespacedName, &helmRepository) err = kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil { if err != nil {
@ -81,42 +81,35 @@ func reconcileSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
if err := kubeClient.Update(ctx, &helmRepository); err != nil { if err := kubeClient.Update(ctx, &helmRepository); err != nil {
return err return err
} }
logger.Successf("source annotated") logger.Successf("HelmRepository source annotated")
logger.Waitingf("waiting for reconciliation") logger.Waitingf("waiting for HelmRepository source reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isHelmRepositoryReady(ctx, kubeClient, name, namespace)); err != nil { isHelmRepositoryReady(ctx, kubeClient, namespacedName, &helmRepository)); err != nil {
return err return err
} }
logger.Successf("HelmRepository source reconciliation completed")
logger.Successf("helm reconciliation completed") if helmRepository.Status.Artifact == nil {
return fmt.Errorf("HelmRepository source reconciliation completed but no artifact was found")
err = kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil {
return err
} }
if helmRepository.Status.Artifact != nil {
logger.Successf("fetched revision %s", helmRepository.Status.Artifact.Revision) logger.Successf("fetched revision %s", helmRepository.Status.Artifact.Revision)
} else {
return fmt.Errorf("helm reconciliation failed, artifact not found")
}
return nil return nil
} }
func isHelmRepositoryReady(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isHelmRepositoryReady(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRepository *sourcev1.HelmRepository) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var helmRepository sourcev1.HelmRepository err := kubeClient.Get(ctx, namespacedName, helmRepository)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &helmRepository)
if err != nil { if err != nil {
return false, err return false, err
} }
// Confirm the state we are observing is for the current generation
if helmRepository.Generation != helmRepository.Status.ObservedGeneration {
return false, nil
}
if c := meta.GetCondition(helmRepository.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(helmRepository.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionTrue: case corev1.ConditionTrue:

@ -78,24 +78,17 @@ func resumeAlertCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Alert reconciliation") logger.Waitingf("waiting for Alert reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isAlertResumed(ctx, kubeClient, name, namespace)); err != nil { isAlertResumed(ctx, kubeClient, namespacedName, &alert)); err != nil {
return err return err
} }
logger.Successf("Alert reconciliation completed") logger.Successf("Alert reconciliation completed")
return nil return nil
} }
func isAlertResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isAlertResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, alert *notificationv1.Alert) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var alert notificationv1.Alert err := kubeClient.Get(ctx, namespacedName, alert)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &alert)
if err != nil { if err != nil {
return false, err return false, err
} }

@ -79,36 +79,25 @@ func resumeHrCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for HelmRelease reconciliation") logger.Waitingf("waiting for HelmRelease reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isHelmReleaseResumed(ctx, kubeClient, name, namespace)); err != nil { isHelmReleaseResumed(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
return err return err
} }
logger.Successf("HelmRelease reconciliation completed") logger.Successf("HelmRelease reconciliation completed")
err = kubeClient.Get(ctx, namespacedName, &helmRelease)
if err != nil {
return err
}
if helmRelease.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision) logger.Successf("applied revision %s", helmRelease.Status.LastAppliedRevision)
} else {
return fmt.Errorf("HelmRelease reconciliation failed")
}
return nil return nil
} }
func isHelmReleaseResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isHelmReleaseResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var helmRelease helmv2.HelmRelease err := kubeClient.Get(ctx, namespacedName, helmRelease)
namespacedName := types.NamespacedName{ if err != nil {
Namespace: namespace, return false, err
Name: name,
} }
err := kubeClient.Get(ctx, namespacedName, &helmRelease) // Confirm the state we are observing is for the current generation
if err != nil { if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
return false, err return false, err
} }

@ -47,7 +47,7 @@ func init() {
func resumeKsCmdRun(cmd *cobra.Command, args []string) error { func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
if len(args) < 1 { if len(args) < 1 {
return fmt.Errorf("kustomization name is required") return fmt.Errorf("Kustomization name is required")
} }
name := args[0] name := args[0]
@ -69,48 +69,37 @@ func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
return err return err
} }
logger.Actionf("resuming kustomization %s in %s namespace", name, namespace) logger.Actionf("resuming Kustomization %s in %s namespace", name, namespace)
kustomization.Spec.Suspend = false kustomization.Spec.Suspend = false
if err := kubeClient.Update(ctx, &kustomization); err != nil { if err := kubeClient.Update(ctx, &kustomization); err != nil {
return err return err
} }
logger.Successf("kustomization resumed") logger.Successf("Kustomization resumed")
logger.Waitingf("waiting for kustomization sync") logger.Waitingf("waiting for Kustomization reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isKustomizationResumed(ctx, kubeClient, name, namespace)); err != nil { isKustomizationResumed(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
return err return err
} }
logger.Successf("Kustomization reconciliation completed")
logger.Successf("kustomization sync completed")
err = kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil {
return err
}
if kustomization.Status.LastAppliedRevision != "" {
logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision) logger.Successf("applied revision %s", kustomization.Status.LastAppliedRevision)
} else {
return fmt.Errorf("kustomization sync failed")
}
return nil return nil
} }
func isKustomizationResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isKustomizationResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var kustomization kustomizev1.Kustomization err := kubeClient.Get(ctx, namespacedName, kustomization)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &kustomization)
if err != nil { if err != nil {
return false, err return false, err
} }
// Confirm the state we are observing is for the current generation
if kustomization.Generation != kustomization.Status.ObservedGeneration {
return false, nil
}
if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil { if c := meta.GetCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
switch c.Status { switch c.Status {
case corev1.ConditionTrue: case corev1.ConditionTrue:

@ -78,24 +78,18 @@ func resumeReceiverCmdRun(cmd *cobra.Command, args []string) error {
logger.Waitingf("waiting for Receiver reconciliation") logger.Waitingf("waiting for Receiver reconciliation")
if err := wait.PollImmediate(pollInterval, timeout, if err := wait.PollImmediate(pollInterval, timeout,
isReceiverResumed(ctx, kubeClient, name, namespace)); err != nil { isReceiverResumed(ctx, kubeClient, namespacedName, &receiver)); err != nil {
return err return err
} }
logger.Successf("Receiver reconciliation completed") logger.Successf("Receiver reconciliation completed")
return nil return nil
} }
func isReceiverResumed(ctx context.Context, kubeClient client.Client, name, namespace string) wait.ConditionFunc { func isReceiverResumed(ctx context.Context, kubeClient client.Client,
namespacedName types.NamespacedName, receiver *notificationv1.Receiver) wait.ConditionFunc {
return func() (bool, error) { return func() (bool, error) {
var receiver notificationv1.Receiver err := kubeClient.Get(ctx, namespacedName, receiver)
namespacedName := types.NamespacedName{
Namespace: namespace,
Name: name,
}
err := kubeClient.Get(ctx, namespacedName, &receiver)
if err != nil { if err != nil {
return false, err return false, err
} }

@ -66,7 +66,7 @@ gotk create helmrelease [name] [flags]
--chart-version string Helm chart version, accepts a semver range (ignored for charts from GitRepository sources) --chart-version string Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)
--depends-on stringArray HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>' --depends-on stringArray HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'
-h, --help help for helmrelease -h, --help help for helmrelease
--release-name string name used for the Helm release, defaults to a composition of '[<target-namespace>-]<hr-name>' --release-name string name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'
--source string source that contains the chart (<kind>/<name>) --source string source that contains the chart (<kind>/<name>)
--target-namespace string namespace to install this release, defaults to the HelmRelease namespace --target-namespace string namespace to install this release, defaults to the HelmRelease namespace
--values string local path to the values.yaml file --values string local path to the values.yaml file

@ -26,7 +26,7 @@ gotk reconcile kustomization [name] [flags]
``` ```
-h, --help help for kustomization -h, --help help for kustomization
--with-source reconcile kustomization source --with-source reconcile Kustomization source
``` ```
### Options inherited from parent commands ### Options inherited from parent commands

Loading…
Cancel
Save