diff --git a/clusterctl/README.md b/clusterctl/README.md index 9be747bf740c..82e3d2cfd58d 100644 --- a/clusterctl/README.md +++ b/clusterctl/README.md @@ -22,11 +22,11 @@ $ go build TBD ### Creating a cluster -1. Create a `cluster.yaml`, `machines.yaml` and `provider-components.yaml` files configured for your cluster. See the provider specific templates and generation tools at `$GOPATH/src/sigs.k8s.io/cluster-api/clusterctl/examples/`. +1. Create the `cluster.yaml`, `machines.yaml`, `provider-components.yaml`, and `addons.yaml` files configured for your cluster. See the provider specific templates and generation tools at `$GOPATH/src/sigs.k8s.io/cluster-api/clusterctl/examples/`. 2. Create a cluster ```shell -clusterctl create cluster --provider [google/vsphere] -c cluster.yaml -m machines.yaml -p provider-components.yaml +clusterctl create cluster --provider [google/vsphere] -c cluster.yaml -m machines.yaml -p provider-components.yaml -a addons.yaml ``` To choose a specific minikube driver, please use the `--vm-driver` command line parameter. For example to use the kvm2 driver with clusterctl you woud add `--vm-driver kvm2` diff --git a/clusterctl/clusterdeployer/clusterdeployer.go b/clusterctl/clusterdeployer/clusterdeployer.go index 2ba5b6291bde..daed029b55a3 100644 --- a/clusterctl/clusterdeployer/clusterdeployer.go +++ b/clusterctl/clusterdeployer/clusterdeployer.go @@ -75,6 +75,7 @@ type ClusterDeployer struct { clientFactory ClientFactory provider ProviderDeployer providerComponents string + addonComponents string kubeconfigOutput string cleanupExternalCluster bool } @@ -84,6 +85,7 @@ func New( clientFactory ClientFactory, provider ProviderDeployer, providerComponents string, + addonComponents string, kubeconfigOutput string, cleanupExternalCluster bool) *ClusterDeployer { return &ClusterDeployer{ @@ -91,6 +93,7 @@ func New( clientFactory: clientFactory, provider: provider, providerComponents: providerComponents, + addonComponents: addonComponents, kubeconfigOutput: kubeconfigOutput, cleanupExternalCluster: cleanupExternalCluster, } @@ -117,28 +120,24 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster }() glog.Info("Applying Cluster API stack to external cluster") - err = d.applyClusterAPIStack(externalClient) - if err != nil { + if err := d.applyClusterAPIStack(externalClient); err != nil { return fmt.Errorf("unable to apply cluster api stack to external cluster: %v", err) } glog.Info("Provisioning internal cluster via external cluster") glog.Infof("Creating cluster object %v on external cluster", cluster.Name) - err = externalClient.CreateClusterObject(cluster) - if err != nil { + if err := externalClient.CreateClusterObject(cluster); err != nil { return fmt.Errorf("unable to create cluster object: %v", err) } glog.Infof("Creating master %v", master.Name) - err = externalClient.CreateMachineObjects([]*clusterv1.Machine{master}) - if err != nil { + if err := externalClient.CreateMachineObjects([]*clusterv1.Machine{master}); err != nil { return fmt.Errorf("unable to create master machine: %v", err) } glog.Infof("Updating external cluster object with master (%s) endpoint", master.Name) - err = d.updateClusterEndpoint(externalClient) - if err != nil { + if err := d.updateClusterEndpoint(externalClient); err != nil { return fmt.Errorf("unable to update external cluster endpoint: %v", err) } @@ -155,8 +154,7 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster }() glog.Info("Applying Cluster API stack to internal cluster") - err = d.applyClusterAPIStackWithPivoting(internalClient, externalClient) - if err != nil { + if err := d.applyClusterAPIStackWithPivoting(internalClient, externalClient); err != nil { return fmt.Errorf("unable to apply cluster api stack to internal cluster: %v", err) } @@ -169,17 +167,20 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster // For some reason, endpoint doesn't get updated in external cluster sometimes. So we // update the internal cluster endpoint as well to be sure. glog.Infof("Updating internal cluster object with master (%s) endpoint", master.Name) - err = d.updateClusterEndpoint(internalClient) - if err != nil { + if err := d.updateClusterEndpoint(internalClient); err != nil { return fmt.Errorf("unable to update internal cluster endpoint: %v", err) } glog.Info("Creating node machines in internal cluster.") - err = internalClient.CreateMachineObjects(nodes) - if err != nil { + if err := internalClient.CreateMachineObjects(nodes); err != nil { return fmt.Errorf("unable to create node machines: %v", err) } + glog.Info("Creating addons in internal cluster.") + if err := internalClient.Apply(d.addonComponents); err != nil { + return fmt.Errorf("unable to apply addons: %v", err) + } + glog.Infof("Done provisioning cluster. You can now access your cluster with kubectl --kubeconfig %v", d.kubeconfigOutput) return nil diff --git a/clusterctl/clusterdeployer/clusterdeployer_test.go b/clusterctl/clusterdeployer/clusterdeployer_test.go index 0af15c37861f..3c3bb2319c62 100644 --- a/clusterctl/clusterdeployer/clusterdeployer_test.go +++ b/clusterctl/clusterdeployer/clusterdeployer_test.go @@ -347,7 +347,7 @@ func TestCreate(t *testing.T) { inputMachines := generateMachines() pcStore := mockProviderComponentsStore{} pcFactory := mockProviderComponentsStoreFactory{NewFromCoreclientsetPCStore: &pcStore} - d := clusterdeployer.New(p, f, pd, "", kubeconfigOut, testcase.cleanupExternal) + d := clusterdeployer.New(p, f, pd, "", "", kubeconfigOut, testcase.cleanupExternal) err := d.Create(inputCluster, inputMachines, &pcFactory) // Validate @@ -410,7 +410,8 @@ func TestCreateProviderComponentsScenarios(t *testing.T) { inputMachines := generateMachines() pcFactory := mockProviderComponentsStoreFactory{NewFromCoreclientsetPCStore: &tc.pcStore} providerComponentsYaml := "-yaml\ndefinition" - d := clusterdeployer.New(p, f, pd, providerComponentsYaml, kubeconfigOut, false) + addonsYaml := "-yaml\ndefinition" + d := clusterdeployer.New(p, f, pd, providerComponentsYaml, addonsYaml, kubeconfigOut, false) err := d.Create(inputCluster, inputMachines, &pcFactory) if err == nil && tc.expectedError != "" { t.Fatalf("error mismatch: got '%v', want '%v'", err, tc.expectedError) diff --git a/clusterctl/cmd/create_cluster.go b/clusterctl/cmd/create_cluster.go index eb46cc7d49a2..4f973507a6e0 100644 --- a/clusterctl/cmd/create_cluster.go +++ b/clusterctl/cmd/create_cluster.go @@ -34,6 +34,7 @@ type CreateOptions struct { Cluster string Machine string ProviderComponents string + AddonComponents string CleanupExternalCluster bool VmDriver string Provider string @@ -78,16 +79,20 @@ func RunCreate(co *CreateOptions) error { if err != nil { return err } + ac, err := ioutil.ReadFile(co.AddonComponents) + if err != nil { + return err + } pcsFactory := clusterdeployer.NewProviderComponentsStoreFactory() d := clusterdeployer.New( mini, clusterdeployer.NewClientFactory(), pd, string(pc), + string(ac), co.KubeconfigOutput, co.CleanupExternalCluster) - err = d.Create(c, m, pcsFactory) - return err + return d.Create(c, m, pcsFactory) } func init() { @@ -95,13 +100,14 @@ func init() { createClusterCmd.Flags().StringVarP(&co.Cluster, "cluster", "c", "", "A yaml file containing cluster object definition") createClusterCmd.Flags().StringVarP(&co.Machine, "machines", "m", "", "A yaml file containing machine object definition(s)") createClusterCmd.Flags().StringVarP(&co.ProviderComponents, "provider-components", "p", "", "A yaml file containing cluster api provider controllers and supporting objects") + createClusterCmd.Flags().StringVarP(&co.AddonComponents, "addon-components", "a", "", "A yaml file containing cluster addons to apply to the internal cluster") // TODO: Remove as soon as code allows /~https://github.com/kubernetes-sigs/cluster-api/issues/157 createClusterCmd.Flags().StringVarP(&co.Provider, "provider", "", "", "Which provider deployment logic to use (google/vsphere)") // Optional flags 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.KubeconfigOutput, "kubeconfig-out", "", "kubeconfig", "where to output the kubeconfig for the provisioned cluster") createCmd.AddCommand(createClusterCmd) } diff --git a/clusterctl/examples/vsphere/addons.yaml.template b/clusterctl/examples/vsphere/addons.yaml.template new file mode 100644 index 000000000000..0693752fdd27 --- /dev/null +++ b/clusterctl/examples/vsphere/addons.yaml.template @@ -0,0 +1,9 @@ +kind: StorageClass +apiVersion: storage.k8s.io/v1 +metadata: + name: standard + annotations: + storageclass.kubernetes.io/is-default-class: "true" +provisioner: kubernetes.io/vsphere-volume +parameters: + datastore: "" \ No newline at end of file diff --git a/clusterctl/examples/vsphere/generate-yaml.sh b/clusterctl/examples/vsphere/generate-yaml.sh index f92c065cb4d0..3cd1ab977c1c 100755 --- a/clusterctl/examples/vsphere/generate-yaml.sh +++ b/clusterctl/examples/vsphere/generate-yaml.sh @@ -79,4 +79,4 @@ cat $PROVIDERCOMPONENT_TEMPLATE_FILE \ > $PROVIDERCOMPONENT_GENERATED_FILE echo "Done generating $PROVIDERCOMPONENT_GENERATED_FILE" -echo "You will still need to generate the cluster.yaml and machines.yaml" +echo "You will still need to generate the cluster.yaml, machines.yaml, and addons.yaml configuration files"