Merge pull request #41216 from marun/kubefed-auto-bind-flags

Automatic merge from submit-queue

[Federation][kubefed]: Bind flag values automatically

Update ``kubefed`` to take advantage of cobra's ability to declare variable bindings at the same time as declaring flags, so that flag values are bound automatically.

Targets #41209 

cc: @kubernetes/sig-federation-pr-reviews @irfanurrehman @madhusudancs
This commit is contained in:
Kubernetes Submit Queue 2017-02-14 15:29:58 -08:00 committed by GitHub
commit af1d04f1f2
7 changed files with 166 additions and 112 deletions

View File

@ -27,6 +27,7 @@ go_library(
"//pkg/kubectl/resource:go_default_library", "//pkg/kubectl/resource:go_default_library",
"//vendor:github.com/golang/glog", "//vendor:github.com/golang/glog",
"//vendor:github.com/spf13/cobra", "//vendor:github.com/spf13/cobra",
"//vendor:github.com/spf13/pflag",
"//vendor:k8s.io/apimachinery/pkg/api/errors", "//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime", "//vendor:k8s.io/apimachinery/pkg/runtime",

View File

@ -24,6 +24,7 @@ go_library(
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",
"//pkg/version:go_default_library", "//pkg/version:go_default_library",
"//vendor:github.com/spf13/cobra", "//vendor:github.com/spf13/cobra",
"//vendor:github.com/spf13/pflag",
"//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/api/resource",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/util/wait", "//vendor:k8s.io/apimachinery/pkg/util/wait",

View File

@ -34,6 +34,7 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"sort"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -57,7 +58,7 @@ import (
"k8s.io/kubernetes/pkg/version" "k8s.io/kubernetes/pkg/version"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sort" "github.com/spf13/pflag"
) )
const ( const (
@ -119,34 +120,64 @@ var (
hyperkubeImageName = "gcr.io/google_containers/hyperkube-amd64" hyperkubeImageName = "gcr.io/google_containers/hyperkube-amd64"
) )
type initFederation struct {
commonOptions util.SubcommandOptions
options initFederationOptions
}
type initFederationOptions struct {
dnsZoneName string
image string
dnsProvider string
etcdPVCapacity string
etcdPersistentStorage bool
dryRun bool
storageBackend string
apiServerOverridesString string
apiServerOverrides map[string]string
controllerManagerOverridesString string
controllerManagerOverrides map[string]string
apiServerServiceTypeString string
apiServerServiceType v1.ServiceType
apiServerAdvertiseAddress string
}
func (o *initFederationOptions) Bind(flags *pflag.FlagSet) {
defaultImage := fmt.Sprintf("%s:%s", hyperkubeImageName, version.Get())
flags.StringVar(&o.dnsZoneName, "dns-zone-name", "", "DNS suffix for this federation. Federated Service DNS names are published with this suffix.")
flags.StringVar(&o.image, "image", defaultImage, "Image to use for federation API server and controller manager binaries.")
flags.StringVar(&o.dnsProvider, "dns-provider", "google-clouddns", "Dns provider to be used for this deployment.")
flags.StringVar(&o.etcdPVCapacity, "etcd-pv-capacity", "10Gi", "Size of persistent volume claim to be used for etcd.")
flags.BoolVar(&o.etcdPersistentStorage, "etcd-persistent-storage", true, "Use persistent volume for etcd. Defaults to 'true'.")
flags.BoolVar(&o.dryRun, "dry-run", false, "dry run without sending commands to server.")
flags.StringVar(&o.storageBackend, "storage-backend", "etcd2", "The storage backend for persistence. Options: 'etcd2' (default), 'etcd3'.")
flags.StringVar(&o.apiServerOverridesString, "apiserver-arg-overrides", "", "comma separated list of federation-apiserver arguments to override: Example \"--arg1=value1,--arg2=value2...\"")
flags.StringVar(&o.controllerManagerOverridesString, "controllermanager-arg-overrides", "", "comma separated list of federation-controller-manager arguments to override: Example \"--arg1=value1,--arg2=value2...\"")
flags.StringVar(&o.apiServerServiceTypeString, apiserverServiceTypeFlag, string(v1.ServiceTypeLoadBalancer), "The type of service to create for federation API server. Options: 'LoadBalancer' (default), 'NodePort'.")
flags.StringVar(&o.apiServerAdvertiseAddress, apiserverAdvertiseAddressFlag, "", "Preferred address to advertise api server nodeport service. Valid only if '"+apiserverServiceTypeFlag+"=NodePort'.")
}
// NewCmdInit defines the `init` command that bootstraps a federation // NewCmdInit defines the `init` command that bootstraps a federation
// control plane inside a set of host clusters. // control plane inside a set of host clusters.
func NewCmdInit(cmdOut io.Writer, config util.AdminConfig) *cobra.Command { func NewCmdInit(cmdOut io.Writer, config util.AdminConfig) *cobra.Command {
opts := &initFederation{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "init FEDERATION_NAME --host-cluster-context=HOST_CONTEXT", Use: "init FEDERATION_NAME --host-cluster-context=HOST_CONTEXT",
Short: "init initializes a federation control plane", Short: "init initializes a federation control plane",
Long: init_long, Long: init_long,
Example: init_example, Example: init_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := initFederation(cmdOut, config, cmd, args) cmdutil.CheckErr(opts.Complete(cmd, args))
cmdutil.CheckErr(err) cmdutil.CheckErr(opts.Run(cmdOut, config))
}, },
} }
defaultImage := fmt.Sprintf("%s:%s", hyperkubeImageName, version.Get()) flags := cmd.Flags()
opts.commonOptions.Bind(flags)
opts.options.Bind(flags)
util.AddSubcommandFlags(cmd)
cmd.Flags().String("dns-zone-name", "", "DNS suffix for this federation. Federated Service DNS names are published with this suffix.")
cmd.Flags().String("image", defaultImage, "Image to use for federation API server and controller manager binaries.")
cmd.Flags().String("dns-provider", "google-clouddns", "Dns provider to be used for this deployment.")
cmd.Flags().String("etcd-pv-capacity", "10Gi", "Size of persistent volume claim to be used for etcd.")
cmd.Flags().Bool("etcd-persistent-storage", true, "Use persistent volume for etcd. Defaults to 'true'.")
cmd.Flags().Bool("dry-run", false, "dry run without sending commands to server.")
cmd.Flags().String("apiserver-arg-overrides", "", "comma separated list of federation-apiserver arguments to override: Example \"--arg1=value1,--arg2=value2...\"")
cmd.Flags().String("controllermanager-arg-overrides", "", "comma separated list of federation-controller-manager arguments to override: Example \"--arg1=value1,--arg2=value2...\"")
cmd.Flags().String("storage-backend", "etcd2", "The storage backend for persistence. Options: 'etcd2' (default), 'etcd3'.")
cmd.Flags().String(apiserverServiceTypeFlag, string(v1.ServiceTypeLoadBalancer), "The type of service to create for federation API server. Options: 'LoadBalancer' (default), 'NodePort'.")
cmd.Flags().String(apiserverAdvertiseAddressFlag, "", "Preferred address to advertise api server nodeport service. Valid only if '"+apiserverServiceTypeFlag+"=NodePort'.")
return cmd return cmd
} }
@ -157,81 +188,79 @@ type entityKeyPairs struct {
admin *triple.KeyPair admin *triple.KeyPair
} }
// initFederation initializes a federation control plane. // Complete ensures that options are valid and marshals them if necessary.
// See the design doc in https://github.com/kubernetes/kubernetes/pull/34484 func (i *initFederation) Complete(cmd *cobra.Command, args []string) error {
// for details. err := i.commonOptions.SetName(cmd, args)
func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Command, args []string) error {
initFlags, err := util.GetSubcommandFlags(cmd, args)
if err != nil { if err != nil {
return err return err
} }
dnsZoneName := cmdutil.GetFlagString(cmd, "dns-zone-name")
image := cmdutil.GetFlagString(cmd, "image")
dnsProvider := cmdutil.GetFlagString(cmd, "dns-provider")
etcdPVCapacity := cmdutil.GetFlagString(cmd, "etcd-pv-capacity")
etcdPersistence := cmdutil.GetFlagBool(cmd, "etcd-persistent-storage")
dryRun := cmdutil.GetDryRunFlag(cmd)
storageBackend := cmdutil.GetFlagString(cmd, "storage-backend")
apiserverServiceType := v1.ServiceType(cmdutil.GetFlagString(cmd, apiserverServiceTypeFlag))
apiserverAdvertiseAddress := cmdutil.GetFlagString(cmd, apiserverAdvertiseAddressFlag)
if apiserverServiceType != v1.ServiceTypeLoadBalancer && apiserverServiceType != v1.ServiceTypeNodePort { i.options.apiServerServiceType = v1.ServiceType(i.options.apiServerServiceTypeString)
return fmt.Errorf("invalid %s: %s, should be either %s or %s", apiserverServiceTypeFlag, apiserverServiceType, v1.ServiceTypeLoadBalancer, v1.ServiceTypeNodePort) if i.options.apiServerServiceType != v1.ServiceTypeLoadBalancer && i.options.apiServerServiceType != v1.ServiceTypeNodePort {
return fmt.Errorf("invalid %s: %s, should be either %s or %s", apiserverServiceTypeFlag, i.options.apiServerServiceType, v1.ServiceTypeLoadBalancer, v1.ServiceTypeNodePort)
} }
if apiserverAdvertiseAddress != "" { if i.options.apiServerAdvertiseAddress != "" {
ip := net.ParseIP(apiserverAdvertiseAddress) ip := net.ParseIP(i.options.apiServerAdvertiseAddress)
if ip == nil { if ip == nil {
return fmt.Errorf("invalid %s: %s, should be a valid ip address", apiserverAdvertiseAddressFlag, apiserverAdvertiseAddress) return fmt.Errorf("invalid %s: %s, should be a valid ip address", apiserverAdvertiseAddressFlag, i.options.apiServerAdvertiseAddress)
} }
if apiserverServiceType != v1.ServiceTypeNodePort { if i.options.apiServerServiceType != v1.ServiceTypeNodePort {
return fmt.Errorf("%s should be passed only with '%s=NodePort'", apiserverAdvertiseAddressFlag, apiserverServiceTypeFlag) return fmt.Errorf("%s should be passed only with '%s=NodePort'", apiserverAdvertiseAddressFlag, apiserverServiceTypeFlag)
} }
} }
apiserverArgOverrides, err := marshallOverrides(cmdutil.GetFlagString(cmd, "apiserver-arg-overrides"))
i.options.apiServerOverrides, err = marshallOverrides(i.options.apiServerOverridesString)
if err != nil { if err != nil {
return fmt.Errorf("Error marshalling --apiserver-arg-overrides: %v", err) return fmt.Errorf("Error marshalling --apiserver-arg-overrides: %v", err)
} }
cmArgOverrides, err := marshallOverrides(cmdutil.GetFlagString(cmd, "controllermanager-arg-overrides")) i.options.controllerManagerOverrides, err = marshallOverrides(i.options.controllerManagerOverridesString)
if err != nil { if err != nil {
return fmt.Errorf("Error marshalling --controllermanager-arg-overrides: %v", err) return fmt.Errorf("Error marshalling --controllermanager-arg-overrides: %v", err)
} }
hostFactory := config.HostFactory(initFlags.Host, initFlags.Kubeconfig) return nil
}
// Run initializes a federation control plane.
// See the design doc in https://github.com/kubernetes/kubernetes/pull/34484
// for details.
func (i *initFederation) Run(cmdOut io.Writer, config util.AdminConfig) error {
hostFactory := config.HostFactory(i.commonOptions.Host, i.commonOptions.Kubeconfig)
hostClientset, err := hostFactory.ClientSet() hostClientset, err := hostFactory.ClientSet()
if err != nil { if err != nil {
return err return err
} }
serverName := fmt.Sprintf("%s-apiserver", initFlags.Name) serverName := fmt.Sprintf("%s-apiserver", i.commonOptions.Name)
serverCredName := fmt.Sprintf("%s-credentials", serverName) serverCredName := fmt.Sprintf("%s-credentials", serverName)
cmName := fmt.Sprintf("%s-controller-manager", initFlags.Name) cmName := fmt.Sprintf("%s-controller-manager", i.commonOptions.Name)
cmKubeconfigName := fmt.Sprintf("%s-kubeconfig", cmName) cmKubeconfigName := fmt.Sprintf("%s-kubeconfig", cmName)
// 1. Create a namespace for federation system components // 1. Create a namespace for federation system components
_, err = createNamespace(hostClientset, initFlags.FederationSystemNamespace, dryRun) _, err = createNamespace(hostClientset, i.commonOptions.FederationSystemNamespace, i.options.dryRun)
if err != nil { if err != nil {
return err return err
} }
// 2. Expose a network endpoint for the federation API server // 2. Expose a network endpoint for the federation API server
svc, ips, hostnames, err := createService(hostClientset, initFlags.FederationSystemNamespace, serverName, apiserverAdvertiseAddress, apiserverServiceType, dryRun) svc, ips, hostnames, err := createService(hostClientset, i.commonOptions.FederationSystemNamespace, serverName, i.options.apiServerAdvertiseAddress, i.options.apiServerServiceType, i.options.dryRun)
if err != nil { if err != nil {
return err return err
} }
// 3. Generate TLS certificates and credentials // 3. Generate TLS certificates and credentials
entKeyPairs, err := genCerts(initFlags.FederationSystemNamespace, initFlags.Name, svc.Name, HostClusterLocalDNSZoneName, ips, hostnames) entKeyPairs, err := genCerts(i.commonOptions.FederationSystemNamespace, i.commonOptions.Name, svc.Name, HostClusterLocalDNSZoneName, ips, hostnames)
if err != nil { if err != nil {
return err return err
} }
_, err = createAPIServerCredentialsSecret(hostClientset, initFlags.FederationSystemNamespace, serverCredName, entKeyPairs, dryRun) _, err = createAPIServerCredentialsSecret(hostClientset, i.commonOptions.FederationSystemNamespace, serverCredName, entKeyPairs, i.options.dryRun)
if err != nil { if err != nil {
return err return err
} }
// 4. Create a kubeconfig secret // 4. Create a kubeconfig secret
_, err = createControllerManagerKubeconfigSecret(hostClientset, initFlags.FederationSystemNamespace, initFlags.Name, svc.Name, cmKubeconfigName, entKeyPairs, dryRun) _, err = createControllerManagerKubeconfigSecret(hostClientset, i.commonOptions.FederationSystemNamespace, i.commonOptions.Name, svc.Name, cmKubeconfigName, entKeyPairs, i.options.dryRun)
if err != nil { if err != nil {
return err return err
} }
@ -240,8 +269,8 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
// API server's state. This is where federation API server's etcd // API server's state. This is where federation API server's etcd
// stores its data. // stores its data.
var pvc *api.PersistentVolumeClaim var pvc *api.PersistentVolumeClaim
if etcdPersistence { if i.options.etcdPersistentStorage {
pvc, err = createPVC(hostClientset, initFlags.FederationSystemNamespace, svc.Name, etcdPVCapacity, dryRun) pvc, err = createPVC(hostClientset, i.commonOptions.FederationSystemNamespace, svc.Name, i.options.etcdPVCapacity, i.options.dryRun)
if err != nil { if err != nil {
return err return err
} }
@ -250,13 +279,13 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
// Since only one IP address can be specified as advertise address, // Since only one IP address can be specified as advertise address,
// we arbitrarily pick the first available IP address // we arbitrarily pick the first available IP address
// Pick user provided apiserverAdvertiseAddress over other available IP addresses. // Pick user provided apiserverAdvertiseAddress over other available IP addresses.
advertiseAddress := apiserverAdvertiseAddress advertiseAddress := i.options.apiServerAdvertiseAddress
if advertiseAddress == "" && len(ips) > 0 { if advertiseAddress == "" && len(ips) > 0 {
advertiseAddress = ips[0] advertiseAddress = ips[0]
} }
// 6. Create federation API server // 6. Create federation API server
_, err = createAPIServer(hostClientset, initFlags.FederationSystemNamespace, serverName, image, serverCredName, advertiseAddress, storageBackend, apiserverArgOverrides, pvc, dryRun) _, err = createAPIServer(hostClientset, i.commonOptions.FederationSystemNamespace, serverName, i.options.image, serverCredName, advertiseAddress, i.options.storageBackend, i.options.apiServerOverrides, pvc, i.options.dryRun)
if err != nil { if err != nil {
return err return err
} }
@ -264,20 +293,20 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
// 7. Create federation controller manager // 7. Create federation controller manager
// 7a. Create a service account in the host cluster for federation // 7a. Create a service account in the host cluster for federation
// controller manager. // controller manager.
sa, err := createControllerManagerSA(hostClientset, initFlags.FederationSystemNamespace, dryRun) sa, err := createControllerManagerSA(hostClientset, i.commonOptions.FederationSystemNamespace, i.options.dryRun)
if err != nil { if err != nil {
return err return err
} }
// 7b. Create RBAC role and role binding for federation controller // 7b. Create RBAC role and role binding for federation controller
// manager service account. // manager service account.
_, _, err = createRoleBindings(hostClientset, initFlags.FederationSystemNamespace, sa.Name, dryRun) _, _, err = createRoleBindings(hostClientset, i.commonOptions.FederationSystemNamespace, sa.Name, i.options.dryRun)
if err != nil { if err != nil {
return err return err
} }
// 7c. Create federation controller manager deployment. // 7c. Create federation controller manager deployment.
_, err = createControllerManager(hostClientset, initFlags.FederationSystemNamespace, initFlags.Name, svc.Name, cmName, image, cmKubeconfigName, dnsZoneName, dnsProvider, sa.Name, cmArgOverrides, dryRun) _, err = createControllerManager(hostClientset, i.commonOptions.FederationSystemNamespace, i.commonOptions.Name, svc.Name, cmName, i.options.image, cmKubeconfigName, i.options.dnsZoneName, i.options.dnsProvider, sa.Name, i.options.controllerManagerOverrides, i.options.dryRun)
if err != nil { if err != nil {
return err return err
} }
@ -291,24 +320,24 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
endpoint = hostnames[0] endpoint = hostnames[0]
} }
// If the service is nodeport, need to append the port to endpoint as it is non-standard port // If the service is nodeport, need to append the port to endpoint as it is non-standard port
if apiserverServiceType == v1.ServiceTypeNodePort { if i.options.apiServerServiceType == v1.ServiceTypeNodePort {
endpoint = endpoint + ":" + strconv.Itoa(int(svc.Spec.Ports[0].NodePort)) endpoint = endpoint + ":" + strconv.Itoa(int(svc.Spec.Ports[0].NodePort))
} }
// 8. Write the federation API server endpoint info, credentials // 8. Write the federation API server endpoint info, credentials
// and context to kubeconfig // and context to kubeconfig
err = updateKubeconfig(config, initFlags.Name, endpoint, entKeyPairs, dryRun) err = updateKubeconfig(config, i.commonOptions.Name, endpoint, entKeyPairs, i.options.dryRun)
if err != nil { if err != nil {
return err return err
} }
if !dryRun { if !i.options.dryRun {
fedPods := []string{serverName, cmName} fedPods := []string{serverName, cmName}
err = waitForPods(hostClientset, fedPods, initFlags.FederationSystemNamespace) err = waitForPods(hostClientset, fedPods, i.commonOptions.FederationSystemNamespace)
if err != nil { if err != nil {
return err return err
} }
err = waitSrvHealthy(config, initFlags.Name, initFlags.Kubeconfig) err = waitSrvHealthy(config, i.commonOptions.Name, i.commonOptions.Kubeconfig)
if err != nil { if err != nil {
return err return err
} }

View File

@ -31,6 +31,7 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag"
) )
const ( const (
@ -57,17 +58,35 @@ var (
kubefed join foo --host-cluster-context=bar`) kubefed join foo --host-cluster-context=bar`)
) )
type joinFederation struct {
commonOptions util.SubcommandOptions
options joinFederationOptions
}
type joinFederationOptions struct {
clusterContext string
secretName string
dryRun bool
}
func (o *joinFederationOptions) Bind(flags *pflag.FlagSet) {
flags.StringVar(&o.clusterContext, "cluster-context", "", "Name of the cluster's context in the local kubeconfig. Defaults to cluster name if unspecified.")
flags.StringVar(&o.secretName, "secret-name", "", "Name of the secret where the cluster's credentials will be stored in the host cluster. This name should be a valid RFC 1035 label. Defaults to cluster name if unspecified.")
}
// NewCmdJoin defines the `join` command that joins a cluster to a // NewCmdJoin defines the `join` command that joins a cluster to a
// federation. // federation.
func NewCmdJoin(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig) *cobra.Command { func NewCmdJoin(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig) *cobra.Command {
opts := &joinFederation{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "join CLUSTER_NAME --host-cluster-context=HOST_CONTEXT", Use: "join CLUSTER_NAME --host-cluster-context=HOST_CONTEXT",
Short: "Join a cluster to a federation", Short: "Join a cluster to a federation",
Long: join_long, Long: join_long,
Example: join_example, Example: join_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := joinFederation(f, cmdOut, config, cmd, args) cmdutil.CheckErr(opts.Complete(cmd, args))
cmdutil.CheckErr(err) cmdutil.CheckErr(opts.Run(f, cmdOut, config, cmd))
}, },
} }
@ -75,45 +94,51 @@ func NewCmdJoin(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig) *c
cmdutil.AddValidateFlags(cmd) cmdutil.AddValidateFlags(cmd)
cmdutil.AddPrinterFlags(cmd) cmdutil.AddPrinterFlags(cmd)
cmdutil.AddGeneratorFlags(cmd, cmdutil.ClusterV1Beta1GeneratorName) cmdutil.AddGeneratorFlags(cmd, cmdutil.ClusterV1Beta1GeneratorName)
util.AddSubcommandFlags(cmd)
cmd.Flags().String("cluster-context", "", "Name of the cluster's context in the local kubeconfig. Defaults to cluster name if unspecified.") flags := cmd.Flags()
cmd.Flags().String("secret-name", "", "Name of the secret where the cluster's credentials will be stored in the host cluster. This name should be a valid RFC 1035 label. Defaults to cluster name if unspecified.") opts.commonOptions.Bind(flags)
opts.options.Bind(flags)
return cmd return cmd
} }
// joinFederation is the implementation of the `join federation` command. // Complete ensures that options are valid and marshals them if necessary.
func joinFederation(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Command, args []string) error { func (j *joinFederation) Complete(cmd *cobra.Command, args []string) error {
joinFlags, err := util.GetSubcommandFlags(cmd, args) err := j.commonOptions.SetName(cmd, args)
if err != nil { if err != nil {
return err return err
} }
clusterContext := cmdutil.GetFlagString(cmd, "cluster-context")
secretName := cmdutil.GetFlagString(cmd, "secret-name")
dryRun := cmdutil.GetDryRunFlag(cmd)
if clusterContext == "" { j.options.dryRun = cmdutil.GetDryRunFlag(cmd)
clusterContext = joinFlags.Name
if j.options.clusterContext == "" {
j.options.clusterContext = j.commonOptions.Name
} }
if secretName == "" { if j.options.secretName == "" {
secretName = joinFlags.Name j.options.secretName = j.commonOptions.Name
} }
glog.V(2).Infof("Args and flags: name %s, host: %s, host-system-namespace: %s, kubeconfig: %s, cluster-context: %s, secret-name: %s, dry-run: %s", joinFlags.Name, joinFlags.Host, joinFlags.FederationSystemNamespace, joinFlags.Kubeconfig, clusterContext, secretName, dryRun) glog.V(2).Infof("Args and flags: name %s, host: %s, host-system-namespace: %s, kubeconfig: %s, cluster-context: %s, secret-name: %s, dry-run: %s", j.commonOptions.Name, j.commonOptions.Host, j.commonOptions.FederationSystemNamespace, j.commonOptions.Kubeconfig, j.options.clusterContext, j.options.secretName, j.options.dryRun)
return nil
}
// Run is the implementation of the `join federation` command.
func (j *joinFederation) Run(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Command) error {
po := config.PathOptions() po := config.PathOptions()
po.LoadingRules.ExplicitPath = joinFlags.Kubeconfig po.LoadingRules.ExplicitPath = j.commonOptions.Kubeconfig
clientConfig, err := po.GetStartingConfig() clientConfig, err := po.GetStartingConfig()
if err != nil { if err != nil {
return err return err
} }
generator, err := clusterGenerator(clientConfig, joinFlags.Name, clusterContext, secretName) generator, err := clusterGenerator(clientConfig, j.commonOptions.Name, j.options.clusterContext, j.options.secretName)
if err != nil { if err != nil {
glog.V(2).Infof("Failed creating cluster generator: %v", err) glog.V(2).Infof("Failed creating cluster generator: %v", err)
return err return err
} }
glog.V(2).Infof("Created cluster generator: %#v", generator) glog.V(2).Infof("Created cluster generator: %#v", generator)
hostFactory := config.HostFactory(joinFlags.Host, joinFlags.Kubeconfig) hostFactory := config.HostFactory(j.commonOptions.Host, j.commonOptions.Kubeconfig)
// We are not using the `kubectl create secret` machinery through // We are not using the `kubectl create secret` machinery through
// `RunCreateSubcommand` as we do to the cluster resource below // `RunCreateSubcommand` as we do to the cluster resource below
@ -130,7 +155,7 @@ func joinFederation(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig
// don't have to print the created secret in the default case. // don't have to print the created secret in the default case.
// Having said that, secret generation machinery could be altered to // Having said that, secret generation machinery could be altered to
// suit our needs, but it is far less invasive and readable this way. // suit our needs, but it is far less invasive and readable this way.
_, err = createSecret(hostFactory, clientConfig, joinFlags.FederationSystemNamespace, clusterContext, secretName, dryRun) _, err = createSecret(hostFactory, clientConfig, j.commonOptions.FederationSystemNamespace, j.options.clusterContext, j.options.secretName, j.options.dryRun)
if err != nil { if err != nil {
glog.V(2).Infof("Failed creating the cluster credentials secret: %v", err) glog.V(2).Infof("Failed creating the cluster credentials secret: %v", err)
return err return err
@ -138,9 +163,9 @@ func joinFederation(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig
glog.V(2).Infof("Cluster credentials secret created") glog.V(2).Infof("Cluster credentials secret created")
return kubectlcmd.RunCreateSubcommand(f, cmd, cmdOut, &kubectlcmd.CreateSubcommandOptions{ return kubectlcmd.RunCreateSubcommand(f, cmd, cmdOut, &kubectlcmd.CreateSubcommandOptions{
Name: joinFlags.Name, Name: j.commonOptions.Name,
StructuredGenerator: generator, StructuredGenerator: generator,
DryRun: dryRun, DryRun: j.options.dryRun,
OutputFormat: cmdutil.GetFlagString(cmd, "output"), OutputFormat: cmdutil.GetFlagString(cmd, "output"),
}) })
} }

View File

@ -47,50 +47,53 @@ var (
kubectl unjoin foo --host-cluster-context=bar`) kubectl unjoin foo --host-cluster-context=bar`)
) )
type unjoinFederation struct {
commonOptions util.SubcommandOptions
}
// NewCmdUnjoin defines the `unjoin` command that removes a cluster // NewCmdUnjoin defines the `unjoin` command that removes a cluster
// from a federation. // from a federation.
func NewCmdUnjoin(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config util.AdminConfig) *cobra.Command { func NewCmdUnjoin(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config util.AdminConfig) *cobra.Command {
opts := &unjoinFederation{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "unjoin CLUSTER_NAME --host-cluster-context=HOST_CONTEXT", Use: "unjoin CLUSTER_NAME --host-cluster-context=HOST_CONTEXT",
Short: "Unjoins a cluster from a federation", Short: "Unjoins a cluster from a federation",
Long: unjoin_long, Long: unjoin_long,
Example: unjoin_example, Example: unjoin_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := unjoinFederation(f, cmdOut, cmdErr, config, cmd, args) cmdutil.CheckErr(opts.commonOptions.SetName(cmd, args))
cmdutil.CheckErr(err) cmdutil.CheckErr(opts.Run(f, cmdOut, cmdErr, config))
}, },
} }
util.AddSubcommandFlags(cmd) flags := cmd.Flags()
opts.commonOptions.Bind(flags)
return cmd return cmd
} }
// unjoinFederation is the implementation of the `unjoin` command. // unjoinFederation is the implementation of the `unjoin` command.
func unjoinFederation(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config util.AdminConfig, cmd *cobra.Command, args []string) error { func (u *unjoinFederation) Run(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config util.AdminConfig) error {
unjoinFlags, err := util.GetSubcommandFlags(cmd, args) cluster, err := popCluster(f, u.commonOptions.Name)
if err != nil {
return err
}
cluster, err := popCluster(f, unjoinFlags.Name)
if err != nil { if err != nil {
return err return err
} }
if cluster == nil { if cluster == nil {
fmt.Fprintf(cmdErr, "WARNING: cluster %q not found in federation, so its credentials' secret couldn't be deleted", unjoinFlags.Name) fmt.Fprintf(cmdErr, "WARNING: cluster %q not found in federation, so its credentials' secret couldn't be deleted", u.commonOptions.Name)
return nil return nil
} }
// We want a separate client factory to communicate with the // We want a separate client factory to communicate with the
// federation host cluster. See join_federation.go for details. // federation host cluster. See join_federation.go for details.
hostFactory := config.HostFactory(unjoinFlags.Host, unjoinFlags.Kubeconfig) hostFactory := config.HostFactory(u.commonOptions.Host, u.commonOptions.Kubeconfig)
err = deleteSecret(hostFactory, cluster.Spec.SecretRef.Name, unjoinFlags.FederationSystemNamespace) err = deleteSecret(hostFactory, cluster.Spec.SecretRef.Name, u.commonOptions.FederationSystemNamespace)
if isNotFound(err) { if isNotFound(err) {
fmt.Fprintf(cmdErr, "WARNING: secret %q not found in the host cluster, so it couldn't be deleted", cluster.Spec.SecretRef.Name) fmt.Fprintf(cmdErr, "WARNING: secret %q not found in the host cluster, so it couldn't be deleted", cluster.Spec.SecretRef.Name)
} else if err != nil { } else if err != nil {
return err return err
} }
_, err = fmt.Fprintf(cmdOut, "Successfully removed cluster %q from federation\n", unjoinFlags.Name) _, err = fmt.Fprintf(cmdOut, "Successfully removed cluster %q from federation\n", u.commonOptions.Name)
return err return err
} }

View File

@ -18,6 +18,7 @@ go_library(
"//pkg/kubectl/cmd:go_default_library", "//pkg/kubectl/cmd:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",
"//vendor:github.com/spf13/cobra", "//vendor:github.com/spf13/cobra",
"//vendor:github.com/spf13/pflag",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/client-go/tools/clientcmd", "//vendor:k8s.io/client-go/tools/clientcmd",
"//vendor:k8s.io/client-go/tools/clientcmd/api", "//vendor:k8s.io/client-go/tools/clientcmd/api",

View File

@ -17,6 +17,8 @@ limitations under the License.
package util package util
import ( import (
"github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api" clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
@ -95,36 +97,28 @@ func (a *adminConfig) getClientConfig(context, kubeconfigPath string) clientcmd.
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, overrides) return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, overrides)
} }
// SubcommandFlags holds the flags required by the subcommands of // SubcommandOptions holds the configuration required by the subcommands of
// `kubefed`. // `kubefed`.
type SubcommandFlags struct { type SubcommandOptions struct {
Name string Name string
Host string Host string
FederationSystemNamespace string FederationSystemNamespace string
Kubeconfig string Kubeconfig string
} }
// AddSubcommandFlags adds the definition for `kubefed` subcommand func (o *SubcommandOptions) Bind(flags *pflag.FlagSet) {
// flags. flags.StringVar(&o.Kubeconfig, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
func AddSubcommandFlags(cmd *cobra.Command) { flags.StringVar(&o.Host, "host-cluster-context", "", "Host cluster context")
cmd.Flags().String("kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") flags.StringVar(&o.FederationSystemNamespace, "federation-system-namespace", DefaultFederationSystemNamespace, "Namespace in the host cluster where the federation system components are installed")
cmd.Flags().String("host-cluster-context", "", "Host cluster context")
cmd.Flags().String("federation-system-namespace", DefaultFederationSystemNamespace, "Namespace in the host cluster where the federation system components are installed")
} }
// GetSubcommandFlags retrieves the command line flag values for the func (o *SubcommandOptions) SetName(cmd *cobra.Command, args []string) error {
// `kubefed` subcommands.
func GetSubcommandFlags(cmd *cobra.Command, args []string) (*SubcommandFlags, error) {
name, err := kubectlcmd.NameFromCommandArgs(cmd, args) name, err := kubectlcmd.NameFromCommandArgs(cmd, args)
if err != nil { if err != nil {
return nil, err return err
} }
return &SubcommandFlags{ o.Name = name
Name: name, return nil
Host: cmdutil.GetFlagString(cmd, "host-cluster-context"),
FederationSystemNamespace: cmdutil.GetFlagString(cmd, "federation-system-namespace"),
Kubeconfig: cmdutil.GetFlagString(cmd, "kubeconfig"),
}, nil
} }
func CreateKubeconfigSecret(clientset *client.Clientset, kubeconfig *clientcmdapi.Config, namespace, name string, dryRun bool) (*api.Secret, error) { func CreateKubeconfigSecret(clientset *client.Clientset, kubeconfig *clientcmdapi.Config, namespace, name string, dryRun bool) (*api.Secret, error) {