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