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,678 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import socket
|
|
5
|
+
from typing import Dict
|
|
6
|
+
from typing import List
|
|
7
|
+
from typing import Mapping
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from typing import Sequence
|
|
10
|
+
from typing import Tuple
|
|
11
|
+
from typing import Type
|
|
12
|
+
|
|
13
|
+
import service_configuration_lib
|
|
14
|
+
|
|
15
|
+
from paasta_tools.autoscaling.utils import AutoscalingParamsDict
|
|
16
|
+
from paasta_tools.autoscaling.utils import MetricsProviderDict
|
|
17
|
+
from paasta_tools.paasta_service_config_loader import PaastaServiceConfigLoader
|
|
18
|
+
from paasta_tools.utils import BranchDictV2
|
|
19
|
+
from paasta_tools.utils import compose_job_id
|
|
20
|
+
from paasta_tools.utils import decompose_job_id
|
|
21
|
+
from paasta_tools.utils import deep_merge_dictionaries
|
|
22
|
+
from paasta_tools.utils import DEFAULT_SOA_DIR
|
|
23
|
+
from paasta_tools.utils import DeployBlacklist
|
|
24
|
+
from paasta_tools.utils import DeployWhitelist
|
|
25
|
+
from paasta_tools.utils import InstanceConfig
|
|
26
|
+
from paasta_tools.utils import InstanceConfigDict
|
|
27
|
+
from paasta_tools.utils import InvalidInstanceConfig
|
|
28
|
+
from paasta_tools.utils import InvalidJobNameError
|
|
29
|
+
from paasta_tools.utils import load_system_paasta_config
|
|
30
|
+
from paasta_tools.utils import SystemPaastaConfig
|
|
31
|
+
|
|
32
|
+
log = logging.getLogger(__name__)
|
|
33
|
+
logging.getLogger("long_running_service_tools").setLevel(logging.WARNING)
|
|
34
|
+
|
|
35
|
+
ZK_PAUSE_AUTOSCALE_PATH = "/autoscaling/paused"
|
|
36
|
+
DEFAULT_CONTAINER_PORT = 8888
|
|
37
|
+
|
|
38
|
+
DEFAULT_AUTOSCALING_SETPOINT = 0.8
|
|
39
|
+
DEFAULT_DESIRED_ACTIVE_REQUESTS_PER_REPLICA = 1
|
|
40
|
+
DEFAULT_ACTIVE_REQUESTS_AUTOSCALING_MOVING_AVERAGE_WINDOW = 1800
|
|
41
|
+
DEFAULT_UWSGI_AUTOSCALING_MOVING_AVERAGE_WINDOW = 1800
|
|
42
|
+
DEFAULT_PISCINA_AUTOSCALING_MOVING_AVERAGE_WINDOW = 1800
|
|
43
|
+
DEFAULT_GUNICORN_AUTOSCALING_MOVING_AVERAGE_WINDOW = 1800
|
|
44
|
+
|
|
45
|
+
METRICS_PROVIDER_CPU = "cpu"
|
|
46
|
+
METRICS_PROVIDER_UWSGI = "uwsgi"
|
|
47
|
+
METRICS_PROVIDER_UWSGI_V2 = "uwsgi-v2"
|
|
48
|
+
METRICS_PROVIDER_GUNICORN = "gunicorn"
|
|
49
|
+
METRICS_PROVIDER_PISCINA = "piscina"
|
|
50
|
+
METRICS_PROVIDER_ACTIVE_REQUESTS = "active-requests"
|
|
51
|
+
METRICS_PROVIDER_PROMQL = "arbitrary_promql"
|
|
52
|
+
|
|
53
|
+
ALL_METRICS_PROVIDERS = [
|
|
54
|
+
METRICS_PROVIDER_CPU,
|
|
55
|
+
METRICS_PROVIDER_UWSGI,
|
|
56
|
+
METRICS_PROVIDER_UWSGI_V2,
|
|
57
|
+
METRICS_PROVIDER_GUNICORN,
|
|
58
|
+
METRICS_PROVIDER_PISCINA,
|
|
59
|
+
METRICS_PROVIDER_ACTIVE_REQUESTS,
|
|
60
|
+
METRICS_PROVIDER_PROMQL,
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class LongRunningServiceConfigDict(InstanceConfigDict, total=False):
|
|
65
|
+
autoscaling: AutoscalingParamsDict
|
|
66
|
+
drain_method: str
|
|
67
|
+
fs_group: int
|
|
68
|
+
container_port: int
|
|
69
|
+
drain_method_params: Dict
|
|
70
|
+
healthcheck_cmd: str
|
|
71
|
+
healthcheck_grace_period_seconds: float
|
|
72
|
+
healthcheck_interval_seconds: float
|
|
73
|
+
healthcheck_max_consecutive_failures: int
|
|
74
|
+
healthcheck_mode: str
|
|
75
|
+
healthcheck_timeout_seconds: float
|
|
76
|
+
healthcheck_uri: str
|
|
77
|
+
instances: int
|
|
78
|
+
max_instances: int
|
|
79
|
+
min_instances: int
|
|
80
|
+
nerve_ns: str
|
|
81
|
+
network_mode: str
|
|
82
|
+
registrations: List[str]
|
|
83
|
+
replication_threshold: int
|
|
84
|
+
bounce_start_deadline: float
|
|
85
|
+
bounce_margin_factor: float
|
|
86
|
+
should_ping_for_unhealthy_pods: bool
|
|
87
|
+
weight: int
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ServiceNamespaceConfig(dict):
|
|
91
|
+
def get_healthcheck_mode(self) -> str:
|
|
92
|
+
"""Get the healthcheck mode for the service. In most cases, this will match the mode
|
|
93
|
+
of the service, but we do provide the opportunity for users to specify both. Default to the mode
|
|
94
|
+
if no healthcheck_mode is specified.
|
|
95
|
+
"""
|
|
96
|
+
healthcheck_mode = self.get("healthcheck_mode", None)
|
|
97
|
+
if not healthcheck_mode:
|
|
98
|
+
return self.get_mode()
|
|
99
|
+
else:
|
|
100
|
+
return healthcheck_mode
|
|
101
|
+
|
|
102
|
+
def get_mode(self) -> str:
|
|
103
|
+
"""Get the mode that the service runs in and check that we support it.
|
|
104
|
+
If the mode is not specified, we check whether the service uses smartstack
|
|
105
|
+
in order to determine the appropriate default value. If proxy_port is specified
|
|
106
|
+
in the config, the service uses smartstack, and we can thus safely assume its mode is http.
|
|
107
|
+
If the mode is not defined and the service does not use smartstack, we set the mode to None.
|
|
108
|
+
"""
|
|
109
|
+
mode = self.get("mode", None)
|
|
110
|
+
if mode is None:
|
|
111
|
+
if not self.is_in_smartstack():
|
|
112
|
+
return None
|
|
113
|
+
else:
|
|
114
|
+
return "http"
|
|
115
|
+
elif mode in ["http", "tcp", "https"]:
|
|
116
|
+
return mode
|
|
117
|
+
else:
|
|
118
|
+
raise InvalidSmartstackMode("Unknown mode: %s" % mode)
|
|
119
|
+
|
|
120
|
+
def get_healthcheck_uri(self) -> str:
|
|
121
|
+
return self.get("healthcheck_uri", "/status")
|
|
122
|
+
|
|
123
|
+
def get_discover(self) -> str:
|
|
124
|
+
return self.get("discover", "region")
|
|
125
|
+
|
|
126
|
+
def is_in_smartstack(self) -> bool:
|
|
127
|
+
return "proxy_port" in self
|
|
128
|
+
|
|
129
|
+
def get_timeout_server_ms(self) -> int:
|
|
130
|
+
return self.get("timeout_server_ms", 1000)
|
|
131
|
+
|
|
132
|
+
def get_longest_timeout_ms(self) -> int:
|
|
133
|
+
"""Calculate the longest amount of time a connection to this service might stay open."""
|
|
134
|
+
return max(
|
|
135
|
+
[self.get_timeout_server_ms()]
|
|
136
|
+
+ list(self.get("endpoint_timeouts", {}).values())
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class LongRunningServiceConfig(InstanceConfig):
|
|
141
|
+
config_dict: LongRunningServiceConfigDict
|
|
142
|
+
|
|
143
|
+
def __init__(
|
|
144
|
+
self,
|
|
145
|
+
service: str,
|
|
146
|
+
cluster: str,
|
|
147
|
+
instance: str,
|
|
148
|
+
config_dict: LongRunningServiceConfigDict,
|
|
149
|
+
branch_dict: Optional[BranchDictV2],
|
|
150
|
+
soa_dir: str = DEFAULT_SOA_DIR,
|
|
151
|
+
) -> None:
|
|
152
|
+
super().__init__(
|
|
153
|
+
cluster=cluster,
|
|
154
|
+
instance=instance,
|
|
155
|
+
service=service,
|
|
156
|
+
config_dict=config_dict,
|
|
157
|
+
branch_dict=branch_dict,
|
|
158
|
+
soa_dir=soa_dir,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def get_bounce_method(self) -> str:
|
|
162
|
+
raise NotImplementedError
|
|
163
|
+
|
|
164
|
+
def get_kubernetes_namespace(self) -> str:
|
|
165
|
+
"""
|
|
166
|
+
Only needed on kubernetes LongRunningServiceConfig
|
|
167
|
+
"""
|
|
168
|
+
raise NotImplementedError
|
|
169
|
+
|
|
170
|
+
def get_sanitised_deployment_name(self) -> str:
|
|
171
|
+
"""
|
|
172
|
+
Only needed on kubernetes LongRunningServiceConfig
|
|
173
|
+
"""
|
|
174
|
+
raise NotImplementedError
|
|
175
|
+
|
|
176
|
+
def get_service_name_smartstack(self) -> str:
|
|
177
|
+
"""
|
|
178
|
+
This is just the service name here
|
|
179
|
+
For cassandra we have to override this to support apollo
|
|
180
|
+
"""
|
|
181
|
+
return self.get_service()
|
|
182
|
+
|
|
183
|
+
def get_env(
|
|
184
|
+
self, system_paasta_config: Optional[SystemPaastaConfig] = None
|
|
185
|
+
) -> Dict[str, str]:
|
|
186
|
+
env = super().get_env(system_paasta_config=system_paasta_config)
|
|
187
|
+
env["PAASTA_PORT"] = str(self.get_container_port())
|
|
188
|
+
return env
|
|
189
|
+
|
|
190
|
+
def get_container_port(self) -> int:
|
|
191
|
+
return self.config_dict.get("container_port", DEFAULT_CONTAINER_PORT)
|
|
192
|
+
|
|
193
|
+
def get_drain_method(self, service_namespace_config: ServiceNamespaceConfig) -> str:
|
|
194
|
+
"""Get the drain method specified in the service's configuration.
|
|
195
|
+
|
|
196
|
+
:param service_config: The service instance's configuration dictionary
|
|
197
|
+
:returns: The drain method specified in the config, or 'noop' if not specified"""
|
|
198
|
+
default = "noop"
|
|
199
|
+
# Default to hacheck draining if the service is in smartstack
|
|
200
|
+
if service_namespace_config.is_in_smartstack():
|
|
201
|
+
default = "hacheck"
|
|
202
|
+
return self.config_dict.get("drain_method", default)
|
|
203
|
+
|
|
204
|
+
def get_drain_method_params(
|
|
205
|
+
self, service_namespace_config: ServiceNamespaceConfig
|
|
206
|
+
) -> Dict:
|
|
207
|
+
"""Get the drain method parameters specified in the service's configuration.
|
|
208
|
+
|
|
209
|
+
:param service_config: The service instance's configuration dictionary
|
|
210
|
+
:returns: The drain_method_params dictionary specified in the config, or {} if not specified"""
|
|
211
|
+
default: Dict = {}
|
|
212
|
+
if service_namespace_config.is_in_smartstack():
|
|
213
|
+
default = {"delay": 60}
|
|
214
|
+
return self.config_dict.get("drain_method_params", default)
|
|
215
|
+
|
|
216
|
+
# FIXME(jlynch|2016-08-02, PAASTA-4964): DEPRECATE nerve_ns and remove it
|
|
217
|
+
def get_nerve_namespace(self) -> str:
|
|
218
|
+
return decompose_job_id(self.get_registrations()[0])[1]
|
|
219
|
+
|
|
220
|
+
def get_registrations(self) -> List[str]:
|
|
221
|
+
for registration in self.get_invalid_registrations():
|
|
222
|
+
log.error(
|
|
223
|
+
"Provided registration {} for service "
|
|
224
|
+
"{} is invalid".format(registration, self.service)
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
registrations = self.config_dict.get("registrations", [])
|
|
228
|
+
|
|
229
|
+
# Backwards compatibility with nerve_ns
|
|
230
|
+
# FIXME(jlynch|2016-08-02, PAASTA-4964): DEPRECATE nerve_ns and remove it
|
|
231
|
+
if not registrations and "nerve_ns" in self.config_dict:
|
|
232
|
+
registrations.append(
|
|
233
|
+
compose_job_id(self.service, self.config_dict["nerve_ns"])
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
return registrations or [compose_job_id(self.service, self.instance)]
|
|
237
|
+
|
|
238
|
+
def get_invalid_registrations(self) -> List[str]:
|
|
239
|
+
registrations = self.config_dict.get("registrations", [])
|
|
240
|
+
invalid_registrations: List[str] = []
|
|
241
|
+
for registration in registrations:
|
|
242
|
+
try:
|
|
243
|
+
decompose_job_id(registration)
|
|
244
|
+
except InvalidJobNameError:
|
|
245
|
+
invalid_registrations.append(registration)
|
|
246
|
+
return invalid_registrations
|
|
247
|
+
|
|
248
|
+
def get_replication_crit_percentage(self) -> int:
|
|
249
|
+
return self.config_dict.get("replication_threshold", 50)
|
|
250
|
+
|
|
251
|
+
def get_fs_group(self) -> Optional[int]:
|
|
252
|
+
return self.config_dict.get("fs_group")
|
|
253
|
+
|
|
254
|
+
def get_healthcheck_uri(
|
|
255
|
+
self, service_namespace_config: ServiceNamespaceConfig
|
|
256
|
+
) -> str:
|
|
257
|
+
return self.config_dict.get(
|
|
258
|
+
"healthcheck_uri", service_namespace_config.get_healthcheck_uri()
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
def get_healthcheck_cmd(self) -> str:
|
|
262
|
+
cmd = self.config_dict.get("healthcheck_cmd", None)
|
|
263
|
+
if cmd is None:
|
|
264
|
+
raise InvalidInstanceConfig(
|
|
265
|
+
"healthcheck mode 'cmd' requires a healthcheck_cmd to run"
|
|
266
|
+
)
|
|
267
|
+
else:
|
|
268
|
+
return cmd
|
|
269
|
+
|
|
270
|
+
def get_healthcheck_grace_period_seconds(self) -> float:
|
|
271
|
+
"""
|
|
272
|
+
How long before kubernetes will start sending healthcheck and liveness probes.
|
|
273
|
+
"""
|
|
274
|
+
return self.config_dict.get("healthcheck_grace_period_seconds", 60)
|
|
275
|
+
|
|
276
|
+
def get_healthcheck_interval_seconds(self) -> float:
|
|
277
|
+
return self.config_dict.get("healthcheck_interval_seconds", 10)
|
|
278
|
+
|
|
279
|
+
def get_healthcheck_timeout_seconds(self) -> float:
|
|
280
|
+
return self.config_dict.get("healthcheck_timeout_seconds", 10)
|
|
281
|
+
|
|
282
|
+
def get_healthcheck_max_consecutive_failures(self) -> int:
|
|
283
|
+
return self.config_dict.get("healthcheck_max_consecutive_failures", 30)
|
|
284
|
+
|
|
285
|
+
def get_healthcheck_mode(
|
|
286
|
+
self, service_namespace_config: ServiceNamespaceConfig
|
|
287
|
+
) -> str:
|
|
288
|
+
mode = self.config_dict.get("healthcheck_mode", None)
|
|
289
|
+
if mode is None:
|
|
290
|
+
mode = service_namespace_config.get_healthcheck_mode()
|
|
291
|
+
elif mode not in ["http", "https", "tcp", "cmd", None]:
|
|
292
|
+
raise InvalidHealthcheckMode("Unknown mode: %s" % mode)
|
|
293
|
+
return mode
|
|
294
|
+
|
|
295
|
+
def get_bounce_start_deadline(self) -> float:
|
|
296
|
+
return self.config_dict.get("bounce_start_deadline", 0)
|
|
297
|
+
|
|
298
|
+
def get_autoscaled_instances(self) -> int:
|
|
299
|
+
raise NotImplementedError()
|
|
300
|
+
|
|
301
|
+
def get_instances(self, with_limit: bool = True) -> int:
|
|
302
|
+
"""Gets the number of instances for a service, ignoring whether the user has requested
|
|
303
|
+
the service to be started or stopped"""
|
|
304
|
+
if self.is_autoscaling_enabled():
|
|
305
|
+
autoscaled_instances = self.get_autoscaled_instances()
|
|
306
|
+
if autoscaled_instances is None:
|
|
307
|
+
return self.get_max_instances()
|
|
308
|
+
else:
|
|
309
|
+
limited_instances = (
|
|
310
|
+
self.limit_instance_count(autoscaled_instances)
|
|
311
|
+
if with_limit
|
|
312
|
+
else autoscaled_instances
|
|
313
|
+
)
|
|
314
|
+
return limited_instances
|
|
315
|
+
else:
|
|
316
|
+
instances = self.config_dict.get("instances", 1)
|
|
317
|
+
log.debug("Autoscaling not enabled, returning %d instances" % instances)
|
|
318
|
+
return instances
|
|
319
|
+
|
|
320
|
+
def get_min_instances(self) -> int:
|
|
321
|
+
return self.config_dict.get("min_instances", 1)
|
|
322
|
+
|
|
323
|
+
def is_autoscaling_enabled(self) -> bool:
|
|
324
|
+
return self.get_max_instances() is not None
|
|
325
|
+
|
|
326
|
+
def get_max_instances(self) -> Optional[int]:
|
|
327
|
+
return self.config_dict.get("max_instances", None)
|
|
328
|
+
|
|
329
|
+
def get_desired_instances(self) -> int:
|
|
330
|
+
"""Get the number of instances specified in zookeeper or the service's configuration.
|
|
331
|
+
If the number of instances in zookeeper is less than min_instances, returns min_instances.
|
|
332
|
+
If the number of instances in zookeeper is greater than max_instances, returns max_instances.
|
|
333
|
+
|
|
334
|
+
Defaults to 0 if not specified in the config.
|
|
335
|
+
|
|
336
|
+
:returns: The number of instances specified in the config, 0 if not
|
|
337
|
+
specified or if desired_state is not 'start'.
|
|
338
|
+
"""
|
|
339
|
+
if self.get_desired_state() == "start":
|
|
340
|
+
return self.get_instances()
|
|
341
|
+
else:
|
|
342
|
+
log.debug("Instance is set to stop. Returning '0' instances")
|
|
343
|
+
return 0
|
|
344
|
+
|
|
345
|
+
def limit_instance_count(self, instances: int) -> int:
|
|
346
|
+
"""
|
|
347
|
+
Returns param instances if it is between min_instances and max_instances.
|
|
348
|
+
Returns max_instances if instances > max_instances
|
|
349
|
+
Returns min_instances if instances < min_instances
|
|
350
|
+
"""
|
|
351
|
+
return max(self.get_min_instances(), min(self.get_max_instances(), instances))
|
|
352
|
+
|
|
353
|
+
def get_autoscaling_params(self) -> AutoscalingParamsDict:
|
|
354
|
+
default_provider_params: MetricsProviderDict = {
|
|
355
|
+
"type": METRICS_PROVIDER_CPU,
|
|
356
|
+
"decision_policy": "proportional",
|
|
357
|
+
"setpoint": DEFAULT_AUTOSCALING_SETPOINT,
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
params = copy.deepcopy(
|
|
361
|
+
self.config_dict.get("autoscaling", AutoscalingParamsDict({}))
|
|
362
|
+
)
|
|
363
|
+
if "metrics_providers" not in params or len(params["metrics_providers"]) == 0:
|
|
364
|
+
params["metrics_providers"] = [default_provider_params]
|
|
365
|
+
else:
|
|
366
|
+
params["metrics_providers"] = [
|
|
367
|
+
deep_merge_dictionaries(
|
|
368
|
+
overrides=provider,
|
|
369
|
+
defaults=default_provider_params,
|
|
370
|
+
)
|
|
371
|
+
for provider in params["metrics_providers"]
|
|
372
|
+
]
|
|
373
|
+
return params
|
|
374
|
+
|
|
375
|
+
def get_autoscaling_metrics_provider(
|
|
376
|
+
self, provider_type: str
|
|
377
|
+
) -> Optional[MetricsProviderDict]:
|
|
378
|
+
autoscaling_params = self.get_autoscaling_params()
|
|
379
|
+
# We only allow one metric provider of each type, so we can bail early if we find a match
|
|
380
|
+
for provider in autoscaling_params["metrics_providers"]:
|
|
381
|
+
if provider["type"] == provider_type:
|
|
382
|
+
return provider
|
|
383
|
+
return None
|
|
384
|
+
|
|
385
|
+
def should_use_metrics_provider(self, provider_type: str) -> bool:
|
|
386
|
+
return (
|
|
387
|
+
self.is_autoscaling_enabled()
|
|
388
|
+
and self.get_autoscaling_metrics_provider(provider_type) is not None
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
def validate(
|
|
392
|
+
self,
|
|
393
|
+
params: Optional[List[str]] = None,
|
|
394
|
+
) -> List[str]:
|
|
395
|
+
error_messages = super().validate(params=params)
|
|
396
|
+
invalid_registrations = self.get_invalid_registrations()
|
|
397
|
+
if invalid_registrations:
|
|
398
|
+
service_instance = compose_job_id(self.service, self.instance)
|
|
399
|
+
registrations_str = ", ".join(invalid_registrations)
|
|
400
|
+
error_messages.append(
|
|
401
|
+
f"Service registrations must be of the form service.registration. "
|
|
402
|
+
f"The following registrations for {service_instance} are "
|
|
403
|
+
f"invalid: {registrations_str}"
|
|
404
|
+
)
|
|
405
|
+
return error_messages
|
|
406
|
+
|
|
407
|
+
def get_bounce_margin_factor(self) -> float:
|
|
408
|
+
return self.config_dict.get("bounce_margin_factor", 0.95)
|
|
409
|
+
|
|
410
|
+
def get_should_ping_for_unhealthy_pods(self, default: bool) -> bool:
|
|
411
|
+
return self.config_dict.get("should_ping_for_unhealthy_pods", default)
|
|
412
|
+
|
|
413
|
+
def get_weight(self) -> int:
|
|
414
|
+
return self.config_dict.get("weight", 10)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class InvalidHealthcheckMode(Exception):
|
|
418
|
+
pass
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def get_healthcheck_for_instance(
|
|
422
|
+
service: str,
|
|
423
|
+
instance: str,
|
|
424
|
+
service_manifest: LongRunningServiceConfig,
|
|
425
|
+
random_port: int,
|
|
426
|
+
soa_dir: str = DEFAULT_SOA_DIR,
|
|
427
|
+
) -> Tuple[Optional[str], Optional[str]]:
|
|
428
|
+
"""
|
|
429
|
+
Returns healthcheck for a given service instance in the form of a tuple (mode, healthcheck_command)
|
|
430
|
+
or (None, None) if no healthcheck
|
|
431
|
+
"""
|
|
432
|
+
namespace = service_manifest.get_nerve_namespace()
|
|
433
|
+
smartstack_config = load_service_namespace_config(
|
|
434
|
+
service=service, namespace=namespace, soa_dir=soa_dir
|
|
435
|
+
)
|
|
436
|
+
mode = service_manifest.get_healthcheck_mode(smartstack_config)
|
|
437
|
+
hostname = socket.getfqdn()
|
|
438
|
+
|
|
439
|
+
if mode == "http" or mode == "https":
|
|
440
|
+
path = service_manifest.get_healthcheck_uri(smartstack_config)
|
|
441
|
+
healthcheck_command = "%s://%s:%d%s" % (mode, hostname, random_port, path)
|
|
442
|
+
elif mode == "tcp":
|
|
443
|
+
healthcheck_command = "%s://%s:%d" % (mode, hostname, random_port)
|
|
444
|
+
elif mode == "cmd":
|
|
445
|
+
healthcheck_command = service_manifest.get_healthcheck_cmd()
|
|
446
|
+
else:
|
|
447
|
+
mode = None
|
|
448
|
+
healthcheck_command = None
|
|
449
|
+
return (mode, healthcheck_command)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def load_service_namespace_config(
|
|
453
|
+
service: str, namespace: str, soa_dir: str = DEFAULT_SOA_DIR
|
|
454
|
+
) -> ServiceNamespaceConfig:
|
|
455
|
+
"""Attempt to read the configuration for a service's namespace in a more strict fashion.
|
|
456
|
+
|
|
457
|
+
Retrieves the following keys:
|
|
458
|
+
|
|
459
|
+
- proxy_port: the proxy port defined for the given namespace
|
|
460
|
+
- healthcheck_mode: the mode for the healthcheck (http or tcp)
|
|
461
|
+
- healthcheck_port: An alternate port to use for health checking
|
|
462
|
+
- healthcheck_uri: URI target for healthchecking
|
|
463
|
+
- healthcheck_timeout_s: healthcheck timeout in seconds
|
|
464
|
+
- healthcheck_body_expect: an expected string in healthcheck response body
|
|
465
|
+
- updown_timeout_s: updown_service timeout in seconds
|
|
466
|
+
- timeout_connect_ms: proxy frontend timeout in milliseconds
|
|
467
|
+
- timeout_server_ms: proxy server backend timeout in milliseconds
|
|
468
|
+
- retries: the number of retries on a proxy backend
|
|
469
|
+
- mode: the mode the service is run in (http or tcp)
|
|
470
|
+
- routes: a list of tuples of (source, destination)
|
|
471
|
+
- discover: the scope at which to discover services e.g. 'habitat'
|
|
472
|
+
- advertise: a list of scopes to advertise services at e.g. ['habitat', 'region']
|
|
473
|
+
- extra_advertise: a list of tuples of (source, destination)
|
|
474
|
+
e.g. [('region:dc6-prod', 'region:useast1-prod')]
|
|
475
|
+
- extra_healthcheck_headers: a dict of HTTP headers that must
|
|
476
|
+
be supplied when health checking. E.g. { 'Host': 'example.com' }
|
|
477
|
+
- lb_policy: Envoy load balancer policies. E.g. "ROUND_ROBIN"
|
|
478
|
+
|
|
479
|
+
:param service: The service name
|
|
480
|
+
:param namespace: The namespace to read
|
|
481
|
+
:param soa_dir: The SOA config directory to read from
|
|
482
|
+
:returns: A dict of the above keys, if they were defined
|
|
483
|
+
"""
|
|
484
|
+
|
|
485
|
+
smartstack_config = service_configuration_lib.read_extra_service_information(
|
|
486
|
+
service_name=service,
|
|
487
|
+
extra_info="smartstack",
|
|
488
|
+
soa_dir=soa_dir,
|
|
489
|
+
deepcopy=False,
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
namespace_config_from_file = smartstack_config.get(namespace, {})
|
|
493
|
+
|
|
494
|
+
service_namespace_config = ServiceNamespaceConfig()
|
|
495
|
+
# We can't really use .get, as we don't want the key to be in the returned
|
|
496
|
+
# dict at all if it doesn't exist in the config file.
|
|
497
|
+
# We also can't just copy the whole dict, as we only care about some keys
|
|
498
|
+
# and there's other things that appear in the smartstack section in
|
|
499
|
+
# several cases.
|
|
500
|
+
key_whitelist = {
|
|
501
|
+
"healthcheck_mode",
|
|
502
|
+
"healthcheck_uri",
|
|
503
|
+
"healthcheck_port",
|
|
504
|
+
"healthcheck_timeout_s",
|
|
505
|
+
"healthcheck_body_expect",
|
|
506
|
+
"updown_timeout_s",
|
|
507
|
+
"proxy_port",
|
|
508
|
+
"timeout_connect_ms",
|
|
509
|
+
"timeout_server_ms",
|
|
510
|
+
"retries",
|
|
511
|
+
"mode",
|
|
512
|
+
"discover",
|
|
513
|
+
"advertise",
|
|
514
|
+
"extra_healthcheck_headers",
|
|
515
|
+
"lb_policy",
|
|
516
|
+
"endpoint_timeouts",
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
for key, value in namespace_config_from_file.items():
|
|
520
|
+
if key in key_whitelist:
|
|
521
|
+
service_namespace_config[key] = value
|
|
522
|
+
|
|
523
|
+
# Other code in paasta_tools checks 'mode' after the config file
|
|
524
|
+
# is loaded, so this ensures that it is set to the appropriate default
|
|
525
|
+
# if not otherwise specified, even if appropriate default is None.
|
|
526
|
+
service_namespace_config["mode"] = service_namespace_config.get_mode()
|
|
527
|
+
|
|
528
|
+
if "routes" in namespace_config_from_file:
|
|
529
|
+
service_namespace_config["routes"] = [
|
|
530
|
+
(route["source"], dest)
|
|
531
|
+
for route in namespace_config_from_file["routes"]
|
|
532
|
+
for dest in route["destinations"]
|
|
533
|
+
]
|
|
534
|
+
|
|
535
|
+
if "extra_advertise" in namespace_config_from_file:
|
|
536
|
+
service_namespace_config["extra_advertise"] = [
|
|
537
|
+
(src, dst)
|
|
538
|
+
for src in namespace_config_from_file["extra_advertise"]
|
|
539
|
+
for dst in namespace_config_from_file["extra_advertise"][src]
|
|
540
|
+
]
|
|
541
|
+
|
|
542
|
+
return service_namespace_config
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
class InvalidSmartstackMode(Exception):
|
|
546
|
+
pass
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def get_proxy_port_for_instance(
|
|
550
|
+
service_config: LongRunningServiceConfig,
|
|
551
|
+
) -> Optional[int]:
|
|
552
|
+
"""Get the proxy_port defined in the first namespace configuration for a
|
|
553
|
+
service instance.
|
|
554
|
+
|
|
555
|
+
This means that the namespace first has to be loaded from the service instance's
|
|
556
|
+
configuration, and then the proxy_port has to loaded from the smartstack configuration
|
|
557
|
+
for that namespace.
|
|
558
|
+
|
|
559
|
+
:param service_config: The instance of the services LongRunningServiceConfig
|
|
560
|
+
:returns: The proxy_port for the service instance, or None if not defined"""
|
|
561
|
+
registration = service_config.get_registrations()[0]
|
|
562
|
+
service, namespace, _, __ = decompose_job_id(registration)
|
|
563
|
+
nerve_dict = load_service_namespace_config(
|
|
564
|
+
service=service, namespace=namespace, soa_dir=service_config.soa_dir
|
|
565
|
+
)
|
|
566
|
+
return nerve_dict.get("proxy_port")
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def host_passes_blacklist(
|
|
570
|
+
host_attributes: Mapping[str, str], blacklist: DeployBlacklist
|
|
571
|
+
) -> bool:
|
|
572
|
+
"""
|
|
573
|
+
:param host: A single host attributes dict
|
|
574
|
+
:param blacklist: A list of lists like [["location_type", "location"], ["foo", "bar"]]
|
|
575
|
+
:returns: boolean, True if the host gets passed the blacklist
|
|
576
|
+
"""
|
|
577
|
+
try:
|
|
578
|
+
for location_type, location in blacklist:
|
|
579
|
+
if host_attributes.get(location_type) == location:
|
|
580
|
+
return False
|
|
581
|
+
except ValueError as e:
|
|
582
|
+
log.error(f"Errors processing the following blacklist: {blacklist}")
|
|
583
|
+
log.error("I will assume the host does not pass\nError was: %s" % e)
|
|
584
|
+
return False
|
|
585
|
+
return True
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def host_passes_whitelist(
|
|
589
|
+
host_attributes: Mapping[str, str], whitelist: DeployWhitelist
|
|
590
|
+
) -> bool:
|
|
591
|
+
"""
|
|
592
|
+
:param host: A single host attributes dict.
|
|
593
|
+
:param whitelist: A 2 item list like ["location_type", ["location1", 'location2']]
|
|
594
|
+
:returns: boolean, True if the host gets past the whitelist
|
|
595
|
+
"""
|
|
596
|
+
# No whitelist, so disable whitelisting behavior.
|
|
597
|
+
if whitelist is None or len(whitelist) == 0:
|
|
598
|
+
return True
|
|
599
|
+
try:
|
|
600
|
+
(location_type, locations) = whitelist
|
|
601
|
+
if host_attributes.get(location_type) in locations:
|
|
602
|
+
return True
|
|
603
|
+
except ValueError as e:
|
|
604
|
+
log.error(f"Errors processing the following whitelist: {whitelist}")
|
|
605
|
+
log.error("I will assume the host does not pass\nError was: %s" % e)
|
|
606
|
+
return False
|
|
607
|
+
return False
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def get_all_namespaces(
|
|
611
|
+
soa_dir: str = DEFAULT_SOA_DIR,
|
|
612
|
+
) -> Sequence[Tuple[str, ServiceNamespaceConfig]]:
|
|
613
|
+
"""Get all the smartstack namespaces across all services.
|
|
614
|
+
This is mostly so synapse can get everything it needs in one call.
|
|
615
|
+
|
|
616
|
+
:param soa_dir: The SOA config directory to read from
|
|
617
|
+
:returns: A list of tuples of the form (service.namespace, namespace_config)"""
|
|
618
|
+
rootdir = os.path.abspath(soa_dir)
|
|
619
|
+
namespace_list: List[Tuple[str, ServiceNamespaceConfig]] = []
|
|
620
|
+
for srv_dir in os.listdir(rootdir):
|
|
621
|
+
namespace_list.extend(get_all_namespaces_for_service(srv_dir, soa_dir))
|
|
622
|
+
return namespace_list
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def get_all_namespaces_for_service(
|
|
626
|
+
service: str, soa_dir: str = DEFAULT_SOA_DIR, full_name: bool = True
|
|
627
|
+
) -> Sequence[Tuple[str, ServiceNamespaceConfig]]:
|
|
628
|
+
"""Get all the smartstack namespaces listed for a given service name.
|
|
629
|
+
|
|
630
|
+
:param service: The service name
|
|
631
|
+
:param soa_dir: The SOA config directory to read from
|
|
632
|
+
:param full_name: A boolean indicating if the service name should be prepended to the namespace in the
|
|
633
|
+
returned tuples as described below (Default: True)
|
|
634
|
+
:returns: A list of tuples of the form (service<SPACER>namespace, namespace_config) if full_name is true,
|
|
635
|
+
otherwise of the form (namespace, namespace_config)
|
|
636
|
+
"""
|
|
637
|
+
service_config = service_configuration_lib.read_service_configuration(
|
|
638
|
+
service, soa_dir
|
|
639
|
+
)
|
|
640
|
+
smartstack = service_config.get("smartstack", {})
|
|
641
|
+
namespace_list = []
|
|
642
|
+
for namespace in smartstack:
|
|
643
|
+
if full_name:
|
|
644
|
+
name = compose_job_id(service, namespace)
|
|
645
|
+
else:
|
|
646
|
+
name = namespace
|
|
647
|
+
namespace_list.append((name, smartstack[namespace]))
|
|
648
|
+
return namespace_list
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def get_expected_instance_count_for_namespace(
|
|
652
|
+
service: str,
|
|
653
|
+
namespace: str,
|
|
654
|
+
instance_type_class: Type[LongRunningServiceConfig],
|
|
655
|
+
cluster: str = None,
|
|
656
|
+
soa_dir: str = DEFAULT_SOA_DIR,
|
|
657
|
+
) -> int:
|
|
658
|
+
"""Get the number of expected instances for a namespace, based on the number
|
|
659
|
+
of instances set to run on that namespace as specified in service configuration files.
|
|
660
|
+
|
|
661
|
+
:param service: The service's name
|
|
662
|
+
:param namespace: The namespace for that service to check
|
|
663
|
+
instance_type_class: The type of the instance, options are e.g. KubernetesDeploymentConfig,
|
|
664
|
+
:param soa_dir: The SOA configuration directory to read from
|
|
665
|
+
:returns: An integer value of the # of expected instances for the namespace"""
|
|
666
|
+
total_expected = 0
|
|
667
|
+
if not cluster:
|
|
668
|
+
cluster = load_system_paasta_config().get_cluster()
|
|
669
|
+
|
|
670
|
+
pscl = PaastaServiceConfigLoader(
|
|
671
|
+
service=service, soa_dir=soa_dir, load_deployments=False
|
|
672
|
+
)
|
|
673
|
+
for job_config in pscl.instance_configs(
|
|
674
|
+
cluster=cluster, instance_type_class=instance_type_class
|
|
675
|
+
):
|
|
676
|
+
if f"{service}.{namespace}" in job_config.get_registrations():
|
|
677
|
+
total_expected += job_config.get_instances()
|
|
678
|
+
return total_expected
|