8
8
from kubernetes .client import V1Container
9
9
from kubernetes .client import V1ContainerPort
10
10
from kubernetes .client import V1HostPathVolumeSource
11
+ from kubernetes .client import V1LabelSelector
11
12
from kubernetes .client import V1ObjectMeta
12
13
from kubernetes .client import V1Pod
13
14
from kubernetes .client import V1PodSecurityContext
16
17
from kubernetes .client import V1ResourceRequirements
17
18
from kubernetes .client import V1SecurityContext
18
19
from kubernetes .client import V1ServiceAccountTokenProjection
20
+ from kubernetes .client import V1TopologySpreadConstraint
19
21
from kubernetes .client import V1Volume
20
22
from kubernetes .client import V1VolumeMount
21
23
from kubernetes .client import V1VolumeProjection
@@ -220,6 +222,7 @@ def test_run_single_request_memory(mock_get_node_affinity, k8s_executor):
220
222
),
221
223
node_selector = {"hello" : "world" },
222
224
affinity = V1Affinity (node_affinity = mock_get_node_affinity .return_value ),
225
+ topology_spread_constraints = [],
223
226
dns_policy = "Default" ,
224
227
service_account_name = task_config .service_account_name ,
225
228
),
@@ -321,6 +324,7 @@ def test_run_single_request_cpu(mock_get_node_affinity, k8s_executor):
321
324
),
322
325
node_selector = {"hello" : "world" },
323
326
affinity = V1Affinity (node_affinity = mock_get_node_affinity .return_value ),
327
+ topology_spread_constraints = [],
324
328
dns_policy = "Default" ,
325
329
service_account_name = task_config .service_account_name ,
326
330
),
@@ -426,6 +430,7 @@ def test_run_both_requests(mock_get_node_affinity, k8s_executor):
426
430
),
427
431
node_selector = {"hello" : "world" },
428
432
affinity = V1Affinity (node_affinity = mock_get_node_affinity .return_value ),
433
+ topology_spread_constraints = [],
429
434
dns_policy = "Default" ,
430
435
service_account_name = task_config .service_account_name ,
431
436
),
@@ -526,6 +531,7 @@ def test_run_no_requests(mock_get_node_affinity, k8s_executor):
526
531
),
527
532
node_selector = {"hello" : "world" },
528
533
affinity = V1Affinity (node_affinity = mock_get_node_affinity .return_value ),
534
+ topology_spread_constraints = [],
529
535
dns_policy = "Default" ,
530
536
service_account_name = task_config .service_account_name ,
531
537
),
@@ -677,6 +683,7 @@ def test_run_authentication_token(mock_get_node_affinity, k8s_executor):
677
683
),
678
684
node_selector = {"hello" : "world" },
679
685
affinity = V1Affinity (node_affinity = mock_get_node_affinity .return_value ),
686
+ topology_spread_constraints = [],
680
687
dns_policy = "Default" ,
681
688
service_account_name = task_config .service_account_name ,
682
689
),
@@ -692,6 +699,111 @@ def test_run_authentication_token(mock_get_node_affinity, k8s_executor):
692
699
]
693
700
694
701
702
+ @mock .patch (
703
+ "task_processing.plugins.kubernetes.kubernetes_pod_executor.get_node_affinity" ,
704
+ autospec = True ,
705
+ )
706
+ def test_run_topology_spread_constraint (mock_get_node_affinity , k8s_executor ):
707
+ task_config = KubernetesTaskConfig (
708
+ name = "fake_task_name" ,
709
+ uuid = "fake_id" ,
710
+ image = "fake_docker_image" ,
711
+ command = "fake_command" ,
712
+ cpus = 1 ,
713
+ cpus_request = 0.5 ,
714
+ memory = 1024 ,
715
+ disk = 1024 ,
716
+ volumes = [],
717
+ projected_sa_volumes = [],
718
+ node_selectors = {"hello" : "world" },
719
+ node_affinities = [dict (key = "a_label" , operator = "In" , value = [])],
720
+ topology_spread_constraints = [
721
+ {
722
+ "max_skew" : 1 ,
723
+ "topology_key" : "topology.kubernetes.io/zone" ,
724
+ "when_unsatisfiable" : "ScheduleAnyway" ,
725
+ "label_selector" : {
726
+ "app.kubernetes.io/managed-by" : "task_proc" ,
727
+ },
728
+ },
729
+ ],
730
+ labels = {
731
+ "some_label" : "some_label_value" ,
732
+ },
733
+ annotations = {
734
+ "paasta.yelp.com/some_annotation" : "some_value" ,
735
+ },
736
+ service_account_name = "testsa" ,
737
+ ports = [8888 ],
738
+ stdin = True ,
739
+ stdin_once = True ,
740
+ tty = True ,
741
+ )
742
+ expected_container = V1Container (
743
+ image = task_config .image ,
744
+ name = "main" ,
745
+ command = ["/bin/sh" , "-c" ],
746
+ args = [task_config .command ],
747
+ security_context = V1SecurityContext (
748
+ capabilities = V1Capabilities (drop = list (task_config .cap_drop )),
749
+ ),
750
+ resources = V1ResourceRequirements (
751
+ limits = {
752
+ "cpu" : 1.0 ,
753
+ "memory" : "1024.0Mi" ,
754
+ "ephemeral-storage" : "1024.0Mi" ,
755
+ },
756
+ requests = {"cpu" : 0.5 },
757
+ ),
758
+ env = [],
759
+ volume_mounts = [],
760
+ ports = [V1ContainerPort (container_port = 8888 )],
761
+ stdin = True ,
762
+ stdin_once = True ,
763
+ tty = True ,
764
+ )
765
+ expected_pod = V1Pod (
766
+ metadata = V1ObjectMeta (
767
+ name = task_config .pod_name ,
768
+ namespace = "task_processing_tests" ,
769
+ labels = {
770
+ "some_label" : "some_label_value" ,
771
+ },
772
+ annotations = {
773
+ "paasta.yelp.com/some_annotation" : "some_value" ,
774
+ },
775
+ ),
776
+ spec = V1PodSpec (
777
+ restart_policy = task_config .restart_policy ,
778
+ containers = [expected_container ],
779
+ volumes = [],
780
+ share_process_namespace = True ,
781
+ security_context = V1PodSecurityContext (
782
+ fs_group = task_config .fs_group ,
783
+ ),
784
+ node_selector = {"hello" : "world" },
785
+ affinity = V1Affinity (node_affinity = mock_get_node_affinity .return_value ),
786
+ topology_spread_constraints = [
787
+ V1TopologySpreadConstraint (
788
+ max_skew = 1 ,
789
+ topology_key = "topology.kubernetes.io/zone" ,
790
+ when_unsatisfiable = "ScheduleAnyway" ,
791
+ label_selector = V1LabelSelector (
792
+ match_labels = {"app.kubernetes.io/managed-by" : "task_proc" }
793
+ ),
794
+ ),
795
+ ],
796
+ dns_policy = "Default" ,
797
+ service_account_name = task_config .service_account_name ,
798
+ ),
799
+ )
800
+
801
+ assert k8s_executor .run (task_config ) == task_config .pod_name
802
+ assert k8s_executor .kube_client .core .create_namespaced_pod .call_args_list == [
803
+ mock .call (body = expected_pod , namespace = "task_processing_tests" )
804
+ ]
805
+
806
+
695
807
def test_process_event_enqueues_task_processing_events_pending_to_running (k8s_executor ):
696
808
mock_pod = mock .Mock (spec = V1Pod )
697
809
mock_pod .metadata .name = "test.1234"
0 commit comments