diff --git a/clusterctl/clusterdeployer/clientfactory.go b/clusterctl/clusterdeployer/clientfactory.go index 3b2942d4a0f5..7596715bcb86 100644 --- a/clusterctl/clusterdeployer/clientfactory.go +++ b/clusterctl/clusterdeployer/clientfactory.go @@ -33,5 +33,5 @@ func (f *clientFactory) NewClusterClientFromKubeconfig(kubeconfig string) (Clust } func (f *clientFactory) NewCoreClientsetFromKubeconfigFile(kubeconfigPath string) (*kubernetes.Clientset, error) { - return clientcmd.NewCoreClientSetForDefaultSearchPath(kubeconfigPath) + return clientcmd.NewCoreClientSetForDefaultSearchPath(kubeconfigPath, clientcmd.NewConfigOverrides()) } diff --git a/clusterctl/clusterdeployer/clusterclient.go b/clusterctl/clusterdeployer/clusterclient.go index ebb0547335f5..32437c4750d1 100644 --- a/clusterctl/clusterdeployer/clusterclient.go +++ b/clusterctl/clusterdeployer/clusterclient.go @@ -28,6 +28,7 @@ import ( "github.com/golang/glog" apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + tcmd "k8s.io/client-go/tools/clientcmd" clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" "sigs.k8s.io/cluster-api/pkg/client/clientset_generated/clientset" "sigs.k8s.io/cluster-api/pkg/clientcmd" @@ -44,9 +45,10 @@ const ( ) type clusterClient struct { - clientSet clientset.Interface - kubeconfigFile string - closeFn func() error + clientSet clientset.Interface + kubeconfigFile string + configOverrides tcmd.ConfigOverrides + closeFn func() error } // NewClusterClient creates and returns the address of a clusterClient, the kubeconfig argument is expected to be the string represenattion @@ -57,7 +59,7 @@ func NewClusterClient(kubeconfig string) (*clusterClient, error) { return nil, err } defer ifErrRemove(&err, f) - c, err := NewClusterClientFromFile(f) + c, err := NewClusterClientFromDefaultSearchPath(f, clientcmd.NewConfigOverrides()) if err != nil { return nil, err } @@ -69,17 +71,18 @@ func (c *clusterClient) removeKubeconfigFile() error { return os.Remove(c.kubeconfigFile) } -// NewClusterClientFromFile creates and returns the address of a clusterClient, the kubeconfigFile argument is expected to be the path to a +// NewClusterClientFromDefaultSearchPath creates and returns the address of a clusterClient, the kubeconfigFile argument is expected to be the path to a // valid kubeconfig file. -func NewClusterClientFromFile(kubeconfigFile string) (*clusterClient, error) { - c, err := clientcmd.NewClusterApiClientForDefaultSearchPath(kubeconfigFile) +func NewClusterClientFromDefaultSearchPath(kubeconfigFile string, overrides tcmd.ConfigOverrides) (*clusterClient, error) { + c, err := clientcmd.NewClusterApiClientForDefaultSearchPath(kubeconfigFile, overrides) if err != nil { return nil, err } return &clusterClient{ - kubeconfigFile: kubeconfigFile, - clientSet: c, + kubeconfigFile: kubeconfigFile, + clientSet: c, + configOverrides: overrides, }, nil } @@ -169,16 +172,30 @@ func (c *clusterClient) WaitForClusterV1alpha1Ready() error { } func (c *clusterClient) kubectlApply(manifest string) error { - r := strings.NewReader(manifest) - cmd := exec.Command("kubectl", "apply", "--kubeconfig", c.kubeconfigFile, "-f", "-") - cmd.Stdin = r - + cmd := exec.Command("kubectl", c.buildKubectlArgs("apply")...) + cmd.Stdin = strings.NewReader(manifest) out, err := cmd.CombinedOutput() - if err == nil { - return nil - } else { + if err != nil { return fmt.Errorf("couldn't kubectl apply: %v, output: %s", err, string(out)) } + return nil +} + +func (c *clusterClient) buildKubectlArgs(commandName string) []string { + args := []string{commandName} + if c.kubeconfigFile != "" { + args = append(args, "--kubeconfig", c.kubeconfigFile) + } + if c.configOverrides.Context.Cluster != "" { + args = append(args, "--cluster", c.configOverrides.Context.Cluster) + } + if c.configOverrides.Context.Namespace != "" { + args = append(args, "--namespace", c.configOverrides.Context.Namespace) + } + if c.configOverrides.Context.AuthInfo != "" { + args = append(args, "--user", c.configOverrides.Context.AuthInfo) + } + return append(args, "-f", "-") } func (c *clusterClient) waitForKubectlApply(manifest string) error { diff --git a/clusterctl/cmd/delete_cluster.go b/clusterctl/cmd/delete_cluster.go index 8830bf4e04e7..baa80d46180c 100644 --- a/clusterctl/cmd/delete_cluster.go +++ b/clusterctl/cmd/delete_cluster.go @@ -17,14 +17,22 @@ limitations under the License. package cmd import ( + "fmt" + + "k8s.io/api/core/v1" + tcmd "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/cluster-api/clusterctl/providercomponents" + "sigs.k8s.io/cluster-api/pkg/clientcmd" + "sigs.k8s.io/cluster-api/pkg/errors" + "github.com/golang/glog" "github.com/spf13/cobra" - "sigs.k8s.io/cluster-api/pkg/errors" ) type DeleteOptions struct { - ClusterName string - ProviderComponents string + KubeconfigPath string + ProviderComponents string + KubeconfigOverrides tcmd.ConfigOverrides } var do = &DeleteOptions{} @@ -34,9 +42,6 @@ var deleteClusterCmd = &cobra.Command{ Short: "Delete kubernetes cluster", Long: `Delete a kubernetes cluster with one command`, Run: func(cmd *cobra.Command, args []string) { - if do.ClusterName == "" { - exitWithHelp(cmd, "Please provide cluster name.") - } if err := RunDelete(); err != nil { glog.Exit(err) } @@ -44,10 +49,33 @@ var deleteClusterCmd = &cobra.Command{ } func init() { + deleteClusterCmd.Flags().StringVarP(&do.KubeconfigPath, "kubeconfig", "", "", "Path to the kubeconfig file to use for connecting to the cluster to be deleted, if empty, the default KUBECONFIG load path is used.") deleteClusterCmd.Flags().StringVarP(&do.ProviderComponents, "provider-components", "p", "", "A yaml file containing cluster api provider controllers and supporting objects, if empty the value is loaded from the cluster's configuration store.") + // BindContextFlags will bind the flags cluster, namespace, and user + tcmd.BindContextFlags(&do.KubeconfigOverrides.Context, deleteClusterCmd.Flags(), tcmd.RecommendedContextOverrideFlags("")) deleteCmd.AddCommand(deleteClusterCmd) } func RunDelete() error { + _, err := loadProviderComponents() + if err != nil { + return err + } return errors.NotImplementedError } + +func loadProviderComponents() (string, error) { + coreClients, err := clientcmd.NewCoreClientSetForDefaultSearchPath(do.KubeconfigPath, do.KubeconfigOverrides) + if err != nil { + return "", fmt.Errorf("error creating core clients: %v", err) + } + pcStore := providercomponents.Store{ + ExplicitPath: do.ProviderComponents, + ConfigMap: coreClients.CoreV1().ConfigMaps(v1.NamespaceDefault), + } + providerComponents, err := pcStore.Load() + if err != nil { + return "", fmt.Errorf("error when loading provider components: %v", err) + } + return providerComponents, nil +} diff --git a/clusterctl/main_integration_test.go b/clusterctl/main_integration_test.go index 96daffdca9ec..7f299d97a04d 100644 --- a/clusterctl/main_integration_test.go +++ b/clusterctl/main_integration_test.go @@ -63,7 +63,6 @@ func TestEmptyAndInvalidArgs(t *testing.T) { {"create cluster with no arguments with invalid flag", []string{"create", "cluster", "--invalid-flag"}, 1, "create-cluster-no-args-invalid-flag.golden"}, {"delete with no arguments", []string{"delete"}, 0, "delete-no-args.golden"}, {"delete with no arguments with invalid flag", []string{"delete", "--invalid-flag"}, 1, "delete-no-args-invalid-flag.golden"}, - {"delete cluster with no arguments", []string{"delete", "cluster"}, 1, "delete-cluster-no-args.golden"}, {"delete cluster with no arguments with invalid flag", []string{"delete", "cluster", "--invalid-flag"}, 1, "delete-cluster-no-args-invalid-flag.golden"}, {"validate with no arguments", []string{"validate"}, 0, "validate-no-args.golden"}, {"validate with no arguments with invalid flag", []string{"validate", "--invalid-flag"}, 1, "validate-no-args-invalid-flag.golden"}, diff --git a/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden b/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden index 523c330b86e9..4c9a68a69a83 100644 --- a/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden +++ b/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden @@ -3,8 +3,12 @@ Usage: clusterctl delete cluster [flags] Flags: + --cluster string The name of the kubeconfig cluster to use -h, --help help for cluster + --kubeconfig string Path to the kubeconfig file to use for connecting to the cluster to be deleted, if empty, the default KUBECONFIG load path is used. + -n, --namespace string If present, the namespace scope for this CLI request -p, --provider-components string A yaml file containing cluster api provider controllers and supporting objects, if empty the value is loaded from the cluster's configuration store. + --user string The name of the kubeconfig user to use Global Flags: --alsologtostderr log to standard error as well as files diff --git a/clusterctl/testdata/delete-cluster-no-args.golden b/clusterctl/testdata/delete-cluster-no-args.golden deleted file mode 100644 index dbdce3ba3168..000000000000 --- a/clusterctl/testdata/delete-cluster-no-args.golden +++ /dev/null @@ -1,19 +0,0 @@ -Please provide cluster name. -Delete a kubernetes cluster with one command - -Usage: - clusterctl delete cluster [flags] - -Flags: - -h, --help help for cluster - -p, --provider-components string A yaml file containing cluster api provider controllers and supporting objects, if empty the value is loaded from the cluster's configuration store. - -Global Flags: - --alsologtostderr log to standard error as well as files - --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s) - --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 - --logtostderr log to standard error instead of files (default true) - --stderrthreshold severity logs at or above this threshold go to stderr (default 2) - -v, --v Level log level for V logs - --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging diff --git a/gcp-deployer/deploy/deploy_helper.go b/gcp-deployer/deploy/deploy_helper.go index a8fad5dee3ab..8c32b3888c33 100644 --- a/gcp-deployer/deploy/deploy_helper.go +++ b/gcp-deployer/deploy/deploy_helper.go @@ -258,11 +258,7 @@ func (d *deployer) copyKubeConfig(cluster *clusterv1.Cluster, master *clusterv1. } func (d *deployer) initApiClient() error { - c, err := clientcmd.NewClusterApiClientForDefaultSearchPath(d.configPath) - if err != nil { - return err - } - kubernetesClientSet, err := clientcmd.NewCoreClientSetForDefaultSearchPath(d.configPath) + kubernetesClientSet, c, err := clientcmd.NewClientsForDefaultSearchpath(d.configPath, clientcmd.NewConfigOverrides()) if err != nil { return err } diff --git a/pkg/clientcmd/configutil.go b/pkg/clientcmd/configutil.go index 0895a905a07e..b23613d9fc09 100644 --- a/pkg/clientcmd/configutil.go +++ b/pkg/clientcmd/configutil.go @@ -27,10 +27,16 @@ import ( "sigs.k8s.io/cluster-api/pkg/client/clientset_generated/clientset" ) +// This is a convienience method to prevent the need of importing both this version of clientcmd and the client-go version +func NewConfigOverrides() clientcmd.ConfigOverrides { + return clientcmd.ConfigOverrides{} +} + // NewCoreClientSetForDefaultSearchPath creates a core kubernetes clientset. If the kubeconfigPath is specified then the configuration is loaded from that path. // Otherwise the default kubeconfig search path is used. -func NewCoreClientSetForDefaultSearchPath(kubeconfigPath string) (*kubernetes.Clientset, error) { - config, err := newRestConfigForDefaultSearchPath(kubeconfigPath) +// The overrides parameter is used to select a specific context of the config, for example, select the context with a given cluster name or namespace. +func NewCoreClientSetForDefaultSearchPath(kubeconfigPath string, overrides clientcmd.ConfigOverrides) (*kubernetes.Clientset, error) { + config, err := newRestConfigForDefaultSearchPath(kubeconfigPath, overrides) if err != nil { return nil, err } @@ -48,8 +54,9 @@ func NewCoreClientSetForKubeconfig(kubeconfig string) (*kubernetes.Clientset, er // NewClusterApiClientForDefaultSearchPath creates a Cluster API clientset. If the kubeconfigPath is specified then the configuration is loaded from that path. // Otherwise the default kubeconfig search path is used. -func NewClusterApiClientForDefaultSearchPath(kubeconfigPath string) (*clientset.Clientset, error) { - config, err := newRestConfigForDefaultSearchPath(kubeconfigPath) +// The overrides parameter is used to select a specific context of the config, for example, select the context with a given cluster name or namespace. +func NewClusterApiClientForDefaultSearchPath(kubeconfigPath string, overrides clientcmd.ConfigOverrides) (*clientset.Clientset, error) { + config, err := newRestConfigForDefaultSearchPath(kubeconfigPath, overrides) if err != nil { return nil, err } @@ -67,17 +74,9 @@ func NewClusterApiClientForKubeconfig(kubeconfig string) (*clientset.Clientset, // NewClientsForDefaultSearchpath creates both a core kubernetes clientset and a cluster-api clientset. If the kubeconfigPath // is specified then the configuration is loaded from that path. Otherwise the default kubeconfig search path is used. -func NewClientsForDefaultSearchpath(kubeconfigPath string) (*kubernetes.Clientset, *clientset.Clientset, error) { - config, err := newRestConfigForDefaultSearchPath(kubeconfigPath) - if err != nil { - return nil, nil, err - } - return newClientsFromRestConfig(config) -} - -// NewClientsForKubeconfig creates both a core kubernetes clientset and a cluster-api clientset. -func NewClientsForKubeconfig(kubeconfig string) (*kubernetes.Clientset, *clientset.Clientset, error) { - config, err := newRestConfigForKubeconfig(kubeconfig) +// The overrides parameter is used to select a specific context of the config, for example, select the context with a given cluster name or namespace. +func NewClientsForDefaultSearchpath(kubeconfigPath string, overrides clientcmd.ConfigOverrides) (*kubernetes.Clientset, *clientset.Clientset, error) { + config, err := newRestConfigForDefaultSearchPath(kubeconfigPath, overrides) if err != nil { return nil, nil, err } @@ -98,14 +97,17 @@ func newClientsFromRestConfig(config *rest.Config) (*kubernetes.Clientset, *clie } // newRestConfig creates a rest.Config for the given apiConfig -func newRestConfig(apiConfig *api.Config) (*rest.Config, error) { - return clientcmd.NewDefaultClientConfig(*apiConfig, &clientcmd.ConfigOverrides{}).ClientConfig() +// The overrides parameter is used to select a specific context of the config, for example, select the context with a given cluster name or namespace. +func newRestConfig(apiConfig *api.Config, overrides clientcmd.ConfigOverrides) (*rest.Config, error) { + return clientcmd.NewDefaultClientConfig(*apiConfig, &overrides).ClientConfig() } // newRestConfigForDefaultSearchPath creates a rest.Config by searching for the kubeconfig on the default search path. If an override 'kubeconfigPath' is // given then that path is used instead of the default path. If no override is given, an attempt is made to load the // 'in cluster' config. If this fails, then the default search path is used. -func newRestConfigForDefaultSearchPath(kubeconfigPath string) (*rest.Config, error) { +// +// The overrides parameter is used to select a specific context of the config, for example, select the context with a given cluster name or namespace. +func newRestConfigForDefaultSearchPath(kubeconfigPath string, overrides clientcmd.ConfigOverrides) (*rest.Config, error) { if kubeconfigPath == "" { config, err := rest.InClusterConfig() // if there is no err, continue because InClusterConfig is only expected to succeed if running inside of a pod. @@ -117,16 +119,16 @@ func newRestConfigForDefaultSearchPath(kubeconfigPath string) (*rest.Config, err if err != nil { return nil, err } - return newRestConfig(apiConfig) + return newRestConfig(apiConfig, overrides) } // newRestConfigForKubeconfig creates a rest.Config for a given kubeconfig string. func newRestConfigForKubeconfig(kubeconfig string) (*rest.Config, error) { - apiConfig, err := newApiConfigForDefaultSearchPath(kubeconfig) + apiConfig, err := newApiConfigForKubeconfig(kubeconfig) if err != nil { return nil, err } - return newRestConfig(apiConfig) + return newRestConfig(apiConfig, clientcmd.ConfigOverrides{}) } // newApiConfigForDefaultSearchPath creates an api.Config by searching for the kubeconfig on the default search path. If an override 'kubeconfigPath' is diff --git a/tools/repair/util/repair.go b/tools/repair/util/repair.go index 2515481b4c81..ab4e3d0e536d 100644 --- a/tools/repair/util/repair.go +++ b/tools/repair/util/repair.go @@ -37,7 +37,7 @@ func NewRepairer(dryRun bool, configPath string) (*repairer, error) { configPath = util.GetDefaultKubeConfigPath() } - c, err := clientcmd.NewClusterApiClientForDefaultSearchPath(configPath) + c, err := clientcmd.NewClusterApiClientForDefaultSearchPath(configPath, clientcmd.NewConfigOverrides()) if err != nil { return nil, err } diff --git a/tools/upgrader/util/upgrade.go b/tools/upgrader/util/upgrade.go index 4da7ee3616fd..deb50c8019a3 100644 --- a/tools/upgrader/util/upgrade.go +++ b/tools/upgrader/util/upgrade.go @@ -41,7 +41,7 @@ func initClient(kubeconfig string) error { if kubeconfig == "" { kubeconfig = util.GetDefaultKubeConfigPath() } - coreClientset, clusterapiClientset, err := clientcmd.NewClientsForDefaultSearchpath(kubeconfig) + coreClientset, clusterapiClientset, err := clientcmd.NewClientsForDefaultSearchpath(kubeconfig, clientcmd.NewConfigOverrides()) if err != nil { glog.Fatalf("Error creating rest config: %v", err) return err