From 4f52b77563c5aa13146982fefbedefd6259eefb9 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Fri, 4 Dec 2020 12:30:20 +0000 Subject: [PATCH] Factor out export command control flow The export command works the same way for most (all?) types. I have made it generic and moved it into export.go, then ported {export,create}_auto_image{repository,policy}.go to use it. Signed-off-by: Michael Bridgen --- cmd/flux/create_auto_imagepolicy.go | 2 +- cmd/flux/create_auto_imagerepository.go | 2 +- cmd/flux/export.go | 81 ++++++++++++++++++ cmd/flux/export_auto_imagepolicy.go | 107 +++++++++--------------- cmd/flux/export_auto_imagerepository.go | 95 ++++++++------------- 5 files changed, 156 insertions(+), 131 deletions(-) diff --git a/cmd/flux/create_auto_imagepolicy.go b/cmd/flux/create_auto_imagepolicy.go index f87e537c..384fd42f 100644 --- a/cmd/flux/create_auto_imagepolicy.go +++ b/cmd/flux/create_auto_imagepolicy.go @@ -98,7 +98,7 @@ func createAutoImagePolicyRun(cmd *cobra.Command, args []string) error { } if export { - return exportImagePolicy(policy) // defined with export command + return printExport(exportImagePolicy(&policy)) } // I don't need these until attempting to upsert the object, but diff --git a/cmd/flux/create_auto_imagerepository.go b/cmd/flux/create_auto_imagerepository.go index 2eeaffd4..274e8091 100644 --- a/cmd/flux/create_auto_imagerepository.go +++ b/cmd/flux/create_auto_imagerepository.go @@ -103,7 +103,7 @@ func createAutoImageRepositoryRun(cmd *cobra.Command, args []string) error { } if export { - return exportImageRepo(repo) // defined with export command + return printExport(exportImageRepository(&repo)) } // I don't need these until attempting to upsert the object, but diff --git a/cmd/flux/export.go b/cmd/flux/export.go index a94ad483..d80ed108 100644 --- a/cmd/flux/export.go +++ b/cmd/flux/export.go @@ -18,8 +18,15 @@ package main import ( "bytes" + "context" + "fmt" "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/yaml" + + "github.com/fluxcd/flux2/internal/utils" ) var exportCmd = &cobra.Command{ @@ -38,6 +45,80 @@ func init() { rootCmd.AddCommand(exportCmd) } +// exportable represents a type that you can fetch from the Kubernetes +// API, then tidy up for serialising. +type exportable interface { + objectContainer + Export() interface{} +} + +// exportableAt represents a type that has a list of values, each of +// which is exportable. +type exportableAt interface { + objectContainer + Len() int + ExportAt(i int) interface{} +} + +type exportCommand struct { + object exportable + list exportableAt +} + +func (export exportCommand) run(cmd *cobra.Command, args []string) error { + if !exportAll && len(args) < 1 { + return fmt.Errorf("name is required") + } + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) + if err != nil { + return err + } + + if exportAll { + err = kubeClient.List(ctx, export.list.AsClientObject(), client.InNamespace(namespace)) + if err != nil { + return err + } + + if export.list.Len() == 0 { + logger.Failuref("no objects found in %s namespace", namespace) + return nil + } + + for i := 0; i < export.list.Len(); i++ { + if err = printExport(export.list.ExportAt(i)); err != nil { + return err + } + } + } else { + name := args[0] + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + err = kubeClient.Get(ctx, namespacedName, export.object.AsClientObject()) + if err != nil { + return err + } + return printExport(export.object.Export()) + } + return nil +} + +func printExport(export interface{}) error { + data, err := yaml.Marshal(export) + if err != nil { + return err + } + fmt.Println("---") + fmt.Println(resourceToString(data)) + return nil +} + func resourceToString(data []byte) string { data = bytes.Replace(data, []byte(" creationTimestamp: null\n"), []byte(""), 1) data = bytes.Replace(data, []byte("status: {}\n"), []byte(""), 1) diff --git a/cmd/flux/export_auto_imagepolicy.go b/cmd/flux/export_auto_imagepolicy.go index 4bb376c8..595940c5 100644 --- a/cmd/flux/export_auto_imagepolicy.go +++ b/cmd/flux/export_auto_imagepolicy.go @@ -17,16 +17,10 @@ limitations under the License. package main import ( - "context" - "fmt" - "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/yaml" + "k8s.io/apimachinery/pkg/runtime" - "github.com/fluxcd/flux2/internal/utils" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1" ) @@ -40,60 +34,19 @@ var exportImagePolicyCmd = &cobra.Command{ # Export a specific policy flux export auto image-policy alpine1x > alpine1x.yaml `, - RunE: exportImagePolicyRun, + RunE: exportCommand{ + object: exportableImagePolicy{&imagev1.ImagePolicy{}}, + list: exportableImagePolicyList{&imagev1.ImagePolicyList{}}, + }.run, } func init() { exportAutoCmd.AddCommand(exportImagePolicyCmd) } -func exportImagePolicyRun(cmd *cobra.Command, args []string) error { - if !exportAll && len(args) < 1 { - return fmt.Errorf("name is required") - } - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) - if err != nil { - return err - } - - if exportAll { - var list imagev1.ImagePolicyList - err = kubeClient.List(ctx, &list, client.InNamespace(namespace)) - if err != nil { - return err - } - - if len(list.Items) == 0 { - logger.Failuref("no imagepolicy objects found in %s namespace", namespace) - return nil - } - - for _, policy := range list.Items { - if err := exportImagePolicy(policy); err != nil { - return err - } - } - } else { - name := args[0] - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: name, - } - var policy imagev1.ImagePolicy - err = kubeClient.Get(ctx, namespacedName, &policy) - if err != nil { - return err - } - return exportImagePolicy(policy) - } - return nil -} - -func exportImagePolicy(policy imagev1.ImagePolicy) error { +// Export returns a ImagePolicy value which has extraneous information +// stripped out. +func exportImagePolicy(item *imagev1.ImagePolicy) interface{} { gvk := imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind) export := imagev1.ImagePolicy{ TypeMeta: metav1.TypeMeta{ @@ -101,20 +54,40 @@ func exportImagePolicy(policy imagev1.ImagePolicy) error { APIVersion: gvk.GroupVersion().String(), }, ObjectMeta: metav1.ObjectMeta{ - Name: policy.Name, - Namespace: policy.Namespace, - Labels: policy.Labels, - Annotations: policy.Annotations, + Name: item.Name, + Namespace: item.Namespace, + Labels: item.Labels, + Annotations: item.Annotations, }, - Spec: policy.Spec, + Spec: item.Spec, } + return export +} - data, err := yaml.Marshal(export) - if err != nil { - return err - } +type exportableImagePolicy struct { + policy *imagev1.ImagePolicy +} + +func (ex exportableImagePolicy) AsClientObject() runtime.Object { + return ex.policy +} + +func (ex exportableImagePolicy) Export() interface{} { + return exportImagePolicy(ex.policy) +} + +type exportableImagePolicyList struct { + list *imagev1.ImagePolicyList +} + +func (ex exportableImagePolicyList) AsClientObject() runtime.Object { + return ex.list +} + +func (ex exportableImagePolicyList) Len() int { + return len(ex.list.Items) +} - fmt.Println("---") - fmt.Println(resourceToString(data)) - return nil +func (ex exportableImagePolicyList) ExportAt(i int) interface{} { + return exportImagePolicy(&ex.list.Items[i]) } diff --git a/cmd/flux/export_auto_imagerepository.go b/cmd/flux/export_auto_imagerepository.go index da988be2..d8a407a5 100644 --- a/cmd/flux/export_auto_imagerepository.go +++ b/cmd/flux/export_auto_imagerepository.go @@ -17,16 +17,10 @@ limitations under the License. package main import ( - "context" - "fmt" - "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/yaml" + "k8s.io/apimachinery/pkg/runtime" - "github.com/fluxcd/flux2/internal/utils" imagev1 "github.com/fluxcd/image-reflector-controller/api/v1alpha1" ) @@ -40,60 +34,17 @@ var exportImageRepositoryCmd = &cobra.Command{ # Export a Provider flux export auto image-repository alpine > alpine.yaml `, - RunE: exportImageRepositoryCmdRun, + RunE: exportCommand{ + object: exportableImageRepository{&imagev1.ImageRepository{}}, + list: exportableImageRepositoryList{&imagev1.ImageRepositoryList{}}, + }.run, } func init() { exportAutoCmd.AddCommand(exportImageRepositoryCmd) } -func exportImageRepositoryCmdRun(cmd *cobra.Command, args []string) error { - if !exportAll && len(args) < 1 { - return fmt.Errorf("name is required") - } - - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - - kubeClient, err := utils.KubeClient(kubeconfig, kubecontext) - if err != nil { - return err - } - - if exportAll { - var list imagev1.ImageRepositoryList - err = kubeClient.List(ctx, &list, client.InNamespace(namespace)) - if err != nil { - return err - } - - if len(list.Items) == 0 { - logger.Failuref("no imagerepository objects found in %s namespace", namespace) - return nil - } - - for _, imageRepo := range list.Items { - if err := exportImageRepo(imageRepo); err != nil { - return err - } - } - } else { - name := args[0] - namespacedName := types.NamespacedName{ - Namespace: namespace, - Name: name, - } - var imageRepo imagev1.ImageRepository - err = kubeClient.Get(ctx, namespacedName, &imageRepo) - if err != nil { - return err - } - return exportImageRepo(imageRepo) - } - return nil -} - -func exportImageRepo(repo imagev1.ImageRepository) error { +func exportImageRepository(repo *imagev1.ImageRepository) interface{} { gvk := imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind) export := imagev1.ImageRepository{ TypeMeta: metav1.TypeMeta{ @@ -108,13 +59,33 @@ func exportImageRepo(repo imagev1.ImageRepository) error { }, Spec: repo.Spec, } + return export +} - data, err := yaml.Marshal(export) - if err != nil { - return err - } +type exportableImageRepository struct { + repo *imagev1.ImageRepository +} + +func (ex exportableImageRepository) AsClientObject() runtime.Object { + return ex.repo +} + +func (ex exportableImageRepository) Export() interface{} { + return exportImageRepository(ex.repo) +} + +type exportableImageRepositoryList struct { + list *imagev1.ImageRepositoryList +} + +func (ex exportableImageRepositoryList) AsClientObject() runtime.Object { + return ex.list +} + +func (ex exportableImageRepositoryList) Len() int { + return len(ex.list.Items) +} - fmt.Println("---") - fmt.Println(resourceToString(data)) - return nil +func (ex exportableImageRepositoryList) ExportAt(i int) interface{} { + return exportImageRepository(&ex.list.Items[i]) }