kubefed: Bind flag values automatically

pull/6/head
Maru Newby 2017-02-09 13:26:06 -08:00
parent 6ea92b47eb
commit b4aa26704d
7 changed files with 166 additions and 112 deletions

View File

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

View File

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

View File

@ -34,6 +34,7 @@ import (
"fmt"
"io"
"net"
"sort"
"strconv"
"strings"
"time"
@ -57,7 +58,7 @@ import (
"k8s.io/kubernetes/pkg/version"
"github.com/spf13/cobra"
"sort"
"github.com/spf13/pflag"
)
const (
@ -119,34 +120,64 @@ var (
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
// control plane inside a set of host clusters.
func NewCmdInit(cmdOut io.Writer, config util.AdminConfig) *cobra.Command {
opts := &initFederation{}
cmd := &cobra.Command{
Use: "init FEDERATION_NAME --host-cluster-context=HOST_CONTEXT",
Short: "init initializes a federation control plane",
Long: init_long,
Example: init_example,
Run: func(cmd *cobra.Command, args []string) {
err := initFederation(cmdOut, config, cmd, args)
cmdutil.CheckErr(err)
cmdutil.CheckErr(opts.Complete(cmd, args))
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
}
@ -157,81 +188,79 @@ type entityKeyPairs struct {
admin *triple.KeyPair
}
// initFederation initializes a federation control plane.
// See the design doc in https://github.com/kubernetes/kubernetes/pull/34484
// for details.
func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Command, args []string) error {
initFlags, err := util.GetSubcommandFlags(cmd, args)
// Complete ensures that options are valid and marshals them if necessary.
func (i *initFederation) Complete(cmd *cobra.Command, args []string) error {
err := i.commonOptions.SetName(cmd, args)
if err != nil {
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 {
return fmt.Errorf("invalid %s: %s, should be either %s or %s", apiserverServiceTypeFlag, apiserverServiceType, v1.ServiceTypeLoadBalancer, v1.ServiceTypeNodePort)
i.options.apiServerServiceType = v1.ServiceType(i.options.apiServerServiceTypeString)
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 != "" {
ip := net.ParseIP(apiserverAdvertiseAddress)
if i.options.apiServerAdvertiseAddress != "" {
ip := net.ParseIP(i.options.apiServerAdvertiseAddress)
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)
}
}
apiserverArgOverrides, err := marshallOverrides(cmdutil.GetFlagString(cmd, "apiserver-arg-overrides"))
i.options.apiServerOverrides, err = marshallOverrides(i.options.apiServerOverridesString)
if err != nil {
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 {
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()
if err != nil {
return err
}
serverName := fmt.Sprintf("%s-apiserver", initFlags.Name)
serverName := fmt.Sprintf("%s-apiserver", i.commonOptions.Name)
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)
// 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 {
return err
}
// 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 {
return err
}
// 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 {
return err
}
_, err = createAPIServerCredentialsSecret(hostClientset, initFlags.FederationSystemNamespace, serverCredName, entKeyPairs, dryRun)
_, err = createAPIServerCredentialsSecret(hostClientset, i.commonOptions.FederationSystemNamespace, serverCredName, entKeyPairs, i.options.dryRun)
if err != nil {
return err
}
// 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 {
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
// stores its data.
var pvc *api.PersistentVolumeClaim
if etcdPersistence {
pvc, err = createPVC(hostClientset, initFlags.FederationSystemNamespace, svc.Name, etcdPVCapacity, dryRun)
if i.options.etcdPersistentStorage {
pvc, err = createPVC(hostClientset, i.commonOptions.FederationSystemNamespace, svc.Name, i.options.etcdPVCapacity, i.options.dryRun)
if err != nil {
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,
// we arbitrarily pick the first available IP address
// Pick user provided apiserverAdvertiseAddress over other available IP addresses.
advertiseAddress := apiserverAdvertiseAddress
advertiseAddress := i.options.apiServerAdvertiseAddress
if advertiseAddress == "" && len(ips) > 0 {
advertiseAddress = ips[0]
}
// 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 {
return err
}
@ -264,20 +293,20 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
// 7. Create federation controller manager
// 7a. Create a service account in the host cluster for federation
// controller manager.
sa, err := createControllerManagerSA(hostClientset, initFlags.FederationSystemNamespace, dryRun)
sa, err := createControllerManagerSA(hostClientset, i.commonOptions.FederationSystemNamespace, i.options.dryRun)
if err != nil {
return err
}
// 7b. Create RBAC role and role binding for federation controller
// 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 {
return err
}
// 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 {
return err
}
@ -291,24 +320,24 @@ func initFederation(cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Comman
endpoint = hostnames[0]
}
// 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))
}
// 8. Write the federation API server endpoint info, credentials
// 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 {
return err
}
if !dryRun {
if !i.options.dryRun {
fedPods := []string{serverName, cmName}
err = waitForPods(hostClientset, fedPods, initFlags.FederationSystemNamespace)
err = waitForPods(hostClientset, fedPods, i.commonOptions.FederationSystemNamespace)
if err != nil {
return err
}
err = waitSrvHealthy(config, initFlags.Name, initFlags.Kubeconfig)
err = waitSrvHealthy(config, i.commonOptions.Name, i.commonOptions.Kubeconfig)
if err != nil {
return err
}

View File

@ -31,6 +31,7 @@ import (
"github.com/golang/glog"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
const (
@ -57,17 +58,35 @@ var (
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
// federation.
func NewCmdJoin(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig) *cobra.Command {
opts := &joinFederation{}
cmd := &cobra.Command{
Use: "join CLUSTER_NAME --host-cluster-context=HOST_CONTEXT",
Short: "Join a cluster to a federation",
Long: join_long,
Example: join_example,
Run: func(cmd *cobra.Command, args []string) {
err := joinFederation(f, cmdOut, config, cmd, args)
cmdutil.CheckErr(err)
cmdutil.CheckErr(opts.Complete(cmd, args))
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.AddPrinterFlags(cmd)
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.")
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.")
flags := cmd.Flags()
opts.commonOptions.Bind(flags)
opts.options.Bind(flags)
return cmd
}
// joinFederation is the implementation of the `join federation` command.
func joinFederation(f cmdutil.Factory, cmdOut io.Writer, config util.AdminConfig, cmd *cobra.Command, args []string) error {
joinFlags, err := util.GetSubcommandFlags(cmd, args)
// Complete ensures that options are valid and marshals them if necessary.
func (j *joinFederation) Complete(cmd *cobra.Command, args []string) error {
err := j.commonOptions.SetName(cmd, args)
if err != nil {
return err
}
clusterContext := cmdutil.GetFlagString(cmd, "cluster-context")
secretName := cmdutil.GetFlagString(cmd, "secret-name")
dryRun := cmdutil.GetDryRunFlag(cmd)
if clusterContext == "" {
clusterContext = joinFlags.Name
j.options.dryRun = cmdutil.GetDryRunFlag(cmd)
if j.options.clusterContext == "" {
j.options.clusterContext = j.commonOptions.Name
}
if secretName == "" {
secretName = joinFlags.Name
if j.options.secretName == "" {
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.LoadingRules.ExplicitPath = joinFlags.Kubeconfig
po.LoadingRules.ExplicitPath = j.commonOptions.Kubeconfig
clientConfig, err := po.GetStartingConfig()
if err != nil {
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 {
glog.V(2).Infof("Failed creating cluster generator: %v", err)
return err
}
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
// `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.
// Having said that, secret generation machinery could be altered to
// 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 {
glog.V(2).Infof("Failed creating the cluster credentials secret: %v", 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")
return kubectlcmd.RunCreateSubcommand(f, cmd, cmdOut, &kubectlcmd.CreateSubcommandOptions{
Name: joinFlags.Name,
Name: j.commonOptions.Name,
StructuredGenerator: generator,
DryRun: dryRun,
DryRun: j.options.dryRun,
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}

View File

@ -47,50 +47,53 @@ var (
kubectl unjoin foo --host-cluster-context=bar`)
)
type unjoinFederation struct {
commonOptions util.SubcommandOptions
}
// NewCmdUnjoin defines the `unjoin` command that removes a cluster
// from a federation.
func NewCmdUnjoin(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config util.AdminConfig) *cobra.Command {
opts := &unjoinFederation{}
cmd := &cobra.Command{
Use: "unjoin CLUSTER_NAME --host-cluster-context=HOST_CONTEXT",
Short: "Unjoins a cluster from a federation",
Long: unjoin_long,
Example: unjoin_example,
Run: func(cmd *cobra.Command, args []string) {
err := unjoinFederation(f, cmdOut, cmdErr, config, cmd, args)
cmdutil.CheckErr(err)
cmdutil.CheckErr(opts.commonOptions.SetName(cmd, args))
cmdutil.CheckErr(opts.Run(f, cmdOut, cmdErr, config))
},
}
util.AddSubcommandFlags(cmd)
flags := cmd.Flags()
opts.commonOptions.Bind(flags)
return cmd
}
// 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 {
unjoinFlags, err := util.GetSubcommandFlags(cmd, args)
if err != nil {
return err
}
cluster, err := popCluster(f, unjoinFlags.Name)
func (u *unjoinFederation) Run(f cmdutil.Factory, cmdOut, cmdErr io.Writer, config util.AdminConfig) error {
cluster, err := popCluster(f, u.commonOptions.Name)
if err != nil {
return err
}
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
}
// We want a separate client factory to communicate with the
// federation host cluster. See join_federation.go for details.
hostFactory := config.HostFactory(unjoinFlags.Host, unjoinFlags.Kubeconfig)
err = deleteSecret(hostFactory, cluster.Spec.SecretRef.Name, unjoinFlags.FederationSystemNamespace)
hostFactory := config.HostFactory(u.commonOptions.Host, u.commonOptions.Kubeconfig)
err = deleteSecret(hostFactory, cluster.Spec.SecretRef.Name, u.commonOptions.FederationSystemNamespace)
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)
} else if err != nil {
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
}

View File

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

View File

@ -17,6 +17,8 @@ limitations under the License.
package util
import (
"github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/clientcmd"
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)
}
// SubcommandFlags holds the flags required by the subcommands of
// SubcommandOptions holds the configuration required by the subcommands of
// `kubefed`.
type SubcommandFlags struct {
type SubcommandOptions struct {
Name string
Host string
FederationSystemNamespace string
Kubeconfig string
}
// AddSubcommandFlags adds the definition for `kubefed` subcommand
// flags.
func AddSubcommandFlags(cmd *cobra.Command) {
cmd.Flags().String("kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
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")
func (o *SubcommandOptions) Bind(flags *pflag.FlagSet) {
flags.StringVar(&o.Kubeconfig, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
flags.StringVar(&o.Host, "host-cluster-context", "", "Host cluster context")
flags.StringVar(&o.FederationSystemNamespace, "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
// `kubefed` subcommands.
func GetSubcommandFlags(cmd *cobra.Command, args []string) (*SubcommandFlags, error) {
func (o *SubcommandOptions) SetName(cmd *cobra.Command, args []string) error {
name, err := kubectlcmd.NameFromCommandArgs(cmd, args)
if err != nil {
return nil, err
return err
}
return &SubcommandFlags{
Name: name,
Host: cmdutil.GetFlagString(cmd, "host-cluster-context"),
FederationSystemNamespace: cmdutil.GetFlagString(cmd, "federation-system-namespace"),
Kubeconfig: cmdutil.GetFlagString(cmd, "kubeconfig"),
}, nil
o.Name = name
return nil
}
func CreateKubeconfigSecret(clientset *client.Clientset, kubeconfig *clientcmdapi.Config, namespace, name string, dryRun bool) (*api.Secret, error) {