paasta-tools 1.21.3__py3-none-any.whl → 1.22.0__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.
- paasta_tools/__init__.py +1 -1
- paasta_tools/api/api.py +5 -0
- paasta_tools/api/api_docs/swagger.json +92 -0
- paasta_tools/api/views/autoscaler.py +122 -0
- paasta_tools/cli/authentication.py +6 -39
- paasta_tools/cli/cmds/autoscale.py +126 -7
- paasta_tools/cli/cmds/logs.py +7 -7
- paasta_tools/cli/schemas/service_schema.json +4 -1
- paasta_tools/generate_deployments_for_service.py +4 -0
- paasta_tools/kubernetes/application/controller_wrappers.py +16 -2
- paasta_tools/kubernetes_tools.py +77 -1
- paasta_tools/paastaapi/api/autoscaler_api.py +140 -1
- paasta_tools/paastaapi/model/autoscaling_override.py +180 -0
- paasta_tools/paastaapi/model/inline_response202.py +182 -0
- paasta_tools/paastaapi/models/__init__.py +2 -0
- paasta_tools/setup_istio_mesh.py +1 -1
- paasta_tools/setup_kubernetes_job.py +105 -1
- paasta_tools/smartstack_tools.py +2 -2
- paasta_tools/spark_tools.py +2 -2
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/generate_deployments_for_service.py +4 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/setup_istio_mesh.py +1 -1
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/setup_kubernetes_job.py +105 -1
- {paasta_tools-1.21.3.dist-info → paasta_tools-1.22.0.dist-info}/METADATA +2 -2
- {paasta_tools-1.21.3.dist-info → paasta_tools-1.22.0.dist-info}/RECORD +77 -75
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/apply_external_resources.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/bounce_log_latency_parser.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/check_autoscaler_max_instances.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/check_cassandracluster_services_replication.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/check_flink_services_health.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/check_kubernetes_api.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/check_kubernetes_services_replication.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/check_manual_oapi_changes.sh +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/check_oom_events.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/check_orphans.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/check_spark_jobs.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/cleanup_kubernetes_cr.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/cleanup_kubernetes_crd.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/cleanup_kubernetes_jobs.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/create_dynamodb_table.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/create_paasta_playground.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/delete_kubernetes_deployments.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/emit_allocated_cpu_metrics.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/generate_all_deployments +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/generate_authenticating_services.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/generate_services_file.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/generate_services_yaml.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/get_running_task_allocation.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/habitat_fixer.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/ide_helper.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/is_pod_healthy_in_proxy.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/is_pod_healthy_in_smartstack.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/kill_bad_containers.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/kubernetes_remove_evicted_pods.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/mass-deploy-tag.sh +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/mock_patch_checker.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/paasta_cleanup_remote_run_resources.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/paasta_cleanup_stale_nodes.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/paasta_deploy_tron_jobs +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/paasta_execute_docker_command.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/paasta_secrets_sync.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/paasta_tabcomplete.sh +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/paasta_update_soa_memcpu.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/render_template.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/rightsizer_soaconfigs_update.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/service_shard_remove.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/service_shard_update.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/setup_kubernetes_cr.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/setup_kubernetes_crd.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/setup_kubernetes_internal_crd.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/setup_prometheus_adapter_config.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/shared_ip_check.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/synapse_srv_namespaces_fact.py +0 -0
- {paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/timeouts_metrics_prom.py +0 -0
- {paasta_tools-1.21.3.dist-info → paasta_tools-1.22.0.dist-info}/LICENSE +0 -0
- {paasta_tools-1.21.3.dist-info → paasta_tools-1.22.0.dist-info}/WHEEL +0 -0
- {paasta_tools-1.21.3.dist-info → paasta_tools-1.22.0.dist-info}/entry_points.txt +0 -0
- {paasta_tools-1.21.3.dist-info → paasta_tools-1.22.0.dist-info}/top_level.txt +0 -0
|
@@ -21,9 +21,12 @@ Command line options:
|
|
|
21
21
|
- -v, --verbose: Verbose output
|
|
22
22
|
"""
|
|
23
23
|
import argparse
|
|
24
|
+
import json
|
|
24
25
|
import logging
|
|
25
26
|
import sys
|
|
27
|
+
import time
|
|
26
28
|
import traceback
|
|
29
|
+
from typing import Dict
|
|
27
30
|
from typing import List
|
|
28
31
|
from typing import Optional
|
|
29
32
|
from typing import Sequence
|
|
@@ -36,7 +39,11 @@ from paasta_tools.kubernetes.application.controller_wrappers import Application
|
|
|
36
39
|
from paasta_tools.kubernetes.application.controller_wrappers import (
|
|
37
40
|
get_application_wrapper,
|
|
38
41
|
)
|
|
42
|
+
from paasta_tools.kubernetes_tools import AUTOSCALING_OVERRIDES_CONFIGMAP_NAME
|
|
43
|
+
from paasta_tools.kubernetes_tools import AUTOSCALING_OVERRIDES_CONFIGMAP_NAMESPACE
|
|
39
44
|
from paasta_tools.kubernetes_tools import ensure_namespace
|
|
45
|
+
from paasta_tools.kubernetes_tools import get_namespaced_configmap
|
|
46
|
+
from paasta_tools.kubernetes_tools import HpaOverride
|
|
40
47
|
from paasta_tools.kubernetes_tools import InvalidKubernetesConfig
|
|
41
48
|
from paasta_tools.kubernetes_tools import KubeClient
|
|
42
49
|
from paasta_tools.kubernetes_tools import KubernetesDeploymentConfig
|
|
@@ -129,6 +136,8 @@ def main() -> None:
|
|
|
129
136
|
kube_client = KubeClient()
|
|
130
137
|
service_instances_valid = True
|
|
131
138
|
|
|
139
|
+
hpa_overrides = get_hpa_overrides(kube_client)
|
|
140
|
+
|
|
132
141
|
# validate the service_instance names
|
|
133
142
|
service_instances_with_valid_names = get_service_instances_with_valid_names(
|
|
134
143
|
service_instances=args.service_instance_list
|
|
@@ -162,6 +171,7 @@ def main() -> None:
|
|
|
162
171
|
soa_dir=soa_dir,
|
|
163
172
|
metrics_interface=deploy_metrics,
|
|
164
173
|
eks=args.eks,
|
|
174
|
+
hpa_overrides=hpa_overrides,
|
|
165
175
|
)
|
|
166
176
|
else:
|
|
167
177
|
setup_kube_succeeded = False
|
|
@@ -239,6 +249,92 @@ def get_kubernetes_deployment_config(
|
|
|
239
249
|
return service_instance_configs_list
|
|
240
250
|
|
|
241
251
|
|
|
252
|
+
def get_hpa_overrides(kube_client: KubeClient) -> Dict[str, Dict[str, HpaOverride]]:
|
|
253
|
+
"""
|
|
254
|
+
Load autoscaling overrides from the ConfigMap once.
|
|
255
|
+
|
|
256
|
+
This function reads the "paasta-autoscaling-overrides" ConfigMap in the "paasta" namespace
|
|
257
|
+
and extracts all valid (non-expired) overrides to return a dictionary mapping
|
|
258
|
+
service.instance pairs to override data (currently, just min_instances and when the
|
|
259
|
+
override should expire by).
|
|
260
|
+
|
|
261
|
+
The incoming ConfigMap is expected to have the following format:
|
|
262
|
+
{
|
|
263
|
+
$SERVICE_A.$INSTANCE_A: {
|
|
264
|
+
"min_instances": 2,
|
|
265
|
+
"expire_after": "2023-10-01T00:00:00Z"
|
|
266
|
+
},
|
|
267
|
+
$SERVICE_A.$INSTANCE_B: {
|
|
268
|
+
"min_instances": 3,
|
|
269
|
+
"expire_after": "2023-10-01T00:00:00Z"
|
|
270
|
+
},
|
|
271
|
+
...
|
|
272
|
+
},
|
|
273
|
+
$SERVICE_B.$INSTANCE_A: {
|
|
274
|
+
"min_instances": 1,
|
|
275
|
+
"expire_after": "2023-10-01T00:00:00Z"
|
|
276
|
+
},
|
|
277
|
+
$SERVICE_B.$INSTANCE_B: {
|
|
278
|
+
"min_instances": 2,
|
|
279
|
+
"expire_after": "2023-10-01T00:00:00Z"
|
|
280
|
+
},
|
|
281
|
+
...
|
|
282
|
+
}
|
|
283
|
+
"""
|
|
284
|
+
overrides: Dict[str, Dict[str, HpaOverride]] = {}
|
|
285
|
+
try:
|
|
286
|
+
configmap = get_namespaced_configmap(
|
|
287
|
+
name=AUTOSCALING_OVERRIDES_CONFIGMAP_NAME,
|
|
288
|
+
namespace=AUTOSCALING_OVERRIDES_CONFIGMAP_NAMESPACE,
|
|
289
|
+
kube_client=kube_client,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
if configmap.data:
|
|
293
|
+
current_time = time.time()
|
|
294
|
+
|
|
295
|
+
for service_instance, override_json in configmap.data.items():
|
|
296
|
+
try:
|
|
297
|
+
service, instance = service_instance.split(".")
|
|
298
|
+
override_metadata = json.loads(override_json)
|
|
299
|
+
expire_after = override_metadata.get("expire_after")
|
|
300
|
+
min_instances = override_metadata.get("min_instances")
|
|
301
|
+
|
|
302
|
+
if expire_after and min_instances:
|
|
303
|
+
if current_time < expire_after:
|
|
304
|
+
if service not in overrides:
|
|
305
|
+
overrides[service] = {}
|
|
306
|
+
|
|
307
|
+
overrides[service][instance] = {
|
|
308
|
+
"min_instances": min_instances,
|
|
309
|
+
"expire_after": expire_after,
|
|
310
|
+
}
|
|
311
|
+
log.info(
|
|
312
|
+
f"Found valid HPA override for {service}: "
|
|
313
|
+
f"{override_metadata.get('min_instances')} (expires at {expire_after})"
|
|
314
|
+
)
|
|
315
|
+
else:
|
|
316
|
+
log.info(
|
|
317
|
+
f"Ignoring expired HPA override for {service}.{instance}"
|
|
318
|
+
f"(expired at {expire_after})"
|
|
319
|
+
)
|
|
320
|
+
else:
|
|
321
|
+
log.warning(
|
|
322
|
+
f"Invalid HPA override for {service}.{instance}: "
|
|
323
|
+
f"missing 'min_instances' or 'expire_after': {override_metadata}"
|
|
324
|
+
)
|
|
325
|
+
except Exception:
|
|
326
|
+
log.exception(
|
|
327
|
+
f"Error parsing override for {service} - proceeding without overrides for this service."
|
|
328
|
+
)
|
|
329
|
+
except Exception:
|
|
330
|
+
# If ConfigMap doesn't exist or there's an error, just return empty dict
|
|
331
|
+
log.exception(
|
|
332
|
+
f"Unable to load the {AUTOSCALING_OVERRIDES_CONFIGMAP_NAME} ConfigMap - proceeding without overrides"
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
return overrides
|
|
336
|
+
|
|
337
|
+
|
|
242
338
|
def setup_kube_deployments(
|
|
243
339
|
kube_client: KubeClient,
|
|
244
340
|
cluster: str,
|
|
@@ -249,6 +345,7 @@ def setup_kube_deployments(
|
|
|
249
345
|
soa_dir: str = DEFAULT_SOA_DIR,
|
|
250
346
|
metrics_interface: metrics_lib.BaseMetrics = metrics_lib.NoMetrics("paasta"),
|
|
251
347
|
eks: bool = False,
|
|
348
|
+
hpa_overrides: Optional[Dict[str, Dict[str, HpaOverride]]] = None,
|
|
252
349
|
) -> bool:
|
|
253
350
|
|
|
254
351
|
if not service_instance_configs_list:
|
|
@@ -260,12 +357,17 @@ def setup_kube_deployments(
|
|
|
260
357
|
for deployment in existing_kube_deployments
|
|
261
358
|
}
|
|
262
359
|
|
|
360
|
+
hpa_overrides = hpa_overrides or {}
|
|
361
|
+
|
|
263
362
|
applications = [
|
|
264
363
|
create_application_object(
|
|
265
364
|
cluster=cluster,
|
|
266
365
|
soa_dir=soa_dir,
|
|
267
366
|
service_instance_config=service_instance,
|
|
268
367
|
eks=eks,
|
|
368
|
+
hpa_override=hpa_overrides.get(service_instance.service, {}).get(
|
|
369
|
+
service_instance.instance, None
|
|
370
|
+
),
|
|
269
371
|
)
|
|
270
372
|
if service_instance
|
|
271
373
|
else (_, None)
|
|
@@ -337,6 +439,7 @@ def create_application_object(
|
|
|
337
439
|
soa_dir: str,
|
|
338
440
|
service_instance_config: Union[KubernetesDeploymentConfig, EksDeploymentConfig],
|
|
339
441
|
eks: bool = False,
|
|
442
|
+
hpa_override: Optional[HpaOverride] = None,
|
|
340
443
|
) -> Tuple[bool, Optional[Application]]:
|
|
341
444
|
try:
|
|
342
445
|
formatted_application = service_instance_config.format_kubernetes_app()
|
|
@@ -344,8 +447,9 @@ def create_application_object(
|
|
|
344
447
|
log.error(traceback.format_exc())
|
|
345
448
|
return False, None
|
|
346
449
|
|
|
347
|
-
app = get_application_wrapper(formatted_application)
|
|
450
|
+
app = get_application_wrapper(formatted_application, hpa_override)
|
|
348
451
|
app.load_local_config(soa_dir, cluster, eks)
|
|
452
|
+
|
|
349
453
|
return True, app
|
|
350
454
|
|
|
351
455
|
|
paasta_tools/smartstack_tools.py
CHANGED
|
@@ -473,7 +473,7 @@ def get_service_discovery_providers(
|
|
|
473
473
|
elif name == "envoy":
|
|
474
474
|
providers.append(EnvoyServiceDiscovery(system_paasta_config))
|
|
475
475
|
else:
|
|
476
|
-
log.
|
|
476
|
+
log.warning("unknown provider")
|
|
477
477
|
return providers
|
|
478
478
|
|
|
479
479
|
|
|
@@ -530,7 +530,7 @@ class BaseReplicationChecker(ReplicationChecker):
|
|
|
530
530
|
)
|
|
531
531
|
break
|
|
532
532
|
except Exception as e:
|
|
533
|
-
log.
|
|
533
|
+
log.warning(
|
|
534
534
|
f"Error while getting replication info for {location} from {hostname}: {e}"
|
|
535
535
|
)
|
|
536
536
|
if hostname == hostnames[-1]:
|
paasta_tools/spark_tools.py
CHANGED
|
@@ -133,7 +133,7 @@ def setup_volume_mounts(volumes: List[DockerVolume]) -> Dict[str, str]:
|
|
|
133
133
|
)
|
|
134
134
|
|
|
135
135
|
if host_path in seen_paths:
|
|
136
|
-
log.
|
|
136
|
+
log.warning(f"Skipping {host_path} - already added a binding for it.")
|
|
137
137
|
continue
|
|
138
138
|
seen_paths.add(host_path)
|
|
139
139
|
|
|
@@ -209,7 +209,7 @@ def auto_add_timeout_for_spark_job(
|
|
|
209
209
|
f"'timeout' could not be added to spark command: '{cmd}' due to error '{e}'. "
|
|
210
210
|
"Please report to #spark."
|
|
211
211
|
)
|
|
212
|
-
log.
|
|
212
|
+
log.warning(err_msg)
|
|
213
213
|
print(PaastaColors.red(err_msg))
|
|
214
214
|
return cmd
|
|
215
215
|
|
{paasta_tools-1.21.3.data → paasta_tools-1.22.0.data}/scripts/generate_deployments_for_service.py
RENAMED
|
@@ -130,6 +130,10 @@ def get_deploy_group_mappings(
|
|
|
130
130
|
v2_mappings: V2_Mappings = {"deployments": {}, "controls": {}}
|
|
131
131
|
git_url = get_git_url(service=service, soa_dir=soa_dir)
|
|
132
132
|
|
|
133
|
+
# Some pseudo-services like toolboxes explicitly have no git_url, and therefore no deployments
|
|
134
|
+
if git_url is None:
|
|
135
|
+
return mappings, v2_mappings
|
|
136
|
+
|
|
133
137
|
# Most of the time of this function is in two parts:
|
|
134
138
|
# 1. getting remote refs from git. (Mostly IO, just waiting for git to get back to us.)
|
|
135
139
|
# 2. loading instance configs. (Mostly CPU, copy.deepcopying yaml over and over again)
|
|
@@ -103,7 +103,7 @@ def load_smartstack_namespaces(soa_dir: str = DEFAULT_SOA_DIR) -> Mapping:
|
|
|
103
103
|
for (ns, details) in svc_namespaces.items():
|
|
104
104
|
namespaces[f"{dir}.{ns}"] = details
|
|
105
105
|
except Exception as err:
|
|
106
|
-
log.
|
|
106
|
+
log.warning(f"Failed to load namespaces for {dir}: {err}")
|
|
107
107
|
|
|
108
108
|
return namespaces
|
|
109
109
|
|
|
@@ -21,9 +21,12 @@ Command line options:
|
|
|
21
21
|
- -v, --verbose: Verbose output
|
|
22
22
|
"""
|
|
23
23
|
import argparse
|
|
24
|
+
import json
|
|
24
25
|
import logging
|
|
25
26
|
import sys
|
|
27
|
+
import time
|
|
26
28
|
import traceback
|
|
29
|
+
from typing import Dict
|
|
27
30
|
from typing import List
|
|
28
31
|
from typing import Optional
|
|
29
32
|
from typing import Sequence
|
|
@@ -36,7 +39,11 @@ from paasta_tools.kubernetes.application.controller_wrappers import Application
|
|
|
36
39
|
from paasta_tools.kubernetes.application.controller_wrappers import (
|
|
37
40
|
get_application_wrapper,
|
|
38
41
|
)
|
|
42
|
+
from paasta_tools.kubernetes_tools import AUTOSCALING_OVERRIDES_CONFIGMAP_NAME
|
|
43
|
+
from paasta_tools.kubernetes_tools import AUTOSCALING_OVERRIDES_CONFIGMAP_NAMESPACE
|
|
39
44
|
from paasta_tools.kubernetes_tools import ensure_namespace
|
|
45
|
+
from paasta_tools.kubernetes_tools import get_namespaced_configmap
|
|
46
|
+
from paasta_tools.kubernetes_tools import HpaOverride
|
|
40
47
|
from paasta_tools.kubernetes_tools import InvalidKubernetesConfig
|
|
41
48
|
from paasta_tools.kubernetes_tools import KubeClient
|
|
42
49
|
from paasta_tools.kubernetes_tools import KubernetesDeploymentConfig
|
|
@@ -129,6 +136,8 @@ def main() -> None:
|
|
|
129
136
|
kube_client = KubeClient()
|
|
130
137
|
service_instances_valid = True
|
|
131
138
|
|
|
139
|
+
hpa_overrides = get_hpa_overrides(kube_client)
|
|
140
|
+
|
|
132
141
|
# validate the service_instance names
|
|
133
142
|
service_instances_with_valid_names = get_service_instances_with_valid_names(
|
|
134
143
|
service_instances=args.service_instance_list
|
|
@@ -162,6 +171,7 @@ def main() -> None:
|
|
|
162
171
|
soa_dir=soa_dir,
|
|
163
172
|
metrics_interface=deploy_metrics,
|
|
164
173
|
eks=args.eks,
|
|
174
|
+
hpa_overrides=hpa_overrides,
|
|
165
175
|
)
|
|
166
176
|
else:
|
|
167
177
|
setup_kube_succeeded = False
|
|
@@ -239,6 +249,92 @@ def get_kubernetes_deployment_config(
|
|
|
239
249
|
return service_instance_configs_list
|
|
240
250
|
|
|
241
251
|
|
|
252
|
+
def get_hpa_overrides(kube_client: KubeClient) -> Dict[str, Dict[str, HpaOverride]]:
|
|
253
|
+
"""
|
|
254
|
+
Load autoscaling overrides from the ConfigMap once.
|
|
255
|
+
|
|
256
|
+
This function reads the "paasta-autoscaling-overrides" ConfigMap in the "paasta" namespace
|
|
257
|
+
and extracts all valid (non-expired) overrides to return a dictionary mapping
|
|
258
|
+
service.instance pairs to override data (currently, just min_instances and when the
|
|
259
|
+
override should expire by).
|
|
260
|
+
|
|
261
|
+
The incoming ConfigMap is expected to have the following format:
|
|
262
|
+
{
|
|
263
|
+
$SERVICE_A.$INSTANCE_A: {
|
|
264
|
+
"min_instances": 2,
|
|
265
|
+
"expire_after": "2023-10-01T00:00:00Z"
|
|
266
|
+
},
|
|
267
|
+
$SERVICE_A.$INSTANCE_B: {
|
|
268
|
+
"min_instances": 3,
|
|
269
|
+
"expire_after": "2023-10-01T00:00:00Z"
|
|
270
|
+
},
|
|
271
|
+
...
|
|
272
|
+
},
|
|
273
|
+
$SERVICE_B.$INSTANCE_A: {
|
|
274
|
+
"min_instances": 1,
|
|
275
|
+
"expire_after": "2023-10-01T00:00:00Z"
|
|
276
|
+
},
|
|
277
|
+
$SERVICE_B.$INSTANCE_B: {
|
|
278
|
+
"min_instances": 2,
|
|
279
|
+
"expire_after": "2023-10-01T00:00:00Z"
|
|
280
|
+
},
|
|
281
|
+
...
|
|
282
|
+
}
|
|
283
|
+
"""
|
|
284
|
+
overrides: Dict[str, Dict[str, HpaOverride]] = {}
|
|
285
|
+
try:
|
|
286
|
+
configmap = get_namespaced_configmap(
|
|
287
|
+
name=AUTOSCALING_OVERRIDES_CONFIGMAP_NAME,
|
|
288
|
+
namespace=AUTOSCALING_OVERRIDES_CONFIGMAP_NAMESPACE,
|
|
289
|
+
kube_client=kube_client,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
if configmap.data:
|
|
293
|
+
current_time = time.time()
|
|
294
|
+
|
|
295
|
+
for service_instance, override_json in configmap.data.items():
|
|
296
|
+
try:
|
|
297
|
+
service, instance = service_instance.split(".")
|
|
298
|
+
override_metadata = json.loads(override_json)
|
|
299
|
+
expire_after = override_metadata.get("expire_after")
|
|
300
|
+
min_instances = override_metadata.get("min_instances")
|
|
301
|
+
|
|
302
|
+
if expire_after and min_instances:
|
|
303
|
+
if current_time < expire_after:
|
|
304
|
+
if service not in overrides:
|
|
305
|
+
overrides[service] = {}
|
|
306
|
+
|
|
307
|
+
overrides[service][instance] = {
|
|
308
|
+
"min_instances": min_instances,
|
|
309
|
+
"expire_after": expire_after,
|
|
310
|
+
}
|
|
311
|
+
log.info(
|
|
312
|
+
f"Found valid HPA override for {service}: "
|
|
313
|
+
f"{override_metadata.get('min_instances')} (expires at {expire_after})"
|
|
314
|
+
)
|
|
315
|
+
else:
|
|
316
|
+
log.info(
|
|
317
|
+
f"Ignoring expired HPA override for {service}.{instance}"
|
|
318
|
+
f"(expired at {expire_after})"
|
|
319
|
+
)
|
|
320
|
+
else:
|
|
321
|
+
log.warning(
|
|
322
|
+
f"Invalid HPA override for {service}.{instance}: "
|
|
323
|
+
f"missing 'min_instances' or 'expire_after': {override_metadata}"
|
|
324
|
+
)
|
|
325
|
+
except Exception:
|
|
326
|
+
log.exception(
|
|
327
|
+
f"Error parsing override for {service} - proceeding without overrides for this service."
|
|
328
|
+
)
|
|
329
|
+
except Exception:
|
|
330
|
+
# If ConfigMap doesn't exist or there's an error, just return empty dict
|
|
331
|
+
log.exception(
|
|
332
|
+
f"Unable to load the {AUTOSCALING_OVERRIDES_CONFIGMAP_NAME} ConfigMap - proceeding without overrides"
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
return overrides
|
|
336
|
+
|
|
337
|
+
|
|
242
338
|
def setup_kube_deployments(
|
|
243
339
|
kube_client: KubeClient,
|
|
244
340
|
cluster: str,
|
|
@@ -249,6 +345,7 @@ def setup_kube_deployments(
|
|
|
249
345
|
soa_dir: str = DEFAULT_SOA_DIR,
|
|
250
346
|
metrics_interface: metrics_lib.BaseMetrics = metrics_lib.NoMetrics("paasta"),
|
|
251
347
|
eks: bool = False,
|
|
348
|
+
hpa_overrides: Optional[Dict[str, Dict[str, HpaOverride]]] = None,
|
|
252
349
|
) -> bool:
|
|
253
350
|
|
|
254
351
|
if not service_instance_configs_list:
|
|
@@ -260,12 +357,17 @@ def setup_kube_deployments(
|
|
|
260
357
|
for deployment in existing_kube_deployments
|
|
261
358
|
}
|
|
262
359
|
|
|
360
|
+
hpa_overrides = hpa_overrides or {}
|
|
361
|
+
|
|
263
362
|
applications = [
|
|
264
363
|
create_application_object(
|
|
265
364
|
cluster=cluster,
|
|
266
365
|
soa_dir=soa_dir,
|
|
267
366
|
service_instance_config=service_instance,
|
|
268
367
|
eks=eks,
|
|
368
|
+
hpa_override=hpa_overrides.get(service_instance.service, {}).get(
|
|
369
|
+
service_instance.instance, None
|
|
370
|
+
),
|
|
269
371
|
)
|
|
270
372
|
if service_instance
|
|
271
373
|
else (_, None)
|
|
@@ -337,6 +439,7 @@ def create_application_object(
|
|
|
337
439
|
soa_dir: str,
|
|
338
440
|
service_instance_config: Union[KubernetesDeploymentConfig, EksDeploymentConfig],
|
|
339
441
|
eks: bool = False,
|
|
442
|
+
hpa_override: Optional[HpaOverride] = None,
|
|
340
443
|
) -> Tuple[bool, Optional[Application]]:
|
|
341
444
|
try:
|
|
342
445
|
formatted_application = service_instance_config.format_kubernetes_app()
|
|
@@ -344,8 +447,9 @@ def create_application_object(
|
|
|
344
447
|
log.error(traceback.format_exc())
|
|
345
448
|
return False, None
|
|
346
449
|
|
|
347
|
-
app = get_application_wrapper(formatted_application)
|
|
450
|
+
app = get_application_wrapper(formatted_application, hpa_override)
|
|
348
451
|
app.load_local_config(soa_dir, cluster, eks)
|
|
452
|
+
|
|
349
453
|
return True, app
|
|
350
454
|
|
|
351
455
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: paasta-tools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.22.0
|
|
4
4
|
Summary: Tools for Yelps SOA infrastructure
|
|
5
5
|
Author: Compute Infrastructure @ Yelp
|
|
6
6
|
Author-email: compute-infra@yelp.com
|
|
@@ -58,7 +58,7 @@ Requires-Dist: requests-cache >=0.4.10
|
|
|
58
58
|
Requires-Dist: retry
|
|
59
59
|
Requires-Dist: ruamel.yaml
|
|
60
60
|
Requires-Dist: sensu-plugin
|
|
61
|
-
Requires-Dist: service-configuration-lib >=3.2
|
|
61
|
+
Requires-Dist: service-configuration-lib >=3.3.2
|
|
62
62
|
Requires-Dist: signalfx
|
|
63
63
|
Requires-Dist: slackclient >=1.2.1
|
|
64
64
|
Requires-Dist: sticht >=1.1.0
|