Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add secret and namespace completion (requires kubectl 1.26) #35

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ Instead you can now do:

# print secret keys
kubectl view-secret <secret>

# decode specific entry
kubectl view-secret <secret> <key>

# decode all contents
kubectl view-secret <secret> -a/--all

# print keys for secret in different namespace
kubectl view-secret <secret> -n/--namespace <ns>

Expand All @@ -36,20 +36,35 @@ 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`.

### 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.

**Note**: If you build from source or download the binary, you'll have to change the name of the binary to `kubectl-view_secret` (`-` to `_` in `view-secret`)
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)
Expand Down
166 changes: 166 additions & 0 deletions pkg/cmd/view-secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func NewCmdViewSecret() *cobra.Command {

return nil
},
ValidArgsFunction: getSecretsOrKeys,
}

cmd.Flags().
Expand All @@ -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
}

Expand Down Expand Up @@ -191,3 +193,167 @@ func ProcessSecret(outWriter, errWriter io.Writer, secret map[string]interface{}

return nil
}

func getNamespaces(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like toComplete is unused

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 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) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

args is unsed, can we ignore _ it?

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
}

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 <TAB><TAB>
// <------------> <-------->
// 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
}