sigs.k8s.io/cluster-api@v1.6.3/controlplane/kubeadm/internal/controllers/status_test.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controllers
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  
    23  	. "github.com/onsi/gomega"
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/client-go/tools/record"
    27  	"k8s.io/klog/v2/klogr"
    28  	"k8s.io/utils/pointer"
    29  	"sigs.k8s.io/controller-runtime/pkg/client"
    30  	"sigs.k8s.io/controller-runtime/pkg/log"
    31  
    32  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    33  	controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
    34  	"sigs.k8s.io/cluster-api/controlplane/kubeadm/internal"
    35  	controlplanev1webhooks "sigs.k8s.io/cluster-api/controlplane/kubeadm/internal/webhooks"
    36  	"sigs.k8s.io/cluster-api/util/conditions"
    37  )
    38  
    39  func TestKubeadmControlPlaneReconciler_updateStatusNoMachines(t *testing.T) {
    40  	g := NewWithT(t)
    41  
    42  	cluster := &clusterv1.Cluster{
    43  		ObjectMeta: metav1.ObjectMeta{
    44  			Name:      "foo",
    45  			Namespace: metav1.NamespaceDefault,
    46  		},
    47  	}
    48  
    49  	kcp := &controlplanev1.KubeadmControlPlane{
    50  		TypeMeta: metav1.TypeMeta{
    51  			Kind:       "KubeadmControlPlane",
    52  			APIVersion: controlplanev1.GroupVersion.String(),
    53  		},
    54  		ObjectMeta: metav1.ObjectMeta{
    55  			Namespace: cluster.Namespace,
    56  			Name:      "foo",
    57  		},
    58  		Spec: controlplanev1.KubeadmControlPlaneSpec{
    59  			Version: "v1.16.6",
    60  			MachineTemplate: controlplanev1.KubeadmControlPlaneMachineTemplate{
    61  				InfrastructureRef: corev1.ObjectReference{
    62  					APIVersion: "test/v1alpha1",
    63  					Kind:       "UnknownInfraMachine",
    64  					Name:       "foo",
    65  				},
    66  			},
    67  		},
    68  	}
    69  	webhook := &controlplanev1webhooks.KubeadmControlPlane{}
    70  	g.Expect(webhook.Default(ctx, kcp)).To(Succeed())
    71  	_, err := webhook.ValidateCreate(ctx, kcp)
    72  	g.Expect(err).ToNot(HaveOccurred())
    73  
    74  	fakeClient := newFakeClient(kcp.DeepCopy(), cluster.DeepCopy())
    75  	log.SetLogger(klogr.New())
    76  
    77  	r := &KubeadmControlPlaneReconciler{
    78  		Client: fakeClient,
    79  		managementCluster: &fakeManagementCluster{
    80  			Machines: map[string]*clusterv1.Machine{},
    81  			Workload: fakeWorkloadCluster{},
    82  		},
    83  		recorder: record.NewFakeRecorder(32),
    84  	}
    85  
    86  	controlPlane := &internal.ControlPlane{
    87  		KCP:     kcp,
    88  		Cluster: cluster,
    89  	}
    90  	controlPlane.InjectTestManagementCluster(r.managementCluster)
    91  
    92  	g.Expect(r.updateStatus(ctx, controlPlane)).To(Succeed())
    93  	g.Expect(kcp.Status.Replicas).To(BeEquivalentTo(0))
    94  	g.Expect(kcp.Status.ReadyReplicas).To(BeEquivalentTo(0))
    95  	g.Expect(kcp.Status.UnavailableReplicas).To(BeEquivalentTo(0))
    96  	g.Expect(kcp.Status.Initialized).To(BeFalse())
    97  	g.Expect(kcp.Status.Ready).To(BeFalse())
    98  	g.Expect(kcp.Status.Selector).NotTo(BeEmpty())
    99  	g.Expect(kcp.Status.FailureMessage).To(BeNil())
   100  	g.Expect(kcp.Status.FailureReason).To(BeEquivalentTo(""))
   101  }
   102  
   103  func TestKubeadmControlPlaneReconciler_updateStatusAllMachinesNotReady(t *testing.T) {
   104  	g := NewWithT(t)
   105  
   106  	cluster := &clusterv1.Cluster{
   107  		ObjectMeta: metav1.ObjectMeta{
   108  			Name:      "foo",
   109  			Namespace: metav1.NamespaceDefault,
   110  		},
   111  	}
   112  
   113  	kcp := &controlplanev1.KubeadmControlPlane{
   114  		TypeMeta: metav1.TypeMeta{
   115  			Kind:       "KubeadmControlPlane",
   116  			APIVersion: controlplanev1.GroupVersion.String(),
   117  		},
   118  		ObjectMeta: metav1.ObjectMeta{
   119  			Namespace: cluster.Namespace,
   120  			Name:      "foo",
   121  		},
   122  		Spec: controlplanev1.KubeadmControlPlaneSpec{
   123  			Version: "v1.16.6",
   124  			MachineTemplate: controlplanev1.KubeadmControlPlaneMachineTemplate{
   125  				InfrastructureRef: corev1.ObjectReference{
   126  					APIVersion: "test/v1alpha1",
   127  					Kind:       "UnknownInfraMachine",
   128  					Name:       "foo",
   129  				},
   130  			},
   131  		},
   132  	}
   133  	webhook := &controlplanev1webhooks.KubeadmControlPlane{}
   134  	g.Expect(webhook.Default(ctx, kcp)).To(Succeed())
   135  	_, err := webhook.ValidateCreate(ctx, kcp)
   136  	g.Expect(err).ToNot(HaveOccurred())
   137  
   138  	machines := map[string]*clusterv1.Machine{}
   139  	objs := []client.Object{cluster.DeepCopy(), kcp.DeepCopy()}
   140  	for i := 0; i < 3; i++ {
   141  		name := fmt.Sprintf("test-%d", i)
   142  		m, n := createMachineNodePair(name, cluster, kcp, false)
   143  		objs = append(objs, n, m)
   144  		machines[m.Name] = m
   145  	}
   146  
   147  	fakeClient := newFakeClient(objs...)
   148  	log.SetLogger(klogr.New())
   149  
   150  	r := &KubeadmControlPlaneReconciler{
   151  		Client: fakeClient,
   152  		managementCluster: &fakeManagementCluster{
   153  			Machines: machines,
   154  			Workload: fakeWorkloadCluster{},
   155  		},
   156  		recorder: record.NewFakeRecorder(32),
   157  	}
   158  
   159  	controlPlane := &internal.ControlPlane{
   160  		KCP:      kcp,
   161  		Cluster:  cluster,
   162  		Machines: machines,
   163  	}
   164  	controlPlane.InjectTestManagementCluster(r.managementCluster)
   165  
   166  	g.Expect(r.updateStatus(ctx, controlPlane)).To(Succeed())
   167  	g.Expect(kcp.Status.Replicas).To(BeEquivalentTo(3))
   168  	g.Expect(kcp.Status.ReadyReplicas).To(BeEquivalentTo(0))
   169  	g.Expect(kcp.Status.UnavailableReplicas).To(BeEquivalentTo(3))
   170  	g.Expect(kcp.Status.Selector).NotTo(BeEmpty())
   171  	g.Expect(kcp.Status.FailureMessage).To(BeNil())
   172  	g.Expect(kcp.Status.FailureReason).To(BeEquivalentTo(""))
   173  	g.Expect(kcp.Status.Initialized).To(BeFalse())
   174  	g.Expect(kcp.Status.Ready).To(BeFalse())
   175  }
   176  
   177  func TestKubeadmControlPlaneReconciler_updateStatusAllMachinesReady(t *testing.T) {
   178  	g := NewWithT(t)
   179  
   180  	cluster := &clusterv1.Cluster{
   181  		ObjectMeta: metav1.ObjectMeta{
   182  			Namespace: metav1.NamespaceDefault,
   183  			Name:      "foo",
   184  		},
   185  	}
   186  
   187  	kcp := &controlplanev1.KubeadmControlPlane{
   188  		TypeMeta: metav1.TypeMeta{
   189  			Kind:       "KubeadmControlPlane",
   190  			APIVersion: controlplanev1.GroupVersion.String(),
   191  		},
   192  		ObjectMeta: metav1.ObjectMeta{
   193  			Namespace: cluster.Namespace,
   194  			Name:      "foo",
   195  		},
   196  		Spec: controlplanev1.KubeadmControlPlaneSpec{
   197  			Version: "v1.16.6",
   198  			MachineTemplate: controlplanev1.KubeadmControlPlaneMachineTemplate{
   199  				InfrastructureRef: corev1.ObjectReference{
   200  					APIVersion: "test/v1alpha1",
   201  					Kind:       "UnknownInfraMachine",
   202  					Name:       "foo",
   203  				},
   204  			},
   205  		},
   206  	}
   207  	webhook := &controlplanev1webhooks.KubeadmControlPlane{}
   208  	g.Expect(webhook.Default(ctx, kcp)).To(Succeed())
   209  	_, err := webhook.ValidateCreate(ctx, kcp)
   210  	g.Expect(err).ToNot(HaveOccurred())
   211  
   212  	objs := []client.Object{cluster.DeepCopy(), kcp.DeepCopy(), kubeadmConfigMap()}
   213  	machines := map[string]*clusterv1.Machine{}
   214  	for i := 0; i < 3; i++ {
   215  		name := fmt.Sprintf("test-%d", i)
   216  		m, n := createMachineNodePair(name, cluster, kcp, true)
   217  		objs = append(objs, n, m)
   218  		machines[m.Name] = m
   219  	}
   220  
   221  	fakeClient := newFakeClient(objs...)
   222  	log.SetLogger(klogr.New())
   223  
   224  	r := &KubeadmControlPlaneReconciler{
   225  		Client: fakeClient,
   226  		managementCluster: &fakeManagementCluster{
   227  			Machines: machines,
   228  			Workload: fakeWorkloadCluster{
   229  				Status: internal.ClusterStatus{
   230  					Nodes:            3,
   231  					ReadyNodes:       3,
   232  					HasKubeadmConfig: true,
   233  				},
   234  			},
   235  		},
   236  		recorder: record.NewFakeRecorder(32),
   237  	}
   238  
   239  	controlPlane := &internal.ControlPlane{
   240  		KCP:      kcp,
   241  		Cluster:  cluster,
   242  		Machines: machines,
   243  	}
   244  	controlPlane.InjectTestManagementCluster(r.managementCluster)
   245  
   246  	g.Expect(r.updateStatus(ctx, controlPlane)).To(Succeed())
   247  	g.Expect(kcp.Status.Replicas).To(BeEquivalentTo(3))
   248  	g.Expect(kcp.Status.ReadyReplicas).To(BeEquivalentTo(3))
   249  	g.Expect(kcp.Status.UnavailableReplicas).To(BeEquivalentTo(0))
   250  	g.Expect(kcp.Status.Selector).NotTo(BeEmpty())
   251  	g.Expect(kcp.Status.FailureMessage).To(BeNil())
   252  	g.Expect(kcp.Status.FailureReason).To(BeEquivalentTo(""))
   253  	g.Expect(kcp.Status.Initialized).To(BeTrue())
   254  	g.Expect(conditions.IsTrue(kcp, controlplanev1.AvailableCondition)).To(BeTrue())
   255  	g.Expect(conditions.IsTrue(kcp, controlplanev1.MachinesCreatedCondition)).To(BeTrue())
   256  	g.Expect(kcp.Status.Ready).To(BeTrue())
   257  }
   258  
   259  func TestKubeadmControlPlaneReconciler_updateStatusMachinesReadyMixed(t *testing.T) {
   260  	g := NewWithT(t)
   261  
   262  	cluster := &clusterv1.Cluster{
   263  		ObjectMeta: metav1.ObjectMeta{
   264  			Name:      "foo",
   265  			Namespace: metav1.NamespaceDefault,
   266  		},
   267  	}
   268  
   269  	kcp := &controlplanev1.KubeadmControlPlane{
   270  		TypeMeta: metav1.TypeMeta{
   271  			Kind:       "KubeadmControlPlane",
   272  			APIVersion: controlplanev1.GroupVersion.String(),
   273  		},
   274  		ObjectMeta: metav1.ObjectMeta{
   275  			Namespace: cluster.Namespace,
   276  			Name:      "foo",
   277  		},
   278  		Spec: controlplanev1.KubeadmControlPlaneSpec{
   279  			Version: "v1.16.6",
   280  			MachineTemplate: controlplanev1.KubeadmControlPlaneMachineTemplate{
   281  				InfrastructureRef: corev1.ObjectReference{
   282  					APIVersion: "test/v1alpha1",
   283  					Kind:       "UnknownInfraMachine",
   284  					Name:       "foo",
   285  				},
   286  			},
   287  		},
   288  	}
   289  	webhook := &controlplanev1webhooks.KubeadmControlPlane{}
   290  	g.Expect(webhook.Default(ctx, kcp)).To(Succeed())
   291  	_, err := webhook.ValidateCreate(ctx, kcp)
   292  	g.Expect(err).ToNot(HaveOccurred())
   293  	machines := map[string]*clusterv1.Machine{}
   294  	objs := []client.Object{cluster.DeepCopy(), kcp.DeepCopy()}
   295  	for i := 0; i < 4; i++ {
   296  		name := fmt.Sprintf("test-%d", i)
   297  		m, n := createMachineNodePair(name, cluster, kcp, false)
   298  		machines[m.Name] = m
   299  		objs = append(objs, n, m)
   300  	}
   301  	m, n := createMachineNodePair("testReady", cluster, kcp, true)
   302  	objs = append(objs, n, m, kubeadmConfigMap())
   303  	machines[m.Name] = m
   304  	fakeClient := newFakeClient(objs...)
   305  	log.SetLogger(klogr.New())
   306  
   307  	r := &KubeadmControlPlaneReconciler{
   308  		Client: fakeClient,
   309  		managementCluster: &fakeManagementCluster{
   310  			Machines: machines,
   311  			Workload: fakeWorkloadCluster{
   312  				Status: internal.ClusterStatus{
   313  					Nodes:            5,
   314  					ReadyNodes:       1,
   315  					HasKubeadmConfig: true,
   316  				},
   317  			},
   318  		},
   319  		recorder: record.NewFakeRecorder(32),
   320  	}
   321  
   322  	controlPlane := &internal.ControlPlane{
   323  		KCP:      kcp,
   324  		Cluster:  cluster,
   325  		Machines: machines,
   326  	}
   327  	controlPlane.InjectTestManagementCluster(r.managementCluster)
   328  
   329  	g.Expect(r.updateStatus(ctx, controlPlane)).To(Succeed())
   330  	g.Expect(kcp.Status.Replicas).To(BeEquivalentTo(5))
   331  	g.Expect(kcp.Status.ReadyReplicas).To(BeEquivalentTo(1))
   332  	g.Expect(kcp.Status.UnavailableReplicas).To(BeEquivalentTo(4))
   333  	g.Expect(kcp.Status.Selector).NotTo(BeEmpty())
   334  	g.Expect(kcp.Status.FailureMessage).To(BeNil())
   335  	g.Expect(kcp.Status.FailureReason).To(BeEquivalentTo(""))
   336  	g.Expect(kcp.Status.Initialized).To(BeTrue())
   337  	g.Expect(kcp.Status.Ready).To(BeTrue())
   338  }
   339  
   340  func TestKubeadmControlPlaneReconciler_machinesCreatedIsIsTrueEvenWhenTheNodesAreNotReady(t *testing.T) {
   341  	g := NewWithT(t)
   342  
   343  	cluster := &clusterv1.Cluster{
   344  		ObjectMeta: metav1.ObjectMeta{
   345  			Name:      "foo",
   346  			Namespace: metav1.NamespaceDefault,
   347  		},
   348  	}
   349  
   350  	kcp := &controlplanev1.KubeadmControlPlane{
   351  		TypeMeta: metav1.TypeMeta{
   352  			Kind:       "KubeadmControlPlane",
   353  			APIVersion: controlplanev1.GroupVersion.String(),
   354  		},
   355  		ObjectMeta: metav1.ObjectMeta{
   356  			Namespace: cluster.Namespace,
   357  			Name:      "foo",
   358  		},
   359  		Spec: controlplanev1.KubeadmControlPlaneSpec{
   360  			Version:  "v1.16.6",
   361  			Replicas: pointer.Int32(3),
   362  			MachineTemplate: controlplanev1.KubeadmControlPlaneMachineTemplate{
   363  				InfrastructureRef: corev1.ObjectReference{
   364  					APIVersion: "test/v1alpha1",
   365  					Kind:       "UnknownInfraMachine",
   366  					Name:       "foo",
   367  				},
   368  			},
   369  		},
   370  	}
   371  	webhook := &controlplanev1webhooks.KubeadmControlPlane{}
   372  	g.Expect(webhook.Default(ctx, kcp)).To(Succeed())
   373  	_, err := webhook.ValidateCreate(ctx, kcp)
   374  	g.Expect(err).ToNot(HaveOccurred())
   375  	machines := map[string]*clusterv1.Machine{}
   376  	objs := []client.Object{cluster.DeepCopy(), kcp.DeepCopy()}
   377  	// Create the desired number of machines
   378  	for i := 0; i < 3; i++ {
   379  		name := fmt.Sprintf("test-%d", i)
   380  		m, n := createMachineNodePair(name, cluster, kcp, false)
   381  		machines[m.Name] = m
   382  		objs = append(objs, n, m)
   383  	}
   384  
   385  	fakeClient := newFakeClient(objs...)
   386  	log.SetLogger(klogr.New())
   387  
   388  	// Set all the machines to `not ready`
   389  	r := &KubeadmControlPlaneReconciler{
   390  		Client: fakeClient,
   391  		managementCluster: &fakeManagementCluster{
   392  			Machines: machines,
   393  			Workload: fakeWorkloadCluster{
   394  				Status: internal.ClusterStatus{
   395  					Nodes:            0,
   396  					ReadyNodes:       0,
   397  					HasKubeadmConfig: true,
   398  				},
   399  			},
   400  		},
   401  		recorder: record.NewFakeRecorder(32),
   402  	}
   403  
   404  	controlPlane := &internal.ControlPlane{
   405  		KCP:      kcp,
   406  		Cluster:  cluster,
   407  		Machines: machines,
   408  	}
   409  	controlPlane.InjectTestManagementCluster(r.managementCluster)
   410  
   411  	g.Expect(r.updateStatus(ctx, controlPlane)).To(Succeed())
   412  	g.Expect(kcp.Status.Replicas).To(BeEquivalentTo(3))
   413  	g.Expect(kcp.Status.ReadyReplicas).To(BeEquivalentTo(0))
   414  	g.Expect(kcp.Status.UnavailableReplicas).To(BeEquivalentTo(3))
   415  	g.Expect(kcp.Status.Ready).To(BeFalse())
   416  	g.Expect(conditions.IsTrue(kcp, controlplanev1.MachinesCreatedCondition)).To(BeTrue())
   417  }
   418  
   419  func kubeadmConfigMap() *corev1.ConfigMap {
   420  	return &corev1.ConfigMap{
   421  		ObjectMeta: metav1.ObjectMeta{
   422  			Name:      "kubeadm-config",
   423  			Namespace: metav1.NamespaceSystem,
   424  		},
   425  	}
   426  }