From f127adc8ea49f47d4e60a4fc4824c0f8c69d81d2 Mon Sep 17 00:00:00 2001
From: stefanprodan <stefan.prodan@gmail.com>
Date: Thu, 30 Apr 2020 00:12:16 +0300
Subject: [PATCH] Implement export to YAML - add export commands for git
 sources and kustomizations - add export e2e tests

---
 .github/workflows/e2e.yaml     |  4 ++
 cmd/tk/delete_source_git.go    |  2 +-
 cmd/tk/export.go               | 20 ++++++++
 cmd/tk/export_kustomization.go | 94 ++++++++++++++++++++++++++++++++++
 cmd/tk/export_source.go        | 14 +++++
 cmd/tk/export_source_git.go    | 93 +++++++++++++++++++++++++++++++++
 cmd/tk/main.go                 |  6 +++
 go.mod                         |  1 +
 8 files changed, 233 insertions(+), 1 deletion(-)
 create mode 100644 cmd/tk/export.go
 create mode 100644 cmd/tk/export_kustomization.go
 create mode 100644 cmd/tk/export_source.go
 create mode 100644 cmd/tk/export_source_git.go

diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml
index 004f731f..42e9e6eb 100644
--- a/.github/workflows/e2e.yaml
+++ b/.github/workflows/e2e.yaml
@@ -80,6 +80,10 @@ jobs:
       - name: tk resume kustomization
         run: |
           ./bin/tk resume kustomization podinfo
+      - name: tk export
+        run: |
+          ./bin/tk export source git --all
+          ./bin/tk export kustomization --all
       - name: tk delete kustomization
         run: |
           ./bin/tk delete kustomization podinfo --silent
diff --git a/cmd/tk/delete_source_git.go b/cmd/tk/delete_source_git.go
index ea9fb5a1..9ff9bb39 100644
--- a/cmd/tk/delete_source_git.go
+++ b/cmd/tk/delete_source_git.go
@@ -3,8 +3,8 @@ package main
 import (
 	"context"
 	"fmt"
-	sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
 
+	sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
 	"github.com/manifoldco/promptui"
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/types"
diff --git a/cmd/tk/export.go b/cmd/tk/export.go
new file mode 100644
index 00000000..55aebbec
--- /dev/null
+++ b/cmd/tk/export.go
@@ -0,0 +1,20 @@
+package main
+
+import (
+	"github.com/spf13/cobra"
+)
+
+var exportCmd = &cobra.Command{
+	Use:   "export",
+	Short: "Export commands",
+}
+
+var (
+	exportAll bool
+)
+
+func init() {
+	exportCmd.PersistentFlags().BoolVar(&exportAll, "all", false, "select all resources")
+
+	rootCmd.AddCommand(exportCmd)
+}
diff --git a/cmd/tk/export_kustomization.go b/cmd/tk/export_kustomization.go
new file mode 100644
index 00000000..b100297d
--- /dev/null
+++ b/cmd/tk/export_kustomization.go
@@ -0,0 +1,94 @@
+package main
+
+import (
+	"context"
+	"fmt"
+
+	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
+	"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"
+)
+
+var exportKsCmd = &cobra.Command{
+	Use:     "kustomization [name]",
+	Aliases: []string{"ks"},
+	Short:   "Export kustomization in YAML format",
+	RunE:    exportKsCmdRun,
+}
+
+func init() {
+	exportCmd.AddCommand(exportKsCmd)
+}
+
+func exportKsCmdRun(cmd *cobra.Command, args []string) error {
+	if !exportAll && len(args) < 1 {
+		return fmt.Errorf("kustomization name is required")
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
+
+	kubeClient, err := utils.kubeClient(kubeconfig)
+	if err != nil {
+		return err
+	}
+
+	if exportAll {
+		var list kustomizev1.KustomizationList
+		err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
+		if err != nil {
+			return err
+		}
+
+		if len(list.Items) == 0 {
+			logFailure("no kustomizations found in %s namespace", namespace)
+			return nil
+		}
+
+		for _, kustomization := range list.Items {
+			if err := exportKs(kustomization); err != nil {
+				return err
+			}
+		}
+	} else {
+		name := args[0]
+		namespacedName := types.NamespacedName{
+			Namespace: namespace,
+			Name:      name,
+		}
+		var kustomization kustomizev1.Kustomization
+		err = kubeClient.Get(ctx, namespacedName, &kustomization)
+		if err != nil {
+			return err
+		}
+		return exportKs(kustomization)
+	}
+	return nil
+}
+
+func exportKs(kustomization kustomizev1.Kustomization) error {
+	gvk := kustomizev1.GroupVersion.WithKind("Kustomization")
+	export := kustomizev1.Kustomization{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       gvk.Kind,
+			APIVersion: gvk.GroupVersion().String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      kustomization.Name,
+			Namespace: kustomization.Namespace,
+		},
+		Spec: kustomization.Spec,
+	}
+
+	data, err := yaml.Marshal(export)
+	if err != nil {
+		return err
+	}
+
+	fmt.Println("---")
+	fmt.Println(string(data))
+	return nil
+}
diff --git a/cmd/tk/export_source.go b/cmd/tk/export_source.go
new file mode 100644
index 00000000..9412d16b
--- /dev/null
+++ b/cmd/tk/export_source.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"github.com/spf13/cobra"
+)
+
+var exportSourceCmd = &cobra.Command{
+	Use:   "source",
+	Short: "Export source commands",
+}
+
+func init() {
+	exportCmd.AddCommand(exportSourceCmd)
+}
diff --git a/cmd/tk/export_source_git.go b/cmd/tk/export_source_git.go
new file mode 100644
index 00000000..18facc57
--- /dev/null
+++ b/cmd/tk/export_source_git.go
@@ -0,0 +1,93 @@
+package main
+
+import (
+	"context"
+	"fmt"
+
+	sourcev1 "github.com/fluxcd/source-controller/api/v1alpha1"
+	"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"
+)
+
+var exportSourceGitCmd = &cobra.Command{
+	Use:   "git [name]",
+	Short: "Export git source in YAML format",
+	RunE:  exportSourceGitCmdRun,
+}
+
+func init() {
+	exportSourceCmd.AddCommand(exportSourceGitCmd)
+}
+
+func exportSourceGitCmdRun(cmd *cobra.Command, args []string) error {
+	if !exportAll && len(args) < 1 {
+		return fmt.Errorf("kustomization name is required")
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
+
+	kubeClient, err := utils.kubeClient(kubeconfig)
+	if err != nil {
+		return err
+	}
+
+	if exportAll {
+		var list sourcev1.GitRepositoryList
+		err = kubeClient.List(ctx, &list, client.InNamespace(namespace))
+		if err != nil {
+			return err
+		}
+
+		if len(list.Items) == 0 {
+			logFailure("no source found in %s namespace", namespace)
+			return nil
+		}
+
+		for _, repository := range list.Items {
+			if err := exportGit(repository); err != nil {
+				return err
+			}
+		}
+	} else {
+		name := args[0]
+		namespacedName := types.NamespacedName{
+			Namespace: namespace,
+			Name:      name,
+		}
+		var repository sourcev1.GitRepository
+		err = kubeClient.Get(ctx, namespacedName, &repository)
+		if err != nil {
+			return err
+		}
+		return exportGit(repository)
+	}
+	return nil
+}
+
+func exportGit(source sourcev1.GitRepository) error {
+	gvk := sourcev1.GroupVersion.WithKind("GitRepository")
+	export := sourcev1.GitRepository{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       gvk.Kind,
+			APIVersion: gvk.GroupVersion().String(),
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      source.Name,
+			Namespace: source.Namespace,
+		},
+		Spec: source.Spec,
+	}
+
+	data, err := yaml.Marshal(export)
+	if err != nil {
+		return err
+	}
+
+	fmt.Println("---")
+	fmt.Println(string(data))
+	return nil
+}
diff --git a/cmd/tk/main.go b/cmd/tk/main.go
index 5ea1f48b..cedadbd1 100644
--- a/cmd/tk/main.go
+++ b/cmd/tk/main.go
@@ -38,6 +38,9 @@ var rootCmd = &cobra.Command{
   # Trigger a git sync
   tk sync source git webapp-latest
 
+  # Export git sources in YAML format
+  tk export source git --all > sources.yaml
+
   # Create a kustomization for deploying a series of microservices
   tk create kustomization webapp-dev \
     --source=webapp-latest \
@@ -56,6 +59,9 @@ var rootCmd = &cobra.Command{
   # Suspend a kustomization reconciliation
   tk suspend kustomization webapp-dev
 
+  # Export kustomizations in YAML format
+  tk export kustomization --all > kustomizations.yaml
+
   # Resume a kustomization reconciliation
   tk resume kustomization webapp-dev
 
diff --git a/go.mod b/go.mod
index e5d8063a..867428c3 100644
--- a/go.mod
+++ b/go.mod
@@ -12,6 +12,7 @@ require (
 	k8s.io/apimachinery v0.18.2
 	k8s.io/client-go v0.18.2
 	sigs.k8s.io/controller-runtime v0.6.0
+	sigs.k8s.io/yaml v1.2.0
 )
 
 // fix AKS auth
-- 
GitLab