From 7a68c4ccf3c25db2f508898c24f9954972999ffa Mon Sep 17 00:00:00 2001 From: stefanprodan Date: Tue, 14 Jul 2020 12:47:21 +0300 Subject: [PATCH] Add reconcile helm commands --- cmd/tk/reconcile_helmrelease.go | 120 +++++++++++++++++++++++++++ cmd/tk/reconcile_kustomization.go | 4 +- cmd/tk/reconcile_source_git.go | 4 +- cmd/tk/reconcile_source_helm.go | 100 ++++++++++++++++++++++ docs/cmd/tk_reconcile.md | 1 + docs/cmd/tk_reconcile_helmrelease.md | 44 ++++++++++ docs/cmd/tk_reconcile_source.md | 1 + docs/cmd/tk_reconcile_source_helm.md | 39 +++++++++ go.mod | 1 + go.sum | 8 ++ mkdocs.yml | 2 + 11 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 cmd/tk/reconcile_helmrelease.go create mode 100644 cmd/tk/reconcile_source_helm.go create mode 100644 docs/cmd/tk_reconcile_helmrelease.md create mode 100644 docs/cmd/tk_reconcile_source_helm.md diff --git a/cmd/tk/reconcile_helmrelease.go b/cmd/tk/reconcile_helmrelease.go new file mode 100644 index 00000000..d8129500 --- /dev/null +++ b/cmd/tk/reconcile_helmrelease.go @@ -0,0 +1,120 @@ +/* +Copyright 2020 The Flux CD contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "fmt" + "time" + + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + + helmv2 "github.com/fluxcd/helm-controller/api/v2alpha1" +) + +var reconcileHrCmd = &cobra.Command{ + Use: "helmrelease [name]", + Aliases: []string{"hr"}, + Short: "Reconcile a HelmRelease resource", + Long: ` +The reconcile kustomization command triggers a reconciliation of a HelmRelease resource and waits for it to finish.`, + Example: ` # Trigger a HelmRelease apply outside of the reconciliation interval + tk reconcile hr podinfo + + # Trigger a reconciliation of the HelmRelease's source and apply changes + tk reconcile hr podinfo --with-source +`, + RunE: reconcileHrCmdRun, +} + +var ( + syncHrWithSource bool +) + +func init() { + reconcileHrCmd.Flags().BoolVar(&syncHrWithSource, "with-source", false, "reconcile HelmRelease source") + + reconcileCmd.AddCommand(reconcileHrCmd) +} + +func reconcileHrCmdRun(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return fmt.Errorf("HelmRelease name is required") + } + name := args[0] + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + kubeClient, err := utils.kubeClient(kubeconfig) + if err != nil { + return err + } + + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + + var helmRelease helmv2.HelmRelease + err = kubeClient.Get(ctx, namespacedName, &helmRelease) + if err != nil { + return err + } + + if syncHrWithSource { + err := syncSourceHelmCmdRun(nil, []string{helmRelease.Spec.Chart.SourceRef.Name}) + if err != nil { + return err + } + } else { + logger.Actionf("annotating HelmRelease %s in %s namespace", name, namespace) + if helmRelease.Annotations == nil { + helmRelease.Annotations = map[string]string{ + helmv2.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), + } + } else { + helmRelease.Annotations[helmv2.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) + } + if err := kubeClient.Update(ctx, &helmRelease); err != nil { + return err + } + logger.Successf("HelmRelease annotated") + } + + logger.Waitingf("waiting for HelmRelease reconciliation") + if err := wait.PollImmediate(pollInterval, timeout, + isKustomizationReady(ctx, kubeClient, name, namespace)); err != nil { + return err + } + + logger.Successf("HelmRelease reconciliation completed") + + err = kubeClient.Get(ctx, namespacedName, &helmRelease) + if err != nil { + return err + } + + if helmRelease.Status.LastAppliedRevision != "" { + logger.Successf("reconciled revision %s", helmRelease.Status.LastAppliedRevision) + } else { + return fmt.Errorf("HelmRelease reconciliation failed") + } + return nil +} diff --git a/cmd/tk/reconcile_kustomization.go b/cmd/tk/reconcile_kustomization.go index e12d5c9d..42cee7a5 100644 --- a/cmd/tk/reconcile_kustomization.go +++ b/cmd/tk/reconcile_kustomization.go @@ -86,10 +86,10 @@ func reconcileKsCmdRun(cmd *cobra.Command, args []string) error { logger.Actionf("annotating kustomization %s in %s namespace", name, namespace) if kustomization.Annotations == nil { kustomization.Annotations = map[string]string{ - kustomizev1.ReconcileAtAnnotation: time.Now().String(), + kustomizev1.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), } } else { - kustomization.Annotations[kustomizev1.ReconcileAtAnnotation] = time.Now().String() + kustomization.Annotations[kustomizev1.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) } if err := kubeClient.Update(ctx, &kustomization); err != nil { return err diff --git a/cmd/tk/reconcile_source_git.go b/cmd/tk/reconcile_source_git.go index 11612ef4..9b0aaf01 100644 --- a/cmd/tk/reconcile_source_git.go +++ b/cmd/tk/reconcile_source_git.go @@ -68,10 +68,10 @@ func syncSourceGitCmdRun(cmd *cobra.Command, args []string) error { if gitRepository.Annotations == nil { gitRepository.Annotations = map[string]string{ - sourcev1.ReconcileAtAnnotation: time.Now().String(), + sourcev1.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), } } else { - gitRepository.Annotations[sourcev1.ReconcileAtAnnotation] = time.Now().String() + gitRepository.Annotations[sourcev1.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) } if err := kubeClient.Update(ctx, &gitRepository); err != nil { return err diff --git a/cmd/tk/reconcile_source_helm.go b/cmd/tk/reconcile_source_helm.go new file mode 100644 index 00000000..21ef3123 --- /dev/null +++ b/cmd/tk/reconcile_source_helm.go @@ -0,0 +1,100 @@ +/* +Copyright 2020 The Flux CD contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "fmt" + sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1" + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "time" +) + +var reconcileSourceHelmCmd = &cobra.Command{ + Use: "helm [name]", + Short: "Reconcile a HelmRepository source", + Long: `The reconcile source command triggers a reconciliation of a HelmRepository resource and waits for it to finish.`, + Example: ` # Trigger a helm repo update for an existing source + tk reconcile source helm podinfo +`, + RunE: syncSourceHelmCmdRun, +} + +func init() { + reconcileSourceCmd.AddCommand(reconcileSourceHelmCmd) +} + +func syncSourceHelmCmdRun(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return fmt.Errorf("source name is required") + } + name := args[0] + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + kubeClient, err := utils.kubeClient(kubeconfig) + if err != nil { + return err + } + + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + + logger.Actionf("annotating source %s in %s namespace", name, namespace) + var helmRepository sourcev1.HelmRepository + err = kubeClient.Get(ctx, namespacedName, &helmRepository) + if err != nil { + return err + } + + if helmRepository.Annotations == nil { + helmRepository.Annotations = map[string]string{ + sourcev1.ReconcileAtAnnotation: time.Now().Format(time.RFC3339Nano), + } + } else { + helmRepository.Annotations[sourcev1.ReconcileAtAnnotation] = time.Now().Format(time.RFC3339Nano) + } + if err := kubeClient.Update(ctx, &helmRepository); err != nil { + return err + } + logger.Successf("source annotated") + + logger.Waitingf("waiting for reconciliation") + if err := wait.PollImmediate(pollInterval, timeout, + isGitRepositoryReady(ctx, kubeClient, name, namespace)); err != nil { + return err + } + + logger.Successf("helm reconciliation completed") + + 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) + } else { + return fmt.Errorf("helm reconciliation failed, artifact not found") + } + return nil +} diff --git a/docs/cmd/tk_reconcile.md b/docs/cmd/tk_reconcile.md index ce8e90bc..c206c997 100644 --- a/docs/cmd/tk_reconcile.md +++ b/docs/cmd/tk_reconcile.md @@ -24,6 +24,7 @@ The reconcile sub-commands trigger a reconciliation of sources and resources. ### SEE ALSO * [tk](tk.md) - Command line utility for assembling Kubernetes CD pipelines +* [tk reconcile helmrelease](tk_reconcile_helmrelease.md) - Reconcile a HelmRelease resource * [tk reconcile kustomization](tk_reconcile_kustomization.md) - Reconcile a Kustomization resource * [tk reconcile source](tk_reconcile_source.md) - Reconcile sources diff --git a/docs/cmd/tk_reconcile_helmrelease.md b/docs/cmd/tk_reconcile_helmrelease.md new file mode 100644 index 00000000..eadf3423 --- /dev/null +++ b/docs/cmd/tk_reconcile_helmrelease.md @@ -0,0 +1,44 @@ +## tk reconcile helmrelease + +Reconcile a HelmRelease resource + +### Synopsis + + +The reconcile kustomization command triggers a reconciliation of a HelmRelease resource and waits for it to finish. + +``` +tk reconcile helmrelease [name] [flags] +``` + +### Examples + +``` + # Trigger a HelmRelease apply outside of the reconciliation interval + tk reconcile hr podinfo + + # Trigger a reconciliation of the HelmRelease's source and apply changes + tk reconcile hr podinfo --with-source + +``` + +### Options + +``` + -h, --help help for helmrelease + --with-source reconcile HelmRelease source +``` + +### Options inherited from parent commands + +``` + --kubeconfig string path to the kubeconfig file (default "~/.kube/config") + --namespace string the namespace scope for this operation (default "gitops-system") + --timeout duration timeout for this operation (default 5m0s) + --verbose print generated objects +``` + +### SEE ALSO + +* [tk reconcile](tk_reconcile.md) - Reconcile sources and resources + diff --git a/docs/cmd/tk_reconcile_source.md b/docs/cmd/tk_reconcile_source.md index aa1b242e..138a4f57 100644 --- a/docs/cmd/tk_reconcile_source.md +++ b/docs/cmd/tk_reconcile_source.md @@ -25,4 +25,5 @@ The reconcile source sub-commands trigger a reconciliation of sources. * [tk reconcile](tk_reconcile.md) - Reconcile sources and resources * [tk reconcile source git](tk_reconcile_source_git.md) - Reconcile a GitRepository source +* [tk reconcile source helm](tk_reconcile_source_helm.md) - Reconcile a HelmRepository source diff --git a/docs/cmd/tk_reconcile_source_helm.md b/docs/cmd/tk_reconcile_source_helm.md new file mode 100644 index 00000000..3f884990 --- /dev/null +++ b/docs/cmd/tk_reconcile_source_helm.md @@ -0,0 +1,39 @@ +## tk reconcile source helm + +Reconcile a HelmRepository source + +### Synopsis + +The reconcile source command triggers a reconciliation of a HelmRepository resource and waits for it to finish. + +``` +tk reconcile source helm [name] [flags] +``` + +### Examples + +``` + # Trigger a helm repo update for an existing source + tk reconcile source helm podinfo + +``` + +### Options + +``` + -h, --help help for helm +``` + +### Options inherited from parent commands + +``` + --kubeconfig string path to the kubeconfig file (default "~/.kube/config") + --namespace string the namespace scope for this operation (default "gitops-system") + --timeout duration timeout for this operation (default 5m0s) + --verbose print generated objects +``` + +### SEE ALSO + +* [tk reconcile source](tk_reconcile_source.md) - Reconcile sources + diff --git a/go.mod b/go.mod index a62e73ca..b6b9cc55 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.14 require ( github.com/blang/semver v3.5.1+incompatible + github.com/fluxcd/helm-controller v0.0.1-alpha.1 github.com/fluxcd/kustomize-controller v0.0.3 github.com/fluxcd/pkg v0.0.3 github.com/fluxcd/source-controller v0.0.5 diff --git a/go.sum b/go.sum index 6afcb27f..68e33989 100644 --- a/go.sum +++ b/go.sum @@ -170,6 +170,8 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwC github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fluxcd/helm-controller v0.0.1-alpha.1 h1:stbyNQdAhHqjL2i+Oi85A5cceOnatt20/fcNc0taeHA= +github.com/fluxcd/helm-controller v0.0.1-alpha.1/go.mod h1:ya+BGrB1Md5DaxQIQmXwf6qNaydZZoa3EZJGpqFFu/Y= github.com/fluxcd/kustomize-controller v0.0.3 h1:oJJo+GNMfmuxg9M/TXkNui2AtV+oxaX/ZNI+33AuJHU= github.com/fluxcd/kustomize-controller v0.0.3/go.mod h1:mOBo09pl4BKZP0DPFoijgsjReMNFLSznYMuKbwqqpOU= github.com/fluxcd/pkg v0.0.3 h1:yhjtpGtD9LxFo8JtwTuUxJyFcX2wSSb0TPptIEpGSmA= @@ -949,6 +951,8 @@ k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= k8s.io/apiextensions-apiserver v0.18.0/go.mod h1:18Cwn1Xws4xnWQNC00FLq1E350b9lUF+aOdIWDOZxgo= k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8= k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY= +k8s.io/apiextensions-apiserver v0.18.4 h1:Y3HGERmS8t9u12YNUFoOISqefaoGRuTc43AYCLzWmWE= +k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio= k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg= k8s.io/apimachinery v0.18.0/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA= k8s.io/apimachinery v0.18.2 h1:44CmtbmkzVDAhCpRVSiP2R5PPrC2RtlIv/MoB8xpdRA= @@ -957,7 +961,9 @@ k8s.io/apimachinery v0.18.4 h1:ST2beySjhqwJoIFk6p7Hp5v5O0hYY6Gngq/gUYXTPIA= k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= k8s.io/apiserver v0.18.0/go.mod h1:3S2O6FeBBd6XTo0njUrLxiqk8GNy6wWOftjhJcXYnjw= k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw= +k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8= k8s.io/cli-runtime v0.18.0/go.mod h1:1eXfmBsIJosjn9LjEBUd2WVPoPAY9XGTqTFcPMIBsUQ= +k8s.io/cli-runtime v0.18.4/go.mod h1:9/hS/Cuf7NVzWR5F/5tyS6xsnclxoPLVtwhnkJG1Y4g= k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k= k8s.io/client-go v0.18.0/go.mod h1:uQSYDYs4WhVZ9i6AIoEZuwUggLVEF64HOD37boKAtF8= k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE= @@ -966,8 +972,10 @@ k8s.io/client-go v0.18.4 h1:un55V1Q/B3JO3A76eS0kUSywgGK/WR3BQ8fHQjNa6Zc= k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g= k8s.io/code-generator v0.18.0/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc= +k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= k8s.io/component-base v0.18.0/go.mod h1:u3BCg0z1uskkzrnAKFzulmYaEpZF7XC9Pf/uFyb1v2c= k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM= +k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= diff --git a/mkdocs.yml b/mkdocs.yml index ffa0555a..916dabcb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -92,8 +92,10 @@ nav: - Suspend kustomization: cmd/tk_suspend_kustomization.md - Reconcile: cmd/tk_reconcile.md - Reconcile kustomization: cmd/tk_reconcile_kustomization.md + - Reconcile helmrelease: cmd/tk_reconcile_helmrelease.md - Reconcile source: cmd/tk_reconcile_source.md - Reconcile source git: cmd/tk_reconcile_source_git.md + - Reconcile source helm: cmd/tk_reconcile_source_helm.md - Uninstall: cmd/tk_uninstall.md - Roadmap: roadmap/index.md - Contributing: contributing/index.md