paasta-tools 1.21.3__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.
Files changed (348) hide show
  1. k8s_itests/__init__.py +0 -0
  2. k8s_itests/test_autoscaling.py +23 -0
  3. k8s_itests/utils.py +38 -0
  4. paasta_tools/__init__.py +20 -0
  5. paasta_tools/adhoc_tools.py +142 -0
  6. paasta_tools/api/__init__.py +13 -0
  7. paasta_tools/api/api.py +330 -0
  8. paasta_tools/api/api_docs/swagger.json +2323 -0
  9. paasta_tools/api/client.py +106 -0
  10. paasta_tools/api/settings.py +33 -0
  11. paasta_tools/api/tweens/__init__.py +6 -0
  12. paasta_tools/api/tweens/auth.py +125 -0
  13. paasta_tools/api/tweens/profiling.py +108 -0
  14. paasta_tools/api/tweens/request_logger.py +124 -0
  15. paasta_tools/api/views/__init__.py +13 -0
  16. paasta_tools/api/views/autoscaler.py +100 -0
  17. paasta_tools/api/views/exception.py +45 -0
  18. paasta_tools/api/views/flink.py +73 -0
  19. paasta_tools/api/views/instance.py +395 -0
  20. paasta_tools/api/views/pause_autoscaler.py +71 -0
  21. paasta_tools/api/views/remote_run.py +113 -0
  22. paasta_tools/api/views/resources.py +76 -0
  23. paasta_tools/api/views/service.py +35 -0
  24. paasta_tools/api/views/version.py +25 -0
  25. paasta_tools/apply_external_resources.py +79 -0
  26. paasta_tools/async_utils.py +109 -0
  27. paasta_tools/autoscaling/__init__.py +0 -0
  28. paasta_tools/autoscaling/autoscaling_service_lib.py +57 -0
  29. paasta_tools/autoscaling/forecasting.py +106 -0
  30. paasta_tools/autoscaling/max_all_k8s_services.py +41 -0
  31. paasta_tools/autoscaling/pause_service_autoscaler.py +77 -0
  32. paasta_tools/autoscaling/utils.py +52 -0
  33. paasta_tools/bounce_lib.py +184 -0
  34. paasta_tools/broadcast_log_to_services.py +62 -0
  35. paasta_tools/cassandracluster_tools.py +210 -0
  36. paasta_tools/check_autoscaler_max_instances.py +212 -0
  37. paasta_tools/check_cassandracluster_services_replication.py +35 -0
  38. paasta_tools/check_flink_services_health.py +203 -0
  39. paasta_tools/check_kubernetes_api.py +57 -0
  40. paasta_tools/check_kubernetes_services_replication.py +141 -0
  41. paasta_tools/check_oom_events.py +244 -0
  42. paasta_tools/check_services_replication_tools.py +324 -0
  43. paasta_tools/check_spark_jobs.py +234 -0
  44. paasta_tools/cleanup_kubernetes_cr.py +138 -0
  45. paasta_tools/cleanup_kubernetes_crd.py +145 -0
  46. paasta_tools/cleanup_kubernetes_jobs.py +344 -0
  47. paasta_tools/cleanup_tron_namespaces.py +96 -0
  48. paasta_tools/cli/__init__.py +13 -0
  49. paasta_tools/cli/authentication.py +85 -0
  50. paasta_tools/cli/cli.py +260 -0
  51. paasta_tools/cli/cmds/__init__.py +13 -0
  52. paasta_tools/cli/cmds/autoscale.py +143 -0
  53. paasta_tools/cli/cmds/check.py +334 -0
  54. paasta_tools/cli/cmds/cook_image.py +147 -0
  55. paasta_tools/cli/cmds/get_docker_image.py +76 -0
  56. paasta_tools/cli/cmds/get_image_version.py +172 -0
  57. paasta_tools/cli/cmds/get_latest_deployment.py +93 -0
  58. paasta_tools/cli/cmds/info.py +155 -0
  59. paasta_tools/cli/cmds/itest.py +117 -0
  60. paasta_tools/cli/cmds/list.py +66 -0
  61. paasta_tools/cli/cmds/list_clusters.py +42 -0
  62. paasta_tools/cli/cmds/list_deploy_queue.py +171 -0
  63. paasta_tools/cli/cmds/list_namespaces.py +84 -0
  64. paasta_tools/cli/cmds/local_run.py +1396 -0
  65. paasta_tools/cli/cmds/logs.py +1601 -0
  66. paasta_tools/cli/cmds/mark_for_deployment.py +1988 -0
  67. paasta_tools/cli/cmds/mesh_status.py +174 -0
  68. paasta_tools/cli/cmds/pause_service_autoscaler.py +107 -0
  69. paasta_tools/cli/cmds/push_to_registry.py +275 -0
  70. paasta_tools/cli/cmds/remote_run.py +252 -0
  71. paasta_tools/cli/cmds/rollback.py +347 -0
  72. paasta_tools/cli/cmds/secret.py +549 -0
  73. paasta_tools/cli/cmds/security_check.py +59 -0
  74. paasta_tools/cli/cmds/spark_run.py +1400 -0
  75. paasta_tools/cli/cmds/start_stop_restart.py +401 -0
  76. paasta_tools/cli/cmds/status.py +2302 -0
  77. paasta_tools/cli/cmds/validate.py +1012 -0
  78. paasta_tools/cli/cmds/wait_for_deployment.py +275 -0
  79. paasta_tools/cli/fsm/__init__.py +13 -0
  80. paasta_tools/cli/fsm/autosuggest.py +82 -0
  81. paasta_tools/cli/fsm/template/README.md +8 -0
  82. paasta_tools/cli/fsm/template/cookiecutter.json +7 -0
  83. paasta_tools/cli/fsm/template/{{cookiecutter.service}}/kubernetes-PROD.yaml +91 -0
  84. paasta_tools/cli/fsm/template/{{cookiecutter.service}}/monitoring.yaml +20 -0
  85. paasta_tools/cli/fsm/template/{{cookiecutter.service}}/service.yaml +8 -0
  86. paasta_tools/cli/fsm/template/{{cookiecutter.service}}/smartstack.yaml +6 -0
  87. paasta_tools/cli/fsm_cmd.py +121 -0
  88. paasta_tools/cli/paasta_tabcomplete.sh +23 -0
  89. paasta_tools/cli/schemas/adhoc_schema.json +199 -0
  90. paasta_tools/cli/schemas/autoscaling_schema.json +91 -0
  91. paasta_tools/cli/schemas/autotuned_defaults/cassandracluster_schema.json +37 -0
  92. paasta_tools/cli/schemas/autotuned_defaults/kubernetes_schema.json +89 -0
  93. paasta_tools/cli/schemas/deploy_schema.json +173 -0
  94. paasta_tools/cli/schemas/eks_schema.json +970 -0
  95. paasta_tools/cli/schemas/kubernetes_schema.json +970 -0
  96. paasta_tools/cli/schemas/rollback_schema.json +160 -0
  97. paasta_tools/cli/schemas/service_schema.json +25 -0
  98. paasta_tools/cli/schemas/smartstack_schema.json +322 -0
  99. paasta_tools/cli/schemas/tron_schema.json +699 -0
  100. paasta_tools/cli/utils.py +1118 -0
  101. paasta_tools/clusterman.py +21 -0
  102. paasta_tools/config_utils.py +385 -0
  103. paasta_tools/contrib/__init__.py +0 -0
  104. paasta_tools/contrib/bounce_log_latency_parser.py +68 -0
  105. paasta_tools/contrib/check_manual_oapi_changes.sh +24 -0
  106. paasta_tools/contrib/check_orphans.py +306 -0
  107. paasta_tools/contrib/create_dynamodb_table.py +35 -0
  108. paasta_tools/contrib/create_paasta_playground.py +105 -0
  109. paasta_tools/contrib/emit_allocated_cpu_metrics.py +50 -0
  110. paasta_tools/contrib/get_running_task_allocation.py +346 -0
  111. paasta_tools/contrib/habitat_fixer.py +86 -0
  112. paasta_tools/contrib/ide_helper.py +316 -0
  113. paasta_tools/contrib/is_pod_healthy_in_proxy.py +139 -0
  114. paasta_tools/contrib/is_pod_healthy_in_smartstack.py +50 -0
  115. paasta_tools/contrib/kill_bad_containers.py +109 -0
  116. paasta_tools/contrib/mass-deploy-tag.sh +44 -0
  117. paasta_tools/contrib/mock_patch_checker.py +86 -0
  118. paasta_tools/contrib/paasta_update_soa_memcpu.py +520 -0
  119. paasta_tools/contrib/render_template.py +129 -0
  120. paasta_tools/contrib/rightsizer_soaconfigs_update.py +348 -0
  121. paasta_tools/contrib/service_shard_remove.py +157 -0
  122. paasta_tools/contrib/service_shard_update.py +373 -0
  123. paasta_tools/contrib/shared_ip_check.py +77 -0
  124. paasta_tools/contrib/timeouts_metrics_prom.py +64 -0
  125. paasta_tools/delete_kubernetes_deployments.py +89 -0
  126. paasta_tools/deployment_utils.py +44 -0
  127. paasta_tools/docker_wrapper.py +234 -0
  128. paasta_tools/docker_wrapper_imports.py +13 -0
  129. paasta_tools/drain_lib.py +351 -0
  130. paasta_tools/dump_locally_running_services.py +71 -0
  131. paasta_tools/eks_tools.py +119 -0
  132. paasta_tools/envoy_tools.py +373 -0
  133. paasta_tools/firewall.py +504 -0
  134. paasta_tools/firewall_logging.py +154 -0
  135. paasta_tools/firewall_update.py +172 -0
  136. paasta_tools/flink_tools.py +345 -0
  137. paasta_tools/flinkeks_tools.py +90 -0
  138. paasta_tools/frameworks/__init__.py +0 -0
  139. paasta_tools/frameworks/adhoc_scheduler.py +71 -0
  140. paasta_tools/frameworks/constraints.py +87 -0
  141. paasta_tools/frameworks/native_scheduler.py +652 -0
  142. paasta_tools/frameworks/native_service_config.py +301 -0
  143. paasta_tools/frameworks/task_store.py +245 -0
  144. paasta_tools/generate_all_deployments +9 -0
  145. paasta_tools/generate_authenticating_services.py +94 -0
  146. paasta_tools/generate_deployments_for_service.py +255 -0
  147. paasta_tools/generate_services_file.py +114 -0
  148. paasta_tools/generate_services_yaml.py +30 -0
  149. paasta_tools/hacheck.py +76 -0
  150. paasta_tools/instance/__init__.py +0 -0
  151. paasta_tools/instance/hpa_metrics_parser.py +122 -0
  152. paasta_tools/instance/kubernetes.py +1362 -0
  153. paasta_tools/iptables.py +240 -0
  154. paasta_tools/kafkacluster_tools.py +143 -0
  155. paasta_tools/kubernetes/__init__.py +0 -0
  156. paasta_tools/kubernetes/application/__init__.py +0 -0
  157. paasta_tools/kubernetes/application/controller_wrappers.py +476 -0
  158. paasta_tools/kubernetes/application/tools.py +90 -0
  159. paasta_tools/kubernetes/bin/__init__.py +0 -0
  160. paasta_tools/kubernetes/bin/kubernetes_remove_evicted_pods.py +164 -0
  161. paasta_tools/kubernetes/bin/paasta_cleanup_remote_run_resources.py +135 -0
  162. paasta_tools/kubernetes/bin/paasta_cleanup_stale_nodes.py +181 -0
  163. paasta_tools/kubernetes/bin/paasta_secrets_sync.py +758 -0
  164. paasta_tools/kubernetes/remote_run.py +558 -0
  165. paasta_tools/kubernetes_tools.py +4679 -0
  166. paasta_tools/list_kubernetes_service_instances.py +128 -0
  167. paasta_tools/list_tron_namespaces.py +60 -0
  168. paasta_tools/long_running_service_tools.py +678 -0
  169. paasta_tools/mac_address.py +44 -0
  170. paasta_tools/marathon_dashboard.py +0 -0
  171. paasta_tools/mesos/__init__.py +0 -0
  172. paasta_tools/mesos/cfg.py +46 -0
  173. paasta_tools/mesos/cluster.py +60 -0
  174. paasta_tools/mesos/exceptions.py +59 -0
  175. paasta_tools/mesos/framework.py +77 -0
  176. paasta_tools/mesos/log.py +48 -0
  177. paasta_tools/mesos/master.py +306 -0
  178. paasta_tools/mesos/mesos_file.py +169 -0
  179. paasta_tools/mesos/parallel.py +52 -0
  180. paasta_tools/mesos/slave.py +115 -0
  181. paasta_tools/mesos/task.py +94 -0
  182. paasta_tools/mesos/util.py +69 -0
  183. paasta_tools/mesos/zookeeper.py +37 -0
  184. paasta_tools/mesos_maintenance.py +848 -0
  185. paasta_tools/mesos_tools.py +1051 -0
  186. paasta_tools/metrics/__init__.py +0 -0
  187. paasta_tools/metrics/metastatus_lib.py +1110 -0
  188. paasta_tools/metrics/metrics_lib.py +217 -0
  189. paasta_tools/monitoring/__init__.py +13 -0
  190. paasta_tools/monitoring/check_k8s_api_performance.py +110 -0
  191. paasta_tools/monitoring_tools.py +652 -0
  192. paasta_tools/monkrelaycluster_tools.py +146 -0
  193. paasta_tools/nrtsearchservice_tools.py +143 -0
  194. paasta_tools/nrtsearchserviceeks_tools.py +68 -0
  195. paasta_tools/oom_logger.py +321 -0
  196. paasta_tools/paasta_deploy_tron_jobs +3 -0
  197. paasta_tools/paasta_execute_docker_command.py +123 -0
  198. paasta_tools/paasta_native_serviceinit.py +21 -0
  199. paasta_tools/paasta_service_config_loader.py +201 -0
  200. paasta_tools/paastaapi/__init__.py +29 -0
  201. paasta_tools/paastaapi/api/__init__.py +3 -0
  202. paasta_tools/paastaapi/api/autoscaler_api.py +302 -0
  203. paasta_tools/paastaapi/api/default_api.py +569 -0
  204. paasta_tools/paastaapi/api/remote_run_api.py +604 -0
  205. paasta_tools/paastaapi/api/resources_api.py +157 -0
  206. paasta_tools/paastaapi/api/service_api.py +1736 -0
  207. paasta_tools/paastaapi/api_client.py +818 -0
  208. paasta_tools/paastaapi/apis/__init__.py +22 -0
  209. paasta_tools/paastaapi/configuration.py +455 -0
  210. paasta_tools/paastaapi/exceptions.py +137 -0
  211. paasta_tools/paastaapi/model/__init__.py +5 -0
  212. paasta_tools/paastaapi/model/adhoc_launch_history.py +176 -0
  213. paasta_tools/paastaapi/model/autoscaler_count_msg.py +176 -0
  214. paasta_tools/paastaapi/model/deploy_queue.py +178 -0
  215. paasta_tools/paastaapi/model/deploy_queue_service_instance.py +194 -0
  216. paasta_tools/paastaapi/model/envoy_backend.py +185 -0
  217. paasta_tools/paastaapi/model/envoy_location.py +184 -0
  218. paasta_tools/paastaapi/model/envoy_status.py +181 -0
  219. paasta_tools/paastaapi/model/flink_cluster_overview.py +188 -0
  220. paasta_tools/paastaapi/model/flink_config.py +173 -0
  221. paasta_tools/paastaapi/model/flink_job.py +186 -0
  222. paasta_tools/paastaapi/model/flink_job_details.py +192 -0
  223. paasta_tools/paastaapi/model/flink_jobs.py +175 -0
  224. paasta_tools/paastaapi/model/float_and_error.py +173 -0
  225. paasta_tools/paastaapi/model/hpa_metric.py +176 -0
  226. paasta_tools/paastaapi/model/inline_object.py +170 -0
  227. paasta_tools/paastaapi/model/inline_response200.py +170 -0
  228. paasta_tools/paastaapi/model/inline_response2001.py +170 -0
  229. paasta_tools/paastaapi/model/instance_bounce_status.py +200 -0
  230. paasta_tools/paastaapi/model/instance_mesh_status.py +186 -0
  231. paasta_tools/paastaapi/model/instance_status.py +220 -0
  232. paasta_tools/paastaapi/model/instance_status_adhoc.py +187 -0
  233. paasta_tools/paastaapi/model/instance_status_cassandracluster.py +173 -0
  234. paasta_tools/paastaapi/model/instance_status_flink.py +173 -0
  235. paasta_tools/paastaapi/model/instance_status_kafkacluster.py +173 -0
  236. paasta_tools/paastaapi/model/instance_status_kubernetes.py +263 -0
  237. paasta_tools/paastaapi/model/instance_status_kubernetes_autoscaling_status.py +187 -0
  238. paasta_tools/paastaapi/model/instance_status_kubernetes_v2.py +197 -0
  239. paasta_tools/paastaapi/model/instance_status_tron.py +204 -0
  240. paasta_tools/paastaapi/model/instance_tasks.py +182 -0
  241. paasta_tools/paastaapi/model/integer_and_error.py +173 -0
  242. paasta_tools/paastaapi/model/kubernetes_container.py +178 -0
  243. paasta_tools/paastaapi/model/kubernetes_container_v2.py +219 -0
  244. paasta_tools/paastaapi/model/kubernetes_healthcheck.py +176 -0
  245. paasta_tools/paastaapi/model/kubernetes_pod.py +201 -0
  246. paasta_tools/paastaapi/model/kubernetes_pod_event.py +176 -0
  247. paasta_tools/paastaapi/model/kubernetes_pod_v2.py +213 -0
  248. paasta_tools/paastaapi/model/kubernetes_replica_set.py +185 -0
  249. paasta_tools/paastaapi/model/kubernetes_version.py +202 -0
  250. paasta_tools/paastaapi/model/remote_run_outcome.py +189 -0
  251. paasta_tools/paastaapi/model/remote_run_start.py +185 -0
  252. paasta_tools/paastaapi/model/remote_run_stop.py +176 -0
  253. paasta_tools/paastaapi/model/remote_run_token.py +173 -0
  254. paasta_tools/paastaapi/model/resource.py +187 -0
  255. paasta_tools/paastaapi/model/resource_item.py +187 -0
  256. paasta_tools/paastaapi/model/resource_value.py +176 -0
  257. paasta_tools/paastaapi/model/smartstack_backend.py +191 -0
  258. paasta_tools/paastaapi/model/smartstack_location.py +181 -0
  259. paasta_tools/paastaapi/model/smartstack_status.py +181 -0
  260. paasta_tools/paastaapi/model/task_tail_lines.py +176 -0
  261. paasta_tools/paastaapi/model_utils.py +1879 -0
  262. paasta_tools/paastaapi/models/__init__.py +62 -0
  263. paasta_tools/paastaapi/rest.py +287 -0
  264. paasta_tools/prune_completed_pods.py +220 -0
  265. paasta_tools/puppet_service_tools.py +59 -0
  266. paasta_tools/py.typed +1 -0
  267. paasta_tools/remote_git.py +127 -0
  268. paasta_tools/run-paasta-api-in-dev-mode.py +57 -0
  269. paasta_tools/run-paasta-api-playground.py +51 -0
  270. paasta_tools/secret_providers/__init__.py +66 -0
  271. paasta_tools/secret_providers/vault.py +214 -0
  272. paasta_tools/secret_tools.py +277 -0
  273. paasta_tools/setup_istio_mesh.py +353 -0
  274. paasta_tools/setup_kubernetes_cr.py +412 -0
  275. paasta_tools/setup_kubernetes_crd.py +138 -0
  276. paasta_tools/setup_kubernetes_internal_crd.py +154 -0
  277. paasta_tools/setup_kubernetes_job.py +353 -0
  278. paasta_tools/setup_prometheus_adapter_config.py +1028 -0
  279. paasta_tools/setup_tron_namespace.py +248 -0
  280. paasta_tools/slack.py +75 -0
  281. paasta_tools/smartstack_tools.py +676 -0
  282. paasta_tools/spark_tools.py +283 -0
  283. paasta_tools/synapse_srv_namespaces_fact.py +42 -0
  284. paasta_tools/tron/__init__.py +0 -0
  285. paasta_tools/tron/client.py +158 -0
  286. paasta_tools/tron/tron_command_context.py +194 -0
  287. paasta_tools/tron/tron_timeutils.py +101 -0
  288. paasta_tools/tron_tools.py +1448 -0
  289. paasta_tools/utils.py +4307 -0
  290. paasta_tools/yaml_tools.py +44 -0
  291. paasta_tools-1.21.3.data/scripts/apply_external_resources.py +79 -0
  292. paasta_tools-1.21.3.data/scripts/bounce_log_latency_parser.py +68 -0
  293. paasta_tools-1.21.3.data/scripts/check_autoscaler_max_instances.py +212 -0
  294. paasta_tools-1.21.3.data/scripts/check_cassandracluster_services_replication.py +35 -0
  295. paasta_tools-1.21.3.data/scripts/check_flink_services_health.py +203 -0
  296. paasta_tools-1.21.3.data/scripts/check_kubernetes_api.py +57 -0
  297. paasta_tools-1.21.3.data/scripts/check_kubernetes_services_replication.py +141 -0
  298. paasta_tools-1.21.3.data/scripts/check_manual_oapi_changes.sh +24 -0
  299. paasta_tools-1.21.3.data/scripts/check_oom_events.py +244 -0
  300. paasta_tools-1.21.3.data/scripts/check_orphans.py +306 -0
  301. paasta_tools-1.21.3.data/scripts/check_spark_jobs.py +234 -0
  302. paasta_tools-1.21.3.data/scripts/cleanup_kubernetes_cr.py +138 -0
  303. paasta_tools-1.21.3.data/scripts/cleanup_kubernetes_crd.py +145 -0
  304. paasta_tools-1.21.3.data/scripts/cleanup_kubernetes_jobs.py +344 -0
  305. paasta_tools-1.21.3.data/scripts/create_dynamodb_table.py +35 -0
  306. paasta_tools-1.21.3.data/scripts/create_paasta_playground.py +105 -0
  307. paasta_tools-1.21.3.data/scripts/delete_kubernetes_deployments.py +89 -0
  308. paasta_tools-1.21.3.data/scripts/emit_allocated_cpu_metrics.py +50 -0
  309. paasta_tools-1.21.3.data/scripts/generate_all_deployments +9 -0
  310. paasta_tools-1.21.3.data/scripts/generate_authenticating_services.py +94 -0
  311. paasta_tools-1.21.3.data/scripts/generate_deployments_for_service.py +255 -0
  312. paasta_tools-1.21.3.data/scripts/generate_services_file.py +114 -0
  313. paasta_tools-1.21.3.data/scripts/generate_services_yaml.py +30 -0
  314. paasta_tools-1.21.3.data/scripts/get_running_task_allocation.py +346 -0
  315. paasta_tools-1.21.3.data/scripts/habitat_fixer.py +86 -0
  316. paasta_tools-1.21.3.data/scripts/ide_helper.py +316 -0
  317. paasta_tools-1.21.3.data/scripts/is_pod_healthy_in_proxy.py +139 -0
  318. paasta_tools-1.21.3.data/scripts/is_pod_healthy_in_smartstack.py +50 -0
  319. paasta_tools-1.21.3.data/scripts/kill_bad_containers.py +109 -0
  320. paasta_tools-1.21.3.data/scripts/kubernetes_remove_evicted_pods.py +164 -0
  321. paasta_tools-1.21.3.data/scripts/mass-deploy-tag.sh +44 -0
  322. paasta_tools-1.21.3.data/scripts/mock_patch_checker.py +86 -0
  323. paasta_tools-1.21.3.data/scripts/paasta_cleanup_remote_run_resources.py +135 -0
  324. paasta_tools-1.21.3.data/scripts/paasta_cleanup_stale_nodes.py +181 -0
  325. paasta_tools-1.21.3.data/scripts/paasta_deploy_tron_jobs +3 -0
  326. paasta_tools-1.21.3.data/scripts/paasta_execute_docker_command.py +123 -0
  327. paasta_tools-1.21.3.data/scripts/paasta_secrets_sync.py +758 -0
  328. paasta_tools-1.21.3.data/scripts/paasta_tabcomplete.sh +23 -0
  329. paasta_tools-1.21.3.data/scripts/paasta_update_soa_memcpu.py +520 -0
  330. paasta_tools-1.21.3.data/scripts/render_template.py +129 -0
  331. paasta_tools-1.21.3.data/scripts/rightsizer_soaconfigs_update.py +348 -0
  332. paasta_tools-1.21.3.data/scripts/service_shard_remove.py +157 -0
  333. paasta_tools-1.21.3.data/scripts/service_shard_update.py +373 -0
  334. paasta_tools-1.21.3.data/scripts/setup_istio_mesh.py +353 -0
  335. paasta_tools-1.21.3.data/scripts/setup_kubernetes_cr.py +412 -0
  336. paasta_tools-1.21.3.data/scripts/setup_kubernetes_crd.py +138 -0
  337. paasta_tools-1.21.3.data/scripts/setup_kubernetes_internal_crd.py +154 -0
  338. paasta_tools-1.21.3.data/scripts/setup_kubernetes_job.py +353 -0
  339. paasta_tools-1.21.3.data/scripts/setup_prometheus_adapter_config.py +1028 -0
  340. paasta_tools-1.21.3.data/scripts/shared_ip_check.py +77 -0
  341. paasta_tools-1.21.3.data/scripts/synapse_srv_namespaces_fact.py +42 -0
  342. paasta_tools-1.21.3.data/scripts/timeouts_metrics_prom.py +64 -0
  343. paasta_tools-1.21.3.dist-info/LICENSE +201 -0
  344. paasta_tools-1.21.3.dist-info/METADATA +74 -0
  345. paasta_tools-1.21.3.dist-info/RECORD +348 -0
  346. paasta_tools-1.21.3.dist-info/WHEEL +5 -0
  347. paasta_tools-1.21.3.dist-info/entry_points.txt +20 -0
  348. paasta_tools-1.21.3.dist-info/top_level.txt +2 -0
@@ -0,0 +1,1396 @@
1
+ #!/usr/bin/env python
2
+ # Copyright 2015-2016 Yelp Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import datetime
16
+ import json
17
+ import os
18
+ import shutil
19
+ import socket
20
+ import subprocess
21
+ import sys
22
+ import tempfile
23
+ import threading
24
+ import time
25
+ import uuid
26
+ from os import execlpe
27
+ from random import randint
28
+ from typing import Optional
29
+ from urllib.parse import urlparse
30
+
31
+ import boto3
32
+ import requests
33
+ from docker import errors
34
+ from mypy_extensions import TypedDict
35
+
36
+ from paasta_tools.adhoc_tools import get_default_interactive_config
37
+ from paasta_tools.cli.authentication import get_service_auth_token
38
+ from paasta_tools.cli.authentication import get_sso_auth_token
39
+ from paasta_tools.cli.cmds.check import makefile_responds_to
40
+ from paasta_tools.cli.cmds.cook_image import paasta_cook_image
41
+ from paasta_tools.cli.utils import figure_out_service_name
42
+ from paasta_tools.cli.utils import get_instance_config
43
+ from paasta_tools.cli.utils import lazy_choices_completer
44
+ from paasta_tools.cli.utils import list_instances
45
+ from paasta_tools.cli.utils import pick_random_port
46
+ from paasta_tools.generate_deployments_for_service import build_docker_image_name
47
+ from paasta_tools.kubernetes_tools import get_kubernetes_secret_env_variables
48
+ from paasta_tools.kubernetes_tools import get_kubernetes_secret_volumes
49
+ from paasta_tools.kubernetes_tools import KUBE_CONFIG_USER_PATH
50
+ from paasta_tools.kubernetes_tools import KubeClient
51
+ from paasta_tools.long_running_service_tools import get_healthcheck_for_instance
52
+ from paasta_tools.paasta_execute_docker_command import execute_in_container
53
+ from paasta_tools.secret_tools import decrypt_secret_environment_variables
54
+ from paasta_tools.secret_tools import decrypt_secret_volumes
55
+ from paasta_tools.tron_tools import parse_time_variables
56
+ from paasta_tools.utils import _run
57
+ from paasta_tools.utils import DEFAULT_SOA_DIR
58
+ from paasta_tools.utils import get_docker_client
59
+ from paasta_tools.utils import get_possible_launched_by_user_variable_from_env
60
+ from paasta_tools.utils import get_username
61
+ from paasta_tools.utils import InstanceConfig
62
+ from paasta_tools.utils import is_secrets_for_teams_enabled
63
+ from paasta_tools.utils import list_clusters
64
+ from paasta_tools.utils import list_services
65
+ from paasta_tools.utils import load_system_paasta_config
66
+ from paasta_tools.utils import NoConfigurationForServiceError
67
+ from paasta_tools.utils import NoDeploymentsAvailable
68
+ from paasta_tools.utils import NoDockerImageError
69
+ from paasta_tools.utils import PaastaColors
70
+ from paasta_tools.utils import PaastaNotConfiguredError
71
+ from paasta_tools.utils import SystemPaastaConfig
72
+ from paasta_tools.utils import Timeout
73
+ from paasta_tools.utils import TimeoutError
74
+ from paasta_tools.utils import validate_service_instance
75
+
76
+
77
+ class AWSSessionCreds(TypedDict):
78
+ AWS_ACCESS_KEY_ID: str
79
+ AWS_SECRET_ACCESS_KEY: str
80
+ AWS_SESSION_TOKEN: str
81
+ AWS_SECURITY_TOKEN: str
82
+
83
+
84
+ def parse_date(date_string):
85
+ return datetime.datetime.strptime(date_string, "%Y-%m-%d")
86
+
87
+
88
+ def perform_http_healthcheck(url, timeout):
89
+ """Returns true if healthcheck on url succeeds, false otherwise
90
+
91
+ :param url: the healthcheck url
92
+ :param timeout: timeout in seconds
93
+ :returns: True if healthcheck succeeds within number of seconds specified by timeout, false otherwise
94
+ """
95
+ try:
96
+ with Timeout(seconds=timeout):
97
+ try:
98
+ res = requests.get(url, verify=False)
99
+ except requests.ConnectionError:
100
+ return (False, "http request failed: connection failed")
101
+ except TimeoutError:
102
+ return (False, "http request timed out after %d seconds" % timeout)
103
+
104
+ if "content-type" in res.headers and "," in res.headers["content-type"]:
105
+ print(
106
+ PaastaColors.yellow(
107
+ "Multiple content-type headers detected in response."
108
+ " The Mesos healthcheck system will treat this as a failure!"
109
+ )
110
+ )
111
+ return (False, "http request succeeded, code %d" % res.status_code)
112
+ # check if response code is valid per https://mesosphere.github.io/marathon/docs/health-checks.html
113
+ elif res.status_code >= 200 and res.status_code < 400:
114
+ return (True, "http request succeeded, code %d" % res.status_code)
115
+ else:
116
+ return (False, "http request failed, code %s" % str(res.status_code))
117
+
118
+
119
+ def perform_tcp_healthcheck(url, timeout):
120
+ """Returns true if successfully connects to host and port, false otherwise
121
+
122
+ :param url: the healthcheck url (in the form tcp://host:port)
123
+ :param timeout: timeout in seconds
124
+ :returns: True if healthcheck succeeds within number of seconds specified by timeout, false otherwise
125
+ """
126
+ url_elem = urlparse(url)
127
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
128
+ sock.settimeout(timeout)
129
+ result = sock.connect_ex((url_elem.hostname, url_elem.port))
130
+ sock.close()
131
+ if result == 0:
132
+ return (True, "tcp connection succeeded")
133
+ else:
134
+ return (False, "%s (timeout %d seconds)" % (os.strerror(result), timeout))
135
+
136
+
137
+ def perform_cmd_healthcheck(docker_client, container_id, command, timeout):
138
+ """Returns true if return code of command is 0 when executed inside container, false otherwise
139
+
140
+ :param docker_client: Docker client object
141
+ :param container_id: Docker container id
142
+ :param command: command to execute
143
+ :param timeout: timeout in seconds
144
+ :returns: True if command exits with return code 0, false otherwise
145
+ """
146
+ (output, return_code) = execute_in_container(
147
+ docker_client, container_id, command, timeout
148
+ )
149
+ if return_code == 0:
150
+ return (True, output)
151
+ else:
152
+ return (False, output)
153
+
154
+
155
+ def run_healthcheck_on_container(
156
+ docker_client, container_id, healthcheck_mode, healthcheck_data, timeout
157
+ ):
158
+ """Performs healthcheck on a container
159
+
160
+ :param container_id: Docker container id
161
+ :param healthcheck_mode: one of 'http', 'https', 'tcp', or 'cmd'
162
+ :param healthcheck_data: a URL when healthcheck_mode is 'http[s]' or 'tcp', a command if healthcheck_mode is 'cmd'
163
+ :param timeout: timeout in seconds for individual check
164
+ :returns: a tuple of (bool, output string)
165
+ """
166
+ healthcheck_result = (False, "unknown")
167
+ if healthcheck_mode == "cmd":
168
+ healthcheck_result = perform_cmd_healthcheck(
169
+ docker_client, container_id, healthcheck_data, timeout
170
+ )
171
+ elif healthcheck_mode == "http" or healthcheck_mode == "https":
172
+ healthcheck_result = perform_http_healthcheck(healthcheck_data, timeout)
173
+ elif healthcheck_mode == "tcp":
174
+ healthcheck_result = perform_tcp_healthcheck(healthcheck_data, timeout)
175
+ else:
176
+ print(
177
+ PaastaColors.yellow(
178
+ "Healthcheck mode '%s' is not currently supported!" % healthcheck_mode
179
+ )
180
+ )
181
+ sys.exit(1)
182
+ return healthcheck_result
183
+
184
+
185
+ def simulate_healthcheck_on_service(
186
+ instance_config,
187
+ docker_client,
188
+ container_id,
189
+ healthcheck_mode,
190
+ healthcheck_data,
191
+ healthcheck_enabled,
192
+ ):
193
+ """Simulates Marathon-style healthcheck on given service if healthcheck is enabled
194
+
195
+ :param instance_config: service manifest
196
+ :param docker_client: Docker client object
197
+ :param container_id: Docker container id
198
+ :param healthcheck_data: tuple url to healthcheck
199
+ :param healthcheck_enabled: boolean
200
+ :returns: healthcheck_passed: boolean
201
+ """
202
+ healthcheck_link = PaastaColors.cyan(healthcheck_data)
203
+ if healthcheck_enabled:
204
+ grace_period = instance_config.get_healthcheck_grace_period_seconds()
205
+ timeout = instance_config.get_healthcheck_timeout_seconds()
206
+ interval = instance_config.get_healthcheck_interval_seconds()
207
+ max_failures = instance_config.get_healthcheck_max_consecutive_failures()
208
+
209
+ print(
210
+ "\nStarting health check via %s (waiting %s seconds before "
211
+ "considering failures due to grace period):"
212
+ % (healthcheck_link, grace_period)
213
+ )
214
+
215
+ # silently start performing health checks until grace period ends or first check succeeds
216
+ graceperiod_end_time = time.time() + grace_period
217
+ after_grace_period_attempts = 0
218
+ healthchecking = True
219
+
220
+ def _stream_docker_logs(container_id, generator):
221
+ while healthchecking:
222
+ try:
223
+ # the generator will block until another log line is available
224
+ log_line = next(generator).decode("utf-8").rstrip("\n")
225
+ if healthchecking:
226
+ print(f"container [{container_id[:12]}]: {log_line}")
227
+ else:
228
+ # stop streaming at first opportunity, since generator.close()
229
+ # cant be used until the container is dead
230
+ break
231
+ except StopIteration: # natural end of logs
232
+ break
233
+
234
+ docker_logs_generator = docker_client.logs(
235
+ container_id, stderr=True, stream=True
236
+ )
237
+ threading.Thread(
238
+ target=_stream_docker_logs,
239
+ daemon=True,
240
+ args=(container_id, docker_logs_generator),
241
+ ).start()
242
+
243
+ while True:
244
+ # First inspect the container for early exits
245
+ container_state = docker_client.inspect_container(container_id)
246
+ if not container_state["State"]["Running"]:
247
+ print(
248
+ PaastaColors.red(
249
+ "Container exited with code {}".format(
250
+ container_state["State"]["ExitCode"]
251
+ )
252
+ )
253
+ )
254
+ healthcheck_passed = False
255
+ break
256
+
257
+ healthcheck_passed, healthcheck_output = run_healthcheck_on_container(
258
+ docker_client, container_id, healthcheck_mode, healthcheck_data, timeout
259
+ )
260
+
261
+ # Yay, we passed the healthcheck
262
+ if healthcheck_passed:
263
+ print(
264
+ "{}'{}' (via {})".format(
265
+ PaastaColors.green("Healthcheck succeeded!: "),
266
+ healthcheck_output,
267
+ healthcheck_link,
268
+ )
269
+ )
270
+ break
271
+
272
+ # Otherwise, print why we failed
273
+ if time.time() < graceperiod_end_time:
274
+ color = PaastaColors.grey
275
+ msg = "(disregarded due to grace period)"
276
+ extra_msg = f" (via: {healthcheck_link}. Output: {healthcheck_output})"
277
+ else:
278
+ # If we've exceeded the grace period, we start incrementing attempts
279
+ after_grace_period_attempts += 1
280
+ color = PaastaColors.red
281
+ msg = "(Attempt {} of {})".format(
282
+ after_grace_period_attempts, max_failures
283
+ )
284
+ extra_msg = f" (via: {healthcheck_link}. Output: {healthcheck_output})"
285
+
286
+ print("{}{}".format(color(f"Healthcheck failed! {msg}"), extra_msg))
287
+
288
+ if after_grace_period_attempts == max_failures:
289
+ break
290
+
291
+ time.sleep(interval)
292
+ healthchecking = False # end docker logs stream
293
+ else:
294
+ print(
295
+ "\nPaaSTA would have healthchecked your service via\n%s" % healthcheck_link
296
+ )
297
+ healthcheck_passed = True
298
+ return healthcheck_passed
299
+
300
+
301
+ def read_local_dockerfile_lines():
302
+ dockerfile = os.path.join(os.getcwd(), "Dockerfile")
303
+ return open(dockerfile).readlines()
304
+
305
+
306
+ def add_subparser(subparsers):
307
+ list_parser = subparsers.add_parser(
308
+ "local-run",
309
+ help="Run service's Docker image locally",
310
+ description=(
311
+ "'paasta local-run' is useful for simulating how a PaaSTA service would be "
312
+ "executed on a real cluster. It analyzes the local soa-configs and constructs "
313
+ "a 'docker run' invocation to match. This is useful as a type of end-to-end "
314
+ "test, ensuring that a service will work inside the docker container as expected. "
315
+ "Additionally, 'local-run' can healthcheck a service per the configured healthcheck.\n\n"
316
+ "Alternatively, 'local-run' can be used with --pull, which will pull the currently "
317
+ "deployed docker image and use it, instead of building one."
318
+ ),
319
+ epilog=(
320
+ "Note: 'paasta local-run' uses docker commands, which may require elevated privileges "
321
+ "to run (sudo)."
322
+ ),
323
+ )
324
+ list_parser.add_argument(
325
+ "-s", "--service", help="The name of the service you wish to inspect"
326
+ ).completer = lazy_choices_completer(list_services)
327
+ list_parser.add_argument(
328
+ "-c",
329
+ "--cluster",
330
+ help=(
331
+ "The name of the cluster you wish to simulate. "
332
+ "If omitted, uses the default cluster defined in the paasta local-run configs"
333
+ ),
334
+ ).completer = lazy_choices_completer(list_clusters)
335
+ list_parser.add_argument(
336
+ "-y",
337
+ "--yelpsoa-config-root",
338
+ dest="yelpsoa_config_root",
339
+ help="A directory from which yelpsoa-configs should be read from",
340
+ default=DEFAULT_SOA_DIR,
341
+ )
342
+ build_pull_group = list_parser.add_mutually_exclusive_group()
343
+ build_pull_group.add_argument(
344
+ "-b",
345
+ "--build",
346
+ help=(
347
+ "Build the docker image to run from scratch using the local Makefile's "
348
+ "'cook-image' target. Defaults to try to use the local Makefile if present."
349
+ ),
350
+ action="store_const",
351
+ const="build",
352
+ dest="action",
353
+ )
354
+ build_pull_group.add_argument(
355
+ "-p",
356
+ "--pull",
357
+ help=(
358
+ "Pull the docker image marked for deployment from the Docker registry and "
359
+ "use that for the local-run. This is the opposite of --build."
360
+ ),
361
+ action="store_const",
362
+ const="pull",
363
+ dest="action",
364
+ )
365
+ build_pull_group.add_argument(
366
+ "-d",
367
+ "--dry-run",
368
+ help="Shows the arguments supplied to docker as json.",
369
+ action="store_const",
370
+ const="dry_run",
371
+ dest="action",
372
+ )
373
+ build_pull_group.set_defaults(action="build")
374
+ list_parser.add_argument(
375
+ "--json-dict",
376
+ help="When running dry run, output the arguments as a json dict",
377
+ action="store_true",
378
+ dest="dry_run_json_dict",
379
+ )
380
+ list_parser.add_argument(
381
+ "-C",
382
+ "--cmd",
383
+ help=(
384
+ "Run Docker container with particular command, "
385
+ 'for example: "bash". By default will use the command or args specified by the '
386
+ "soa-configs or what was specified in the Dockerfile"
387
+ ),
388
+ required=False,
389
+ default=None,
390
+ )
391
+ list_parser.add_argument(
392
+ "-i",
393
+ "--instance",
394
+ help=(
395
+ "Simulate a docker run for a particular instance of the service, like 'main' or 'canary'. "
396
+ "NOTE: if you don't specify an instance, PaaSTA will run in interactive mode"
397
+ ),
398
+ required=False,
399
+ default=None,
400
+ ).completer = lazy_choices_completer(list_instances)
401
+ list_parser.add_argument(
402
+ "--date",
403
+ default=datetime.datetime.today().strftime("%Y-%m-%d"),
404
+ help="Date to use for interpolating date variables in a job. Defaults to use %(default)s.",
405
+ type=parse_date,
406
+ )
407
+ list_parser.add_argument(
408
+ "-v",
409
+ "--verbose",
410
+ help="Show Docker commands output",
411
+ action="store_true",
412
+ required=False,
413
+ default=True,
414
+ )
415
+ list_parser.add_argument(
416
+ "-I",
417
+ "--interactive",
418
+ help=(
419
+ 'Run container in interactive mode. If interactive is set the default command will be "bash" '
420
+ 'unless otherwise set by the "--cmd" flag'
421
+ ),
422
+ action="store_true",
423
+ required=False,
424
+ default=False,
425
+ )
426
+ list_parser.add_argument(
427
+ "-k",
428
+ "--no-healthcheck",
429
+ help="Disable simulated healthcheck",
430
+ dest="healthcheck",
431
+ action="store_false",
432
+ required=False,
433
+ default=True,
434
+ )
435
+ list_parser.add_argument(
436
+ "-t",
437
+ "--healthcheck-only",
438
+ help="Terminates container after healthcheck (exits with status code 0 on success, 1 otherwise)",
439
+ dest="healthcheck_only",
440
+ action="store_true",
441
+ required=False,
442
+ default=False,
443
+ )
444
+ list_parser.add_argument(
445
+ "-o",
446
+ "--port",
447
+ help="Specify a port number to use. If not set, a random non-conflicting port will be found.",
448
+ type=int,
449
+ dest="user_port",
450
+ required=False,
451
+ default=False,
452
+ )
453
+ list_parser.add_argument(
454
+ "--vault-auth-method",
455
+ help="Override how we auth with vault, defaults to token if not present",
456
+ type=str,
457
+ dest="vault_auth_method",
458
+ required=False,
459
+ default="token",
460
+ choices=["token", "ldap"],
461
+ )
462
+ list_parser.add_argument(
463
+ "--vault-token-file",
464
+ help="Override vault token file, defaults to %(default)s",
465
+ type=str,
466
+ dest="vault_token_file",
467
+ required=False,
468
+ default="/var/spool/.paasta_vault_token",
469
+ )
470
+ list_parser.add_argument(
471
+ "--skip-secrets",
472
+ help="Skip decrypting secrets, useful if running non-interactively",
473
+ dest="skip_secrets",
474
+ required=False,
475
+ action="store_true",
476
+ default=False,
477
+ )
478
+ list_parser.add_argument(
479
+ "--assume-role-aws-account",
480
+ "--aws-account",
481
+ "-a",
482
+ help="Specify AWS account from which to source credentials",
483
+ )
484
+ list_parser.add_argument(
485
+ "--assume-role-arn",
486
+ help=(
487
+ "role ARN to assume before launching the service. "
488
+ "Example format: arn:aws:iam::01234567890:role/rolename"
489
+ ),
490
+ type=str,
491
+ dest="assume_role_arn",
492
+ required=False,
493
+ default="",
494
+ )
495
+ list_parser.add_argument(
496
+ "--assume-pod-identity",
497
+ help="If pod identity is set via yelpsoa-configs, attempt to assume it",
498
+ action="store_true",
499
+ dest="assume_pod_identity",
500
+ required=False,
501
+ default=False,
502
+ )
503
+ list_parser.add_argument(
504
+ "--use-okta-role",
505
+ help="Call aws-okta and run the service within the context of the returned credentials",
506
+ dest="use_okta_role",
507
+ action="store_true",
508
+ required=False,
509
+ default=False,
510
+ )
511
+ list_parser.add_argument(
512
+ "--sha",
513
+ help=(
514
+ "SHA to run instead of the currently marked-for-deployment SHA. Ignored when used with --build."
515
+ " Must be a version that exists in the registry, i.e. it has been built by Jenkins."
516
+ ),
517
+ type=str,
518
+ dest="sha",
519
+ required=False,
520
+ default=None,
521
+ )
522
+ list_parser.add_argument(
523
+ "--volume",
524
+ dest="volumes",
525
+ action="append",
526
+ type=str,
527
+ default=[],
528
+ required=False,
529
+ help=(
530
+ "Same as the -v / --volume parameter to docker run: hostPath:containerPath[:mode]"
531
+ ),
532
+ )
533
+ service_auth_group = list_parser.add_mutually_exclusive_group()
534
+ service_auth_group.add_argument(
535
+ "--use-service-auth-token",
536
+ help=(
537
+ "Acquire service authentication token for the underlying instance,"
538
+ " and set it in the container environment"
539
+ ),
540
+ action="store_true",
541
+ dest="use_service_auth_token",
542
+ required=False,
543
+ default=False,
544
+ )
545
+ service_auth_group.add_argument(
546
+ "--use-sso-service-auth-token",
547
+ help=(
548
+ "Acquire service authentication token from SSO provider,"
549
+ " and set it in the container environment"
550
+ ),
551
+ action="store_true",
552
+ dest="use_sso_service_auth_token",
553
+ required=False,
554
+ default=False,
555
+ )
556
+
557
+ list_parser.set_defaults(command=paasta_local_run)
558
+
559
+
560
+ def get_container_name():
561
+ return "paasta_local_run_{}_{}".format(get_username(), randint(1, 999999))
562
+
563
+
564
+ def get_docker_run_cmd(
565
+ memory,
566
+ chosen_port,
567
+ container_port,
568
+ container_name,
569
+ volumes,
570
+ env,
571
+ interactive,
572
+ docker_hash,
573
+ command,
574
+ net,
575
+ docker_params,
576
+ detach,
577
+ ):
578
+ cmd = ["paasta_docker_wrapper", "run"]
579
+ for k in env.keys():
580
+ cmd.append("--env")
581
+ cmd.append(f"{k}")
582
+ cmd.append("--memory=%dm" % memory)
583
+ for i in docker_params:
584
+ cmd.append(f"--{i['key']}={i['value']}")
585
+ if net == "bridge" and container_port is not None:
586
+ cmd.append("--publish=%d:%d" % (chosen_port, container_port))
587
+ elif net == "host":
588
+ cmd.append("--net=host")
589
+ cmd.append("--name=%s" % container_name)
590
+ for volume in volumes:
591
+ cmd.append("--volume=%s" % volume)
592
+ if interactive:
593
+ cmd.append("--interactive=true")
594
+ if sys.stdin.isatty():
595
+ cmd.append("--tty=true")
596
+ else:
597
+ if detach:
598
+ cmd.append("--detach=true")
599
+ cmd.append("%s" % docker_hash)
600
+ if command:
601
+ if isinstance(command, str):
602
+ cmd.extend(("sh", "-c", command))
603
+ else:
604
+ cmd.extend(command)
605
+ return cmd
606
+
607
+
608
+ class LostContainerException(Exception):
609
+ pass
610
+
611
+
612
+ def docker_pull_image(docker_url):
613
+ """Pull an image via ``docker pull``. Uses the actual pull command instead of the python
614
+ bindings due to the docker auth/registry transition. Once we are past Docker 1.6
615
+ we can use better credential management, but for now this function assumes the
616
+ user running the command has already been authorized for the registry"""
617
+ print(
618
+ "Please wait while the image (%s) is pulled (times out after 30m)..."
619
+ % docker_url,
620
+ file=sys.stderr,
621
+ )
622
+ with Timeout(
623
+ seconds=1800, error_message=f"Timed out pulling docker image from {docker_url}"
624
+ ), open(os.devnull, mode="wb") as DEVNULL:
625
+ ret, _ = _run("docker pull %s" % docker_url, stream=True, stdin=DEVNULL)
626
+ if ret != 0:
627
+ print(
628
+ "\nPull failed. Are you authorized to run docker commands?",
629
+ file=sys.stderr,
630
+ )
631
+ sys.exit(ret)
632
+
633
+
634
+ def get_container_id(docker_client, container_name):
635
+ """Use 'docker_client' to find the container we started, identifiable by
636
+ its 'container_name'. If we can't find the id, raise
637
+ LostContainerException.
638
+ """
639
+ containers = docker_client.containers(all=False)
640
+ for container in containers:
641
+ if "/%s" % container_name in container.get("Names", []):
642
+ return container.get("Id")
643
+ raise LostContainerException(
644
+ "Can't find the container I just launched so I can't do anything else.\n"
645
+ "Try docker 'ps --all | grep %s' to see where it went.\n"
646
+ "Here were all the containers:\n"
647
+ "%s" % (container_name, containers)
648
+ )
649
+
650
+
651
+ def _cleanup_container(docker_client, container_id):
652
+ if docker_client.inspect_container(container_id)["State"].get("OOMKilled", False):
653
+ print(
654
+ PaastaColors.red(
655
+ "Your service was killed by the OOM Killer!\n"
656
+ "You've exceeded the memory limit, try increasing the mem parameter in your soa_configs"
657
+ ),
658
+ file=sys.stderr,
659
+ )
660
+ print("\nStopping and removing the old container %s..." % container_id)
661
+ print("(Please wait or you may leave an orphaned container.)")
662
+ try:
663
+ docker_client.stop(container_id)
664
+ docker_client.remove_container(container_id)
665
+ print("...done")
666
+ except errors.APIError:
667
+ print(
668
+ PaastaColors.yellow(
669
+ "Could not clean up container! You should stop and remove container '%s' manually."
670
+ % container_id
671
+ )
672
+ )
673
+
674
+
675
+ def get_local_run_environment_vars(instance_config, port0, framework):
676
+ """Returns a dictionary of environment variables to simulate what would be available to
677
+ a paasta service running in a container"""
678
+ hostname = socket.getfqdn()
679
+ docker_image = instance_config.get_docker_image()
680
+ if docker_image == "":
681
+ # In a local_run environment, the docker_image may not be available
682
+ # so we can fall-back to the injected DOCKER_TAG per the paasta contract
683
+ docker_image = os.environ["DOCKER_TAG"]
684
+ env = {
685
+ "HOST": hostname,
686
+ "PAASTA_DOCKER_IMAGE": docker_image,
687
+ "PAASTA_LAUNCHED_BY": get_possible_launched_by_user_variable_from_env(),
688
+ "PAASTA_HOST": hostname,
689
+ # Kubernetes instances remove PAASTA_CLUSTER, so we need to re-add it ourselves
690
+ "PAASTA_CLUSTER": instance_config.get_cluster(),
691
+ }
692
+
693
+ return env
694
+
695
+
696
+ def check_if_port_free(port):
697
+ temp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
698
+ try:
699
+ temp_socket.bind(("127.0.0.1", port))
700
+ except socket.error:
701
+ return False
702
+ finally:
703
+ temp_socket.close()
704
+ return True
705
+
706
+
707
+ def resolve_aws_account_from_runtimeenv() -> str:
708
+ try:
709
+ with open("/nail/etc/runtimeenv") as runtimeenv_file:
710
+ runtimeenv = runtimeenv_file.read()
711
+ except FileNotFoundError:
712
+ print(
713
+ "Unable to determine environment for AWS account name. Using 'dev'",
714
+ file=sys.stderr,
715
+ )
716
+ runtimeenv = "dev"
717
+
718
+ runtimeenv_to_account_overrides = {
719
+ "stage": "dev",
720
+ "corp": "corpprod",
721
+ }
722
+ return runtimeenv_to_account_overrides.get(runtimeenv, runtimeenv)
723
+
724
+
725
+ def assume_aws_role(
726
+ instance_config: InstanceConfig,
727
+ service: str,
728
+ assume_role_arn: str,
729
+ assume_pod_identity: bool,
730
+ use_okta_role: bool,
731
+ aws_account: str,
732
+ ) -> AWSSessionCreds:
733
+ """Runs AWS cli to assume into the correct role, then extract and return the ENV variables from that session"""
734
+ pod_identity = instance_config.get_iam_role()
735
+ if assume_role_arn:
736
+ pod_identity = assume_role_arn
737
+ if assume_pod_identity and not pod_identity:
738
+ print(
739
+ f"Error: --assume-pod-identity passed but no pod identity was found for this instance ({instance_config.instance})",
740
+ file=sys.stderr,
741
+ )
742
+ sys.exit(1)
743
+
744
+ if pod_identity and (assume_pod_identity or assume_role_arn):
745
+ print(
746
+ "Calling aws-okta to assume role {} using account {}".format(
747
+ pod_identity, aws_account
748
+ )
749
+ )
750
+ elif use_okta_role:
751
+ print(f"Calling aws-okta using account {aws_account}")
752
+ elif "AWS_ROLE_ARN" in os.environ and "AWS_WEB_IDENTITY_TOKEN_FILE" in os.environ:
753
+ # Get a session using the current pod identity
754
+ print(
755
+ f"Found Pod Identity token in env. Assuming into role {os.environ['AWS_ROLE_ARN']}."
756
+ )
757
+ boto_session = boto3.Session()
758
+ credentials = boto_session.get_credentials()
759
+ assumed_creds_dict: AWSSessionCreds = {
760
+ "AWS_ACCESS_KEY_ID": credentials.access_key,
761
+ "AWS_SECRET_ACCESS_KEY": credentials.secret_key,
762
+ "AWS_SESSION_TOKEN": credentials.token,
763
+ "AWS_SECURITY_TOKEN": credentials.token,
764
+ }
765
+ return assumed_creds_dict
766
+ else:
767
+ # use_okta_role, assume_pod_identity, and assume_role are all empty, and there's no
768
+ # pod identity (web identity token) in the env. This shouldn't happen
769
+ print(
770
+ "Error: assume_aws_role called without required arguments and no pod identity env",
771
+ file=sys.stderr,
772
+ )
773
+ sys.exit(1)
774
+ # local-run will sometimes run as root - make sure that we get the actual
775
+ # users AWS credentials instead of looking for non-existent root AWS
776
+ # credentials
777
+ if os.getuid() == 0:
778
+ aws_okta_cmd = [
779
+ "sudo",
780
+ "-u",
781
+ get_username(),
782
+ f"HOME=/nail/home/{get_username()}",
783
+ "aws-okta",
784
+ "-a",
785
+ aws_account,
786
+ "-o",
787
+ "json",
788
+ ]
789
+ else:
790
+ aws_okta_cmd = ["aws-okta", "-a", aws_account, "-o", "json"]
791
+ cmd = subprocess.run(aws_okta_cmd, stdout=subprocess.PIPE)
792
+ if cmd.returncode != 0:
793
+ print(
794
+ "Error calling aws-okta. Remove --assume-pod-identity to run without pod identity role",
795
+ file=sys.stderr,
796
+ )
797
+ sys.exit(1)
798
+ cmd_output = json.loads(cmd.stdout.decode("utf-8"))
799
+
800
+ if not use_okta_role:
801
+ boto_session = boto3.Session(
802
+ aws_access_key_id=cmd_output["AccessKeyId"],
803
+ aws_secret_access_key=cmd_output["SecretAccessKey"],
804
+ aws_session_token=cmd_output["SessionToken"],
805
+ )
806
+ sts_client = boto_session.client("sts")
807
+ assumed_role = sts_client.assume_role(
808
+ RoleArn=pod_identity, RoleSessionName=f"{get_username()}-local-run"
809
+ )
810
+ # The contents of "Credentials" key from assume_role is the same as from aws-okta
811
+ cmd_output = assumed_role["Credentials"]
812
+
813
+ creds_dict: AWSSessionCreds = {
814
+ "AWS_ACCESS_KEY_ID": cmd_output["AccessKeyId"],
815
+ "AWS_SECRET_ACCESS_KEY": cmd_output["SecretAccessKey"],
816
+ "AWS_SESSION_TOKEN": cmd_output["SessionToken"],
817
+ "AWS_SECURITY_TOKEN": cmd_output["SessionToken"],
818
+ }
819
+ return creds_dict
820
+
821
+
822
+ def run_docker_container(
823
+ docker_client,
824
+ service,
825
+ instance,
826
+ docker_url,
827
+ volumes,
828
+ interactive,
829
+ command,
830
+ healthcheck,
831
+ healthcheck_only,
832
+ user_port,
833
+ instance_config,
834
+ secret_provider_name,
835
+ soa_dir=DEFAULT_SOA_DIR,
836
+ dry_run=False,
837
+ json_dict=False,
838
+ framework=None,
839
+ secret_provider_kwargs={},
840
+ skip_secrets=False,
841
+ assume_pod_identity=False,
842
+ assume_role_arn="",
843
+ use_okta_role=False,
844
+ assume_role_aws_account: Optional[str] = None,
845
+ use_service_auth_token: bool = False,
846
+ use_sso_service_auth_token: bool = False,
847
+ ):
848
+ """docker-py has issues running a container with a TTY attached, so for
849
+ consistency we execute 'docker run' directly in both interactive and
850
+ non-interactive modes.
851
+
852
+ In non-interactive mode when the run is complete, stop the container and
853
+ remove it (with docker-py).
854
+ """
855
+ if user_port:
856
+ if check_if_port_free(user_port):
857
+ chosen_port = user_port
858
+ else:
859
+ print(
860
+ PaastaColors.red(
861
+ "The chosen port is already in use!\n"
862
+ "Try specifying another one, or omit (--port|-o) and paasta will find a free one for you"
863
+ ),
864
+ file=sys.stderr,
865
+ )
866
+ sys.exit(1)
867
+ else:
868
+ chosen_port = pick_random_port(service)
869
+ environment = instance_config.get_env()
870
+ secret_volumes = {} # type: ignore
871
+ if not skip_secrets:
872
+ # if secrets_for_owner_team enabled in yelpsoa for service
873
+ if is_secrets_for_teams_enabled(service, soa_dir):
874
+ try:
875
+ kube_client = KubeClient(
876
+ config_file=KUBE_CONFIG_USER_PATH, context=instance_config.cluster
877
+ )
878
+ secret_environment = get_kubernetes_secret_env_variables(
879
+ kube_client, environment, service, instance_config.get_namespace()
880
+ )
881
+ secret_volumes = get_kubernetes_secret_volumes(
882
+ kube_client,
883
+ instance_config.get_secret_volumes(),
884
+ service,
885
+ instance_config.get_namespace(),
886
+ )
887
+ except Exception as e:
888
+ print(
889
+ f"Failed to retrieve kubernetes secrets with {e.__class__.__name__}: {e}"
890
+ )
891
+ print(
892
+ "If you don't need the secrets for local-run, you can add --skip-secrets"
893
+ )
894
+ sys.exit(1)
895
+ else:
896
+ try:
897
+ secret_environment = decrypt_secret_environment_variables(
898
+ secret_provider_name=secret_provider_name,
899
+ environment=environment,
900
+ soa_dir=soa_dir,
901
+ service_name=instance_config.get_service(),
902
+ cluster_name=instance_config.cluster,
903
+ secret_provider_kwargs=secret_provider_kwargs,
904
+ )
905
+ secret_volumes = decrypt_secret_volumes(
906
+ secret_provider_name=secret_provider_name,
907
+ secret_volumes_config=instance_config.get_secret_volumes(),
908
+ soa_dir=soa_dir,
909
+ service_name=instance_config.get_service(),
910
+ cluster_name=instance_config.cluster,
911
+ secret_provider_kwargs=secret_provider_kwargs,
912
+ )
913
+ except Exception as e:
914
+ print(f"Failed to decrypt secrets with {e.__class__.__name__}: {e}")
915
+ print(
916
+ "If you don't need the secrets for local-run, you can add --skip-secrets"
917
+ )
918
+ sys.exit(1)
919
+ environment.update(secret_environment)
920
+ if (
921
+ assume_role_arn
922
+ or assume_pod_identity
923
+ or use_okta_role
924
+ or "AWS_WEB_IDENTITY_TOKEN_FILE" in os.environ
925
+ ):
926
+ aws_creds = assume_aws_role(
927
+ instance_config,
928
+ service,
929
+ assume_role_arn,
930
+ assume_pod_identity,
931
+ use_okta_role,
932
+ assume_role_aws_account,
933
+ )
934
+ environment.update(aws_creds)
935
+
936
+ if use_service_auth_token:
937
+ environment["YELP_SVC_AUTHZ_TOKEN"] = get_service_auth_token()
938
+ elif use_sso_service_auth_token:
939
+ environment["YELP_SVC_AUTHZ_TOKEN"] = get_sso_auth_token()
940
+
941
+ local_run_environment = get_local_run_environment_vars(
942
+ instance_config=instance_config, port0=chosen_port, framework=framework
943
+ )
944
+ environment.update(local_run_environment)
945
+ net = instance_config.get_net()
946
+ memory = instance_config.get_mem()
947
+ container_name = get_container_name()
948
+ docker_params = instance_config.format_docker_parameters()
949
+
950
+ healthcheck_mode, healthcheck_data = get_healthcheck_for_instance(
951
+ service, instance, instance_config, chosen_port, soa_dir=soa_dir
952
+ )
953
+ if healthcheck_mode is None:
954
+ container_port = None
955
+ interactive = True
956
+ elif not user_port and not healthcheck and not healthcheck_only:
957
+ container_port = None
958
+ else:
959
+ try:
960
+ container_port = instance_config.get_container_port()
961
+ except AttributeError:
962
+ container_port = None
963
+
964
+ simulate_healthcheck = (
965
+ healthcheck_only or healthcheck
966
+ ) and healthcheck_mode is not None
967
+
968
+ for container_mount_path, secret_content in secret_volumes.items():
969
+ temp_secret_folder = tempfile.mktemp(dir=os.environ.get("TMPDIR", "/nail/tmp"))
970
+ os.makedirs(temp_secret_folder, exist_ok=True)
971
+ temp_secret_filename = os.path.join(temp_secret_folder, str(uuid.uuid4()))
972
+ # write the secret contents
973
+ # Permissions will automatically be set to readable by "users" group
974
+ # TODO: Make this readable only by "nobody" user? What about other non-standard users that people sometimes use inside the container?
975
+ # -rw-r--r-- 1 dpopes users 3.2K Nov 28 19:16 854bdbad-30b8-4681-ae4e-854cb28075c5
976
+ try:
977
+ # First try to write the file as a string
978
+ # This is for text like config files
979
+ with open(temp_secret_filename, "w") as f:
980
+ f.write(secret_content)
981
+ except TypeError:
982
+ # If that fails, try to write it as bytes
983
+ # This is for binary files like TLS keys
984
+ with open(temp_secret_filename, "wb") as fb:
985
+ fb.write(secret_content)
986
+
987
+ # Append this to the list of volumes passed to docker run
988
+ volumes.append(f"{temp_secret_filename}:{container_mount_path}:ro")
989
+
990
+ docker_run_args = dict(
991
+ memory=memory,
992
+ chosen_port=chosen_port,
993
+ container_port=container_port,
994
+ container_name=container_name,
995
+ volumes=volumes,
996
+ env=environment,
997
+ interactive=interactive,
998
+ detach=simulate_healthcheck,
999
+ docker_hash=docker_url,
1000
+ command=command,
1001
+ net=net,
1002
+ docker_params=docker_params,
1003
+ )
1004
+ docker_run_cmd = get_docker_run_cmd(**docker_run_args)
1005
+ joined_docker_run_cmd = " ".join(docker_run_cmd)
1006
+
1007
+ if dry_run:
1008
+ if json_dict:
1009
+ print(json.dumps(docker_run_args))
1010
+ else:
1011
+ print(json.dumps(docker_run_cmd))
1012
+ return 0
1013
+ else:
1014
+ print("Running docker command:\n%s" % PaastaColors.grey(joined_docker_run_cmd))
1015
+
1016
+ merged_env = {**os.environ, **environment}
1017
+
1018
+ if interactive or not simulate_healthcheck:
1019
+ # NOTE: This immediately replaces us with the docker run cmd. Docker
1020
+ # run knows how to clean up the running container in this situation.
1021
+ wrapper_path = shutil.which("paasta_docker_wrapper")
1022
+ # To properly simulate mesos, we pop the PATH, which is not available to
1023
+ # The executor
1024
+ merged_env.pop("PATH")
1025
+ execlpe(wrapper_path, *docker_run_cmd, merged_env)
1026
+ # For testing, when execlpe is patched out and doesn't replace us, we
1027
+ # still want to bail out.
1028
+ return 0
1029
+
1030
+ container_started = False
1031
+ container_id = None
1032
+ try:
1033
+ (returncode, output) = _run(docker_run_cmd, env=merged_env)
1034
+ if returncode != 0:
1035
+ print(
1036
+ "Failure trying to start your container!"
1037
+ "Returncode: %d"
1038
+ "Output:"
1039
+ "%s"
1040
+ ""
1041
+ "Fix that problem and try again."
1042
+ "http://y/paasta-troubleshooting" % (returncode, output),
1043
+ sep="\n",
1044
+ )
1045
+ # Container failed to start so no need to cleanup; just bail.
1046
+ sys.exit(1)
1047
+ container_started = True
1048
+ container_id = get_container_id(docker_client, container_name)
1049
+ print("Found our container running with CID %s" % container_id)
1050
+
1051
+ if simulate_healthcheck:
1052
+ healthcheck_result = simulate_healthcheck_on_service(
1053
+ instance_config=instance_config,
1054
+ docker_client=docker_client,
1055
+ container_id=container_id,
1056
+ healthcheck_mode=healthcheck_mode,
1057
+ healthcheck_data=healthcheck_data,
1058
+ healthcheck_enabled=healthcheck,
1059
+ )
1060
+
1061
+ def _output_exit_code():
1062
+ returncode = docker_client.inspect_container(container_id)["State"][
1063
+ "ExitCode"
1064
+ ]
1065
+ print(f"Container exited: {returncode})")
1066
+
1067
+ if healthcheck_only:
1068
+ if container_started:
1069
+ _output_exit_code()
1070
+ _cleanup_container(docker_client, container_id)
1071
+ if healthcheck_mode is None:
1072
+ print(
1073
+ "--healthcheck-only, but no healthcheck is defined for this instance!"
1074
+ )
1075
+ sys.exit(1)
1076
+ elif healthcheck_result is True:
1077
+ sys.exit(0)
1078
+ else:
1079
+ sys.exit(1)
1080
+
1081
+ running = docker_client.inspect_container(container_id)["State"]["Running"]
1082
+ if running:
1083
+ print("Your service is now running! Tailing stdout and stderr:")
1084
+ for line in docker_client.logs(
1085
+ container_id,
1086
+ stderr=True,
1087
+ stream=True,
1088
+ ):
1089
+ # writing to sys.stdout.buffer lets us write the raw bytes we
1090
+ # get from the docker client without having to convert them to
1091
+ # a utf-8 string
1092
+ sys.stdout.buffer.write(line)
1093
+ sys.stdout.flush()
1094
+ else:
1095
+ _output_exit_code()
1096
+ returncode = 3
1097
+
1098
+ except KeyboardInterrupt:
1099
+ returncode = 3
1100
+
1101
+ # Cleanup if the container exits on its own or interrupted.
1102
+ if container_started:
1103
+ returncode = docker_client.inspect_container(container_id)["State"]["ExitCode"]
1104
+ _cleanup_container(docker_client, container_id)
1105
+ return returncode
1106
+
1107
+
1108
+ def format_command_for_type(command, instance_type, date):
1109
+ """
1110
+ Given an instance_type, return a function that appropriately formats
1111
+ the command to be run.
1112
+ """
1113
+ if instance_type == "tron":
1114
+ interpolated_command = parse_time_variables(command, date)
1115
+ return interpolated_command
1116
+ else:
1117
+ return command
1118
+
1119
+
1120
+ def configure_and_run_docker_container(
1121
+ docker_client,
1122
+ docker_url,
1123
+ docker_sha,
1124
+ service,
1125
+ instance,
1126
+ cluster,
1127
+ system_paasta_config,
1128
+ args,
1129
+ assume_role_aws_account,
1130
+ pull_image=False,
1131
+ dry_run=False,
1132
+ ):
1133
+ """
1134
+ Run Docker container by image hash with args set in command line.
1135
+ Function prints the output of run command in stdout.
1136
+ """
1137
+
1138
+ if instance is None and args.healthcheck_only:
1139
+ print("With --healthcheck-only, --instance MUST be provided!", file=sys.stderr)
1140
+ return 1
1141
+ if instance is None and not sys.stdin.isatty():
1142
+ print(
1143
+ "--instance and --cluster must be specified when using paasta local-run without a tty!",
1144
+ file=sys.stderr,
1145
+ )
1146
+ return 1
1147
+
1148
+ soa_dir = args.yelpsoa_config_root
1149
+ volumes = args.volumes
1150
+ load_deployments = (docker_url is None or pull_image) and not docker_sha
1151
+ interactive = args.interactive
1152
+
1153
+ try:
1154
+ if instance is None:
1155
+ instance_type = "adhoc"
1156
+ instance = "interactive"
1157
+ instance_config = get_default_interactive_config(
1158
+ service=service,
1159
+ cluster=cluster,
1160
+ soa_dir=soa_dir,
1161
+ load_deployments=load_deployments,
1162
+ )
1163
+ interactive = True
1164
+ else:
1165
+ instance_type = validate_service_instance(
1166
+ service, instance, cluster, soa_dir
1167
+ )
1168
+ instance_config = get_instance_config(
1169
+ service=service,
1170
+ instance=instance,
1171
+ cluster=cluster,
1172
+ load_deployments=load_deployments,
1173
+ soa_dir=soa_dir,
1174
+ )
1175
+ except NoConfigurationForServiceError as e:
1176
+ print(str(e), file=sys.stderr)
1177
+ return 1
1178
+ except NoDeploymentsAvailable:
1179
+ print(
1180
+ PaastaColors.red(
1181
+ "Error: No deployments.json found in %(soa_dir)s/%(service)s. "
1182
+ "You can generate this by running: "
1183
+ "generate_deployments_for_service -d %(soa_dir)s -s %(service)s"
1184
+ % {"soa_dir": soa_dir, "service": service}
1185
+ ),
1186
+ sep="\n",
1187
+ file=sys.stderr,
1188
+ )
1189
+ return 1
1190
+
1191
+ if docker_sha is not None:
1192
+ instance_config.branch_dict = {
1193
+ "git_sha": docker_sha,
1194
+ "docker_image": build_docker_image_name(service=service, sha=docker_sha),
1195
+ "desired_state": "start",
1196
+ "force_bounce": None,
1197
+ }
1198
+
1199
+ if docker_url is None:
1200
+ try:
1201
+ docker_url = instance_config.get_docker_url()
1202
+ except NoDockerImageError:
1203
+ if instance_config.get_deploy_group() is None:
1204
+ print(
1205
+ PaastaColors.red(
1206
+ f"Error: {service}.{instance} has no 'deploy_group' set. Please set one so "
1207
+ "the proper image can be used to run for this service."
1208
+ ),
1209
+ sep="",
1210
+ file=sys.stderr,
1211
+ )
1212
+ else:
1213
+ print(
1214
+ PaastaColors.red(
1215
+ "Error: No sha has been marked for deployment for the %s deploy group.\n"
1216
+ "Please ensure this service has either run through a jenkins pipeline "
1217
+ "or paasta mark-for-deployment has been run for %s\n"
1218
+ % (instance_config.get_deploy_group(), service)
1219
+ ),
1220
+ sep="",
1221
+ file=sys.stderr,
1222
+ )
1223
+ return 1
1224
+
1225
+ if pull_image:
1226
+ docker_pull_image(docker_url)
1227
+
1228
+ for volume in instance_config.get_volumes(
1229
+ system_paasta_config.get_volumes(),
1230
+ ):
1231
+ if os.path.exists(volume["hostPath"]):
1232
+ volumes.append(
1233
+ "{}:{}:{}".format(
1234
+ volume["hostPath"], volume["containerPath"], volume["mode"].lower()
1235
+ )
1236
+ )
1237
+ else:
1238
+ print(
1239
+ PaastaColors.yellow(
1240
+ "Warning: Path %s does not exist on this host. Skipping this binding."
1241
+ % volume["hostPath"]
1242
+ ),
1243
+ file=sys.stderr,
1244
+ )
1245
+
1246
+ if interactive is True and args.cmd is None:
1247
+ command = "bash"
1248
+ elif args.cmd:
1249
+ command = args.cmd
1250
+ else:
1251
+ command_from_config = instance_config.get_cmd()
1252
+ if command_from_config:
1253
+ command = format_command_for_type(
1254
+ command=command_from_config, instance_type=instance_type, date=args.date
1255
+ )
1256
+ else:
1257
+ command = instance_config.get_args()
1258
+
1259
+ secret_provider_kwargs = {
1260
+ "vault_cluster_config": system_paasta_config.get_vault_cluster_config(),
1261
+ "vault_auth_method": args.vault_auth_method,
1262
+ "vault_token_file": args.vault_token_file,
1263
+ }
1264
+
1265
+ return run_docker_container(
1266
+ docker_client=docker_client,
1267
+ service=service,
1268
+ instance=instance,
1269
+ docker_url=docker_url,
1270
+ volumes=volumes,
1271
+ interactive=interactive,
1272
+ command=command,
1273
+ healthcheck=args.healthcheck,
1274
+ healthcheck_only=args.healthcheck_only,
1275
+ user_port=args.user_port,
1276
+ instance_config=instance_config,
1277
+ soa_dir=args.yelpsoa_config_root,
1278
+ dry_run=dry_run,
1279
+ json_dict=args.dry_run_json_dict,
1280
+ framework=instance_type,
1281
+ secret_provider_name=system_paasta_config.get_secret_provider_name(),
1282
+ secret_provider_kwargs=secret_provider_kwargs,
1283
+ skip_secrets=args.skip_secrets,
1284
+ assume_pod_identity=args.assume_pod_identity,
1285
+ assume_role_arn=args.assume_role_arn,
1286
+ assume_role_aws_account=assume_role_aws_account,
1287
+ use_okta_role=args.use_okta_role,
1288
+ use_service_auth_token=args.use_service_auth_token,
1289
+ use_sso_service_auth_token=args.use_sso_service_auth_token,
1290
+ )
1291
+
1292
+
1293
+ def docker_config_available():
1294
+ home = os.path.expanduser("~")
1295
+ oldconfig = os.path.join(home, ".dockercfg")
1296
+ newconfig = os.path.join(home, ".docker", "config.json")
1297
+ return (os.path.isfile(oldconfig) and os.access(oldconfig, os.R_OK)) or (
1298
+ os.path.isfile(newconfig) and os.access(newconfig, os.R_OK)
1299
+ )
1300
+
1301
+
1302
+ def paasta_local_run(args):
1303
+ if args.action == "pull" and os.geteuid() != 0 and not docker_config_available():
1304
+ print("Re-executing paasta local-run --pull with sudo..")
1305
+ os.execvp("sudo", ["sudo", "-H"] + sys.argv)
1306
+ if args.action == "build" and not makefile_responds_to("cook-image"):
1307
+ print(
1308
+ "A local Makefile with a 'cook-image' target is required for --build",
1309
+ file=sys.stderr,
1310
+ )
1311
+ print(
1312
+ "If you meant to pull the docker image from the registry, explicitly pass --pull",
1313
+ file=sys.stderr,
1314
+ )
1315
+ return 1
1316
+
1317
+ try:
1318
+ system_paasta_config = load_system_paasta_config()
1319
+ except PaastaNotConfiguredError:
1320
+ print(
1321
+ PaastaColors.yellow(
1322
+ "Warning: Couldn't load config files from '/etc/paasta'. This indicates"
1323
+ "PaaSTA is not configured locally on this host, and local-run may not behave"
1324
+ "the same way it would behave on a server configured for PaaSTA."
1325
+ ),
1326
+ sep="\n",
1327
+ )
1328
+ system_paasta_config = SystemPaastaConfig({"volumes": []}, "/etc/paasta")
1329
+
1330
+ local_run_config = system_paasta_config.get_local_run_config()
1331
+
1332
+ service = figure_out_service_name(args, soa_dir=args.yelpsoa_config_root)
1333
+
1334
+ if args.cluster:
1335
+ cluster = args.cluster
1336
+ else:
1337
+ try:
1338
+ cluster = local_run_config["default_cluster"]
1339
+ except KeyError:
1340
+ print(
1341
+ PaastaColors.red(
1342
+ "PaaSTA on this machine has not been configured with a default cluster."
1343
+ "Please pass one to local-run using '-c'."
1344
+ ),
1345
+ sep="\n",
1346
+ file=sys.stderr,
1347
+ )
1348
+ return 1
1349
+ assume_role_aws_account = args.assume_role_aws_account or (
1350
+ system_paasta_config.get_kube_clusters()
1351
+ .get(cluster, {})
1352
+ .get("aws_account", resolve_aws_account_from_runtimeenv())
1353
+ )
1354
+
1355
+ instance = args.instance
1356
+ docker_client = get_docker_client()
1357
+
1358
+ docker_sha = None
1359
+ docker_url = None
1360
+
1361
+ if args.action == "build":
1362
+ default_tag = "paasta-local-run-{}-{}".format(service, get_username())
1363
+ docker_url = os.environ.get("DOCKER_TAG", default_tag)
1364
+ os.environ["DOCKER_TAG"] = docker_url
1365
+ pull_image = False
1366
+ cook_return = paasta_cook_image(
1367
+ args=None, service=service, soa_dir=args.yelpsoa_config_root
1368
+ )
1369
+ if cook_return != 0:
1370
+ return cook_return
1371
+ elif args.action == "dry_run":
1372
+ pull_image = False
1373
+ docker_url = None
1374
+ docker_sha = args.sha
1375
+ else:
1376
+ pull_image = True
1377
+ docker_url = None
1378
+ docker_sha = args.sha
1379
+
1380
+ try:
1381
+ return configure_and_run_docker_container(
1382
+ docker_client=docker_client,
1383
+ docker_url=docker_url,
1384
+ docker_sha=docker_sha,
1385
+ service=service,
1386
+ instance=instance,
1387
+ cluster=cluster,
1388
+ args=args,
1389
+ pull_image=pull_image,
1390
+ system_paasta_config=system_paasta_config,
1391
+ dry_run=args.action == "dry_run",
1392
+ assume_role_aws_account=assume_role_aws_account,
1393
+ )
1394
+ except errors.APIError as e:
1395
+ print("Can't run Docker container. Error: %s" % str(e), file=sys.stderr)
1396
+ return 1