From 6f834bc37a6a32e0437c36f44efdf059ff50affa Mon Sep 17 00:00:00 2001 From: Charles Moulliard Date: Tue, 24 Dec 2024 10:45:59 +0100 Subject: [PATCH] Improve the code to print the secrets using printTable (#464) Signed-off-by: cmoulliard --- pkg/cmd/get/clusters.go | 106 +++----------------- pkg/cmd/get/root.go | 2 +- pkg/cmd/get/secrets.go | 152 ++++++++++++++++------------- pkg/cmd/get/secrets_test.go | 47 +++++---- pkg/cmd/get/templates/secrets.tmpl | 7 -- pkg/entity/allocated.go | 6 ++ pkg/entity/capacity.go | 7 ++ pkg/entity/cluster.go | 10 ++ pkg/entity/node.go | 9 ++ pkg/entity/secret.go | 11 +++ pkg/printer/cluster.go | 64 ++++++++++++ pkg/printer/printer.go | 30 ++++++ pkg/printer/secret.go | 61 ++++++++++++ 13 files changed, 328 insertions(+), 184 deletions(-) delete mode 100644 pkg/cmd/get/templates/secrets.tmpl create mode 100644 pkg/entity/allocated.go create mode 100644 pkg/entity/capacity.go create mode 100644 pkg/entity/cluster.go create mode 100644 pkg/entity/node.go create mode 100644 pkg/entity/secret.go create mode 100644 pkg/printer/cluster.go create mode 100644 pkg/printer/printer.go create mode 100644 pkg/printer/secret.go diff --git a/pkg/cmd/get/clusters.go b/pkg/cmd/get/clusters.go index 6c3bac36..22a13be2 100644 --- a/pkg/cmd/get/clusters.go +++ b/pkg/cmd/get/clusters.go @@ -1,22 +1,23 @@ package get import ( - "bytes" "context" "fmt" "github.com/cnoe-io/idpbuilder/api/v1alpha1" "github.com/cnoe-io/idpbuilder/pkg/cmd/helpers" + "github.com/cnoe-io/idpbuilder/pkg/entity" "github.com/cnoe-io/idpbuilder/pkg/k8s" "github.com/cnoe-io/idpbuilder/pkg/kind" + "github.com/cnoe-io/idpbuilder/pkg/printer" "github.com/cnoe-io/idpbuilder/pkg/util" "github.com/spf13/cobra" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "k8s.io/cli-runtime/pkg/printers" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" + "os" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/kind/pkg/cluster" "slices" @@ -28,34 +29,6 @@ type ClusterManager struct { clients map[string]client.Client // map of cluster name to client } -type Cluster struct { - Name string - URLKubeApi string - KubePort int32 - TlsCheck bool - ExternalPort int32 - Nodes []Node -} - -type Node struct { - Name string - InternalIP string - ExternalIP string - Capacity Capacity - Allocated Allocated -} - -type Capacity struct { - Memory float64 - Pods int64 - Cpu int64 -} - -type Allocated struct { - Cpu string - Memory string -} - var ClustersCmd = &cobra.Command{ Use: "clusters", Short: "Get idp clusters", @@ -74,13 +47,15 @@ func list(cmd *cobra.Command, args []string) error { if err != nil { return err } else { - // Convert the list of the clusters to Table of clusters - printTable(printers.PrintOptions{}, generateClusterTable(clusters)) - return nil + clusterPrinter := printer.ClusterPrinter{ + Clusters: clusters, + OutWriter: os.Stdout, + } + return clusterPrinter.PrintOutput(outputFormat) } } -func populateClusterList() ([]Cluster, error) { +func populateClusterList() ([]entity.Cluster, error) { logger := helpers.CmdLogger detectOpt, err := util.DetectKindNodeProvider() @@ -106,7 +81,7 @@ func populateClusterList() ([]Cluster, error) { } // Create an empty array of clusters to collect the information - clusterList := []Cluster{} + clusterList := []entity.Cluster{} // List the idp builder clusters according to the provider: podman or docker provider := cluster.NewProvider(cluster.ProviderWithLogger(kind.KindLoggerFromLogr(&logger)), detectOpt) @@ -122,7 +97,7 @@ func populateClusterList() ([]Cluster, error) { } for _, cluster := range clusters { - aCluster := Cluster{Name: cluster} + aCluster := entity.Cluster{Name: cluster} // Search about the idp cluster within the kubeconfig file and show information c, found := findClusterByName(config, "kind-"+cluster) @@ -164,7 +139,7 @@ func populateClusterList() ([]Cluster, error) { for _, node := range nodeList.Items { nodeName := node.Name - aNode := Node{} + aNode := entity.Node{} aNode.Name = nodeName for _, addr := range node.Status.Addresses { @@ -183,7 +158,7 @@ func populateClusterList() ([]Cluster, error) { cpu := resources[corev1.ResourceCPU] pods := resources[corev1.ResourcePods] - aNode.Capacity = Capacity{ + aNode.Capacity = entity.Capacity{ Memory: float64(memory.Value()) / (1024 * 1024 * 1024), Cpu: cpu.Value(), Pods: pods.Value(), @@ -206,60 +181,11 @@ func populateClusterList() ([]Cluster, error) { return clusterList, nil } -func generateClusterTable(clusterTable []Cluster) metav1.Table { - table := &metav1.Table{} - table.ColumnDefinitions = []metav1.TableColumnDefinition{ - {Name: "Name", Type: "string"}, - {Name: "External-Port", Type: "string"}, - {Name: "Kube-Api", Type: "string"}, - {Name: "TLS", Type: "string"}, - {Name: "Kube-Port", Type: "string"}, - {Name: "Nodes", Type: "string"}, - } - for _, cluster := range clusterTable { - row := metav1.TableRow{ - Cells: []interface{}{ - cluster.Name, - cluster.ExternalPort, - cluster.URLKubeApi, - cluster.TlsCheck, - cluster.KubePort, - generateNodeData(cluster.Nodes), - }, - } - table.Rows = append(table.Rows, row) - } - return *table -} - -func printTable(opts printers.PrintOptions, table metav1.Table) { - logger := helpers.CmdLogger - out := bytes.NewBuffer([]byte{}) - printer := printers.NewTablePrinter(opts) - err := printer.PrintObj(&table, out) - if err != nil { - logger.Error(err, "failed to print the table.") - return - } - fmt.Println(out.String()) -} - -func generateNodeData(nodes []Node) string { - var result string - for i, aNode := range nodes { - result += aNode.Name - if i < len(nodes)-1 { - result += "," - } - } - return result -} - -func printAllocatedResources(ctx context.Context, k8sClient client.Client, nodeName string) (Allocated, error) { +func printAllocatedResources(ctx context.Context, k8sClient client.Client, nodeName string) (entity.Allocated, error) { // List all pods on the specified node var podList corev1.PodList if err := k8sClient.List(ctx, &podList, client.MatchingFields{"spec.nodeName": nodeName}); err != nil { - return Allocated{}, fmt.Errorf("failed to list pods on node %s.", nodeName) + return entity.Allocated{}, fmt.Errorf("failed to list pods on node %s.", nodeName) } // Initialize counters for CPU and memory requests @@ -278,7 +204,7 @@ func printAllocatedResources(ctx context.Context, k8sClient client.Client, nodeN } } - allocated := Allocated{ + allocated := entity.Allocated{ Memory: totalMemory.String(), Cpu: totalCPU.String(), } diff --git a/pkg/cmd/get/root.go b/pkg/cmd/get/root.go index c767b8c4..0bd62036 100644 --- a/pkg/cmd/get/root.go +++ b/pkg/cmd/get/root.go @@ -22,7 +22,7 @@ func init() { GetCmd.AddCommand(ClustersCmd) GetCmd.AddCommand(SecretsCmd) GetCmd.PersistentFlags().StringSliceVarP(&packages, "packages", "p", []string{}, "names of packages.") - GetCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "", "Output format. json or yaml.") + GetCmd.PersistentFlags().StringVarP(&outputFormat, "output", "o", "table", "Output format: table (default if not specified), json or yaml.") GetCmd.PersistentFlags().StringVarP(&helpers.KubeConfigPath, "kubeconfig", "", "", "kube config file Path.") } diff --git a/pkg/cmd/get/secrets.go b/pkg/cmd/get/secrets.go index dc6d94c2..e731b24c 100644 --- a/pkg/cmd/get/secrets.go +++ b/pkg/cmd/get/secrets.go @@ -2,13 +2,14 @@ package get import ( "context" - "embed" - "encoding/json" "fmt" + "github.com/cnoe-io/idpbuilder/pkg/entity" + "github.com/cnoe-io/idpbuilder/pkg/printer" "io" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "os" "path/filepath" - "text/template" + "strings" "github.com/cnoe-io/idpbuilder/api/v1alpha1" "github.com/cnoe-io/idpbuilder/pkg/build" @@ -20,19 +21,14 @@ import ( "k8s.io/apimachinery/pkg/selection" "k8s.io/client-go/util/homedir" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/yaml" ) const ( - secretTemplatePath = "templates/secrets.tmpl" argoCDAdminUsername = "admin" argoCDInitialAdminSecretName = "argocd-initial-admin-secret" giteaAdminSecretName = "gitea-credential" ) -//go:embed templates -var templates embed.FS - var SecretsCmd = &cobra.Command{ Use: "secrets", Short: "retrieve secrets from the cluster", @@ -42,10 +38,12 @@ var SecretsCmd = &cobra.Command{ } // well known secrets that are part of the core packages -var corePkgSecrets = map[string][]string{ - "argocd": []string{argoCDInitialAdminSecretName}, - "gitea": []string{giteaAdminSecretName}, -} +var ( + corePkgSecrets = map[string][]string{ + "argocd": []string{argoCDInitialAdminSecretName}, + "gitea": []string{giteaAdminSecretName}, + } +) type TemplateData struct { Name string `json:"name"` @@ -85,7 +83,11 @@ func getSecretsE(cmd *cobra.Command, args []string) error { func printAllPackageSecrets(ctx context.Context, outWriter io.Writer, kubeClient client.Client, format string) error { selector := labels.NewSelector() - secretsToPrint := make([]any, 0, 2) + secrets := []entity.Secret{} + secretPrinter := printer.SecretPrinter{ + Secrets: secrets, + OutWriter: outWriter, + } for k, v := range corePkgSecrets { for i := range v { @@ -96,29 +98,34 @@ func printAllPackageSecrets(ctx context.Context, outWriter io.Writer, kubeClient } return fmt.Errorf("getting secret %s in %s: %w", v[i], k, sErr) } - secretsToPrint = append(secretsToPrint, secretToTemplateData(secret)) + secrets = append(secrets, populateSecret(secret, true)) } } - secrets, err := getSecretsByCNOELabel(ctx, kubeClient, selector) + cnoeLabelSecrets, err := getSecretsByCNOELabel(ctx, kubeClient, selector) if err != nil { return fmt.Errorf("listing secrets: %w", err) } - for i := range secrets.Items { - secretsToPrint = append(secretsToPrint, secretToTemplateData(secrets.Items[i])) + for i := range cnoeLabelSecrets.Items { + secrets = append(secrets, populateSecret(cnoeLabelSecrets.Items[i], false)) } - if len(secretsToPrint) == 0 { + if len(secrets) == 0 { fmt.Println("no secrets found") return nil } - return printOutput(secretTemplatePath, outWriter, secretsToPrint, format) + + secretPrinter.Secrets = secrets + return secretPrinter.PrintOutput(format) } func printPackageSecrets(ctx context.Context, outWriter io.Writer, kubeClient client.Client, format string) error { selector := labels.NewSelector() - secretsToPrint := make([]any, 0, 2) + secrets := []entity.Secret{} + secretPrinter := printer.SecretPrinter{ + OutWriter: outWriter, + } for i := range packages { p := packages[i] @@ -132,7 +139,7 @@ func printPackageSecrets(ctx context.Context, outWriter io.Writer, kubeClient cl } return fmt.Errorf("getting secret %s in %s: %w", secretNames[j], p, sErr) } - secretsToPrint = append(secretsToPrint, secretToTemplateData(secret)) + secrets = append(secrets, populateSecret(secret, true)) } continue } @@ -144,69 +151,82 @@ func printPackageSecrets(ctx context.Context, outWriter io.Writer, kubeClient cl pkgSelector := selector.Add(*req) - secrets, pErr := getSecretsByCNOELabel(ctx, kubeClient, pkgSelector) - if pErr != nil { - return fmt.Errorf("listing secrets: %w", pErr) + cnoeLabelSecrets, err := getSecretsByCNOELabel(ctx, kubeClient, pkgSelector) + if err != nil { + return fmt.Errorf("listing secrets: %w", err) } - for j := range secrets.Items { - secretsToPrint = append(secretsToPrint, secretToTemplateData(secrets.Items[j])) + for i := range cnoeLabelSecrets.Items { + secrets = append(secrets, populateSecret(cnoeLabelSecrets.Items[i], false)) + } + + if len(secrets) == 0 { + fmt.Println("no secrets found") + return nil } } - return printOutput(secretTemplatePath, outWriter, secretsToPrint, format) + secretPrinter.Secrets = secrets + return secretPrinter.PrintOutput(format) } -func renderTemplate(templatePath string, outWriter io.Writer, data []any) error { - tmpl, err := templates.ReadFile(templatePath) - if err != nil { - return fmt.Errorf("failed to read template: %w", err) +func generateSecretTable(secretTable []entity.Secret) metav1.Table { + table := &metav1.Table{} + table.ColumnDefinitions = []metav1.TableColumnDefinition{ + {Name: "Name", Type: "string"}, + {Name: "Namespace", Type: "string"}, + {Name: "Username", Type: "string"}, + {Name: "Password", Type: "string"}, + {Name: "Token", Type: "string"}, + {Name: "Data", Type: "string"}, } + for _, secret := range secretTable { + var dataEntries []string - t, err := template.New("secrets").Parse(string(tmpl)) - if err != nil { - return fmt.Errorf("parsing template: %w", err) - } - for i := range data { - tErr := t.Execute(outWriter, data[i]) - if tErr != nil { - return fmt.Errorf("executing template for data %s : %w", data[i], tErr) + if !secret.IsCore { + for key, value := range secret.Data { + dataEntries = append(dataEntries, fmt.Sprintf("%s=%s", key, value)) + } } - } - return nil -} - -func printOutput(templatePath string, outWriter io.Writer, data []any, format string) error { - switch format { - case "json": - enc := json.NewEncoder(outWriter) - enc.SetEscapeHTML(false) - enc.SetIndent("", " ") - return enc.Encode(data) - case "yaml": - b, err := yaml.Marshal(data) - if err != nil { - return err + dataString := strings.Join(dataEntries, ", ") + row := metav1.TableRow{ + Cells: []interface{}{ + secret.Name, + secret.Namespace, + secret.Username, + secret.Password, + secret.Token, + dataString, + }, } - _, err = outWriter.Write(b) - return err - case "": - return renderTemplate(templatePath, outWriter, data) - default: - return fmt.Errorf("output format %s is not supported", format) + table.Rows = append(table.Rows, row) } + return *table } -func secretToTemplateData(s v1.Secret) TemplateData { - data := TemplateData{ +func populateSecret(s v1.Secret, isCoreSecret bool) entity.Secret { + secret := entity.Secret{ Name: s.Name, Namespace: s.Namespace, - Data: make(map[string]string), } - for k, v := range s.Data { - data.Data[k] = string(v) + + if isCoreSecret { + secret.IsCore = true + secret.Username = string(s.Data["username"]) + secret.Password = string(s.Data["password"]) + secret.Token = string(s.Data["token"]) + secret.Data = nil + } else { + newData := make(map[string]string) + for key, value := range s.Data { + newData[key] = string(value) + } + if len(newData) > 0 { + secret.Data = newData + } } - return data + + return secret } func getSecretsByCNOELabel(ctx context.Context, kubeClient client.Client, l labels.Selector) (v1.SecretList, error) { diff --git a/pkg/cmd/get/secrets_test.go b/pkg/cmd/get/secrets_test.go index 6bd3d0f0..07842c1c 100644 --- a/pkg/cmd/get/secrets_test.go +++ b/pkg/cmd/get/secrets_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/cnoe-io/idpbuilder/api/v1alpha1" + "github.com/cnoe-io/idpbuilder/pkg/entity" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" v1 "k8s.io/api/core/v1" @@ -97,7 +98,7 @@ func TestPrintPackageSecrets(t *testing.T) { fClient.On("Get", ctx, c.getKeys[j], mock.Anything, mock.Anything).Return(c.err) } - err := printPackageSecrets(ctx, io.Discard, fClient, "") + err := printPackageSecrets(ctx, io.Discard, fClient, "table") fClient.AssertExpectations(t) assert.Nil(t, err) } @@ -134,7 +135,7 @@ func TestPrintAllPackageSecrets(t *testing.T) { for j := range c.getKeys { fClient.On("Get", ctx, c.getKeys[j], mock.Anything, mock.Anything).Return(c.err) } - err := printAllPackageSecrets(ctx, io.Discard, fClient, "") + err := printAllPackageSecrets(ctx, io.Discard, fClient, "table") fClient.AssertExpectations(t) assert.Nil(t, err) } @@ -144,26 +145,24 @@ func TestOutput(t *testing.T) { ctx := context.Background() r, _ := labels.NewRequirement(v1alpha1.CLISecretLabelKey, selection.Equals, []string{v1alpha1.CLISecretLabelValue}) - corePkgData := map[string]TemplateData{ + corePkgData := map[string]entity.Secret{ argoCDInitialAdminSecretName: { + IsCore: true, Name: argoCDInitialAdminSecretName, Namespace: "argocd", - Data: map[string]string{ - "username": "admin", - "password": "abc", - }, + Username: "admin", + Password: "abc", }, giteaAdminSecretName: { + IsCore: true, Name: giteaAdminSecretName, Namespace: "gitea", - Data: map[string]string{ - "username": "admin", - "password": "abc", - }, + Username: "admin", + Password: "abc", }, } - packageData := map[string]TemplateData{ + packageData := map[string]entity.Secret{ "name1": { Name: "name1", Namespace: "ns1", @@ -190,12 +189,12 @@ func TestOutput(t *testing.T) { fClient.On("Get", ctx, client.ObjectKey{Name: argoCDInitialAdminSecretName, Namespace: "argocd"}, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { arg := args.Get(2).(*v1.Secret) - sec := templateDataToSecret(corePkgData[argoCDInitialAdminSecretName]) + sec := secretDataToSecret(corePkgData[argoCDInitialAdminSecretName]) *arg = sec }).Return(nil) fClient.On("Get", ctx, client.ObjectKey{Name: giteaAdminSecretName, Namespace: "gitea"}, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { arg := args.Get(2).(*v1.Secret) - sec := templateDataToSecret(corePkgData[giteaAdminSecretName]) + sec := secretDataToSecret(corePkgData[giteaAdminSecretName]) *arg = sec }).Return(nil) @@ -203,7 +202,7 @@ func TestOutput(t *testing.T) { arg := args.Get(1).(*v1.SecretList) secs := make([]v1.Secret, 0, 2) for k := range packageData { - s := templateDataToSecret(packageData[k]) + s := secretDataToSecret(packageData[k]) secs = append(secs, s) } arg.Items = secs @@ -217,7 +216,7 @@ func TestOutput(t *testing.T) { assert.Nil(t, err) // verify received json data - var received []TemplateData + var received []entity.Secret err = json.Unmarshal(buffer.Bytes(), &received) assert.Nil(t, err) assert.Equal(t, 4, len(received)) @@ -226,6 +225,8 @@ func TestOutput(t *testing.T) { rec := received[i] c, ok := corePkgData[rec.Name] if ok { + // Set the IsCore bool field to true as the v1.Secret don't include it ! + rec.IsCore = true assert.Equal(t, c, rec) delete(corePkgData, rec.Name) continue @@ -236,17 +237,23 @@ func TestOutput(t *testing.T) { delete(packageData, rec.Name) continue } - t.Fatalf("found an invalid element: %s", rec) + t.Fatalf("found an invalid element: %s", rec.Name) } } assert.Equal(t, 0, len(corePkgData)) assert.Equal(t, 0, len(packageData)) } -func templateDataToSecret(data TemplateData) v1.Secret { +func secretDataToSecret(data entity.Secret) v1.Secret { d := make(map[string][]byte) - for k := range data.Data { - d[k] = []byte(data.Data[k]) + if data.IsCore { + d["username"] = []byte(data.Username) + d["password"] = []byte(data.Password) + d["token"] = []byte(data.Token) + } else { + for k := range data.Data { + d[k] = []byte(data.Data[k]) + } } return v1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: data.Name, Namespace: data.Namespace}, diff --git a/pkg/cmd/get/templates/secrets.tmpl b/pkg/cmd/get/templates/secrets.tmpl deleted file mode 100644 index 18bc9029..00000000 --- a/pkg/cmd/get/templates/secrets.tmpl +++ /dev/null @@ -1,7 +0,0 @@ ---------------------------- -Name: {{ .Name }} -Namespace: {{ .Namespace }} -Data: -{{- range $key, $value := .Data }} - {{ $key }} : {{ $value }} -{{- end }} diff --git a/pkg/entity/allocated.go b/pkg/entity/allocated.go new file mode 100644 index 00000000..297f024b --- /dev/null +++ b/pkg/entity/allocated.go @@ -0,0 +1,6 @@ +package entity + +type Allocated struct { + Cpu string + Memory string +} diff --git a/pkg/entity/capacity.go b/pkg/entity/capacity.go new file mode 100644 index 00000000..cbe910dd --- /dev/null +++ b/pkg/entity/capacity.go @@ -0,0 +1,7 @@ +package entity + +type Capacity struct { + Memory float64 + Pods int64 + Cpu int64 +} diff --git a/pkg/entity/cluster.go b/pkg/entity/cluster.go new file mode 100644 index 00000000..582e29c0 --- /dev/null +++ b/pkg/entity/cluster.go @@ -0,0 +1,10 @@ +package entity + +type Cluster struct { + Name string + URLKubeApi string + KubePort int32 + TlsCheck bool + ExternalPort int32 + Nodes []Node +} diff --git a/pkg/entity/node.go b/pkg/entity/node.go new file mode 100644 index 00000000..7d07d5a6 --- /dev/null +++ b/pkg/entity/node.go @@ -0,0 +1,9 @@ +package entity + +type Node struct { + Name string + InternalIP string + ExternalIP string + Capacity Capacity + Allocated Allocated +} diff --git a/pkg/entity/secret.go b/pkg/entity/secret.go new file mode 100644 index 00000000..f7c2b53a --- /dev/null +++ b/pkg/entity/secret.go @@ -0,0 +1,11 @@ +package entity + +type Secret struct { + IsCore bool + Name string `json:"name"` + Namespace string `json:"namespace"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Token string `json:"token,omitempty"` + Data map[string]string `json:"data,omitempty"` +} diff --git a/pkg/printer/cluster.go b/pkg/printer/cluster.go new file mode 100644 index 00000000..4ebd4c55 --- /dev/null +++ b/pkg/printer/cluster.go @@ -0,0 +1,64 @@ +package printer + +import ( + "fmt" + "github.com/cnoe-io/idpbuilder/pkg/entity" + "io" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ClusterPrinter struct { + Clusters []entity.Cluster + OutWriter io.Writer +} + +func (cp ClusterPrinter) PrintOutput(format string) error { + switch format { + case "json": + return PrintDataAsJson(cp.Clusters, cp.OutWriter) + case "yaml": + return PrintDataAsYaml(cp.Clusters, cp.OutWriter) + case "table": + return PrintDataAsTable(generateClusterTable(cp.Clusters), cp.OutWriter) + default: + return fmt.Errorf("output format %s is not supported", format) + } +} + +func generateClusterTable(input []entity.Cluster) metav1.Table { + table := &metav1.Table{} + table.ColumnDefinitions = []metav1.TableColumnDefinition{ + {Name: "Name", Type: "string"}, + {Name: "External-Port", Type: "string"}, + {Name: "Kube-Api", Type: "string"}, + {Name: "TLS", Type: "string"}, + {Name: "Kube-Port", Type: "string"}, + {Name: "Nodes", Type: "string"}, + } + + for _, cluster := range input { + row := metav1.TableRow{ + Cells: []interface{}{ + cluster.Name, + cluster.ExternalPort, + cluster.URLKubeApi, + cluster.TlsCheck, + cluster.KubePort, + generateNodeData(cluster.Nodes), + }, + } + table.Rows = append(table.Rows, row) + } + return *table +} + +func generateNodeData(nodes []entity.Node) string { + var result string + for i, aNode := range nodes { + result += aNode.Name + if i < len(nodes)-1 { + result += "," + } + } + return result +} diff --git a/pkg/printer/printer.go b/pkg/printer/printer.go new file mode 100644 index 00000000..634c314e --- /dev/null +++ b/pkg/printer/printer.go @@ -0,0 +1,30 @@ +package printer + +import ( + "encoding/json" + "io" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/cli-runtime/pkg/printers" + "sigs.k8s.io/yaml" +) + +func PrintDataAsTable(table metav1.Table, outWriter io.Writer) error { + printer := printers.NewTablePrinter(printers.PrintOptions{}) + return printer.PrintObj(&table, outWriter) +} + +func PrintDataAsJson(data any, outWriter io.Writer) error { + enc := json.NewEncoder(outWriter) + enc.SetEscapeHTML(false) + enc.SetIndent("", " ") + return enc.Encode(data) +} + +func PrintDataAsYaml(data any, outWriter io.Writer) error { + b, err := yaml.Marshal(data) + if err != nil { + return err + } + _, err = outWriter.Write(b) + return err +} diff --git a/pkg/printer/secret.go b/pkg/printer/secret.go new file mode 100644 index 00000000..425f961d --- /dev/null +++ b/pkg/printer/secret.go @@ -0,0 +1,61 @@ +package printer + +import ( + "fmt" + "github.com/cnoe-io/idpbuilder/pkg/entity" + "io" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "strings" +) + +type SecretPrinter struct { + Secrets []entity.Secret + OutWriter io.Writer +} + +func (sp SecretPrinter) PrintOutput(format string) error { + switch format { + case "json": + return PrintDataAsJson(sp.Secrets, sp.OutWriter) + case "yaml": + return PrintDataAsYaml(sp.Secrets, sp.OutWriter) + case "table": + return PrintDataAsTable(generateSecretTable(sp.Secrets), sp.OutWriter) + default: + return fmt.Errorf("output format %s is not supported", format) + } +} + +func generateSecretTable(secretTable []entity.Secret) metav1.Table { + table := &metav1.Table{} + table.ColumnDefinitions = []metav1.TableColumnDefinition{ + {Name: "Name", Type: "string"}, + {Name: "Namespace", Type: "string"}, + {Name: "Username", Type: "string"}, + {Name: "Password", Type: "string"}, + {Name: "Token", Type: "string"}, + {Name: "Data", Type: "string"}, + } + for _, secret := range secretTable { + var dataEntries []string + + if !secret.IsCore { + for key, value := range secret.Data { + dataEntries = append(dataEntries, fmt.Sprintf("%s=%s", key, value)) + } + } + dataString := strings.Join(dataEntries, ", ") + row := metav1.TableRow{ + Cells: []interface{}{ + secret.Name, + secret.Namespace, + secret.Username, + secret.Password, + secret.Token, + dataString, + }, + } + table.Rows = append(table.Rows, row) + } + return *table +}