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,86 @@
|
|
|
1
|
+
#!/usr/bin/env python3.8
|
|
2
|
+
import ast
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MockChecker(ast.NodeVisitor):
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.errors = 0
|
|
9
|
+
self.init_module_imports()
|
|
10
|
+
|
|
11
|
+
def init_module_imports(self):
|
|
12
|
+
self.imported_patch = False
|
|
13
|
+
self.imported_mock = False
|
|
14
|
+
|
|
15
|
+
def check_files(self, files):
|
|
16
|
+
for file in files:
|
|
17
|
+
self.check_file(file)
|
|
18
|
+
|
|
19
|
+
def check_file(self, filename):
|
|
20
|
+
self.current_filename = filename
|
|
21
|
+
try:
|
|
22
|
+
with open(filename, "r") as fd:
|
|
23
|
+
try:
|
|
24
|
+
file_ast = ast.parse(fd.read())
|
|
25
|
+
except SyntaxError as error:
|
|
26
|
+
print("SyntaxError on file %s:%d" % (filename, error.lineno))
|
|
27
|
+
return
|
|
28
|
+
except IOError:
|
|
29
|
+
print("Error opening filename: %s" % filename)
|
|
30
|
+
return
|
|
31
|
+
self.init_module_imports()
|
|
32
|
+
self.visit(file_ast)
|
|
33
|
+
|
|
34
|
+
def _call_uses_patch(self, node):
|
|
35
|
+
try:
|
|
36
|
+
return node.func.id == "patch"
|
|
37
|
+
except AttributeError:
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
def _call_uses_mock_patch(self, node):
|
|
41
|
+
try:
|
|
42
|
+
return node.func.value.id == "mock" and node.func.attr == "patch"
|
|
43
|
+
except AttributeError:
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
def visit_Import(self, node):
|
|
47
|
+
if [name for name in node.names if "mock" == name.name]:
|
|
48
|
+
self.imported_mock = True
|
|
49
|
+
|
|
50
|
+
def visit_ImportFrom(self, node):
|
|
51
|
+
if node.module == "mock" and (
|
|
52
|
+
name for name in node.names if "patch" == name.name
|
|
53
|
+
):
|
|
54
|
+
self.imported_patch = True
|
|
55
|
+
|
|
56
|
+
def visit_Call(self, node):
|
|
57
|
+
try:
|
|
58
|
+
if (self.imported_patch and self._call_uses_patch(node)) or (
|
|
59
|
+
self.imported_mock and self._call_uses_mock_patch(node)
|
|
60
|
+
):
|
|
61
|
+
if not any(
|
|
62
|
+
[keyword for keyword in node.keywords if keyword.arg == "autospec"]
|
|
63
|
+
):
|
|
64
|
+
print(
|
|
65
|
+
"%s:%d: Found a mock without an autospec!"
|
|
66
|
+
% (self.current_filename, node.lineno)
|
|
67
|
+
)
|
|
68
|
+
self.errors += 1
|
|
69
|
+
except AttributeError:
|
|
70
|
+
pass
|
|
71
|
+
self.generic_visit(node)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def main(filenames):
|
|
75
|
+
checker = MockChecker()
|
|
76
|
+
checker.check_files(filenames)
|
|
77
|
+
if checker.errors == 0:
|
|
78
|
+
sys.exit(0)
|
|
79
|
+
else:
|
|
80
|
+
print("You probably meant to specify 'autospec=True' in these tests.")
|
|
81
|
+
print("If you really don't want to, specify 'autospec=None'")
|
|
82
|
+
sys.exit(1)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
main(sys.argv[1:])
|
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
import contextlib
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import subprocess
|
|
8
|
+
import tempfile
|
|
9
|
+
import time
|
|
10
|
+
from http.client import HTTPConnection
|
|
11
|
+
|
|
12
|
+
import requests
|
|
13
|
+
import ruamel.yaml as yaml
|
|
14
|
+
|
|
15
|
+
from paasta_tools.utils import DEFAULT_SOA_CONFIGS_GIT_URL
|
|
16
|
+
from paasta_tools.utils import format_git_url
|
|
17
|
+
from paasta_tools.utils import load_system_paasta_config
|
|
18
|
+
|
|
19
|
+
requests_log = logging.getLogger("requests.packages.urllib3")
|
|
20
|
+
logging.basicConfig(level=logging.INFO)
|
|
21
|
+
log = logging.getLogger()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def parse_args():
|
|
25
|
+
parser = argparse.ArgumentParser(description="")
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"-s",
|
|
28
|
+
"--splunk-creds",
|
|
29
|
+
help="Service credentials for Splunk API, user:pass",
|
|
30
|
+
dest="splunk_creds",
|
|
31
|
+
required=True,
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"-f",
|
|
35
|
+
"--criteria-filter",
|
|
36
|
+
help="Filter Splunk search results criteria field. Default: *",
|
|
37
|
+
dest="criteria_filter",
|
|
38
|
+
required=False,
|
|
39
|
+
default="*",
|
|
40
|
+
)
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"-j",
|
|
43
|
+
"--jira-creds",
|
|
44
|
+
help="Service credentials for JIRA API, user:pass",
|
|
45
|
+
dest="jira_creds",
|
|
46
|
+
required=False,
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"-t",
|
|
50
|
+
"--ticket",
|
|
51
|
+
help="Create JIRA tickets for every service in corresponding project (not available in bulk mode)",
|
|
52
|
+
action="store_true",
|
|
53
|
+
dest="ticket",
|
|
54
|
+
default=False,
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"-r",
|
|
58
|
+
"--reviews",
|
|
59
|
+
help="Guess owners of each service and create reviews automatically",
|
|
60
|
+
action="store_true",
|
|
61
|
+
dest="create_reviews",
|
|
62
|
+
default=False,
|
|
63
|
+
)
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"-p",
|
|
66
|
+
"--publish-reviews",
|
|
67
|
+
help="Guess owners of each service and publish reviews automatically",
|
|
68
|
+
action="store_true",
|
|
69
|
+
dest="publish_reviews",
|
|
70
|
+
default=False,
|
|
71
|
+
)
|
|
72
|
+
parser.add_argument(
|
|
73
|
+
"-b",
|
|
74
|
+
"--bulk",
|
|
75
|
+
help="Patch all services in the report with only one code review",
|
|
76
|
+
action="store_true",
|
|
77
|
+
dest="bulk",
|
|
78
|
+
default=False,
|
|
79
|
+
)
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
"--app",
|
|
82
|
+
help="Splunk app of the CSV file",
|
|
83
|
+
default="yelp_performance",
|
|
84
|
+
required=False,
|
|
85
|
+
dest="splunk_app",
|
|
86
|
+
)
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"-c",
|
|
89
|
+
"--csv-report",
|
|
90
|
+
help="Splunk csv file from which to pull data.",
|
|
91
|
+
required=True,
|
|
92
|
+
dest="csv_report",
|
|
93
|
+
)
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
"-y",
|
|
96
|
+
"--yelpsoa-configs-dir",
|
|
97
|
+
help="Use provided existing yelpsoa-configs instead of cloning the repo in a temporary dir. Only avail with -b option",
|
|
98
|
+
dest="YELPSOA_DIR",
|
|
99
|
+
required=False,
|
|
100
|
+
)
|
|
101
|
+
parser.add_argument(
|
|
102
|
+
"-l",
|
|
103
|
+
"--local",
|
|
104
|
+
help="Do not create a branch. Implies -y and -b.",
|
|
105
|
+
action="store_true",
|
|
106
|
+
dest="no_branch",
|
|
107
|
+
default=False,
|
|
108
|
+
)
|
|
109
|
+
parser.add_argument(
|
|
110
|
+
"-v",
|
|
111
|
+
"--verbose",
|
|
112
|
+
help="Debug mode.",
|
|
113
|
+
action="store_true",
|
|
114
|
+
dest="verbose",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return parser.parse_args()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def tempdir():
|
|
121
|
+
tmp = tempfile.TemporaryDirectory(prefix="repo", dir="/nail/tmp")
|
|
122
|
+
log.debug(f"Created temp directory: {tmp.name}")
|
|
123
|
+
return tmp
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@contextlib.contextmanager
|
|
127
|
+
def cwd(path):
|
|
128
|
+
pwd = os.getcwd()
|
|
129
|
+
os.chdir(path)
|
|
130
|
+
log.debug(f"Switching from directory {pwd} to {path}")
|
|
131
|
+
try:
|
|
132
|
+
yield
|
|
133
|
+
finally:
|
|
134
|
+
log.debug(f"Switching back from directory {path} to {pwd}")
|
|
135
|
+
os.chdir(pwd)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def get_report_from_splunk(creds, app, filename, criteria_filter):
|
|
139
|
+
"""Expect a table containing at least the following fields:
|
|
140
|
+
criteria (<service> kubernetes-<cluster_name> <instance>)
|
|
141
|
+
service_owner (Optional)
|
|
142
|
+
project (Required to create tickets)
|
|
143
|
+
estimated_monthly_savings (Optional)
|
|
144
|
+
search_time (Unix time)
|
|
145
|
+
one of the following pairs:
|
|
146
|
+
- current_cpus and suggested_cpus
|
|
147
|
+
- current_mem and suggested_mem
|
|
148
|
+
- current_disk and suggested_disk
|
|
149
|
+
- suggested_hacheck_cpus
|
|
150
|
+
- suggested_cpu_burst_add
|
|
151
|
+
- suggested_min_instances
|
|
152
|
+
- suggested_max_instances
|
|
153
|
+
"""
|
|
154
|
+
url = f"https://splunk-api.yelpcorp.com/servicesNS/nobody/{app}/search/jobs/export"
|
|
155
|
+
search = (
|
|
156
|
+
'| inputlookup {filename} | search criteria="{criteria_filter}"'
|
|
157
|
+
'| eval _time = search_time | where _time > relative_time(now(),"-7d")'
|
|
158
|
+
).format(filename=filename, criteria_filter=criteria_filter)
|
|
159
|
+
log.debug(f"Sending this query to Splunk: {search}\n")
|
|
160
|
+
data = {"output_mode": "json", "search": search}
|
|
161
|
+
creds = creds.split(":")
|
|
162
|
+
resp = requests.post(url, data=data, auth=(creds[0], creds[1]))
|
|
163
|
+
resp_text = resp.text.split("\n")
|
|
164
|
+
log.info("Found {} services to rightsize".format(len(resp_text) - 1))
|
|
165
|
+
resp_text = [x for x in resp_text if x]
|
|
166
|
+
resp_text = [json.loads(x) for x in resp_text]
|
|
167
|
+
services_to_update = {}
|
|
168
|
+
for d in resp_text:
|
|
169
|
+
if "result" not in d:
|
|
170
|
+
raise ValueError(f"Splunk request didn't return any results: {resp_text}")
|
|
171
|
+
criteria = d["result"]["criteria"]
|
|
172
|
+
serv = {}
|
|
173
|
+
serv["service"] = criteria.split(" ")[0]
|
|
174
|
+
serv["cluster"] = criteria.split(" ")[1]
|
|
175
|
+
serv["instance"] = criteria.split(" ")[2]
|
|
176
|
+
serv["owner"] = d["result"].get("service_owner", "Unavailable")
|
|
177
|
+
serv["date"] = d["result"]["_time"].split(" ")[0]
|
|
178
|
+
serv["money"] = d["result"].get("estimated_monthly_savings", 0)
|
|
179
|
+
serv["project"] = d["result"].get("project", "Unavailable")
|
|
180
|
+
serv["cpus"] = d["result"].get("suggested_cpus")
|
|
181
|
+
serv["old_cpus"] = d["result"].get("current_cpus")
|
|
182
|
+
serv["mem"] = d["result"].get("suggested_mem")
|
|
183
|
+
serv["old_mem"] = d["result"].get("current_mem")
|
|
184
|
+
serv["disk"] = d["result"].get("suggested_disk")
|
|
185
|
+
serv["old_disk"] = d["result"].get("current_disk")
|
|
186
|
+
serv["min_instances"] = d["result"].get("suggested_min_instances")
|
|
187
|
+
serv["max_instances"] = d["result"].get("suggested_max_instances")
|
|
188
|
+
serv["hacheck_cpus"] = d["result"].get("suggested_hacheck_cpus")
|
|
189
|
+
serv["cpu_burst_add"] = d["result"].get("suggested_cpu_burst_add")
|
|
190
|
+
services_to_update[criteria] = serv
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
"search": search,
|
|
194
|
+
"results": services_to_update,
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def clone_in(target_dir, system_paasta_config=None):
|
|
199
|
+
if not system_paasta_config:
|
|
200
|
+
system_paasta_config = load_system_paasta_config()
|
|
201
|
+
repo_config = system_paasta_config.get_git_repo_config("yelpsoa-configs")
|
|
202
|
+
|
|
203
|
+
remote = format_git_url(
|
|
204
|
+
system_paasta_config.get_git_config()["git_user"],
|
|
205
|
+
repo_config.get("git_server", DEFAULT_SOA_CONFIGS_GIT_URL),
|
|
206
|
+
repo_config["repo_name"],
|
|
207
|
+
)
|
|
208
|
+
subprocess.check_call(("git", "clone", remote, target_dir))
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def create_branch(branch_name):
|
|
212
|
+
subprocess.check_call(("git", "checkout", "master"))
|
|
213
|
+
subprocess.check_call(("git", "checkout", "-b", branch_name))
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def bulk_commit(filenames, originating_search):
|
|
217
|
+
message = f"Rightsizer bulk update\n\nSplunk search:\n{originating_search}"
|
|
218
|
+
subprocess.check_call(["git", "add"] + filenames)
|
|
219
|
+
subprocess.check_call(("git", "commit", "-n", "-m", message))
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def bulk_review(filenames, originating_search, publish=False):
|
|
223
|
+
reviewers = set(get_reviewers_in_group("right-sizer"))
|
|
224
|
+
for filename in filenames:
|
|
225
|
+
reviewers = reviewers.union(get_reviewers(filename))
|
|
226
|
+
|
|
227
|
+
reviewers_arg = " ".join(list(reviewers))
|
|
228
|
+
summary = "Rightsizer bulk update"
|
|
229
|
+
description = (
|
|
230
|
+
"This is an automated bulk review. It will be shipped automatically if a primary reviewer gives a shipit. If you think this should not be shipped, talk to one of the primary reviewers. \n\n"
|
|
231
|
+
"This review is based on results from the following Splunk search:\n"
|
|
232
|
+
f"{originating_search}"
|
|
233
|
+
)
|
|
234
|
+
review_cmd = [
|
|
235
|
+
"review-branch",
|
|
236
|
+
f"--summary={summary}",
|
|
237
|
+
f"--description={description}",
|
|
238
|
+
"--reviewers",
|
|
239
|
+
reviewers_arg,
|
|
240
|
+
"--server",
|
|
241
|
+
"https://reviewboard.yelpcorp.com",
|
|
242
|
+
]
|
|
243
|
+
if publish:
|
|
244
|
+
review_cmd.append("-p")
|
|
245
|
+
|
|
246
|
+
subprocess.check_call(review_cmd)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def commit(filename, serv):
|
|
250
|
+
message = "Updating {} for {}provisioned cpu from {} to {} cpus".format(
|
|
251
|
+
filename, serv["state"], serv["old_cpus"], serv["cpus"]
|
|
252
|
+
)
|
|
253
|
+
log.debug(f"Commit {filename} with the following message: {message}")
|
|
254
|
+
subprocess.check_call(("git", "add", filename))
|
|
255
|
+
subprocess.check_call(("git", "commit", "-n", "-m", message))
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def get_reviewers_in_group(group_name):
|
|
259
|
+
"""Using rbt's target-groups argument overrides our configured default review groups.
|
|
260
|
+
So we'll expand the group into usernames and pass those users in the group individually.
|
|
261
|
+
"""
|
|
262
|
+
rightsizer_reviewers = json.loads(
|
|
263
|
+
subprocess.check_output(
|
|
264
|
+
(
|
|
265
|
+
"rbt",
|
|
266
|
+
"api-get",
|
|
267
|
+
"--server",
|
|
268
|
+
"https://reviewboard.yelpcorp.com",
|
|
269
|
+
f"groups/{group_name}/users/",
|
|
270
|
+
)
|
|
271
|
+
).decode("UTF-8")
|
|
272
|
+
)
|
|
273
|
+
return [user.get("username", "") for user in rightsizer_reviewers.get("users", {})]
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def get_reviewers(filename):
|
|
277
|
+
recent_authors = set()
|
|
278
|
+
authors = (
|
|
279
|
+
subprocess.check_output(("git", "log", "--format=%ae", "--", filename))
|
|
280
|
+
.decode("UTF-8")
|
|
281
|
+
.splitlines()
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
authors = [x.split("@")[0] for x in authors]
|
|
285
|
+
for author in authors:
|
|
286
|
+
if "no-reply" in author:
|
|
287
|
+
continue
|
|
288
|
+
recent_authors.add(author)
|
|
289
|
+
if len(recent_authors) >= 3:
|
|
290
|
+
break
|
|
291
|
+
return recent_authors
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def review(filename, summary, description, publish_review):
|
|
295
|
+
all_reviewers = get_reviewers(filename).union(get_reviewers_in_group("right-sizer"))
|
|
296
|
+
reviewers_arg = " ".join(all_reviewers)
|
|
297
|
+
publish_arg = "-p" if publish_review is True else "-d"
|
|
298
|
+
subprocess.check_call(
|
|
299
|
+
(
|
|
300
|
+
"review-branch",
|
|
301
|
+
f"--summary={summary}",
|
|
302
|
+
f"--description={description}",
|
|
303
|
+
publish_arg,
|
|
304
|
+
"--reviewers",
|
|
305
|
+
reviewers_arg,
|
|
306
|
+
"--server",
|
|
307
|
+
"https://reviewboard.yelpcorp.com",
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def edit_soa_configs(filename, instance, cpu, mem, disk):
|
|
313
|
+
if os.path.islink(filename):
|
|
314
|
+
real_filename = os.path.realpath(filename)
|
|
315
|
+
os.remove(filename)
|
|
316
|
+
else:
|
|
317
|
+
real_filename = filename
|
|
318
|
+
try:
|
|
319
|
+
with open(real_filename, "r") as fi:
|
|
320
|
+
yams = fi.read()
|
|
321
|
+
yams = yams.replace("cpus: .", "cpus: 0.")
|
|
322
|
+
data = yaml.round_trip_load(yams, preserve_quotes=True)
|
|
323
|
+
|
|
324
|
+
instdict = data[instance]
|
|
325
|
+
if cpu:
|
|
326
|
+
instdict["cpus"] = float(cpu)
|
|
327
|
+
if mem:
|
|
328
|
+
mem = max(128, round(float(mem)))
|
|
329
|
+
instdict["mem"] = mem
|
|
330
|
+
if disk:
|
|
331
|
+
instdict["disk"] = round(float(disk))
|
|
332
|
+
out = yaml.round_trip_dump(data, width=120)
|
|
333
|
+
|
|
334
|
+
with open(filename, "w") as fi:
|
|
335
|
+
fi.write(out)
|
|
336
|
+
except FileNotFoundError:
|
|
337
|
+
log.exception(f"Could not find {filename}")
|
|
338
|
+
except KeyError:
|
|
339
|
+
log.exception(f"Error in {filename}. Will continue")
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def create_jira_ticket(serv, creds, description, JIRA):
|
|
343
|
+
creds = creds.split(":")
|
|
344
|
+
options = {"server": "https://jira.yelpcorp.com"}
|
|
345
|
+
jira_cli = JIRA(options=options, basic_auth=(creds[0], creds[1])) # noqa: F821
|
|
346
|
+
jira_ticket = {}
|
|
347
|
+
# Sometimes a project has required fields we can't predict
|
|
348
|
+
try:
|
|
349
|
+
jira_ticket = {
|
|
350
|
+
"project": {"key": serv["project"]},
|
|
351
|
+
"description": description,
|
|
352
|
+
"issuetype": {"name": "Improvement"},
|
|
353
|
+
"labels": ["perf-watching", "paasta-rightsizer"],
|
|
354
|
+
"summary": "{s}.{i} in {c} may be {o}provisioned".format(
|
|
355
|
+
s=serv["service"],
|
|
356
|
+
i=serv["instance"],
|
|
357
|
+
c=serv["cluster"],
|
|
358
|
+
o=serv["state"],
|
|
359
|
+
),
|
|
360
|
+
}
|
|
361
|
+
tick = jira_cli.create_issue(fields=jira_ticket)
|
|
362
|
+
except Exception:
|
|
363
|
+
jira_ticket["project"] = {"key": "PEOBS"}
|
|
364
|
+
jira_ticket["labels"].append(serv["service"])
|
|
365
|
+
tick = jira_cli.create_issue(fields=jira_ticket)
|
|
366
|
+
return tick.key
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _get_dashboard_qs_param(param, value):
|
|
370
|
+
# Some dashboards may ask for query string params like param=value, but not this provider.
|
|
371
|
+
return f"variables%5B%5D={param}%3D{param}:{value}"
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def generate_ticket_content(serv):
|
|
375
|
+
cpus = float(serv["cpus"])
|
|
376
|
+
provisioned_state = "over"
|
|
377
|
+
if cpus > float(serv["old_cpus"]):
|
|
378
|
+
provisioned_state = "under"
|
|
379
|
+
|
|
380
|
+
serv["state"] = provisioned_state
|
|
381
|
+
ticket_desc = (
|
|
382
|
+
"This ticket and CR have been auto-generated to help keep PaaSTA right-sized."
|
|
383
|
+
"\nPEOBS will review this CR and give a shipit. Then an ops deputy from your team can merge"
|
|
384
|
+
" if these values look good for your service after review."
|
|
385
|
+
"\nOpen an issue with any concerns and someone from PEOBS will respond."
|
|
386
|
+
"\nWe suspect that {s}.{i} in {c} may have been {o}-provisioned"
|
|
387
|
+
" during the 1 week prior to {d}. It initially had {x} cpus, but based on the below dashboard,"
|
|
388
|
+
" we recommend {y} cpus."
|
|
389
|
+
"\n- Dashboard: https://y.yelpcorp.com/{o}provisioned?{cluster_param}&{service_param}&{instance_param}"
|
|
390
|
+
"\n- Service owner: {n}"
|
|
391
|
+
"\n- Estimated monthly excess cost: ${m}"
|
|
392
|
+
"\n\nFor more information and sizing examples for larger services:"
|
|
393
|
+
"\n- Runbook: https://y.yelpcorp.com/rb-provisioning-alert"
|
|
394
|
+
"\n- Alert owner: pe-observability@yelp.com"
|
|
395
|
+
).format(
|
|
396
|
+
s=serv["service"],
|
|
397
|
+
c=serv["cluster"],
|
|
398
|
+
i=serv["instance"],
|
|
399
|
+
o=provisioned_state,
|
|
400
|
+
d=serv["date"],
|
|
401
|
+
n=serv["owner"],
|
|
402
|
+
m=serv["money"],
|
|
403
|
+
x=serv["old_cpus"],
|
|
404
|
+
y=serv["cpus"],
|
|
405
|
+
cluster_param=_get_dashboard_qs_param("paasta_cluster", serv["cluster"]),
|
|
406
|
+
service_param=_get_dashboard_qs_param("paasta_service", serv["service"]),
|
|
407
|
+
instance_param=_get_dashboard_qs_param("paasta_instance", serv["instance"]),
|
|
408
|
+
)
|
|
409
|
+
summary = f"Rightsizing {serv['service']}.{serv['instance']} in {serv['cluster']} to make it not have {provisioned_state}-provisioned cpu" # noqa: E501
|
|
410
|
+
return (summary, ticket_desc)
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def bulk_rightsize(report, create_code_review, publish_code_review, create_new_branch):
|
|
414
|
+
if create_new_branch:
|
|
415
|
+
branch = "rightsize-bulk-{}".format(int(time.time()))
|
|
416
|
+
create_branch(branch)
|
|
417
|
+
|
|
418
|
+
filenames = []
|
|
419
|
+
for _, serv in report["results"].items():
|
|
420
|
+
filename = "{}/{}.yaml".format(serv["service"], serv["cluster"])
|
|
421
|
+
filenames.append(filename)
|
|
422
|
+
cpus = serv.get("cpus", None)
|
|
423
|
+
mem = serv.get("mem", None)
|
|
424
|
+
disk = serv.get("disk", None)
|
|
425
|
+
edit_soa_configs(filename, serv["instance"], cpus, mem, disk)
|
|
426
|
+
if create_code_review:
|
|
427
|
+
bulk_commit(filenames, report["search"])
|
|
428
|
+
bulk_review(filenames, report["search"], publish_code_review)
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def individual_rightsize(
|
|
432
|
+
report, create_tickets, jira_creds, create_review, publish_review, JIRA
|
|
433
|
+
):
|
|
434
|
+
for _, serv in report["results"].items():
|
|
435
|
+
filename = "{}/{}.yaml".format(serv["service"], serv["cluster"])
|
|
436
|
+
summary, ticket_desc = generate_ticket_content(serv)
|
|
437
|
+
|
|
438
|
+
if create_tickets is True:
|
|
439
|
+
branch = create_jira_ticket(serv, jira_creds, ticket_desc, JIRA)
|
|
440
|
+
else:
|
|
441
|
+
branch = "rightsize-{}".format(int(time.time() * 1000))
|
|
442
|
+
|
|
443
|
+
create_branch(branch)
|
|
444
|
+
cpus = serv.get("cpus", None)
|
|
445
|
+
mem = serv.get("mem", None)
|
|
446
|
+
disk = serv.get("disk", None)
|
|
447
|
+
edit_soa_configs(filename, serv["instance"], cpus, mem, disk)
|
|
448
|
+
try:
|
|
449
|
+
commit(filename, serv)
|
|
450
|
+
if create_review:
|
|
451
|
+
review(filename, summary, ticket_desc, publish_review)
|
|
452
|
+
except Exception:
|
|
453
|
+
log.exception(
|
|
454
|
+
(
|
|
455
|
+
"\nUnable to push changes to {f}. Check if {f} conforms to"
|
|
456
|
+
"yelpsoa-configs yaml rules. No review created. To see the"
|
|
457
|
+
"cpu suggestion for this service check {t}."
|
|
458
|
+
).format(f=filename, t=branch)
|
|
459
|
+
)
|
|
460
|
+
continue
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def main():
|
|
464
|
+
args = parse_args()
|
|
465
|
+
|
|
466
|
+
if args.verbose:
|
|
467
|
+
log.setLevel(logging.DEBUG)
|
|
468
|
+
requests_log.setLevel(logging.DEBUG)
|
|
469
|
+
HTTPConnection.debuglevel = 2
|
|
470
|
+
requests_log.propagate = True
|
|
471
|
+
|
|
472
|
+
# Safety checks
|
|
473
|
+
if args.no_branch and not args.YELPSOA_DIR:
|
|
474
|
+
log.error(
|
|
475
|
+
"You must specify --yelpsoa-configs-dir to work on if you use the --local option"
|
|
476
|
+
)
|
|
477
|
+
return False
|
|
478
|
+
|
|
479
|
+
if args.ticket:
|
|
480
|
+
if not args.jira_creds:
|
|
481
|
+
raise ValueError("No JIRA creds specified")
|
|
482
|
+
# Only import the jira module if we need too
|
|
483
|
+
from jira.client import JIRA # noqa: F401
|
|
484
|
+
else:
|
|
485
|
+
JIRA = None
|
|
486
|
+
|
|
487
|
+
report = get_report_from_splunk(
|
|
488
|
+
args.splunk_creds, args.splunk_app, args.csv_report, args.criteria_filter
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
tmpdir = tempdir() # Create a tmp dir even if we are not using it
|
|
492
|
+
|
|
493
|
+
working_dir = args.YELPSOA_DIR
|
|
494
|
+
system_paasta_config = load_system_paasta_config()
|
|
495
|
+
if working_dir is None:
|
|
496
|
+
# Working in a temporary directory
|
|
497
|
+
working_dir = os.path.join("rightsizer", tmpdir.name)
|
|
498
|
+
clone_in(working_dir, system_paasta_config=system_paasta_config)
|
|
499
|
+
|
|
500
|
+
with cwd(working_dir):
|
|
501
|
+
if args.bulk or args.no_branch:
|
|
502
|
+
log.info("Running in bulk mode")
|
|
503
|
+
bulk_rightsize(
|
|
504
|
+
report, args.create_reviews, args.publish_reviews, not args.no_branch
|
|
505
|
+
)
|
|
506
|
+
else:
|
|
507
|
+
individual_rightsize(
|
|
508
|
+
report,
|
|
509
|
+
args.ticket,
|
|
510
|
+
args.jira_creds,
|
|
511
|
+
args.create_reviews,
|
|
512
|
+
args.publish_reviews,
|
|
513
|
+
JIRA,
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
tmpdir.cleanup() # Cleanup any tmpdire used
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
if __name__ == "__main__":
|
|
520
|
+
main()
|