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 }