From 1724f61d204e0c7ee686e919fc17e2c246979a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Valais?= Date: Fri, 3 Feb 2023 16:03:43 +0100 Subject: [PATCH 1/3] add secret and namespace completion For this to work, you will need kubectl 1.26 and a shim that you can create with the following command: cat <<'EOF' >~/bin/kubectl_complete-view_secret && chmod u+x ~/bin/kubectl_complete-view_secret kubectl view-secret __complete "$@" EOF See: https://github.com/kubernetes/kubernetes/pull/105867 --- pkg/cmd/view-secret.go | 100 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/pkg/cmd/view-secret.go b/pkg/cmd/view-secret.go index e346281..379cf46 100644 --- a/pkg/cmd/view-secret.go +++ b/pkg/cmd/view-secret.go @@ -82,6 +82,7 @@ func NewCmdViewSecret() *cobra.Command { return nil }, + ValidArgsFunction: getSecrets, } cmd.Flags(). @@ -92,6 +93,7 @@ func NewCmdViewSecret() *cobra.Command { cmd.Flags().StringVarP(&res.customContext, "context", "c", res.customContext, "override the current context") cmd.Flags().StringVarP(&res.kubeConfig, "kubeconfig", "k", res.kubeConfig, "explicitly provide the kubeconfig to use") + cmd.Root().RegisterFlagCompletionFunc("namespace", getNamespaces) return cmd } @@ -191,3 +193,101 @@ func ProcessSecret(outWriter, errWriter io.Writer, secret map[string]interface{} return nil } + +func getNamespaces(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + ctxOverride, _ := cmd.Flags().GetString("context") + kubeConfigOverride, _ := cmd.Flags().GetString("kubeconfig") + + var res, cmdErr bytes.Buffer + commandArgs := []string{"get", "namespaces", "-o", "json"} + + if ctxOverride != "" { + commandArgs = append(commandArgs, "--context", ctxOverride) + } + + if kubeConfigOverride != "" { + commandArgs = append(commandArgs, "--kubeconfig", kubeConfigOverride) + } + + out := exec.Command("kubectl", commandArgs...) + out.Stdout = &res + out.Stderr = &cmdErr + err := out.Run() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + var parsed struct { + Items []struct { + Metadata struct { + Name string `json:"name"` + } `json:"metadata,omitempty"` + } + } + if err := json.Unmarshal(res.Bytes(), &parsed); err != nil { + return nil, cobra.ShellCompDirectiveError + } + + // turn into a list of strings + var namespaces []string + for _, item := range parsed.Items { + namespaces = append(namespaces, item.Metadata.Name) + } + + return namespaces, cobra.ShellCompDirectiveNoFileComp +} + +func getSecrets(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + nsOverride, _ := cmd.Flags().GetString("namespace") + ctxOverride, _ := cmd.Flags().GetString("context") + kubeConfigOverride, _ := cmd.Flags().GetString("kubeconfig") + + var res, cmdErr bytes.Buffer + commandArgs := []string{"get", "secrets", "-o", "json"} + if nsOverride != "" { + commandArgs = append(commandArgs, "-n", nsOverride) + } + + if ctxOverride != "" { + commandArgs = append(commandArgs, "--context", ctxOverride) + } + + if kubeConfigOverride != "" { + commandArgs = append(commandArgs, "--kubeconfig", kubeConfigOverride) + } + + out := exec.Command("kubectl", commandArgs...) + out.Stdout = &res + out.Stderr = &cmdErr + err := out.Run() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + var parsed struct { + Items []struct { + Metadata struct { + Name string `json:"name"` + } `json:"metadata,omitempty"` + } + } + if err := json.Unmarshal(res.Bytes(), &parsed); err != nil { + return nil, cobra.ShellCompDirectiveError + } + + // turn into a list of strings + var names []string + for _, item := range parsed.Items { + names = append(names, item.Metadata.Name) + } + + return names, cobra.ShellCompDirectiveNoFileComp +} From 457b031eec218a098009d5fd0ea0243424618d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Valais?= Date: Fri, 3 Feb 2023 16:12:03 +0100 Subject: [PATCH 2/3] talk about completion in the readme --- README.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 69b9714..e1b3a34 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,13 @@ Instead you can now do: # print secret keys kubectl view-secret - + # decode specific entry kubectl view-secret - + # decode all contents kubectl view-secret -a/--all - + # print keys for secret in different namespace kubectl view-secret -n/--namespace @@ -36,6 +36,19 @@ Instead you can now do: ## Usage +### Shell completion + +To enable completion for this plugin, you will need to use kubectl 1.26 or +above. Then, create a script named `kubectl_complete-view_secret` with the +following content: + +```bash +#!/bin/bash +kubectl view-secret __complete "$@" +``` + +and put it somewhere in your PATH. + ### Krew This plugin is available through [krew](https://krew.dev) via `kubectl krew install view-secret`. @@ -43,6 +56,7 @@ This plugin is available through [krew](https://krew.dev) via `kubectl krew inst ### Binary releases #### GitHub + You can find the latest binaries in the [releases](https://github.com/elsesiy/kubectl-view-secret/releases) section. To install it, place it somewhere in your `$PATH` for `kubectl` to pick it up. @@ -50,6 +64,7 @@ To install it, place it somewhere in your `$PATH` for `kubectl` to pick it up. due to the enforced naming convention for plugins by `kubectl`. More on this [here](https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/#naming-a-plugin). #### AUR package + You can find the latest package description for Arch users [here](https://aur.archlinux.org/packages/kubectl-view-secret-bin). Contribution by [@jocelynthode](https://github.com/jocelynthode) From 64921149ee34926795cff815be02bb62f0909a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Valais?= Date: Fri, 3 Feb 2023 17:16:29 +0100 Subject: [PATCH 3/3] completion: also complete data keys --- pkg/cmd/view-secret.go | 74 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/view-secret.go b/pkg/cmd/view-secret.go index 379cf46..cbd559b 100644 --- a/pkg/cmd/view-secret.go +++ b/pkg/cmd/view-secret.go @@ -82,7 +82,7 @@ func NewCmdViewSecret() *cobra.Command { return nil }, - ValidArgsFunction: getSecrets, + ValidArgsFunction: getSecretsOrKeys, } cmd.Flags(). @@ -241,11 +241,21 @@ func getNamespaces(cmd *cobra.Command, args []string, toComplete string) ([]stri return namespaces, cobra.ShellCompDirectiveNoFileComp } -func getSecrets(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) > 0 { - return nil, cobra.ShellCompDirectiveNoFileComp +func getSecretsOrKeys(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // The first argument is the secret name. + if len(args) == 0 { + return getSecrets(cmd, args, toComplete) } + // The second argument is the key. + if len(args) == 1 { + return getSecretKeys(cmd, args, toComplete) + } + + return nil, cobra.ShellCompDirectiveNoFileComp +} + +func getSecrets(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { nsOverride, _ := cmd.Flags().GetString("namespace") ctxOverride, _ := cmd.Flags().GetString("context") kubeConfigOverride, _ := cmd.Flags().GetString("kubeconfig") @@ -291,3 +301,59 @@ func getSecrets(cmd *cobra.Command, args []string, toComplete string) ([]string, return names, cobra.ShellCompDirectiveNoFileComp } + +func getSecretKeys(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + // We are now completing the second argument, the key: + // + // kubectl view-secret example-secret + // <------------> <--------> + // args[0] toComplete + // + + if len(args) != 1 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + secretName := args[0] + + nsOverride, _ := cmd.Flags().GetString("namespace") + ctxOverride, _ := cmd.Flags().GetString("context") + kubeConfigOverride, _ := cmd.Flags().GetString("kubeconfig") + + var res, cmdErr bytes.Buffer + commandArgs := []string{"get", "secrets", secretName, "-o", "json"} + if nsOverride != "" { + commandArgs = append(commandArgs, "-n", nsOverride) + } + + if ctxOverride != "" { + commandArgs = append(commandArgs, "--context", ctxOverride) + } + + if kubeConfigOverride != "" { + commandArgs = append(commandArgs, "--kubeconfig", kubeConfigOverride) + } + + out := exec.Command("kubectl", commandArgs...) + out.Stdout = &res + out.Stderr = &cmdErr + err := out.Run() + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + + var parsed struct { + Data map[string]string `json:"data,omitempty"` + } + + if err := json.Unmarshal(res.Bytes(), &parsed); err != nil { + return nil, cobra.ShellCompDirectiveError + } + + var keys []string + for k := range parsed.Data { + keys = append(keys, k) + } + sort.Strings(keys) + + return keys, cobra.ShellCompDirectiveNoFileComp +}