@@ -93,6 +93,14 @@ type PersistentVolumeClaimConfig struct {
93
93
VolumeMode * v1.PersistentVolumeMode
94
94
}
95
95
96
+ // NodeSelection specifies where to run a pod, using a combination of fixed node name,
97
+ // node selector and/or affinity.
98
+ type NodeSelection struct {
99
+ Name string
100
+ Selector map [string ]string
101
+ Affinity * v1.Affinity
102
+ }
103
+
96
104
// Clean up a pv and pvc in a single pv/pvc test case.
97
105
// Note: delete errors are appended to []error so that we can attempt to delete both the pvc and pv.
98
106
func PVPVCCleanup (c clientset.Interface , ns string , pv * v1.PersistentVolume , pvc * v1.PersistentVolumeClaim ) []error {
@@ -873,14 +881,16 @@ func CreateNginxPod(client clientset.Interface, namespace string, nodeSelector m
873
881
874
882
// create security pod with given claims
875
883
func CreateSecPod (client clientset.Interface , namespace string , pvclaims []* v1.PersistentVolumeClaim , isPrivileged bool , command string , hostIPC bool , hostPID bool , seLinuxLabel * v1.SELinuxOptions , fsGroup * int64 , timeout time.Duration ) (* v1.Pod , error ) {
876
- return CreateSecPodWithNodeName (client , namespace , pvclaims , isPrivileged , command , hostIPC , hostPID , seLinuxLabel , fsGroup , "" , timeout )
884
+ return CreateSecPodWithNodeSelection (client , namespace , pvclaims , isPrivileged , command , hostIPC , hostPID , seLinuxLabel , fsGroup , NodeSelection {} , timeout )
877
885
}
878
886
879
887
// create security pod with given claims
880
- func CreateSecPodWithNodeName (client clientset.Interface , namespace string , pvclaims []* v1.PersistentVolumeClaim , isPrivileged bool , command string , hostIPC bool , hostPID bool , seLinuxLabel * v1.SELinuxOptions , fsGroup * int64 , nodeName string , timeout time.Duration ) (* v1.Pod , error ) {
888
+ func CreateSecPodWithNodeSelection (client clientset.Interface , namespace string , pvclaims []* v1.PersistentVolumeClaim , isPrivileged bool , command string , hostIPC bool , hostPID bool , seLinuxLabel * v1.SELinuxOptions , fsGroup * int64 , node NodeSelection , timeout time.Duration ) (* v1.Pod , error ) {
881
889
pod := MakeSecPod (namespace , pvclaims , isPrivileged , command , hostIPC , hostPID , seLinuxLabel , fsGroup )
882
- // Setting nodeName
883
- pod .Spec .NodeName = nodeName
890
+ // Setting node
891
+ pod .Spec .NodeName = node .Name
892
+ pod .Spec .NodeSelector = node .Selector
893
+ pod .Spec .Affinity = node .Affinity
884
894
885
895
pod , err := client .CoreV1 ().Pods (namespace ).Create (pod )
886
896
if err != nil {
@@ -900,6 +910,44 @@ func CreateSecPodWithNodeName(client clientset.Interface, namespace string, pvcl
900
910
return pod , nil
901
911
}
902
912
913
+ // SetNodeAffinityRequirement sets affinity with specified operator to nodeName to nodeSelection
914
+ func SetNodeAffinityRequirement (nodeSelection * NodeSelection , operator v1.NodeSelectorOperator , nodeName string ) {
915
+ // Add node-anti-affinity.
916
+ if nodeSelection .Affinity == nil {
917
+ nodeSelection .Affinity = & v1.Affinity {}
918
+ }
919
+ if nodeSelection .Affinity .NodeAffinity == nil {
920
+ nodeSelection .Affinity .NodeAffinity = & v1.NodeAffinity {}
921
+ }
922
+ if nodeSelection .Affinity .NodeAffinity .RequiredDuringSchedulingIgnoredDuringExecution == nil {
923
+ nodeSelection .Affinity .NodeAffinity .RequiredDuringSchedulingIgnoredDuringExecution = & v1.NodeSelector {}
924
+ }
925
+ nodeSelection .Affinity .NodeAffinity .RequiredDuringSchedulingIgnoredDuringExecution .NodeSelectorTerms = append (nodeSelection .Affinity .NodeAffinity .RequiredDuringSchedulingIgnoredDuringExecution .NodeSelectorTerms ,
926
+ v1.NodeSelectorTerm {
927
+ // https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity warns
928
+ // that "the value of kubernetes.io/hostname may be the same as the Node name in some environments and a different value in other environments".
929
+ // So this might be cleaner:
930
+ // MatchFields: []v1.NodeSelectorRequirement{
931
+ // {Key: "name", Operator: v1.NodeSelectorOpNotIn, Values: []string{nodeName}},
932
+ // },
933
+ // However, "name", "Name", "ObjectMeta.Name" all got rejected with "not a valid field selector key".
934
+
935
+ MatchExpressions : []v1.NodeSelectorRequirement {
936
+ {Key : "kubernetes.io/hostname" , Operator : operator , Values : []string {nodeName }},
937
+ },
938
+ })
939
+ }
940
+
941
+ // SetAffinity sets affinity to nodeName to nodeSelection
942
+ func SetAffinity (nodeSelection * NodeSelection , nodeName string ) {
943
+ SetNodeAffinityRequirement (nodeSelection , v1 .NodeSelectorOpIn , nodeName )
944
+ }
945
+
946
+ // SetAntiAffinity sets anti-affinity to nodeName to nodeSelection
947
+ func SetAntiAffinity (nodeSelection * NodeSelection , nodeName string ) {
948
+ SetNodeAffinityRequirement (nodeSelection , v1 .NodeSelectorOpNotIn , nodeName )
949
+ }
950
+
903
951
// Define and create a pod with a mounted PV. Pod runs infinite loop until killed.
904
952
func CreateClientPod (c clientset.Interface , ns string , pvc * v1.PersistentVolumeClaim ) (* v1.Pod , error ) {
905
953
return CreatePod (c , ns , nil , []* v1.PersistentVolumeClaim {pvc }, true , "" )
0 commit comments