@@ -42,16 +42,14 @@ import (
42
42
"k8s.io/apimachinery/pkg/labels"
43
43
kerrors "k8s.io/apimachinery/pkg/util/errors"
44
44
"k8s.io/apimachinery/pkg/util/intstr"
45
- utilversion "k8s.io/apimachinery/pkg/util/version"
46
45
"k8s.io/apimachinery/pkg/util/wait"
47
- "k8s.io/apimachinery/pkg/version"
48
46
"k8s.io/client-go/kubernetes"
49
47
"k8s.io/client-go/tools/cache"
50
48
"k8s.io/klog/v2"
51
- "k8s.io/utils/ptr"
52
49
toolscache "sigs.k8s.io/controller-runtime/pkg/cache"
53
50
"sigs.k8s.io/controller-runtime/pkg/client"
54
51
52
+ clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
55
53
controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
56
54
. "sigs.k8s.io/cluster-api/test/framework/ginkgoextensions"
57
55
"sigs.k8s.io/cluster-api/test/framework/internal/log"
@@ -493,115 +491,200 @@ func WaitForDNSUpgrade(ctx context.Context, input WaitForDNSUpgradeInput, interv
493
491
type DeployUnevictablePodInput struct {
494
492
WorkloadClusterProxy ClusterProxy
495
493
ControlPlane * controlplanev1.KubeadmControlPlane
494
+ MachineDeployment * clusterv1.MachineDeployment
496
495
DeploymentName string
497
496
Namespace string
497
+ NodeSelector map [string ]string
498
498
499
499
WaitForDeploymentAvailableInterval []interface {}
500
500
}
501
501
502
+ // DeployUnevictablePod will deploy a Deployment on a ControlPlane or MachineDeployment.
503
+ // It will deploy one Pod replica to each Machine and then deploy a PDB to ensure none of the Pods can be evicted.
502
504
func DeployUnevictablePod (ctx context.Context , input DeployUnevictablePodInput ) {
503
505
Expect (input .DeploymentName ).ToNot (BeNil (), "Need a deployment name in DeployUnevictablePod" )
504
506
Expect (input .Namespace ).ToNot (BeNil (), "Need a namespace in DeployUnevictablePod" )
505
507
Expect (input .WorkloadClusterProxy ).ToNot (BeNil (), "Need a workloadClusterProxy in DeployUnevictablePod" )
508
+ Expect ((input .MachineDeployment == nil && input .ControlPlane != nil ) ||
509
+ (input .MachineDeployment != nil && input .ControlPlane == nil )).To (BeTrue (), "Either MachineDeployment or ControlPlane must be set in DeployUnevictablePod" )
506
510
507
511
EnsureNamespace (ctx , input .WorkloadClusterProxy .GetClient (), input .Namespace )
508
512
509
- workloadDeployment := & appsv1.Deployment {
513
+ workloadDeployment := generateDeployment (generateDeploymentInput {
514
+ ControlPlane : input .ControlPlane ,
515
+ MachineDeployment : input .MachineDeployment ,
516
+ Name : input .DeploymentName ,
517
+ Namespace : input .Namespace ,
518
+ NodeSelector : input .NodeSelector ,
519
+ })
520
+
521
+ workloadClient := input .WorkloadClusterProxy .GetClientSet ()
522
+
523
+ AddDeploymentToWorkloadCluster (ctx , AddDeploymentToWorkloadClusterInput {
524
+ Namespace : input .Namespace ,
525
+ ClientSet : workloadClient ,
526
+ Deployment : workloadDeployment ,
527
+ })
528
+
529
+ budget := & policyv1.PodDisruptionBudget {
510
530
ObjectMeta : metav1.ObjectMeta {
511
531
Name : input .DeploymentName ,
512
532
Namespace : input .Namespace ,
513
533
},
534
+ Spec : policyv1.PodDisruptionBudgetSpec {
535
+ Selector : & metav1.LabelSelector {
536
+ MatchLabels : map [string ]string {
537
+ "app" : "nonstop" ,
538
+ "deployment" : input .DeploymentName ,
539
+ },
540
+ },
541
+ // Setting MaxUnavailable to 0 means no Pods can be evicted / unavailable.
542
+ MaxUnavailable : & intstr.IntOrString {
543
+ Type : intstr .Int ,
544
+ IntVal : 0 ,
545
+ },
546
+ },
547
+ }
548
+
549
+ AddPodDisruptionBudget (ctx , AddPodDisruptionBudgetInput {
550
+ Namespace : input .Namespace ,
551
+ ClientSet : workloadClient ,
552
+ Budget : budget ,
553
+ })
554
+
555
+ WaitForDeploymentsAvailable (ctx , WaitForDeploymentsAvailableInput {
556
+ Getter : input .WorkloadClusterProxy .GetClient (),
557
+ Deployment : workloadDeployment ,
558
+ }, input .WaitForDeploymentAvailableInterval ... )
559
+ }
560
+
561
+ type DeployEvictablePodInput struct {
562
+ WorkloadClusterProxy ClusterProxy
563
+ ControlPlane * controlplanev1.KubeadmControlPlane
564
+ MachineDeployment * clusterv1.MachineDeployment
565
+ DeploymentName string
566
+ Namespace string
567
+ NodeSelector map [string ]string
568
+
569
+ ModifyDeployment func (deployment * appsv1.Deployment )
570
+
571
+ WaitForDeploymentAvailableInterval []interface {}
572
+ }
573
+
574
+ // DeployEvictablePod will deploy a Deployment on a ControlPlane or MachineDeployment.
575
+ // It will deploy one Pod replica to each Machine.
576
+ func DeployEvictablePod (ctx context.Context , input DeployEvictablePodInput ) {
577
+ Expect (input .DeploymentName ).ToNot (BeNil (), "Need a deployment name in DeployUnevictablePod" )
578
+ Expect (input .Namespace ).ToNot (BeNil (), "Need a namespace in DeployUnevictablePod" )
579
+ Expect (input .WorkloadClusterProxy ).ToNot (BeNil (), "Need a workloadClusterProxy in DeployUnevictablePod" )
580
+ Expect ((input .MachineDeployment == nil && input .ControlPlane != nil ) ||
581
+ (input .MachineDeployment != nil && input .ControlPlane == nil )).To (BeTrue (), "Either MachineDeployment or ControlPlane must be set in DeployUnevictablePod" )
582
+
583
+ EnsureNamespace (ctx , input .WorkloadClusterProxy .GetClient (), input .Namespace )
584
+
585
+ workloadDeployment := generateDeployment (generateDeploymentInput {
586
+ ControlPlane : input .ControlPlane ,
587
+ MachineDeployment : input .MachineDeployment ,
588
+ Name : input .DeploymentName ,
589
+ Namespace : input .Namespace ,
590
+ NodeSelector : input .NodeSelector ,
591
+ })
592
+
593
+ input .ModifyDeployment (workloadDeployment )
594
+
595
+ workloadClient := input .WorkloadClusterProxy .GetClientSet ()
596
+
597
+ AddDeploymentToWorkloadCluster (ctx , AddDeploymentToWorkloadClusterInput {
598
+ Namespace : input .Namespace ,
599
+ ClientSet : workloadClient ,
600
+ Deployment : workloadDeployment ,
601
+ })
602
+
603
+ WaitForDeploymentsAvailable (ctx , WaitForDeploymentsAvailableInput {
604
+ Getter : input .WorkloadClusterProxy .GetClient (),
605
+ Deployment : workloadDeployment ,
606
+ }, input .WaitForDeploymentAvailableInterval ... )
607
+ }
608
+
609
+ type generateDeploymentInput struct {
610
+ ControlPlane * controlplanev1.KubeadmControlPlane
611
+ MachineDeployment * clusterv1.MachineDeployment
612
+ Name string
613
+ Namespace string
614
+ NodeSelector map [string ]string
615
+ }
616
+
617
+ func generateDeployment (input generateDeploymentInput ) * appsv1.Deployment {
618
+ workloadDeployment := & appsv1.Deployment {
619
+ ObjectMeta : metav1.ObjectMeta {
620
+ Name : input .Name ,
621
+ Namespace : input .Namespace ,
622
+ },
514
623
Spec : appsv1.DeploymentSpec {
515
- Replicas : ptr.To [int32 ](4 ),
516
624
Selector : & metav1.LabelSelector {
517
625
MatchLabels : map [string ]string {
518
- "app" : "nonstop" ,
626
+ "app" : "nonstop" ,
627
+ "deployment" : input .Name ,
519
628
},
520
629
},
521
630
Template : corev1.PodTemplateSpec {
522
631
ObjectMeta : metav1.ObjectMeta {
523
632
Labels : map [string ]string {
524
- "app" : "nonstop" ,
633
+ "app" : "nonstop" ,
634
+ "deployment" : input .Name ,
525
635
},
526
636
},
527
637
Spec : corev1.PodSpec {
528
638
Containers : []corev1.Container {
529
639
{
530
- Name : "web " ,
640
+ Name : "main " ,
531
641
Image : "registry.k8s.io/pause:3.10" ,
532
642
},
533
643
},
644
+ Affinity : & corev1.Affinity {
645
+ // Make sure only 1 Pod of this Deployment can run on the same Node.
646
+ PodAntiAffinity : & corev1.PodAntiAffinity {
647
+ RequiredDuringSchedulingIgnoredDuringExecution : []corev1.PodAffinityTerm {
648
+ {
649
+ LabelSelector : & metav1.LabelSelector {
650
+ MatchExpressions : []metav1.LabelSelectorRequirement {
651
+ {
652
+ Key : "deployment" ,
653
+ Operator : "In" ,
654
+ Values : []string {input .Name },
655
+ },
656
+ },
657
+ },
658
+ TopologyKey : "kubernetes.io/hostname" ,
659
+ },
660
+ },
661
+ },
662
+ },
534
663
},
535
664
},
536
665
},
537
666
}
538
- workloadClient := input .WorkloadClusterProxy .GetClientSet ()
539
667
540
668
if input .ControlPlane != nil {
541
- var serverVersion * version.Info
542
- Eventually (func () error {
543
- var err error
544
- serverVersion , err = workloadClient .ServerVersion ()
545
- return err
546
- }, retryableOperationTimeout , retryableOperationInterval ).Should (Succeed (), "failed to get server version" )
547
-
548
- // Use the control-plane label for Kubernetes version >= v1.20.0.
549
- if utilversion .MustParseGeneric (serverVersion .String ()).AtLeast (utilversion .MustParseGeneric ("v1.20.0" )) {
550
- workloadDeployment .Spec .Template .Spec .NodeSelector = map [string ]string {nodeRoleControlPlane : "" }
551
- } else {
552
- workloadDeployment .Spec .Template .Spec .NodeSelector = map [string ]string {nodeRoleOldControlPlane : "" }
553
- }
554
-
669
+ workloadDeployment .Spec .Template .Spec .NodeSelector = map [string ]string {nodeRoleControlPlane : "" }
555
670
workloadDeployment .Spec .Template .Spec .Tolerations = []corev1.Toleration {
556
- {
557
- Key : nodeRoleOldControlPlane ,
558
- Effect : "NoSchedule" ,
559
- },
560
671
{
561
672
Key : nodeRoleControlPlane ,
562
673
Effect : "NoSchedule" ,
563
674
},
564
675
}
676
+ workloadDeployment .Spec .Replicas = input .ControlPlane .Spec .Replicas
565
677
}
566
- AddDeploymentToWorkloadCluster (ctx , AddDeploymentToWorkloadClusterInput {
567
- Namespace : input .Namespace ,
568
- ClientSet : workloadClient ,
569
- Deployment : workloadDeployment ,
570
- })
571
-
572
- budget := & policyv1.PodDisruptionBudget {
573
- TypeMeta : metav1.TypeMeta {
574
- Kind : "PodDisruptionBudget" ,
575
- APIVersion : "policy/v1" ,
576
- },
577
- ObjectMeta : metav1.ObjectMeta {
578
- Name : input .DeploymentName ,
579
- Namespace : input .Namespace ,
580
- },
581
- Spec : policyv1.PodDisruptionBudgetSpec {
582
- Selector : & metav1.LabelSelector {
583
- MatchLabels : map [string ]string {
584
- "app" : "nonstop" ,
585
- },
586
- },
587
- MaxUnavailable : & intstr.IntOrString {
588
- Type : intstr .Int ,
589
- IntVal : 1 ,
590
- StrVal : "1" ,
591
- },
592
- },
678
+ if input .MachineDeployment != nil {
679
+ workloadDeployment .Spec .Replicas = input .MachineDeployment .Spec .Replicas
593
680
}
594
681
595
- AddPodDisruptionBudget (ctx , AddPodDisruptionBudgetInput {
596
- Namespace : input .Namespace ,
597
- ClientSet : workloadClient ,
598
- Budget : budget ,
599
- })
682
+ // Note: If set, the NodeSelector field overwrites the NodeSelector we set above for control plane nodes.
683
+ if input .NodeSelector != nil {
684
+ workloadDeployment .Spec .Template .Spec .NodeSelector = input .NodeSelector
685
+ }
600
686
601
- WaitForDeploymentsAvailable (ctx , WaitForDeploymentsAvailableInput {
602
- Getter : input .WorkloadClusterProxy .GetClient (),
603
- Deployment : workloadDeployment ,
604
- }, input .WaitForDeploymentAvailableInterval ... )
687
+ return workloadDeployment
605
688
}
606
689
607
690
type AddDeploymentToWorkloadClusterInput struct {
0 commit comments