diff --git a/clusterctl/clusterdeployer/clusterdeployer.go b/clusterctl/clusterdeployer/clusterdeployer.go index aacef834c987..2a1958d5560b 100644 --- a/clusterctl/clusterdeployer/clusterdeployer.go +++ b/clusterctl/clusterdeployer/clusterdeployer.go @@ -229,12 +229,14 @@ func (d *ClusterDeployer) createExternalCluster() (ClusterClient, func(), error) if err := d.externalProvisioner.Create(); err != nil { return nil, cleanupFn, fmt.Errorf("could not create external control plane: %v", err) } + if d.cleanupExternalCluster { cleanupFn = func() { glog.Info("Cleaning up external cluster.") d.externalProvisioner.Delete() } } + externalKubeconfig, err := d.externalProvisioner.GetKubeconfig() if err != nil { return nil, cleanupFn, fmt.Errorf("unable to get external cluster kubeconfig: %v", err) @@ -243,6 +245,7 @@ func (d *ClusterDeployer) createExternalCluster() (ClusterClient, func(), error) if err != nil { return nil, cleanupFn, fmt.Errorf("unable to create external client: %v", err) } + return externalClient, cleanupFn, nil } diff --git a/clusterctl/clusterdeployer/externalclusterprovisioner/externalbootstrapcluster.go b/clusterctl/clusterdeployer/externalclusterprovisioner/externalbootstrapcluster.go new file mode 100644 index 000000000000..c11e2c581d20 --- /dev/null +++ b/clusterctl/clusterdeployer/externalclusterprovisioner/externalbootstrapcluster.go @@ -0,0 +1,52 @@ +package externalclusterprovisioner + +import ( + "io/ioutil" + "os" + "fmt" +) + +// Represents an actual external cluster being used for bootstrapping, should not be able to +// actually delete or create, but can point to actual kubeconfig file. +type ExternalBootstrapCluster struct { + kubeconfigPath string + kubeconfigFile string +} + +// NewExternalCluster creates a new external k8s bootstrap cluster object +// We should clean up any lingering resources when clusterctl is complete. +// TODO https://github.com/kubernetes-sigs/cluster-api/issues/448 +func NewExternalCluster(kubeconfigPath string) (*ExternalBootstrapCluster, error) { + if _, err := os.Stat(kubeconfigPath); os.IsNotExist(err) { + return nil, fmt.Errorf("file at %s does not exist", kubeconfigPath) + } + + return &ExternalBootstrapCluster{kubeconfigPath:kubeconfigPath}, nil +} + +// Create implements clusterdeployer.ClusterProvisioner interface +func (e *ExternalBootstrapCluster) Create() error { + // noop + return nil +} +// Delete implements clusterdeployer.ClusterProvisioner interface +func (e *ExternalBootstrapCluster) Delete() error { + // noop + return nil +} + +// GetKubeconfig implements clusterdeployer.ClusterProvisioner interface +func (e *ExternalBootstrapCluster) GetKubeconfig() (string, error) { + + if e.kubeconfigFile == "" { + b, err := ioutil.ReadFile(e.kubeconfigPath) + if err != nil { + return "", err + } + + e.kubeconfigFile = string(b) + } + + + return e.kubeconfigFile, nil +} diff --git a/clusterctl/clusterdeployer/externalclusterprovisioner/externalbootstrapcluster_test.go b/clusterctl/clusterdeployer/externalclusterprovisioner/externalbootstrapcluster_test.go new file mode 100644 index 000000000000..2f6c6ee75f81 --- /dev/null +++ b/clusterctl/clusterdeployer/externalclusterprovisioner/externalbootstrapcluster_test.go @@ -0,0 +1,50 @@ +package externalclusterprovisioner + +import ( + "testing" + "os" + "io/ioutil" +) + +func TestGetKubeconfig(t *testing.T) { + const contents = "dfserfafaew" + f, err := createTempFile(contents) + if err != nil { + t.Fatal("Unable to create test file.") + } + defer os.Remove(f) + + t.Run("invalid path given", func(t *testing.T){ + _, err = NewExternalCluster("") + if err == nil { + t.Fatal("Should not be able create External Cluster Provisioner.") + } + }) + + t.Run("file exists", func(t *testing.T) { + + ec, err := NewExternalCluster(f) + if err != nil { + t.Fatal("Should be able create External Cluster Provisioner.") + } + + c, err := ec.GetKubeconfig() + if err != nil { + t.Fatalf("Unexpected err. Got: %v", err) + return + } + if c != contents { + t.Fatalf("Unexpected contents. Got: %v, Want: %v", c, contents) + } + }) +} + +func createTempFile(contents string) (string, error) { + f, err := ioutil.TempFile("", "") + if err != nil { + return "", err + } + defer f.Close() + f.WriteString(contents) + return f.Name(), nil +} \ No newline at end of file diff --git a/clusterctl/cmd/create_cluster.go b/clusterctl/cmd/create_cluster.go index bef9661ca011..970198e96db4 100644 --- a/clusterctl/cmd/create_cluster.go +++ b/clusterctl/cmd/create_cluster.go @@ -25,20 +25,22 @@ import ( "github.com/spf13/cobra" "sigs.k8s.io/cluster-api/clusterctl/clusterdeployer" "sigs.k8s.io/cluster-api/clusterctl/clusterdeployer/minikube" + "sigs.k8s.io/cluster-api/clusterctl/clusterdeployer/externalclusterprovisioner" clustercommon "sigs.k8s.io/cluster-api/pkg/apis/cluster/common" clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" "sigs.k8s.io/cluster-api/pkg/util" ) type CreateOptions struct { - Cluster string - Machine string - ProviderComponents string - AddonComponents string - CleanupExternalCluster bool - VmDriver string - Provider string - KubeconfigOutput string + Cluster string + Machine string + ProviderComponents string + AddonComponents string + CleanupExternalCluster bool + VmDriver string + Provider string + KubeconfigOutput string + ExistingClusterKubeconfigPath string } var co = &CreateOptions{} @@ -73,7 +75,17 @@ func RunCreate(co *CreateOptions) error { return err } - mini := minikube.New(co.VmDriver) + var externalProvider clusterdeployer.ClusterProvisioner + if co.ExistingClusterKubeconfigPath != "" { + externalProvider, err = externalclusterprovisioner.NewExternalCluster(co.ExistingClusterKubeconfigPath) + if err != nil { + return err + } + } else { + externalProvider = minikube.New(co.VmDriver) + + } + pd, err := getProvider(co.Provider) if err != nil { return err @@ -91,7 +103,7 @@ func RunCreate(co *CreateOptions) error { } pcsFactory := clusterdeployer.NewProviderComponentsStoreFactory() d := clusterdeployer.New( - mini, + externalProvider, clusterdeployer.NewClientFactory(), string(pc), string(ac), @@ -112,6 +124,7 @@ func init() { createClusterCmd.Flags().BoolVarP(&co.CleanupExternalCluster, "cleanup-external-cluster", "", true, "Whether to cleanup the external cluster after bootstrap") createClusterCmd.Flags().StringVarP(&co.VmDriver, "vm-driver", "", "", "Which vm driver to use for minikube") createClusterCmd.Flags().StringVarP(&co.KubeconfigOutput, "kubeconfig-out", "", "kubeconfig", "Where to output the kubeconfig for the provisioned cluster") + createClusterCmd.Flags().StringVarP(&co.ExistingClusterKubeconfigPath, "existing-bootstrap-cluster-kubeconfig", "", "", "Path to an existing cluster's kubeconfig for bootstrapping (intead of using minikube)") createCmd.AddCommand(createClusterCmd) } diff --git a/clusterctl/testdata/create-cluster-no-args-invalid-flag.golden b/clusterctl/testdata/create-cluster-no-args-invalid-flag.golden index 46ea57cc3ebf..624c2ae2abb2 100644 --- a/clusterctl/testdata/create-cluster-no-args-invalid-flag.golden +++ b/clusterctl/testdata/create-cluster-no-args-invalid-flag.golden @@ -3,15 +3,16 @@ Usage: clusterctl create cluster [flags] Flags: - -a, --addon-components string A yaml file containing cluster addons to apply to the internal cluster - --cleanup-external-cluster Whether to cleanup the external cluster after bootstrap (default true) - -c, --cluster string A yaml file containing cluster object definition - -h, --help help for cluster - --kubeconfig-out string Where to output the kubeconfig for the provisioned cluster (default "kubeconfig") - -m, --machines string A yaml file containing machine object definition(s) - --provider string Which provider deployment logic to use (google/vsphere/azure) - -p, --provider-components string A yaml file containing cluster api provider controllers and supporting objects - --vm-driver string Which vm driver to use for minikube + -a, --addon-components string A yaml file containing cluster addons to apply to the internal cluster + --cleanup-external-cluster Whether to cleanup the external cluster after bootstrap (default true) + -c, --cluster string A yaml file containing cluster object definition + --existing-bootstrap-cluster-kubeconfig string Path to an existing cluster's kubeconfig for bootstrapping (intead of using minikube) + -h, --help help for cluster + --kubeconfig-out string Where to output the kubeconfig for the provisioned cluster (default "kubeconfig") + -m, --machines string A yaml file containing machine object definition(s) + --provider string Which provider deployment logic to use (google/vsphere/azure) + -p, --provider-components string A yaml file containing cluster api provider controllers and supporting objects + --vm-driver string Which vm driver to use for minikube Global Flags: --alsologtostderr log to standard error as well as files diff --git a/clusterctl/testdata/create-cluster-no-args.golden b/clusterctl/testdata/create-cluster-no-args.golden index dfc920e4b753..2e883c8615e2 100644 --- a/clusterctl/testdata/create-cluster-no-args.golden +++ b/clusterctl/testdata/create-cluster-no-args.golden @@ -5,15 +5,16 @@ Usage: clusterctl create cluster [flags] Flags: - -a, --addon-components string A yaml file containing cluster addons to apply to the internal cluster - --cleanup-external-cluster Whether to cleanup the external cluster after bootstrap (default true) - -c, --cluster string A yaml file containing cluster object definition - -h, --help help for cluster - --kubeconfig-out string Where to output the kubeconfig for the provisioned cluster (default "kubeconfig") - -m, --machines string A yaml file containing machine object definition(s) - --provider string Which provider deployment logic to use (google/vsphere/azure) - -p, --provider-components string A yaml file containing cluster api provider controllers and supporting objects - --vm-driver string Which vm driver to use for minikube + -a, --addon-components string A yaml file containing cluster addons to apply to the internal cluster + --cleanup-external-cluster Whether to cleanup the external cluster after bootstrap (default true) + -c, --cluster string A yaml file containing cluster object definition + --existing-bootstrap-cluster-kubeconfig string Path to an existing cluster's kubeconfig for bootstrapping (intead of using minikube) + -h, --help help for cluster + --kubeconfig-out string Where to output the kubeconfig for the provisioned cluster (default "kubeconfig") + -m, --machines string A yaml file containing machine object definition(s) + --provider string Which provider deployment logic to use (google/vsphere/azure) + -p, --provider-components string A yaml file containing cluster api provider controllers and supporting objects + --vm-driver string Which vm driver to use for minikube Global Flags: --alsologtostderr log to standard error as well as files