From b758e6c3a53d6d0a910045b572bdd43636e4b61e Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Fri, 9 Jun 2017 14:05:49 -0400 Subject: [PATCH] Add `oadm migrate etcd-ttl` which encodes upstream TTL migration This is in support of v2 -> v3 --- contrib/completions/bash/oadm | 58 +++++++++++ contrib/completions/bash/oc | 56 ++++++++++ contrib/completions/bash/openshift | 116 +++++++++++++++++++++ contrib/completions/zsh/oadm | 58 +++++++++++ contrib/completions/zsh/oc | 56 ++++++++++ pkg/cmd/admin/admin.go | 2 + pkg/cmd/admin/migrate/etcd/ttl.go | 162 +++++++++++++++++++++++++++++ 7 files changed, 508 insertions(+) create mode 100644 pkg/cmd/admin/migrate/etcd/ttl.go diff --git a/contrib/completions/bash/oadm b/contrib/completions/bash/oadm index 2e311aa4b211..b3af21d7865b 100644 --- a/contrib/completions/bash/oadm +++ b/contrib/completions/bash/oadm @@ -2702,6 +2702,63 @@ _oadm_manage-node() noun_aliases=() } +_oadm_migrate_etcd-ttl() +{ + last_command="oadm_migrate_etcd-ttl" + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--cacert=") + local_nonpersistent_flags+=("--cacert=") + flags+=("--cert=") + local_nonpersistent_flags+=("--cert=") + flags+=("--etcd-address=") + local_nonpersistent_flags+=("--etcd-address=") + flags+=("--key=") + local_nonpersistent_flags+=("--key=") + flags+=("--lease-duration=") + local_nonpersistent_flags+=("--lease-duration=") + flags+=("--ttl-keys-prefix=") + local_nonpersistent_flags+=("--ttl-keys-prefix=") + flags+=("--as=") + flags+=("--azure-container-registry-config=") + flags+=("--certificate-authority=") + flags_with_completion+=("--certificate-authority") + flags_completion+=("_filedir") + flags+=("--client-certificate=") + flags_with_completion+=("--client-certificate") + flags_completion+=("_filedir") + flags+=("--client-key=") + flags_with_completion+=("--client-key") + flags_completion+=("_filedir") + flags+=("--cluster=") + flags+=("--config=") + flags_with_completion+=("--config") + flags_completion+=("_filedir") + flags+=("--context=") + flags+=("--google-json-key=") + flags+=("--insecure-skip-tls-verify") + flags+=("--log-flush-frequency=") + flags+=("--loglevel=") + flags+=("--logspec=") + flags+=("--match-server-version") + flags+=("--namespace=") + two_word_flags+=("-n") + flags+=("--request-timeout=") + flags+=("--server=") + flags+=("--token=") + flags+=("--user=") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _oadm_migrate_image-references() { last_command="oadm_migrate_image-references" @@ -2844,6 +2901,7 @@ _oadm_migrate() { last_command="oadm_migrate" commands=() + commands+=("etcd-ttl") commands+=("image-references") commands+=("storage") diff --git a/contrib/completions/bash/oc b/contrib/completions/bash/oc index b611f23209fd..2404455d76b8 100644 --- a/contrib/completions/bash/oc +++ b/contrib/completions/bash/oc @@ -2779,6 +2779,61 @@ _oc_adm_manage-node() noun_aliases=() } +_oc_adm_migrate_etcd-ttl() +{ + last_command="oc_adm_migrate_etcd-ttl" + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--cacert=") + local_nonpersistent_flags+=("--cacert=") + flags+=("--cert=") + local_nonpersistent_flags+=("--cert=") + flags+=("--etcd-address=") + local_nonpersistent_flags+=("--etcd-address=") + flags+=("--key=") + local_nonpersistent_flags+=("--key=") + flags+=("--lease-duration=") + local_nonpersistent_flags+=("--lease-duration=") + flags+=("--ttl-keys-prefix=") + local_nonpersistent_flags+=("--ttl-keys-prefix=") + flags+=("--as=") + flags+=("--certificate-authority=") + flags_with_completion+=("--certificate-authority") + flags_completion+=("_filedir") + flags+=("--client-certificate=") + flags_with_completion+=("--client-certificate") + flags_completion+=("_filedir") + flags+=("--client-key=") + flags_with_completion+=("--client-key") + flags_completion+=("_filedir") + flags+=("--cluster=") + flags+=("--config=") + flags_with_completion+=("--config") + flags_completion+=("_filedir") + flags+=("--context=") + flags+=("--insecure-skip-tls-verify") + flags+=("--log-flush-frequency=") + flags+=("--loglevel=") + flags+=("--logspec=") + flags+=("--match-server-version") + flags+=("--namespace=") + two_word_flags+=("-n") + flags+=("--request-timeout=") + flags+=("--server=") + flags+=("--token=") + flags+=("--user=") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _oc_adm_migrate_image-references() { last_command="oc_adm_migrate_image-references" @@ -2917,6 +2972,7 @@ _oc_adm_migrate() { last_command="oc_adm_migrate" commands=() + commands+=("etcd-ttl") commands+=("image-references") commands+=("storage") diff --git a/contrib/completions/bash/openshift b/contrib/completions/bash/openshift index 325b46a84ce7..8c5fba65a728 100644 --- a/contrib/completions/bash/openshift +++ b/contrib/completions/bash/openshift @@ -2702,6 +2702,63 @@ _openshift_admin_manage-node() noun_aliases=() } +_openshift_admin_migrate_etcd-ttl() +{ + last_command="openshift_admin_migrate_etcd-ttl" + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--cacert=") + local_nonpersistent_flags+=("--cacert=") + flags+=("--cert=") + local_nonpersistent_flags+=("--cert=") + flags+=("--etcd-address=") + local_nonpersistent_flags+=("--etcd-address=") + flags+=("--key=") + local_nonpersistent_flags+=("--key=") + flags+=("--lease-duration=") + local_nonpersistent_flags+=("--lease-duration=") + flags+=("--ttl-keys-prefix=") + local_nonpersistent_flags+=("--ttl-keys-prefix=") + flags+=("--as=") + flags+=("--azure-container-registry-config=") + flags+=("--certificate-authority=") + flags_with_completion+=("--certificate-authority") + flags_completion+=("_filedir") + flags+=("--client-certificate=") + flags_with_completion+=("--client-certificate") + flags_completion+=("_filedir") + flags+=("--client-key=") + flags_with_completion+=("--client-key") + flags_completion+=("_filedir") + flags+=("--cluster=") + flags+=("--config=") + flags_with_completion+=("--config") + flags_completion+=("_filedir") + flags+=("--context=") + flags+=("--google-json-key=") + flags+=("--insecure-skip-tls-verify") + flags+=("--log-flush-frequency=") + flags+=("--loglevel=") + flags+=("--logspec=") + flags+=("--match-server-version") + flags+=("--namespace=") + two_word_flags+=("-n") + flags+=("--request-timeout=") + flags+=("--server=") + flags+=("--token=") + flags+=("--user=") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _openshift_admin_migrate_image-references() { last_command="openshift_admin_migrate_image-references" @@ -2844,6 +2901,7 @@ _openshift_admin_migrate() { last_command="openshift_admin_migrate" commands=() + commands+=("etcd-ttl") commands+=("image-references") commands+=("storage") @@ -7833,6 +7891,63 @@ _openshift_cli_adm_manage-node() noun_aliases=() } +_openshift_cli_adm_migrate_etcd-ttl() +{ + last_command="openshift_cli_adm_migrate_etcd-ttl" + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--cacert=") + local_nonpersistent_flags+=("--cacert=") + flags+=("--cert=") + local_nonpersistent_flags+=("--cert=") + flags+=("--etcd-address=") + local_nonpersistent_flags+=("--etcd-address=") + flags+=("--key=") + local_nonpersistent_flags+=("--key=") + flags+=("--lease-duration=") + local_nonpersistent_flags+=("--lease-duration=") + flags+=("--ttl-keys-prefix=") + local_nonpersistent_flags+=("--ttl-keys-prefix=") + flags+=("--as=") + flags+=("--azure-container-registry-config=") + flags+=("--certificate-authority=") + flags_with_completion+=("--certificate-authority") + flags_completion+=("_filedir") + flags+=("--client-certificate=") + flags_with_completion+=("--client-certificate") + flags_completion+=("_filedir") + flags+=("--client-key=") + flags_with_completion+=("--client-key") + flags_completion+=("_filedir") + flags+=("--cluster=") + flags+=("--config=") + flags_with_completion+=("--config") + flags_completion+=("_filedir") + flags+=("--context=") + flags+=("--google-json-key=") + flags+=("--insecure-skip-tls-verify") + flags+=("--log-flush-frequency=") + flags+=("--loglevel=") + flags+=("--logspec=") + flags+=("--match-server-version") + flags+=("--namespace=") + two_word_flags+=("-n") + flags+=("--request-timeout=") + flags+=("--server=") + flags+=("--token=") + flags+=("--user=") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _openshift_cli_adm_migrate_image-references() { last_command="openshift_cli_adm_migrate_image-references" @@ -7975,6 +8090,7 @@ _openshift_cli_adm_migrate() { last_command="openshift_cli_adm_migrate" commands=() + commands+=("etcd-ttl") commands+=("image-references") commands+=("storage") diff --git a/contrib/completions/zsh/oadm b/contrib/completions/zsh/oadm index fea315d83c05..45357dc64c9b 100644 --- a/contrib/completions/zsh/oadm +++ b/contrib/completions/zsh/oadm @@ -2851,6 +2851,63 @@ _oadm_manage-node() noun_aliases=() } +_oadm_migrate_etcd-ttl() +{ + last_command="oadm_migrate_etcd-ttl" + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--cacert=") + local_nonpersistent_flags+=("--cacert=") + flags+=("--cert=") + local_nonpersistent_flags+=("--cert=") + flags+=("--etcd-address=") + local_nonpersistent_flags+=("--etcd-address=") + flags+=("--key=") + local_nonpersistent_flags+=("--key=") + flags+=("--lease-duration=") + local_nonpersistent_flags+=("--lease-duration=") + flags+=("--ttl-keys-prefix=") + local_nonpersistent_flags+=("--ttl-keys-prefix=") + flags+=("--as=") + flags+=("--azure-container-registry-config=") + flags+=("--certificate-authority=") + flags_with_completion+=("--certificate-authority") + flags_completion+=("_filedir") + flags+=("--client-certificate=") + flags_with_completion+=("--client-certificate") + flags_completion+=("_filedir") + flags+=("--client-key=") + flags_with_completion+=("--client-key") + flags_completion+=("_filedir") + flags+=("--cluster=") + flags+=("--config=") + flags_with_completion+=("--config") + flags_completion+=("_filedir") + flags+=("--context=") + flags+=("--google-json-key=") + flags+=("--insecure-skip-tls-verify") + flags+=("--log-flush-frequency=") + flags+=("--loglevel=") + flags+=("--logspec=") + flags+=("--match-server-version") + flags+=("--namespace=") + two_word_flags+=("-n") + flags+=("--request-timeout=") + flags+=("--server=") + flags+=("--token=") + flags+=("--user=") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _oadm_migrate_image-references() { last_command="oadm_migrate_image-references" @@ -2993,6 +3050,7 @@ _oadm_migrate() { last_command="oadm_migrate" commands=() + commands+=("etcd-ttl") commands+=("image-references") commands+=("storage") diff --git a/contrib/completions/zsh/oc b/contrib/completions/zsh/oc index efef6196fc10..6925bab5ac33 100644 --- a/contrib/completions/zsh/oc +++ b/contrib/completions/zsh/oc @@ -2928,6 +2928,61 @@ _oc_adm_manage-node() noun_aliases=() } +_oc_adm_migrate_etcd-ttl() +{ + last_command="oc_adm_migrate_etcd-ttl" + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--cacert=") + local_nonpersistent_flags+=("--cacert=") + flags+=("--cert=") + local_nonpersistent_flags+=("--cert=") + flags+=("--etcd-address=") + local_nonpersistent_flags+=("--etcd-address=") + flags+=("--key=") + local_nonpersistent_flags+=("--key=") + flags+=("--lease-duration=") + local_nonpersistent_flags+=("--lease-duration=") + flags+=("--ttl-keys-prefix=") + local_nonpersistent_flags+=("--ttl-keys-prefix=") + flags+=("--as=") + flags+=("--certificate-authority=") + flags_with_completion+=("--certificate-authority") + flags_completion+=("_filedir") + flags+=("--client-certificate=") + flags_with_completion+=("--client-certificate") + flags_completion+=("_filedir") + flags+=("--client-key=") + flags_with_completion+=("--client-key") + flags_completion+=("_filedir") + flags+=("--cluster=") + flags+=("--config=") + flags_with_completion+=("--config") + flags_completion+=("_filedir") + flags+=("--context=") + flags+=("--insecure-skip-tls-verify") + flags+=("--log-flush-frequency=") + flags+=("--loglevel=") + flags+=("--logspec=") + flags+=("--match-server-version") + flags+=("--namespace=") + two_word_flags+=("-n") + flags+=("--request-timeout=") + flags+=("--server=") + flags+=("--token=") + flags+=("--user=") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _oc_adm_migrate_image-references() { last_command="oc_adm_migrate_image-references" @@ -3066,6 +3121,7 @@ _oc_adm_migrate() { last_command="oc_adm_migrate" commands=() + commands+=("etcd-ttl") commands+=("image-references") commands+=("storage") diff --git a/pkg/cmd/admin/admin.go b/pkg/cmd/admin/admin.go index 2f3081335a2f..92d8278d4339 100644 --- a/pkg/cmd/admin/admin.go +++ b/pkg/cmd/admin/admin.go @@ -14,6 +14,7 @@ import ( "github.com/openshift/origin/pkg/cmd/admin/groups" "github.com/openshift/origin/pkg/cmd/admin/image" "github.com/openshift/origin/pkg/cmd/admin/migrate" + migrateetcd "github.com/openshift/origin/pkg/cmd/admin/migrate/etcd" migrateimages "github.com/openshift/origin/pkg/cmd/admin/migrate/images" migratestorage "github.com/openshift/origin/pkg/cmd/admin/migrate/storage" "github.com/openshift/origin/pkg/cmd/admin/network" @@ -93,6 +94,7 @@ func NewCommandAdmin(name, fullName string, in io.Reader, out io.Writer, errout // Migration commands migrateimages.NewCmdMigrateImageReferences("image-references", fullName+" "+migrate.MigrateRecommendedName+" image-references", f, in, out, errout), migratestorage.NewCmdMigrateAPIStorage("storage", fullName+" "+migrate.MigrateRecommendedName+" storage", f, in, out, errout), + migrateetcd.NewCmdMigrateTTLs("etcd-ttl", fullName+" "+migrate.MigrateRecommendedName+" image-references", f, in, out, errout), ), top.NewCommandTop(top.TopRecommendedName, fullName+" "+top.TopRecommendedName, f, out, errout), image.NewCmdVerifyImageSignature(name, fullName+" "+image.VerifyRecommendedName, f, out, errout), diff --git a/pkg/cmd/admin/migrate/etcd/ttl.go b/pkg/cmd/admin/migrate/etcd/ttl.go new file mode 100644 index 000000000000..2a639a69d52c --- /dev/null +++ b/pkg/cmd/admin/migrate/etcd/ttl.go @@ -0,0 +1,162 @@ +package etcd + +import ( + "fmt" + "io" + "strings" + "time" + + "k8s.io/kubernetes/pkg/kubectl/cmd/templates" + kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + + "github.com/coreos/etcd/clientv3" + "github.com/coreos/etcd/pkg/transport" + "github.com/golang/glog" + "github.com/openshift/origin/pkg/cmd/util/clientcmd" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "golang.org/x/net/context" +) + +var ( + internalMigrateTTLLong = templates.LongDesc(` + Attach etcd keys to v3 leases to assist in migration from etcd v2 + + This command updates keys to associate them with an etcd v3 lease. In etcd v2, keys have an + innate TTL field which has been altered in the new schema. This can be used to set a timeout + on keys migrated from the etcd v2 schema to etcd v3 is intended to be used after that upgrade + is complete on events and access tokens. + + Any resource impacted by this command will be removed from etcd after the lease-duration + expires. Be VERY CAREFUL in which values you place to --ttl-keys-prefix, and ensure you + have an up to date backup of your etcd database.`) + + internalMigrateTTLExample = templates.Examples(` + # Migrate TTLs for keys under /kubernetes.io/events to a 2 hour lease + %[1]s --etcd-address=localhost:2379 --ttl-keys-prefix=/kubernetes.io/events/ --lease-duration=2h`) +) + +type MigrateTTLReferenceOptions struct { + Out, ErrOut io.Writer + + etcdAddress string + ttlKeysPrefix string + leaseDuration time.Duration + certFile string + keyFile string + caFile string +} + +// NewCmdMigrateTTLs helps move etcd v2 TTL keys to etcd v3 lease keys. +func NewCmdMigrateTTLs(name, fullName string, f *clientcmd.Factory, in io.Reader, out, errout io.Writer) *cobra.Command { + options := &MigrateTTLReferenceOptions{ + Out: out, + ErrOut: errout, + } + cmd := &cobra.Command{ + Use: fmt.Sprintf("%s --etcd-address=HOST --ttl-keys-prefix=PATH", name), + Short: "Attach keys to etcd v3 leases to assist in etcd v2 migrations", + Long: internalMigrateTTLLong, + Example: fmt.Sprintf(internalMigrateTTLExample, fullName), + Run: func(cmd *cobra.Command, args []string) { + kcmdutil.CheckErr(options.Run()) + }, + } + + options.Bind(cmd.Flags()) + + return cmd +} + +func (o *MigrateTTLReferenceOptions) Bind(flag *pflag.FlagSet) { + flag.StringVar(&o.etcdAddress, "etcd-address", "", "Etcd address") + flag.StringVar(&o.ttlKeysPrefix, "ttl-keys-prefix", "", "Prefix for TTL keys") + flag.DurationVar(&o.leaseDuration, "lease-duration", time.Hour, "Lease duration (seconds granularity)") + flag.StringVar(&o.certFile, "cert", "", "identify secure client using this TLS certificate file") + flag.StringVar(&o.keyFile, "key", "", "identify secure client using this TLS key file") + flag.StringVar(&o.caFile, "cacert", "", "verify certificates of TLS-enabled secure servers using this CA bundle") +} + +func generateClientConfig(o *MigrateTTLReferenceOptions) (*clientv3.Config, error) { + if o.etcdAddress == "" { + return nil, fmt.Errorf("--etcd-address flag is required") + } + if o.ttlKeysPrefix == "" { + return nil, fmt.Errorf("--ttl-keys-prefix flag is required") + } + + c := &clientv3.Config{ + Endpoints: []string{o.etcdAddress}, + } + + var cfgtls *transport.TLSInfo + tlsinfo := transport.TLSInfo{} + if o.certFile != "" { + tlsinfo.CertFile = o.certFile + cfgtls = &tlsinfo + } + + if o.keyFile != "" { + tlsinfo.KeyFile = o.keyFile + cfgtls = &tlsinfo + } + + if o.caFile != "" { + tlsinfo.CAFile = o.caFile + cfgtls = &tlsinfo + } + + if cfgtls != nil { + glog.V(4).Infof("TLS configuration: %#v", cfgtls) + clientTLS, err := cfgtls.ClientConfig() + if err != nil { + return nil, err + } + c.TLS = clientTLS + } + return c, nil +} + +func (o *MigrateTTLReferenceOptions) Run() error { + c, err := generateClientConfig(o) + if err != nil { + return err + } + glog.V(4).Infof("Using client config: %#v", c) + + client, err := clientv3.New(*c) + if err != nil { + return fmt.Errorf("unable to create etcd client: %v", err) + } + + // Make sure that ttlKeysPrefix is ended with "/" so that we only get children "directories". + if !strings.HasSuffix(o.ttlKeysPrefix, "/") { + o.ttlKeysPrefix += "/" + } + ctx := context.Background() + + objectsResp, err := client.KV.Get(ctx, o.ttlKeysPrefix, clientv3.WithPrefix()) + if err != nil { + return fmt.Errorf("unable to get objects to attach to the lease: %v", err) + } + + lease, err := client.Lease.Grant(ctx, int64(o.leaseDuration/time.Second)) + if err != nil { + return fmt.Errorf("unable to create lease: %v", err) + } + fmt.Fprintf(o.Out, "info: Lease with TTL %d created\n", lease.TTL) + + fmt.Fprintf(o.Out, "info: Attaching lease to %d entries\n", len(objectsResp.Kvs)) + errors := 0 + for _, kv := range objectsResp.Kvs { + _, err := client.KV.Put(ctx, string(kv.Key), string(kv.Value), clientv3.WithLease(lease.ID)) + if err != nil { + fmt.Fprintf(o.ErrOut, "error: Unable to attach lease to %s: %v\n", string(kv.Key), err) + errors++ + } + } + if errors != 0 { + return fmt.Errorf("unable to complete migration, encountered %d errors", errors) + } + return nil +}