@@ -13,6 +13,7 @@ import (
1313 . "github.com/onsi/gomega"
1414
1515 corev1 "k8s.io/api/core/v1"
16+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1617 crclient "sigs.k8s.io/controller-runtime/pkg/client"
1718
1819 imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
@@ -865,6 +866,209 @@ var _ = Describe("Controllers/Podplacement/PodReconciler", func() {
865866 }).Should (Succeed (), "failed to set preferred node affinity in pod" )
866867
867868 })
869+ It ("should set source label tracking for PPC preferences" , func () {
870+ By ("Create an ephemeral namespace" )
871+ ns := NewEphemeralNamespace ()
872+ err := k8sClient .Create (ctx , ns )
873+ Expect (err ).NotTo (HaveOccurred ())
874+ //nolint:errcheck
875+ defer k8sClient .Delete (ctx , ns )
876+ By ("Creating a PodPlacementConfig" )
877+ ppc1 := NewPodPlacementConfig ().
878+ WithName ("test-ppc-source" ).
879+ WithNamespace (ns .Name ).
880+ WithPriority (100 ).
881+ WithNodeAffinityScoring (true ).
882+ WithNodeAffinityScoringTerm (utils .ArchitecturePpc64le , 50 ).
883+ Build ()
884+ Expect (k8sClient .Create (ctx , ppc1 )).To (Succeed ())
885+ By ("Creating a matching pod" )
886+ pod := NewPod ().
887+ WithContainersImages (fmt .Sprintf ("%s/%s/%s:latest" , registryAddress ,
888+ registry .PublicRepo , registry .ComputeNameByMediaType (imgspecv1 .MediaTypeImageManifest ))).
889+ WithGenerateName ("test-pod-" ).
890+ WithNamespace (ns .Name ).
891+ Build ()
892+ Expect (k8sClient .Create (ctx , pod )).To (Succeed ())
893+ By ("Waiting for the pod to be reconciled" )
894+ Eventually (func (g Gomega ) {
895+ err := k8sClient .Get (ctx , crclient .ObjectKeyFromObject (pod ), pod )
896+ g .Expect (err ).NotTo (HaveOccurred ())
897+ g .Expect (pod .Spec .SchedulingGates ).NotTo (ContainElement (corev1.PodSchedulingGate {
898+ Name : utils .SchedulingGateName ,
899+ }), "scheduling gate not removed" )
900+ }).Should (Succeed ())
901+ By ("Verifying the source label is correctly set" )
902+ Eventually (func (g Gomega ) {
903+ // Get pod from the API server
904+ err := k8sClient .Get (ctx , crclient .ObjectKeyFromObject (pod ), pod )
905+ g .Expect (err ).NotTo (HaveOccurred (), "failed to get pod" , err )
906+ g .Expect (pod .Labels ).To (HaveKeyWithValue (utils .PreferredNodeAffinityLabel , utils .NodeAffinityLabelValueSet ),
907+ "preferred node affinity label not found" )
908+ g .Expect (pod .Labels ).To (HaveKeyWithValue (utils .PreferredNodeAffinitySourceLabel , "PodPlacementConfig/test-ppc-source" ),
909+ "source label should indicate PPC" )
910+ }).Should (Succeed (), "failed to set source label" )
911+ })
912+ It ("should set all-duplicates label when lower-priority PPC is blocked" , func () {
913+ By ("Create an ephemeral namespace" )
914+ ns := NewEphemeralNamespace ()
915+ err := k8sClient .Create (ctx , ns )
916+ Expect (err ).NotTo (HaveOccurred ())
917+ //nolint:errcheck
918+ defer k8sClient .Delete (ctx , ns )
919+ By ("Creating high-priority PPC that sets ppc64le" )
920+ ppcHigh := NewPodPlacementConfig ().
921+ WithName ("test-ppc-high" ).
922+ WithNamespace (ns .Name ).
923+ WithPriority (100 ).
924+ WithNodeAffinityScoring (true ).
925+ WithNodeAffinityScoringTerm (utils .ArchitecturePpc64le , 50 ).
926+ Build ()
927+ Expect (k8sClient .Create (ctx , ppcHigh )).To (Succeed ())
928+ By ("Creating lower-priority PPC that also tries to set ppc64le" )
929+ ppcLow := NewPodPlacementConfig ().
930+ WithName ("test-ppc-low" ).
931+ WithNamespace (ns .Name ).
932+ WithPriority (50 ).
933+ WithNodeAffinityScoring (true ).
934+ WithNodeAffinityScoringTerm (utils .ArchitecturePpc64le , 30 ).
935+ Build ()
936+ Expect (k8sClient .Create (ctx , ppcLow )).To (Succeed ())
937+ By ("Creating a matching pod" )
938+ pod := NewPod ().
939+ WithContainersImages (fmt .Sprintf ("%s/%s/%s:latest" , registryAddress ,
940+ registry .PublicRepo , registry .ComputeNameByMediaType (imgspecv1 .MediaTypeImageManifest ))).
941+ WithGenerateName ("test-pod-" ).
942+ WithNamespace (ns .Name ).
943+ Build ()
944+ Expect (k8sClient .Create (ctx , pod )).To (Succeed ())
945+ By ("Waiting for the pod to be reconciled" )
946+ Eventually (func (g Gomega ) {
947+ err := k8sClient .Get (ctx , crclient .ObjectKeyFromObject (pod ), pod )
948+ g .Expect (err ).NotTo (HaveOccurred ())
949+ g .Expect (pod .Spec .SchedulingGates ).NotTo (ContainElement (corev1.PodSchedulingGate {
950+ Name : utils .SchedulingGateName ,
951+ }), "scheduling gate not removed" )
952+ }).Should (Succeed ())
953+ By ("Verifying the preferred affinity is set by high-priority PPC only" )
954+ Eventually (func (g Gomega ) {
955+ // Get pod from the API server
956+ err := k8sClient .Get (ctx , crclient .ObjectKeyFromObject (pod ), pod )
957+ g .Expect (err ).NotTo (HaveOccurred (), "failed to get pod" , err )
958+ g .Expect (pod .Labels ).To (HaveKeyWithValue (utils .PreferredNodeAffinitySourceLabel , "PodPlacementConfig/test-ppc-high" ),
959+ "source should be high-priority PPC" )
960+ preferences := []NodeAffinityTerm {
961+ {Arch : []string {utils .ArchitecturePpc64le }, Weight : 50 },
962+ }
963+ g .Expect (* pod ).To (HaveEquivalentPreferredNodeAffinity (
964+ NewNodeAffinityBuilder ().WithPreferredNodeAffinity (preferences ).Build ()),
965+ "should only have high-priority PPC preferences" )
966+ }).Should (Succeed (), "failed to apply correct preferences" )
967+ })
968+ It ("should apply PPC only to pods matching label selector" , func () {
969+ By ("Create an ephemeral namespace" )
970+ ns := NewEphemeralNamespace ()
971+ err := k8sClient .Create (ctx , ns )
972+ Expect (err ).NotTo (HaveOccurred ())
973+ //nolint:errcheck
974+ defer k8sClient .Delete (ctx , ns )
975+ By ("Creating a PPC with label selector app=backend" )
976+ ppc := NewPodPlacementConfig ().
977+ WithName ("test-ppc-selector" ).
978+ WithNamespace (ns .Name ).
979+ WithPriority (100 ).
980+ WithNodeAffinityScoring (true ).
981+ WithNodeAffinityScoringTerm (utils .ArchitecturePpc64le , 60 ).
982+ WithLabelSelector (& metav1.LabelSelector {
983+ MatchLabels : map [string ]string {"app" : "backend" },
984+ }).
985+ Build ()
986+ Expect (k8sClient .Create (ctx , ppc )).To (Succeed ())
987+ By ("Creating a matching pod with app=backend label" )
988+ matchingPod := NewPod ().
989+ WithContainersImages (fmt .Sprintf ("%s/%s/%s:latest" , registryAddress ,
990+ registry .PublicRepo , registry .ComputeNameByMediaType (imgspecv1 .MediaTypeImageManifest ))).
991+ WithGenerateName ("matching-pod-" ).
992+ WithNamespace (ns .Name ).
993+ WithLabels ("app" , "backend" ).
994+ Build ()
995+ Expect (k8sClient .Create (ctx , matchingPod )).To (Succeed ())
996+ By ("Creating a non-matching pod with app=frontend label" )
997+ nonMatchingPod := NewPod ().
998+ WithContainersImages (fmt .Sprintf ("%s/%s/%s:latest" , registryAddress ,
999+ registry .PublicRepo , registry .ComputeNameByMediaType (imgspecv1 .MediaTypeImageManifest ))).
1000+ WithGenerateName ("non-matching-pod-" ).
1001+ WithNamespace (ns .Name ).
1002+ WithLabels ("app" , "frontend" ).
1003+ Build ()
1004+ Expect (k8sClient .Create (ctx , nonMatchingPod )).To (Succeed ())
1005+ By ("Waiting for matching pod to be reconciled" )
1006+ Eventually (func (g Gomega ) {
1007+ err := k8sClient .Get (ctx , crclient .ObjectKeyFromObject (matchingPod ), matchingPod )
1008+ g .Expect (err ).NotTo (HaveOccurred ())
1009+ g .Expect (matchingPod .Spec .SchedulingGates ).NotTo (ContainElement (corev1.PodSchedulingGate {
1010+ Name : utils .SchedulingGateName ,
1011+ }), "scheduling gate not removed" )
1012+ }).Should (Succeed ())
1013+ By ("Verifying matching pod gets PPC preferences" )
1014+ Eventually (func (g Gomega ) {
1015+ err := k8sClient .Get (ctx , crclient .ObjectKeyFromObject (matchingPod ), matchingPod )
1016+ g .Expect (err ).NotTo (HaveOccurred ())
1017+ g .Expect (matchingPod .Labels ).To (HaveKeyWithValue (utils .PreferredNodeAffinitySourceLabel , "PodPlacementConfig/test-ppc-selector" ))
1018+ }).Should (Succeed ())
1019+ By ("Verifying non-matching pod gets CPPC preferences" )
1020+ Eventually (func (g Gomega ) {
1021+ err := k8sClient .Get (ctx , crclient .ObjectKeyFromObject (nonMatchingPod ), nonMatchingPod )
1022+ g .Expect (err ).NotTo (HaveOccurred ())
1023+ g .Expect (nonMatchingPod .Labels ).To (HaveKeyWithValue (utils .PreferredNodeAffinitySourceLabel , v1beta1 .ClusterPodPlacementConfigKind ))
1024+ }).Should (Succeed ())
1025+ })
1026+ It ("should apply PPC with empty label selector to all pods" , func () {
1027+ By ("Create an ephemeral namespace" )
1028+ ns := NewEphemeralNamespace ()
1029+ err := k8sClient .Create (ctx , ns )
1030+ Expect (err ).NotTo (HaveOccurred ())
1031+ //nolint:errcheck
1032+ defer k8sClient .Delete (ctx , ns )
1033+ By ("Creating a PPC with nil label selector" )
1034+ ppc := NewPodPlacementConfig ().
1035+ WithName ("test-ppc-empty-selector" ).
1036+ WithNamespace (ns .Name ).
1037+ WithPriority (100 ).
1038+ WithNodeAffinityScoring (true ).
1039+ WithNodeAffinityScoringTerm (utils .ArchitecturePpc64le , 70 ).
1040+ Build ()
1041+ Expect (k8sClient .Create (ctx , ppc )).To (Succeed ())
1042+ By ("Creating a pod without specific labels" )
1043+ pod := NewPod ().
1044+ WithContainersImages (fmt .Sprintf ("%s/%s/%s:latest" , registryAddress ,
1045+ registry .PublicRepo , registry .ComputeNameByMediaType (imgspecv1 .MediaTypeImageManifest ))).
1046+ WithGenerateName ("test-pod-" ).
1047+ WithNamespace (ns .Name ).
1048+ Build ()
1049+ Expect (k8sClient .Create (ctx , pod )).To (Succeed ())
1050+ By ("Waiting for the pod to be reconciled" )
1051+ Eventually (func (g Gomega ) {
1052+ err := k8sClient .Get (ctx , crclient .ObjectKeyFromObject (pod ), pod )
1053+ g .Expect (err ).NotTo (HaveOccurred ())
1054+ g .Expect (pod .Spec .SchedulingGates ).NotTo (ContainElement (corev1.PodSchedulingGate {
1055+ Name : utils .SchedulingGateName ,
1056+ }), "scheduling gate not removed" )
1057+ }).Should (Succeed ())
1058+ By ("Verifying pod gets PPC preferences (empty selector matches all)" )
1059+ Eventually (func (g Gomega ) {
1060+ err := k8sClient .Get (ctx , crclient .ObjectKeyFromObject (pod ), pod )
1061+ g .Expect (err ).NotTo (HaveOccurred ())
1062+ g .Expect (pod .Labels ).To (HaveKeyWithValue (utils .PreferredNodeAffinitySourceLabel , "PodPlacementConfig/test-ppc-empty-selector" ))
1063+ preferences := []NodeAffinityTerm {
1064+ {Arch : []string {utils .ArchitecturePpc64le }, Weight : 70 },
1065+ {Arch : []string {utils .ArchitectureArm64 }, Weight : 50 },
1066+ }
1067+ g .Expect (* pod ).To (HaveEquivalentPreferredNodeAffinity (
1068+ NewNodeAffinityBuilder ().WithPreferredNodeAffinity (preferences ).Build ()),
1069+ "should have PPC and CPPC preferences" )
1070+ }).Should (Succeed ())
1071+ })
8681072 })
8691073 })
8701074})
0 commit comments