Implement completion for "kops promote keypair"

This commit is contained in:
John Gardiner Myers 2021-06-29 19:47:05 -07:00
parent db4c5dc178
commit d8e592c421
6 changed files with 124 additions and 70 deletions

View File

@ -28,6 +28,8 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2" "k8s.io/klog/v2"
kopsapi "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/client/simple"
"k8s.io/kops/pkg/commands/commandutils" "k8s.io/kops/pkg/commands/commandutils"
"k8s.io/kops/cmd/kops/util" "k8s.io/kops/cmd/kops/util"
@ -82,6 +84,10 @@ var rotatableKeysets = sets.NewString(
"service-account", "service-account",
) )
func rotatableKeysetFilter(name string, _ *fi.Keyset) bool {
return rotatableKeysets.Has(name)
}
// NewCmdCreateKeypair returns a create keypair command. // NewCmdCreateKeypair returns a create keypair command.
func NewCmdCreateKeypair(f *util.Factory, out io.Writer) *cobra.Command { func NewCmdCreateKeypair(f *util.Factory, out io.Writer) *cobra.Command {
options := &CreateKeypairOptions{} options := &CreateKeypairOptions{}
@ -226,45 +232,51 @@ func RunCreateKeypair(ctx context.Context, f *util.Factory, out io.Writer, optio
return nil return nil
} }
func completeCreateKeyset(options *CreateKeypairOptions, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { func completeKeyset(cluster *kopsapi.Cluster, clientSet simple.Clientset, args []string, filter func(name string, keyset *fi.Keyset) bool) (keyset *fi.Keyset, keyStore fi.CAStore, completions []string, directive cobra.ShellCompDirective) {
commandutils.ConfigureKlogForCompletion()
options.ClusterName = rootCommand.ClusterName(false)
if options.ClusterName == "" {
return []string{"--name"}, cobra.ShellCompDirectiveNoFileComp
}
ctx := context.TODO()
cluster, err := GetCluster(ctx, &rootCommand, options.ClusterName)
if err != nil {
return commandutils.CompletionError("getting cluster", err)
}
clientSet, err := rootCommand.Clientset()
if err != nil {
return commandutils.CompletionError("getting clientset", err)
}
keyStore, err := clientSet.KeyStore(cluster) keyStore, err := clientSet.KeyStore(cluster)
if err != nil { if err != nil {
return commandutils.CompletionError("getting keystore", err) completions, directive := commandutils.CompletionError("getting keystore", err)
return nil, nil, completions, directive
} }
if len(args) == 0 { if len(args) == 0 {
list, err := keyStore.ListKeysets() list, err := keyStore.ListKeysets()
if err != nil { if err != nil {
return commandutils.CompletionError("listing keysets", err) completions, directive := commandutils.CompletionError("listing keystore", err)
return nil, nil, completions, directive
} }
var keysets []string var keysets []string
for name := range list { for name, keyset := range list {
if rotatableKeysets.Has(name) { if filter(name, keyset) {
keysets = append(keysets, name) keysets = append(keysets, name)
} }
} }
return keysets, cobra.ShellCompDirectiveNoFileComp return nil, nil, keysets, cobra.ShellCompDirectiveNoFileComp
}
keyset, err = keyStore.FindKeyset(args[0])
if err != nil {
completions, directive := commandutils.CompletionError("finding keyset", err)
return nil, nil, completions, directive
}
return keyset, keyStore, nil, 0
}
func completeCreateKeyset(options *CreateKeypairOptions, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
commandutils.ConfigureKlogForCompletion()
ctx := context.TODO()
cluster, clientSet, completions, directive := GetClusterForCompletion(ctx, &rootCommand)
if cluster == nil {
return completions, directive
}
keyset, _, completions, directive := completeKeyset(cluster, clientSet, args, rotatableKeysetFilter)
if keyset == nil {
return completions, directive
} }
if len(args) > 1 { if len(args) > 1 {

View File

@ -22,27 +22,16 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/kops/cmd/kops/util" "k8s.io/kops/cmd/kops/util"
"k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
) )
var ( var (
promoteLong = templates.LongDesc(i18n.T(`
Promote a resource.`))
promoteExample = templates.Examples(i18n.T(`
# Promote the newest ca keypair to be the primary.
kops promote keypair ca
`))
promoteShort = i18n.T(`Promote a resource.`) promoteShort = i18n.T(`Promote a resource.`)
) )
func NewCmdPromote(f *util.Factory, out io.Writer) *cobra.Command { func NewCmdPromote(f *util.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "promote", Use: "promote",
Short: promoteShort, Short: promoteShort,
Long: promoteLong,
Example: promoteExample,
} }
// create subcommands // create subcommands

View File

@ -23,8 +23,9 @@ import (
"math/big" "math/big"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/klog/v2"
"k8s.io/kops/cmd/kops/util" "k8s.io/kops/cmd/kops/util"
"k8s.io/kops/pkg/commands/commandutils"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates" "k8s.io/kubectl/pkg/util/templates"
) )
@ -58,35 +59,39 @@ func NewCmdPromoteKeypair(f *util.Factory, out io.Writer) *cobra.Command {
options := &PromoteKeypairOptions{} options := &PromoteKeypairOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "keypair KEYSET [ID]", Use: "keypair keyset [id]",
Short: promoteKeypairShort, Short: promoteKeypairShort,
Long: promoteKeypairLong, Long: promoteKeypairLong,
Example: promoteKeypairExample, Example: promoteKeypairExample,
Run: func(cmd *cobra.Command, args []string) { Args: func(cmd *cobra.Command, args []string) error {
ctx := context.TODO()
options.ClusterName = rootCommand.ClusterName(true) options.ClusterName = rootCommand.ClusterName(true)
if options.ClusterName == "" { if options.ClusterName == "" {
exitWithError(fmt.Errorf("--name is required")) return fmt.Errorf("--name is required")
return
} }
if len(args) == 0 { if len(args) == 0 {
exitWithError(fmt.Errorf("must specify name of keyset promote keypair in")) return fmt.Errorf("must specify name of keyset promote keypair in")
}
if len(args) > 2 {
exitWithError(fmt.Errorf("can only promote to one keyset at a time"))
} }
options.Keyset = args[0] options.Keyset = args[0]
if len(args) > 2 {
return fmt.Errorf("can only promote to one keyset at a time")
}
if len(args) > 1 { if len(args) > 1 {
options.KeypairID = args[1] options.KeypairID = args[1]
} }
err := RunPromoteKeypair(ctx, f, out, options) return nil
if err != nil { },
exitWithError(err) ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
} return completePromoteKeyset(options, args, toComplete)
},
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.TODO()
return RunPromoteKeypair(ctx, f, out, options)
}, },
} }
@ -101,17 +106,17 @@ func RunPromoteKeypair(ctx context.Context, f *util.Factory, out io.Writer, opti
cluster, err := GetCluster(ctx, f, options.ClusterName) cluster, err := GetCluster(ctx, f, options.ClusterName)
if err != nil { if err != nil {
return fmt.Errorf("error getting cluster: %q: %v", options.ClusterName, err) return fmt.Errorf("getting cluster: %q: %v", options.ClusterName, err)
} }
clientSet, err := f.Clientset() clientSet, err := f.Clientset()
if err != nil { if err != nil {
return fmt.Errorf("error getting clientset: %v", err) return fmt.Errorf("getting clientset: %v", err)
} }
keyStore, err := clientSet.KeyStore(cluster) keyStore, err := clientSet.KeyStore(cluster)
if err != nil { if err != nil {
return fmt.Errorf("error getting keystore: %v", err) return fmt.Errorf("getting keystore: %v", err)
} }
keyset, err := keyStore.FindKeyset(options.Keyset) keyset, err := keyStore.FindKeyset(options.Keyset)
@ -151,9 +156,46 @@ func RunPromoteKeypair(ctx context.Context, f *util.Factory, out io.Writer, opti
keyset.Primary = keyset.Items[keypairID] keyset.Primary = keyset.Items[keypairID]
err = keyStore.StoreKeyset(options.Keyset, keyset) err = keyStore.StoreKeyset(options.Keyset, keyset)
if err != nil { if err != nil {
return fmt.Errorf("error writing keyset: %v", err) return fmt.Errorf("writing keyset: %v", err)
} }
klog.Infof("promoted keypair %s", keypairID) fmt.Fprintf(out, "promoted keypair %s", keypairID)
return nil return nil
} }
func completePromoteKeyset(options *PromoteKeypairOptions, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
commandutils.ConfigureKlogForCompletion()
ctx := context.TODO()
cluster, clientSet, completions, directive := GetClusterForCompletion(ctx, &rootCommand)
if cluster == nil {
return completions, directive
}
keyset, _, completions, directive := completeKeyset(cluster, clientSet, args, rotatableKeysetFilter)
if keyset == nil {
return completions, directive
}
if len(args) == 1 {
return completeKeypairID(keyset, func(keyset *fi.Keyset, item *fi.KeysetItem) bool {
return item.DistrustTimestamp == nil && item.Id != keyset.Primary.Id
})
}
if len(args) > 2 {
return commandutils.CompletionError("too many arguments", nil)
}
return nil, cobra.ShellCompDirectiveNoFileComp
}
func completeKeypairID(keyset *fi.Keyset, filter func(keyset *fi.Keyset, item *fi.KeysetItem) bool) (completions []string, directive cobra.ShellCompDirective) {
for _, item := range keyset.Items {
if filter(keyset, item) {
completions = append(completions, item.Id)
}
}
return completions, cobra.ShellCompDirectiveNoFileComp
}

View File

@ -302,6 +302,28 @@ func GetCluster(ctx context.Context, factory commandutils.Factory, clusterName s
return cluster, nil return cluster, nil
} }
func GetClusterForCompletion(ctx context.Context, factory commandutils.Factory) (cluster *kopsapi.Cluster, clientSet simple.Clientset, completions []string, directive cobra.ShellCompDirective) {
clusterName := rootCommand.ClusterName(false)
if clusterName == "" {
return nil, nil, []string{"--name"}, cobra.ShellCompDirectiveNoFileComp
}
cluster, err := GetCluster(ctx, &rootCommand, clusterName)
if err != nil {
completions, directive := commandutils.CompletionError("getting cluster", err)
return nil, nil, completions, directive
}
clientSet, err = rootCommand.Clientset()
if err != nil {
completions, directive := commandutils.CompletionError("getting clientset", err)
return nil, nil, completions, directive
}
return cluster, clientSet, nil, 0
}
// ConsumeStdin reads all the bytes available from stdin // ConsumeStdin reads all the bytes available from stdin
func ConsumeStdin() ([]byte, error) { func ConsumeStdin() ([]byte, error) {
file := os.Stdin file := os.Stdin

View File

@ -5,17 +5,6 @@
Promote a resource. Promote a resource.
### Synopsis
Promote a resource.
### Examples
```
# Promote the newest ca keypair to be the primary.
kops promote keypair ca
```
### Options ### Options
``` ```

View File

@ -10,7 +10,7 @@ Promote a keypair to be the primary, used for signing.
Promote a keypair to be the primary, used for signing. Promote a keypair to be the primary, used for signing.
``` ```
kops promote keypair KEYSET [ID] [flags] kops promote keypair keyset [id] [flags]
``` ```
### Examples ### Examples