paasta-tools 1.27.0__py3-none-any.whl → 1.35.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of paasta-tools might be problematic. Click here for more details.

Files changed (192) hide show
  1. paasta_tools/__init__.py +1 -1
  2. paasta_tools/api/api_docs/swagger.json +9 -1
  3. paasta_tools/api/tweens/auth.py +2 -1
  4. paasta_tools/api/views/instance.py +9 -2
  5. paasta_tools/api/views/remote_run.py +2 -0
  6. paasta_tools/async_utils.py +4 -1
  7. paasta_tools/bounce_lib.py +8 -5
  8. paasta_tools/check_services_replication_tools.py +10 -4
  9. paasta_tools/check_spark_jobs.py +1 -1
  10. paasta_tools/cli/cli.py +4 -4
  11. paasta_tools/cli/cmds/autoscale.py +2 -0
  12. paasta_tools/cli/cmds/check.py +2 -0
  13. paasta_tools/cli/cmds/cook_image.py +2 -0
  14. paasta_tools/cli/cmds/get_docker_image.py +2 -0
  15. paasta_tools/cli/cmds/get_image_version.py +2 -0
  16. paasta_tools/cli/cmds/get_latest_deployment.py +2 -0
  17. paasta_tools/cli/cmds/info.py +10 -3
  18. paasta_tools/cli/cmds/itest.py +2 -0
  19. paasta_tools/cli/cmds/list_namespaces.py +2 -0
  20. paasta_tools/cli/cmds/local_run.py +122 -27
  21. paasta_tools/cli/cmds/logs.py +31 -7
  22. paasta_tools/cli/cmds/mark_for_deployment.py +14 -4
  23. paasta_tools/cli/cmds/mesh_status.py +3 -2
  24. paasta_tools/cli/cmds/push_to_registry.py +2 -0
  25. paasta_tools/cli/cmds/remote_run.py +156 -12
  26. paasta_tools/cli/cmds/rollback.py +6 -2
  27. paasta_tools/cli/cmds/secret.py +4 -2
  28. paasta_tools/cli/cmds/security_check.py +2 -0
  29. paasta_tools/cli/cmds/spark_run.py +7 -3
  30. paasta_tools/cli/cmds/status.py +59 -29
  31. paasta_tools/cli/cmds/validate.py +325 -40
  32. paasta_tools/cli/cmds/wait_for_deployment.py +2 -0
  33. paasta_tools/cli/schemas/adhoc_schema.json +3 -0
  34. paasta_tools/cli/schemas/autoscaling_schema.json +3 -2
  35. paasta_tools/cli/schemas/eks_schema.json +24 -1
  36. paasta_tools/cli/schemas/kubernetes_schema.json +1 -0
  37. paasta_tools/cli/schemas/smartstack_schema.json +14 -0
  38. paasta_tools/cli/utils.py +34 -20
  39. paasta_tools/contrib/bounce_log_latency_parser.py +1 -1
  40. paasta_tools/contrib/check_orphans.py +1 -1
  41. paasta_tools/contrib/get_running_task_allocation.py +1 -1
  42. paasta_tools/contrib/ide_helper.py +14 -14
  43. paasta_tools/contrib/mock_patch_checker.py +1 -1
  44. paasta_tools/contrib/paasta_update_soa_memcpu.py +10 -14
  45. paasta_tools/contrib/render_template.py +1 -1
  46. paasta_tools/contrib/shared_ip_check.py +1 -1
  47. paasta_tools/generate_deployments_for_service.py +2 -0
  48. paasta_tools/instance/hpa_metrics_parser.py +3 -5
  49. paasta_tools/instance/kubernetes.py +70 -36
  50. paasta_tools/kubernetes/application/controller_wrappers.py +23 -2
  51. paasta_tools/kubernetes/remote_run.py +52 -25
  52. paasta_tools/kubernetes_tools.py +60 -69
  53. paasta_tools/long_running_service_tools.py +15 -5
  54. paasta_tools/mesos/master.py +1 -1
  55. paasta_tools/metrics/metastatus_lib.py +1 -25
  56. paasta_tools/metrics/metrics_lib.py +12 -3
  57. paasta_tools/paastaapi/__init__.py +1 -1
  58. paasta_tools/paastaapi/api/autoscaler_api.py +1 -1
  59. paasta_tools/paastaapi/api/default_api.py +1 -1
  60. paasta_tools/paastaapi/api/remote_run_api.py +1 -1
  61. paasta_tools/paastaapi/api/resources_api.py +1 -1
  62. paasta_tools/paastaapi/api/service_api.py +1 -1
  63. paasta_tools/paastaapi/api_client.py +1 -1
  64. paasta_tools/paastaapi/configuration.py +2 -2
  65. paasta_tools/paastaapi/exceptions.py +1 -1
  66. paasta_tools/paastaapi/model/adhoc_launch_history.py +1 -1
  67. paasta_tools/paastaapi/model/autoscaler_count_msg.py +1 -1
  68. paasta_tools/paastaapi/model/autoscaling_override.py +1 -1
  69. paasta_tools/paastaapi/model/deploy_queue.py +1 -1
  70. paasta_tools/paastaapi/model/deploy_queue_service_instance.py +1 -1
  71. paasta_tools/paastaapi/model/envoy_backend.py +1 -1
  72. paasta_tools/paastaapi/model/envoy_location.py +1 -1
  73. paasta_tools/paastaapi/model/envoy_status.py +1 -1
  74. paasta_tools/paastaapi/model/flink_cluster_overview.py +1 -1
  75. paasta_tools/paastaapi/model/flink_config.py +1 -1
  76. paasta_tools/paastaapi/model/flink_job.py +1 -1
  77. paasta_tools/paastaapi/model/flink_job_details.py +1 -1
  78. paasta_tools/paastaapi/model/flink_jobs.py +1 -1
  79. paasta_tools/paastaapi/model/float_and_error.py +1 -1
  80. paasta_tools/paastaapi/model/hpa_metric.py +1 -1
  81. paasta_tools/paastaapi/model/inline_object.py +1 -1
  82. paasta_tools/paastaapi/model/inline_response200.py +1 -1
  83. paasta_tools/paastaapi/model/inline_response2001.py +1 -1
  84. paasta_tools/paastaapi/model/inline_response202.py +1 -1
  85. paasta_tools/paastaapi/model/inline_response403.py +1 -1
  86. paasta_tools/paastaapi/model/instance_bounce_status.py +1 -1
  87. paasta_tools/paastaapi/model/instance_mesh_status.py +1 -1
  88. paasta_tools/paastaapi/model/instance_status.py +1 -1
  89. paasta_tools/paastaapi/model/instance_status_adhoc.py +1 -1
  90. paasta_tools/paastaapi/model/instance_status_cassandracluster.py +1 -1
  91. paasta_tools/paastaapi/model/instance_status_flink.py +1 -1
  92. paasta_tools/paastaapi/model/instance_status_kafkacluster.py +1 -1
  93. paasta_tools/paastaapi/model/instance_status_kubernetes.py +1 -1
  94. paasta_tools/paastaapi/model/instance_status_kubernetes_autoscaling_status.py +1 -1
  95. paasta_tools/paastaapi/model/instance_status_kubernetes_v2.py +1 -1
  96. paasta_tools/paastaapi/model/instance_status_tron.py +1 -1
  97. paasta_tools/paastaapi/model/instance_tasks.py +1 -1
  98. paasta_tools/paastaapi/model/integer_and_error.py +1 -1
  99. paasta_tools/paastaapi/model/kubernetes_container.py +1 -1
  100. paasta_tools/paastaapi/model/kubernetes_container_v2.py +1 -1
  101. paasta_tools/paastaapi/model/kubernetes_healthcheck.py +1 -1
  102. paasta_tools/paastaapi/model/kubernetes_pod.py +1 -1
  103. paasta_tools/paastaapi/model/kubernetes_pod_event.py +1 -1
  104. paasta_tools/paastaapi/model/kubernetes_pod_v2.py +1 -1
  105. paasta_tools/paastaapi/model/kubernetes_replica_set.py +1 -1
  106. paasta_tools/paastaapi/model/kubernetes_version.py +4 -1
  107. paasta_tools/paastaapi/model/remote_run_outcome.py +1 -1
  108. paasta_tools/paastaapi/model/remote_run_start.py +4 -1
  109. paasta_tools/paastaapi/model/remote_run_stop.py +1 -1
  110. paasta_tools/paastaapi/model/remote_run_token.py +1 -1
  111. paasta_tools/paastaapi/model/resource.py +1 -1
  112. paasta_tools/paastaapi/model/resource_item.py +1 -1
  113. paasta_tools/paastaapi/model/resource_value.py +1 -1
  114. paasta_tools/paastaapi/model/smartstack_backend.py +1 -1
  115. paasta_tools/paastaapi/model/smartstack_location.py +1 -1
  116. paasta_tools/paastaapi/model/smartstack_status.py +1 -1
  117. paasta_tools/paastaapi/model/task_tail_lines.py +1 -1
  118. paasta_tools/paastaapi/model_utils.py +1 -1
  119. paasta_tools/paastaapi/rest.py +1 -1
  120. paasta_tools/remote_git.py +2 -2
  121. paasta_tools/run-paasta-api-in-dev-mode.py +2 -2
  122. paasta_tools/run-paasta-api-playground.py +2 -2
  123. paasta_tools/setup_kubernetes_job.py +43 -1
  124. paasta_tools/setup_prometheus_adapter_config.py +82 -0
  125. paasta_tools/setup_tron_namespace.py +2 -2
  126. paasta_tools/tron_tools.py +4 -1
  127. paasta_tools/utils.py +29 -11
  128. paasta_tools/yaml_tools.py +1 -1
  129. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/check_orphans.py +1 -1
  130. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/check_spark_jobs.py +1 -1
  131. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/generate_deployments_for_service.py +2 -0
  132. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/get_running_task_allocation.py +1 -1
  133. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/ide_helper.py +14 -14
  134. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/paasta_update_soa_memcpu.py +10 -14
  135. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/setup_kubernetes_job.py +43 -1
  136. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/setup_prometheus_adapter_config.py +82 -0
  137. paasta_tools-1.35.8.dist-info/METADATA +79 -0
  138. {paasta_tools-1.27.0.dist-info → paasta_tools-1.35.8.dist-info}/RECORD +186 -191
  139. {paasta_tools-1.27.0.dist-info → paasta_tools-1.35.8.dist-info}/WHEEL +1 -1
  140. paasta_tools/frameworks/adhoc_scheduler.py +0 -71
  141. paasta_tools/frameworks/native_scheduler.py +0 -652
  142. paasta_tools/frameworks/task_store.py +0 -245
  143. paasta_tools/mesos_maintenance.py +0 -848
  144. paasta_tools/paasta_native_serviceinit.py +0 -21
  145. paasta_tools-1.27.0.dist-info/METADATA +0 -75
  146. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/apply_external_resources.py +0 -0
  147. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/bounce_log_latency_parser.py +0 -0
  148. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/check_autoscaler_max_instances.py +0 -0
  149. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/check_cassandracluster_services_replication.py +0 -0
  150. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/check_flink_services_health.py +0 -0
  151. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/check_kubernetes_api.py +0 -0
  152. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/check_kubernetes_services_replication.py +0 -0
  153. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/check_manual_oapi_changes.sh +0 -0
  154. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/check_oom_events.py +0 -0
  155. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/cleanup_kubernetes_cr.py +0 -0
  156. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/cleanup_kubernetes_crd.py +0 -0
  157. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/cleanup_kubernetes_jobs.py +0 -0
  158. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/create_dynamodb_table.py +0 -0
  159. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/create_paasta_playground.py +0 -0
  160. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/delete_kubernetes_deployments.py +0 -0
  161. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/emit_allocated_cpu_metrics.py +0 -0
  162. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/generate_all_deployments +0 -0
  163. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/generate_authenticating_services.py +0 -0
  164. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/generate_services_file.py +0 -0
  165. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/generate_services_yaml.py +0 -0
  166. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/habitat_fixer.py +0 -0
  167. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/is_pod_healthy_in_proxy.py +0 -0
  168. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/is_pod_healthy_in_smartstack.py +0 -0
  169. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/kill_bad_containers.py +0 -0
  170. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/kubernetes_remove_evicted_pods.py +0 -0
  171. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/mass-deploy-tag.sh +0 -0
  172. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/mock_patch_checker.py +0 -0
  173. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/paasta_cleanup_remote_run_resources.py +0 -0
  174. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/paasta_cleanup_stale_nodes.py +0 -0
  175. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/paasta_deploy_tron_jobs +0 -0
  176. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/paasta_execute_docker_command.py +0 -0
  177. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/paasta_secrets_sync.py +0 -0
  178. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/paasta_tabcomplete.sh +0 -0
  179. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/render_template.py +0 -0
  180. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/rightsizer_soaconfigs_update.py +0 -0
  181. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/service_shard_remove.py +0 -0
  182. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/service_shard_update.py +0 -0
  183. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/setup_istio_mesh.py +0 -0
  184. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/setup_kubernetes_cr.py +0 -0
  185. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/setup_kubernetes_crd.py +0 -0
  186. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/setup_kubernetes_internal_crd.py +0 -0
  187. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/shared_ip_check.py +0 -0
  188. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/synapse_srv_namespaces_fact.py +0 -0
  189. {paasta_tools-1.27.0.data → paasta_tools-1.35.8.data}/scripts/timeouts_metrics_prom.py +0 -0
  190. {paasta_tools-1.27.0.dist-info → paasta_tools-1.35.8.dist-info}/entry_points.txt +0 -0
  191. {paasta_tools-1.27.0.dist-info → paasta_tools-1.35.8.dist-info/licenses}/LICENSE +0 -0
  192. {paasta_tools-1.27.0.dist-info → paasta_tools-1.35.8.dist-info}/top_level.txt +0 -0
paasta_tools/cli/utils.py CHANGED
@@ -26,6 +26,7 @@ import socket
26
26
  import subprocess
27
27
  from collections import defaultdict
28
28
  from shlex import quote
29
+ from typing import Any
29
30
  from typing import Callable
30
31
  from typing import Collection
31
32
  from typing import Generator
@@ -378,7 +379,7 @@ def list_service_instances(soa_dir: str = DEFAULT_SOA_DIR):
378
379
  return the_list
379
380
 
380
381
 
381
- def list_instances(**kwargs):
382
+ def list_instances(**kwargs: Any) -> list[str]:
382
383
  """Returns a sorted list of all possible instance names
383
384
  for tab completion. We try to guess what service you might be
384
385
  operating on, otherwise we just provide *all* of them
@@ -576,7 +577,8 @@ def lazy_choices_completer(list_func):
576
577
 
577
578
  def figure_out_service_name(args, soa_dir=DEFAULT_SOA_DIR):
578
579
  """Figures out and validates the input service name"""
579
- service = args.service or guess_service_name()
580
+ # most cmds should be doing this rstrip already - but just in case this is called from some other path...
581
+ service = args.service.rstrip("/") if args.service else guess_service_name()
580
582
  try:
581
583
  validate_service_name(service, soa_dir=soa_dir)
582
584
  except NoSuchService as service_not_found:
@@ -597,42 +599,54 @@ def get_jenkins_build_output_url():
597
599
 
598
600
  InstanceListerSig = Callable[
599
601
  [
600
- NamedArg(str, "service"),
601
- NamedArg(Optional[str], "cluster"),
602
- NamedArg(str, "instance_type"),
603
- NamedArg(str, "soa_dir"),
602
+ NamedArg(
603
+ str,
604
+ "service", # noqa: F821 # flake8 false-positive, these are not var references
605
+ ),
606
+ NamedArg(Optional[str], "cluster"), # noqa: F821 # flake8 false-positive
607
+ NamedArg(str, "instance_type"), # noqa: F821 # flake8 false-positive
608
+ NamedArg(str, "soa_dir"), # noqa: F821 # flake8 false-positive
604
609
  ],
605
610
  List[Tuple[str, str]],
606
611
  ]
607
612
 
608
613
  InstanceLoaderSig = Callable[
609
614
  [
610
- NamedArg(str, "service"),
611
- NamedArg(str, "instance"),
612
- NamedArg(str, "cluster"),
613
- NamedArg(bool, "load_deployments"),
614
- NamedArg(str, "soa_dir"),
615
+ NamedArg(
616
+ str,
617
+ "service", # noqa: F821 # flake8 false-positive, these are not var references
618
+ ),
619
+ NamedArg(str, "instance"), # noqa: F821 # flake8 false-positive
620
+ NamedArg(str, "cluster"), # noqa: F821 # flake8 false-positive
621
+ NamedArg(bool, "load_deployments"), # noqa: F821 # flake8 false-positive
622
+ NamedArg(str, "soa_dir"), # noqa: F821 # flake8 false-positive
615
623
  ],
616
624
  InstanceConfig,
617
625
  ]
618
626
 
619
627
  LongRunningServiceListerSig = Callable[
620
628
  [
621
- NamedArg(str, "service"),
622
- NamedArg(Optional[str], "cluster"),
623
- NamedArg(str, "instance_type"),
624
- NamedArg(str, "soa_dir"),
629
+ NamedArg(
630
+ str,
631
+ "service", # noqa: F821 # flake8 false-positive, these are not var references
632
+ ),
633
+ NamedArg(Optional[str], "cluster"), # noqa: F821 # flake8 false-positive
634
+ NamedArg(str, "instance_type"), # noqa: F821 # flake8 false-positive
635
+ NamedArg(str, "soa_dir"), # noqa: F821 # flake8 false-positive
625
636
  ],
626
637
  List[Tuple[str, str]],
627
638
  ]
628
639
 
629
640
  LongRunningServiceLoaderSig = Callable[
630
641
  [
631
- NamedArg(str, "service"),
632
- NamedArg(str, "instance"),
633
- NamedArg(str, "cluster"),
634
- NamedArg(bool, "load_deployments"),
635
- NamedArg(str, "soa_dir"),
642
+ NamedArg(
643
+ str,
644
+ "service", # noqa: F821 # flake8 false-positive, these are not var references
645
+ ),
646
+ NamedArg(str, "instance"), # noqa: F821 # flake8 false-positive
647
+ NamedArg(str, "cluster"), # noqa: F821 # flake8 false-positive
648
+ NamedArg(bool, "load_deployments"), # noqa: F821 # flake8 false-positive
649
+ NamedArg(str, "soa_dir"), # noqa: F821 # flake8 false-positive
636
650
  ],
637
651
  LongRunningServiceConfig,
638
652
  ]
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env python3.8
1
+ #!/usr/bin/env python3.10
2
2
  import itertools
3
3
  import json
4
4
  import sys
@@ -56,7 +56,7 @@ def get_zk_data(ignored_services: Set[str]) -> SmartstackData:
56
56
  zk = KazooClient(hosts=zk_hosts)
57
57
  zk.start()
58
58
 
59
- logger.debug(f"pulling smartstack data from zookeeper")
59
+ logger.debug("pulling smartstack data from zookeeper")
60
60
  zk_data = {}
61
61
  services = zk.get_children(PREFIX)
62
62
  for service in services:
@@ -1,5 +1,6 @@
1
1
  #!/opt/venvs/paasta-tools/bin/python
2
2
  import argparse
3
+ import json
3
4
  import time
4
5
  from typing import Any
5
6
  from typing import Dict
@@ -12,7 +13,6 @@ from typing import Optional
12
13
  from typing import Tuple
13
14
 
14
15
  import a_sync
15
- import simplejson as json
16
16
  from kubernetes.client import V1Pod
17
17
  from kubernetes.client import V1ResourceRequirements
18
18
 
@@ -53,12 +53,12 @@ def install_vscode_support() -> None:
53
53
  "python": "${workspaceFolder}/.paasta/bin/python",
54
54
  "program": "${workspaceFolder}/.paasta/bin/tox",
55
55
  "subProcess": True,
56
- "args": ["-e", "py38-linux,docs,mypy,tests"],
56
+ "args": ["-e", "py310-linux,docs,mypy,tests"],
57
57
  },
58
58
  {
59
59
  "name": "paasta cli",
60
60
  "cwd": "${workspaceFolder}",
61
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
61
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
62
62
  "type": "python",
63
63
  "request": "launch",
64
64
  "module": "paasta_tools.cli.cli",
@@ -66,7 +66,7 @@ def install_vscode_support() -> None:
66
66
  {
67
67
  "name": "paasta rollback",
68
68
  "cwd": "${workspaceFolder}",
69
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
69
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
70
70
  "type": "python",
71
71
  "request": "launch",
72
72
  "module": "paasta_tools.cli.cli",
@@ -83,7 +83,7 @@ def install_vscode_support() -> None:
83
83
  {
84
84
  "name": "paasta mark-for-deployment",
85
85
  "cwd": "${workspaceFolder}",
86
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
86
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
87
87
  "type": "python",
88
88
  "request": "launch",
89
89
  "module": "paasta_tools.cli.cli",
@@ -101,7 +101,7 @@ def install_vscode_support() -> None:
101
101
  {
102
102
  "name": "paasta status",
103
103
  "cwd": "${workspaceFolder}",
104
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
104
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
105
105
  "type": "python",
106
106
  "request": "launch",
107
107
  "module": "paasta_tools.cli.cli",
@@ -118,7 +118,7 @@ def install_vscode_support() -> None:
118
118
  {
119
119
  "name": "paasta playground",
120
120
  "cwd": "${workspaceFolder}",
121
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
121
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
122
122
  "type": "python",
123
123
  "request": "launch",
124
124
  "module": "paasta_tools.cli.cli",
@@ -138,7 +138,7 @@ def install_vscode_support() -> None:
138
138
  {
139
139
  "name": "paasta status playground",
140
140
  "cwd": "${workspaceFolder}",
141
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
141
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
142
142
  "type": "python",
143
143
  "request": "launch",
144
144
  "module": "paasta_tools.cli.cli",
@@ -157,7 +157,7 @@ def install_vscode_support() -> None:
157
157
  {
158
158
  "name": "paasta logs",
159
159
  "cwd": "${workspaceFolder}",
160
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
160
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
161
161
  "type": "python",
162
162
  "request": "launch",
163
163
  "module": "paasta_tools.cli.cli",
@@ -175,7 +175,7 @@ def install_vscode_support() -> None:
175
175
  "name": "paasta validate",
176
176
  # This command has to be ran from inside the service repo in yelpsoa-configs
177
177
  "cwd": "${userHome}/pg/yelpsoa-configs/",
178
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
178
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
179
179
  "type": "python",
180
180
  "request": "launch",
181
181
  "module": "paasta_tools.cli.cli",
@@ -184,10 +184,10 @@ def install_vscode_support() -> None:
184
184
  {
185
185
  # 1) Follow step 1 in "Running the PaaSTA HTTP API Locally" wiki
186
186
  # 2) Run this "paasta API" test to debug paasta API
187
- # 3) Run client command, e.g. PAASTA_SYSTEM_CONFIG_DIR=./etc_paasta_for_development/ .tox/py38-linux/bin/python paasta_tools/cli/cli.py status --clusters norcal-devc --service katamari_test_service
187
+ # 3) Run client command, e.g. PAASTA_SYSTEM_CONFIG_DIR=./etc_paasta_for_development/ .tox/py310-linux/bin/python paasta_tools/cli/cli.py status --clusters norcal-devc --service katamari_test_service
188
188
  "name": "paasta API",
189
189
  "cwd": "${workspaceFolder}",
190
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
190
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
191
191
  "type": "python",
192
192
  "request": "launch",
193
193
  "module": "paasta_tools.run-paasta-api-in-dev-mode",
@@ -203,7 +203,7 @@ def install_vscode_support() -> None:
203
203
  {
204
204
  "name": "paasta API playground",
205
205
  "cwd": "${workspaceFolder}",
206
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
206
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
207
207
  "type": "python",
208
208
  "request": "launch",
209
209
  "module": "paasta_tools.run-paasta-api-playground",
@@ -221,7 +221,7 @@ def install_vscode_support() -> None:
221
221
  {
222
222
  "name": "Run setup k8s job in playground",
223
223
  "cwd": "${workspaceFolder}",
224
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
224
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
225
225
  "type": "python",
226
226
  "request": "launch",
227
227
  "module": "paasta_tools.setup_kubernetes_job",
@@ -244,7 +244,7 @@ def install_vscode_support() -> None:
244
244
  {
245
245
  "name": "Generate deployments.json in playground",
246
246
  "cwd": "${workspaceFolder}",
247
- "python": "${workspaceFolder}/.tox/py38-linux/bin/python",
247
+ "python": "${workspaceFolder}/.tox/py310-linux/bin/python",
248
248
  "type": "python",
249
249
  "request": "launch",
250
250
  "module": "paasta_tools.generate_deployments_for_service",
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env python3.8
1
+ #!/usr/bin/env python3.10
2
2
  import ast
3
3
  import sys
4
4
 
@@ -181,6 +181,9 @@ def get_report_from_splunk(creds, app, filename, criteria_filter):
181
181
  "date": d["result"]["_time"].split(" ")[0],
182
182
  "instance": criteria.split(" ")[2],
183
183
  "money": d["result"].get("estimated_monthly_savings", 0),
184
+ "old_cpus": d["result"].get("current_cpus"),
185
+ "old_disk": d["result"].get("current_disk"),
186
+ "old_mem": d["result"].get("current_mem"),
184
187
  "owner": d["result"].get("service_owner", "Unavailable"),
185
188
  "project": d["result"].get("project", "Unavailable"),
186
189
  "service": criteria.split(" ")[0],
@@ -192,24 +195,17 @@ def get_report_from_splunk(creds, app, filename, criteria_filter):
192
195
  "max_instances": d["result"].get("suggested_max_instances"),
193
196
  "mem": d["result"].get("suggested_mem"),
194
197
  "min_instances": d["result"].get("suggested_min_instances"),
195
- "old_cpus": d["result"].get("current_cpus"),
196
- "old_disk": d["result"].get("current_disk"),
197
- "old_mem": d["result"].get("current_mem"),
198
198
  }
199
199
 
200
200
  # the report we get is all strings, so we need to convert them to the right types
201
201
  field_conversions = {
202
- "current_cpus": float,
203
- "suggested_cpu_burst_add": float,
204
- "suggested_cpus": float,
205
- "suggested_disk": int,
206
- "suggested_hacheck_cpus": float,
207
- "suggested_max_instances": int,
208
- "suggested_mem": int,
209
- "suggested_min_instances": int,
210
- # not quite sure why these are floats...they're ints in soaconfigs
211
- "current_disk": _force_str_to_int,
212
- "current_mem": _force_str_to_int,
202
+ "cpus": float,
203
+ "cpu_burst_add": float,
204
+ "disk": int,
205
+ "hacheck_cpus": float,
206
+ "max_instances": int,
207
+ "mem": int,
208
+ "min_instances": int,
213
209
  }
214
210
 
215
211
  # merge results if we've already seen rows for this service
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env python3.8
1
+ #!/usr/bin/env python3.10
2
2
  import argparse
3
3
  import os
4
4
  import re
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env python3.8
1
+ #!/usr/bin/env python3.10
2
2
  import sys
3
3
  from collections import defaultdict
4
4
 
@@ -101,6 +101,8 @@ def parse_args() -> argparse.Namespace:
101
101
  "--service",
102
102
  required=True,
103
103
  help="Service name to make the deployments.json for",
104
+ # strip any potential trailing / for folks tab-completing directories
105
+ type=lambda x: x.rstrip("/"),
104
106
  )
105
107
  args = parser.parse_args()
106
108
  return args
@@ -1,8 +1,6 @@
1
1
  from typing import Optional
2
2
 
3
- from kubernetes.client.models.v2beta2_object_metric_status import (
4
- V2beta2ObjectMetricStatus,
5
- )
3
+ from kubernetes.client import V2ObjectMetricStatus
6
4
  from mypy_extensions import TypedDict
7
5
 
8
6
 
@@ -102,7 +100,7 @@ class HPAMetricsParser:
102
100
  )
103
101
 
104
102
  def parse_object_metric(
105
- self, metric_spec: V2beta2ObjectMetricStatus, status: HPAMetricsDict
103
+ self, metric_spec: V2ObjectMetricStatus, status: HPAMetricsDict
106
104
  ) -> None:
107
105
  status["name"] = metric_spec.metric.name
108
106
  status["target_value"] = (
@@ -112,7 +110,7 @@ class HPAMetricsParser:
112
110
  )
113
111
 
114
112
  def parse_object_metric_current(
115
- self, metric_spec: V2beta2ObjectMetricStatus, status: HPAMetricsDict
113
+ self, metric_spec: V2ObjectMetricStatus, status: HPAMetricsDict
116
114
  ) -> None:
117
115
  status["name"] = metric_spec.metric.name
118
116
  status["current_value"] = (
@@ -1,4 +1,6 @@
1
1
  import asyncio
2
+ import logging
3
+ from asyncio.tasks import Task
2
4
  from collections import defaultdict
3
5
  from enum import Enum
4
6
  from typing import Any
@@ -16,6 +18,7 @@ from typing import Union
16
18
 
17
19
  import a_sync
18
20
  import pytz
21
+ import requests.exceptions
19
22
  from kubernetes.client import V1Container
20
23
  from kubernetes.client import V1ControllerRevision
21
24
  from kubernetes.client import V1Pod
@@ -74,6 +77,8 @@ INSTANCE_TYPE_CR_ID = dict(
74
77
  monkrelaycluster=monkrelaycluster_tools.cr_id,
75
78
  )
76
79
 
80
+ logger = logging.getLogger(__name__)
81
+
77
82
 
78
83
  class ServiceMesh(Enum):
79
84
  SMARTSTACK = "smartstack"
@@ -99,6 +104,7 @@ class KubernetesVersionDict(TypedDict, total=False):
99
104
  config_sha: str
100
105
  pods: Sequence[Mapping[str, Any]]
101
106
  namespace: str
107
+ container_port: Optional[int]
102
108
 
103
109
 
104
110
  def cr_id(service: str, instance: str, instance_type: str) -> Mapping[str, str]:
@@ -346,31 +352,49 @@ async def mesh_status(
346
352
 
347
353
  pods = await pods_task
348
354
  for location, hosts in node_hostname_by_location.items():
349
- host = replication_checker.get_hostname_in_pool(hosts, instance_pool)
350
- if service_mesh == ServiceMesh.SMARTSTACK:
351
- mesh_status["locations"].append(
352
- _build_smartstack_location_dict(
353
- synapse_host=host,
354
- synapse_port=settings.system_paasta_config.get_synapse_port(),
355
- synapse_haproxy_url_format=settings.system_paasta_config.get_synapse_haproxy_url_format(),
356
- registration=registration,
357
- pods=pods,
358
- location=location,
359
- should_return_individual_backends=should_return_individual_backends,
360
- )
361
- )
362
- elif service_mesh == ServiceMesh.ENVOY:
363
- mesh_status["locations"].append(
364
- _build_envoy_location_dict(
365
- envoy_host=host,
366
- envoy_admin_port=settings.system_paasta_config.get_envoy_admin_port(),
367
- envoy_admin_endpoint_format=settings.system_paasta_config.get_envoy_admin_endpoint_format(),
368
- registration=registration,
369
- pods=pods,
370
- location=location,
371
- should_return_individual_backends=should_return_individual_backends,
372
- )
373
- )
355
+ max_retries = 3
356
+
357
+ for attempt in range(max_retries):
358
+ host = replication_checker.get_hostname_in_pool(hosts, instance_pool)
359
+ try:
360
+ if service_mesh == ServiceMesh.SMARTSTACK:
361
+ location_dict = _build_smartstack_location_dict(
362
+ synapse_host=host,
363
+ synapse_port=settings.system_paasta_config.get_synapse_port(),
364
+ synapse_haproxy_url_format=settings.system_paasta_config.get_synapse_haproxy_url_format(),
365
+ registration=registration,
366
+ pods=pods,
367
+ location=location,
368
+ should_return_individual_backends=should_return_individual_backends,
369
+ )
370
+ elif service_mesh == ServiceMesh.ENVOY:
371
+ location_dict = _build_envoy_location_dict(
372
+ envoy_host=host,
373
+ envoy_admin_port=settings.system_paasta_config.get_envoy_admin_port(),
374
+ envoy_admin_endpoint_format=settings.system_paasta_config.get_envoy_admin_endpoint_format(),
375
+ registration=registration,
376
+ pods=pods,
377
+ location=location,
378
+ should_return_individual_backends=should_return_individual_backends,
379
+ )
380
+
381
+ mesh_status["locations"].append(location_dict)
382
+ return mesh_status
383
+
384
+ except requests.exceptions.ConnectTimeout:
385
+ if attempt < max_retries - 1:
386
+ logger.warning(
387
+ "attempt %s/%s: Unable to connect to %s, retrying (on another host, hopefully)...",
388
+ attempt,
389
+ max_retries,
390
+ host,
391
+ )
392
+ continue
393
+ else:
394
+ logger.critical(
395
+ "Unable to connect to %s, not retrying again.", host
396
+ )
397
+ raise
374
398
  return mesh_status
375
399
 
376
400
 
@@ -641,7 +665,7 @@ async def kubernetes_status_v2(
641
665
  kube_client, job_config, job_config.get_kubernetes_namespace()
642
666
  )
643
667
  )
644
- tasks.append(autoscaling_task)
668
+ tasks.append(autoscaling_task) # type: ignore # PAASTA-18698; ignoring due to unexpected type mismatch
645
669
  else:
646
670
  autoscaling_task = None
647
671
 
@@ -653,7 +677,7 @@ async def kubernetes_status_v2(
653
677
  namespaces=relevant_namespaces,
654
678
  )
655
679
  )
656
- tasks.append(pods_task)
680
+ tasks.append(pods_task) # type: ignore # PAASTA-18698; ignoring due to unexpected type mismatch
657
681
 
658
682
  service_namespace_config = kubernetes_tools.load_service_namespace_config(
659
683
  service=service,
@@ -674,9 +698,9 @@ async def kubernetes_status_v2(
674
698
  )
675
699
  )
676
700
  backends_task = asyncio.create_task(
677
- get_backends_from_mesh_status(mesh_status_task)
701
+ get_backends_from_mesh_status(mesh_status_task) # type: ignore # PAASTA-18698; ignoring due to unexpected type mismatch
678
702
  )
679
- tasks.extend([mesh_status_task, backends_task])
703
+ tasks.extend([mesh_status_task, backends_task]) # type: ignore # PAASTA-18698; ignoring due to unexpected type mismatch
680
704
  else:
681
705
  mesh_status_task = None
682
706
  backends_task = None
@@ -685,7 +709,7 @@ async def kubernetes_status_v2(
685
709
  pod_status_by_sha_and_readiness_task = asyncio.create_task(
686
710
  get_pod_status_tasks_by_sha_and_readiness(
687
711
  pods_task,
688
- backends_task,
712
+ backends_task, # type: ignore # PAASTA-18698; ignoring due to unexpected type mismatch
689
713
  kube_client,
690
714
  verbose,
691
715
  )
@@ -696,15 +720,16 @@ async def kubernetes_status_v2(
696
720
  service=service,
697
721
  instance=instance,
698
722
  namespaces=relevant_namespaces,
699
- pod_status_by_sha_and_readiness_task=pod_status_by_sha_and_readiness_task,
723
+ pod_status_by_sha_and_readiness_task=pod_status_by_sha_and_readiness_task, # type: ignore # PAASTA-18698; ignoring due to unexpected type mismatch
724
+ container_port=job_config.get_container_port(),
700
725
  )
701
726
  )
702
- tasks.extend([pod_status_by_sha_and_readiness_task, versions_task])
727
+ tasks.extend([pod_status_by_sha_and_readiness_task, versions_task]) # type: ignore # PAASTA-18698; ignoring due to unexpected type mismatch
703
728
  else:
704
729
  pod_status_by_replicaset_task = asyncio.create_task(
705
730
  get_pod_status_tasks_by_replicaset(
706
731
  pods_task,
707
- backends_task,
732
+ backends_task, # type: ignore # PAASTA-18698; ignoring due to unexpected type mismatch
708
733
  kube_client,
709
734
  verbose,
710
735
  )
@@ -715,10 +740,11 @@ async def kubernetes_status_v2(
715
740
  service=service,
716
741
  instance=instance,
717
742
  namespaces=relevant_namespaces,
718
- pod_status_by_replicaset_task=pod_status_by_replicaset_task,
743
+ pod_status_by_replicaset_task=pod_status_by_replicaset_task, # type: ignore # PAASTA-18698; ignoring due to unexpected type mismatch
744
+ container_port=job_config.get_container_port(),
719
745
  )
720
746
  )
721
- tasks.extend([pod_status_by_replicaset_task, versions_task])
747
+ tasks.extend([pod_status_by_replicaset_task, versions_task]) # type: ignore # PAASTA-18698; ignoring due to unexpected type mismatch
722
748
 
723
749
  await asyncio.gather(*tasks, return_exceptions=True)
724
750
 
@@ -787,6 +813,7 @@ async def get_versions_for_replicasets(
787
813
  instance: str,
788
814
  namespaces: Iterable[str],
789
815
  pod_status_by_replicaset_task: "asyncio.Future[Mapping[str, Sequence[asyncio.Future[Dict[str, Any]]]]]",
816
+ container_port: Optional[int],
790
817
  ) -> List[KubernetesVersionDict]:
791
818
 
792
819
  replicaset_list: List[V1ReplicaSet] = []
@@ -814,6 +841,7 @@ async def get_versions_for_replicasets(
814
841
  replicaset,
815
842
  kube_client,
816
843
  pod_status_by_replicaset.get(replicaset.metadata.name),
844
+ container_port,
817
845
  )
818
846
  for replicaset in actually_running_replicasets
819
847
  ]
@@ -825,6 +853,7 @@ async def get_replicaset_status(
825
853
  replicaset: V1ReplicaSet,
826
854
  client: kubernetes_tools.KubeClient,
827
855
  pod_status_tasks: Sequence["asyncio.Future[Dict[str, Any]]"],
856
+ container_port: Optional[int],
828
857
  ) -> KubernetesVersionDict:
829
858
  return {
830
859
  "name": replicaset.metadata.name,
@@ -839,6 +868,7 @@ async def get_replicaset_status(
839
868
  "config_sha": replicaset.metadata.labels.get("paasta.yelp.com/config_sha"),
840
869
  "pods": await asyncio.gather(*pod_status_tasks) if pod_status_tasks else [],
841
870
  "namespace": replicaset.metadata.namespace,
871
+ "container_port": container_port,
842
872
  }
843
873
 
844
874
 
@@ -1062,6 +1092,7 @@ async def get_versions_for_controller_revisions(
1062
1092
  instance: str,
1063
1093
  namespaces: Iterable[str],
1064
1094
  pod_status_by_sha_and_readiness_task: "asyncio.Future[Mapping[Tuple[str, str], Mapping[bool, Sequence[asyncio.Future[Mapping[str, Any]]]]]]",
1095
+ container_port: Optional[int] = None,
1065
1096
  ) -> List[KubernetesVersionDict]:
1066
1097
  controller_revision_list: List[V1ControllerRevision] = []
1067
1098
 
@@ -1091,6 +1122,7 @@ async def get_versions_for_controller_revisions(
1091
1122
  cr,
1092
1123
  kube_client,
1093
1124
  pod_status_by_sha_and_readiness[(git_sha, config_sha)],
1125
+ container_port=container_port,
1094
1126
  )
1095
1127
  for (git_sha, config_sha), cr in cr_by_shas.items()
1096
1128
  ]
@@ -1105,6 +1137,7 @@ async def get_version_for_controller_revision(
1105
1137
  pod_status_tasks_by_readiness: Mapping[
1106
1138
  bool, Sequence["asyncio.Future[Mapping[str, Any]]"]
1107
1139
  ],
1140
+ container_port: Optional[int] = None,
1108
1141
  ) -> KubernetesVersionDict:
1109
1142
  all_pod_status_tasks = [
1110
1143
  task for tasks in pod_status_tasks_by_readiness.values() for task in tasks
@@ -1121,6 +1154,7 @@ async def get_version_for_controller_revision(
1121
1154
  "config_sha": cr.metadata.labels.get("paasta.yelp.com/config_sha"),
1122
1155
  "pods": [task.result() for task in all_pod_status_tasks],
1123
1156
  "namespace": cr.metadata.namespace,
1157
+ "container_port": container_port,
1124
1158
  }
1125
1159
 
1126
1160
 
@@ -1156,7 +1190,7 @@ async def kubernetes_status(
1156
1190
 
1157
1191
  # this task is necessary for mesh_status, but most other use cases want
1158
1192
  # just the list of pods
1159
- pods_task = asyncio.create_task(
1193
+ pods_task: Task[Sequence[V1Pod]] = asyncio.create_task(
1160
1194
  kubernetes_tools.pods_for_service_instance(
1161
1195
  service=job_config.service,
1162
1196
  instance=job_config.instance,
@@ -173,19 +173,31 @@ class Application(ABC):
173
173
  self, kube_client: KubeClient, namespace: str
174
174
  ) -> V1PodDisruptionBudget:
175
175
  max_unavailable: Union[str, int]
176
+
177
+ system_paasta_config = load_system_paasta_config()
178
+
176
179
  if "bounce_margin_factor" in self.soa_config.config_dict:
177
180
  max_unavailable = (
178
181
  f"{int((1 - self.soa_config.get_bounce_margin_factor()) * 100)}%"
179
182
  )
180
183
  else:
181
- system_paasta_config = load_system_paasta_config()
182
184
  max_unavailable = system_paasta_config.get_pdb_max_unavailable()
183
185
 
186
+ if "unhealthy_pod_eviction_policy" in self.soa_config.config_dict:
187
+ unhealthy_pod_eviction_policy = (
188
+ self.soa_config.get_unhealthy_pod_eviction_policy()
189
+ )
190
+ else:
191
+ unhealthy_pod_eviction_policy = (
192
+ system_paasta_config.get_unhealthy_pod_eviction_policy()
193
+ )
194
+
184
195
  pdr = pod_disruption_budget_for_service_instance(
185
196
  service=self.kube_deployment.service,
186
197
  instance=self.kube_deployment.instance,
187
198
  max_unavailable=max_unavailable,
188
199
  namespace=namespace,
200
+ unhealthy_pod_eviction_policy=unhealthy_pod_eviction_policy,
189
201
  )
190
202
  try:
191
203
  existing_pdr = kube_client.policy.read_namespaced_pod_disruption_budget(
@@ -198,12 +210,21 @@ class Application(ABC):
198
210
  raise
199
211
 
200
212
  if existing_pdr:
213
+ """
214
+ Update the pod disruption budget only if spec.max_unavailable
215
+ or spec.unhealthy_pod_eviction_policy have changed;
216
+ ignore changes to other fields
217
+ """
201
218
  if existing_pdr.spec.min_available is not None:
202
219
  logging.info(
203
220
  "Not updating poddisruptionbudget: can't have both "
204
221
  "min_available and max_unavailable"
205
222
  )
206
- elif existing_pdr.spec.max_unavailable != pdr.spec.max_unavailable:
223
+ elif (
224
+ existing_pdr.spec.max_unavailable != pdr.spec.max_unavailable
225
+ or existing_pdr.spec.unhealthy_pod_eviction_policy
226
+ != pdr.spec.unhealthy_pod_eviction_policy
227
+ ):
207
228
  logging.info(f"Updating poddisruptionbudget {pdr.metadata.name}")
208
229
  return kube_client.policy.patch_namespaced_pod_disruption_budget(
209
230
  name=pdr.metadata.name, namespace=pdr.metadata.namespace, body=pdr