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.
- k8s_itests/__init__.py +0 -0
- k8s_itests/test_autoscaling.py +23 -0
- k8s_itests/utils.py +38 -0
- paasta_tools/__init__.py +20 -0
- paasta_tools/adhoc_tools.py +142 -0
- paasta_tools/api/__init__.py +13 -0
- paasta_tools/api/api.py +330 -0
- paasta_tools/api/api_docs/swagger.json +2323 -0
- paasta_tools/api/client.py +106 -0
- paasta_tools/api/settings.py +33 -0
- paasta_tools/api/tweens/__init__.py +6 -0
- paasta_tools/api/tweens/auth.py +125 -0
- paasta_tools/api/tweens/profiling.py +108 -0
- paasta_tools/api/tweens/request_logger.py +124 -0
- paasta_tools/api/views/__init__.py +13 -0
- paasta_tools/api/views/autoscaler.py +100 -0
- paasta_tools/api/views/exception.py +45 -0
- paasta_tools/api/views/flink.py +73 -0
- paasta_tools/api/views/instance.py +395 -0
- paasta_tools/api/views/pause_autoscaler.py +71 -0
- paasta_tools/api/views/remote_run.py +113 -0
- paasta_tools/api/views/resources.py +76 -0
- paasta_tools/api/views/service.py +35 -0
- paasta_tools/api/views/version.py +25 -0
- paasta_tools/apply_external_resources.py +79 -0
- paasta_tools/async_utils.py +109 -0
- paasta_tools/autoscaling/__init__.py +0 -0
- paasta_tools/autoscaling/autoscaling_service_lib.py +57 -0
- paasta_tools/autoscaling/forecasting.py +106 -0
- paasta_tools/autoscaling/max_all_k8s_services.py +41 -0
- paasta_tools/autoscaling/pause_service_autoscaler.py +77 -0
- paasta_tools/autoscaling/utils.py +52 -0
- paasta_tools/bounce_lib.py +184 -0
- paasta_tools/broadcast_log_to_services.py +62 -0
- paasta_tools/cassandracluster_tools.py +210 -0
- paasta_tools/check_autoscaler_max_instances.py +212 -0
- paasta_tools/check_cassandracluster_services_replication.py +35 -0
- paasta_tools/check_flink_services_health.py +203 -0
- paasta_tools/check_kubernetes_api.py +57 -0
- paasta_tools/check_kubernetes_services_replication.py +141 -0
- paasta_tools/check_oom_events.py +244 -0
- paasta_tools/check_services_replication_tools.py +324 -0
- paasta_tools/check_spark_jobs.py +234 -0
- paasta_tools/cleanup_kubernetes_cr.py +138 -0
- paasta_tools/cleanup_kubernetes_crd.py +145 -0
- paasta_tools/cleanup_kubernetes_jobs.py +344 -0
- paasta_tools/cleanup_tron_namespaces.py +96 -0
- paasta_tools/cli/__init__.py +13 -0
- paasta_tools/cli/authentication.py +85 -0
- paasta_tools/cli/cli.py +260 -0
- paasta_tools/cli/cmds/__init__.py +13 -0
- paasta_tools/cli/cmds/autoscale.py +143 -0
- paasta_tools/cli/cmds/check.py +334 -0
- paasta_tools/cli/cmds/cook_image.py +147 -0
- paasta_tools/cli/cmds/get_docker_image.py +76 -0
- paasta_tools/cli/cmds/get_image_version.py +172 -0
- paasta_tools/cli/cmds/get_latest_deployment.py +93 -0
- paasta_tools/cli/cmds/info.py +155 -0
- paasta_tools/cli/cmds/itest.py +117 -0
- paasta_tools/cli/cmds/list.py +66 -0
- paasta_tools/cli/cmds/list_clusters.py +42 -0
- paasta_tools/cli/cmds/list_deploy_queue.py +171 -0
- paasta_tools/cli/cmds/list_namespaces.py +84 -0
- paasta_tools/cli/cmds/local_run.py +1396 -0
- paasta_tools/cli/cmds/logs.py +1601 -0
- paasta_tools/cli/cmds/mark_for_deployment.py +1988 -0
- paasta_tools/cli/cmds/mesh_status.py +174 -0
- paasta_tools/cli/cmds/pause_service_autoscaler.py +107 -0
- paasta_tools/cli/cmds/push_to_registry.py +275 -0
- paasta_tools/cli/cmds/remote_run.py +252 -0
- paasta_tools/cli/cmds/rollback.py +347 -0
- paasta_tools/cli/cmds/secret.py +549 -0
- paasta_tools/cli/cmds/security_check.py +59 -0
- paasta_tools/cli/cmds/spark_run.py +1400 -0
- paasta_tools/cli/cmds/start_stop_restart.py +401 -0
- paasta_tools/cli/cmds/status.py +2302 -0
- paasta_tools/cli/cmds/validate.py +1012 -0
- paasta_tools/cli/cmds/wait_for_deployment.py +275 -0
- paasta_tools/cli/fsm/__init__.py +13 -0
- paasta_tools/cli/fsm/autosuggest.py +82 -0
- paasta_tools/cli/fsm/template/README.md +8 -0
- paasta_tools/cli/fsm/template/cookiecutter.json +7 -0
- paasta_tools/cli/fsm/template/{{cookiecutter.service}}/kubernetes-PROD.yaml +91 -0
- paasta_tools/cli/fsm/template/{{cookiecutter.service}}/monitoring.yaml +20 -0
- paasta_tools/cli/fsm/template/{{cookiecutter.service}}/service.yaml +8 -0
- paasta_tools/cli/fsm/template/{{cookiecutter.service}}/smartstack.yaml +6 -0
- paasta_tools/cli/fsm_cmd.py +121 -0
- paasta_tools/cli/paasta_tabcomplete.sh +23 -0
- paasta_tools/cli/schemas/adhoc_schema.json +199 -0
- paasta_tools/cli/schemas/autoscaling_schema.json +91 -0
- paasta_tools/cli/schemas/autotuned_defaults/cassandracluster_schema.json +37 -0
- paasta_tools/cli/schemas/autotuned_defaults/kubernetes_schema.json +89 -0
- paasta_tools/cli/schemas/deploy_schema.json +173 -0
- paasta_tools/cli/schemas/eks_schema.json +970 -0
- paasta_tools/cli/schemas/kubernetes_schema.json +970 -0
- paasta_tools/cli/schemas/rollback_schema.json +160 -0
- paasta_tools/cli/schemas/service_schema.json +25 -0
- paasta_tools/cli/schemas/smartstack_schema.json +322 -0
- paasta_tools/cli/schemas/tron_schema.json +699 -0
- paasta_tools/cli/utils.py +1118 -0
- paasta_tools/clusterman.py +21 -0
- paasta_tools/config_utils.py +385 -0
- paasta_tools/contrib/__init__.py +0 -0
- paasta_tools/contrib/bounce_log_latency_parser.py +68 -0
- paasta_tools/contrib/check_manual_oapi_changes.sh +24 -0
- paasta_tools/contrib/check_orphans.py +306 -0
- paasta_tools/contrib/create_dynamodb_table.py +35 -0
- paasta_tools/contrib/create_paasta_playground.py +105 -0
- paasta_tools/contrib/emit_allocated_cpu_metrics.py +50 -0
- paasta_tools/contrib/get_running_task_allocation.py +346 -0
- paasta_tools/contrib/habitat_fixer.py +86 -0
- paasta_tools/contrib/ide_helper.py +316 -0
- paasta_tools/contrib/is_pod_healthy_in_proxy.py +139 -0
- paasta_tools/contrib/is_pod_healthy_in_smartstack.py +50 -0
- paasta_tools/contrib/kill_bad_containers.py +109 -0
- paasta_tools/contrib/mass-deploy-tag.sh +44 -0
- paasta_tools/contrib/mock_patch_checker.py +86 -0
- paasta_tools/contrib/paasta_update_soa_memcpu.py +520 -0
- paasta_tools/contrib/render_template.py +129 -0
- paasta_tools/contrib/rightsizer_soaconfigs_update.py +348 -0
- paasta_tools/contrib/service_shard_remove.py +157 -0
- paasta_tools/contrib/service_shard_update.py +373 -0
- paasta_tools/contrib/shared_ip_check.py +77 -0
- paasta_tools/contrib/timeouts_metrics_prom.py +64 -0
- paasta_tools/delete_kubernetes_deployments.py +89 -0
- paasta_tools/deployment_utils.py +44 -0
- paasta_tools/docker_wrapper.py +234 -0
- paasta_tools/docker_wrapper_imports.py +13 -0
- paasta_tools/drain_lib.py +351 -0
- paasta_tools/dump_locally_running_services.py +71 -0
- paasta_tools/eks_tools.py +119 -0
- paasta_tools/envoy_tools.py +373 -0
- paasta_tools/firewall.py +504 -0
- paasta_tools/firewall_logging.py +154 -0
- paasta_tools/firewall_update.py +172 -0
- paasta_tools/flink_tools.py +345 -0
- paasta_tools/flinkeks_tools.py +90 -0
- paasta_tools/frameworks/__init__.py +0 -0
- paasta_tools/frameworks/adhoc_scheduler.py +71 -0
- paasta_tools/frameworks/constraints.py +87 -0
- paasta_tools/frameworks/native_scheduler.py +652 -0
- paasta_tools/frameworks/native_service_config.py +301 -0
- paasta_tools/frameworks/task_store.py +245 -0
- paasta_tools/generate_all_deployments +9 -0
- paasta_tools/generate_authenticating_services.py +94 -0
- paasta_tools/generate_deployments_for_service.py +255 -0
- paasta_tools/generate_services_file.py +114 -0
- paasta_tools/generate_services_yaml.py +30 -0
- paasta_tools/hacheck.py +76 -0
- paasta_tools/instance/__init__.py +0 -0
- paasta_tools/instance/hpa_metrics_parser.py +122 -0
- paasta_tools/instance/kubernetes.py +1362 -0
- paasta_tools/iptables.py +240 -0
- paasta_tools/kafkacluster_tools.py +143 -0
- paasta_tools/kubernetes/__init__.py +0 -0
- paasta_tools/kubernetes/application/__init__.py +0 -0
- paasta_tools/kubernetes/application/controller_wrappers.py +476 -0
- paasta_tools/kubernetes/application/tools.py +90 -0
- paasta_tools/kubernetes/bin/__init__.py +0 -0
- paasta_tools/kubernetes/bin/kubernetes_remove_evicted_pods.py +164 -0
- paasta_tools/kubernetes/bin/paasta_cleanup_remote_run_resources.py +135 -0
- paasta_tools/kubernetes/bin/paasta_cleanup_stale_nodes.py +181 -0
- paasta_tools/kubernetes/bin/paasta_secrets_sync.py +758 -0
- paasta_tools/kubernetes/remote_run.py +558 -0
- paasta_tools/kubernetes_tools.py +4679 -0
- paasta_tools/list_kubernetes_service_instances.py +128 -0
- paasta_tools/list_tron_namespaces.py +60 -0
- paasta_tools/long_running_service_tools.py +678 -0
- paasta_tools/mac_address.py +44 -0
- paasta_tools/marathon_dashboard.py +0 -0
- paasta_tools/mesos/__init__.py +0 -0
- paasta_tools/mesos/cfg.py +46 -0
- paasta_tools/mesos/cluster.py +60 -0
- paasta_tools/mesos/exceptions.py +59 -0
- paasta_tools/mesos/framework.py +77 -0
- paasta_tools/mesos/log.py +48 -0
- paasta_tools/mesos/master.py +306 -0
- paasta_tools/mesos/mesos_file.py +169 -0
- paasta_tools/mesos/parallel.py +52 -0
- paasta_tools/mesos/slave.py +115 -0
- paasta_tools/mesos/task.py +94 -0
- paasta_tools/mesos/util.py +69 -0
- paasta_tools/mesos/zookeeper.py +37 -0
- paasta_tools/mesos_maintenance.py +848 -0
- paasta_tools/mesos_tools.py +1051 -0
- paasta_tools/metrics/__init__.py +0 -0
- paasta_tools/metrics/metastatus_lib.py +1110 -0
- paasta_tools/metrics/metrics_lib.py +217 -0
- paasta_tools/monitoring/__init__.py +13 -0
- paasta_tools/monitoring/check_k8s_api_performance.py +110 -0
- paasta_tools/monitoring_tools.py +652 -0
- paasta_tools/monkrelaycluster_tools.py +146 -0
- paasta_tools/nrtsearchservice_tools.py +143 -0
- paasta_tools/nrtsearchserviceeks_tools.py +68 -0
- paasta_tools/oom_logger.py +321 -0
- paasta_tools/paasta_deploy_tron_jobs +3 -0
- paasta_tools/paasta_execute_docker_command.py +123 -0
- paasta_tools/paasta_native_serviceinit.py +21 -0
- paasta_tools/paasta_service_config_loader.py +201 -0
- paasta_tools/paastaapi/__init__.py +29 -0
- paasta_tools/paastaapi/api/__init__.py +3 -0
- paasta_tools/paastaapi/api/autoscaler_api.py +302 -0
- paasta_tools/paastaapi/api/default_api.py +569 -0
- paasta_tools/paastaapi/api/remote_run_api.py +604 -0
- paasta_tools/paastaapi/api/resources_api.py +157 -0
- paasta_tools/paastaapi/api/service_api.py +1736 -0
- paasta_tools/paastaapi/api_client.py +818 -0
- paasta_tools/paastaapi/apis/__init__.py +22 -0
- paasta_tools/paastaapi/configuration.py +455 -0
- paasta_tools/paastaapi/exceptions.py +137 -0
- paasta_tools/paastaapi/model/__init__.py +5 -0
- paasta_tools/paastaapi/model/adhoc_launch_history.py +176 -0
- paasta_tools/paastaapi/model/autoscaler_count_msg.py +176 -0
- paasta_tools/paastaapi/model/deploy_queue.py +178 -0
- paasta_tools/paastaapi/model/deploy_queue_service_instance.py +194 -0
- paasta_tools/paastaapi/model/envoy_backend.py +185 -0
- paasta_tools/paastaapi/model/envoy_location.py +184 -0
- paasta_tools/paastaapi/model/envoy_status.py +181 -0
- paasta_tools/paastaapi/model/flink_cluster_overview.py +188 -0
- paasta_tools/paastaapi/model/flink_config.py +173 -0
- paasta_tools/paastaapi/model/flink_job.py +186 -0
- paasta_tools/paastaapi/model/flink_job_details.py +192 -0
- paasta_tools/paastaapi/model/flink_jobs.py +175 -0
- paasta_tools/paastaapi/model/float_and_error.py +173 -0
- paasta_tools/paastaapi/model/hpa_metric.py +176 -0
- paasta_tools/paastaapi/model/inline_object.py +170 -0
- paasta_tools/paastaapi/model/inline_response200.py +170 -0
- paasta_tools/paastaapi/model/inline_response2001.py +170 -0
- paasta_tools/paastaapi/model/instance_bounce_status.py +200 -0
- paasta_tools/paastaapi/model/instance_mesh_status.py +186 -0
- paasta_tools/paastaapi/model/instance_status.py +220 -0
- paasta_tools/paastaapi/model/instance_status_adhoc.py +187 -0
- paasta_tools/paastaapi/model/instance_status_cassandracluster.py +173 -0
- paasta_tools/paastaapi/model/instance_status_flink.py +173 -0
- paasta_tools/paastaapi/model/instance_status_kafkacluster.py +173 -0
- paasta_tools/paastaapi/model/instance_status_kubernetes.py +263 -0
- paasta_tools/paastaapi/model/instance_status_kubernetes_autoscaling_status.py +187 -0
- paasta_tools/paastaapi/model/instance_status_kubernetes_v2.py +197 -0
- paasta_tools/paastaapi/model/instance_status_tron.py +204 -0
- paasta_tools/paastaapi/model/instance_tasks.py +182 -0
- paasta_tools/paastaapi/model/integer_and_error.py +173 -0
- paasta_tools/paastaapi/model/kubernetes_container.py +178 -0
- paasta_tools/paastaapi/model/kubernetes_container_v2.py +219 -0
- paasta_tools/paastaapi/model/kubernetes_healthcheck.py +176 -0
- paasta_tools/paastaapi/model/kubernetes_pod.py +201 -0
- paasta_tools/paastaapi/model/kubernetes_pod_event.py +176 -0
- paasta_tools/paastaapi/model/kubernetes_pod_v2.py +213 -0
- paasta_tools/paastaapi/model/kubernetes_replica_set.py +185 -0
- paasta_tools/paastaapi/model/kubernetes_version.py +202 -0
- paasta_tools/paastaapi/model/remote_run_outcome.py +189 -0
- paasta_tools/paastaapi/model/remote_run_start.py +185 -0
- paasta_tools/paastaapi/model/remote_run_stop.py +176 -0
- paasta_tools/paastaapi/model/remote_run_token.py +173 -0
- paasta_tools/paastaapi/model/resource.py +187 -0
- paasta_tools/paastaapi/model/resource_item.py +187 -0
- paasta_tools/paastaapi/model/resource_value.py +176 -0
- paasta_tools/paastaapi/model/smartstack_backend.py +191 -0
- paasta_tools/paastaapi/model/smartstack_location.py +181 -0
- paasta_tools/paastaapi/model/smartstack_status.py +181 -0
- paasta_tools/paastaapi/model/task_tail_lines.py +176 -0
- paasta_tools/paastaapi/model_utils.py +1879 -0
- paasta_tools/paastaapi/models/__init__.py +62 -0
- paasta_tools/paastaapi/rest.py +287 -0
- paasta_tools/prune_completed_pods.py +220 -0
- paasta_tools/puppet_service_tools.py +59 -0
- paasta_tools/py.typed +1 -0
- paasta_tools/remote_git.py +127 -0
- paasta_tools/run-paasta-api-in-dev-mode.py +57 -0
- paasta_tools/run-paasta-api-playground.py +51 -0
- paasta_tools/secret_providers/__init__.py +66 -0
- paasta_tools/secret_providers/vault.py +214 -0
- paasta_tools/secret_tools.py +277 -0
- paasta_tools/setup_istio_mesh.py +353 -0
- paasta_tools/setup_kubernetes_cr.py +412 -0
- paasta_tools/setup_kubernetes_crd.py +138 -0
- paasta_tools/setup_kubernetes_internal_crd.py +154 -0
- paasta_tools/setup_kubernetes_job.py +353 -0
- paasta_tools/setup_prometheus_adapter_config.py +1028 -0
- paasta_tools/setup_tron_namespace.py +248 -0
- paasta_tools/slack.py +75 -0
- paasta_tools/smartstack_tools.py +676 -0
- paasta_tools/spark_tools.py +283 -0
- paasta_tools/synapse_srv_namespaces_fact.py +42 -0
- paasta_tools/tron/__init__.py +0 -0
- paasta_tools/tron/client.py +158 -0
- paasta_tools/tron/tron_command_context.py +194 -0
- paasta_tools/tron/tron_timeutils.py +101 -0
- paasta_tools/tron_tools.py +1448 -0
- paasta_tools/utils.py +4307 -0
- paasta_tools/yaml_tools.py +44 -0
- paasta_tools-1.21.3.data/scripts/apply_external_resources.py +79 -0
- paasta_tools-1.21.3.data/scripts/bounce_log_latency_parser.py +68 -0
- paasta_tools-1.21.3.data/scripts/check_autoscaler_max_instances.py +212 -0
- paasta_tools-1.21.3.data/scripts/check_cassandracluster_services_replication.py +35 -0
- paasta_tools-1.21.3.data/scripts/check_flink_services_health.py +203 -0
- paasta_tools-1.21.3.data/scripts/check_kubernetes_api.py +57 -0
- paasta_tools-1.21.3.data/scripts/check_kubernetes_services_replication.py +141 -0
- paasta_tools-1.21.3.data/scripts/check_manual_oapi_changes.sh +24 -0
- paasta_tools-1.21.3.data/scripts/check_oom_events.py +244 -0
- paasta_tools-1.21.3.data/scripts/check_orphans.py +306 -0
- paasta_tools-1.21.3.data/scripts/check_spark_jobs.py +234 -0
- paasta_tools-1.21.3.data/scripts/cleanup_kubernetes_cr.py +138 -0
- paasta_tools-1.21.3.data/scripts/cleanup_kubernetes_crd.py +145 -0
- paasta_tools-1.21.3.data/scripts/cleanup_kubernetes_jobs.py +344 -0
- paasta_tools-1.21.3.data/scripts/create_dynamodb_table.py +35 -0
- paasta_tools-1.21.3.data/scripts/create_paasta_playground.py +105 -0
- paasta_tools-1.21.3.data/scripts/delete_kubernetes_deployments.py +89 -0
- paasta_tools-1.21.3.data/scripts/emit_allocated_cpu_metrics.py +50 -0
- paasta_tools-1.21.3.data/scripts/generate_all_deployments +9 -0
- paasta_tools-1.21.3.data/scripts/generate_authenticating_services.py +94 -0
- paasta_tools-1.21.3.data/scripts/generate_deployments_for_service.py +255 -0
- paasta_tools-1.21.3.data/scripts/generate_services_file.py +114 -0
- paasta_tools-1.21.3.data/scripts/generate_services_yaml.py +30 -0
- paasta_tools-1.21.3.data/scripts/get_running_task_allocation.py +346 -0
- paasta_tools-1.21.3.data/scripts/habitat_fixer.py +86 -0
- paasta_tools-1.21.3.data/scripts/ide_helper.py +316 -0
- paasta_tools-1.21.3.data/scripts/is_pod_healthy_in_proxy.py +139 -0
- paasta_tools-1.21.3.data/scripts/is_pod_healthy_in_smartstack.py +50 -0
- paasta_tools-1.21.3.data/scripts/kill_bad_containers.py +109 -0
- paasta_tools-1.21.3.data/scripts/kubernetes_remove_evicted_pods.py +164 -0
- paasta_tools-1.21.3.data/scripts/mass-deploy-tag.sh +44 -0
- paasta_tools-1.21.3.data/scripts/mock_patch_checker.py +86 -0
- paasta_tools-1.21.3.data/scripts/paasta_cleanup_remote_run_resources.py +135 -0
- paasta_tools-1.21.3.data/scripts/paasta_cleanup_stale_nodes.py +181 -0
- paasta_tools-1.21.3.data/scripts/paasta_deploy_tron_jobs +3 -0
- paasta_tools-1.21.3.data/scripts/paasta_execute_docker_command.py +123 -0
- paasta_tools-1.21.3.data/scripts/paasta_secrets_sync.py +758 -0
- paasta_tools-1.21.3.data/scripts/paasta_tabcomplete.sh +23 -0
- paasta_tools-1.21.3.data/scripts/paasta_update_soa_memcpu.py +520 -0
- paasta_tools-1.21.3.data/scripts/render_template.py +129 -0
- paasta_tools-1.21.3.data/scripts/rightsizer_soaconfigs_update.py +348 -0
- paasta_tools-1.21.3.data/scripts/service_shard_remove.py +157 -0
- paasta_tools-1.21.3.data/scripts/service_shard_update.py +373 -0
- paasta_tools-1.21.3.data/scripts/setup_istio_mesh.py +353 -0
- paasta_tools-1.21.3.data/scripts/setup_kubernetes_cr.py +412 -0
- paasta_tools-1.21.3.data/scripts/setup_kubernetes_crd.py +138 -0
- paasta_tools-1.21.3.data/scripts/setup_kubernetes_internal_crd.py +154 -0
- paasta_tools-1.21.3.data/scripts/setup_kubernetes_job.py +353 -0
- paasta_tools-1.21.3.data/scripts/setup_prometheus_adapter_config.py +1028 -0
- paasta_tools-1.21.3.data/scripts/shared_ip_check.py +77 -0
- paasta_tools-1.21.3.data/scripts/synapse_srv_namespaces_fact.py +42 -0
- paasta_tools-1.21.3.data/scripts/timeouts_metrics_prom.py +64 -0
- paasta_tools-1.21.3.dist-info/LICENSE +201 -0
- paasta_tools-1.21.3.dist-info/METADATA +74 -0
- paasta_tools-1.21.3.dist-info/RECORD +348 -0
- paasta_tools-1.21.3.dist-info/WHEEL +5 -0
- paasta_tools-1.21.3.dist-info/entry_points.txt +20 -0
- paasta_tools-1.21.3.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,1118 @@
|
|
|
1
|
+
# Copyright 2015-2016 Yelp Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
import argparse
|
|
15
|
+
import difflib
|
|
16
|
+
import fnmatch
|
|
17
|
+
import getpass
|
|
18
|
+
import hashlib
|
|
19
|
+
import logging
|
|
20
|
+
import os
|
|
21
|
+
import pty
|
|
22
|
+
import random
|
|
23
|
+
import re
|
|
24
|
+
import shutil
|
|
25
|
+
import socket
|
|
26
|
+
import subprocess
|
|
27
|
+
from collections import defaultdict
|
|
28
|
+
from shlex import quote
|
|
29
|
+
from typing import Callable
|
|
30
|
+
from typing import Collection
|
|
31
|
+
from typing import Generator
|
|
32
|
+
from typing import Iterable
|
|
33
|
+
from typing import List
|
|
34
|
+
from typing import Mapping
|
|
35
|
+
from typing import NamedTuple
|
|
36
|
+
from typing import Optional
|
|
37
|
+
from typing import Sequence
|
|
38
|
+
from typing import Set
|
|
39
|
+
from typing import Tuple
|
|
40
|
+
|
|
41
|
+
import ephemeral_port_reserve
|
|
42
|
+
from mypy_extensions import NamedArg
|
|
43
|
+
|
|
44
|
+
from paasta_tools import remote_git
|
|
45
|
+
from paasta_tools.adhoc_tools import load_adhoc_job_config
|
|
46
|
+
from paasta_tools.api.client import get_paasta_oapi_client
|
|
47
|
+
from paasta_tools.api.client import PaastaOApiClient
|
|
48
|
+
from paasta_tools.cassandracluster_tools import load_cassandracluster_instance_config
|
|
49
|
+
from paasta_tools.cli.authentication import get_sso_auth_token
|
|
50
|
+
from paasta_tools.eks_tools import EksDeploymentConfig
|
|
51
|
+
from paasta_tools.eks_tools import load_eks_service_config
|
|
52
|
+
from paasta_tools.flink_tools import load_flink_instance_config
|
|
53
|
+
from paasta_tools.flinkeks_tools import load_flinkeks_instance_config
|
|
54
|
+
from paasta_tools.kafkacluster_tools import load_kafkacluster_instance_config
|
|
55
|
+
from paasta_tools.kubernetes_tools import KubernetesDeploymentConfig
|
|
56
|
+
from paasta_tools.kubernetes_tools import load_kubernetes_service_config
|
|
57
|
+
from paasta_tools.long_running_service_tools import LongRunningServiceConfig
|
|
58
|
+
from paasta_tools.monkrelaycluster_tools import load_monkrelaycluster_instance_config
|
|
59
|
+
from paasta_tools.nrtsearchservice_tools import load_nrtsearchservice_instance_config
|
|
60
|
+
from paasta_tools.nrtsearchserviceeks_tools import (
|
|
61
|
+
load_nrtsearchserviceeks_instance_config,
|
|
62
|
+
)
|
|
63
|
+
from paasta_tools.paasta_service_config_loader import PaastaServiceConfigLoader
|
|
64
|
+
from paasta_tools.tron_tools import load_tron_instance_config
|
|
65
|
+
from paasta_tools.utils import _log
|
|
66
|
+
from paasta_tools.utils import _run
|
|
67
|
+
from paasta_tools.utils import compose_job_id
|
|
68
|
+
from paasta_tools.utils import DEFAULT_SOA_CONFIGS_GIT_URL
|
|
69
|
+
from paasta_tools.utils import DEFAULT_SOA_DIR
|
|
70
|
+
from paasta_tools.utils import get_service_instance_list
|
|
71
|
+
from paasta_tools.utils import INSTANCE_TYPE_TO_K8S_NAMESPACE
|
|
72
|
+
from paasta_tools.utils import INSTANCE_TYPES
|
|
73
|
+
from paasta_tools.utils import InstanceConfig
|
|
74
|
+
from paasta_tools.utils import list_all_instances_for_service
|
|
75
|
+
from paasta_tools.utils import list_clusters
|
|
76
|
+
from paasta_tools.utils import list_services
|
|
77
|
+
from paasta_tools.utils import load_system_paasta_config
|
|
78
|
+
from paasta_tools.utils import PAASTA_K8S_INSTANCE_TYPES
|
|
79
|
+
from paasta_tools.utils import PaastaColors
|
|
80
|
+
from paasta_tools.utils import SystemPaastaConfig
|
|
81
|
+
from paasta_tools.utils import validate_service_instance
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
log = logging.getLogger(__name__)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def is_file_in_dir(file_name, path):
|
|
88
|
+
"""Recursively search path for file_name.
|
|
89
|
+
|
|
90
|
+
:param file_name: a string of a file name to find
|
|
91
|
+
:param path: a string path
|
|
92
|
+
:param file_ext: a string of a file extension
|
|
93
|
+
:return: a boolean
|
|
94
|
+
"""
|
|
95
|
+
for root, dirnames, filenames in os.walk(path):
|
|
96
|
+
for filename in filenames:
|
|
97
|
+
if fnmatch.fnmatch(filename, file_name):
|
|
98
|
+
return os.path.join(root, filename)
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def get_file_contents(path):
|
|
103
|
+
"""Open a file for reading
|
|
104
|
+
|
|
105
|
+
:param path: path of file to read
|
|
106
|
+
"""
|
|
107
|
+
with open(path) as p:
|
|
108
|
+
return p.read()
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def check_mark():
|
|
112
|
+
"""
|
|
113
|
+
:return: string that can print a checkmark
|
|
114
|
+
"""
|
|
115
|
+
return PaastaColors.green("\u2713")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def x_mark():
|
|
119
|
+
"""
|
|
120
|
+
:return: string that can print an x-mark
|
|
121
|
+
"""
|
|
122
|
+
return PaastaColors.red("\u2717")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def info_mark() -> str:
|
|
126
|
+
"""
|
|
127
|
+
:return: string that can print an info symbol
|
|
128
|
+
"""
|
|
129
|
+
return PaastaColors.blue("\u2139")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def success(msg):
|
|
133
|
+
"""Format a paasta check success message.
|
|
134
|
+
|
|
135
|
+
:param msg: a string
|
|
136
|
+
:return: a beautiful string
|
|
137
|
+
"""
|
|
138
|
+
return "{} {}".format(check_mark(), msg)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def failure(msg, link):
|
|
142
|
+
"""Format a paasta check failure message.
|
|
143
|
+
|
|
144
|
+
:param msg: a string
|
|
145
|
+
:return: a beautiful string
|
|
146
|
+
"""
|
|
147
|
+
return "{} {} {}".format(x_mark(), msg, PaastaColors.blue(link))
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def info_message(msg: str) -> str:
|
|
151
|
+
return f"{info_mark()} {msg}"
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class PaastaCheckMessages:
|
|
155
|
+
|
|
156
|
+
"""Collection of message printed out by 'paasta check'.
|
|
157
|
+
Helpful as it avoids cumbersome maintenance of the unit tests.
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
DEPLOY_YAML_FOUND = success("deploy.yaml exists for a Jenkins pipeline")
|
|
161
|
+
|
|
162
|
+
DEPLOY_YAML_MISSING = failure(
|
|
163
|
+
"No deploy.yaml exists, so your service cannot be deployed.\n "
|
|
164
|
+
"Please push a deploy.yaml.\n "
|
|
165
|
+
"More info:",
|
|
166
|
+
"http://y/yelpsoa-configs",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
DEPLOY_SECURITY_FOUND = success(
|
|
170
|
+
"Found a security-check entry in your deploy pipeline"
|
|
171
|
+
)
|
|
172
|
+
DEPLOY_SECURITY_MISSING = failure(
|
|
173
|
+
"No 'security-check' entry was found in your deploy.yaml.\n"
|
|
174
|
+
"Please add a security-check entry *AFTER* the itest entry in deploy.yaml\n"
|
|
175
|
+
"so your docker image can be checked against known security vulnerabilities.\n"
|
|
176
|
+
"More info:",
|
|
177
|
+
"http://paasta.readthedocs.io/en/latest/generated/paasta_tools.cli.cmds.security_check.html",
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
DOCKERFILE_FOUND = success("Found Dockerfile")
|
|
181
|
+
|
|
182
|
+
DOCKERFILE_MISSING = failure(
|
|
183
|
+
"Dockerfile not found. Create a Dockerfile and try again.\n " "More info:",
|
|
184
|
+
"http://y/paasta-runbook-dockerfile",
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
DOCKERFILE_YELPCORP = success(
|
|
188
|
+
"Your Dockerfile pulls from the standard Yelp images."
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
DOCKERFILE_NOT_YELPCORP = failure(
|
|
192
|
+
"Your Dockerfile does not use the standard Yelp images.\n "
|
|
193
|
+
"This is bad because your `docker pulls` will be slow and you won't be "
|
|
194
|
+
"using the local mirrors.\n"
|
|
195
|
+
"More info:",
|
|
196
|
+
"http://y/base-docker-images",
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
GIT_REPO_FOUND = success("Git repo found in the expected location.")
|
|
200
|
+
|
|
201
|
+
ADHOC_YAML_FOUND = success("Found adhoc.yaml file.")
|
|
202
|
+
|
|
203
|
+
MAKEFILE_FOUND = success("A Makefile is present")
|
|
204
|
+
MAKEFILE_MISSING = failure(
|
|
205
|
+
"No Makefile available. Please make a Makefile that responds\n"
|
|
206
|
+
"to the proper targets. More info:",
|
|
207
|
+
"http://paasta.readthedocs.io/en/latest/about/contract.html",
|
|
208
|
+
)
|
|
209
|
+
MAKEFILE_RESPONDS_BUILD_IMAGE = success(
|
|
210
|
+
"The Makefile responds to `make cook-image`"
|
|
211
|
+
)
|
|
212
|
+
MAKEFILE_RESPONDS_BUILD_IMAGE_FAIL = failure(
|
|
213
|
+
"The Makefile does not have a `make cook-image` target. local-run needs\n"
|
|
214
|
+
"this and expects it to build your docker image. More info:",
|
|
215
|
+
"http://paasta.readthedocs.io/en/latest/about/contract.html",
|
|
216
|
+
)
|
|
217
|
+
MAKEFILE_RESPONDS_ITEST = success("The Makefile responds to `make itest`")
|
|
218
|
+
MAKEFILE_RESPONDS_ITEST_FAIL = failure(
|
|
219
|
+
"The Makefile does not have a `make itest` target. Jenkins needs\n"
|
|
220
|
+
"this and expects it to build and itest your docker image. More info:",
|
|
221
|
+
"http://paasta.readthedocs.io/en/latest/about/contract.html",
|
|
222
|
+
)
|
|
223
|
+
MAKEFILE_RESPONDS_TEST = success("The Makefile responds to `make test`")
|
|
224
|
+
MAKEFILE_RESPONDS_TEST_FAIL = failure(
|
|
225
|
+
"The Makefile does not have a `make test` target. Jenkins needs\n"
|
|
226
|
+
"this and expects it to run unit tests. More info:",
|
|
227
|
+
"http://paasta.readthedocs.io/en/latest/about/contract.html",
|
|
228
|
+
)
|
|
229
|
+
MAKEFILE_HAS_A_TAB = success("The Makefile contains a tab character")
|
|
230
|
+
MAKEFILE_HAS_NO_TABS = failure(
|
|
231
|
+
"The Makefile contains no tab characters. Make sure you\n"
|
|
232
|
+
"didn't accidentally paste spaces (which `make` does not respect)\n"
|
|
233
|
+
"instead of a tab.",
|
|
234
|
+
"http://paasta.readthedocs.io/en/latest/about/contract.html",
|
|
235
|
+
)
|
|
236
|
+
MAKEFILE_HAS_DOCKER_TAG = success("The Makefile contains a docker tag")
|
|
237
|
+
MAKEFILE_HAS_NO_DOCKER_TAG = failure(
|
|
238
|
+
"The Makefile contains no reference to DOCKER_TAG. Make sure you\n"
|
|
239
|
+
"specify a DOCKER_TAG and that your itest tags your docker image with $DOCKER_TAG.",
|
|
240
|
+
"http://paasta.readthedocs.io/en/latest/about/contract.html",
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
SENSU_MONITORING_FOUND = success("monitoring.yaml found for Sensu monitoring")
|
|
244
|
+
|
|
245
|
+
SENSU_MONITORING_MISSING = failure(
|
|
246
|
+
"Your service is not using Sensu (monitoring.yaml).\n "
|
|
247
|
+
"Please setup a monitoring.yaml so we know where to send alerts.\n "
|
|
248
|
+
"More info:",
|
|
249
|
+
"http://y/monitoring-yaml",
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
SENSU_TEAM_MISSING = failure(
|
|
253
|
+
"Cannot get team name. Ensure 'team' field is set in monitoring.yaml.\n"
|
|
254
|
+
" More info:",
|
|
255
|
+
"http://y/monitoring-yaml",
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
SMARTSTACK_YAML_FOUND = success("Found smartstack.yaml file")
|
|
259
|
+
|
|
260
|
+
SMARTSTACK_PORT_MISSING = failure(
|
|
261
|
+
"Could not determine port. "
|
|
262
|
+
"Ensure 'proxy_port' is set in smartstack.yaml.\n "
|
|
263
|
+
"More info:",
|
|
264
|
+
"http://y/smartstack-cep323",
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
@staticmethod
|
|
268
|
+
def git_repo_missing(git_url):
|
|
269
|
+
git_url = PaastaColors.cyan(git_url)
|
|
270
|
+
return failure(
|
|
271
|
+
"Could not find Git repo %s. "
|
|
272
|
+
"Your service must be there.\n"
|
|
273
|
+
" More info:" % git_url,
|
|
274
|
+
"http://y/yelpsoa-configs",
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
@staticmethod
|
|
278
|
+
def sensu_team_found(team_name):
|
|
279
|
+
return success(
|
|
280
|
+
"Your service uses Sensu and team '%s' will get alerts." % team_name
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
@staticmethod
|
|
284
|
+
def smartstack_port_found(instance, port):
|
|
285
|
+
return success(
|
|
286
|
+
"Instance '%s' of your service is using smartstack port %d "
|
|
287
|
+
"and will be automatically load balanced" % (instance, port)
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
@staticmethod
|
|
291
|
+
def service_dir_found(service, soa_dir):
|
|
292
|
+
message = "yelpsoa-config directory for {} found in {}".format(
|
|
293
|
+
PaastaColors.cyan(service), soa_dir
|
|
294
|
+
)
|
|
295
|
+
return success(message)
|
|
296
|
+
|
|
297
|
+
@staticmethod
|
|
298
|
+
def service_dir_missing(service, soa_dir):
|
|
299
|
+
message = (
|
|
300
|
+
"Failed to locate yelpsoa-config directory for %s in %s.\n"
|
|
301
|
+
" Please follow the guide linked below to get boilerplate."
|
|
302
|
+
% (service, soa_dir)
|
|
303
|
+
)
|
|
304
|
+
return failure(message, "http://y/paasta-deploy")
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class NoSuchService(Exception):
|
|
308
|
+
|
|
309
|
+
"""Exception to be raised in the event that the service
|
|
310
|
+
name can not be guessed.
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
GUESS_ERROR_MSG = (
|
|
314
|
+
"Could not determine service name.\n"
|
|
315
|
+
"Please run this from the root of a copy "
|
|
316
|
+
"(git clone) of your service.\n"
|
|
317
|
+
"Alternatively, supply the %s name you wish to "
|
|
318
|
+
"inspect with the %s option."
|
|
319
|
+
% (PaastaColors.cyan("SERVICE"), PaastaColors.cyan("-s"))
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
CHECK_ERROR_MSG = (
|
|
323
|
+
"not found. Please provide a valid service name.\n"
|
|
324
|
+
"Ensure that a directory of the same name exists in %s."
|
|
325
|
+
% PaastaColors.green("/nail/etc/services")
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
def __init__(self, service):
|
|
329
|
+
self.service = service
|
|
330
|
+
|
|
331
|
+
def __str__(self):
|
|
332
|
+
if self.service:
|
|
333
|
+
return "SERVICE: {} {}".format(
|
|
334
|
+
PaastaColors.cyan(self.service), self.CHECK_ERROR_MSG
|
|
335
|
+
)
|
|
336
|
+
else:
|
|
337
|
+
return self.GUESS_ERROR_MSG
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def guess_service_name():
|
|
341
|
+
"""Deduce the service name from the pwd
|
|
342
|
+
:return : A string representing the service name
|
|
343
|
+
"""
|
|
344
|
+
return os.path.basename(os.getcwd())
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def validate_service_name(service, soa_dir=DEFAULT_SOA_DIR):
|
|
348
|
+
"""Determine whether directory named service exists in the provided soa_dir
|
|
349
|
+
:param service: a string of the name of the service you wish to check exists
|
|
350
|
+
:param soa_dir: directory to look for service names
|
|
351
|
+
:return : boolean True
|
|
352
|
+
:raises: NoSuchService exception
|
|
353
|
+
"""
|
|
354
|
+
if not service or not os.path.isdir(os.path.join(soa_dir, service)):
|
|
355
|
+
raise NoSuchService(service)
|
|
356
|
+
return True
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def list_paasta_services(soa_dir: str = DEFAULT_SOA_DIR):
|
|
360
|
+
"""Returns a sorted list of services that happen to have at
|
|
361
|
+
least one service.instance, which indicates it is on PaaSTA
|
|
362
|
+
"""
|
|
363
|
+
the_list = []
|
|
364
|
+
for service in list_services(soa_dir):
|
|
365
|
+
if list_all_instances_for_service(service, soa_dir=soa_dir):
|
|
366
|
+
the_list.append(service)
|
|
367
|
+
return the_list
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def list_service_instances(soa_dir: str = DEFAULT_SOA_DIR):
|
|
371
|
+
"""Returns a sorted list of service<SPACER>instance names"""
|
|
372
|
+
the_list = []
|
|
373
|
+
for service in list_services(soa_dir):
|
|
374
|
+
for instance in list_all_instances_for_service(
|
|
375
|
+
service=service, soa_dir=soa_dir
|
|
376
|
+
):
|
|
377
|
+
the_list.append(compose_job_id(service, instance))
|
|
378
|
+
return the_list
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def list_instances(**kwargs):
|
|
382
|
+
"""Returns a sorted list of all possible instance names
|
|
383
|
+
for tab completion. We try to guess what service you might be
|
|
384
|
+
operating on, otherwise we just provide *all* of them
|
|
385
|
+
"""
|
|
386
|
+
all_instances: Set[str] = set()
|
|
387
|
+
service = guess_service_name()
|
|
388
|
+
try:
|
|
389
|
+
validate_service_name(service)
|
|
390
|
+
all_instances = set(list_all_instances_for_service(service))
|
|
391
|
+
except NoSuchService:
|
|
392
|
+
for service in list_services():
|
|
393
|
+
for instance in list_all_instances_for_service(service):
|
|
394
|
+
all_instances.add(instance)
|
|
395
|
+
return sorted(all_instances)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def calculate_remote_masters(
|
|
399
|
+
cluster: str, system_paasta_config: SystemPaastaConfig
|
|
400
|
+
) -> Tuple[List[str], str]:
|
|
401
|
+
"""Given a cluster, do a DNS lookup of that cluster (which
|
|
402
|
+
happens to point, eventually, to the Mesos masters in that cluster).
|
|
403
|
+
Return IPs of those Mesos masters.
|
|
404
|
+
"""
|
|
405
|
+
|
|
406
|
+
cluster_fqdn = system_paasta_config.get_cluster_fqdn_format().format(
|
|
407
|
+
cluster=cluster
|
|
408
|
+
)
|
|
409
|
+
try:
|
|
410
|
+
_, _, ips = socket.gethostbyname_ex(cluster_fqdn)
|
|
411
|
+
output = None
|
|
412
|
+
except socket.gaierror as e:
|
|
413
|
+
output = f"ERROR while doing DNS lookup of {cluster_fqdn}:\n{e.strerror}\n "
|
|
414
|
+
ips = []
|
|
415
|
+
return (ips, output)
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def find_connectable_master(
|
|
419
|
+
masters: Sequence[str],
|
|
420
|
+
) -> Tuple[Optional[str], Optional[str]]:
|
|
421
|
+
"""For each host in the iterable 'masters', try various connectivity
|
|
422
|
+
checks. For each master that fails, emit an error message about which check
|
|
423
|
+
failed and move on to the next master.
|
|
424
|
+
|
|
425
|
+
If a master passes all checks, return a tuple of the connectable master and
|
|
426
|
+
None. If no masters pass all checks, return a tuple of None and the output
|
|
427
|
+
from the DNS lookup.
|
|
428
|
+
"""
|
|
429
|
+
timeout = 6.0 # seconds
|
|
430
|
+
|
|
431
|
+
connectable_master = None
|
|
432
|
+
for master in masters:
|
|
433
|
+
rc, output = check_ssh_on_master(master, timeout=timeout)
|
|
434
|
+
if rc is True:
|
|
435
|
+
connectable_master = master
|
|
436
|
+
output = None
|
|
437
|
+
break
|
|
438
|
+
return (connectable_master, output)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
class NoMasterError(Exception):
|
|
442
|
+
pass
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def connectable_master(cluster: str, system_paasta_config: SystemPaastaConfig) -> str:
|
|
446
|
+
masters, output = calculate_remote_masters(cluster, system_paasta_config)
|
|
447
|
+
if masters == []:
|
|
448
|
+
raise NoMasterError("ERROR: %s" % output)
|
|
449
|
+
|
|
450
|
+
random.shuffle(masters)
|
|
451
|
+
|
|
452
|
+
master, output = find_connectable_master(masters)
|
|
453
|
+
if not master:
|
|
454
|
+
raise NoMasterError(
|
|
455
|
+
f"ERROR: could not find connectable master in cluster {cluster}\nOutput: {output}"
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
return master
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def check_ssh_on_master(master, timeout=10):
|
|
462
|
+
"""Given a master, attempt to ssh to the master and run a simple command
|
|
463
|
+
with sudo to verify that ssh and sudo work properly. Return a tuple of the
|
|
464
|
+
success status (True or False) and any output from attempting the check.
|
|
465
|
+
"""
|
|
466
|
+
check_command = "ssh -A -n -o StrictHostKeyChecking=no %s /bin/true" % master
|
|
467
|
+
rc, output = _run(check_command, timeout=timeout)
|
|
468
|
+
if rc == 0:
|
|
469
|
+
return (True, None)
|
|
470
|
+
if rc == 255: # ssh error
|
|
471
|
+
reason = "Return code was %d which probably means an ssh failure." % rc
|
|
472
|
+
hint = "HINT: Are you allowed to ssh to this machine %s?" % master
|
|
473
|
+
if rc == 1: # sudo error
|
|
474
|
+
reason = "Return code was %d which probably means a sudo failure." % rc
|
|
475
|
+
hint = "HINT: Is your ssh agent forwarded? (ssh-add -l)"
|
|
476
|
+
if rc == -9: # timeout error
|
|
477
|
+
reason = (
|
|
478
|
+
"Return code was %d which probably means ssh took too long and timed out."
|
|
479
|
+
% rc
|
|
480
|
+
)
|
|
481
|
+
hint = "HINT: Is there network latency? Try running somewhere closer to the cluster."
|
|
482
|
+
else: # unknown error
|
|
483
|
+
reason = "Return code was %d which is an unknown failure." % rc
|
|
484
|
+
hint = "HINT: Talk to #paasta and pastebin this output"
|
|
485
|
+
output = (
|
|
486
|
+
"ERROR cannot run check command %(check_command)s\n"
|
|
487
|
+
"%(reason)s\n"
|
|
488
|
+
"%(hint)s\n"
|
|
489
|
+
"Output from check command: %(output)s"
|
|
490
|
+
% {
|
|
491
|
+
"check_command": check_command,
|
|
492
|
+
"reason": reason,
|
|
493
|
+
"hint": hint,
|
|
494
|
+
"output": output,
|
|
495
|
+
}
|
|
496
|
+
)
|
|
497
|
+
return (False, output)
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
def run_on_master(
|
|
501
|
+
cluster,
|
|
502
|
+
system_paasta_config,
|
|
503
|
+
cmd_parts,
|
|
504
|
+
timeout=None,
|
|
505
|
+
err_code=-1,
|
|
506
|
+
graceful_exit=False,
|
|
507
|
+
stdin=None,
|
|
508
|
+
):
|
|
509
|
+
"""Find connectable master for :cluster: and :system_paasta_config: args and
|
|
510
|
+
invoke command from :cmd_parts:, wrapping it in ssh call.
|
|
511
|
+
|
|
512
|
+
:returns (exit code, output)
|
|
513
|
+
|
|
514
|
+
:param cluster: cluster to find master in
|
|
515
|
+
:param system_paasta_config: system configuration to lookup master data
|
|
516
|
+
:param cmd_parts: passed into paasta_tools.utils._run as command along with
|
|
517
|
+
ssh bits
|
|
518
|
+
:param timeout: see paasta_tools.utils._run documentation (default: None)
|
|
519
|
+
:param err_code: code to return along with error message when something goes
|
|
520
|
+
wrong (default: -1)
|
|
521
|
+
:param graceful_exit: wrap command in a bash script that waits for input and
|
|
522
|
+
kills the original command; trap SIGINT and send newline into stdin
|
|
523
|
+
"""
|
|
524
|
+
try:
|
|
525
|
+
master = connectable_master(cluster, system_paasta_config)
|
|
526
|
+
except NoMasterError as e:
|
|
527
|
+
return (err_code, str(e))
|
|
528
|
+
|
|
529
|
+
if graceful_exit:
|
|
530
|
+
# Signals don't travel over ssh, kill process when anything lands on stdin instead
|
|
531
|
+
# The procedure here is:
|
|
532
|
+
# 1. send process to background and capture it's pid
|
|
533
|
+
# 2. wait for stdin with timeout in a loop, exit when original process finished
|
|
534
|
+
# 3. kill original process if loop finished (something on stdin)
|
|
535
|
+
cmd_parts.append(
|
|
536
|
+
"& p=$!; "
|
|
537
|
+
+ "while ! read -t1; do ! kill -0 $p 2>/dev/null && kill $$; done; "
|
|
538
|
+
+ "kill $p; wait"
|
|
539
|
+
)
|
|
540
|
+
stdin = subprocess.PIPE
|
|
541
|
+
stdin_interrupt = True
|
|
542
|
+
popen_kwargs = {"preexec_fn": os.setsid}
|
|
543
|
+
else:
|
|
544
|
+
stdin_interrupt = False
|
|
545
|
+
popen_kwargs = {}
|
|
546
|
+
|
|
547
|
+
cmd_parts = [
|
|
548
|
+
"ssh",
|
|
549
|
+
"-q",
|
|
550
|
+
"-t",
|
|
551
|
+
"-t",
|
|
552
|
+
"-A",
|
|
553
|
+
master,
|
|
554
|
+
"sudo /bin/bash -c %s" % quote(" ".join(cmd_parts)),
|
|
555
|
+
]
|
|
556
|
+
|
|
557
|
+
log.debug("Running %s" % " ".join(cmd_parts))
|
|
558
|
+
|
|
559
|
+
return _run(
|
|
560
|
+
cmd_parts,
|
|
561
|
+
timeout=timeout,
|
|
562
|
+
stream=True,
|
|
563
|
+
stdin=stdin,
|
|
564
|
+
stdin_interrupt=stdin_interrupt,
|
|
565
|
+
popen_kwargs=popen_kwargs,
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def lazy_choices_completer(list_func):
|
|
570
|
+
def inner(prefix, **kwargs):
|
|
571
|
+
options = list_func(**kwargs)
|
|
572
|
+
return [o for o in options if o.startswith(prefix)]
|
|
573
|
+
|
|
574
|
+
return inner
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def figure_out_service_name(args, soa_dir=DEFAULT_SOA_DIR):
|
|
578
|
+
"""Figures out and validates the input service name"""
|
|
579
|
+
service = args.service or guess_service_name()
|
|
580
|
+
try:
|
|
581
|
+
validate_service_name(service, soa_dir=soa_dir)
|
|
582
|
+
except NoSuchService as service_not_found:
|
|
583
|
+
print(service_not_found)
|
|
584
|
+
exit(1)
|
|
585
|
+
return service
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def get_jenkins_build_output_url():
|
|
589
|
+
"""Returns the URL for Jenkins job's output.
|
|
590
|
+
Returns None if it's not available.
|
|
591
|
+
"""
|
|
592
|
+
build_output = os.environ.get("BUILD_URL")
|
|
593
|
+
if build_output:
|
|
594
|
+
build_output = build_output + "console"
|
|
595
|
+
return build_output
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
InstanceListerSig = Callable[
|
|
599
|
+
[
|
|
600
|
+
NamedArg(str, "service"),
|
|
601
|
+
NamedArg(Optional[str], "cluster"),
|
|
602
|
+
NamedArg(str, "instance_type"),
|
|
603
|
+
NamedArg(str, "soa_dir"),
|
|
604
|
+
],
|
|
605
|
+
List[Tuple[str, str]],
|
|
606
|
+
]
|
|
607
|
+
|
|
608
|
+
InstanceLoaderSig = Callable[
|
|
609
|
+
[
|
|
610
|
+
NamedArg(str, "service"),
|
|
611
|
+
NamedArg(str, "instance"),
|
|
612
|
+
NamedArg(str, "cluster"),
|
|
613
|
+
NamedArg(bool, "load_deployments"),
|
|
614
|
+
NamedArg(str, "soa_dir"),
|
|
615
|
+
],
|
|
616
|
+
InstanceConfig,
|
|
617
|
+
]
|
|
618
|
+
|
|
619
|
+
LongRunningServiceListerSig = Callable[
|
|
620
|
+
[
|
|
621
|
+
NamedArg(str, "service"),
|
|
622
|
+
NamedArg(Optional[str], "cluster"),
|
|
623
|
+
NamedArg(str, "instance_type"),
|
|
624
|
+
NamedArg(str, "soa_dir"),
|
|
625
|
+
],
|
|
626
|
+
List[Tuple[str, str]],
|
|
627
|
+
]
|
|
628
|
+
|
|
629
|
+
LongRunningServiceLoaderSig = Callable[
|
|
630
|
+
[
|
|
631
|
+
NamedArg(str, "service"),
|
|
632
|
+
NamedArg(str, "instance"),
|
|
633
|
+
NamedArg(str, "cluster"),
|
|
634
|
+
NamedArg(bool, "load_deployments"),
|
|
635
|
+
NamedArg(str, "soa_dir"),
|
|
636
|
+
],
|
|
637
|
+
LongRunningServiceConfig,
|
|
638
|
+
]
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
class InstanceTypeHandler(NamedTuple):
|
|
642
|
+
lister: InstanceListerSig
|
|
643
|
+
loader: InstanceLoaderSig
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
class LongRunningInstanceTypeHandler(NamedTuple):
|
|
647
|
+
lister: LongRunningServiceListerSig
|
|
648
|
+
loader: LongRunningServiceLoaderSig
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
INSTANCE_TYPE_HANDLERS: Mapping[str, InstanceTypeHandler] = defaultdict(
|
|
652
|
+
lambda: InstanceTypeHandler(None, None),
|
|
653
|
+
adhoc=InstanceTypeHandler(get_service_instance_list, load_adhoc_job_config),
|
|
654
|
+
kubernetes=InstanceTypeHandler(
|
|
655
|
+
get_service_instance_list, load_kubernetes_service_config
|
|
656
|
+
),
|
|
657
|
+
eks=InstanceTypeHandler(get_service_instance_list, load_eks_service_config),
|
|
658
|
+
tron=InstanceTypeHandler(get_service_instance_list, load_tron_instance_config),
|
|
659
|
+
flink=InstanceTypeHandler(get_service_instance_list, load_flink_instance_config),
|
|
660
|
+
flinkeks=InstanceTypeHandler(
|
|
661
|
+
get_service_instance_list, load_flinkeks_instance_config
|
|
662
|
+
),
|
|
663
|
+
cassandracluster=InstanceTypeHandler(
|
|
664
|
+
get_service_instance_list, load_cassandracluster_instance_config
|
|
665
|
+
),
|
|
666
|
+
kafkacluster=InstanceTypeHandler(
|
|
667
|
+
get_service_instance_list, load_kafkacluster_instance_config
|
|
668
|
+
),
|
|
669
|
+
nrtsearchservice=InstanceTypeHandler(
|
|
670
|
+
get_service_instance_list, load_nrtsearchservice_instance_config
|
|
671
|
+
),
|
|
672
|
+
nrtsearchserviceeks=InstanceTypeHandler(
|
|
673
|
+
get_service_instance_list, load_nrtsearchserviceeks_instance_config
|
|
674
|
+
),
|
|
675
|
+
monkrelays=InstanceTypeHandler(
|
|
676
|
+
get_service_instance_list, load_monkrelaycluster_instance_config
|
|
677
|
+
),
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
LONG_RUNNING_INSTANCE_TYPE_HANDLERS: Mapping[
|
|
681
|
+
str, LongRunningInstanceTypeHandler
|
|
682
|
+
] = defaultdict(
|
|
683
|
+
lambda: LongRunningInstanceTypeHandler(None, None),
|
|
684
|
+
kubernetes=LongRunningInstanceTypeHandler(
|
|
685
|
+
get_service_instance_list, load_kubernetes_service_config
|
|
686
|
+
),
|
|
687
|
+
flink=LongRunningInstanceTypeHandler(
|
|
688
|
+
get_service_instance_list, load_flink_instance_config
|
|
689
|
+
),
|
|
690
|
+
flinkeks=LongRunningInstanceTypeHandler(
|
|
691
|
+
get_service_instance_list, load_flinkeks_instance_config
|
|
692
|
+
),
|
|
693
|
+
cassandracluster=LongRunningInstanceTypeHandler(
|
|
694
|
+
get_service_instance_list, load_cassandracluster_instance_config
|
|
695
|
+
),
|
|
696
|
+
kafkacluster=LongRunningInstanceTypeHandler(
|
|
697
|
+
get_service_instance_list, load_kafkacluster_instance_config
|
|
698
|
+
),
|
|
699
|
+
nrtsearchservice=LongRunningInstanceTypeHandler(
|
|
700
|
+
get_service_instance_list, load_nrtsearchservice_instance_config
|
|
701
|
+
),
|
|
702
|
+
nrtsearchserviceeks=LongRunningInstanceTypeHandler(
|
|
703
|
+
get_service_instance_list, load_nrtsearchserviceeks_instance_config
|
|
704
|
+
),
|
|
705
|
+
monkrelays=LongRunningInstanceTypeHandler(
|
|
706
|
+
get_service_instance_list, load_monkrelaycluster_instance_config
|
|
707
|
+
),
|
|
708
|
+
eks=LongRunningInstanceTypeHandler(
|
|
709
|
+
get_service_instance_list, load_eks_service_config
|
|
710
|
+
),
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def get_instance_config(
|
|
715
|
+
service: str,
|
|
716
|
+
instance: str,
|
|
717
|
+
cluster: str,
|
|
718
|
+
soa_dir: str = DEFAULT_SOA_DIR,
|
|
719
|
+
load_deployments: bool = False,
|
|
720
|
+
instance_type: Optional[str] = None,
|
|
721
|
+
) -> InstanceConfig:
|
|
722
|
+
"""Returns the InstanceConfig object for whatever type of instance
|
|
723
|
+
it is. (kubernetes)"""
|
|
724
|
+
if instance_type is None:
|
|
725
|
+
instance_type = validate_service_instance(
|
|
726
|
+
service=service, instance=instance, cluster=cluster, soa_dir=soa_dir
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
instance_config_loader = INSTANCE_TYPE_HANDLERS[instance_type].loader
|
|
730
|
+
if instance_config_loader is None:
|
|
731
|
+
raise NotImplementedError(
|
|
732
|
+
"instance is %s of type %s which is not supported by paasta"
|
|
733
|
+
% (instance, instance_type)
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
return instance_config_loader(
|
|
737
|
+
service=service,
|
|
738
|
+
instance=instance,
|
|
739
|
+
cluster=cluster,
|
|
740
|
+
load_deployments=load_deployments,
|
|
741
|
+
soa_dir=soa_dir,
|
|
742
|
+
)
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
def get_namespaces_for_secret(
|
|
746
|
+
service: str, cluster: str, secret_name: str, soa_dir: str = DEFAULT_SOA_DIR
|
|
747
|
+
) -> Set[str]:
|
|
748
|
+
secret_to_k8s_namespace = set()
|
|
749
|
+
|
|
750
|
+
k8s_instance_type_classes = {
|
|
751
|
+
"kubernetes": KubernetesDeploymentConfig,
|
|
752
|
+
"eks": EksDeploymentConfig,
|
|
753
|
+
}
|
|
754
|
+
for instance_type in INSTANCE_TYPES:
|
|
755
|
+
if instance_type in PAASTA_K8S_INSTANCE_TYPES:
|
|
756
|
+
config_loader = PaastaServiceConfigLoader(service, soa_dir)
|
|
757
|
+
for service_instance_config in config_loader.instance_configs(
|
|
758
|
+
cluster=cluster,
|
|
759
|
+
instance_type_class=k8s_instance_type_classes[instance_type],
|
|
760
|
+
):
|
|
761
|
+
secret_to_k8s_namespace.add(service_instance_config.get_namespace())
|
|
762
|
+
else:
|
|
763
|
+
instances = get_service_instance_list(
|
|
764
|
+
service=service,
|
|
765
|
+
instance_type=instance_type,
|
|
766
|
+
cluster=cluster,
|
|
767
|
+
soa_dir=soa_dir,
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
for serv, instance in instances:
|
|
771
|
+
config = get_instance_config(serv, instance, cluster, soa_dir)
|
|
772
|
+
if secret_name in config.get_env():
|
|
773
|
+
secret_to_k8s_namespace.add(
|
|
774
|
+
INSTANCE_TYPE_TO_K8S_NAMESPACE[instance_type]
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
return secret_to_k8s_namespace
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
def select_k8s_secret_namespace(namespaces: Set[str]) -> Optional[str]:
|
|
781
|
+
namespaces_count = len(namespaces)
|
|
782
|
+
|
|
783
|
+
if not namespaces_count:
|
|
784
|
+
return None
|
|
785
|
+
|
|
786
|
+
if namespaces_count == 1:
|
|
787
|
+
return namespaces.pop()
|
|
788
|
+
|
|
789
|
+
# prioritise paasta, tron namespaces when found
|
|
790
|
+
for namespace in namespaces:
|
|
791
|
+
if namespace.startswith("paasta"):
|
|
792
|
+
return namespace
|
|
793
|
+
if namespace == "tron":
|
|
794
|
+
return namespace
|
|
795
|
+
|
|
796
|
+
# only experimental k8s namespaces
|
|
797
|
+
return namespaces.pop()
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
def extract_tags(paasta_tag: str) -> Mapping[str, str]:
|
|
801
|
+
"""Returns a dictionary containing information from a git tag"""
|
|
802
|
+
regex = r"^refs/tags/(?:paasta-){1,2}(?P<deploy_group>[a-zA-Z0-9._-]+)(?:\+(?P<image_version>.*)){0,1}-(?P<tstamp>\d{8}T\d{6})-(?P<tag>.*?)$"
|
|
803
|
+
regex_match = re.match(regex, paasta_tag)
|
|
804
|
+
return regex_match.groupdict() if regex_match else {}
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
def list_deploy_groups(
|
|
808
|
+
service: Optional[str], soa_dir: str = DEFAULT_SOA_DIR, parsed_args=None, **kwargs
|
|
809
|
+
) -> Set:
|
|
810
|
+
return set(
|
|
811
|
+
filter(
|
|
812
|
+
None,
|
|
813
|
+
{
|
|
814
|
+
config.get_deploy_group()
|
|
815
|
+
for config in get_instance_configs_for_service(
|
|
816
|
+
service=service
|
|
817
|
+
if service is not None
|
|
818
|
+
else parsed_args.service or guess_service_name(),
|
|
819
|
+
soa_dir=soa_dir,
|
|
820
|
+
)
|
|
821
|
+
},
|
|
822
|
+
)
|
|
823
|
+
)
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
def validate_given_deploy_groups(
|
|
827
|
+
all_deploy_groups: Collection[str], args_deploy_groups: Collection[str]
|
|
828
|
+
) -> Tuple[Set[str], Set[str]]:
|
|
829
|
+
"""Given two lists of deploy groups, return the intersection and difference between them.
|
|
830
|
+
|
|
831
|
+
:param all_deploy_groups: instances actually belonging to a service
|
|
832
|
+
:param args_deploy_groups: the desired instances
|
|
833
|
+
:returns: a tuple with (common, difference) indicating deploy groups common in both
|
|
834
|
+
lists and those only in args_deploy_groups
|
|
835
|
+
"""
|
|
836
|
+
invalid_deploy_groups: Set[str]
|
|
837
|
+
valid_deploy_groups = set(args_deploy_groups).intersection(all_deploy_groups)
|
|
838
|
+
invalid_deploy_groups = set(args_deploy_groups).difference(all_deploy_groups)
|
|
839
|
+
|
|
840
|
+
return valid_deploy_groups, invalid_deploy_groups
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
def short_to_full_git_sha(short, refs):
|
|
844
|
+
"""Converts a short git sha to a full SHA
|
|
845
|
+
|
|
846
|
+
:param short: A short Git SHA represented as a string
|
|
847
|
+
:param refs: A list of refs in the Git repository
|
|
848
|
+
:return: The full Git SHA or None if one can't be found
|
|
849
|
+
"""
|
|
850
|
+
return [sha for sha in set(refs.values()) if sha.startswith(short)]
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def validate_short_git_sha(value):
|
|
854
|
+
pattern = re.compile("[a-f0-9]{4,40}")
|
|
855
|
+
if not pattern.match(value):
|
|
856
|
+
raise argparse.ArgumentTypeError("%s is not a valid Git SHA" % value)
|
|
857
|
+
return value
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
def validate_full_git_sha(value: str) -> str:
|
|
861
|
+
pattern = re.compile("[a-f0-9]{40}")
|
|
862
|
+
if not pattern.match(value):
|
|
863
|
+
raise argparse.ArgumentTypeError(
|
|
864
|
+
"%s is not a full Git SHA, and PaaSTA needs the full SHA" % value
|
|
865
|
+
)
|
|
866
|
+
return value
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
def validate_git_sha(sha, git_url):
|
|
870
|
+
try:
|
|
871
|
+
validate_full_git_sha(sha)
|
|
872
|
+
return sha
|
|
873
|
+
except argparse.ArgumentTypeError:
|
|
874
|
+
refs = remote_git.list_remote_refs(git_url)
|
|
875
|
+
commits = short_to_full_git_sha(short=sha, refs=refs)
|
|
876
|
+
if len(commits) != 1:
|
|
877
|
+
raise ValueError(
|
|
878
|
+
"%s matched %d Git SHAs (with refs pointing at them). Must match exactly 1."
|
|
879
|
+
% (sha, len(commits))
|
|
880
|
+
)
|
|
881
|
+
return commits[0]
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
def get_subparser(subparsers, function, command, help_text, description):
|
|
885
|
+
new_parser = subparsers.add_parser(
|
|
886
|
+
command,
|
|
887
|
+
help=help_text,
|
|
888
|
+
description=(description),
|
|
889
|
+
epilog=(
|
|
890
|
+
"Note: This command requires SSH and sudo privileges on the remote PaaSTA "
|
|
891
|
+
"nodes."
|
|
892
|
+
),
|
|
893
|
+
)
|
|
894
|
+
new_parser.add_argument(
|
|
895
|
+
"-s",
|
|
896
|
+
"--service",
|
|
897
|
+
help="The name of the service you wish to inspect",
|
|
898
|
+
required=True,
|
|
899
|
+
).completer = lazy_choices_completer(list_services)
|
|
900
|
+
new_parser.add_argument(
|
|
901
|
+
"-c",
|
|
902
|
+
"--cluster",
|
|
903
|
+
help="Cluster on which the service is running"
|
|
904
|
+
"For example: --cluster pnw-prod",
|
|
905
|
+
required=True,
|
|
906
|
+
).completer = lazy_choices_completer(list_clusters)
|
|
907
|
+
new_parser.add_argument(
|
|
908
|
+
"-i",
|
|
909
|
+
"--instance",
|
|
910
|
+
help="The instance that you wish to inspect" "For example: --instance main",
|
|
911
|
+
required=True,
|
|
912
|
+
default="main",
|
|
913
|
+
) # No completer because we need to know service first and we can't until some other stuff has happened
|
|
914
|
+
new_parser.add_argument(
|
|
915
|
+
"-H",
|
|
916
|
+
"--host",
|
|
917
|
+
dest="host",
|
|
918
|
+
default=None,
|
|
919
|
+
help="Specify a specific host on which to run. Defaults to"
|
|
920
|
+
" one that is running the service chosen at random",
|
|
921
|
+
)
|
|
922
|
+
new_parser.add_argument(
|
|
923
|
+
"-m",
|
|
924
|
+
"--mesos-id",
|
|
925
|
+
dest="mesos_id",
|
|
926
|
+
default=None,
|
|
927
|
+
help="A specific mesos task ID, must match a task "
|
|
928
|
+
"running on the specified host. If not specified we "
|
|
929
|
+
"will pick a task at random",
|
|
930
|
+
)
|
|
931
|
+
new_parser.set_defaults(command=function)
|
|
932
|
+
return new_parser
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
def get_instance_configs_for_service(
|
|
936
|
+
service: str,
|
|
937
|
+
soa_dir: str,
|
|
938
|
+
type_filter: Optional[Iterable[str]] = None,
|
|
939
|
+
clusters: Optional[Sequence[str]] = None,
|
|
940
|
+
instances: Optional[Sequence[str]] = None,
|
|
941
|
+
) -> Generator[InstanceConfig, None, None]:
|
|
942
|
+
if not clusters:
|
|
943
|
+
clusters = list_clusters(service=service, soa_dir=soa_dir)
|
|
944
|
+
|
|
945
|
+
if type_filter is None:
|
|
946
|
+
type_filter = INSTANCE_TYPE_HANDLERS.keys()
|
|
947
|
+
|
|
948
|
+
for cluster in clusters:
|
|
949
|
+
for instance_type, instance_handlers in INSTANCE_TYPE_HANDLERS.items():
|
|
950
|
+
if instance_type not in type_filter:
|
|
951
|
+
continue
|
|
952
|
+
|
|
953
|
+
instance_lister, instance_loader = instance_handlers
|
|
954
|
+
|
|
955
|
+
for _, instance in instance_lister(
|
|
956
|
+
service=service,
|
|
957
|
+
cluster=cluster,
|
|
958
|
+
soa_dir=soa_dir,
|
|
959
|
+
instance_type=instance_type,
|
|
960
|
+
):
|
|
961
|
+
if instances and instance not in instances:
|
|
962
|
+
continue
|
|
963
|
+
|
|
964
|
+
yield instance_loader(
|
|
965
|
+
service=service,
|
|
966
|
+
instance=instance,
|
|
967
|
+
cluster=cluster,
|
|
968
|
+
soa_dir=soa_dir,
|
|
969
|
+
load_deployments=False,
|
|
970
|
+
)
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
def get_container_name(task):
|
|
974
|
+
container_name = "mesos-{}".format(task.executor["container"])
|
|
975
|
+
return container_name
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
def pick_random_port(service_name):
|
|
979
|
+
"""Return a random port.
|
|
980
|
+
|
|
981
|
+
Tries to return the same port for the same service each time, when
|
|
982
|
+
possible.
|
|
983
|
+
"""
|
|
984
|
+
hash_key = f"{service_name},{getpass.getuser()}".encode("utf8")
|
|
985
|
+
hash_number = int(hashlib.sha1(hash_key).hexdigest(), 16)
|
|
986
|
+
preferred_port = 33000 + (hash_number % 25000)
|
|
987
|
+
return ephemeral_port_reserve.reserve("0.0.0.0", preferred_port)
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
def trigger_deploys(
|
|
991
|
+
service: str,
|
|
992
|
+
system_config: Optional["SystemPaastaConfig"] = None,
|
|
993
|
+
) -> None:
|
|
994
|
+
"""Connects to the deploymentsd watcher on sysgit, which is an extremely simple
|
|
995
|
+
service that listens for a service string and then generates a service deployment"""
|
|
996
|
+
logline = f"Notifying soa-configs primary to generate a deployment for {service}"
|
|
997
|
+
_log(service=service, line=logline, component="deploy", level="event")
|
|
998
|
+
if not system_config:
|
|
999
|
+
system_config = load_system_paasta_config()
|
|
1000
|
+
server = system_config.get_git_repo_config("yelpsoa-configs").get(
|
|
1001
|
+
"deploy_server",
|
|
1002
|
+
DEFAULT_SOA_CONFIGS_GIT_URL,
|
|
1003
|
+
)
|
|
1004
|
+
|
|
1005
|
+
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
1006
|
+
try:
|
|
1007
|
+
client.connect((server, 5049))
|
|
1008
|
+
client.send(f"{service}\n".encode("utf-8"))
|
|
1009
|
+
finally:
|
|
1010
|
+
client.close()
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
def verify_instances(
|
|
1014
|
+
args_instances: str,
|
|
1015
|
+
service: str,
|
|
1016
|
+
clusters: Sequence[str],
|
|
1017
|
+
soa_dir: str = DEFAULT_SOA_DIR,
|
|
1018
|
+
) -> Sequence[str]:
|
|
1019
|
+
"""Verify that a list of instances specified by user is correct for this service.
|
|
1020
|
+
|
|
1021
|
+
:param args_instances: a list of instances.
|
|
1022
|
+
:param service: the service name
|
|
1023
|
+
:param cluster: a list of clusters
|
|
1024
|
+
:returns: a list of instances specified in args_instances without any exclusions.
|
|
1025
|
+
"""
|
|
1026
|
+
unverified_instances = args_instances.split(",")
|
|
1027
|
+
service_instances: Set[str] = list_all_instances_for_service(
|
|
1028
|
+
service, clusters=clusters, soa_dir=soa_dir
|
|
1029
|
+
)
|
|
1030
|
+
|
|
1031
|
+
misspelled_instances: Sequence[str] = [
|
|
1032
|
+
i for i in unverified_instances if i not in service_instances
|
|
1033
|
+
]
|
|
1034
|
+
|
|
1035
|
+
if len(misspelled_instances) == 0:
|
|
1036
|
+
return misspelled_instances
|
|
1037
|
+
|
|
1038
|
+
# Check for instances with suffixes other than Tron instances (i.e. Flink instances)
|
|
1039
|
+
instances_without_suffixes = [x.split(".")[0] for x in unverified_instances]
|
|
1040
|
+
|
|
1041
|
+
misspelled_instances = [
|
|
1042
|
+
i for i in instances_without_suffixes if i not in service_instances
|
|
1043
|
+
]
|
|
1044
|
+
|
|
1045
|
+
if misspelled_instances:
|
|
1046
|
+
suggestions: List[str] = []
|
|
1047
|
+
for instance in misspelled_instances:
|
|
1048
|
+
matches = difflib.get_close_matches(
|
|
1049
|
+
instance, service_instances, n=5, cutoff=0.5
|
|
1050
|
+
)
|
|
1051
|
+
suggestions.extend(matches) # type: ignore
|
|
1052
|
+
suggestions = list(set(suggestions))
|
|
1053
|
+
|
|
1054
|
+
if clusters:
|
|
1055
|
+
message = "{} doesn't have any instances matching {} on {}.".format(
|
|
1056
|
+
service,
|
|
1057
|
+
", ".join(sorted(misspelled_instances)),
|
|
1058
|
+
", ".join(sorted(clusters)),
|
|
1059
|
+
)
|
|
1060
|
+
else:
|
|
1061
|
+
message = "{} doesn't have any instances matching {}.".format(
|
|
1062
|
+
service, ", ".join(sorted(misspelled_instances))
|
|
1063
|
+
)
|
|
1064
|
+
|
|
1065
|
+
print(PaastaColors.red(message))
|
|
1066
|
+
|
|
1067
|
+
if suggestions:
|
|
1068
|
+
print("Did you mean any of these?")
|
|
1069
|
+
for instance in sorted(suggestions):
|
|
1070
|
+
print(" %s" % instance)
|
|
1071
|
+
|
|
1072
|
+
return misspelled_instances
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
def get_paasta_oapi_api_clustername(cluster: str, is_eks: bool) -> str:
|
|
1076
|
+
"""
|
|
1077
|
+
We'll be doing a tiny bit of lying while we have both EKS and non-EKS
|
|
1078
|
+
clusters: these will generally share the same PaaSTA name (i.e., the
|
|
1079
|
+
soaconfigs suffix will stay the same) - but we'll need a way to route API
|
|
1080
|
+
requests to the correct place. To do so, we'll add "fake" entries to our
|
|
1081
|
+
api_endpoints SystemPaastaConfig that are the PaaSTA clustername with an
|
|
1082
|
+
"eks-" prefix
|
|
1083
|
+
"""
|
|
1084
|
+
return f"eks-{cluster}" if is_eks else cluster
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
def get_paasta_oapi_client_with_auth(
|
|
1088
|
+
cluster: str = None,
|
|
1089
|
+
system_paasta_config: SystemPaastaConfig = None,
|
|
1090
|
+
http_res: bool = False,
|
|
1091
|
+
) -> Optional[PaastaOApiClient]:
|
|
1092
|
+
return get_paasta_oapi_client(
|
|
1093
|
+
cluster=cluster,
|
|
1094
|
+
system_paasta_config=system_paasta_config,
|
|
1095
|
+
http_res=http_res,
|
|
1096
|
+
auth_token=get_sso_auth_token(paasta_apis=True),
|
|
1097
|
+
)
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
def run_interactive_cli(
|
|
1101
|
+
cmd: str, shell: str = "/bin/bash", term: str = "xterm-256color"
|
|
1102
|
+
):
|
|
1103
|
+
"""Runs interactive command in a pseudo terminal, handling terminal size management
|
|
1104
|
+
|
|
1105
|
+
:param str cmd: shell command
|
|
1106
|
+
:param str shell: shell utility to use as wrapper
|
|
1107
|
+
:param str term: terminal type
|
|
1108
|
+
"""
|
|
1109
|
+
cols, rows = shutil.get_terminal_size()
|
|
1110
|
+
if not os.path.isabs(shell):
|
|
1111
|
+
shell = shutil.which(shell)
|
|
1112
|
+
wrapped_cmd = (
|
|
1113
|
+
f"export SHELL={shell};"
|
|
1114
|
+
f"export TERM={term};"
|
|
1115
|
+
f"stty columns {cols} rows {rows};"
|
|
1116
|
+
f"exec {cmd}"
|
|
1117
|
+
)
|
|
1118
|
+
pty.spawn([shell, "-c", wrapped_cmd])
|