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,164 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# Copyright 2015-2019 Yelp Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
"""
|
|
16
|
+
Usage: ./kubernetes_remove_evicted_pods.py [options]
|
|
17
|
+
|
|
18
|
+
Removes Evicted pods and notifies service owners
|
|
19
|
+
"""
|
|
20
|
+
import argparse
|
|
21
|
+
import logging
|
|
22
|
+
from collections import defaultdict
|
|
23
|
+
from collections import namedtuple
|
|
24
|
+
from typing import Dict
|
|
25
|
+
from typing import List
|
|
26
|
+
from typing import Mapping
|
|
27
|
+
from typing import Sequence
|
|
28
|
+
|
|
29
|
+
from kubernetes.client import V1DeleteOptions
|
|
30
|
+
from pysensu_yelp import Status
|
|
31
|
+
|
|
32
|
+
from paasta_tools.kubernetes_tools import get_all_pods
|
|
33
|
+
from paasta_tools.kubernetes_tools import KubeClient
|
|
34
|
+
from paasta_tools.kubernetes_tools import V1Pod
|
|
35
|
+
from paasta_tools.monitoring_tools import send_event
|
|
36
|
+
from paasta_tools.utils import DEFAULT_SOA_DIR
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
log = logging.getLogger(__name__)
|
|
40
|
+
EvictedPod = namedtuple("EvictedPod", ["podname", "namespace", "eviction_msg"])
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def parse_args() -> argparse.Namespace:
|
|
44
|
+
parser = argparse.ArgumentParser(
|
|
45
|
+
description="Removes evicted pods and notifies service owners"
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"-d",
|
|
49
|
+
"--soa-dir",
|
|
50
|
+
dest="soa_dir",
|
|
51
|
+
default=DEFAULT_SOA_DIR,
|
|
52
|
+
help="define a different soa config directory",
|
|
53
|
+
)
|
|
54
|
+
parser.add_argument(
|
|
55
|
+
"-v", "--verbose", action="store_true", dest="verbose", default=False
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"-n", "--dry-run", action="store_true", dest="dry_run", default=False
|
|
59
|
+
)
|
|
60
|
+
args = parser.parse_args()
|
|
61
|
+
return args
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_evicted_pods(pods: Sequence[V1Pod]) -> Sequence[V1Pod]:
|
|
65
|
+
return [
|
|
66
|
+
pod
|
|
67
|
+
for pod in pods
|
|
68
|
+
if pod.status.phase == "Failed" and pod.status.reason == "Evicted"
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_pod_service(pod: V1Pod) -> str:
|
|
73
|
+
if pod.metadata.labels is not None:
|
|
74
|
+
return pod.metadata.labels.get("paasta.yelp.com/service")
|
|
75
|
+
else:
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def notify_service_owners(
|
|
80
|
+
services: Mapping[str, Sequence[EvictedPod]],
|
|
81
|
+
soa_dir: str,
|
|
82
|
+
dry_run: bool,
|
|
83
|
+
) -> None:
|
|
84
|
+
check_overrides = {
|
|
85
|
+
"page": False,
|
|
86
|
+
"alert_after": "0m",
|
|
87
|
+
"realert_every": 1,
|
|
88
|
+
"tip": "Pods can be Evicted if they go over the allowed quota for a given resource. Check the Eviction message to figure out which resource quota was breached",
|
|
89
|
+
}
|
|
90
|
+
for service in services.keys():
|
|
91
|
+
check_name = f"pod-eviction.{service}"
|
|
92
|
+
check_output = "The following pods have been evicted and will be removed from the cluster:\n"
|
|
93
|
+
for pod in services[service]:
|
|
94
|
+
check_output += f"- {pod.podname}: {pod.eviction_msg}\n"
|
|
95
|
+
if dry_run:
|
|
96
|
+
log.info(f"Would have notified owners for service {service}")
|
|
97
|
+
else:
|
|
98
|
+
log.info(f"Notifying owners for service {service}")
|
|
99
|
+
send_event(
|
|
100
|
+
service,
|
|
101
|
+
check_name,
|
|
102
|
+
check_overrides,
|
|
103
|
+
Status.CRITICAL,
|
|
104
|
+
check_output,
|
|
105
|
+
soa_dir,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def remove_pods(
|
|
110
|
+
client: KubeClient,
|
|
111
|
+
services: Mapping[str, Sequence[EvictedPod]],
|
|
112
|
+
dry_run: bool,
|
|
113
|
+
) -> None:
|
|
114
|
+
delete_options = V1DeleteOptions()
|
|
115
|
+
for service in services:
|
|
116
|
+
# Do not remove more than 2 pods per run
|
|
117
|
+
for pod in services[service][0:2]:
|
|
118
|
+
if dry_run:
|
|
119
|
+
log.info(f"Would have removed pod {pod.podname}")
|
|
120
|
+
else:
|
|
121
|
+
client.core.delete_namespaced_pod(
|
|
122
|
+
pod.podname,
|
|
123
|
+
pod.namespace,
|
|
124
|
+
body=delete_options,
|
|
125
|
+
grace_period_seconds=0,
|
|
126
|
+
propagation_policy="Background",
|
|
127
|
+
)
|
|
128
|
+
log.info(f"Removing pod {pod.podname}")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def evicted_pods_per_service(
|
|
132
|
+
client: KubeClient,
|
|
133
|
+
) -> Mapping[str, Sequence[EvictedPod]]:
|
|
134
|
+
all_pods = get_all_pods(kube_client=client, namespace="")
|
|
135
|
+
evicted_pods = get_evicted_pods(all_pods)
|
|
136
|
+
log.info(f"Pods in evicted state: {[pod.metadata.name for pod in evicted_pods]}")
|
|
137
|
+
evicted_pods_aggregated: Dict[str, List[EvictedPod]] = defaultdict(list)
|
|
138
|
+
for pod in evicted_pods:
|
|
139
|
+
service = get_pod_service(pod)
|
|
140
|
+
if service:
|
|
141
|
+
evicted_pods_aggregated[service].append(
|
|
142
|
+
EvictedPod(
|
|
143
|
+
pod.metadata.name, pod.metadata.namespace, pod.status.message
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
else:
|
|
147
|
+
log.info(f"Could not get service name for pod {pod.metadata.name}")
|
|
148
|
+
return evicted_pods_aggregated
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def main() -> None:
|
|
152
|
+
args = parse_args()
|
|
153
|
+
if args.verbose:
|
|
154
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
155
|
+
else:
|
|
156
|
+
logging.basicConfig(level=logging.INFO)
|
|
157
|
+
kube_client = KubeClient()
|
|
158
|
+
|
|
159
|
+
evicted_pods = evicted_pods_per_service(kube_client)
|
|
160
|
+
remove_pods(kube_client, evicted_pods, args.dry_run)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if __name__ == "__main__":
|
|
164
|
+
main()
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# Copyright 2015-2019 Yelp Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
import argparse
|
|
16
|
+
import logging
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from datetime import timedelta
|
|
19
|
+
from datetime import timezone
|
|
20
|
+
from typing import Any
|
|
21
|
+
from typing import Callable
|
|
22
|
+
from typing import Sequence
|
|
23
|
+
from typing import Tuple
|
|
24
|
+
|
|
25
|
+
from paasta_tools.kubernetes.remote_run import get_max_job_duration_limit
|
|
26
|
+
from paasta_tools.kubernetes.remote_run import get_remote_run_jobs
|
|
27
|
+
from paasta_tools.kubernetes.remote_run import get_remote_run_role_bindings
|
|
28
|
+
from paasta_tools.kubernetes.remote_run import get_remote_run_roles
|
|
29
|
+
from paasta_tools.kubernetes.remote_run import get_remote_run_service_accounts
|
|
30
|
+
from paasta_tools.kubernetes_tools import get_all_managed_namespaces
|
|
31
|
+
from paasta_tools.kubernetes_tools import KubeClient
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
ListingFuncType = Callable[[KubeClient, str], Sequence[Any]]
|
|
35
|
+
DeletionFuncType = Callable[[str, str], Any]
|
|
36
|
+
log = logging.getLogger(__name__)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def clean_namespace(
|
|
40
|
+
kube_client: KubeClient,
|
|
41
|
+
namespace: str,
|
|
42
|
+
auth_age_limit: datetime,
|
|
43
|
+
job_age_limit: datetime,
|
|
44
|
+
dry_run: bool = False,
|
|
45
|
+
):
|
|
46
|
+
"""Clean ephemeral remote-run resource in a namespace
|
|
47
|
+
|
|
48
|
+
:param KubeClient kube_client: kubernetes client
|
|
49
|
+
:param str namepsace: kubernetes namespace
|
|
50
|
+
:param datetime auth_age_limit: expiration time for authentication resources
|
|
51
|
+
:param datetime job_age_limit: expiration time for job resources
|
|
52
|
+
:param bool dry_run: delete resources for real or not
|
|
53
|
+
"""
|
|
54
|
+
dry_run_msg = " (dry_run)" if dry_run else ""
|
|
55
|
+
cleanup_actions: Sequence[Tuple[DeletionFuncType, ListingFuncType, datetime]] = (
|
|
56
|
+
(
|
|
57
|
+
kube_client.core.delete_namespaced_service_account,
|
|
58
|
+
get_remote_run_service_accounts,
|
|
59
|
+
auth_age_limit,
|
|
60
|
+
),
|
|
61
|
+
(
|
|
62
|
+
kube_client.rbac.delete_namespaced_role,
|
|
63
|
+
get_remote_run_roles,
|
|
64
|
+
auth_age_limit,
|
|
65
|
+
),
|
|
66
|
+
(
|
|
67
|
+
kube_client.rbac.delete_namespaced_role_binding,
|
|
68
|
+
get_remote_run_role_bindings,
|
|
69
|
+
auth_age_limit,
|
|
70
|
+
),
|
|
71
|
+
(
|
|
72
|
+
kube_client.batches.delete_namespaced_job,
|
|
73
|
+
get_remote_run_jobs,
|
|
74
|
+
job_age_limit,
|
|
75
|
+
),
|
|
76
|
+
)
|
|
77
|
+
for delete_func, list_func, age_limit in cleanup_actions:
|
|
78
|
+
for entity in list_func(kube_client, namespace):
|
|
79
|
+
if (
|
|
80
|
+
not entity.metadata.name.startswith("remote-run-")
|
|
81
|
+
or entity.metadata.creation_timestamp > age_limit
|
|
82
|
+
):
|
|
83
|
+
continue
|
|
84
|
+
log.info(f"Deleting {entity.metadata.name} in {namespace}{dry_run_msg}")
|
|
85
|
+
if not dry_run:
|
|
86
|
+
delete_func(entity.metadata.name, namespace)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def parse_args() -> argparse.Namespace:
|
|
90
|
+
parser = argparse.ArgumentParser(
|
|
91
|
+
description="Clean ephemeral Kubernetes resources created by remote-run invocations",
|
|
92
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
93
|
+
)
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
"--max-age",
|
|
96
|
+
type=int,
|
|
97
|
+
default=600,
|
|
98
|
+
help="Maximum age, in seconds, resources are allowed to have",
|
|
99
|
+
)
|
|
100
|
+
parser.add_argument(
|
|
101
|
+
"-n",
|
|
102
|
+
"--dry-run",
|
|
103
|
+
action="store_true",
|
|
104
|
+
default=False,
|
|
105
|
+
help="Do not actually delete resources",
|
|
106
|
+
)
|
|
107
|
+
parser.add_argument(
|
|
108
|
+
"-v",
|
|
109
|
+
"--verbose",
|
|
110
|
+
action="store_true",
|
|
111
|
+
default=False,
|
|
112
|
+
help="More verbose logging",
|
|
113
|
+
)
|
|
114
|
+
return parser.parse_args()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def main():
|
|
118
|
+
args = parse_args()
|
|
119
|
+
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
|
|
120
|
+
kube_client = KubeClient()
|
|
121
|
+
now = datetime.now(tz=timezone.utc)
|
|
122
|
+
age_limit = now - timedelta(seconds=args.max_age)
|
|
123
|
+
job_age_limit = now - timedelta(seconds=get_max_job_duration_limit())
|
|
124
|
+
for namespace in get_all_managed_namespaces(kube_client):
|
|
125
|
+
clean_namespace(
|
|
126
|
+
kube_client,
|
|
127
|
+
namespace,
|
|
128
|
+
auth_age_limit=age_limit,
|
|
129
|
+
job_age_limit=job_age_limit,
|
|
130
|
+
dry_run=args.dry_run,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
if __name__ == "__main__":
|
|
135
|
+
main()
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# Copyright 2015-2019 Yelp Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
"""
|
|
16
|
+
Usage: ./paasta_cleanup_stale_nodes.py [options]
|
|
17
|
+
|
|
18
|
+
Removes terminated Kubernetes nodes from the Kubernetes API.
|
|
19
|
+
|
|
20
|
+
Command line options:
|
|
21
|
+
|
|
22
|
+
- -v, --verbose: Verbose output
|
|
23
|
+
- -n, --dry-run: Only report what would have been deleted
|
|
24
|
+
"""
|
|
25
|
+
import argparse
|
|
26
|
+
import logging
|
|
27
|
+
import sys
|
|
28
|
+
from typing import List
|
|
29
|
+
from typing import Sequence
|
|
30
|
+
from typing import Tuple
|
|
31
|
+
|
|
32
|
+
import boto3
|
|
33
|
+
from boto3_type_annotations.ec2 import Client
|
|
34
|
+
from botocore.exceptions import ClientError
|
|
35
|
+
from kubernetes.client import V1DeleteOptions
|
|
36
|
+
from kubernetes.client import V1Node
|
|
37
|
+
from kubernetes.client.rest import ApiException
|
|
38
|
+
|
|
39
|
+
from paasta_tools.kubernetes_tools import get_all_nodes
|
|
40
|
+
from paasta_tools.kubernetes_tools import KubeClient
|
|
41
|
+
|
|
42
|
+
log = logging.getLogger(__name__)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def parse_args() -> argparse.Namespace:
|
|
46
|
+
parser = argparse.ArgumentParser(description="Remove terminated Kubernetes nodes")
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"-v", "--verbose", action="store_true", dest="verbose", default=False
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"-n", "--dry-run", action="store_true", dest="dry_run", default=False
|
|
52
|
+
)
|
|
53
|
+
args = parser.parse_args()
|
|
54
|
+
return args
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def nodes_for_cleanup(ec2_client: Client, nodes: Sequence[V1Node]) -> List[V1Node]:
|
|
58
|
+
not_ready = [
|
|
59
|
+
node
|
|
60
|
+
for node in nodes
|
|
61
|
+
if not is_node_ready(node)
|
|
62
|
+
and "node-role.kubernetes.io/control-plane" not in node.metadata.labels
|
|
63
|
+
]
|
|
64
|
+
terminated = terminated_nodes(ec2_client, not_ready)
|
|
65
|
+
return terminated
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def terminated_nodes(ec2_client: Client, nodes: Sequence[V1Node]) -> List[V1Node]:
|
|
69
|
+
instance_ids = [node.spec.provider_id.split("/")[-1] for node in nodes]
|
|
70
|
+
# if there are any instances that don't exist in the query to describe_instance_status
|
|
71
|
+
# then amazon won't return the results for any, so we have to query the instances
|
|
72
|
+
# one by one
|
|
73
|
+
statuses = [
|
|
74
|
+
does_instance_exist(ec2_client, instance_id) for instance_id in instance_ids
|
|
75
|
+
]
|
|
76
|
+
for node, status in zip(nodes, statuses):
|
|
77
|
+
log.debug(f"{node.metadata.name} exists: {status}")
|
|
78
|
+
|
|
79
|
+
return [node for node, status in zip(nodes, statuses) if not status]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def does_instance_exist(ec2_client: Client, instance_id: str):
|
|
83
|
+
try:
|
|
84
|
+
instance = ec2_client.describe_instance_status(InstanceIds=[instance_id])
|
|
85
|
+
if instance["InstanceStatuses"]:
|
|
86
|
+
status = instance["InstanceStatuses"][0]["InstanceState"]["Name"]
|
|
87
|
+
# see possible values at https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.describe_instance_status
|
|
88
|
+
# 'pending'|'running'|'shutting-down'|'terminated'|'stopping'|'stopped'
|
|
89
|
+
# it's unlikely that we'll ever be in this situation with a pending node - the common case is that
|
|
90
|
+
# the node is either running and been marked as not ready, or it's being shutdown
|
|
91
|
+
if status not in ("running", "pending"):
|
|
92
|
+
return False
|
|
93
|
+
return True
|
|
94
|
+
|
|
95
|
+
log.debug(
|
|
96
|
+
f"no instance status in response for {instance_id}; assuming to have been terminated"
|
|
97
|
+
)
|
|
98
|
+
return False
|
|
99
|
+
except ClientError as e:
|
|
100
|
+
if e.response["Error"]["Code"] == "InvalidInstanceID.NotFound":
|
|
101
|
+
log.debug(
|
|
102
|
+
f"instance {instance_id} not found; assuming to have been terminated"
|
|
103
|
+
)
|
|
104
|
+
return False
|
|
105
|
+
else:
|
|
106
|
+
log.error(f"error fetching instance status for {instance_id}")
|
|
107
|
+
raise e
|
|
108
|
+
return True
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def terminate_nodes(
|
|
112
|
+
client: KubeClient, nodes: List[str]
|
|
113
|
+
) -> Tuple[List[str], List[Tuple[str, Exception]]]:
|
|
114
|
+
success = []
|
|
115
|
+
errors = []
|
|
116
|
+
for node in nodes:
|
|
117
|
+
try:
|
|
118
|
+
body = V1DeleteOptions()
|
|
119
|
+
client.core.delete_node(node, body=body, propagation_policy="foreground")
|
|
120
|
+
except ApiException as e:
|
|
121
|
+
errors.append((node, e))
|
|
122
|
+
continue
|
|
123
|
+
success.append(node)
|
|
124
|
+
return (success, errors)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def is_node_ready(node: V1Node) -> bool:
|
|
128
|
+
for condition in node.status.conditions:
|
|
129
|
+
if condition.type == "Ready":
|
|
130
|
+
return condition.status == "True"
|
|
131
|
+
log.error(
|
|
132
|
+
f"no KubeletReady condition found for node {node.metadata.name}. Conditions {node.status.conditions}"
|
|
133
|
+
)
|
|
134
|
+
return True
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def main() -> None:
|
|
138
|
+
args = parse_args()
|
|
139
|
+
if args.verbose:
|
|
140
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
141
|
+
else:
|
|
142
|
+
logging.basicConfig(level=logging.INFO)
|
|
143
|
+
|
|
144
|
+
dry_run = args.dry_run
|
|
145
|
+
|
|
146
|
+
kube_client = KubeClient()
|
|
147
|
+
all_nodes = get_all_nodes(kube_client)
|
|
148
|
+
log.debug(f"found nodes in cluster {[node.metadata.name for node in all_nodes]}")
|
|
149
|
+
|
|
150
|
+
# we depend on iam credentials existing on the host for this to run.
|
|
151
|
+
# anywhere else, and you'll need to set credentials using environment variables
|
|
152
|
+
# we also make the assumption that all nodes are in the same region here
|
|
153
|
+
region = all_nodes[0].metadata.labels["failure-domain.beta.kubernetes.io/region"]
|
|
154
|
+
ec2_client = boto3.client("ec2", region)
|
|
155
|
+
|
|
156
|
+
filtered_nodes = nodes_for_cleanup(ec2_client, all_nodes)
|
|
157
|
+
if logging.DEBUG >= logging.root.level:
|
|
158
|
+
log.debug(
|
|
159
|
+
f"nodes to be deleted: {[node.metadata.name for node in filtered_nodes]}"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
if not dry_run:
|
|
163
|
+
success, errors = terminate_nodes(
|
|
164
|
+
kube_client, [node.metadata.name for node in filtered_nodes]
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
success, errors = [], []
|
|
168
|
+
log.info("dry run mode detected: not deleting nodes")
|
|
169
|
+
|
|
170
|
+
for node_name in success:
|
|
171
|
+
log.info(f"successfully deleted node {node_name}")
|
|
172
|
+
|
|
173
|
+
for node_name, exception in errors:
|
|
174
|
+
log.error(f"error deleting node: {node_name}: {exception}")
|
|
175
|
+
|
|
176
|
+
if errors:
|
|
177
|
+
sys.exit(1)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
if __name__ == "__main__":
|
|
181
|
+
main()
|