From da0e454eebd25150d60176733b37d8ca7d2304ff Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Fri, 17 Aug 2018 18:13:13 -0700 Subject: [PATCH 1/4] implement APIs for multi namespace support for Cluster API objects add unit tests for multi namespace scenarios --- clusterctl/clusterdeployer/clusterclient.go | 146 ++- clusterctl/clusterdeployer/clusterdeployer.go | 134 +-- .../clusterdeployer/clusterdeployer_test.go | 918 +++++++++++++----- clusterctl/cmd/delete_cluster.go | 4 +- ...delete-cluster-no-args-invalid-flag.golden | 1 + .../example/openapi-gen/BUILD.bazel | 20 + .../kube-openapi/pkg/aggregator/BUILD.bazel | 13 + .../kube-openapi/pkg/builder/BUILD.bazel | 19 + .../kube-openapi/pkg/common/BUILD.bazel | 16 + .../kube-openapi/pkg/generators/BUILD.bazel | 17 + .../kube-openapi/pkg/handler/BUILD.bazel | 21 + .../k8s.io/kube-openapi/pkg/util/BUILD.bazel | 12 + .../kube-openapi/pkg/util/proto/BUILD.bazel | 17 + .../pkg/util/proto/testing/BUILD.bazel | 14 + .../pkg/util/proto/validation/BUILD.bazel | 14 + 15 files changed, 1039 insertions(+), 327 deletions(-) create mode 100644 vendor/k8s.io/kube-openapi/example/openapi-gen/BUILD.bazel create mode 100644 vendor/k8s.io/kube-openapi/pkg/aggregator/BUILD.bazel create mode 100644 vendor/k8s.io/kube-openapi/pkg/builder/BUILD.bazel create mode 100644 vendor/k8s.io/kube-openapi/pkg/common/BUILD.bazel create mode 100644 vendor/k8s.io/kube-openapi/pkg/generators/BUILD.bazel create mode 100644 vendor/k8s.io/kube-openapi/pkg/handler/BUILD.bazel create mode 100644 vendor/k8s.io/kube-openapi/pkg/util/BUILD.bazel create mode 100644 vendor/k8s.io/kube-openapi/pkg/util/proto/BUILD.bazel create mode 100644 vendor/k8s.io/kube-openapi/pkg/util/proto/testing/BUILD.bazel create mode 100644 vendor/k8s.io/kube-openapi/pkg/util/proto/validation/BUILD.bazel diff --git a/clusterctl/clusterdeployer/clusterclient.go b/clusterctl/clusterdeployer/clusterclient.go index d6abc669a08c..8931b4863ae9 100644 --- a/clusterctl/clusterdeployer/clusterclient.go +++ b/clusterctl/clusterdeployer/clusterclient.go @@ -104,12 +104,33 @@ func (c *clusterClient) Apply(manifest string) error { return c.waitForKubectlApply(manifest) } -func (c *clusterClient) GetClusterObjects() ([]*clusterv1.Cluster, error) { +func (c *clusterClient) GetContextNamespace() string { + if c.configOverrides.Context.Namespace == "" { + c.configOverrides.Context.Namespace = apiv1.NamespaceDefault + } + return c.configOverrides.Context.Namespace +} + +func (c *clusterClient) GetClusterObject(name, ns string) (*clusterv1.Cluster, error) { + clustersInNamespace, err := c.GetClusterObjectsInNamespace(ns) + if err != nil { + return nil, err + } + var cluster *clusterv1.Cluster + for _, nc := range clustersInNamespace { + if nc.Name == name { + cluster = nc + break + } + } + return cluster, nil +} + +func (c *clusterClient) GetClusterObjectsInNamespace(namespace string) ([]*clusterv1.Cluster, error) { clusters := []*clusterv1.Cluster{} - // TODO: Iterate over all namespaces where we could have Cluster API Objects https://github.com/kubernetes-sigs/cluster-api/issues/252 - clusterlist, err := c.clientSet.ClusterV1alpha1().Clusters(apiv1.NamespaceDefault).List(metav1.ListOptions{}) + clusterlist, err := c.clientSet.ClusterV1alpha1().Clusters(namespace).List(metav1.ListOptions{}) if err != nil { - return nil, fmt.Errorf("error listing cluster objects: %v", err) + return nil, fmt.Errorf("error listing cluster objects in namespace %q: %v", namespace, err) } for i := 0; i < len(clusterlist.Items); i++ { @@ -118,11 +139,15 @@ func (c *clusterClient) GetClusterObjects() ([]*clusterv1.Cluster, error) { return clusters, nil } -func (c *clusterClient) GetMachineDeploymentObjects() ([]*clusterv1.MachineDeployment, error) { +func (c *clusterClient) GetClusterObjects() ([]*clusterv1.Cluster, error) { // TODO: Iterate over all namespaces where we could have Cluster API Objects https://github.com/kubernetes-sigs/cluster-api/issues/252 - machineDeploymentList, err := c.clientSet.ClusterV1alpha1().MachineDeployments(apiv1.NamespaceDefault).List(metav1.ListOptions{}) + return c.GetClusterObjectsInNamespace(apiv1.NamespaceDefault) +} + +func (c *clusterClient) GetMachineDeploymentObjectsInNamespace(namespace string) ([]*clusterv1.MachineDeployment, error) { + machineDeploymentList, err := c.clientSet.ClusterV1alpha1().MachineDeployments(namespace).List(metav1.ListOptions{}) if err != nil { - return nil, fmt.Errorf("error listing machine deployment objects: %v", err) + return nil, fmt.Errorf("error listing machine deployment objects in namespace %q: %v", namespace, err) } var machineDeployments []*clusterv1.MachineDeployment for i := 0; i < len(machineDeploymentList.Items); i++ { @@ -131,11 +156,15 @@ func (c *clusterClient) GetMachineDeploymentObjects() ([]*clusterv1.MachineDeplo return machineDeployments, nil } -func (c *clusterClient) GetMachineSetObjects() ([]*clusterv1.MachineSet, error) { +func (c *clusterClient) GetMachineDeploymentObjects() ([]*clusterv1.MachineDeployment, error) { // TODO: Iterate over all namespaces where we could have Cluster API Objects https://github.com/kubernetes-sigs/cluster-api/issues/252 + return c.GetMachineDeploymentObjectsInNamespace(apiv1.NamespaceDefault) +} + +func (c *clusterClient) GetMachineSetObjectsInNamespace(namespace string) ([]*clusterv1.MachineSet, error) { machineSetList, err := c.clientSet.ClusterV1alpha1().MachineSets(apiv1.NamespaceDefault).List(metav1.ListOptions{}) if err != nil { - return nil, fmt.Errorf("error listing machine set objects: %v", err) + return nil, fmt.Errorf("error listing machine set objects in namespace %q: %v", namespace, err) } var machineSets []*clusterv1.MachineSet for i := 0; i < len(machineSetList.Items); i++ { @@ -144,12 +173,16 @@ func (c *clusterClient) GetMachineSetObjects() ([]*clusterv1.MachineSet, error) return machineSets, nil } -func (c *clusterClient) GetMachineObjects() ([]*clusterv1.Machine, error) { +func (c *clusterClient) GetMachineSetObjects() ([]*clusterv1.MachineSet, error) { // TODO: Iterate over all namespaces where we could have Cluster API Objects https://github.com/kubernetes-sigs/cluster-api/issues/252 + return c.GetMachineSetObjectsInNamespace(apiv1.NamespaceDefault) +} + +func (c *clusterClient) GetMachineObjectsInNamespace(namespace string) ([]*clusterv1.Machine, error) { machines := []*clusterv1.Machine{} - machineslist, err := c.clientSet.ClusterV1alpha1().Machines(apiv1.NamespaceDefault).List(metav1.ListOptions{}) + machineslist, err := c.clientSet.ClusterV1alpha1().Machines(namespace).List(metav1.ListOptions{}) if err != nil { - return nil, fmt.Errorf("error listing machine objects: %v", err) + return nil, fmt.Errorf("error listing machine objects in namespace %q: %v", namespace, err) } for i := 0; i < len(machineslist.Items); i++ { @@ -158,46 +191,52 @@ func (c *clusterClient) GetMachineObjects() ([]*clusterv1.Machine, error) { return machines, nil } +func (c *clusterClient) GetMachineObjects() ([]*clusterv1.Machine, error) { + // TODO: Iterate over all namespaces where we could have Cluster API Objects https://github.com/kubernetes-sigs/cluster-api/issues/252 + return c.GetMachineObjectsInNamespace(apiv1.NamespaceDefault) +} + func (c *clusterClient) CreateClusterObject(cluster *clusterv1.Cluster) error { - // TODO: Support specific namespaces https://github.com/kubernetes-sigs/cluster-api/issues/252 - _, err := c.clientSet.ClusterV1alpha1().Clusters(apiv1.NamespaceDefault).Create(cluster) + namespace := c.configOverrides.Context.Namespace + if cluster.Namespace != "" { + namespace = cluster.Namespace + } + + _, err := c.clientSet.ClusterV1alpha1().Clusters(namespace).Create(cluster) if err != nil { - return fmt.Errorf("error creating cluster: %v", err) + return fmt.Errorf("error creating cluster in namespace %v: %v", namespace, err) } return err } -func (c *clusterClient) CreateMachineDeploymentObjects(deployments []*clusterv1.MachineDeployment) error { - // TODO: Support specific namespaces https://github.com/kubernetes-sigs/cluster-api/issues/252 +func (c *clusterClient) CreateMachineDeploymentObjects(deployments []*clusterv1.MachineDeployment, namespace string) error { for _, deploy := range deployments { // TODO: Run in parallel https://github.com/kubernetes-sigs/cluster-api/issues/258 - _, err := c.clientSet.ClusterV1alpha1().MachineDeployments(apiv1.NamespaceDefault).Create(deploy) + _, err := c.clientSet.ClusterV1alpha1().MachineDeployments(namespace).Create(deploy) if err != nil { - return fmt.Errorf("error creating a machine deployment object: %v", err) + return fmt.Errorf("error creating a machine deployment object in namespace %q: %v", namespace, err) } } return nil } -func (c *clusterClient) CreateMachineSetObjects(machineSets []*clusterv1.MachineSet) error { - // TODO: Support specific namespaces https://github.com/kubernetes-sigs/cluster-api/issues/252 +func (c *clusterClient) CreateMachineSetObjects(machineSets []*clusterv1.MachineSet, namespace string) error { for _, ms := range machineSets { // TODO: Run in parallel https://github.com/kubernetes-sigs/cluster-api/issues/258 - _, err := c.clientSet.ClusterV1alpha1().MachineSets(apiv1.NamespaceDefault).Create(ms) + _, err := c.clientSet.ClusterV1alpha1().MachineSets(namespace).Create(ms) if err != nil { - return fmt.Errorf("error creating a machine set object: %v", err) + return fmt.Errorf("error creating a machine set object in namespace %q: %v", namespace, err) } } return nil } -func (c *clusterClient) CreateMachineObjects(machines []*clusterv1.Machine) error { - // TODO: Support specific namespaces https://github.com/kubernetes-sigs/cluster-api/issues/252 +func (c *clusterClient) CreateMachineObjects(machines []*clusterv1.Machine, namespace string) error { for _, machine := range machines { // TODO: Run in parallel https://github.com/kubernetes-sigs/cluster-api/issues/258 - createdMachine, err := c.clientSet.ClusterV1alpha1().Machines(apiv1.NamespaceDefault).Create(machine) + createdMachine, err := c.clientSet.ClusterV1alpha1().Machines(namespace).Create(machine) if err != nil { - return fmt.Errorf("error creating a machine object: %v", err) + return fmt.Errorf("error creating a machine object in namespace %v: %v", namespace, err) } err = waitForMachineReady(c.clientSet, createdMachine) if err != nil { @@ -208,70 +247,81 @@ func (c *clusterClient) CreateMachineObjects(machines []*clusterv1.Machine) erro } func (c *clusterClient) DeleteClusterObjects() error { - err := c.clientSet.ClusterV1alpha1().Clusters(apiv1.NamespaceDefault).DeleteCollection(newDeleteOptions(), metav1.ListOptions{}) + return c.DeleteClusterObjectsInNamespace(apiv1.NamespaceDefault) +} + +func (c *clusterClient) DeleteClusterObjectsInNamespace(namespace string) error { + err := c.clientSet.ClusterV1alpha1().Clusters(namespace).DeleteCollection(newDeleteOptions(), metav1.ListOptions{}) if err != nil { - return fmt.Errorf("error deleting cluster objects: %v", err) + return fmt.Errorf("error deleting cluster objects in namespace %q: %v", namespace, err) } err = c.waitForClusterDelete() if err != nil { - return fmt.Errorf("error waiting for cluster(s) deletion to complete: %v", err) + return fmt.Errorf("error waiting for cluster(s) deletion to complete in namespace %q: %v", namespace, err) } return nil } func (c *clusterClient) DeleteMachineDeploymentObjects() error { - err := c.clientSet.ClusterV1alpha1().MachineDeployments(apiv1.NamespaceDefault).DeleteCollection(newDeleteOptions(), metav1.ListOptions{}) + return c.DeleteMachineDeploymentObjectsInNamespace(apiv1.NamespaceDefault) +} + +func (c *clusterClient) DeleteMachineDeploymentObjectsInNamespace(namespace string) error { + err := c.clientSet.ClusterV1alpha1().MachineDeployments(namespace).DeleteCollection(newDeleteOptions(), metav1.ListOptions{}) if err != nil { - return fmt.Errorf("error deleting machine deployment objects: %v", err) + return fmt.Errorf("error deleting machine deployment objects in namespace %q: %v", namespace, err) } err = c.waitForMachineDeploymentsDelete() if err != nil { - return fmt.Errorf("error waiting for machine deployment(s) deletion to complete: %v", err) + return fmt.Errorf("error waiting for machine deployment(s) deletion to complete in namespace %q: %v", namespace, err) } return nil } func (c *clusterClient) DeleteMachineSetObjects() error { - err := c.clientSet.ClusterV1alpha1().MachineSets(apiv1.NamespaceDefault).DeleteCollection(newDeleteOptions(), metav1.ListOptions{}) + return c.DeleteMachineSetObjectsInNamespace(apiv1.NamespaceDefault) +} + +func (c *clusterClient) DeleteMachineSetObjectsInNamespace(namespace string) error { + err := c.clientSet.ClusterV1alpha1().MachineSets(namespace).DeleteCollection(newDeleteOptions(), metav1.ListOptions{}) if err != nil { - return fmt.Errorf("error deleting machine set objects: %v", err) + return fmt.Errorf("error deleting machine set objects in namespace %q: %v", namespace, err) } err = c.waitForMachineSetsDelete() if err != nil { - return fmt.Errorf("error waiting for machine set(s) deletion to complete: %v", err) + return fmt.Errorf("error waiting for machine set(s) deletion to complete in namespace %q: %v", namespace, err) } return nil } func (c *clusterClient) DeleteMachineObjects() error { - err := c.clientSet.ClusterV1alpha1().Machines(apiv1.NamespaceDefault).DeleteCollection(newDeleteOptions(), metav1.ListOptions{}) + return c.DeleteMachineObjectsInNamespace(apiv1.NamespaceDefault) +} + +func (c *clusterClient) DeleteMachineObjectsInNamespace(namespace string) error { + err := c.clientSet.ClusterV1alpha1().Machines(namespace).DeleteCollection(newDeleteOptions(), metav1.ListOptions{}) if err != nil { - return fmt.Errorf("error deleting machine objects: %v", err) + return fmt.Errorf("error deleting machine objects in namespace %q: %v", namespace, err) } err = c.waitForMachinesDelete() if err != nil { - return fmt.Errorf("error waiting for machine(s) deletion to complete: %v", err) + return fmt.Errorf("error waiting for machine(s) deletion to complete in namespace %q: %v", namespace, err) } return nil } func newDeleteOptions() *metav1.DeleteOptions { - propogationPolicy := metav1.DeletePropagationForeground + propagationPolicy := metav1.DeletePropagationForeground return &metav1.DeleteOptions{ - PropagationPolicy: &propogationPolicy, + PropagationPolicy: &propagationPolicy, } } -func (c *clusterClient) UpdateClusterObjectEndpoint(masterIP string) error { - clusters, err := c.GetClusterObjects() +func (c *clusterClient) UpdateClusterObjectEndpoint(masterIP, clusterName, ns string) error { + cluster, err := c.GetClusterObject(clusterName, ns) if err != nil { return err } - if len(clusters) != 1 { - // TODO: Do not assume default namespace nor single cluster https://github.com/kubernetes-sigs/cluster-api/issues/252 - return fmt.Errorf("More than the one expected cluster found %v", clusters) - } - cluster := clusters[0] cluster.Status.APIEndpoints = append(cluster.Status.APIEndpoints, clusterv1.APIEndpoint{ Host: masterIP, diff --git a/clusterctl/clusterdeployer/clusterdeployer.go b/clusterctl/clusterdeployer/clusterdeployer.go index 7265a181d0be..8dbd3f9792eb 100644 --- a/clusterctl/clusterdeployer/clusterdeployer.go +++ b/clusterctl/clusterdeployer/clusterdeployer.go @@ -23,6 +23,7 @@ import ( "strings" "time" + apiv1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" "sigs.k8s.io/cluster-api/pkg/deployer" @@ -49,22 +50,32 @@ type ClusterProvisioner interface { // Provides interaction with a cluster type ClusterClient interface { + GetContextNamespace() string Apply(string) error Delete(string) error WaitForClusterV1alpha1Ready() error GetClusterObjects() ([]*clusterv1.Cluster, error) + GetClusterObjectsInNamespace(string) ([]*clusterv1.Cluster, error) + GetClusterObject(string, string) (*clusterv1.Cluster, error) GetMachineDeploymentObjects() ([]*clusterv1.MachineDeployment, error) + GetMachineDeploymentObjectsInNamespace(string) ([]*clusterv1.MachineDeployment, error) GetMachineSetObjects() ([]*clusterv1.MachineSet, error) + GetMachineSetObjectsInNamespace(string) ([]*clusterv1.MachineSet, error) GetMachineObjects() ([]*clusterv1.Machine, error) + GetMachineObjectsInNamespace(ns string) ([]*clusterv1.Machine, error) CreateClusterObject(*clusterv1.Cluster) error - CreateMachineDeploymentObjects([]*clusterv1.MachineDeployment) error - CreateMachineSetObjects([]*clusterv1.MachineSet) error - CreateMachineObjects([]*clusterv1.Machine) error + CreateMachineDeploymentObjects([]*clusterv1.MachineDeployment, string) error + CreateMachineSetObjects([]*clusterv1.MachineSet, string) error + CreateMachineObjects([]*clusterv1.Machine, string) error + DeleteClusterObjectsInNamespace(string) error DeleteClusterObjects() error + DeleteMachineDeploymentObjectsInNamespace(string) error DeleteMachineDeploymentObjects() error + DeleteMachineSetObjectsInNamespace(string) error DeleteMachineSetObjects() error + DeleteMachineObjectsInNamespace(string) error DeleteMachineObjects() error - UpdateClusterObjectEndpoint(string) error + UpdateClusterObjectEndpoint(string, string, string) error Close() error } @@ -111,12 +122,11 @@ const ( timeoutKubeconfigReady = 20 * time.Minute ) -// Creates the a cluster from the provided cluster definition and machine list. - +// Create the a cluster from the provided cluster definition and machine list. func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*clusterv1.Machine, provider ProviderDeployer, kubeconfigOutput string, providerComponentsStoreFactory ProviderComponentsStoreFactory) error { master, nodes, err := extractMasterMachine(machines) if err != nil { - return fmt.Errorf("unable to seperate master machines from node machines: %v", err) + return fmt.Errorf("unable to separate master machines from node machines: %v", err) } glog.Info("Creating bootstrap cluster") @@ -127,37 +137,42 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster } defer closeClient(bootstrapClient, "bootstrap") + if cluster.Namespace == "" { + cluster.Namespace = bootstrapClient.GetContextNamespace() + } + // TODO: @ashisha Create cluster.Namespace in bootstrap and target clusters? + glog.Info("Applying Cluster API stack to bootstrap cluster") - if err := d.applyClusterAPIStack(bootstrapClient); err != nil { + if err := d.applyClusterAPIStack(bootstrapClient, cluster.Namespace); err != nil { return fmt.Errorf("unable to apply cluster api stack to bootstrap cluster: %v", err) } glog.Info("Provisioning target cluster via bootstrap cluster") - glog.Infof("Creating cluster object %v on bootstrap cluster", cluster.Name) + glog.Infof("Creating cluster object %v on bootstrap cluster in namespace %v", cluster.Name, cluster.Namespace) if err := bootstrapClient.CreateClusterObject(cluster); err != nil { return fmt.Errorf("unable to create cluster object: %v", err) } - glog.Infof("Creating master %v", master.Name) - if err := bootstrapClient.CreateMachineObjects([]*clusterv1.Machine{master}); err != nil { + glog.Infof("Creating master %v in namespace %v", master.Name, cluster.Namespace) + if err := bootstrapClient.CreateMachineObjects([]*clusterv1.Machine{master}, cluster.Namespace); err != nil { return fmt.Errorf("unable to create master machine: %v", err) } - glog.Infof("Updating bootstrap cluster object with master (%s) endpoint", master.Name) - if err := d.updateClusterEndpoint(bootstrapClient, provider); err != nil { + glog.Infof("Updating bootstrap cluster object for cluster %v in namespace %v with master (%s) endpoint", cluster.Name, cluster.Namespace, master.Name) + if err := d.updateClusterEndpoint(bootstrapClient, provider, cluster.Name, cluster.Namespace); err != nil { return fmt.Errorf("unable to update bootstrap cluster endpoint: %v", err) } glog.Info("Creating target cluster") - targetClient, err := d.createTargetClusterClient(bootstrapClient, provider, kubeconfigOutput) + targetClient, err := d.createTargetCluster(bootstrapClient, provider, kubeconfigOutput, cluster.Name, cluster.Namespace) if err != nil { return fmt.Errorf("unable to create target cluster: %v", err) } defer closeClient(targetClient, "target") glog.Info("Applying Cluster API stack to target cluster") - if err := d.applyClusterAPIStackWithPivoting(targetClient, bootstrapClient); err != nil { + if err := d.applyClusterAPIStackWithPivoting(targetClient, bootstrapClient, cluster.Namespace); err != nil { return fmt.Errorf("unable to apply cluster api stack to target cluster: %v", err) } @@ -170,12 +185,12 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster // For some reason, endpoint doesn't get updated in bootstrap cluster sometimes. So we // update the target cluster endpoint as well to be sure. glog.Infof("Updating target cluster object with master (%s) endpoint", master.Name) - if err := d.updateClusterEndpoint(targetClient, provider); err != nil { + if err := d.updateClusterEndpoint(targetClient, provider, cluster.Name, cluster.Namespace); err != nil { return fmt.Errorf("unable to update target cluster endpoint: %v", err) } glog.Info("Creating node machines in target cluster.") - if err := targetClient.CreateMachineObjects(nodes); err != nil { + if err := targetClient.CreateMachineObjects(nodes, cluster.Namespace); err != nil { return fmt.Errorf("unable to create node machines: %v", err) } @@ -191,7 +206,7 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster return nil } -func (d *ClusterDeployer) Delete(targetClient ClusterClient) error { +func (d *ClusterDeployer) Delete(targetClient ClusterClient, namespace string) error { glog.Info("Creating bootstrap cluster") bootstrapClient, cleanupBootstrapCluster, err := d.createBootstrapCluster() defer cleanupBootstrapCluster() @@ -201,7 +216,7 @@ func (d *ClusterDeployer) Delete(targetClient ClusterClient) error { defer closeClient(bootstrapClient, "bootstrap") glog.Info("Applying Cluster API stack to bootstrap cluster") - if err = d.applyClusterAPIStack(bootstrapClient); err != nil { + if err = d.applyClusterAPIStack(bootstrapClient, namespace); err != nil { return fmt.Errorf("unable to apply cluster api stack to bootstrap cluster: %v", err) } @@ -212,12 +227,12 @@ func (d *ClusterDeployer) Delete(targetClient ClusterClient) error { } glog.Info("Copying objects from target cluster to bootstrap cluster") - if err = pivot(targetClient, bootstrapClient); err != nil { + if err = pivotNamespace(targetClient, bootstrapClient, namespace); err != nil { return fmt.Errorf("unable to copy objects from target to bootstrap cluster: %v", err) } glog.Info("Deleting objects from bootstrap cluster") - if err = deleteObjects(bootstrapClient); err != nil { + if err = deleteObjectsInNamespace(bootstrapClient, namespace); err != nil { return fmt.Errorf("unable to finish deleting objects in bootstrap cluster, resources may have been leaked: %v", err) } glog.Info("Deletion of cluster complete") @@ -250,8 +265,8 @@ func (d *ClusterDeployer) createBootstrapCluster() (ClusterClient, func(), error return bootstrapClient, cleanupFn, nil } -func (d *ClusterDeployer) createTargetClusterClient(bootstrapClient ClusterClient, provider ProviderDeployer, kubeconfigOutput string) (ClusterClient, error) { - cluster, master, _, err := getClusterAPIObjects(bootstrapClient) +func (d *ClusterDeployer) createTargetCluster(bootstrapClient ClusterClient, provider ProviderDeployer, kubeconfigOutput, clusterName, namespace string) (ClusterClient, error) { + cluster, master, _, err := getClusterAPIObject(bootstrapClient, clusterName, namespace) if err != nil { return nil, err } @@ -274,11 +289,11 @@ func (d *ClusterDeployer) createTargetClusterClient(bootstrapClient ClusterClien return targetClient, nil } -func (d *ClusterDeployer) updateClusterEndpoint(client ClusterClient, provider ProviderDeployer) error { +func (d *ClusterDeployer) updateClusterEndpoint(client ClusterClient, provider ProviderDeployer, clusterName, namespace string) error { // Update cluster endpoint. Needed till this logic moves into cluster controller. // TODO: https://github.com/kubernetes-sigs/cluster-api/issues/158 // Fetch fresh objects. - cluster, master, _, err := getClusterAPIObjects(client) + cluster, master, _, err := getClusterAPIObject(client, clusterName, namespace) if err != nil { return err } @@ -286,7 +301,7 @@ func (d *ClusterDeployer) updateClusterEndpoint(client ClusterClient, provider P if err != nil { return fmt.Errorf("unable to get master IP: %v", err) } - err = client.UpdateClusterObjectEndpoint(masterIP) + err = client.UpdateClusterObjectEndpoint(masterIP, clusterName, namespace) if err != nil { return fmt.Errorf("unable to update cluster endpoint: %v", err) } @@ -309,36 +324,36 @@ func (d *ClusterDeployer) saveProviderComponentsToCluster(factory ProviderCompon return nil } -func (d *ClusterDeployer) applyClusterAPIStack(client ClusterClient) error { - glog.Info("Applying Cluster API APIServer") - err := d.applyClusterAPIApiserver(client) +func (d *ClusterDeployer) applyClusterAPIStack(client ClusterClient, namespace string) error { + glog.Infof("Applying Cluster API APIServer in namespace %v", namespace) + err := d.applyClusterAPIApiserver(client, namespace) if err != nil { - return fmt.Errorf("unable to apply cluster apiserver: %v", err) + return fmt.Errorf("unable to apply cluster apiserver in namespace %v: %v", namespace, err) } glog.Info("Applying Cluster API Provider Components") - err = d.applyClusterAPIControllers(client) + err = d.applyClusterAPIControllers(client, namespace) if err != nil { return fmt.Errorf("unable to apply cluster api controllers: %v", err) } return nil } -func (d *ClusterDeployer) applyClusterAPIStackWithPivoting(client ClusterClient, source ClusterClient) error { +func (d *ClusterDeployer) applyClusterAPIStackWithPivoting(client, source ClusterClient, namespace string) error { glog.Info("Applying Cluster API APIServer") - err := d.applyClusterAPIApiserver(client) + err := d.applyClusterAPIApiserver(client, namespace) if err != nil { return fmt.Errorf("unable to apply cluster api apiserver: %v", err) } glog.Info("Pivoting Cluster API objects from bootstrap to target cluster.") - err = pivot(source, client) + err = pivotNamespace(source, client, namespace) if err != nil { return fmt.Errorf("unable to pivot cluster API objects: %v", err) } glog.Info("Applying Cluster API Provider Components.") - err = d.applyClusterAPIControllers(client) + err = d.applyClusterAPIControllers(client, namespace) if err != nil { return fmt.Errorf("unable to apply cluster api controllers: %v", err) } @@ -346,7 +361,7 @@ func (d *ClusterDeployer) applyClusterAPIStackWithPivoting(client ClusterClient, return nil } -func (d *ClusterDeployer) applyClusterAPIApiserver(client ClusterClient) error { +func (d *ClusterDeployer) applyClusterAPIApiserver(client ClusterClient, namespace string) error { yaml, err := deployer.GetApiServerYaml() if err != nil { return fmt.Errorf("unable to generate apiserver yaml: %v", err) @@ -359,7 +374,7 @@ func (d *ClusterDeployer) applyClusterAPIApiserver(client ClusterClient) error { return client.WaitForClusterV1alpha1Ready() } -func (d *ClusterDeployer) applyClusterAPIControllers(client ClusterClient) error { +func (d *ClusterDeployer) applyClusterAPIControllers(client ClusterClient, namespace string) error { return client.Apply(d.providerComponents) } @@ -388,7 +403,7 @@ func waitForKubeconfigReady(provider ProviderDeployer, cluster *clusterv1.Cluste return kubeconfig, err } -func pivot(from, to ClusterClient) error { +func pivotNamespace(from, to ClusterClient, namespace string) error { if err := from.WaitForClusterV1alpha1Ready(); err != nil { return fmt.Errorf("cluster v1alpha1 resource not ready on source cluster") } @@ -397,7 +412,7 @@ func pivot(from, to ClusterClient) error { return fmt.Errorf("cluster v1alpha1 resource not ready on target cluster") } - clusters, err := from.GetClusterObjects() + clusters, err := from.GetClusterObjectsInNamespace(namespace) if err != nil { return err } @@ -411,33 +426,33 @@ func pivot(from, to ClusterClient) error { glog.Infof("Moved Cluster '%s'", cluster.GetName()) } - fromDeployments, err := from.GetMachineDeploymentObjects() + fromDeployments, err := from.GetMachineDeploymentObjectsInNamespace(namespace) if err != nil { return err } for _, deployment := range fromDeployments { // New objects cannot have a specified resource version. Clear it out. deployment.SetResourceVersion("") - if err = to.CreateMachineDeploymentObjects([]*clusterv1.MachineDeployment{deployment}); err != nil { + if err = to.CreateMachineDeploymentObjects([]*clusterv1.MachineDeployment{deployment}, namespace); err != nil { return fmt.Errorf("error moving MachineDeployment '%v': %v", deployment.GetName(), err) } glog.Infof("Moved MachineDeployment %v", deployment.GetName()) } - fromMachineSets, err := from.GetMachineSetObjects() + fromMachineSets, err := from.GetMachineSetObjectsInNamespace(namespace) if err != nil { return err } for _, machineSet := range fromMachineSets { // New objects cannot have a specified resource version. Clear it out. machineSet.SetResourceVersion("") - if err := to.CreateMachineSetObjects([]*clusterv1.MachineSet{machineSet}); err != nil { + if err := to.CreateMachineSetObjects([]*clusterv1.MachineSet{machineSet}, namespace); err != nil { return fmt.Errorf("error moving MachineSet '%v': %v", machineSet.GetName(), err) } glog.Infof("Moved MachineSet %v", machineSet.GetName()) } - machines, err := from.GetMachineObjects() + machines, err := from.GetMachineObjectsInNamespace(namespace) if err != nil { return err } @@ -445,7 +460,7 @@ func pivot(from, to ClusterClient) error { for _, machine := range machines { // New objects cannot have a specified resource version. Clear it out. machine.SetResourceVersion("") - if err = to.CreateMachineObjects([]*clusterv1.Machine{machine}); err != nil { + if err = to.CreateMachineObjects([]*clusterv1.Machine{machine}, namespace); err != nil { return fmt.Errorf("error moving Machine '%v': %v", machine.GetName(), err) } glog.Infof("Moved Machine '%s'", machine.GetName()) @@ -453,25 +468,25 @@ func pivot(from, to ClusterClient) error { return nil } -func deleteObjects(client ClusterClient) error { +func deleteObjectsInNamespace(client ClusterClient, namespace string) error { var errors []string glog.Infof("Deleting machine deployments") - if err := client.DeleteMachineDeploymentObjects(); err != nil { + if err := client.DeleteMachineDeploymentObjectsInNamespace(namespace); err != nil { err = fmt.Errorf("error deleting machine deployments: %v", err) errors = append(errors, err.Error()) } glog.Infof("Deleting machine sets") - if err := client.DeleteMachineSetObjects(); err != nil { + if err := client.DeleteMachineSetObjectsInNamespace(namespace); err != nil { err = fmt.Errorf("error deleting machine sets: %v", err) errors = append(errors, err.Error()) } glog.Infof("Deleting machines") - if err := client.DeleteMachineObjects(); err != nil { + if err := client.DeleteMachineObjectsInNamespace(namespace); err != nil { err = fmt.Errorf("error deleting machines: %v", err) errors = append(errors, err.Error()) } glog.Infof("Deleting clusters") - if err := client.DeleteClusterObjects(); err != nil { + if err := client.DeleteClusterObjectsInNamespace(namespace); err != nil { err = fmt.Errorf("error deleting clusters: %v", err) errors = append(errors, err.Error()) } @@ -481,22 +496,23 @@ func deleteObjects(client ClusterClient) error { return nil } -func getClusterAPIObjects(client ClusterClient) (*clusterv1.Cluster, *clusterv1.Machine, []*clusterv1.Machine, error) { - machines, err := client.GetMachineObjects() +func deleteObjects(client ClusterClient) error { + return deleteObjectsInNamespace(client, apiv1.NamespaceDefault) +} + +func getClusterAPIObject(client ClusterClient, clusterName, namespace string) (*clusterv1.Cluster, *clusterv1.Machine, []*clusterv1.Machine, error) { + machines, err := client.GetMachineObjectsInNamespace(namespace) if err != nil { return nil, nil, nil, fmt.Errorf("unable to fetch machines: %v", err) } - clusters, err := client.GetClusterObjects() + cluster, err := client.GetClusterObject(clusterName, namespace) if err != nil { - return nil, nil, nil, fmt.Errorf("unable to fetch clusters: %v", err) + return nil, nil, nil, fmt.Errorf("unable to fetch cluster %v in namespace %v: %v", clusterName, namespace, err) } - if len(clusters) != 1 { - return nil, nil, nil, fmt.Errorf("fetched not exactly one cluster object. Count %v", len(clusters)) - } - cluster := clusters[0] + master, nodes, err := extractMasterMachine(machines) if err != nil { - return nil, nil, nil, fmt.Errorf("unable to fetch master machine: %v", err) + return nil, nil, nil, fmt.Errorf("unable to fetch master machine in cluster %v in namespace %v: %v", clusterName, namespace, err) } return cluster, master, nodes, nil } diff --git a/clusterctl/clusterdeployer/clusterdeployer_test.go b/clusterctl/clusterdeployer/clusterdeployer_test.go index da31a4d7631c..d2ed19d74823 100644 --- a/clusterctl/clusterdeployer/clusterdeployer_test.go +++ b/clusterctl/clusterdeployer/clusterdeployer_test.go @@ -83,28 +83,38 @@ func (m *mockProviderComponentsStore) Load() (string, error) { } type testClusterClient struct { - ApplyErr error - DeleteErr error - WaitForClusterV1alpha1ReadyErr error - GetClusterObjectsErr error - GetMachineDeploymentObjectsErr error - GetMachineSetObjectsErr error - GetMachineObjectsErr error - CreateClusterObjectErr error - CreateMachineObjectsErr error - CreateMachineSetObjectsErr error - CreateMachineDeploymentsObjectsErr error - DeleteClusterObjectsErr error - DeleteMachineObjectsErr error - DeleteMachineSetObjectsErr error - DeleteMachineDeploymentsObjectsErr error - UpdateClusterObjectEndpointErr error - CloseErr error - - clusters []*clusterv1.Cluster - machineDeployments []*clusterv1.MachineDeployment - machineSets []*clusterv1.MachineSet - machines []*clusterv1.Machine + ApplyErr error + DeleteErr error + WaitForClusterV1alpha1ReadyErr error + GetClusterObjectsErr error + GetClusterObjectErr error + GetClusterObjectsInNamespaceErr error + GetMachineDeploymentObjectsErr error + GetMachineDeploymentObjectsInNamespaceErr error + GetMachineSetObjectsErr error + GetMachineSetObjectsInNamespaceErr error + GetMachineObjectsErr error + GetMachineObjectsInNamespaceErr error + CreateClusterObjectErr error + CreateMachineObjectsErr error + CreateMachineSetObjectsErr error + CreateMachineDeploymentsObjectsErr error + DeleteClusterObjectsErr error + DeleteClusterObjectsInNamespaceErr error + DeleteMachineObjectsErr error + DeleteMachineObjectsInNamespaceErr error + DeleteMachineSetObjectsErr error + DeleteMachineSetObjectsInNamespaceErr error + DeleteMachineDeploymentsObjectsErr error + DeleteMachineDeploymentsObjectsInNamespaceErr error + UpdateClusterObjectEndpointErr error + CloseErr error + + clusters map[string][]*clusterv1.Cluster + machineDeployments map[string][]*clusterv1.MachineDeployment + machineSets map[string][]*clusterv1.MachineSet + machines map[string][]*clusterv1.Machine + contextNamespace string } func (c *testClusterClient) Apply(string) error { @@ -115,75 +125,155 @@ func (c *testClusterClient) Delete(string) error { return c.DeleteErr } +func (c *testClusterClient) GetContextNamespace() string { + if c.contextNamespace == "" { + return "foo" + } + return c.contextNamespace +} + func (c *testClusterClient) WaitForClusterV1alpha1Ready() error { return c.WaitForClusterV1alpha1ReadyErr } func (c *testClusterClient) GetClusterObjects() ([]*clusterv1.Cluster, error) { - return c.clusters, c.GetClusterObjectsErr + return c.clusters[metav1.NamespaceDefault], c.GetClusterObjectsErr +} + +func (c *testClusterClient) GetClusterObject(clusterName, namespace string) (*clusterv1.Cluster, error) { + if c.GetClusterObjectErr != nil { + return nil, c.GetClusterObjectErr + } + var cluster *clusterv1.Cluster + for _, nc := range c.clusters[namespace] { + if nc.Name == clusterName { + cluster = nc + break + } + } + return cluster, nil +} + +func (c *testClusterClient) GetClusterObjectsInNamespace(namespace string) ([]*clusterv1.Cluster, error) { + if c.GetClusterObjectsInNamespaceErr != nil { + return nil, c.GetClusterObjectsInNamespaceErr + } + return c.clusters[namespace], nil } func (c *testClusterClient) GetMachineDeploymentObjects() ([]*clusterv1.MachineDeployment, error) { - return c.machineDeployments, c.GetMachineDeploymentObjectsErr + return c.machineDeployments[metav1.NamespaceDefault], c.GetMachineDeploymentObjectsErr +} + +func (c *testClusterClient) GetMachineDeploymentObjectsInNamespace(namespace string) ([]*clusterv1.MachineDeployment, error) { + return c.machineDeployments[namespace], c.GetMachineDeploymentObjectsInNamespaceErr } func (c *testClusterClient) GetMachineSetObjects() ([]*clusterv1.MachineSet, error) { - return c.machineSets, c.GetMachineSetObjectsErr + return c.machineSets[metav1.NamespaceDefault], c.GetMachineSetObjectsErr +} + +func (c *testClusterClient) GetMachineSetObjectsInNamespace(namespace string) ([]*clusterv1.MachineSet, error) { + return c.machineSets[namespace], c.GetMachineSetObjectsInNamespaceErr } func (c *testClusterClient) GetMachineObjects() ([]*clusterv1.Machine, error) { - return c.machines, c.GetMachineObjectsErr + return c.machines[metav1.NamespaceDefault], c.GetMachineObjectsErr +} + +func (c *testClusterClient) GetMachineObjectsInNamespace(namespace string) ([]*clusterv1.Machine, error) { + return c.machines[namespace], c.GetMachineObjectsInNamespaceErr } func (c *testClusterClient) CreateClusterObject(cluster *clusterv1.Cluster) error { if c.CreateClusterObjectErr == nil { - c.clusters = append(c.clusters, cluster) + if c.clusters == nil { + c.clusters = make(map[string][]*clusterv1.Cluster) + } + c.clusters[cluster.Namespace] = append(c.clusters[cluster.Namespace], cluster) return nil } return c.CreateClusterObjectErr } -func (c *testClusterClient) CreateMachineDeploymentObjects(deployments []*clusterv1.MachineDeployment) error { +func (c *testClusterClient) CreateMachineDeploymentObjects(deployments []*clusterv1.MachineDeployment, namespace string) error { if c.CreateMachineDeploymentsObjectsErr == nil { - c.machineDeployments = append(c.machineDeployments, deployments...) + if c.machineDeployments == nil { + c.machineDeployments = make(map[string][]*clusterv1.MachineDeployment) + } + c.machineDeployments[namespace] = append(c.machineDeployments[namespace], deployments...) return nil } return c.CreateMachineDeploymentsObjectsErr } -func (c *testClusterClient) CreateMachineSetObjects(machineSets []*clusterv1.MachineSet) error { +func (c *testClusterClient) CreateMachineSetObjects(machineSets []*clusterv1.MachineSet, namespace string) error { if c.CreateMachineSetObjectsErr == nil { - c.machineSets = append(c.machineSets, machineSets...) + if c.machineSets == nil { + c.machineSets = make(map[string][]*clusterv1.MachineSet) + } + c.machineSets[namespace] = append(c.machineSets[namespace], machineSets...) return nil } return c.CreateMachineSetObjectsErr } -func (c *testClusterClient) CreateMachineObjects(machines []*clusterv1.Machine) error { +func (c *testClusterClient) CreateMachineObjects(machines []*clusterv1.Machine, namespace string) error { if c.CreateMachineObjectsErr == nil { - c.machines = append(c.machines, machines...) + if c.machines == nil { + c.machines = make(map[string][]*clusterv1.Machine) + } + c.machines[namespace] = append(c.machines[namespace], machines...) return nil } return c.CreateMachineObjectsErr } +func (c *testClusterClient) DeleteClusterObjectsInNamespace(ns string) error { + if c.DeleteClusterObjectsInNamespaceErr == nil { + delete(c.clusters, ns) + } + return c.DeleteClusterObjectsInNamespaceErr +} + func (c *testClusterClient) DeleteClusterObjects() error { return c.DeleteClusterObjectsErr } +func (c *testClusterClient) DeleteMachineDeploymentObjectsInNamespace(ns string) error { + if c.DeleteMachineDeploymentsObjectsInNamespaceErr == nil { + delete(c.machineDeployments, ns) + } + return c.DeleteMachineDeploymentsObjectsInNamespaceErr +} + func (c *testClusterClient) DeleteMachineDeploymentObjects() error { return c.DeleteMachineDeploymentsObjectsErr } +func (c *testClusterClient) DeleteMachineSetObjectsInNamespace(ns string) error { + if c.DeleteMachineSetObjectsInNamespaceErr == nil { + delete(c.machineSets, ns) + } + return c.DeleteMachineSetObjectsInNamespaceErr +} + func (c *testClusterClient) DeleteMachineSetObjects() error { return c.DeleteMachineSetObjectsErr } +func (c *testClusterClient) DeleteMachineObjectsInNamespace(ns string) error { + if c.DeleteMachineObjectsInNamespaceErr == nil { + delete(c.machines, ns) + } + return c.DeleteMachineObjectsInNamespaceErr +} + func (c *testClusterClient) DeleteMachineObjects() error { return c.DeleteMachineObjectsErr } -func (c *testClusterClient) UpdateClusterObjectEndpoint(string) error { +func (c *testClusterClient) UpdateClusterObjectEndpoint(string, string, string) error { return c.UpdateClusterObjectEndpointErr } func (c *testClusterClient) Close() error { @@ -228,160 +318,253 @@ func (d *testProviderDeployer) GetKubeConfig(_ *clusterv1.Cluster, _ *clusterv1. return d.kubeconfig, d.GetKubeConfigErr } -func TestCreate(t *testing.T) { +func TestClusterCreate(t *testing.T) { const bootstrapKubeconfig = "bootstrap" const targetKubeconfig = "target" - var testcases = []struct { - name string - provisionExternalErr error - factoryClusterClientErr error - bootstrapClient *testClusterClient - targetClient *testClusterClient - cleanupExternal bool - expectErr bool - expectExternalExists bool - expectExternalCreated bool - expectedInternalClusters int - expectedInternalMachines int + testcases := []struct { + name string + provisionExternalErr error + factoryClusterClientErr error + bootstrapClient *testClusterClient + targetClient *testClusterClient + namespaceToInputCluster map[string][]*clusterv1.Cluster + cleanupExternal bool + expectErr bool + expectExternalExists bool + expectExternalCreated bool + namespaceToExpectedInternalMachines map[string]int + expectedTotalInternalClustersCount int // across all namespaces }{ { - name: "success", - targetClient: &testClusterClient{}, - bootstrapClient: &testClusterClient{}, - cleanupExternal: true, - expectExternalExists: false, - expectExternalCreated: true, - expectedInternalClusters: 1, - expectedInternalMachines: 2, + name: "success one cluster one namespace", + bootstrapClient: &testClusterClient{}, + targetClient: &testClusterClient{}, + cleanupExternal: true, + expectExternalExists: false, + expectExternalCreated: true, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{ + "foo": getClustersForNamespace("foo", 1), + }, + expectedTotalInternalClustersCount: 1, + }, + { + name: "success 1 clusters per namespace with 3 namespaces", + bootstrapClient: &testClusterClient{}, + targetClient: &testClusterClient{}, + cleanupExternal: true, + expectExternalExists: false, + expectExternalCreated: true, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{ + "foo": getClustersForNamespace("foo", 1), + "bar": getClustersForNamespace("bar", 1), + "baz": getClustersForNamespace("baz", 1), + }, + expectedTotalInternalClustersCount: 3, }, { - name: "success no cleaning bootstrap", - targetClient: &testClusterClient{}, - bootstrapClient: &testClusterClient{}, - cleanupExternal: false, - expectExternalExists: true, - expectExternalCreated: true, - expectedInternalClusters: 1, - expectedInternalMachines: 2, + name: "success no cleaning bootstrap", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: false, + expectExternalExists: true, + expectExternalCreated: true, + expectedTotalInternalClustersCount: 1, }, { - name: "fail provision bootstrap cluster", - targetClient: &testClusterClient{}, - bootstrapClient: &testClusterClient{}, - provisionExternalErr: fmt.Errorf("Test failure"), - expectErr: true, + name: "success create cluster with \"\" namespace and bootstrapClientContext namespace", + bootstrapClient: &testClusterClient{contextNamespace: "foo"}, + targetClient: &testClusterClient{}, + cleanupExternal: true, + expectExternalExists: false, + expectExternalCreated: true, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{ + "": getClustersForNamespace("", 1), + }, + expectedTotalInternalClustersCount: 1, }, { - name: "fail create clients", - targetClient: &testClusterClient{}, - bootstrapClient: &testClusterClient{}, - cleanupExternal: true, - expectExternalCreated: true, - factoryClusterClientErr: fmt.Errorf("Test failure"), - expectErr: true, + name: "success cluster with \"\" namespace and \"\" bootstrapClientContext namespace", + bootstrapClient: &testClusterClient{}, + targetClient: &testClusterClient{}, + cleanupExternal: true, + expectExternalExists: false, + expectExternalCreated: true, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{ + "": getClustersForNamespace("", 1), + }, + expectedTotalInternalClustersCount: 1, }, { - name: "fail apply yaml to bootstrap cluster", - targetClient: &testClusterClient{}, - bootstrapClient: &testClusterClient{ApplyErr: fmt.Errorf("Test failure")}, - cleanupExternal: true, - expectExternalCreated: true, - expectErr: true, + name: "fail provision multiple clusters in a namespace", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{"foo": getClustersForNamespace("foo", 3)}, + expectErr: true, + cleanupExternal: true, + expectExternalExists: false, + expectExternalCreated: true, }, { - name: "fail waiting for api ready on bootstrap cluster", - targetClient: &testClusterClient{}, - bootstrapClient: &testClusterClient{WaitForClusterV1alpha1ReadyErr: fmt.Errorf("Test failure")}, - cleanupExternal: true, - expectExternalCreated: true, - expectErr: true, + name: "fail provision bootstrap cluster", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + provisionExternalErr: fmt.Errorf("Test failure"), + expectErr: true, }, { - name: "fail getting bootstrap cluster objects", - targetClient: &testClusterClient{}, - bootstrapClient: &testClusterClient{GetClusterObjectsErr: fmt.Errorf("Test failure")}, - cleanupExternal: true, - expectExternalCreated: true, - expectErr: true, + name: "fail provision bootstrap cluster", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + provisionExternalErr: fmt.Errorf("Test failure"), + expectErr: true, }, { - name: "fail getting bootstrap machine objects", - targetClient: &testClusterClient{}, - bootstrapClient: &testClusterClient{GetMachineObjectsErr: fmt.Errorf("Test failure")}, - cleanupExternal: true, - expectExternalCreated: true, - expectErr: true, + name: "fail create clients", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + factoryClusterClientErr: fmt.Errorf("Test failure"), + expectErr: true, }, { - name: "fail create cluster", - targetClient: &testClusterClient{}, - bootstrapClient: &testClusterClient{CreateClusterObjectErr: fmt.Errorf("Test failure")}, - cleanupExternal: true, - expectExternalCreated: true, - expectErr: true, + name: "fail apply yaml to bootstrap cluster", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{ApplyErr: fmt.Errorf("Test failure")}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, }, { - name: "fail create master", - targetClient: &testClusterClient{}, - bootstrapClient: &testClusterClient{CreateMachineObjectsErr: fmt.Errorf("Test failure")}, - cleanupExternal: true, - expectExternalCreated: true, - expectErr: true, + name: "fail waiting for api ready on bootstrap cluster", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{WaitForClusterV1alpha1ReadyErr: fmt.Errorf("Test failure")}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, }, { - name: "fail update bootstrap cluster endpoint", - targetClient: &testClusterClient{}, - bootstrapClient: &testClusterClient{UpdateClusterObjectEndpointErr: fmt.Errorf("Test failure")}, - cleanupExternal: true, - expectExternalCreated: true, - expectErr: true, + name: "fail getting bootstrap cluster objects", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{GetClusterObjectsInNamespaceErr: fmt.Errorf("Test failure")}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, }, { - name: "fail apply yaml to target cluster", - targetClient: &testClusterClient{ApplyErr: fmt.Errorf("Test failure")}, - bootstrapClient: &testClusterClient{}, - cleanupExternal: true, - expectExternalCreated: true, - expectErr: true, + name: "fail getting bootstrap machine objects", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{GetMachineObjectsInNamespaceErr: fmt.Errorf("Test failure")}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, }, { - name: "fail wait for api ready on target cluster", - targetClient: &testClusterClient{WaitForClusterV1alpha1ReadyErr: fmt.Errorf("Test failure")}, - bootstrapClient: &testClusterClient{}, - cleanupExternal: true, - expectExternalCreated: true, - expectErr: true, + name: "fail create cluster", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{CreateClusterObjectErr: fmt.Errorf("Test failure")}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, }, { - name: "fail create target cluster", - targetClient: &testClusterClient{CreateClusterObjectErr: fmt.Errorf("Test failure")}, - bootstrapClient: &testClusterClient{}, - cleanupExternal: true, - expectExternalCreated: true, - expectErr: true, + name: "fail create master", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{CreateMachineObjectsErr: fmt.Errorf("Test failure")}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, }, { - name: "fail create nodes", - targetClient: &testClusterClient{CreateMachineObjectsErr: fmt.Errorf("Test failure")}, - bootstrapClient: &testClusterClient{}, - cleanupExternal: true, - expectExternalCreated: true, - expectedInternalClusters: 1, - expectErr: true, + name: "fail update bootstrap cluster endpoint", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{UpdateClusterObjectEndpointErr: fmt.Errorf("Test failure")}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, }, { - name: "fail update cluster endpoint target", - targetClient: &testClusterClient{UpdateClusterObjectEndpointErr: fmt.Errorf("Test failure")}, - bootstrapClient: &testClusterClient{}, - cleanupExternal: true, - expectExternalCreated: true, - expectedInternalClusters: 1, - expectedInternalMachines: 1, - expectErr: true, + name: "fail apply yaml to target cluster", + targetClient: &testClusterClient{ApplyErr: fmt.Errorf("Test failure")}, + bootstrapClient: &testClusterClient{}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, + }, + { + name: "fail wait for api ready on target cluster", + targetClient: &testClusterClient{WaitForClusterV1alpha1ReadyErr: fmt.Errorf("Test failure")}, + bootstrapClient: &testClusterClient{}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, + }, + { + name: "fail create target cluster", + targetClient: &testClusterClient{CreateClusterObjectErr: fmt.Errorf("Test failure")}, + bootstrapClient: &testClusterClient{}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, + }, + { + name: "fail create nodes", + targetClient: &testClusterClient{CreateMachineObjectsErr: fmt.Errorf("Test failure")}, + bootstrapClient: &testClusterClient{}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, + }, + { + name: "fail update cluster endpoint target", + targetClient: &testClusterClient{UpdateClusterObjectEndpointErr: fmt.Errorf("Test failure")}, + bootstrapClient: &testClusterClient{}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1)}, + cleanupExternal: true, + expectExternalCreated: true, + expectErr: true, }, } + for _, testcase := range testcases { + t.Run(testcase.name, func(t *testing.T) { kubeconfigOut := newTempFile(t) defer os.Remove(kubeconfigOut) @@ -398,40 +581,66 @@ func TestCreate(t *testing.T) { f.clusterClients[targetKubeconfig] = testcase.targetClient f.ClusterClientErr = testcase.factoryClusterClientErr - // Create - inputCluster := &clusterv1.Cluster{} - inputCluster.Name = "test-cluster" - inputMachines := generateMachines() pcStore := mockProviderComponentsStore{} pcFactory := mockProviderComponentsStoreFactory{NewFromCoreclientsetPCStore: &pcStore} d := New(p, f, "", "", testcase.cleanupExternal) - err := d.Create(inputCluster, inputMachines, pd, kubeconfigOut, &pcFactory) + inputMachines := generateMachines() - // Validate - if (testcase.expectErr && err == nil) || (!testcase.expectErr && err != nil) { - t.Fatalf("Unexpected returned error. Got: %v, Want Err: %v", err, testcase.expectErr) - } - if testcase.expectExternalExists != p.clusterExists { - t.Errorf("Unexpected bootstrap cluster existance. Got: %v, Want: %v", p.clusterExists, testcase.expectExternalExists) - } - if testcase.expectExternalCreated != p.clusterCreated { - t.Errorf("Unexpected bootstrap cluster provisioning. Got: %v, Want: %v", p.clusterCreated, testcase.expectExternalCreated) - } - if testcase.expectedInternalClusters != len(testcase.targetClient.clusters) { - t.Fatalf("Unexpected cluster count. Got: %v, Want: %v", len(testcase.targetClient.clusters), testcase.expectedInternalClusters) - } - if testcase.expectedInternalClusters > 1 && inputCluster.Name != testcase.targetClient.clusters[0].Name { - t.Errorf("Provisioned cluster has unexpected name. Got: %v, Want: %v", testcase.targetClient.clusters[0].Name, inputCluster.Name) - } + for namespace, inputClusters := range testcase.namespaceToInputCluster { + ns := namespace + if ns == "" { + ns = testcase.bootstrapClient.GetContextNamespace() + } - if testcase.expectedInternalMachines != len(testcase.targetClient.machines) { - t.Fatalf("Unexpected machine count. Got: %v, Want: %v", len(testcase.targetClient.machines), testcase.expectedInternalMachines) + var err error + for _, inputCluster := range inputClusters { + inputCluster.Name = fmt.Sprintf("%s-cluster", ns) + err = d.Create(inputCluster, inputMachines, pd, kubeconfigOut, &pcFactory) + if err != nil { + break + } + } + if (testcase.expectErr && err == nil) || (!testcase.expectErr && err != nil) { + t.Fatalf("Unexpected error returned. Got: %v, Want Err: %v", err, testcase.expectErr) + } + if testcase.expectExternalExists != p.clusterExists { + t.Errorf("Unexpected bootstrap cluster existence. Got: %v, Want: %v", p.clusterExists, testcase.expectExternalExists) + } + if testcase.expectExternalCreated != p.clusterCreated { + t.Errorf("Unexpected bootstrap cluster provisioning. Got: %v, Want: %v", p.clusterCreated, testcase.expectExternalCreated) + } + testcase.namespaceToExpectedInternalMachines[ns] = len(inputMachines) } - if testcase.expectedInternalMachines == len(inputMachines) { - for i := range inputMachines { - if inputMachines[i].Name != testcase.targetClient.machines[i].Name { - t.Fatalf("Unexpected machine name at %v. Got: %v, Want: %v", i, inputMachines[i].Name, testcase.targetClient.machines[i].Name) + + if !testcase.expectErr { + // Validate namespaces + for namespace := range testcase.namespaceToInputCluster { + ns := namespace + if ns == "" { + ns = testcase.bootstrapClient.GetContextNamespace() + } + if len(testcase.targetClient.clusters[ns]) != 1 { + t.Fatalf("Unexpected cluster count in namespace %q. Got: %d, Want: %d", ns, len(testcase.targetClient.clusters[ns]), 1) } + + expectedClusterName := fmt.Sprintf("%s-cluster", ns) + if testcase.targetClient.clusters[ns][0].Name != expectedClusterName { + t.Fatalf("Unexpected cluster name in namespace %q. Got: %q, Want: %q", ns, testcase.targetClient.clusters[ns][0].Name, expectedClusterName) + } + + if len(testcase.targetClient.machines[ns]) != testcase.namespaceToExpectedInternalMachines[ns] { + t.Fatalf("Unexpected machine count in namespace %q. Got: %d, Want: %d", ns, len(testcase.targetClient.machines[ns]), testcase.namespaceToExpectedInternalMachines[ns]) + } + + for i := range inputMachines { + if inputMachines[i].Name != testcase.targetClient.machines[ns][i].Name { + t.Fatalf("Unexpected machine name at %v in namespace %q. Got: %v, Want: %v", i, ns, inputMachines[i].Name, testcase.targetClient.machines[ns][i].Name) + } + } + } + // Validate across all namespaces + if len(testcase.targetClient.clusters) != testcase.expectedTotalInternalClustersCount { + t.Fatalf("Unexpected cluster count across all namespaces. Got: %d, Want: %d", len(testcase.targetClient.clusters), testcase.expectedTotalInternalClustersCount) } } }) @@ -555,16 +764,17 @@ func TestDeleteCleanupExternalCluster(t *testing.T) { testCases := []struct { name string + namespace string cleanupExternalCluster bool provisionExternalErr error bootstrapClient *testClusterClient targetClient *testClusterClient expectedErrorMessage string }{ - {"success with cleanup", true, nil, &testClusterClient{}, &testClusterClient{}, ""}, - {"success without cleanup", false, nil, &testClusterClient{}, &testClusterClient{}, ""}, - {"error with cleanup", true, nil, &testClusterClient{}, &testClusterClient{GetMachineSetObjectsErr: fmt.Errorf("get machine sets error")}, "unable to copy objects from target to bootstrap cluster: get machine sets error"}, - {"error without cleanup", true, nil, &testClusterClient{}, &testClusterClient{GetMachineSetObjectsErr: fmt.Errorf("get machine sets error")}, "unable to copy objects from target to bootstrap cluster: get machine sets error"}, + {"success with cleanup", metav1.NamespaceDefault, true, nil, &testClusterClient{}, &testClusterClient{}, ""}, + {"success without cleanup", metav1.NamespaceDefault, false, nil, &testClusterClient{}, &testClusterClient{}, ""}, + {"error with cleanup", metav1.NamespaceDefault, true, nil, &testClusterClient{}, &testClusterClient{GetMachineSetObjectsInNamespaceErr: fmt.Errorf("get machine sets error")}, "unable to copy objects from target to bootstrap cluster: get machine sets error"}, + {"error without cleanup", metav1.NamespaceDefault, true, nil, &testClusterClient{}, &testClusterClient{GetMachineSetObjectsInNamespaceErr: fmt.Errorf("get machine sets error")}, "unable to copy objects from target to bootstrap cluster: get machine sets error"}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { @@ -575,10 +785,10 @@ func TestDeleteCleanupExternalCluster(t *testing.T) { f.clusterClients[bootstrapKubeconfig] = tc.bootstrapClient f.clusterClients[targetKubeconfig] = tc.targetClient d := New(p, f, "", "", tc.cleanupExternalCluster) - err := d.Delete(tc.targetClient) + err := d.Delete(tc.targetClient, tc.namespace) if err != nil || tc.expectedErrorMessage != "" { if err == nil { - t.Errorf("expected error") + t.Errorf("expected error %q", tc.expectedErrorMessage) } else if err.Error() != tc.expectedErrorMessage { t.Errorf("Unexpected error: got '%v', want: '%v'", err, tc.expectedErrorMessage) } @@ -590,53 +800,310 @@ func TestDeleteCleanupExternalCluster(t *testing.T) { } } -func TestDeleteBasicScenarios(t *testing.T) { +func TestDeleteClusters(t *testing.T) { const bootstrapKubeconfig = "bootstrap" const targetKubeconfig = "target" testCases := []struct { - name string - provisionExternalErr error - NewCoreClientsetErr error - bootstrapClient *testClusterClient - targetClient *testClusterClient - expectedErrorMessage string + name string + namespace string + provisionExternalErr error + NewCoreClientsetErr error + bootstrapClient *testClusterClient + targetClient *testClusterClient + expectedErrorMessage string + expectedExternalClusterCount int + expectError bool }{ - {"success", nil, nil, &testClusterClient{}, &testClusterClient{}, ""}, - {"error creating core client", nil, fmt.Errorf("error creating core client"), &testClusterClient{}, &testClusterClient{}, "could not create bootstrap cluster: unable to create bootstrap client: error creating core client"}, - {"fail provision bootstrap cluster", fmt.Errorf("minikube error"), nil, &testClusterClient{}, &testClusterClient{}, "could not create bootstrap cluster: could not create bootstrap control plane: minikube error"}, - {"fail apply yaml to bootstrap cluster", nil, nil, &testClusterClient{ApplyErr: fmt.Errorf("yaml apply error")}, &testClusterClient{}, "unable to apply cluster api stack to bootstrap cluster: unable to apply cluster apiserver: unable to apply apiserver yaml: yaml apply error"}, - {"fail delete provider components should succeed", nil, nil, &testClusterClient{}, &testClusterClient{DeleteErr: fmt.Errorf("kubectl delete error")}, ""}, - {"error listing machines", nil, nil, &testClusterClient{}, &testClusterClient{GetMachineObjectsErr: fmt.Errorf("get machines error")}, "unable to copy objects from target to bootstrap cluster: get machines error"}, - {"error listing machine sets", nil, nil, &testClusterClient{}, &testClusterClient{GetMachineSetObjectsErr: fmt.Errorf("get machine sets error")}, "unable to copy objects from target to bootstrap cluster: get machine sets error"}, - {"error listing machine deployments", nil, nil, &testClusterClient{}, &testClusterClient{GetMachineDeploymentObjectsErr: fmt.Errorf("get machine deployments error")}, "unable to copy objects from target to bootstrap cluster: get machine deployments error"}, - {"error listing clusters", nil, nil, &testClusterClient{}, &testClusterClient{GetClusterObjectsErr: fmt.Errorf("get clusters error")}, "unable to copy objects from target to bootstrap cluster: get clusters error"}, - {"error creating machines", nil, nil, &testClusterClient{CreateMachineObjectsErr: fmt.Errorf("create machines error")}, &testClusterClient{machines: generateMachines()}, "unable to copy objects from target to bootstrap cluster: error moving Machine 'test-master': create machines error"}, - {"error creating machine sets", nil, nil, &testClusterClient{CreateMachineSetObjectsErr: fmt.Errorf("create machine sets error")}, &testClusterClient{machineSets: newMachineSetsFixture()}, "unable to copy objects from target to bootstrap cluster: error moving MachineSet 'machine-set-name-1': create machine sets error"}, - {"error creating machine deployments", nil, nil, &testClusterClient{CreateMachineDeploymentsObjectsErr: fmt.Errorf("create machine deployments error")}, &testClusterClient{machineDeployments: newMachineDeploymentsFixture()}, "unable to copy objects from target to bootstrap cluster: error moving MachineDeployment 'machine-deployment-name-1': create machine deployments error"}, - {"error creating cluster", nil, nil, &testClusterClient{CreateClusterObjectErr: fmt.Errorf("create cluster error")}, &testClusterClient{clusters: newClustersFixture()}, "unable to copy objects from target to bootstrap cluster: error moving Cluster 'cluster-name-1': create cluster error"}, - {"error deleting machines", nil, nil, &testClusterClient{DeleteMachineObjectsErr: fmt.Errorf("delete machines error")}, &testClusterClient{}, "unable to finish deleting objects in bootstrap cluster, resources may have been leaked: error(s) encountered deleting objects from bootstrap cluster: [error deleting machines: delete machines error]"}, - {"error deleting machine sets", nil, nil, &testClusterClient{DeleteMachineSetObjectsErr: fmt.Errorf("delete machine sets error")}, &testClusterClient{}, "unable to finish deleting objects in bootstrap cluster, resources may have been leaked: error(s) encountered deleting objects from bootstrap cluster: [error deleting machine sets: delete machine sets error]"}, - {"error deleting machine deployments", nil, nil, &testClusterClient{DeleteMachineDeploymentsObjectsErr: fmt.Errorf("delete machine deployments error")}, &testClusterClient{}, "unable to finish deleting objects in bootstrap cluster, resources may have been leaked: error(s) encountered deleting objects from bootstrap cluster: [error deleting machine deployments: delete machine deployments error]"}, - {"error deleting clusters", nil, nil, &testClusterClient{DeleteClusterObjectsErr: fmt.Errorf("delete clusters error")}, &testClusterClient{}, "unable to finish deleting objects in bootstrap cluster, resources may have been leaked: error(s) encountered deleting objects from bootstrap cluster: [error deleting clusters: delete clusters error]"}, - {"error deleting machines and clusters", nil, nil, &testClusterClient{DeleteMachineObjectsErr: fmt.Errorf("delete machines error"), DeleteClusterObjectsErr: fmt.Errorf("delete clusters error")}, &testClusterClient{}, "unable to finish deleting objects in bootstrap cluster, resources may have been leaked: error(s) encountered deleting objects from bootstrap cluster: [error deleting machines: delete machines error, error deleting clusters: delete clusters error]"}, + { + name: "success delete 1/1 cluster, 0 clusters remaining", + namespace: metav1.NamespaceDefault, + bootstrapClient: &testClusterClient{ + clusters: map[string][]*clusterv1.Cluster{ + metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1), + }, + machines: map[string][]*clusterv1.Machine{ + metav1.NamespaceDefault: generateMachines(), + }, + machineSets: map[string][]*clusterv1.MachineSet{ + metav1.NamespaceDefault: newMachineSetsFixture(), + }, + machineDeployments: map[string][]*clusterv1.MachineDeployment{ + metav1.NamespaceDefault: newMachineDeploymentsFixture(), + }, + }, + targetClient: &testClusterClient{ + clusters: map[string][]*clusterv1.Cluster{ + metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1), + }, + machines: map[string][]*clusterv1.Machine{ + metav1.NamespaceDefault: generateMachines(), + }, + machineSets: map[string][]*clusterv1.MachineSet{ + metav1.NamespaceDefault: newMachineSetsFixture(), + }, + machineDeployments: map[string][]*clusterv1.MachineDeployment{ + metav1.NamespaceDefault: newMachineDeploymentsFixture(), + }, + }, + expectedExternalClusterCount: 0, + }, + { + name: "success delete 1/3 clusters, 2 clusters remaining", + namespace: "foo", + bootstrapClient: &testClusterClient{ + clusters: map[string][]*clusterv1.Cluster{ + "foo": getClustersForNamespace("foo", 1), + "bar": getClustersForNamespace("bar", 1), + "baz": getClustersForNamespace("baz", 1), + }, + machines: map[string][]*clusterv1.Machine{ + "foo": generateMachines(), + "bar": generateMachines(), + "baz": generateMachines(), + }, + machineSets: map[string][]*clusterv1.MachineSet{ + "foo": newMachineSetsFixture(), + "bar": newMachineSetsFixture(), + "baz": newMachineSetsFixture(), + }, + machineDeployments: map[string][]*clusterv1.MachineDeployment{ + "foo": newMachineDeploymentsFixture(), + "bar": newMachineDeploymentsFixture(), + "baz": newMachineDeploymentsFixture(), + }, + }, + targetClient: &testClusterClient{ + clusters: map[string][]*clusterv1.Cluster{ + "foo": getClustersForNamespace("foo", 1), + "bar": getClustersForNamespace("bar", 1), + "baz": getClustersForNamespace("baz", 1), + }, + machines: map[string][]*clusterv1.Machine{ + "foo": generateMachines(), + "bar": generateMachines(), + "baz": generateMachines(), + }, + machineSets: map[string][]*clusterv1.MachineSet{ + "foo": newMachineSetsFixture(), + "bar": newMachineSetsFixture(), + "baz": newMachineSetsFixture(), + }, + machineDeployments: map[string][]*clusterv1.MachineDeployment{ + "foo": newMachineDeploymentsFixture(), + "bar": newMachineDeploymentsFixture(), + "baz": newMachineDeploymentsFixture(), + }, + }, + expectedExternalClusterCount: 2, + }, + { + name: "error creating core client", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: fmt.Errorf("error creating core client"), + bootstrapClient: &testClusterClient{}, + targetClient: &testClusterClient{}, + expectedErrorMessage: "could not create bootstrap cluster: unable to create bootstrap client: error creating core client", + }, + { + name: "fail provision bootstrap cluster", + namespace: metav1.NamespaceDefault, + provisionExternalErr: fmt.Errorf("minikube error"), + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{}, + targetClient: &testClusterClient{}, + expectedErrorMessage: "could not create bootstrap cluster: could not create bootstrap control plane: minikube error", + }, + { + name: "fail apply yaml to bootstrap cluster", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{ApplyErr: fmt.Errorf("yaml apply error")}, + targetClient: &testClusterClient{}, + expectedErrorMessage: "unable to apply cluster api stack to bootstrap cluster: unable to apply cluster apiserver in namespace default: unable to apply apiserver yaml: yaml apply error", + }, + { + name: "fail delete provider components should succeed", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{}, + targetClient: &testClusterClient{DeleteErr: fmt.Errorf("kubectl delete error")}, + expectedErrorMessage: "", + }, + { + name: "error listing machines", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{}, + targetClient: &testClusterClient{GetMachineObjectsInNamespaceErr: fmt.Errorf("get machines error")}, + expectedErrorMessage: "unable to copy objects from target to bootstrap cluster: get machines error", + }, + { + name: "error listing machine sets", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{}, + targetClient: &testClusterClient{GetMachineSetObjectsInNamespaceErr: fmt.Errorf("get machine sets error")}, + expectedErrorMessage: "unable to copy objects from target to bootstrap cluster: get machine sets error", + }, + { + name: "error listing machine deployments", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{}, + targetClient: &testClusterClient{GetMachineDeploymentObjectsInNamespaceErr: fmt.Errorf("get machine deployments error")}, + expectedErrorMessage: "unable to copy objects from target to bootstrap cluster: get machine deployments error", + }, + { + name: "error listing clusters", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{}, + targetClient: &testClusterClient{GetClusterObjectsInNamespaceErr: fmt.Errorf("get clusters error")}, + expectedErrorMessage: "unable to copy objects from target to bootstrap cluster: get clusters error", + }, + { + name: "error creating machines", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{CreateMachineObjectsErr: fmt.Errorf("create machines error")}, + targetClient: &testClusterClient{ + machines: map[string][]*clusterv1.Machine{ + metav1.NamespaceDefault: generateMachines(), + }, + }, + expectedErrorMessage: "unable to copy objects from target to bootstrap cluster: error moving Machine 'test-master': create machines error", + }, + { + name: "error creating machine sets", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{CreateMachineSetObjectsErr: fmt.Errorf("create machine sets error")}, + targetClient: &testClusterClient{ + machineSets: map[string][]*clusterv1.MachineSet{ + metav1.NamespaceDefault: newMachineSetsFixture(), + }, + }, + expectedErrorMessage: "unable to copy objects from target to bootstrap cluster: error moving MachineSet 'machine-set-name-1': create machine sets error", + }, + { + name: "error creating machine deployments", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{CreateMachineDeploymentsObjectsErr: fmt.Errorf("create machine deployments error")}, + targetClient: &testClusterClient{ + machineDeployments: map[string][]*clusterv1.MachineDeployment{ + metav1.NamespaceDefault: newMachineDeploymentsFixture(), + }}, + expectedErrorMessage: "unable to copy objects from target to bootstrap cluster: error moving MachineDeployment 'machine-deployment-name-1': create machine deployments error", + }, + { + name: "error creating cluster", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{CreateClusterObjectErr: fmt.Errorf("create cluster error")}, + targetClient: &testClusterClient{ + clusters: map[string][]*clusterv1.Cluster{ + metav1.NamespaceDefault: getClustersForNamespace(metav1.NamespaceDefault, 1), + }, + }, + expectedErrorMessage: "unable to copy objects from target to bootstrap cluster: error moving Cluster 'default-cluster': create cluster error", + }, + + { + name: "error deleting machines", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{DeleteMachineObjectsInNamespaceErr: fmt.Errorf("delete machines error")}, + targetClient: &testClusterClient{}, + expectedErrorMessage: "unable to finish deleting objects in bootstrap cluster, resources may have been leaked: error(s) encountered deleting objects from bootstrap cluster: [error deleting machines: delete machines error]", + }, + { + name: "error deleting machine sets", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{DeleteMachineSetObjectsInNamespaceErr: fmt.Errorf("delete machine sets error")}, + targetClient: &testClusterClient{}, + expectedErrorMessage: "unable to finish deleting objects in bootstrap cluster, resources may have been leaked: error(s) encountered deleting objects from bootstrap cluster: [error deleting machine sets: delete machine sets error]", + }, + { + name: "error deleting machine deployments", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{DeleteMachineDeploymentsObjectsInNamespaceErr: fmt.Errorf("delete machine deployments error")}, + targetClient: &testClusterClient{}, + expectedErrorMessage: "unable to finish deleting objects in bootstrap cluster, resources may have been leaked: error(s) encountered deleting objects from bootstrap cluster: [error deleting machine deployments: delete machine deployments error]", + }, + { + name: "error deleting clusters", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{DeleteClusterObjectsInNamespaceErr: fmt.Errorf("delete clusters error")}, + targetClient: &testClusterClient{}, + expectedErrorMessage: "unable to finish deleting objects in bootstrap cluster, resources may have been leaked: error(s) encountered deleting objects from bootstrap cluster: [error deleting clusters: delete clusters error]", + }, + { + name: "error deleting machines and clusters", + namespace: metav1.NamespaceDefault, + provisionExternalErr: nil, + NewCoreClientsetErr: nil, + bootstrapClient: &testClusterClient{DeleteMachineObjectsInNamespaceErr: fmt.Errorf("delete machines error"), DeleteClusterObjectsInNamespaceErr: fmt.Errorf("delete clusters error")}, + targetClient: &testClusterClient{}, + expectedErrorMessage: "unable to finish deleting objects in bootstrap cluster, resources may have been leaked: error(s) encountered deleting objects from bootstrap cluster: [error deleting machines: delete machines error, error deleting clusters: delete clusters error]", + }, } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { kubeconfigOut := newTempFile(t) defer os.Remove(kubeconfigOut) - p := &testClusterProvisioner{err: tc.provisionExternalErr, kubeconfig: bootstrapKubeconfig} + p := &testClusterProvisioner{err: testCase.provisionExternalErr, kubeconfig: bootstrapKubeconfig} f := newTestClusterClientFactory() - f.clusterClients[bootstrapKubeconfig] = tc.bootstrapClient - f.clusterClients[targetKubeconfig] = tc.targetClient - f.ClusterClientErr = tc.NewCoreClientsetErr + f.clusterClients[bootstrapKubeconfig] = testCase.bootstrapClient + f.clusterClients[targetKubeconfig] = testCase.targetClient + f.ClusterClientErr = testCase.NewCoreClientsetErr d := New(p, f, "", "", true) - err := d.Delete(tc.targetClient) - if err != nil || tc.expectedErrorMessage != "" { + + err := d.Delete(testCase.targetClient, testCase.namespace) + if err != nil || testCase.expectedErrorMessage != "" { if err == nil { - t.Errorf("expected error") - } else if err.Error() != tc.expectedErrorMessage { - t.Errorf("Unexpected error: got '%v', want: '%v'", err, tc.expectedErrorMessage) + t.Errorf("expected error %q", testCase.expectedErrorMessage) + } else if err.Error() != testCase.expectedErrorMessage { + t.Errorf("Unexpected error: got '%v', want: '%v'", err, testCase.expectedErrorMessage) + } + } + + if !testCase.expectError { + if len(testCase.bootstrapClient.clusters[testCase.namespace]) != 0 { + t.Fatalf("Unexpected cluster count in namespace %q. Got: %d, Want: 0", testCase.namespace, len(testCase.targetClient.clusters[testCase.namespace])) + } + if len(testCase.bootstrapClient.machines[testCase.namespace]) != 0 { + t.Fatalf("Unexpected machine count in namespace %q. Got: %d, Want: 0", testCase.namespace, len(testCase.targetClient.machines[testCase.namespace])) + } + if len(testCase.bootstrapClient.machineSets[testCase.namespace]) != 0 { + t.Fatalf("Unexpected machineSets count in namespace %q. Got: %d, Want: 0", testCase.namespace, len(testCase.targetClient.machineSets[testCase.namespace])) + } + if len(testCase.bootstrapClient.machineDeployments[testCase.namespace]) != 0 { + t.Fatalf("Unexpected machineDeployments count in namespace %q. Got: %d, Want: 0", testCase.namespace, len(testCase.targetClient.machineDeployments[testCase.namespace])) + } + + if len(testCase.bootstrapClient.clusters) != testCase.expectedExternalClusterCount { + t.Fatalf("Unexpected remaining cluster count. Got: %d, Want: %d", len(testCase.bootstrapClient.clusters), testCase.expectedExternalClusterCount) } } }) @@ -707,6 +1174,19 @@ func newMachineSetsFixture() []*clusterv1.MachineSet { } } +func getClustersForNamespace(namespace string, count int) []*clusterv1.Cluster { + var clusters []*clusterv1.Cluster + for i := 0; i < count; i++ { + clusters = append(clusters, &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-cluster", namespace), + Namespace: namespace, + }, + }) + } + return clusters +} + func newMachineDeploymentsFixture() []*clusterv1.MachineDeployment { return []*clusterv1.MachineDeployment{ &clusterv1.MachineDeployment{ObjectMeta: metav1.ObjectMeta{Name: "machine-deployment-name-1"}}, diff --git a/clusterctl/cmd/delete_cluster.go b/clusterctl/cmd/delete_cluster.go index a5feeb1ad8ea..d3bf7e3a65bd 100644 --- a/clusterctl/cmd/delete_cluster.go +++ b/clusterctl/cmd/delete_cluster.go @@ -33,6 +33,7 @@ import ( type DeleteOptions struct { KubeconfigPath string ProviderComponents string + ClusterNamespace string KubeconfigOverrides tcmd.ConfigOverrides } @@ -52,6 +53,7 @@ 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.") + deleteClusterCmd.Flags().StringVarP(&do.ClusterNamespace, "cluster-namespace", "", v1.NamespaceDefault, "Namespace where the cluster to be deleted resides") // BindContextFlags will bind the flags cluster, namespace, and user tcmd.BindContextFlags(&do.KubeconfigOverrides.Context, deleteClusterCmd.Flags(), tcmd.RecommendedContextOverrideFlags("")) deleteCmd.AddCommand(deleteClusterCmd) @@ -73,7 +75,7 @@ func RunDelete() error { providerComponents, "", true) - return deployer.Delete(clusterClient) + return deployer.Delete(clusterClient, do.ClusterNamespace) } func loadProviderComponents() (string, error) { diff --git a/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden b/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden index 4c9a68a69a83..62cf1d8e9b34 100644 --- a/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden +++ b/clusterctl/testdata/delete-cluster-no-args-invalid-flag.golden @@ -4,6 +4,7 @@ Usage: Flags: --cluster string The name of the kubeconfig cluster to use + --cluster-namespace string Namespace where the cluster to be deleted resides (default "default") -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 diff --git a/vendor/k8s.io/kube-openapi/example/openapi-gen/BUILD.bazel b/vendor/k8s.io/kube-openapi/example/openapi-gen/BUILD.bazel new file mode 100644 index 000000000000..ae6a730f5f1b --- /dev/null +++ b/vendor/k8s.io/kube-openapi/example/openapi-gen/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "go_default_library", + srcs = ["main.go"], + importmap = "sigs.k8s.io/cluster-api/vendor/k8s.io/kube-openapi/example/openapi-gen", + importpath = "k8s.io/kube-openapi/example/openapi-gen", + visibility = ["//visibility:private"], + deps = [ + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/k8s.io/gengo/args:go_default_library", + "//vendor/k8s.io/kube-openapi/pkg/generators:go_default_library", + ], +) + +go_binary( + name = "openapi-gen", + embed = [":go_default_library"], + visibility = ["//visibility:public"], +) diff --git a/vendor/k8s.io/kube-openapi/pkg/aggregator/BUILD.bazel b/vendor/k8s.io/kube-openapi/pkg/aggregator/BUILD.bazel new file mode 100644 index 000000000000..7b2f766908d0 --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/aggregator/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["aggregator.go"], + importmap = "sigs.k8s.io/cluster-api/vendor/k8s.io/kube-openapi/pkg/aggregator", + importpath = "k8s.io/kube-openapi/pkg/aggregator", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/go-openapi/spec:go_default_library", + "//vendor/k8s.io/kube-openapi/pkg/util:go_default_library", + ], +) diff --git a/vendor/k8s.io/kube-openapi/pkg/builder/BUILD.bazel b/vendor/k8s.io/kube-openapi/pkg/builder/BUILD.bazel new file mode 100644 index 000000000000..6ebd45516f3e --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/builder/BUILD.bazel @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "openapi.go", + "util.go", + ], + importmap = "sigs.k8s.io/cluster-api/vendor/k8s.io/kube-openapi/pkg/builder", + importpath = "k8s.io/kube-openapi/pkg/builder", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/emicklei/go-restful:go_default_library", + "//vendor/github.com/go-openapi/spec:go_default_library", + "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library", + "//vendor/k8s.io/kube-openapi/pkg/util:go_default_library", + ], +) diff --git a/vendor/k8s.io/kube-openapi/pkg/common/BUILD.bazel b/vendor/k8s.io/kube-openapi/pkg/common/BUILD.bazel new file mode 100644 index 000000000000..a99ba9422c42 --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/common/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "common.go", + "doc.go", + ], + importmap = "sigs.k8s.io/cluster-api/vendor/k8s.io/kube-openapi/pkg/common", + importpath = "k8s.io/kube-openapi/pkg/common", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/emicklei/go-restful:go_default_library", + "//vendor/github.com/go-openapi/spec:go_default_library", + ], +) diff --git a/vendor/k8s.io/kube-openapi/pkg/generators/BUILD.bazel b/vendor/k8s.io/kube-openapi/pkg/generators/BUILD.bazel new file mode 100644 index 000000000000..ca377bc0fabb --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/generators/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["openapi.go"], + importmap = "sigs.k8s.io/cluster-api/vendor/k8s.io/kube-openapi/pkg/generators", + importpath = "k8s.io/kube-openapi/pkg/generators", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/k8s.io/gengo/args:go_default_library", + "//vendor/k8s.io/gengo/generator:go_default_library", + "//vendor/k8s.io/gengo/namer:go_default_library", + "//vendor/k8s.io/gengo/types:go_default_library", + "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library", + ], +) diff --git a/vendor/k8s.io/kube-openapi/pkg/handler/BUILD.bazel b/vendor/k8s.io/kube-openapi/pkg/handler/BUILD.bazel new file mode 100644 index 000000000000..8830f340e6b1 --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/handler/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["handler.go"], + importmap = "sigs.k8s.io/cluster-api/vendor/k8s.io/kube-openapi/pkg/handler", + importpath = "k8s.io/kube-openapi/pkg/handler", + visibility = ["//visibility:public"], + deps = [ + "//vendor/bitbucket.org/ww/goautoneg:go_default_library", + "//vendor/github.com/NYTimes/gziphandler:go_default_library", + "//vendor/github.com/emicklei/go-restful:go_default_library", + "//vendor/github.com/go-openapi/spec:go_default_library", + "//vendor/github.com/golang/protobuf/proto:go_default_library", + "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", + "//vendor/github.com/googleapis/gnostic/compiler:go_default_library", + "//vendor/gopkg.in/yaml.v2:go_default_library", + "//vendor/k8s.io/kube-openapi/pkg/builder:go_default_library", + "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library", + ], +) diff --git a/vendor/k8s.io/kube-openapi/pkg/util/BUILD.bazel b/vendor/k8s.io/kube-openapi/pkg/util/BUILD.bazel new file mode 100644 index 000000000000..b128259133db --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/util/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "trie.go", + "util.go", + ], + importmap = "sigs.k8s.io/cluster-api/vendor/k8s.io/kube-openapi/pkg/util", + importpath = "k8s.io/kube-openapi/pkg/util", + visibility = ["//visibility:public"], +) diff --git a/vendor/k8s.io/kube-openapi/pkg/util/proto/BUILD.bazel b/vendor/k8s.io/kube-openapi/pkg/util/proto/BUILD.bazel new file mode 100644 index 000000000000..cd7198395536 --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/util/proto/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "document.go", + "openapi.go", + ], + importmap = "sigs.k8s.io/cluster-api/vendor/k8s.io/kube-openapi/pkg/util/proto", + importpath = "k8s.io/kube-openapi/pkg/util/proto", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", + "//vendor/gopkg.in/yaml.v2:go_default_library", + ], +) diff --git a/vendor/k8s.io/kube-openapi/pkg/util/proto/testing/BUILD.bazel b/vendor/k8s.io/kube-openapi/pkg/util/proto/testing/BUILD.bazel new file mode 100644 index 000000000000..573d9c08f671 --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/util/proto/testing/BUILD.bazel @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["openapi.go"], + importmap = "sigs.k8s.io/cluster-api/vendor/k8s.io/kube-openapi/pkg/util/proto/testing", + importpath = "k8s.io/kube-openapi/pkg/util/proto/testing", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", + "//vendor/github.com/googleapis/gnostic/compiler:go_default_library", + "//vendor/gopkg.in/yaml.v2:go_default_library", + ], +) diff --git a/vendor/k8s.io/kube-openapi/pkg/util/proto/validation/BUILD.bazel b/vendor/k8s.io/kube-openapi/pkg/util/proto/validation/BUILD.bazel new file mode 100644 index 000000000000..ecf6808d7c13 --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/util/proto/validation/BUILD.bazel @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "errors.go", + "types.go", + "validation.go", + ], + importmap = "sigs.k8s.io/cluster-api/vendor/k8s.io/kube-openapi/pkg/util/proto/validation", + importpath = "k8s.io/kube-openapi/pkg/util/proto/validation", + visibility = ["//visibility:public"], + deps = ["//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library"], +) From 6f3b1244ac51ee48cf6cfe1b84ae0c7f0686bf72 Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Thu, 23 Aug 2018 17:46:26 -0700 Subject: [PATCH 2/4] implement methods to handle namespace create and delete --- clusterctl/clusterdeployer/BUILD.bazel | 1 + clusterctl/clusterdeployer/clusterclient.go | 34 +++++++- clusterctl/clusterdeployer/clusterdeployer.go | 31 ++++++-- .../clusterdeployer/clusterdeployer_test.go | 77 ++++++++++++++++++- 4 files changed, 132 insertions(+), 11 deletions(-) diff --git a/clusterctl/clusterdeployer/BUILD.bazel b/clusterctl/clusterdeployer/BUILD.bazel index 5031838efba9..0fbf7b75fe98 100644 --- a/clusterctl/clusterdeployer/BUILD.bazel +++ b/clusterctl/clusterdeployer/BUILD.bazel @@ -19,6 +19,7 @@ go_library( "//pkg/util:go_default_library", "//vendor/github.com/golang/glog:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/tools/clientcmd:go_default_library", diff --git a/clusterctl/clusterdeployer/clusterclient.go b/clusterctl/clusterdeployer/clusterclient.go index 8931b4863ae9..e1bf73a4eadd 100644 --- a/clusterctl/clusterdeployer/clusterclient.go +++ b/clusterctl/clusterdeployer/clusterclient.go @@ -25,6 +25,7 @@ import ( "time" apiv1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" 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" @@ -73,6 +74,37 @@ func (c *clusterClient) removeKubeconfigFile() error { return os.Remove(c.kubeconfigFile) } +func (c *clusterClient) EnsureNamespace(namespaceName string) error { + clientset, err := clientcmd.NewCoreClientSetForKubeconfig(c.kubeconfigFile) + if err != nil { + return fmt.Errorf("error creating core clientset: %v", err) + } + + namespace := apiv1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespaceName, + }, + } + _, err = clientset.CoreV1().Namespaces().Create(&namespace) + if err != nil && !apierrors.IsAlreadyExists(err) { + return err + } + return nil +} + +func (c *clusterClient) DeleteNamespace(namespaceName string) error { + clientset, err := clientcmd.NewCoreClientSetForKubeconfig(c.kubeconfigFile) + if err != nil { + return fmt.Errorf("error creating core clientset: %v", err) + } + + err = clientset.CoreV1().Namespaces().Delete(namespaceName, &metav1.DeleteOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + return err + } + return nil +} + // NewClusterClientFromDefaultSearchPath creates and returns the address of a clusterClient, the kubeconfigFile argument is expected to be the path to a // valid kubeconfig file. func NewClusterClientFromDefaultSearchPath(kubeconfigFile string, overrides tcmd.ConfigOverrides) (*clusterClient, error) { @@ -197,7 +229,7 @@ func (c *clusterClient) GetMachineObjects() ([]*clusterv1.Machine, error) { } func (c *clusterClient) CreateClusterObject(cluster *clusterv1.Cluster) error { - namespace := c.configOverrides.Context.Namespace + namespace := c.GetContextNamespace() if cluster.Namespace != "" { namespace = cluster.Namespace } diff --git a/clusterctl/clusterdeployer/clusterdeployer.go b/clusterctl/clusterdeployer/clusterdeployer.go index 8dbd3f9792eb..3c12bcc27b87 100644 --- a/clusterctl/clusterdeployer/clusterdeployer.go +++ b/clusterctl/clusterdeployer/clusterdeployer.go @@ -76,6 +76,8 @@ type ClusterClient interface { DeleteMachineObjectsInNamespace(string) error DeleteMachineObjects() error UpdateClusterObjectEndpoint(string, string, string) error + EnsureNamespace(string) error + DeleteNamespace(string) error Close() error } @@ -140,7 +142,11 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster if cluster.Namespace == "" { cluster.Namespace = bootstrapClient.GetContextNamespace() } - // TODO: @ashisha Create cluster.Namespace in bootstrap and target clusters? + + err = bootstrapClient.EnsureNamespace(cluster.Namespace) + if err != nil { + return fmt.Errorf("unable to ensure namespace %q in bootstrap cluster: %v", cluster.Namespace, err) + } glog.Info("Applying Cluster API stack to bootstrap cluster") if err := d.applyClusterAPIStack(bootstrapClient, cluster.Namespace); err != nil { @@ -165,7 +171,7 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster } glog.Info("Creating target cluster") - targetClient, err := d.createTargetCluster(bootstrapClient, provider, kubeconfigOutput, cluster.Name, cluster.Namespace) + targetClient, err := d.createTargetClusterClient(bootstrapClient, provider, kubeconfigOutput, cluster.Name, cluster.Namespace) if err != nil { return fmt.Errorf("unable to create target cluster: %v", err) } @@ -182,6 +188,11 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster return fmt.Errorf("unable to save provider components to target cluster: %v", err) } + err = targetClient.EnsureNamespace(cluster.Namespace) + if err != nil { + return fmt.Errorf("unable to ensure namespace %q in targetCluster: %v", cluster.Namespace, err) + } + // For some reason, endpoint doesn't get updated in bootstrap cluster sometimes. So we // update the target cluster endpoint as well to be sure. glog.Infof("Updating target cluster object with master (%s) endpoint", master.Name) @@ -235,6 +246,7 @@ func (d *ClusterDeployer) Delete(targetClient ClusterClient, namespace string) e if err = deleteObjectsInNamespace(bootstrapClient, namespace); err != nil { return fmt.Errorf("unable to finish deleting objects in bootstrap cluster, resources may have been leaked: %v", err) } + glog.Info("Deletion of cluster complete") return nil @@ -265,7 +277,7 @@ func (d *ClusterDeployer) createBootstrapCluster() (ClusterClient, func(), error return bootstrapClient, cleanupFn, nil } -func (d *ClusterDeployer) createTargetCluster(bootstrapClient ClusterClient, provider ProviderDeployer, kubeconfigOutput, clusterName, namespace string) (ClusterClient, error) { +func (d *ClusterDeployer) createTargetClusterClient(bootstrapClient ClusterClient, provider ProviderDeployer, kubeconfigOutput, clusterName, namespace string) (ClusterClient, error) { cluster, master, _, err := getClusterAPIObject(bootstrapClient, clusterName, namespace) if err != nil { return nil, err @@ -470,26 +482,31 @@ func pivotNamespace(from, to ClusterClient, namespace string) error { func deleteObjectsInNamespace(client ClusterClient, namespace string) error { var errors []string - glog.Infof("Deleting machine deployments") + glog.Infof("Deleting machine deployments in namespace %q", namespace) if err := client.DeleteMachineDeploymentObjectsInNamespace(namespace); err != nil { err = fmt.Errorf("error deleting machine deployments: %v", err) errors = append(errors, err.Error()) } - glog.Infof("Deleting machine sets") + glog.Infof("Deleting machine sets in namespace %q", namespace) if err := client.DeleteMachineSetObjectsInNamespace(namespace); err != nil { err = fmt.Errorf("error deleting machine sets: %v", err) errors = append(errors, err.Error()) } - glog.Infof("Deleting machines") + glog.Infof("Deleting machines in namespace %q", namespace) if err := client.DeleteMachineObjectsInNamespace(namespace); err != nil { err = fmt.Errorf("error deleting machines: %v", err) errors = append(errors, err.Error()) } - glog.Infof("Deleting clusters") + glog.Infof("Deleting clusters in namespace %q", namespace) if err := client.DeleteClusterObjectsInNamespace(namespace); err != nil { err = fmt.Errorf("error deleting clusters: %v", err) errors = append(errors, err.Error()) } + glog.Infof("Deleting namespace %q", namespace) + if err := client.DeleteNamespace(namespace); err != nil { + err = fmt.Errorf("error deleting namespace: %v", err) + errors = append(errors, err.Error()) + } if len(errors) > 0 { return fmt.Errorf("error(s) encountered deleting objects from bootstrap cluster: [%v]", strings.Join(errors, ", ")) } diff --git a/clusterctl/clusterdeployer/clusterdeployer_test.go b/clusterctl/clusterdeployer/clusterdeployer_test.go index d2ed19d74823..360fffe757bb 100644 --- a/clusterctl/clusterdeployer/clusterdeployer_test.go +++ b/clusterctl/clusterdeployer/clusterdeployer_test.go @@ -108,12 +108,15 @@ type testClusterClient struct { DeleteMachineDeploymentsObjectsErr error DeleteMachineDeploymentsObjectsInNamespaceErr error UpdateClusterObjectEndpointErr error + EnsureNamespaceErr error + DeleteNamespaceErr error CloseErr error clusters map[string][]*clusterv1.Cluster machineDeployments map[string][]*clusterv1.MachineDeployment machineSets map[string][]*clusterv1.MachineSet machines map[string][]*clusterv1.Machine + namespaces []string contextNamespace string } @@ -280,6 +283,40 @@ func (c *testClusterClient) Close() error { return c.CloseErr } +func (c *testClusterClient) EnsureNamespace(nsName string) error { + if len(c.namespaces) == 0 { + c.namespaces = append(c.namespaces, nsName) + } + if exists := contains(c.namespaces, nsName); !exists { + c.namespaces = append(c.namespaces, nsName) + } + return c.EnsureNamespaceErr +} + +func (c *testClusterClient) DeleteNamespace(namespaceName string) error { + var ns []string + for _, n := range c.namespaces { + if n == namespaceName { + continue + } + ns = append(ns, n) + } + c.namespaces = ns + + return c.DeleteNamespaceErr +} + +func contains(s []string, e string) bool { + exists := false + for _, existingNs := range s { + if existingNs == e { + exists = true + break + } + } + return exists +} + type testClusterClientFactory struct { ClusterClientErr error clusterClients map[string]*testClusterClient @@ -401,6 +438,28 @@ func TestClusterCreate(t *testing.T) { }, expectedTotalInternalClustersCount: 1, }, + { + name: "fail ensureNamespace in bootstrap cluster", + targetClient: &testClusterClient{}, + bootstrapClient: &testClusterClient{EnsureNamespaceErr: fmt.Errorf("Test failure")}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{"foo": getClustersForNamespace("foo", 3)}, + expectErr: true, + cleanupExternal: true, + expectExternalExists: false, + expectExternalCreated: true, + }, + { + name: "fail ensureNamespace in target cluster", + targetClient: &testClusterClient{EnsureNamespaceErr: fmt.Errorf("Test failure")}, + bootstrapClient: &testClusterClient{}, + namespaceToExpectedInternalMachines: make(map[string]int), + namespaceToInputCluster: map[string][]*clusterv1.Cluster{"foo": getClustersForNamespace("foo", 3)}, + expectErr: true, + cleanupExternal: true, + expectExternalExists: false, + expectExternalCreated: true, + }, { name: "fail provision multiple clusters in a namespace", targetClient: &testClusterClient{}, @@ -564,7 +623,6 @@ func TestClusterCreate(t *testing.T) { } for _, testcase := range testcases { - t.Run(testcase.name, func(t *testing.T) { kubeconfigOut := newTempFile(t) defer os.Remove(kubeconfigOut) @@ -637,6 +695,14 @@ func TestClusterCreate(t *testing.T) { t.Fatalf("Unexpected machine name at %v in namespace %q. Got: %v, Want: %v", i, ns, inputMachines[i].Name, testcase.targetClient.machines[ns][i].Name) } } + + if !contains(testcase.targetClient.namespaces, ns) { + t.Fatalf("Expected namespace %q in target namespace not found. Got: NotFound, Want: Found", ns) + } + + if !contains(testcase.bootstrapClient.namespaces, ns) { + t.Fatalf("Expected namespace %q in bootstrap namespace not found. Got: NotFound, Want: Found", ns) + } } // Validate across all namespaces if len(testcase.targetClient.clusters) != testcase.expectedTotalInternalClustersCount { @@ -800,7 +866,7 @@ func TestDeleteCleanupExternalCluster(t *testing.T) { } } -func TestDeleteClusters(t *testing.T) { +func TestClusterDelete(t *testing.T) { const bootstrapKubeconfig = "bootstrap" const targetKubeconfig = "target" @@ -1101,10 +1167,15 @@ func TestDeleteClusters(t *testing.T) { if len(testCase.bootstrapClient.machineDeployments[testCase.namespace]) != 0 { t.Fatalf("Unexpected machineDeployments count in namespace %q. Got: %d, Want: 0", testCase.namespace, len(testCase.targetClient.machineDeployments[testCase.namespace])) } - if len(testCase.bootstrapClient.clusters) != testCase.expectedExternalClusterCount { t.Fatalf("Unexpected remaining cluster count. Got: %d, Want: %d", len(testCase.bootstrapClient.clusters), testCase.expectedExternalClusterCount) } + if contains(testCase.bootstrapClient.namespaces, testCase.namespace) { + t.Fatalf("Unexpected remaining namespace %q in bootstrap cluster. Got: Found, Want: NotFound", testCase.namespace) + } + if contains(testCase.targetClient.namespaces, testCase.namespace) { + t.Fatalf("Unexpected remaining namespace %q in target cluster. Got: Found, Want: NotFound", testCase.namespace) + } } }) } From da42f3570e06e473a008760faedfcd9581471b6f Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Thu, 30 Aug 2018 11:45:38 -0700 Subject: [PATCH 3/4] add deprecation comments and logs and remove unused methods --- clusterctl/clusterdeployer/clusterclient.go | 20 +++++++++++++++---- clusterctl/clusterdeployer/clusterdeployer.go | 5 ----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/clusterctl/clusterdeployer/clusterclient.go b/clusterctl/clusterdeployer/clusterclient.go index e1bf73a4eadd..eebed12f4556 100644 --- a/clusterctl/clusterdeployer/clusterclient.go +++ b/clusterctl/clusterdeployer/clusterclient.go @@ -171,8 +171,9 @@ func (c *clusterClient) GetClusterObjectsInNamespace(namespace string) ([]*clust return clusters, nil } +// Deprecated API. Please do not extend or use. func (c *clusterClient) GetClusterObjects() ([]*clusterv1.Cluster, error) { - // TODO: Iterate over all namespaces where we could have Cluster API Objects https://github.com/kubernetes-sigs/cluster-api/issues/252 + glog.V(2).Info("GetClusterObjects API is deprecated, use GetClusterObjectsInNamespace instead") return c.GetClusterObjectsInNamespace(apiv1.NamespaceDefault) } @@ -188,8 +189,9 @@ func (c *clusterClient) GetMachineDeploymentObjectsInNamespace(namespace string) return machineDeployments, nil } +// Deprecated API. Please do not extend or use. func (c *clusterClient) GetMachineDeploymentObjects() ([]*clusterv1.MachineDeployment, error) { - // TODO: Iterate over all namespaces where we could have Cluster API Objects https://github.com/kubernetes-sigs/cluster-api/issues/252 + glog.V(2).Info("GetMachineDeploymentObjects API is deprecated, use GetMachineDeploymentObjectsInNamespace instead") return c.GetMachineDeploymentObjectsInNamespace(apiv1.NamespaceDefault) } @@ -205,8 +207,9 @@ func (c *clusterClient) GetMachineSetObjectsInNamespace(namespace string) ([]*cl return machineSets, nil } +// Deprecated API. Please do not extend or use. func (c *clusterClient) GetMachineSetObjects() ([]*clusterv1.MachineSet, error) { - // TODO: Iterate over all namespaces where we could have Cluster API Objects https://github.com/kubernetes-sigs/cluster-api/issues/252 + glog.V(2).Info("GetMachineSetObjects API is deprecated, use GetMachineSetObjectsInNamespace instead") return c.GetMachineSetObjectsInNamespace(apiv1.NamespaceDefault) } @@ -223,8 +226,9 @@ func (c *clusterClient) GetMachineObjectsInNamespace(namespace string) ([]*clust return machines, nil } +// Deprecated API. Please do not extend or use. func (c *clusterClient) GetMachineObjects() ([]*clusterv1.Machine, error) { - // TODO: Iterate over all namespaces where we could have Cluster API Objects https://github.com/kubernetes-sigs/cluster-api/issues/252 + glog.V(2).Info("GetMachineObjects API is deprecated, use GetMachineObjectsInNamespace instead") return c.GetMachineObjectsInNamespace(apiv1.NamespaceDefault) } @@ -278,7 +282,9 @@ func (c *clusterClient) CreateMachineObjects(machines []*clusterv1.Machine, name return nil } +// Deprecated API. Please do not extend or use. func (c *clusterClient) DeleteClusterObjects() error { + glog.V(2).Info("DeleteClusterObjects API is deprecated, use DeleteClusterObjectsInNamespace instead") return c.DeleteClusterObjectsInNamespace(apiv1.NamespaceDefault) } @@ -294,7 +300,9 @@ func (c *clusterClient) DeleteClusterObjectsInNamespace(namespace string) error return nil } +// Deprecated API. Please do not extend or use. func (c *clusterClient) DeleteMachineDeploymentObjects() error { + glog.V(2).Info("DeleteMachineDeploymentObjects API is deprecated, use DeleteMachineDeploymentObjectsInNamespace instead") return c.DeleteMachineDeploymentObjectsInNamespace(apiv1.NamespaceDefault) } @@ -310,7 +318,9 @@ func (c *clusterClient) DeleteMachineDeploymentObjectsInNamespace(namespace stri return nil } +// Deprecated API. Please do not extend or use. func (c *clusterClient) DeleteMachineSetObjects() error { + glog.V(2).Info("DeleteMachineSetObjects API is deprecated, use DeleteMachineSetObjectsInNamespace instead") return c.DeleteMachineSetObjectsInNamespace(apiv1.NamespaceDefault) } @@ -326,7 +336,9 @@ func (c *clusterClient) DeleteMachineSetObjectsInNamespace(namespace string) err return nil } +// Deprecated API. Please do not extend or use. func (c *clusterClient) DeleteMachineObjects() error { + glog.V(2).Info("DeleteMachineObjects API is deprecated, use DeleteMachineObjectsInNamespace instead") return c.DeleteMachineObjectsInNamespace(apiv1.NamespaceDefault) } diff --git a/clusterctl/clusterdeployer/clusterdeployer.go b/clusterctl/clusterdeployer/clusterdeployer.go index 3c12bcc27b87..fc593ae2a659 100644 --- a/clusterctl/clusterdeployer/clusterdeployer.go +++ b/clusterctl/clusterdeployer/clusterdeployer.go @@ -23,7 +23,6 @@ import ( "strings" "time" - apiv1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" "sigs.k8s.io/cluster-api/pkg/deployer" @@ -513,10 +512,6 @@ func deleteObjectsInNamespace(client ClusterClient, namespace string) error { return nil } -func deleteObjects(client ClusterClient) error { - return deleteObjectsInNamespace(client, apiv1.NamespaceDefault) -} - func getClusterAPIObject(client ClusterClient, clusterName, namespace string) (*clusterv1.Cluster, *clusterv1.Machine, []*clusterv1.Machine, error) { machines, err := client.GetMachineObjectsInNamespace(namespace) if err != nil { From 31c617e60ec27271434b1d488f2e8c43af8d3d0f Mon Sep 17 00:00:00 2001 From: Ashish Amarnath Date: Wed, 5 Sep 2018 11:32:51 -0700 Subject: [PATCH 4/4] addressing code review comments Fix: cluster client should not attempt deletion of the default namespace Make logs and error messages consistent when referring to cluster.Namespace Update unit tests --- clusterctl/clusterdeployer/clusterclient.go | 5 ++++- clusterctl/clusterdeployer/clusterdeployer.go | 8 ++++---- clusterctl/clusterdeployer/clusterdeployer_test.go | 7 ++++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/clusterctl/clusterdeployer/clusterclient.go b/clusterctl/clusterdeployer/clusterclient.go index eebed12f4556..0bc4a02723b5 100644 --- a/clusterctl/clusterdeployer/clusterclient.go +++ b/clusterctl/clusterdeployer/clusterclient.go @@ -93,6 +93,9 @@ func (c *clusterClient) EnsureNamespace(namespaceName string) error { } func (c *clusterClient) DeleteNamespace(namespaceName string) error { + if namespaceName == apiv1.NamespaceDefault { + return nil + } clientset, err := clientcmd.NewCoreClientSetForKubeconfig(c.kubeconfigFile) if err != nil { return fmt.Errorf("error creating core clientset: %v", err) @@ -138,7 +141,7 @@ func (c *clusterClient) Apply(manifest string) error { func (c *clusterClient) GetContextNamespace() string { if c.configOverrides.Context.Namespace == "" { - c.configOverrides.Context.Namespace = apiv1.NamespaceDefault + return apiv1.NamespaceDefault } return c.configOverrides.Context.Namespace } diff --git a/clusterctl/clusterdeployer/clusterdeployer.go b/clusterctl/clusterdeployer/clusterdeployer.go index fc593ae2a659..8df6a8ee639d 100644 --- a/clusterctl/clusterdeployer/clusterdeployer.go +++ b/clusterctl/clusterdeployer/clusterdeployer.go @@ -123,7 +123,7 @@ const ( timeoutKubeconfigReady = 20 * time.Minute ) -// Create the a cluster from the provided cluster definition and machine list. +// Create the cluster from the provided cluster definition and machine list. func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*clusterv1.Machine, provider ProviderDeployer, kubeconfigOutput string, providerComponentsStoreFactory ProviderComponentsStoreFactory) error { master, nodes, err := extractMasterMachine(machines) if err != nil { @@ -154,17 +154,17 @@ func (d *ClusterDeployer) Create(cluster *clusterv1.Cluster, machines []*cluster glog.Info("Provisioning target cluster via bootstrap cluster") - glog.Infof("Creating cluster object %v on bootstrap cluster in namespace %v", cluster.Name, cluster.Namespace) + glog.Infof("Creating cluster object %v on bootstrap cluster in namespace %q", cluster.Name, cluster.Namespace) if err := bootstrapClient.CreateClusterObject(cluster); err != nil { return fmt.Errorf("unable to create cluster object: %v", err) } - glog.Infof("Creating master %v in namespace %v", master.Name, cluster.Namespace) + glog.Infof("Creating master %v in namespace %q", master.Name, cluster.Namespace) if err := bootstrapClient.CreateMachineObjects([]*clusterv1.Machine{master}, cluster.Namespace); err != nil { return fmt.Errorf("unable to create master machine: %v", err) } - glog.Infof("Updating bootstrap cluster object for cluster %v in namespace %v with master (%s) endpoint", cluster.Name, cluster.Namespace, master.Name) + glog.Infof("Updating bootstrap cluster object for cluster %v in namespace %q with master (%s) endpoint", cluster.Name, cluster.Namespace, master.Name) if err := d.updateClusterEndpoint(bootstrapClient, provider, cluster.Name, cluster.Namespace); err != nil { return fmt.Errorf("unable to update bootstrap cluster endpoint: %v", err) } diff --git a/clusterctl/clusterdeployer/clusterdeployer_test.go b/clusterctl/clusterdeployer/clusterdeployer_test.go index 360fffe757bb..3fc571a2b574 100644 --- a/clusterctl/clusterdeployer/clusterdeployer_test.go +++ b/clusterctl/clusterdeployer/clusterdeployer_test.go @@ -22,6 +22,7 @@ import ( "os" "testing" + apiv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" @@ -294,6 +295,10 @@ func (c *testClusterClient) EnsureNamespace(nsName string) error { } func (c *testClusterClient) DeleteNamespace(namespaceName string) error { + if namespaceName == apiv1.NamespaceDefault { + return nil + } + var ns []string for _, n := range c.namespaces { if n == namespaceName { @@ -1173,7 +1178,7 @@ func TestClusterDelete(t *testing.T) { if contains(testCase.bootstrapClient.namespaces, testCase.namespace) { t.Fatalf("Unexpected remaining namespace %q in bootstrap cluster. Got: Found, Want: NotFound", testCase.namespace) } - if contains(testCase.targetClient.namespaces, testCase.namespace) { + if testCase.namespace != apiv1.NamespaceDefault && contains(testCase.targetClient.namespaces, testCase.namespace) { t.Fatalf("Unexpected remaining namespace %q in target cluster. Got: Found, Want: NotFound", testCase.namespace) } }