metadata_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package predicates
  14. import (
  15. "fmt"
  16. "reflect"
  17. "sort"
  18. "testing"
  19. "k8s.io/api/core/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
  22. schedulertesting "k8s.io/kubernetes/pkg/scheduler/testing"
  23. )
  24. // sortablePods lets us to sort pods.
  25. type sortablePods []*v1.Pod
  26. func (s sortablePods) Less(i, j int) bool {
  27. return s[i].Namespace < s[j].Namespace ||
  28. (s[i].Namespace == s[j].Namespace && s[i].Name < s[j].Name)
  29. }
  30. func (s sortablePods) Len() int { return len(s) }
  31. func (s sortablePods) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  32. var _ = sort.Interface(&sortablePods{})
  33. // sortableServices allows us to sort services.
  34. type sortableServices []*v1.Service
  35. func (s sortableServices) Less(i, j int) bool {
  36. return s[i].Namespace < s[j].Namespace ||
  37. (s[i].Namespace == s[j].Namespace && s[i].Name < s[j].Name)
  38. }
  39. func (s sortableServices) Len() int { return len(s) }
  40. func (s sortableServices) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
  41. var _ = sort.Interface(&sortableServices{})
  42. // predicateMetadataEquivalent returns true if the two metadata are equivalent.
  43. // Note: this function does not compare podRequest.
  44. func predicateMetadataEquivalent(meta1, meta2 *predicateMetadata) error {
  45. if !reflect.DeepEqual(meta1.pod, meta2.pod) {
  46. return fmt.Errorf("pods are not the same")
  47. }
  48. if meta1.podBestEffort != meta2.podBestEffort {
  49. return fmt.Errorf("podBestEfforts are not equal")
  50. }
  51. if meta1.serviceAffinityInUse != meta1.serviceAffinityInUse {
  52. return fmt.Errorf("serviceAffinityInUses are not equal")
  53. }
  54. if len(meta1.podPorts) != len(meta2.podPorts) {
  55. return fmt.Errorf("podPorts are not equal")
  56. }
  57. for !reflect.DeepEqual(meta1.podPorts, meta2.podPorts) {
  58. return fmt.Errorf("podPorts are not equal")
  59. }
  60. if !reflect.DeepEqual(meta1.topologyPairsPotentialAffinityPods, meta2.topologyPairsPotentialAffinityPods) {
  61. return fmt.Errorf("topologyPairsPotentialAffinityPods are not equal")
  62. }
  63. if !reflect.DeepEqual(meta1.topologyPairsPotentialAntiAffinityPods, meta2.topologyPairsPotentialAntiAffinityPods) {
  64. return fmt.Errorf("topologyPairsPotentialAntiAffinityPods are not equal")
  65. }
  66. if !reflect.DeepEqual(meta1.topologyPairsAntiAffinityPodsMap.podToTopologyPairs,
  67. meta2.topologyPairsAntiAffinityPodsMap.podToTopologyPairs) {
  68. return fmt.Errorf("topologyPairsAntiAffinityPodsMap.podToTopologyPairs are not equal")
  69. }
  70. if !reflect.DeepEqual(meta1.topologyPairsAntiAffinityPodsMap.topologyPairToPods,
  71. meta2.topologyPairsAntiAffinityPodsMap.topologyPairToPods) {
  72. return fmt.Errorf("topologyPairsAntiAffinityPodsMap.topologyPairToPods are not equal")
  73. }
  74. if meta1.serviceAffinityInUse {
  75. sortablePods1 := sortablePods(meta1.serviceAffinityMatchingPodList)
  76. sort.Sort(sortablePods1)
  77. sortablePods2 := sortablePods(meta2.serviceAffinityMatchingPodList)
  78. sort.Sort(sortablePods2)
  79. if !reflect.DeepEqual(sortablePods1, sortablePods2) {
  80. return fmt.Errorf("serviceAffinityMatchingPodLists are not euqal")
  81. }
  82. sortableServices1 := sortableServices(meta1.serviceAffinityMatchingPodServices)
  83. sort.Sort(sortableServices1)
  84. sortableServices2 := sortableServices(meta2.serviceAffinityMatchingPodServices)
  85. sort.Sort(sortableServices2)
  86. if !reflect.DeepEqual(sortableServices1, sortableServices2) {
  87. return fmt.Errorf("serviceAffinityMatchingPodServices are not euqal")
  88. }
  89. }
  90. return nil
  91. }
  92. func TestPredicateMetadata_AddRemovePod(t *testing.T) {
  93. var label1 = map[string]string{
  94. "region": "r1",
  95. "zone": "z11",
  96. }
  97. var label2 = map[string]string{
  98. "region": "r1",
  99. "zone": "z12",
  100. }
  101. var label3 = map[string]string{
  102. "region": "r2",
  103. "zone": "z21",
  104. }
  105. selector1 := map[string]string{"foo": "bar"}
  106. antiAffinityFooBar := &v1.PodAntiAffinity{
  107. RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  108. {
  109. LabelSelector: &metav1.LabelSelector{
  110. MatchExpressions: []metav1.LabelSelectorRequirement{
  111. {
  112. Key: "foo",
  113. Operator: metav1.LabelSelectorOpIn,
  114. Values: []string{"bar"},
  115. },
  116. },
  117. },
  118. TopologyKey: "region",
  119. },
  120. },
  121. }
  122. antiAffinityComplex := &v1.PodAntiAffinity{
  123. RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  124. {
  125. LabelSelector: &metav1.LabelSelector{
  126. MatchExpressions: []metav1.LabelSelectorRequirement{
  127. {
  128. Key: "foo",
  129. Operator: metav1.LabelSelectorOpIn,
  130. Values: []string{"bar", "buzz"},
  131. },
  132. },
  133. },
  134. TopologyKey: "region",
  135. },
  136. {
  137. LabelSelector: &metav1.LabelSelector{
  138. MatchExpressions: []metav1.LabelSelectorRequirement{
  139. {
  140. Key: "service",
  141. Operator: metav1.LabelSelectorOpNotIn,
  142. Values: []string{"bar", "security", "test"},
  143. },
  144. },
  145. },
  146. TopologyKey: "zone",
  147. },
  148. },
  149. }
  150. affinityComplex := &v1.PodAffinity{
  151. RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  152. {
  153. LabelSelector: &metav1.LabelSelector{
  154. MatchExpressions: []metav1.LabelSelectorRequirement{
  155. {
  156. Key: "foo",
  157. Operator: metav1.LabelSelectorOpIn,
  158. Values: []string{"bar", "buzz"},
  159. },
  160. },
  161. },
  162. TopologyKey: "region",
  163. },
  164. {
  165. LabelSelector: &metav1.LabelSelector{
  166. MatchExpressions: []metav1.LabelSelectorRequirement{
  167. {
  168. Key: "service",
  169. Operator: metav1.LabelSelectorOpNotIn,
  170. Values: []string{"bar", "security", "test"},
  171. },
  172. },
  173. },
  174. TopologyKey: "zone",
  175. },
  176. },
  177. }
  178. tests := []struct {
  179. name string
  180. pendingPod *v1.Pod
  181. addedPod *v1.Pod
  182. existingPods []*v1.Pod
  183. nodes []*v1.Node
  184. services []*v1.Service
  185. }{
  186. {
  187. name: "no anti-affinity or service affinity exist",
  188. pendingPod: &v1.Pod{
  189. ObjectMeta: metav1.ObjectMeta{Name: "pending", Labels: selector1},
  190. },
  191. existingPods: []*v1.Pod{
  192. {ObjectMeta: metav1.ObjectMeta{Name: "p1", Labels: selector1},
  193. Spec: v1.PodSpec{NodeName: "nodeA"},
  194. },
  195. {ObjectMeta: metav1.ObjectMeta{Name: "p2"},
  196. Spec: v1.PodSpec{NodeName: "nodeC"},
  197. },
  198. },
  199. addedPod: &v1.Pod{
  200. ObjectMeta: metav1.ObjectMeta{Name: "addedPod", Labels: selector1},
  201. Spec: v1.PodSpec{NodeName: "nodeB"},
  202. },
  203. nodes: []*v1.Node{
  204. {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: label1}},
  205. {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: label2}},
  206. {ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: label3}},
  207. },
  208. },
  209. {
  210. name: "metadata anti-affinity terms are updated correctly after adding and removing a pod",
  211. pendingPod: &v1.Pod{
  212. ObjectMeta: metav1.ObjectMeta{Name: "pending", Labels: selector1},
  213. },
  214. existingPods: []*v1.Pod{
  215. {ObjectMeta: metav1.ObjectMeta{Name: "p1", Labels: selector1},
  216. Spec: v1.PodSpec{NodeName: "nodeA"},
  217. },
  218. {ObjectMeta: metav1.ObjectMeta{Name: "p2"},
  219. Spec: v1.PodSpec{
  220. NodeName: "nodeC",
  221. Affinity: &v1.Affinity{
  222. PodAntiAffinity: antiAffinityFooBar,
  223. },
  224. },
  225. },
  226. },
  227. addedPod: &v1.Pod{
  228. ObjectMeta: metav1.ObjectMeta{Name: "addedPod", Labels: selector1},
  229. Spec: v1.PodSpec{
  230. NodeName: "nodeB",
  231. Affinity: &v1.Affinity{
  232. PodAntiAffinity: antiAffinityFooBar,
  233. },
  234. },
  235. },
  236. nodes: []*v1.Node{
  237. {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: label1}},
  238. {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: label2}},
  239. {ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: label3}},
  240. },
  241. },
  242. {
  243. name: "metadata service-affinity data are updated correctly after adding and removing a pod",
  244. pendingPod: &v1.Pod{
  245. ObjectMeta: metav1.ObjectMeta{Name: "pending", Labels: selector1},
  246. },
  247. existingPods: []*v1.Pod{
  248. {ObjectMeta: metav1.ObjectMeta{Name: "p1", Labels: selector1},
  249. Spec: v1.PodSpec{NodeName: "nodeA"},
  250. },
  251. {ObjectMeta: metav1.ObjectMeta{Name: "p2"},
  252. Spec: v1.PodSpec{NodeName: "nodeC"},
  253. },
  254. },
  255. addedPod: &v1.Pod{
  256. ObjectMeta: metav1.ObjectMeta{Name: "addedPod", Labels: selector1},
  257. Spec: v1.PodSpec{NodeName: "nodeB"},
  258. },
  259. services: []*v1.Service{{Spec: v1.ServiceSpec{Selector: selector1}}},
  260. nodes: []*v1.Node{
  261. {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: label1}},
  262. {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: label2}},
  263. {ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: label3}},
  264. },
  265. },
  266. {
  267. name: "metadata anti-affinity terms and service affinity data are updated correctly after adding and removing a pod",
  268. pendingPod: &v1.Pod{
  269. ObjectMeta: metav1.ObjectMeta{Name: "pending", Labels: selector1},
  270. },
  271. existingPods: []*v1.Pod{
  272. {ObjectMeta: metav1.ObjectMeta{Name: "p1", Labels: selector1},
  273. Spec: v1.PodSpec{NodeName: "nodeA"},
  274. },
  275. {ObjectMeta: metav1.ObjectMeta{Name: "p2"},
  276. Spec: v1.PodSpec{
  277. NodeName: "nodeC",
  278. Affinity: &v1.Affinity{
  279. PodAntiAffinity: antiAffinityFooBar,
  280. },
  281. },
  282. },
  283. },
  284. addedPod: &v1.Pod{
  285. ObjectMeta: metav1.ObjectMeta{Name: "addedPod", Labels: selector1},
  286. Spec: v1.PodSpec{
  287. NodeName: "nodeA",
  288. Affinity: &v1.Affinity{
  289. PodAntiAffinity: antiAffinityComplex,
  290. },
  291. },
  292. },
  293. services: []*v1.Service{{Spec: v1.ServiceSpec{Selector: selector1}}},
  294. nodes: []*v1.Node{
  295. {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: label1}},
  296. {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: label2}},
  297. {ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: label3}},
  298. },
  299. },
  300. {
  301. name: "metadata matching pod affinity and anti-affinity are updated correctly after adding and removing a pod",
  302. pendingPod: &v1.Pod{
  303. ObjectMeta: metav1.ObjectMeta{Name: "pending", Labels: selector1},
  304. },
  305. existingPods: []*v1.Pod{
  306. {ObjectMeta: metav1.ObjectMeta{Name: "p1", Labels: selector1},
  307. Spec: v1.PodSpec{NodeName: "nodeA"},
  308. },
  309. {ObjectMeta: metav1.ObjectMeta{Name: "p2"},
  310. Spec: v1.PodSpec{
  311. NodeName: "nodeC",
  312. Affinity: &v1.Affinity{
  313. PodAntiAffinity: antiAffinityFooBar,
  314. PodAffinity: affinityComplex,
  315. },
  316. },
  317. },
  318. },
  319. addedPod: &v1.Pod{
  320. ObjectMeta: metav1.ObjectMeta{Name: "addedPod", Labels: selector1},
  321. Spec: v1.PodSpec{
  322. NodeName: "nodeA",
  323. Affinity: &v1.Affinity{
  324. PodAntiAffinity: antiAffinityComplex,
  325. },
  326. },
  327. },
  328. services: []*v1.Service{{Spec: v1.ServiceSpec{Selector: selector1}}},
  329. nodes: []*v1.Node{
  330. {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: label1}},
  331. {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: label2}},
  332. {ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: label3}},
  333. },
  334. },
  335. }
  336. for _, test := range tests {
  337. t.Run(test.name, func(t *testing.T) {
  338. allPodLister := schedulertesting.FakePodLister(append(test.existingPods, test.addedPod))
  339. // getMeta creates predicate meta data given the list of pods.
  340. getMeta := func(lister schedulertesting.FakePodLister) (*predicateMetadata, map[string]*schedulernodeinfo.NodeInfo) {
  341. nodeInfoMap := schedulernodeinfo.CreateNodeNameToInfoMap(lister, test.nodes)
  342. // nodeList is a list of non-pointer nodes to feed to FakeNodeListInfo.
  343. nodeList := []v1.Node{}
  344. for _, n := range test.nodes {
  345. nodeList = append(nodeList, *n)
  346. }
  347. _, precompute := NewServiceAffinityPredicate(lister, schedulertesting.FakeServiceLister(test.services), FakeNodeListInfo(nodeList), nil)
  348. RegisterPredicateMetadataProducer("ServiceAffinityMetaProducer", precompute)
  349. pmf := PredicateMetadataFactory{lister}
  350. meta := pmf.GetMetadata(test.pendingPod, nodeInfoMap)
  351. return meta.(*predicateMetadata), nodeInfoMap
  352. }
  353. // allPodsMeta is meta data produced when all pods, including test.addedPod
  354. // are given to the metadata producer.
  355. allPodsMeta, _ := getMeta(allPodLister)
  356. // existingPodsMeta1 is meta data produced for test.existingPods (without test.addedPod).
  357. existingPodsMeta1, nodeInfoMap := getMeta(schedulertesting.FakePodLister(test.existingPods))
  358. // Add test.addedPod to existingPodsMeta1 and make sure meta is equal to allPodsMeta
  359. nodeInfo := nodeInfoMap[test.addedPod.Spec.NodeName]
  360. if err := existingPodsMeta1.AddPod(test.addedPod, nodeInfo); err != nil {
  361. t.Errorf("error adding pod to meta: %v", err)
  362. }
  363. if err := predicateMetadataEquivalent(allPodsMeta, existingPodsMeta1); err != nil {
  364. t.Errorf("meta data are not equivalent: %v", err)
  365. }
  366. // Remove the added pod and from existingPodsMeta1 an make sure it is equal
  367. // to meta generated for existing pods.
  368. existingPodsMeta2, _ := getMeta(schedulertesting.FakePodLister(test.existingPods))
  369. if err := existingPodsMeta1.RemovePod(test.addedPod); err != nil {
  370. t.Errorf("error removing pod from meta: %v", err)
  371. }
  372. if err := predicateMetadataEquivalent(existingPodsMeta1, existingPodsMeta2); err != nil {
  373. t.Errorf("meta data are not equivalent: %v", err)
  374. }
  375. })
  376. }
  377. }
  378. // TestPredicateMetadata_ShallowCopy tests the ShallowCopy function. It is based
  379. // on the idea that shallow-copy should produce an object that is deep-equal to the original
  380. // object.
  381. func TestPredicateMetadata_ShallowCopy(t *testing.T) {
  382. selector1 := map[string]string{"foo": "bar"}
  383. source := predicateMetadata{
  384. pod: &v1.Pod{
  385. ObjectMeta: metav1.ObjectMeta{
  386. Name: "test",
  387. Namespace: "testns",
  388. },
  389. },
  390. podBestEffort: true,
  391. podRequest: &schedulernodeinfo.Resource{
  392. MilliCPU: 1000,
  393. Memory: 300,
  394. AllowedPodNumber: 4,
  395. },
  396. podPorts: []*v1.ContainerPort{
  397. {
  398. Name: "name",
  399. HostPort: 10,
  400. ContainerPort: 20,
  401. Protocol: "TCP",
  402. HostIP: "1.2.3.4",
  403. },
  404. },
  405. topologyPairsAntiAffinityPodsMap: &topologyPairsMaps{
  406. topologyPairToPods: map[topologyPair]podSet{
  407. {key: "name", value: "machine1"}: {
  408. &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p2", Labels: selector1},
  409. Spec: v1.PodSpec{NodeName: "nodeC"},
  410. }: struct{}{},
  411. },
  412. {key: "name", value: "machine2"}: {
  413. &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p1", Labels: selector1},
  414. Spec: v1.PodSpec{NodeName: "nodeA"},
  415. }: struct{}{},
  416. },
  417. },
  418. podToTopologyPairs: map[string]topologyPairSet{
  419. "p2_": {
  420. topologyPair{key: "name", value: "machine1"}: struct{}{},
  421. },
  422. "p1_": {
  423. topologyPair{key: "name", value: "machine2"}: struct{}{},
  424. },
  425. },
  426. },
  427. topologyPairsPotentialAffinityPods: &topologyPairsMaps{
  428. topologyPairToPods: map[topologyPair]podSet{
  429. {key: "name", value: "nodeA"}: {
  430. &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p1", Labels: selector1},
  431. Spec: v1.PodSpec{NodeName: "nodeA"},
  432. }: struct{}{},
  433. },
  434. {key: "name", value: "nodeC"}: {
  435. &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p2"},
  436. Spec: v1.PodSpec{
  437. NodeName: "nodeC",
  438. },
  439. }: struct{}{},
  440. &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p6", Labels: selector1},
  441. Spec: v1.PodSpec{NodeName: "nodeC"},
  442. }: struct{}{},
  443. },
  444. },
  445. podToTopologyPairs: map[string]topologyPairSet{
  446. "p1_": {
  447. topologyPair{key: "name", value: "nodeA"}: struct{}{},
  448. },
  449. "p2_": {
  450. topologyPair{key: "name", value: "nodeC"}: struct{}{},
  451. },
  452. "p6_": {
  453. topologyPair{key: "name", value: "nodeC"}: struct{}{},
  454. },
  455. },
  456. },
  457. topologyPairsPotentialAntiAffinityPods: &topologyPairsMaps{
  458. topologyPairToPods: map[topologyPair]podSet{
  459. {key: "name", value: "nodeN"}: {
  460. &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p1", Labels: selector1},
  461. Spec: v1.PodSpec{NodeName: "nodeN"},
  462. }: struct{}{},
  463. &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p2"},
  464. Spec: v1.PodSpec{
  465. NodeName: "nodeM",
  466. },
  467. }: struct{}{},
  468. &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p3"},
  469. Spec: v1.PodSpec{
  470. NodeName: "nodeM",
  471. },
  472. }: struct{}{},
  473. },
  474. {key: "name", value: "nodeM"}: {
  475. &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "p6", Labels: selector1},
  476. Spec: v1.PodSpec{NodeName: "nodeM"},
  477. }: struct{}{},
  478. },
  479. },
  480. podToTopologyPairs: map[string]topologyPairSet{
  481. "p1_": {
  482. topologyPair{key: "name", value: "nodeN"}: struct{}{},
  483. },
  484. "p2_": {
  485. topologyPair{key: "name", value: "nodeN"}: struct{}{},
  486. },
  487. "p3_": {
  488. topologyPair{key: "name", value: "nodeN"}: struct{}{},
  489. },
  490. "p6_": {
  491. topologyPair{key: "name", value: "nodeM"}: struct{}{},
  492. },
  493. },
  494. },
  495. serviceAffinityInUse: true,
  496. serviceAffinityMatchingPodList: []*v1.Pod{
  497. {ObjectMeta: metav1.ObjectMeta{Name: "pod1"}},
  498. {ObjectMeta: metav1.ObjectMeta{Name: "pod2"}},
  499. },
  500. serviceAffinityMatchingPodServices: []*v1.Service{
  501. {ObjectMeta: metav1.ObjectMeta{Name: "service1"}},
  502. },
  503. }
  504. if !reflect.DeepEqual(source.ShallowCopy().(*predicateMetadata), &source) {
  505. t.Errorf("Copy is not equal to source!")
  506. }
  507. }
  508. // TestGetTPMapMatchingIncomingAffinityAntiAffinity tests against method getTPMapMatchingIncomingAffinityAntiAffinity
  509. // on Anti Affinity cases
  510. func TestGetTPMapMatchingIncomingAffinityAntiAffinity(t *testing.T) {
  511. newPodAffinityTerms := func(keys ...string) []v1.PodAffinityTerm {
  512. var terms []v1.PodAffinityTerm
  513. for _, key := range keys {
  514. terms = append(terms, v1.PodAffinityTerm{
  515. LabelSelector: &metav1.LabelSelector{
  516. MatchExpressions: []metav1.LabelSelectorRequirement{
  517. {
  518. Key: key,
  519. Operator: metav1.LabelSelectorOpExists,
  520. },
  521. },
  522. },
  523. TopologyKey: "hostname",
  524. })
  525. }
  526. return terms
  527. }
  528. newPod := func(labels ...string) *v1.Pod {
  529. labelMap := make(map[string]string)
  530. for _, l := range labels {
  531. labelMap[l] = ""
  532. }
  533. return &v1.Pod{
  534. ObjectMeta: metav1.ObjectMeta{Name: "normal", Labels: labelMap},
  535. Spec: v1.PodSpec{NodeName: "nodeA"},
  536. }
  537. }
  538. normalPodA := newPod("aaa")
  539. normalPodB := newPod("bbb")
  540. normalPodAB := newPod("aaa", "bbb")
  541. nodeA := &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"hostname": "nodeA"}}}
  542. tests := []struct {
  543. name string
  544. existingPods []*v1.Pod
  545. nodes []*v1.Node
  546. pod *v1.Pod
  547. wantAffinityPodsMaps *topologyPairsMaps
  548. wantAntiAffinityPodsMaps *topologyPairsMaps
  549. wantErr bool
  550. }{
  551. {
  552. name: "nil test",
  553. nodes: []*v1.Node{nodeA},
  554. pod: &v1.Pod{
  555. ObjectMeta: metav1.ObjectMeta{Name: "aaa-normal"},
  556. },
  557. wantAffinityPodsMaps: newTopologyPairsMaps(),
  558. wantAntiAffinityPodsMaps: newTopologyPairsMaps(),
  559. },
  560. {
  561. name: "incoming pod without affinity/anti-affinity causes a no-op",
  562. existingPods: []*v1.Pod{normalPodA},
  563. nodes: []*v1.Node{nodeA},
  564. pod: &v1.Pod{
  565. ObjectMeta: metav1.ObjectMeta{Name: "aaa-normal"},
  566. },
  567. wantAffinityPodsMaps: newTopologyPairsMaps(),
  568. wantAntiAffinityPodsMaps: newTopologyPairsMaps(),
  569. },
  570. {
  571. name: "no pod has label that violates incoming pod's affinity and anti-affinity",
  572. existingPods: []*v1.Pod{normalPodB},
  573. nodes: []*v1.Node{nodeA},
  574. pod: &v1.Pod{
  575. ObjectMeta: metav1.ObjectMeta{Name: "aaa-anti"},
  576. Spec: v1.PodSpec{
  577. Affinity: &v1.Affinity{
  578. PodAffinity: &v1.PodAffinity{
  579. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa"),
  580. },
  581. PodAntiAffinity: &v1.PodAntiAffinity{
  582. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa"),
  583. },
  584. },
  585. },
  586. },
  587. wantAffinityPodsMaps: newTopologyPairsMaps(),
  588. wantAntiAffinityPodsMaps: newTopologyPairsMaps(),
  589. },
  590. {
  591. name: "existing pod matches incoming pod's affinity and anti-affinity - single term case",
  592. existingPods: []*v1.Pod{normalPodA},
  593. nodes: []*v1.Node{nodeA},
  594. pod: &v1.Pod{
  595. ObjectMeta: metav1.ObjectMeta{Name: "affi-antiaffi"},
  596. Spec: v1.PodSpec{
  597. Affinity: &v1.Affinity{
  598. PodAffinity: &v1.PodAffinity{
  599. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa"),
  600. },
  601. PodAntiAffinity: &v1.PodAntiAffinity{
  602. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa"),
  603. },
  604. },
  605. },
  606. },
  607. wantAffinityPodsMaps: &topologyPairsMaps{
  608. topologyPairToPods: map[topologyPair]podSet{
  609. {key: "hostname", value: "nodeA"}: {normalPodA: struct{}{}},
  610. },
  611. podToTopologyPairs: map[string]topologyPairSet{
  612. "normal_": {
  613. topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
  614. },
  615. },
  616. },
  617. wantAntiAffinityPodsMaps: &topologyPairsMaps{
  618. topologyPairToPods: map[topologyPair]podSet{
  619. {key: "hostname", value: "nodeA"}: {normalPodA: struct{}{}},
  620. },
  621. podToTopologyPairs: map[string]topologyPairSet{
  622. "normal_": {
  623. topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
  624. },
  625. },
  626. },
  627. },
  628. {
  629. name: "existing pod matches incoming pod's affinity and anti-affinity - mutiple terms case",
  630. existingPods: []*v1.Pod{normalPodAB},
  631. nodes: []*v1.Node{nodeA},
  632. pod: &v1.Pod{
  633. ObjectMeta: metav1.ObjectMeta{Name: "affi-antiaffi"},
  634. Spec: v1.PodSpec{
  635. Affinity: &v1.Affinity{
  636. PodAffinity: &v1.PodAffinity{
  637. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "bbb"),
  638. },
  639. PodAntiAffinity: &v1.PodAntiAffinity{
  640. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa"),
  641. },
  642. },
  643. },
  644. },
  645. wantAffinityPodsMaps: &topologyPairsMaps{
  646. topologyPairToPods: map[topologyPair]podSet{
  647. {key: "hostname", value: "nodeA"}: {normalPodAB: struct{}{}},
  648. },
  649. podToTopologyPairs: map[string]topologyPairSet{
  650. "normal_": {
  651. topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
  652. },
  653. },
  654. },
  655. wantAntiAffinityPodsMaps: &topologyPairsMaps{
  656. topologyPairToPods: map[topologyPair]podSet{
  657. {key: "hostname", value: "nodeA"}: {normalPodAB: struct{}{}},
  658. },
  659. podToTopologyPairs: map[string]topologyPairSet{
  660. "normal_": {
  661. topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
  662. },
  663. },
  664. },
  665. },
  666. {
  667. name: "existing pod not match incoming pod's affinity but matches anti-affinity",
  668. existingPods: []*v1.Pod{normalPodA},
  669. nodes: []*v1.Node{nodeA},
  670. pod: &v1.Pod{
  671. ObjectMeta: metav1.ObjectMeta{Name: "affi-antiaffi"},
  672. Spec: v1.PodSpec{
  673. Affinity: &v1.Affinity{
  674. PodAffinity: &v1.PodAffinity{
  675. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "bbb"),
  676. },
  677. PodAntiAffinity: &v1.PodAntiAffinity{
  678. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "bbb"),
  679. },
  680. },
  681. },
  682. },
  683. wantAffinityPodsMaps: newTopologyPairsMaps(),
  684. wantAntiAffinityPodsMaps: &topologyPairsMaps{
  685. topologyPairToPods: map[topologyPair]podSet{
  686. {key: "hostname", value: "nodeA"}: {normalPodA: struct{}{}},
  687. },
  688. podToTopologyPairs: map[string]topologyPairSet{
  689. "normal_": {
  690. topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
  691. },
  692. },
  693. },
  694. },
  695. {
  696. name: "incoming pod's anti-affinity has more than one term - existing pod violates partial term - case 1",
  697. existingPods: []*v1.Pod{normalPodAB},
  698. nodes: []*v1.Node{nodeA},
  699. pod: &v1.Pod{
  700. ObjectMeta: metav1.ObjectMeta{Name: "anaffi-antiaffiti"},
  701. Spec: v1.PodSpec{
  702. Affinity: &v1.Affinity{
  703. PodAffinity: &v1.PodAffinity{
  704. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "ccc"),
  705. },
  706. PodAntiAffinity: &v1.PodAntiAffinity{
  707. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "ccc"),
  708. },
  709. },
  710. },
  711. },
  712. wantAffinityPodsMaps: newTopologyPairsMaps(),
  713. wantAntiAffinityPodsMaps: &topologyPairsMaps{
  714. topologyPairToPods: map[topologyPair]podSet{
  715. {key: "hostname", value: "nodeA"}: {normalPodAB: struct{}{}},
  716. },
  717. podToTopologyPairs: map[string]topologyPairSet{
  718. "normal_": {
  719. topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
  720. },
  721. },
  722. },
  723. },
  724. {
  725. name: "incoming pod's anti-affinity has more than one term - existing pod violates partial term - case 2",
  726. existingPods: []*v1.Pod{normalPodB},
  727. nodes: []*v1.Node{nodeA},
  728. pod: &v1.Pod{
  729. ObjectMeta: metav1.ObjectMeta{Name: "affi-antiaffi"},
  730. Spec: v1.PodSpec{
  731. Affinity: &v1.Affinity{
  732. PodAffinity: &v1.PodAffinity{
  733. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "bbb"),
  734. },
  735. PodAntiAffinity: &v1.PodAntiAffinity{
  736. RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "bbb"),
  737. },
  738. },
  739. },
  740. },
  741. wantAffinityPodsMaps: newTopologyPairsMaps(),
  742. wantAntiAffinityPodsMaps: &topologyPairsMaps{
  743. topologyPairToPods: map[topologyPair]podSet{
  744. {key: "hostname", value: "nodeA"}: {normalPodB: struct{}{}},
  745. },
  746. podToTopologyPairs: map[string]topologyPairSet{
  747. "normal_": {
  748. topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
  749. },
  750. },
  751. },
  752. },
  753. }
  754. for _, tt := range tests {
  755. t.Run(tt.name, func(t *testing.T) {
  756. nodeInfoMap := schedulernodeinfo.CreateNodeNameToInfoMap(tt.existingPods, tt.nodes)
  757. gotAffinityPodsMaps, gotAntiAffinityPodsMaps, err := getTPMapMatchingIncomingAffinityAntiAffinity(tt.pod, nodeInfoMap)
  758. if (err != nil) != tt.wantErr {
  759. t.Errorf("getTPMapMatchingIncomingAffinityAntiAffinity() error = %v, wantErr %v", err, tt.wantErr)
  760. return
  761. }
  762. if !reflect.DeepEqual(gotAffinityPodsMaps, tt.wantAffinityPodsMaps) {
  763. t.Errorf("getTPMapMatchingIncomingAffinityAntiAffinity() gotAffinityPodsMaps = %#v, want %#v", gotAffinityPodsMaps, tt.wantAffinityPodsMaps)
  764. }
  765. if !reflect.DeepEqual(gotAntiAffinityPodsMaps, tt.wantAntiAffinityPodsMaps) {
  766. t.Errorf("getTPMapMatchingIncomingAffinityAntiAffinity() gotAntiAffinityPodsMaps = %#v, want %#v", gotAntiAffinityPodsMaps, tt.wantAntiAffinityPodsMaps)
  767. }
  768. })
  769. }
  770. }