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 }