Skip to content

Commit

Permalink
Added bootstrap-only components to clusterctl as an option
Browse files Browse the repository at this point in the history
Allows clusterctl to apply boostrap cluster only components that will not
be applied to the target cluster.

Resolves #556
  • Loading branch information
Loc Nguyen committed Feb 28, 2019
1 parent f8d548b commit 1729b6a
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 15 deletions.
9 changes: 9 additions & 0 deletions cmd/clusterctl/clusterdeployer/clusterdeployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type ClusterDeployer struct {
clientFactory clusterclient.Factory
providerComponents string
addonComponents string
bootstrapComponents string
cleanupBootstrapCluster bool
}

Expand All @@ -43,12 +44,14 @@ func New(
clientFactory clusterclient.Factory,
providerComponents string,
addonComponents string,
bootstrapComponents string,
cleanupBootstrapCluster bool) *ClusterDeployer {
return &ClusterDeployer{
bootstrapProvisioner: bootstrapProvisioner,
clientFactory: clientFactory,
providerComponents: providerComponents,
addonComponents: addonComponents,
bootstrapComponents: bootstrapComponents,
cleanupBootstrapCluster: cleanupBootstrapCluster,
}
}
Expand All @@ -67,6 +70,12 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster
}
defer closeClient(bootstrapClient, "bootstrap")

if d.bootstrapComponents != "" {
if err := phases.ApplyBootstrapComponents(bootstrapClient, d.bootstrapComponents); err != nil {
return errors.Wrap(err, "unable to apply bootstrap components to bootstrap cluster")
}
}

klog.Info("Applying Cluster API stack to bootstrap cluster")
if err := phases.ApplyClusterAPIComponents(bootstrapClient, d.providerComponents); err != nil {
return errors.Wrap(err, "unable to apply cluster api stack to bootstrap cluster")
Expand Down
36 changes: 30 additions & 6 deletions cmd/clusterctl/clusterdeployer/clusterdeployer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ func (m *mockProviderComponentsStore) Load() (string, error) {
return m.LoadResult, m.LoadErr
}

type stringCheckFunc func(string) error

type testClusterClient struct {
ApplyErr error
DeleteErr error
Expand Down Expand Up @@ -116,6 +118,8 @@ type testClusterClient struct {
DeleteNamespaceErr error
CloseErr error

ApplyFunc stringCheckFunc

clusters map[string][]*clusterv1.Cluster
machineDeployments map[string][]*clusterv1.MachineDeployment
machineSets map[string][]*clusterv1.MachineSet
Expand All @@ -124,7 +128,10 @@ type testClusterClient struct {
contextNamespace string
}

func (c *testClusterClient) Apply(string) error {
func (c *testClusterClient) Apply(yaml string) error {
if c.ApplyFunc != nil {
return c.ApplyFunc(yaml)
}
return c.ApplyErr
}

Expand Down Expand Up @@ -366,6 +373,7 @@ func (d *testProviderDeployer) GetKubeConfig(_ *clusterv1.Cluster, _ *clusterv1.
func TestClusterCreate(t *testing.T) {
const bootstrapKubeconfig = "bootstrap"
const targetKubeconfig = "target"
const bootstrapComponent = "bootstrap-only"

testcases := []struct {
name string
Expand Down Expand Up @@ -599,7 +607,7 @@ func TestClusterCreate(t *testing.T) {
expectErr: true,
},
{
name: "fail create target cluster",
name: "fail create target cluster",
targetClient: &testClusterClient{CreateClusterObjectErr: errors.New("Test failure")},
bootstrapClient: &testClusterClient{},
namespaceToExpectedInternalMachines: make(map[string]int),
Expand Down Expand Up @@ -628,6 +636,22 @@ func TestClusterCreate(t *testing.T) {
expectExternalCreated: true,
expectErr: true,
},
{
name: "success bootstrap_only components not applied to target cluster",
targetClient: &testClusterClient{ApplyFunc: func(yaml string) error {
if yaml == bootstrapComponent {
return errors.New("Test failure. Bootstrap component should not be applied to target cluster")
}
return nil
}},
bootstrapClient: &testClusterClient{},
namespaceToExpectedInternalMachines: make(map[string]int),
namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)},
cleanupExternal: true,
expectExternalCreated: true,
expectErr: false,
expectedTotalInternalClustersCount: 1,
},
}

for _, testcase := range testcases {
Expand All @@ -649,7 +673,7 @@ func TestClusterCreate(t *testing.T) {

pcStore := mockProviderComponentsStore{}
pcFactory := mockProviderComponentsStoreFactory{NewFromCoreclientsetPCStore: &pcStore}
d := New(p, f, "", "", testcase.cleanupExternal)
d := New(p, f, "", "", bootstrapComponent, testcase.cleanupExternal)
inputMachines := generateMachines()

for namespace, inputClusters := range testcase.namespaceToInputCluster {
Expand Down Expand Up @@ -751,7 +775,7 @@ func TestCreateProviderComponentsScenarios(t *testing.T) {
pcFactory := mockProviderComponentsStoreFactory{NewFromCoreclientsetPCStore: &tc.pcStore}
providerComponentsYaml := "-yaml\ndefinition"
addonsYaml := "-yaml\ndefinition"
d := New(p, f, providerComponentsYaml, addonsYaml, false)
d := New(p, f, providerComponentsYaml, addonsYaml, "", false)
err := d.Create(inputCluster, inputMachines, pd, kubeconfigOut, &pcFactory)
if err == nil && tc.expectedError != "" {
t.Fatalf("error mismatch: got '%v', want '%v'", err, tc.expectedError)
Expand Down Expand Up @@ -858,7 +882,7 @@ func TestDeleteCleanupExternalCluster(t *testing.T) {
f := newTestClusterClientFactory()
f.clusterClients[bootstrapKubeconfig] = tc.bootstrapClient
f.clusterClients[targetKubeconfig] = tc.targetClient
d := New(p, f, "", "", tc.cleanupExternalCluster)
d := New(p, f, "", "", "", tc.cleanupExternalCluster)
err := d.Delete(tc.targetClient, tc.namespace)
if err != nil || tc.expectedErrorMessage != "" {
if err == nil {
Expand Down Expand Up @@ -1151,7 +1175,7 @@ func TestClusterDelete(t *testing.T) {
f.clusterClients[bootstrapKubeconfig] = testCase.bootstrapClient
f.clusterClients[targetKubeconfig] = testCase.targetClient
f.ClusterClientErr = testCase.NewCoreClientsetErr
d := New(p, f, "", "", true)
d := New(p, f, "", "", "", true)

err := d.Delete(testCase.targetClient, testCase.namespace)
if err != nil || testCase.expectedErrorMessage != "" {
Expand Down
2 changes: 2 additions & 0 deletions cmd/clusterctl/cmd/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go_library(
srcs = [
"alpha.go",
"alpha_phase_apply_addons.go",
"alpha_phase_apply_boostrap_components.go",
"alpha_phase_apply_cluster.go",
"alpha_phase_apply_cluster_api_components.go",
"alpha_phase_apply_machines.go",
Expand Down Expand Up @@ -38,6 +39,7 @@ go_library(
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
Expand Down
80 changes: 80 additions & 0 deletions cmd/clusterctl/cmd/alpha_phase_apply_boostrap_components.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
"io/ioutil"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"k8s.io/klog"
"sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/clusterclient"
"sigs.k8s.io/cluster-api/cmd/clusterctl/phases"
)

type AlphaPhaseApplyBootstrapComponentsOptions struct {
Kubeconfig string
BootstrapComponents string
}

var pabco = &AlphaPhaseApplyBootstrapComponentsOptions{}

var alphaPhaseApplyBootstrapComponentsCmd = &cobra.Command{
Use: "apply-bootstrap-components",
Short: "Apply boostrap components",
Long: `Apply bootstrap components`,
Run: func(cmd *cobra.Command, args []string) {
if pabco.BootstrapComponents == "" {
exitWithHelp(cmd, "Please provide yaml file for bootstrap component definition.")
}

if pabco.Kubeconfig == "" {
exitWithHelp(cmd, "Please provide a kubeconfig file.")
}

if err := RunAlphaPhaseApplyBootstrapComponents(pabco); err != nil {
klog.Exit(err)
}
},
}

func RunAlphaPhaseApplyBootstrapComponents(pabco *AlphaPhaseApplyBootstrapComponentsOptions) error {
kubeconfig, err := ioutil.ReadFile(pabco.Kubeconfig)
if err != nil {
return err
}

pc, err := ioutil.ReadFile(pabco.BootstrapComponents)
if err != nil {
return errors.Wrapf(err, "error loading provider components file %q", pabco.BootstrapComponents)
}

clientFactory := clusterclient.NewFactory()
client, err := clientFactory.NewClientFromKubeconfig(string(kubeconfig))
if err != nil {
return errors.Wrap(err, "unable to create cluster client")
}

return phases.ApplyBootstrapComponents(client, string(pc))
}

func init() {
// Required flags
alphaPhaseApplyBootstrapComponentsCmd.Flags().StringVarP(&pabco.Kubeconfig, "kubeconfig", "", "", "Path for the kubeconfig file to use")
alphaPhaseApplyBootstrapComponentsCmd.Flags().StringVarP(&pabco.BootstrapComponents, "bootstrap-components", "b", "", "A yaml file containing bootstrap cluster components")
alphaPhasesCmd.AddCommand(alphaPhaseApplyBootstrapComponentsCmd)
}
23 changes: 16 additions & 7 deletions cmd/clusterctl/cmd/create_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ import (
)

type CreateOptions struct {
Cluster string
Machine string
ProviderComponents string
AddonComponents string
Provider string
KubeconfigOutput string
BootstrapFlags bootstrap.Options
Cluster string
Machine string
ProviderComponents string
AddonComponents string
BootstrapOnlyComponents string
Provider string
KubeconfigOutput string
BootstrapFlags bootstrap.Options
}

var co = &CreateOptions{}
Expand Down Expand Up @@ -92,13 +93,20 @@ func RunCreate(co *CreateOptions) error {
return errors.Wrapf(err, "error loading addons file %q", co.AddonComponents)
}
}
var bc []byte
if co.BootstrapOnlyComponents != "" {
if bc, err = ioutil.ReadFile(co.BootstrapOnlyComponents); err != nil {
return errors.Wrapf(err, "error loading bootstrap only component file %q", co.BootstrapOnlyComponents)
}
}
pcsFactory := clusterdeployer.NewProviderComponentsStoreFactory()

d := clusterdeployer.New(
bootstrapProvider,
clusterclient.NewFactory(),
string(pc),
string(ac),
string(bc),
co.BootstrapFlags.Cleanup)

return d.Create(c, m, pd, co.KubeconfigOutput, pcsFactory)
Expand All @@ -118,6 +126,7 @@ func init() {

// Optional flags
createClusterCmd.Flags().StringVarP(&co.AddonComponents, "addon-components", "a", "", "A yaml file containing cluster addons to apply to the internal cluster")
createClusterCmd.Flags().StringVarP(&co.BootstrapOnlyComponents, "bootstrap-only-components", "", "", "A yaml file containing components to apply only on the bootstrap cluster (before the provider components are applied) but not the provisioned cluster")
createClusterCmd.Flags().StringVarP(&co.KubeconfigOutput, "kubeconfig-out", "", "kubeconfig", "Where to output the kubeconfig for the provisioned cluster")

co.BootstrapFlags.AddFlags(createClusterCmd.Flags())
Expand Down
1 change: 1 addition & 0 deletions cmd/clusterctl/cmd/delete_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ func RunDelete() error {
clusterclient.NewFactory(),
providerComponents,
"",
"",
do.BootstrapFlags.Cleanup)

return deployer.Delete(clusterClient, do.ClusterNamespace)
Expand Down
1 change: 1 addition & 0 deletions cmd/clusterctl/phases/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go_library(
name = "go_default_library",
srcs = [
"applyaddons.go",
"applybootstrapcomponents.go",
"applycluster.go",
"applyclusterapicomponents.go",
"applymachines.go",
Expand Down
32 changes: 32 additions & 0 deletions cmd/clusterctl/phases/applybootstrapcomponents.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package phases

import (
"github.com/pkg/errors"
"k8s.io/klog"
"sigs.k8s.io/cluster-api/cmd/clusterctl/clusterdeployer/clusterclient"
)

func ApplyBootstrapComponents(client clusterclient.Client, components string) error {
klog.Info("Applying Bootstrap-only Components")
if err := client.Apply(components); err != nil {
return errors.Wrap(err, "unable to apply bootstrap-only components")
}

return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Flags:
--bootstrap-cluster-cleanup Whether to cleanup the bootstrap cluster after bootstrap. (default true)
-e, --bootstrap-cluster-kubeconfig string Sets the bootstrap cluster to be an existing Kubernetes cluster.
--bootstrap-flags strings Command line flags to be passed to the chosen bootstrapper
--bootstrap-only-components string A yaml file containing components to apply only on the bootstrap cluster (before the provider components are applied) but not the provisioned cluster
--bootstrap-type string The cluster bootstrapper to use. (default "none")
-c, --cluster string A yaml file containing cluster object definition. Required.
-h, --help help for cluster
Expand Down
1 change: 1 addition & 0 deletions cmd/clusterctl/testdata/create-cluster-no-args.golden
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Flags:
--bootstrap-cluster-cleanup Whether to cleanup the bootstrap cluster after bootstrap. (default true)
-e, --bootstrap-cluster-kubeconfig string Sets the bootstrap cluster to be an existing Kubernetes cluster.
--bootstrap-flags strings Command line flags to be passed to the chosen bootstrapper
--bootstrap-only-components string A yaml file containing components to apply only on the bootstrap cluster (before the provider components are applied) but not the provisioned cluster
--bootstrap-type string The cluster bootstrapper to use. (default "none")
-c, --cluster string A yaml file containing cluster object definition. Required.
-h, --help help for cluster
Expand Down
2 changes: 0 additions & 2 deletions cmd/clusterctl/validation/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ go_library(
"//pkg/apis/cluster/v1alpha1:go_default_library",
"//pkg/controller/noderefutil:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/golang.org/x/net/context:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/sigs.k8s.io/controller-runtime/pkg/client:go_default_library",
Expand All @@ -34,7 +33,6 @@ go_test(
"//pkg/apis/cluster/common:go_default_library",
"//pkg/apis/cluster/v1alpha1:go_default_library",
"//pkg/apis/cluster/v1alpha1/testutil:go_default_library",
"//vendor/golang.org/x/net/context:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
Expand Down

0 comments on commit 1729b6a

Please sign in to comment.