From 433628791bf5123e1ecf0c18ea283a31ce4ff4e8 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 7 Dec 2020 15:06:42 +0000 Subject: [PATCH] Add create auto image-update command This adds the create subcommand, without attempting any refactoring. NB the TODO: the image/v1alpha1 API does not yet export a const for the name of the kind. The field `RunInterval` will likely be changed to `Interval` (with a value field), at some point, too. Signed-off-by: Michael Bridgen --- cmd/flux/create_auto_imageupdateauto.go | 209 ++++++++++++++++++++++ docs/cmd/flux_create_auto.md | 1 + docs/cmd/flux_create_auto_image-update.md | 42 +++++ 3 files changed, 252 insertions(+) create mode 100644 cmd/flux/create_auto_imageupdateauto.go create mode 100644 docs/cmd/flux_create_auto_image-update.md diff --git a/cmd/flux/create_auto_imageupdateauto.go b/cmd/flux/create_auto_imageupdateauto.go new file mode 100644 index 00000000..a89b412b --- /dev/null +++ b/cmd/flux/create_auto_imageupdateauto.go @@ -0,0 +1,209 @@ +/* +Copyright 2020 The Flux authors + +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" + + "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" + 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" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + "github.com/fluxcd/flux2/internal/utils" + autov1 "github.com/fluxcd/image-automation-controller/api/v1alpha1" + "github.com/fluxcd/pkg/apis/meta" +) + +var createAutoImageUpdateCmd = &cobra.Command{ + Use: "image-update ", + Short: "Create or update an ImageUpdateAutomation object", + Long: `The create auto image-update command generates an ImageUpdateAutomation resource. +An ImageUpdateAutomation object specifies an automated update to images +mentioned in YAMLs in a git repository.`, + RunE: createAutoImageUpdateRun, +} + +type imageUpdateFlags struct { + // git checkout spec + gitRepoRef string + branch string + // commit spec + commitTemplate string + authorName string + authorEmail string +} + +var imageUpdateArgs = imageUpdateFlags{} + +func init() { + flags := createAutoImageUpdateCmd.Flags() + flags.StringVar(&imageUpdateArgs.gitRepoRef, "git-repo-ref", "", "the name of a GitRepository resource with details of the upstream git repository") + flags.StringVar(&imageUpdateArgs.branch, "branch", "", "the branch to push commits to") + flags.StringVar(&imageUpdateArgs.commitTemplate, "commit-template", "", "a template for commit messages") + flags.StringVar(&imageUpdateArgs.authorName, "author-name", "", "the name to use for commit author") + flags.StringVar(&imageUpdateArgs.authorEmail, "author-email", "", "the email to use for commit author") + + createAutoCmd.AddCommand(createAutoImageUpdateCmd) +} + +func createAutoImageUpdateRun(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return fmt.Errorf("ImageUpdateAutomation name is required") + } + objectName := args[0] + + if imageUpdateArgs.gitRepoRef == "" { + return fmt.Errorf("a reference to a GitRepository is required (--git-repo-ref)") + } + + labels, err := parseLabels() + if err != nil { + return err + } + + var update = autov1.ImageUpdateAutomation{ + ObjectMeta: metav1.ObjectMeta{ + Name: objectName, + Namespace: namespace, + Labels: labels, + }, + Spec: autov1.ImageUpdateAutomationSpec{ + Checkout: autov1.GitCheckoutSpec{ + GitRepositoryRef: corev1.LocalObjectReference{ + Name: imageUpdateArgs.gitRepoRef, + }, + Branch: imageUpdateArgs.branch, + }, + RunInterval: &metav1.Duration{Duration: interval}, + Update: autov1.UpdateStrategy{ + Setters: &autov1.SettersStrategy{}, + }, + Commit: autov1.CommitSpec{ + AuthorName: imageUpdateArgs.authorName, + AuthorEmail: imageUpdateArgs.authorEmail, + MessageTemplate: imageUpdateArgs.commitTemplate, + }, + }, + } + + if export { + return printExport(exportImageUpdateAutomation(&update)) + } + + // I don't need these until attempting to upsert the object, but + // for consistency with other create commands, the following are + // given a chance to error out before reporting any progress. + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) + if err != nil { + return err + } + + logger.Generatef("generating ImageUpdateAutomation") + logger.Actionf("applying ImageUpdateAutomation") + namespacedName, err := upsertImageUpdateAutomation(ctx, kubeClient, &update) + if err != nil { + return err + } + + logger.Waitingf("waiting for ImageUpdateAutomation reconciliation") + if err := wait.PollImmediate(pollInterval, timeout, + isImageUpdateAutomationReady(ctx, kubeClient, namespacedName, &update)); err != nil { + return err + } + logger.Successf("ImageUpdateAutomation reconciliation completed") + + return nil +} + +func upsertImageUpdateAutomation(ctx context.Context, kubeClient client.Client, update *autov1.ImageUpdateAutomation) (types.NamespacedName, error) { + nsname := types.NamespacedName{ + Namespace: update.GetNamespace(), + Name: update.GetName(), + } + + var existing autov1.ImageUpdateAutomation + existing.SetName(nsname.Name) + existing.SetNamespace(nsname.Namespace) + op, err := controllerutil.CreateOrUpdate(ctx, kubeClient, &existing, func() error { + existing.Spec = update.Spec + existing.Labels = update.Labels + return nil + }) + if err != nil { + return nsname, err + } + + switch op { + case controllerutil.OperationResultCreated: + logger.Successf("ImageUpdateAutomation created") + case controllerutil.OperationResultUpdated: + logger.Successf("ImageUpdateAutomation updated") + } + return nsname, nil +} + +func isImageUpdateAutomationReady(ctx context.Context, kubeClient client.Client, + namespacedName types.NamespacedName, update *autov1.ImageUpdateAutomation) wait.ConditionFunc { + return func() (bool, error) { + err := kubeClient.Get(ctx, namespacedName, update) + if err != nil { + return false, err + } + + // Confirm the state we are observing is for the current generation + if update.Generation != update.Status.ObservedGeneration { + return false, nil + } + + if c := apimeta.FindStatusCondition(update.Status.Conditions, 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 + } +} + +func exportImageUpdateAutomation(update *autov1.ImageUpdateAutomation) interface{} { + gvk := autov1.GroupVersion.WithKind("ImageUpdateAutomation") // TODO replace with constant + export := autov1.ImageUpdateAutomation{ + TypeMeta: metav1.TypeMeta{ + Kind: gvk.Kind, + APIVersion: gvk.GroupVersion().String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: update.Name, + Namespace: update.Namespace, + Labels: update.Labels, + Annotations: update.Annotations, + }, + Spec: update.Spec, + } + return export +} diff --git a/docs/cmd/flux_create_auto.md b/docs/cmd/flux_create_auto.md index dd24e331..bae311d3 100644 --- a/docs/cmd/flux_create_auto.md +++ b/docs/cmd/flux_create_auto.md @@ -32,4 +32,5 @@ being available. * [flux create](flux_create.md) - Create or update sources and resources * [flux create auto image-policy](flux_create_auto_image-policy.md) - Create or update an ImagePolicy object * [flux create auto image-repository](flux_create_auto_image-repository.md) - Create or update an ImageRepository object +* [flux create auto image-update](flux_create_auto_image-update.md) - Create or update an ImageUpdateAutomation object diff --git a/docs/cmd/flux_create_auto_image-update.md b/docs/cmd/flux_create_auto_image-update.md new file mode 100644 index 00000000..57571afc --- /dev/null +++ b/docs/cmd/flux_create_auto_image-update.md @@ -0,0 +1,42 @@ +## flux create auto image-update + +Create or update an ImageUpdateAutomation object + +### Synopsis + +The create auto image-update command generates an ImageUpdateAutomation resource. +An ImageUpdateAutomation object specifies an automated update to images +mentioned in YAMLs in a git repository. + +``` +flux create auto image-update [flags] +``` + +### Options + +``` + --author-email string the email to use for commit author + --author-name string the name to use for commit author + --branch string the branch to push commits to + --commit-template string a template for commit messages + --git-repo-ref string the name of a GitRepository resource with details of the upstream git repository + -h, --help help for image-update +``` + +### Options inherited from parent commands + +``` + --context string kubernetes context to use + --export export in YAML format to stdout + --interval duration source sync interval (default 1m0s) + --kubeconfig string path to the kubeconfig file (default "~/.kube/config") + --label strings set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2) + -n, --namespace string the namespace scope for this operation (default "flux-system") + --timeout duration timeout for this operation (default 5m0s) + --verbose print generated objects +``` + +### SEE ALSO + +* [flux create auto](flux_create_auto.md) - Create or update resources dealing with automation +