From 72ba6877441348fb56ec30096ed168a66a9e23c5 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sat, 19 Jun 2021 13:36:11 -0700 Subject: [PATCH 1/2] Split out get, describe, and delete keypairs commands --- cmd/kops/BUILD.bazel | 4 +- cmd/kops/delete.go | 5 +- cmd/kops/delete_keypair.go | 140 +++++++++++++++ cmd/kops/delete_secret.go | 12 +- ...scribe_secrets.go => describe_keypairs.go} | 77 +++------ cmd/kops/get.go | 1 + cmd/kops/get_keypairs.go | 160 ++++++++++++++++++ cmd/kops/get_secrets.go | 34 +--- docs/cli/kops.md | 2 +- docs/cli/kops_delete.md | 5 +- docs/cli/kops_delete_cluster.md | 2 +- docs/cli/kops_delete_instance.md | 2 +- docs/cli/kops_delete_instancegroup.md | 2 +- docs/cli/kops_delete_keypair.md | 53 ++++++ docs/cli/kops_delete_secret.md | 2 +- docs/cli/kops_describe.md | 2 +- ...e_secrets.md => kops_describe_keypairs.md} | 15 +- docs/cli/kops_get.md | 1 + docs/cli/kops_get_keypairs.md | 57 +++++++ 19 files changed, 460 insertions(+), 116 deletions(-) create mode 100644 cmd/kops/delete_keypair.go rename cmd/kops/{describe_secrets.go => describe_keypairs.go} (68%) create mode 100644 cmd/kops/get_keypairs.go create mode 100644 docs/cli/kops_delete_keypair.md rename docs/cli/{kops_describe_secrets.md => kops_describe_keypairs.md} (88%) create mode 100644 docs/cli/kops_get_keypairs.md diff --git a/cmd/kops/BUILD.bazel b/cmd/kops/BUILD.bazel index f1f050ca6ea73..38cde28ab40a7 100644 --- a/cmd/kops/BUILD.bazel +++ b/cmd/kops/BUILD.bazel @@ -19,9 +19,10 @@ go_library( "delete_cluster.go", "delete_instance.go", "delete_instancegroup.go", + "delete_keypair.go", "delete_secret.go", "describe.go", - "describe_secrets.go", + "describe_keypairs.go", "edit.go", "edit_cluster.go", "edit_instancegroup.go", @@ -34,6 +35,7 @@ go_library( "get_cluster.go", "get_instancegroups.go", "get_instances.go", + "get_keypairs.go", "get_secrets.go", "import.go", "import_cluster.go", diff --git a/cmd/kops/delete.go b/cmd/kops/delete.go index 44cd09d401b0c..ab55a3c5b682c 100644 --- a/cmd/kops/delete.go +++ b/cmd/kops/delete.go @@ -42,7 +42,7 @@ type DeleteOptions struct { var ( deleteLong = templates.LongDesc(i18n.T(` - Delete Kubernetes clusters, instancegroups, instances, and secrets, or a combination of the before mentioned. + Delete Kubernetes clusters, instancegroups, instances, keypairs, and secrets, or a combination of the before mentioned. `)) deleteExample = templates.Examples(i18n.T(` @@ -63,7 +63,7 @@ var ( kops delete ig --name=k8s-cluster.example.com node-example --yes `)) - deleteShort = i18n.T("Delete clusters,instancegroups, instances, or secrets.") + deleteShort = i18n.T("Delete clusters, instancegroups, instances, keypairs, or secrets.") ) func NewCmdDelete(f *util.Factory, out io.Writer) *cobra.Command { @@ -92,6 +92,7 @@ func NewCmdDelete(f *util.Factory, out io.Writer) *cobra.Command { // create subcommands cmd.AddCommand(NewCmdDeleteCluster(f, out)) cmd.AddCommand(NewCmdDeleteInstanceGroup(f, out)) + cmd.AddCommand(NewCmdDeleteKeypair(f, out)) cmd.AddCommand(NewCmdDeleteSecret(f, out)) cmd.AddCommand(NewCmdDeleteInstance(f, out)) diff --git a/cmd/kops/delete_keypair.go b/cmd/kops/delete_keypair.go new file mode 100644 index 0000000000000..141df61fa0731 --- /dev/null +++ b/cmd/kops/delete_keypair.go @@ -0,0 +1,140 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "fmt" + "io" + + "github.com/spf13/cobra" + "k8s.io/kops/cmd/kops/util" + "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kubectl/pkg/util/i18n" + "k8s.io/kubectl/pkg/util/templates" +) + +var ( + deleteKeypairLong = templates.LongDesc(i18n.T(` + Delete a keypair.`)) + + deleteKeypairExample = templates.Examples(i18n.T(` + # Syntax: kops delete keypair KEYSET ID + kops delete keypair ca 5938372002934847 + + `)) + + deleteKeypairShort = i18n.T(`Delete a keypair.`) +) + +type DeleteKeypairOptions struct { + ClusterName string + Keyset string + KeypairID string +} + +func NewCmdDeleteKeypair(f *util.Factory, out io.Writer) *cobra.Command { + options := &DeleteKeypairOptions{} + + cmd := &cobra.Command{ + Use: "keypair", + Short: deleteKeypairShort, + Long: deleteKeypairLong, + Example: deleteKeypairExample, + Run: func(cmd *cobra.Command, args []string) { + ctx := context.TODO() + + if len(args) != 2 && len(args) != 3 { + exitWithError(fmt.Errorf("Syntax: ")) + } + + options.Keyset = args[0] + options.KeypairID = args[1] + + options.ClusterName = rootCommand.ClusterName() + + err := RunDeleteKeypair(ctx, f, out, options) + if err != nil { + exitWithError(err) + } + }, + } + + return cmd +} + +func RunDeleteKeypair(ctx context.Context, f *util.Factory, out io.Writer, options *DeleteKeypairOptions) error { + if options.ClusterName == "" { + return fmt.Errorf("ClusterName is required") + } + if options.Keyset == "" { + return fmt.Errorf("Keyset is required") + } + if options.KeypairID == "" { + return fmt.Errorf("KeypairID is required") + } + + clientset, err := f.Clientset() + if err != nil { + return err + } + + cluster, err := GetCluster(ctx, f, options.ClusterName) + if err != nil { + return err + } + + keyStore, err := clientset.KeyStore(cluster) + if err != nil { + return err + } + + keypairs, err := listKeypairs(keyStore, []string{options.Keyset}) + if err != nil { + return err + } + + { + var matches []*fi.KeystoreItem + for _, s := range keypairs { + if s.ID == options.KeypairID { + matches = append(matches, s) + } + } + keypairs = matches + } + + if len(keypairs) == 0 { + return fmt.Errorf("keypair not found") + } + + if len(keypairs) != 1 { + // TODO: it would be friendly to print the matching keys + return fmt.Errorf("found multiple matching keypairs; specify the id of the key") + } + + keyset := &kops.Keyset{} + keyset.Name = keypairs[0].Name + keyset.Spec.Type = keypairs[0].Type + err = keyStore.DeleteKeysetItem(keyset, keypairs[0].ID) + if err != nil { + return fmt.Errorf("error deleting keypair: %v", err) + } + + return nil +} diff --git a/cmd/kops/delete_secret.go b/cmd/kops/delete_secret.go index 59acca3a95503..e521d19ae3541 100644 --- a/cmd/kops/delete_secret.go +++ b/cmd/kops/delete_secret.go @@ -104,11 +104,6 @@ func RunDeleteSecret(ctx context.Context, f *util.Factory, out io.Writer, option return err } - keyStore, err := clientset.KeyStore(cluster) - if err != nil { - return err - } - secretStore, err := clientset.SecretStore(cluster) if err != nil { return err @@ -119,7 +114,7 @@ func RunDeleteSecret(ctx context.Context, f *util.Factory, out io.Writer, option return err } - secrets, err := listSecrets(keyStore, secretStore, sshCredentialStore, options.SecretType, []string{options.SecretName}) + secrets, err := listSecrets(secretStore, sshCredentialStore, options.SecretType, []string{options.SecretName}) if err != nil { return err } @@ -153,11 +148,6 @@ func RunDeleteSecret(ctx context.Context, f *util.Factory, out io.Writer, option sshCredential.Spec.PublicKey = string(secrets[0].Data) } err = sshCredentialStore.DeleteSSHCredential(sshCredential) - default: - keyset := &kops.Keyset{} - keyset.Name = secrets[0].Name - keyset.Spec.Type = secrets[0].Type - err = keyStore.DeleteKeysetItem(keyset, secrets[0].ID) } if err != nil { return fmt.Errorf("error deleting secret: %v", err) diff --git a/cmd/kops/describe_secrets.go b/cmd/kops/describe_keypairs.go similarity index 68% rename from cmd/kops/describe_secrets.go rename to cmd/kops/describe_keypairs.go index aa7fd96622094..78e80051dc3df 100644 --- a/cmd/kops/describe_secrets.go +++ b/cmd/kops/describe_keypairs.go @@ -27,7 +27,6 @@ import ( "text/tabwriter" "github.com/spf13/cobra" - "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/pki" "k8s.io/kops/upup/pkg/fi" "k8s.io/kubectl/pkg/util/i18n" @@ -35,33 +34,32 @@ import ( ) var ( - describeSecretLong = templates.LongDesc(i18n.T(` - Get additional information about cluster secrets. + describeKeypairLong = templates.LongDesc(i18n.T(` + Get additional information about keypairs. `)) - describeSecretExample = templates.Examples(i18n.T(` - # Describe a secret - kops describe secrets admin + describeKeypairExample = templates.Examples(i18n.T(` + # Describe a keypair + kops describe keypairs ca `)) - describeSecretShort = i18n.T(`Describe a cluster secret`) + describeKeypairShort = i18n.T(`Describe a cluster keypair`) ) -type DescribeSecretsCommand struct { - Type string +type DescribeKeypairsCommand struct { } -var describeSecretsCommand DescribeSecretsCommand +var describeKeypairsCommand DescribeKeypairsCommand func init() { cmd := &cobra.Command{ - Use: "secrets", - Aliases: []string{"secret"}, - Short: describeSecretShort, - Long: describeSecretLong, - Example: describeSecretExample, + Use: "keypairs", + Aliases: []string{"keypair"}, + Short: describeKeypairShort, + Long: describeKeypairLong, + Example: describeKeypairExample, Run: func(cmd *cobra.Command, args []string) { ctx := context.TODO() - err := describeSecretsCommand.Run(ctx, args) + err := describeKeypairsCommand.Run(ctx, args) if err != nil { exitWithError(err) } @@ -69,11 +67,9 @@ func init() { } describeCmd.cobraCommand.AddCommand(cmd) - - cmd.Flags().StringVarP(&describeSecretsCommand.Type, "type", "", "", "Filter by secret type") } -func (c *DescribeSecretsCommand) Run(ctx context.Context, args []string) error { +func (c *DescribeKeypairsCommand) Run(ctx context.Context, args []string) error { cluster, err := rootCommand.Cluster(ctx) if err != nil { return err @@ -89,23 +85,13 @@ func (c *DescribeSecretsCommand) Run(ctx context.Context, args []string) error { return err } - secretStore, err := clientset.SecretStore(cluster) - if err != nil { - return err - } - - sshCredentialStore, err := clientset.SSHCredentialStore(cluster) - if err != nil { - return err - } - - items, err := listSecrets(keyStore, secretStore, sshCredentialStore, c.Type, args) + items, err := listKeypairs(keyStore, args) if err != nil { return err } if len(items) == 0 { - fmt.Fprintf(os.Stderr, "No secrets found\n") + fmt.Fprintf(os.Stderr, "No keypairs found\n") return nil } @@ -121,24 +107,9 @@ func (c *DescribeSecretsCommand) Run(ctx context.Context, args []string) error { fmt.Fprintf(w, "Type:\t%s\n", i.Type) fmt.Fprintf(w, "Id:\t%s\n", i.ID) - switch i.Type { - case kops.SecretTypeKeypair: - err = describeKeypair(keyStore, i, &b) - if err != nil { - return err - } - - case SecretTypeSSHPublicKey: - err = describeSSHPublicKey(i, &b) - if err != nil { - return err - } - - case kops.SecretTypeSecret: - err = describeSecret(i, &b) - if err != nil { - return err - } + err = describeKeypair(keyStore, i, &b) + if err != nil { + return err } b.WriteString("\n") @@ -200,11 +171,3 @@ func describeKeypair(keyStore fi.CAStore, item *fi.KeystoreItem, w *bytes.Buffer return nil } - -func describeSecret(item *fi.KeystoreItem, w *bytes.Buffer) error { - return nil -} - -func describeSSHPublicKey(item *fi.KeystoreItem, w *bytes.Buffer) error { - return nil -} diff --git a/cmd/kops/get.go b/cmd/kops/get.go index 30bd98542996c..00d7bd6a531b1 100644 --- a/cmd/kops/get.go +++ b/cmd/kops/get.go @@ -109,6 +109,7 @@ func NewCmdGet(f *util.Factory, out io.Writer) *cobra.Command { cmd.AddCommand(NewCmdGetAssets(f, out, options)) cmd.AddCommand(NewCmdGetCluster(f, out, options)) cmd.AddCommand(NewCmdGetInstanceGroups(f, out, options)) + cmd.AddCommand(NewCmdGetKeypairs(f, out, options)) cmd.AddCommand(NewCmdGetSecrets(f, out, options)) cmd.AddCommand(NewCmdGetInstances(f, out, options)) diff --git a/cmd/kops/get_keypairs.go b/cmd/kops/get_keypairs.go new file mode 100644 index 0000000000000..1b3c72f193f64 --- /dev/null +++ b/cmd/kops/get_keypairs.go @@ -0,0 +1,160 @@ +/* +Copyright 2021 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "fmt" + "io" + "os" + + "github.com/spf13/cobra" + "k8s.io/kops/cmd/kops/util" + "k8s.io/kops/upup/pkg/fi" + "k8s.io/kops/util/pkg/tables" + "k8s.io/kubectl/pkg/util/i18n" + "k8s.io/kubectl/pkg/util/templates" +) + +var ( + getKeypairLong = templates.LongDesc(i18n.T(` + Display one or many keypairs.`)) + + getKeypairExample = templates.Examples(i18n.T(` + # List the cluster CA keypairs. + kops get keypairs ca + + # List the service-account keypairs. + kops get keypairs service-account`)) + + getKeypairShort = i18n.T(`Get one or many keypairs.`) +) + +type GetKeypairsOptions struct { + *GetOptions +} + +func NewCmdGetKeypairs(f *util.Factory, out io.Writer, getOptions *GetOptions) *cobra.Command { + options := GetKeypairsOptions{ + GetOptions: getOptions, + } + cmd := &cobra.Command{ + Use: "keypairs", + Aliases: []string{"keypair"}, + Short: getKeypairShort, + Long: getKeypairLong, + Example: getKeypairExample, + Run: func(cmd *cobra.Command, args []string) { + ctx := context.TODO() + err := RunGetKeypairs(ctx, &options, args) + if err != nil { + exitWithError(err) + } + }, + } + + return cmd +} + +func listKeypairs(keyStore fi.CAStore, names []string) ([]*fi.KeystoreItem, error) { + var items []*fi.KeystoreItem + + { + l, err := keyStore.ListKeysets() + if err != nil { + return nil, fmt.Errorf("error listing Keysets: %v", err) + } + + for _, keyset := range l { + for _, key := range keyset.Spec.Keys { + item := &fi.KeystoreItem{ + Name: keyset.Name, + Type: keyset.Spec.Type, + ID: key.Id, + } + items = append(items, item) + } + } + } + + if len(names) != 0 { + var matches []*fi.KeystoreItem + for _, arg := range names { + var found []*fi.KeystoreItem + for _, i := range items { + if i.Name == arg { + found = append(found, i) + } + } + + matches = append(matches, found...) + } + items = matches + } + + return items, nil +} + +func RunGetKeypairs(ctx context.Context, options *GetKeypairsOptions, args []string) error { + cluster, err := rootCommand.Cluster(ctx) + if err != nil { + return err + } + + clientset, err := rootCommand.Clientset() + if err != nil { + return err + } + + keyStore, err := clientset.KeyStore(cluster) + if err != nil { + return err + } + + items, err := listKeypairs(keyStore, args) + if err != nil { + return err + } + + if len(items) == 0 { + return fmt.Errorf("no keypairs found") + } + switch options.output { + + case OutputTable: + + t := &tables.Table{} + t.AddColumn("NAME", func(i *fi.KeystoreItem) string { + return i.Name + }) + t.AddColumn("ID", func(i *fi.KeystoreItem) string { + return i.ID + }) + t.AddColumn("TYPE", func(i *fi.KeystoreItem) string { + return string(i.Type) + }) + return t.Render(items, os.Stdout, "TYPE", "NAME", "ID") + + case OutputYaml: + return fmt.Errorf("yaml output format is not (currently) supported for keypairs") + case OutputJSON: + return fmt.Errorf("json output format is not (currently) supported for keypairs") + + default: + return fmt.Errorf("Unknown output format: %q", options.output) + } +} diff --git a/cmd/kops/get_secrets.go b/cmd/kops/get_secrets.go index f41893293cfca..f77e0d8d57366 100644 --- a/cmd/kops/get_secrets.go +++ b/cmd/kops/get_secrets.go @@ -80,40 +80,21 @@ func NewCmdGetSecrets(f *util.Factory, out io.Writer, getOptions *GetOptions) *c return cmd } -func listSecrets(keyStore fi.CAStore, secretStore fi.SecretStore, sshCredentialStore fi.SSHCredentialStore, secretType string, names []string) ([]*fi.KeystoreItem, error) { +func listSecrets(secretStore fi.SecretStore, sshCredentialStore fi.SSHCredentialStore, secretType string, names []string) ([]*fi.KeystoreItem, error) { var items []*fi.KeystoreItem findType := strings.ToLower(secretType) switch findType { case "": // OK - case "sshpublickey", "keypair", "secret": + case "sshpublickey", "secret": // OK + case "keypair": + return nil, fmt.Errorf("use 'kops get keypairs %s' instead", secretType) default: return nil, fmt.Errorf("unknown secret type %q", secretType) } - { - l, err := keyStore.ListKeysets() - if err != nil { - return nil, fmt.Errorf("error listing Keysets: %v", err) - } - - for _, keyset := range l { - if findType != "" && findType != strings.ToLower(string(keyset.Spec.Type)) { - continue - } - for _, key := range keyset.Spec.Keys { - item := &fi.KeystoreItem{ - Name: keyset.Name, - Type: keyset.Spec.Type, - ID: key.Id, - } - items = append(items, item) - } - } - } - if findType == "" || findType == strings.ToLower(string(kops.SecretTypeSecret)) { names, err := secretStore.ListSecrets() if err != nil { @@ -194,11 +175,6 @@ func RunGetSecrets(ctx context.Context, options *GetSecretsOptions, args []strin return err } - keyStore, err := clientset.KeyStore(cluster) - if err != nil { - return err - } - secretStore, err := clientset.SecretStore(cluster) if err != nil { return err @@ -209,7 +185,7 @@ func RunGetSecrets(ctx context.Context, options *GetSecretsOptions, args []strin return err } - items, err := listSecrets(keyStore, secretStore, sshCredentialStore, options.Type, args) + items, err := listSecrets(secretStore, sshCredentialStore, options.Type, args) if err != nil { return err } diff --git a/docs/cli/kops.md b/docs/cli/kops.md index 1fadc5807a3c8..72873dc1f5775 100644 --- a/docs/cli/kops.md +++ b/docs/cli/kops.md @@ -39,7 +39,7 @@ kOps is Kubernetes Operations. * [kops completion](kops_completion.md) - Output shell completion code for the given shell (bash or zsh). * [kops create](kops_create.md) - Create a resource by command line, filename or stdin. -* [kops delete](kops_delete.md) - Delete clusters,instancegroups, instances, or secrets. +* [kops delete](kops_delete.md) - Delete clusters, instancegroups, instances, keypairs, or secrets. * [kops describe](kops_describe.md) - Describe a resource. * [kops edit](kops_edit.md) - Edit clusters and other resources. * [kops export](kops_export.md) - Export configuration. diff --git a/docs/cli/kops_delete.md b/docs/cli/kops_delete.md index 379a7bcffddfa..dae4e9ce309ac 100644 --- a/docs/cli/kops_delete.md +++ b/docs/cli/kops_delete.md @@ -3,11 +3,11 @@ ## kops delete -Delete clusters,instancegroups, instances, or secrets. +Delete clusters, instancegroups, instances, keypairs, or secrets. ### Synopsis -Delete Kubernetes clusters, instancegroups, instances, and secrets, or a combination of the before mentioned. +Delete Kubernetes clusters, instancegroups, instances, keypairs, and secrets, or a combination of the before mentioned. ``` kops delete -f FILENAME [--yes] [flags] @@ -68,5 +68,6 @@ kops delete -f FILENAME [--yes] [flags] * [kops delete cluster](kops_delete_cluster.md) - Delete a cluster. * [kops delete instance](kops_delete_instance.md) - Delete an instance * [kops delete instancegroup](kops_delete_instancegroup.md) - Delete instancegroup +* [kops delete keypair](kops_delete_keypair.md) - Delete a keypair. * [kops delete secret](kops_delete_secret.md) - Delete a secret diff --git a/docs/cli/kops_delete_cluster.md b/docs/cli/kops_delete_cluster.md index bd8caa52a6034..7b97e1323f6aa 100644 --- a/docs/cli/kops_delete_cluster.md +++ b/docs/cli/kops_delete_cluster.md @@ -54,5 +54,5 @@ kops delete cluster CLUSTERNAME [--yes] [flags] ### SEE ALSO -* [kops delete](kops_delete.md) - Delete clusters,instancegroups, instances, or secrets. +* [kops delete](kops_delete.md) - Delete clusters, instancegroups, instances, keypairs, or secrets. diff --git a/docs/cli/kops_delete_instance.md b/docs/cli/kops_delete_instance.md index b20b83e362aca..c155bf1720f78 100644 --- a/docs/cli/kops_delete_instance.md +++ b/docs/cli/kops_delete_instance.md @@ -64,5 +64,5 @@ kops delete instance [flags] ### SEE ALSO -* [kops delete](kops_delete.md) - Delete clusters,instancegroups, instances, or secrets. +* [kops delete](kops_delete.md) - Delete clusters, instancegroups, instances, keypairs, or secrets. diff --git a/docs/cli/kops_delete_instancegroup.md b/docs/cli/kops_delete_instancegroup.md index 27c8cdc1fd8cb..d51b77cdede8f 100644 --- a/docs/cli/kops_delete_instancegroup.md +++ b/docs/cli/kops_delete_instancegroup.md @@ -52,5 +52,5 @@ kops delete instancegroup [flags] ### SEE ALSO -* [kops delete](kops_delete.md) - Delete clusters,instancegroups, instances, or secrets. +* [kops delete](kops_delete.md) - Delete clusters, instancegroups, instances, keypairs, or secrets. diff --git a/docs/cli/kops_delete_keypair.md b/docs/cli/kops_delete_keypair.md new file mode 100644 index 0000000000000..1c5bc648f9db5 --- /dev/null +++ b/docs/cli/kops_delete_keypair.md @@ -0,0 +1,53 @@ + + + +## kops delete keypair + +Delete a keypair. + +### Synopsis + +Delete a keypair. + +``` +kops delete keypair [flags] +``` + +### Examples + +``` + # Syntax: kops delete keypair KEYSET ID + kops delete keypair ca 5938372002934847 +``` + +### Options + +``` + -h, --help help for keypair +``` + +### Options inherited from parent commands + +``` + --add_dir_header If true, adds the file directory to the header of the log messages + --alsologtostderr log to standard error as well as files + --config string yaml config file (default is $HOME/.kops.yaml) + --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_dir string If non-empty, write log files in this directory + --log_file string If non-empty, use this log file + --log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) + --logtostderr log to standard error instead of files (default true) + --name string Name of cluster. Overrides KOPS_CLUSTER_NAME environment variable + --one_output If true, only write logs to their native severity level (vs also writing to each lower severity level) + --skip_headers If true, avoid header prefixes in the log messages + --skip_log_headers If true, avoid headers when opening log files + --state string Location of state storage (kops 'config' file). Overrides KOPS_STATE_STORE environment variable + --stderrthreshold severity logs at or above this threshold go to stderr (default 2) + -v, --v Level number for the log level verbosity + --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging +``` + +### SEE ALSO + +* [kops delete](kops_delete.md) - Delete clusters, instancegroups, instances, keypairs, or secrets. + diff --git a/docs/cli/kops_delete_secret.md b/docs/cli/kops_delete_secret.md index 45b233025b601..ffa0d2fddcec0 100644 --- a/docs/cli/kops_delete_secret.md +++ b/docs/cli/kops_delete_secret.md @@ -50,5 +50,5 @@ kops delete secret [flags] ### SEE ALSO -* [kops delete](kops_delete.md) - Delete clusters,instancegroups, instances, or secrets. +* [kops delete](kops_delete.md) - Delete clusters, instancegroups, instances, keypairs, or secrets. diff --git a/docs/cli/kops_describe.md b/docs/cli/kops_describe.md index 4823ff592299a..6130b3542858e 100644 --- a/docs/cli/kops_describe.md +++ b/docs/cli/kops_describe.md @@ -45,5 +45,5 @@ Get additional information about cloud and cluster resources. ### SEE ALSO * [kops](kops.md) - kOps is Kubernetes Operations. -* [kops describe secrets](kops_describe_secrets.md) - Describe a cluster secret +* [kops describe keypairs](kops_describe_keypairs.md) - Describe a cluster keypair diff --git a/docs/cli/kops_describe_secrets.md b/docs/cli/kops_describe_keypairs.md similarity index 88% rename from docs/cli/kops_describe_secrets.md rename to docs/cli/kops_describe_keypairs.md index 4d810defc895c..c2253a17d4f42 100644 --- a/docs/cli/kops_describe_secrets.md +++ b/docs/cli/kops_describe_keypairs.md @@ -1,30 +1,29 @@ -## kops describe secrets +## kops describe keypairs -Describe a cluster secret +Describe a cluster keypair ### Synopsis -Get additional information about cluster secrets. +Get additional information about keypairs. ``` -kops describe secrets [flags] +kops describe keypairs [flags] ``` ### Examples ``` - # Describe a secret - kops describe secrets admin + # Describe a keypair + kops describe keypairs ca ``` ### Options ``` - -h, --help help for secrets - --type string Filter by secret type + -h, --help help for keypairs ``` ### Options inherited from parent commands diff --git a/docs/cli/kops_get.md b/docs/cli/kops_get.md index 68cbdbd8329bc..fe343a2bed527 100644 --- a/docs/cli/kops_get.md +++ b/docs/cli/kops_get.md @@ -74,5 +74,6 @@ kops get [flags] * [kops get clusters](kops_get_clusters.md) - Get one or many clusters. * [kops get instancegroups](kops_get_instancegroups.md) - Get one or many instancegroups * [kops get instances](kops_get_instances.md) - Display cluster instances. +* [kops get keypairs](kops_get_keypairs.md) - Get one or many keypairs. * [kops get secrets](kops_get_secrets.md) - Get one or many secrets. diff --git a/docs/cli/kops_get_keypairs.md b/docs/cli/kops_get_keypairs.md new file mode 100644 index 0000000000000..ef4f84588f2e4 --- /dev/null +++ b/docs/cli/kops_get_keypairs.md @@ -0,0 +1,57 @@ + + + +## kops get keypairs + +Get one or many keypairs. + +### Synopsis + +Display one or many keypairs. + +``` +kops get keypairs [flags] +``` + +### Examples + +``` + # List the cluster CA keypairs. + kops get keypairs ca + + # List the service-account keypairs. + kops get keypairs service-account +``` + +### Options + +``` + -h, --help help for keypairs +``` + +### Options inherited from parent commands + +``` + --add_dir_header If true, adds the file directory to the header of the log messages + --alsologtostderr log to standard error as well as files + --config string yaml config file (default is $HOME/.kops.yaml) + --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_dir string If non-empty, write log files in this directory + --log_file string If non-empty, use this log file + --log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) + --logtostderr log to standard error instead of files (default true) + --name string Name of cluster. Overrides KOPS_CLUSTER_NAME environment variable + --one_output If true, only write logs to their native severity level (vs also writing to each lower severity level) + -o, --output string output format. One of: table, yaml, json (default "table") + --skip_headers If true, avoid header prefixes in the log messages + --skip_log_headers If true, avoid headers when opening log files + --state string Location of state storage (kops 'config' file). Overrides KOPS_STATE_STORE environment variable + --stderrthreshold severity logs at or above this threshold go to stderr (default 2) + -v, --v Level number for the log level verbosity + --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging +``` + +### SEE ALSO + +* [kops get](kops_get.md) - Get one or many resources. + From 5cc7a379ced28e65c1979ab7e31595c744a0db03 Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sat, 19 Jun 2021 16:58:29 -0700 Subject: [PATCH 2/2] Fix VFSCAStore.ListKeysets() --- upup/pkg/fi/vfs_castore.go | 63 ++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/upup/pkg/fi/vfs_castore.go b/upup/pkg/fi/vfs_castore.go index 6f69fd5a02806..a8abdfdeecc03 100644 --- a/upup/pkg/fi/vfs_castore.go +++ b/upup/pkg/fi/vfs_castore.go @@ -341,51 +341,42 @@ func (c *VFSCAStore) FindCertificateKeyset(name string) (*kops.Keyset, error) { // ListKeysets implements CAStore::ListKeysets func (c *VFSCAStore) ListKeysets() ([]*kops.Keyset, error) { - keysets := make(map[string]*kops.Keyset) + baseDir := c.basedir.Join("private") + files, err := baseDir.ReadTree() + if err != nil { + return nil, fmt.Errorf("error reading directory %q: %v", baseDir, err) + } - { - baseDir := c.basedir.Join("issued") - files, err := baseDir.ReadTree() + var keysets []*kops.Keyset + + for _, f := range files { + relativePath, err := vfs.RelativePath(baseDir, f) if err != nil { - return nil, fmt.Errorf("error reading directory %q: %v", baseDir, err) + return nil, err } - for _, f := range files { - relativePath, err := vfs.RelativePath(baseDir, f) - if err != nil { - return nil, err - } - - tokens := strings.Split(relativePath, "/") - if len(tokens) != 2 { - klog.V(2).Infof("ignoring unexpected file in keystore: %q", f) - continue - } + tokens := strings.Split(relativePath, "/") + if len(tokens) != 2 || tokens[1] != "keyset.yaml" { + klog.V(2).Infof("ignoring unexpected file in keystore: %q", f) + continue + } - name := tokens[0] - keyset := keysets[name] - if keyset == nil { - keyset = &kops.Keyset{} - keyset.Name = tokens[0] - keyset.Spec.Type = kops.SecretTypeKeypair - keysets[name] = keyset - } + name := tokens[0] + loadedKeyset, err := c.loadKeyset(baseDir.Join(name)) + if err != nil { + klog.Warningf("ignoring keyset %q: %w", name, err) + continue + } - if tokens[1] == "keyset.yaml" { - // TODO: Should we load the keyset to get the actual ids? - } else { - keyset.Spec.Keys = append(keyset.Spec.Keys, kops.KeysetItem{ - Id: strings.TrimSuffix(tokens[1], ".crt"), - }) - } + keyset, err := loadedKeyset.ToAPIObject(name, true) + if err != nil { + klog.Warningf("ignoring keyset %q: %w", name, err) + continue } + keysets = append(keysets, keyset) } - var items []*kops.Keyset - for _, v := range keysets { - items = append(items, v) - } - return items, nil + return keysets, nil } // ListSSHCredentials implements SSHCredentialStore::ListSSHCredentials