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

Implement completion for "kops create keypair" #11888

Merged
merged 1 commit into from
Jun 29, 2021
Merged
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
124 changes: 102 additions & 22 deletions cmd/kops/create_keypair.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import (
"time"

"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2"
"k8s.io/kops/pkg/commands/commandutils"

"k8s.io/kops/cmd/kops/util"
"k8s.io/kops/pkg/pki"
Expand All @@ -39,7 +41,22 @@ import (
var (
createKeypairLong = templates.LongDesc(i18n.T(`
Add a CA certificate and private key to a keyset.
`))

If neither a certificate nor a private key is provided, a new self-signed
certificate and private key will be generated.

If no certificate is provided but a private key is, a self-signed
certificate will be generated from the provided private key.

If a certificate is provided but no private key is, the certificate
will be added to the keyset without a private key. Such a certificate
cannot be made primary.

One of the certificate/private key pairs in each keyset must be primary.
The primary keypair is the one used to issue certificates (or, for the
"service-account" keyset, service-account tokens). As a consequence, the
first entry in a keyset must be made primary.
`))

createKeypairExample = templates.Examples(i18n.T(`
Add a CA certificate and private key to a keyset.
Expand All @@ -59,43 +76,45 @@ type CreateKeypairOptions struct {
Primary bool
}

var keysetCommonNames = map[string]string{
"ca": "kubernetes",
"etcd-clients-ca-cilium": "etcd-clients-ca-cilium",
"service-account": "service-account",
}
var rotatableKeysets = sets.NewString(
"ca",
"etcd-clients-ca-cilium",
"service-account",
)

// NewCmdCreateKeypair returns a create keypair command.
func NewCmdCreateKeypair(f *util.Factory, out io.Writer) *cobra.Command {
options := &CreateKeypairOptions{}

cmd := &cobra.Command{
Use: "keypair KEYSET",
Use: "keypair keyset",
Short: createKeypairShort,
Long: createKeypairLong,
Example: createKeypairExample,
Run: func(cmd *cobra.Command, args []string) {
ctx := context.TODO()

options.ClusterName = rootCommand.ClusterName()
Args: func(cmd *cobra.Command, args []string) error {
options.ClusterName = rootCommand.ClusterName(true)

if options.ClusterName == "" {
exitWithError(fmt.Errorf("--name is required"))
return
return fmt.Errorf("--name is required")
}

if len(args) == 0 {
exitWithError(fmt.Errorf("must specify name of keyset to add keypair to"))
}
if len(args) != 1 {
exitWithError(fmt.Errorf("can only add to one keyset at a time"))
return fmt.Errorf("must specify name of keyset to add keypair to")
}

options.Keyset = args[0]

err := RunCreateKeypair(ctx, f, out, options)
if err != nil {
exitWithError(err)
if len(args) != 1 {
return fmt.Errorf("can only add to one keyset at a time")
}

return nil
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return completeCreateKeyset(options, args, toComplete)
},
RunE: func(cmd *cobra.Command, args []string) error {
return RunCreateKeypair(context.TODO(), f, out, options)
},
}

Expand All @@ -108,8 +127,7 @@ func NewCmdCreateKeypair(f *util.Factory, out io.Writer) *cobra.Command {

// RunCreateKeypair adds a custom CA certificate and private key.
func RunCreateKeypair(ctx context.Context, f *util.Factory, out io.Writer, options *CreateKeypairOptions) error {
commonName := keysetCommonNames[options.Keyset]
if commonName == "" {
if !rotatableKeysets.Has(options.Keyset) {
return fmt.Errorf("adding keypair to %q is not supported", options.Keyset)
}

Expand Down Expand Up @@ -151,6 +169,10 @@ func RunCreateKeypair(ctx context.Context, f *util.Factory, out io.Writer, optio
}
}

commonName := options.Keyset
if commonName == "ca" {
commonName = "kubernetes"
}
req := pki.IssueCertRequest{
Type: "ca",
Subject: pkix.Name{CommonName: "cn=" + commonName},
Expand Down Expand Up @@ -203,3 +225,61 @@ func RunCreateKeypair(ctx context.Context, f *util.Factory, out io.Writer, optio
}
return nil
}

func completeCreateKeyset(options *CreateKeypairOptions, args []string, toComplete string) ([]string, 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)
if err != nil {
return commandutils.CompletionError("getting keystore", err)
}

if len(args) == 0 {
list, err := keyStore.ListKeysets()
if err != nil {
return commandutils.CompletionError("listing keysets", err)
}

var keysets []string
for name := range list {
if rotatableKeysets.Has(name) {
keysets = append(keysets, name)
}
}

return keysets, cobra.ShellCompDirectiveNoFileComp
}

if len(args) > 1 {
return commandutils.CompletionError("too many arguments", nil)
}

var flags []string
if options.CertPath == "" {
flags = append(flags, "--cert")
}
if options.PrivateKeyPath == "" {
flags = append(flags, "--key")
}
if !options.Primary && (options.CertPath == "" || options.PrivateKeyPath != "") {
flags = append(flags, "--primary")
}
return flags, cobra.ShellCompDirectiveNoFileComp
}
2 changes: 1 addition & 1 deletion cmd/kops/create_secret_cilium_encryptionconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func NewCmdCreateSecretCiliumEncryptionConfig(f *util.Factory, out io.Writer) *c
exitWithError(err)
}

options.ClusterName = rootCommand.ClusterName()
options.ClusterName = rootCommand.ClusterName(true)

err = RunCreateSecretCiliumEncryptionConfig(ctx, f, os.Stdout, options)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/create_secret_dockerconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func NewCmdCreateSecretDockerConfig(f *util.Factory, out io.Writer) *cobra.Comma
exitWithError(err)
}

options.ClusterName = rootCommand.ClusterName()
options.ClusterName = rootCommand.ClusterName(true)

err = RunCreateSecretDockerConfig(ctx, f, os.Stdout, options)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/create_secret_encryptionconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func NewCmdCreateSecretEncryptionConfig(f *util.Factory, out io.Writer) *cobra.C
exitWithError(err)
}

options.ClusterName = rootCommand.ClusterName()
options.ClusterName = rootCommand.ClusterName(true)

err = RunCreateSecretEncryptionConfig(ctx, f, os.Stdout, options)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/create_secret_sshpublickey.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func NewCmdCreateSecretPublicKey(f *util.Factory, out io.Writer) *cobra.Command
exitWithError(err)
}

options.ClusterName = rootCommand.ClusterName()
options.ClusterName = rootCommand.ClusterName(true)

err = RunCreateSecretPublicKey(ctx, f, os.Stdout, options)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/create_secret_weave_encryptionconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func NewCmdCreateSecretWeaveEncryptionConfig(f *util.Factory, out io.Writer) *co
exitWithError(err)
}

options.ClusterName = rootCommand.ClusterName()
options.ClusterName = rootCommand.ClusterName(true)

err = RunCreateSecretWeaveEncryptionConfig(ctx, f, options)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/delete_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func NewCmdDeleteInstance(f *util.Factory, out io.Writer) *cobra.Command {
cmd.Run = func(cmd *cobra.Command, args []string) {
ctx := context.TODO()

clusterName := rootCommand.ClusterName()
clusterName := rootCommand.ClusterName(true)

if clusterName == "" {
exitWithError(fmt.Errorf("--name is required"))
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/delete_instancegroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func NewCmdDeleteInstanceGroup(f *util.Factory, out io.Writer) *cobra.Command {
groupName := args[0]
options.GroupName = groupName

options.ClusterName = rootCommand.ClusterName()
options.ClusterName = rootCommand.ClusterName(true)

if !options.Yes {
message := fmt.Sprintf("Do you really want to delete instance group %q? This action cannot be undone.", groupName)
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/delete_secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func NewCmdDeleteSecret(f *util.Factory, out io.Writer) *cobra.Command {
options.SecretID = args[2]
}

options.ClusterName = rootCommand.ClusterName()
options.ClusterName = rootCommand.ClusterName(true)

err := RunDeleteSecret(ctx, f, out, options)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/distrust_keypair.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func NewCmdDistrustKeypair(f *util.Factory, out io.Writer) *cobra.Command {
Run: func(cmd *cobra.Command, args []string) {
ctx := context.TODO()

options.ClusterName = rootCommand.ClusterName()
options.ClusterName = rootCommand.ClusterName(true)
if options.ClusterName == "" {
exitWithError(fmt.Errorf("--name is required"))
return
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/get_assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func NewCmdGetAssets(f *util.Factory, out io.Writer, getOptions *GetOptions) *co

func RunGetAssets(ctx context.Context, f *util.Factory, out io.Writer, options *GetAssetsOptions) error {

clusterName := rootCommand.ClusterName()
clusterName := rootCommand.ClusterName(true)
options.clusterName = clusterName
if clusterName == "" {
return fmt.Errorf("--name is required")
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/get_instancegroups.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func NewCmdGetInstanceGroups(f *util.Factory, out io.Writer, getOptions *GetOpti
func RunGetInstanceGroups(ctx context.Context, options *GetInstanceGroupsOptions, args []string) error {
out := os.Stdout

clusterName := rootCommand.ClusterName()
clusterName := rootCommand.ClusterName(true)
if clusterName == "" {
return fmt.Errorf("--name is required")
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/get_instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func RunGetInstances(ctx context.Context, f *util.Factory, out io.Writer, option
return err
}

clusterName := rootCommand.ClusterName()
clusterName := rootCommand.ClusterName(true)
options.clusterName = clusterName
if clusterName == "" {
return fmt.Errorf("--name is required")
Expand Down
4 changes: 2 additions & 2 deletions cmd/kops/promote_keypair.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func NewCmdPromoteKeypair(f *util.Factory, out io.Writer) *cobra.Command {
Run: func(cmd *cobra.Command, args []string) {
ctx := context.TODO()

options.ClusterName = rootCommand.ClusterName()
options.ClusterName = rootCommand.ClusterName(true)

if options.ClusterName == "" {
exitWithError(fmt.Errorf("--name is required"))
Expand Down Expand Up @@ -95,7 +95,7 @@ func NewCmdPromoteKeypair(f *util.Factory, out io.Writer) *cobra.Command {

// RunPromoteKeypair promotes a keypair.
func RunPromoteKeypair(ctx context.Context, f *util.Factory, out io.Writer, options *PromoteKeypairOptions) error {
if keysetCommonNames[options.Keyset] == "" {
if !rotatableKeysets.Has(options.Keyset) {
return fmt.Errorf("promoting keypairs for %q is not supported", options.Keyset)
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/rollingupdatecluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func NewCmdRollingUpdateCluster(f *util.Factory, out io.Writer) *cobra.Command {
return
}

clusterName := rootCommand.ClusterName()
clusterName := rootCommand.ClusterName(true)
if clusterName == "" {
exitWithError(fmt.Errorf("--name is required"))
return
Expand Down
28 changes: 7 additions & 21 deletions cmd/kops/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,22 +235,14 @@ func (c *RootCmd) ProcessArgs(args []string) error {
return fmt.Errorf("expected a single <clustername> to be passed as an argument")
}

func (c *RootCmd) ClusterName() string {
func (c *RootCmd) ClusterName(verbose bool) string {
if c.clusterName != "" {
return c.clusterName
}

c.clusterName = ClusterNameFromKubecfg()

return c.clusterName
}

func ClusterNameFromKubecfg() string {
// Read from kubeconfig
pathOptions := clientcmd.NewDefaultPathOptions()

clusterName := ""

config, err := pathOptions.GetStartingConfig()
if err != nil {
klog.Warningf("error reading kubecfg: %v", err)
Expand All @@ -263,28 +255,22 @@ func ClusterNameFromKubecfg() string {
} else if context.Cluster == "" {
klog.Warningf("context %q in kubecfg did not have a cluster", config.CurrentContext)
} else {
fmt.Fprintf(os.Stderr, "Using cluster from kubectl context: %s\n\n", context.Cluster)
clusterName = context.Cluster
if verbose {
fmt.Fprintf(os.Stderr, "Using cluster from kubectl context: %s\n\n", context.Cluster)
}
c.clusterName = context.Cluster
}
}

//config, err := readKubectlClusterConfig()
//if err != nil {
// klog.Warningf("error reading kubecfg: %v", err)
//} else if config != nil && config.Name != "" {
// fmt.Fprintf(os.Stderr, "Using cluster from kubectl context: %s\n\n", config.Name)
// c.clusterName = config.Name
//}

return clusterName
return c.clusterName
}

func (c *RootCmd) Clientset() (simple.Clientset, error) {
return c.factory.Clientset()
}

func (c *RootCmd) Cluster(ctx context.Context) (*kopsapi.Cluster, error) {
clusterName := c.ClusterName()
clusterName := c.ClusterName(true)
if clusterName == "" {
return nil, fmt.Errorf("--name is required")
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/set_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func NewCmdSetCluster(f *util.Factory, out io.Writer) *cobra.Command {
}

if options.ClusterName == "" {
options.ClusterName = rootCommand.ClusterName()
options.ClusterName = rootCommand.ClusterName(true)
}

if err := commands.RunSetCluster(ctx, f, cmd, out, options); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/set_instancegroups.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func NewCmdSetInstancegroup(f *util.Factory, out io.Writer) *cobra.Command {
}
}

options.ClusterName = rootCommand.ClusterName()
options.ClusterName = rootCommand.ClusterName(true)

if err := commands.RunSetInstancegroup(ctx, f, cmd, out, options); err != nil {
exitWithError(err)
Expand Down
Loading