sigs.k8s.io/cluster-api@v1.7.1/exp/topology/scope/blueprint_test.go (about) 1 /* 2 Copyright 2022 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 scope 18 19 import ( 20 "testing" 21 "time" 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/apimachinery/pkg/apis/meta/v1/unstructured" 27 "k8s.io/apimachinery/pkg/util/intstr" 28 "k8s.io/utils/ptr" 29 30 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 31 "sigs.k8s.io/cluster-api/internal/test/builder" 32 ) 33 34 func TestIsControlPlaneMachineHealthCheckEnabled(t *testing.T) { 35 tests := []struct { 36 name string 37 blueprint *ClusterBlueprint 38 want bool 39 }{ 40 { 41 name: "should return false if the control plane does not have infrastructure machine", 42 blueprint: &ClusterBlueprint{ 43 ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class"). 44 Build(), 45 }, 46 want: false, 47 }, 48 { 49 name: "should return false if no MachineHealthCheck is defined in ClusterClass or ClusterTopology", 50 blueprint: &ClusterBlueprint{ 51 ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class"). 52 WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}). 53 Build(), 54 Topology: builder.ClusterTopology(). 55 WithClass("cluster-class"). 56 Build(), 57 }, 58 want: false, 59 }, 60 { 61 name: "should return true if MachineHealthCheck if defined in ClusterClass, not defined in cluster topology and enable is not set", 62 blueprint: &ClusterBlueprint{ 63 ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class"). 64 WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}). 65 WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{}). 66 Build(), 67 Topology: builder.ClusterTopology(). 68 WithClass("cluster-class"). 69 Build(), 70 }, 71 want: true, 72 }, 73 { 74 name: "should return false if MachineHealthCheck if defined in ClusterClass, not defined in cluster topology and enable is false", 75 blueprint: &ClusterBlueprint{ 76 ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class"). 77 WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}). 78 WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{}). 79 Build(), 80 Topology: builder.ClusterTopology(). 81 WithClass("cluster-class"). 82 WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{ 83 Enable: ptr.To(false), 84 }). 85 Build(), 86 }, 87 want: false, 88 }, 89 { 90 name: "should return true if MachineHealthCheck if defined in ClusterClass, not defined in cluster topology and enable is true", 91 blueprint: &ClusterBlueprint{ 92 ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class"). 93 WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}). 94 WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckClass{}). 95 Build(), 96 Topology: builder.ClusterTopology(). 97 WithClass("cluster-class"). 98 WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{ 99 Enable: ptr.To(true), 100 }). 101 Build(), 102 }, 103 want: true, 104 }, 105 { 106 name: "should return true if MachineHealthCheck if defined in cluster topology, not defined in ClusterClass and enable is not set", 107 blueprint: &ClusterBlueprint{ 108 ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class"). 109 WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}). 110 Build(), 111 Topology: builder.ClusterTopology(). 112 WithClass("cluster-class"). 113 WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{ 114 MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{ 115 UnhealthyConditions: []clusterv1.UnhealthyCondition{ 116 { 117 Type: corev1.NodeReady, 118 Status: corev1.ConditionUnknown, 119 Timeout: metav1.Duration{Duration: 5 * time.Minute}, 120 }, 121 }, 122 }, 123 }). 124 Build(), 125 }, 126 want: true, 127 }, 128 { 129 name: "should return false if MachineHealthCheck if defined in cluster topology, not defined in ClusterClass and enable is false", 130 blueprint: &ClusterBlueprint{ 131 ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class"). 132 WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}). 133 Build(), 134 Topology: builder.ClusterTopology(). 135 WithClass("cluster-class"). 136 WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{ 137 Enable: ptr.To(false), 138 MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{ 139 UnhealthyConditions: []clusterv1.UnhealthyCondition{ 140 { 141 Type: corev1.NodeReady, 142 Status: corev1.ConditionUnknown, 143 Timeout: metav1.Duration{Duration: 5 * time.Minute}, 144 }, 145 }, 146 }, 147 }). 148 Build(), 149 }, 150 want: false, 151 }, 152 { 153 name: "should return true if MachineHealthCheck if defined in cluster topology, not defined in ClusterClass and enable is true", 154 blueprint: &ClusterBlueprint{ 155 ClusterClass: builder.ClusterClass(metav1.NamespaceDefault, "cluster-class"). 156 WithControlPlaneInfrastructureMachineTemplate(&unstructured.Unstructured{}). 157 Build(), 158 Topology: builder.ClusterTopology(). 159 WithClass("cluster-class"). 160 WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{ 161 Enable: ptr.To(true), 162 MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{ 163 UnhealthyConditions: []clusterv1.UnhealthyCondition{ 164 { 165 Type: corev1.NodeReady, 166 Status: corev1.ConditionUnknown, 167 Timeout: metav1.Duration{Duration: 5 * time.Minute}, 168 }, 169 }, 170 }, 171 }). 172 Build(), 173 }, 174 want: true, 175 }, 176 } 177 178 for _, tt := range tests { 179 t.Run(tt.name, func(t *testing.T) { 180 g := NewWithT(t) 181 g.Expect(tt.blueprint.IsControlPlaneMachineHealthCheckEnabled()).To(Equal(tt.want)) 182 }) 183 } 184 } 185 186 func TestControlPlaneMachineHealthCheckClass(t *testing.T) { 187 mhcInClusterClass := &clusterv1.MachineHealthCheckClass{ 188 UnhealthyConditions: []clusterv1.UnhealthyCondition{ 189 { 190 Type: corev1.NodeReady, 191 Status: corev1.ConditionFalse, 192 Timeout: metav1.Duration{Duration: 10 * time.Minute}, 193 }, 194 }, 195 } 196 197 percent50 := intstr.FromString("50%") 198 mhcInClusterTopology := &clusterv1.MachineHealthCheckClass{ 199 UnhealthyConditions: []clusterv1.UnhealthyCondition{ 200 { 201 Type: corev1.NodeReady, 202 Status: corev1.ConditionFalse, 203 Timeout: metav1.Duration{Duration: 20 * time.Minute}, 204 }, 205 }, 206 MaxUnhealthy: &percent50, 207 } 208 209 tests := []struct { 210 name string 211 blueprint *ClusterBlueprint 212 want *clusterv1.MachineHealthCheckClass 213 }{ 214 { 215 name: "should return the MachineHealthCheck from cluster topology if defined - should take precedence over MachineHealthCheck in ClusterClass", 216 blueprint: &ClusterBlueprint{ 217 Topology: builder.ClusterTopology(). 218 WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{ 219 MachineHealthCheckClass: *mhcInClusterTopology, 220 }). 221 Build(), 222 ControlPlane: &ControlPlaneBlueprint{ 223 MachineHealthCheck: mhcInClusterClass, 224 }, 225 }, 226 want: mhcInClusterTopology, 227 }, 228 { 229 name: "should return the MachineHealthCheck from ClusterClass if no MachineHealthCheck is defined in cluster topology", 230 blueprint: &ClusterBlueprint{ 231 Topology: builder.ClusterTopology(). 232 WithControlPlaneMachineHealthCheck(&clusterv1.MachineHealthCheckTopology{}). 233 Build(), 234 ControlPlane: &ControlPlaneBlueprint{ 235 MachineHealthCheck: mhcInClusterClass, 236 }, 237 }, 238 want: mhcInClusterClass, 239 }, 240 } 241 242 for _, tt := range tests { 243 t.Run(tt.name, func(t *testing.T) { 244 g := NewWithT(t) 245 g.Expect(tt.blueprint.ControlPlaneMachineHealthCheckClass()).To(BeComparableTo(tt.want)) 246 }) 247 } 248 } 249 250 func TestIsMachineDeploymentMachineHealthCheckEnabled(t *testing.T) { 251 tests := []struct { 252 name string 253 blueprint *ClusterBlueprint 254 mdTopology *clusterv1.MachineDeploymentTopology 255 want bool 256 }{ 257 { 258 name: "should return false if MachineHealthCheck is not defined in ClusterClass and cluster topology", 259 blueprint: &ClusterBlueprint{ 260 MachineDeployments: map[string]*MachineDeploymentBlueprint{ 261 "worker-class": {}, 262 }, 263 }, 264 mdTopology: &clusterv1.MachineDeploymentTopology{ 265 Class: "worker-class", 266 }, 267 want: false, 268 }, 269 { 270 name: "should return true if MachineHealthCheck is defined in ClusterClass and enable is not set", 271 blueprint: &ClusterBlueprint{ 272 MachineDeployments: map[string]*MachineDeploymentBlueprint{ 273 "worker-class": { 274 MachineHealthCheck: &clusterv1.MachineHealthCheckClass{}, 275 }, 276 }, 277 }, 278 mdTopology: &clusterv1.MachineDeploymentTopology{ 279 Class: "worker-class", 280 }, 281 want: true, 282 }, 283 { 284 name: "should return false if MachineHealthCheck is defined in ClusterClass and enable is false", 285 blueprint: &ClusterBlueprint{ 286 MachineDeployments: map[string]*MachineDeploymentBlueprint{ 287 "worker-class": { 288 MachineHealthCheck: &clusterv1.MachineHealthCheckClass{}, 289 }, 290 }, 291 }, 292 mdTopology: &clusterv1.MachineDeploymentTopology{ 293 Class: "worker-class", 294 MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{ 295 Enable: ptr.To(false), 296 }, 297 }, 298 want: false, 299 }, 300 { 301 name: "should return true if MachineHealthCheck is defined in ClusterClass and enable is true", 302 blueprint: &ClusterBlueprint{ 303 MachineDeployments: map[string]*MachineDeploymentBlueprint{ 304 "worker-class": { 305 MachineHealthCheck: &clusterv1.MachineHealthCheckClass{}, 306 }, 307 }, 308 }, 309 mdTopology: &clusterv1.MachineDeploymentTopology{ 310 Class: "worker-class", 311 MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{ 312 Enable: ptr.To(true), 313 }, 314 }, 315 want: true, 316 }, 317 { 318 name: "should return true if MachineHealthCheck is defined in cluster topology and enable is not set", 319 blueprint: &ClusterBlueprint{ 320 MachineDeployments: map[string]*MachineDeploymentBlueprint{ 321 "worker-class": {}, 322 }, 323 }, 324 mdTopology: &clusterv1.MachineDeploymentTopology{ 325 Class: "worker-class", 326 MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{ 327 MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{ 328 UnhealthyConditions: []clusterv1.UnhealthyCondition{ 329 { 330 Type: corev1.NodeReady, 331 Status: corev1.ConditionUnknown, 332 Timeout: metav1.Duration{Duration: 5 * time.Minute}, 333 }, 334 }, 335 }, 336 }, 337 }, 338 want: true, 339 }, 340 { 341 name: "should return false if MachineHealthCheck is defined in cluster topology and enable is false", 342 blueprint: &ClusterBlueprint{ 343 MachineDeployments: map[string]*MachineDeploymentBlueprint{ 344 "worker-class": {}, 345 }, 346 }, 347 mdTopology: &clusterv1.MachineDeploymentTopology{ 348 Class: "worker-class", 349 MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{ 350 Enable: ptr.To(false), 351 MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{ 352 UnhealthyConditions: []clusterv1.UnhealthyCondition{ 353 { 354 Type: corev1.NodeReady, 355 Status: corev1.ConditionUnknown, 356 Timeout: metav1.Duration{Duration: 5 * time.Minute}, 357 }, 358 }, 359 }, 360 }, 361 }, 362 want: false, 363 }, 364 { 365 name: "should return true if MachineHealthCheck is defined in cluster topology and enable is true", 366 blueprint: &ClusterBlueprint{ 367 MachineDeployments: map[string]*MachineDeploymentBlueprint{ 368 "worker-class": {}, 369 }, 370 }, 371 mdTopology: &clusterv1.MachineDeploymentTopology{ 372 Class: "worker-class", 373 MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{ 374 Enable: ptr.To(true), 375 MachineHealthCheckClass: clusterv1.MachineHealthCheckClass{ 376 UnhealthyConditions: []clusterv1.UnhealthyCondition{ 377 { 378 Type: corev1.NodeReady, 379 Status: corev1.ConditionUnknown, 380 Timeout: metav1.Duration{Duration: 5 * time.Minute}, 381 }, 382 }, 383 }, 384 }, 385 }, 386 want: true, 387 }, 388 } 389 390 for _, tt := range tests { 391 t.Run(tt.name, func(t *testing.T) { 392 g := NewWithT(t) 393 g.Expect(tt.blueprint.IsMachineDeploymentMachineHealthCheckEnabled(tt.mdTopology)).To(BeComparableTo(tt.want)) 394 }) 395 } 396 } 397 398 func TestMachineDeploymentMachineHealthCheckClass(t *testing.T) { 399 mhcInClusterClass := &clusterv1.MachineHealthCheckClass{ 400 UnhealthyConditions: []clusterv1.UnhealthyCondition{ 401 { 402 Type: corev1.NodeReady, 403 Status: corev1.ConditionFalse, 404 Timeout: metav1.Duration{Duration: 10 * time.Minute}, 405 }, 406 }, 407 } 408 409 percent50 := intstr.FromString("50%") 410 mhcInClusterTopology := &clusterv1.MachineHealthCheckClass{ 411 UnhealthyConditions: []clusterv1.UnhealthyCondition{ 412 { 413 Type: corev1.NodeReady, 414 Status: corev1.ConditionFalse, 415 Timeout: metav1.Duration{Duration: 20 * time.Minute}, 416 }, 417 }, 418 MaxUnhealthy: &percent50, 419 } 420 421 tests := []struct { 422 name string 423 blueprint *ClusterBlueprint 424 mdTopology *clusterv1.MachineDeploymentTopology 425 want *clusterv1.MachineHealthCheckClass 426 }{ 427 { 428 name: "should return the MachineHealthCheck from cluster topology if defined - should take precedence over MachineHealthCheck in ClusterClass", 429 blueprint: &ClusterBlueprint{ 430 MachineDeployments: map[string]*MachineDeploymentBlueprint{ 431 "worker-class": { 432 MachineHealthCheck: mhcInClusterClass, 433 }, 434 }, 435 }, 436 mdTopology: &clusterv1.MachineDeploymentTopology{ 437 Class: "worker-class", 438 MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{ 439 MachineHealthCheckClass: *mhcInClusterTopology, 440 }, 441 }, 442 want: mhcInClusterTopology, 443 }, 444 { 445 name: "should return the MachineHealthCheck from ClusterClass if no MachineHealthCheck is defined in cluster topology", 446 blueprint: &ClusterBlueprint{ 447 MachineDeployments: map[string]*MachineDeploymentBlueprint{ 448 "worker-class": { 449 MachineHealthCheck: mhcInClusterClass, 450 }, 451 }, 452 }, 453 mdTopology: &clusterv1.MachineDeploymentTopology{ 454 Class: "worker-class", 455 MachineHealthCheck: &clusterv1.MachineHealthCheckTopology{}, 456 }, 457 want: mhcInClusterClass, 458 }, 459 } 460 461 for _, tt := range tests { 462 t.Run(tt.name, func(t *testing.T) { 463 g := NewWithT(t) 464 g.Expect(tt.blueprint.MachineDeploymentMachineHealthCheckClass(tt.mdTopology)).To(BeComparableTo(tt.want)) 465 }) 466 } 467 }