chartreuse 7.0.0.dev1__tar.gz → 7.0.2__tar.gz
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.
- {chartreuse-7.0.0.dev1/src/chartreuse.egg-info → chartreuse-7.0.2}/PKG-INFO +1 -1
- chartreuse-7.0.2/VERSION +1 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse/chartreuse_restore.py +2 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse/kubernetes_helper.py +60 -9
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2/src/chartreuse.egg-info}/PKG-INFO +1 -1
- chartreuse-7.0.0.dev1/VERSION +0 -1
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/LICENCE.md +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/MANIFEST.in +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/README.md +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/pyproject.toml +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/setup.cfg +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse/__init__.py +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse/chartreuse.py +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse/chartreuse_upgrade.py +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse/config_loader.py +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse/utils/__init__.py +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse/utils/alembic_migration_helper.py +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse/utils/command.py +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse.egg-info/SOURCES.txt +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse.egg-info/dependency_links.txt +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse.egg-info/entry_points.txt +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse.egg-info/requires.txt +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse.egg-info/top_level.txt +0 -0
- {chartreuse-7.0.0.dev1 → chartreuse-7.0.2}/src/chartreuse.egg-info/zip-safe +0 -0
chartreuse-7.0.2/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
7.0.2
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
|
+
from .chartreuse import configure_logging
|
|
4
5
|
from .chartreuse_upgrade import ensure_safe_run
|
|
5
6
|
from .kubernetes_helper import KubernetesDeploymentManager
|
|
6
7
|
|
|
@@ -22,6 +23,7 @@ def main() -> None:
|
|
|
22
23
|
the annotation set by stop_pods() are touched. If this program fails, the deployment is
|
|
23
24
|
considered as failed: stranded workers must be visible, not a log line.
|
|
24
25
|
"""
|
|
26
|
+
configure_logging()
|
|
25
27
|
ensure_safe_run()
|
|
26
28
|
|
|
27
29
|
release_name: str = os.environ["CHARTREUSE_RELEASE_NAME"]
|
|
@@ -9,6 +9,7 @@ Depends only on the official kubernetes client.
|
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import functools
|
|
12
|
+
import json
|
|
12
13
|
import logging
|
|
13
14
|
import os
|
|
14
15
|
import pprint
|
|
@@ -20,10 +21,18 @@ import kubernetes
|
|
|
20
21
|
|
|
21
22
|
logger = logging.getLogger(__name__)
|
|
22
23
|
|
|
24
|
+
# Target-rename prefix used by the pre-7.0.1 HPA disable mechanism. Only the repair path
|
|
25
|
+
# (re_enable_hpa) remains, for HPAs left renamed by an older chartreuse.
|
|
26
|
+
# TODO: remove in 8.0 together with re_enable_hpa.
|
|
23
27
|
HPA_ID_PREFIX = "wm--disabled--kube"
|
|
28
|
+
# A Pod in a terminal phase holds no resource: it is not a living replica.
|
|
29
|
+
TERMINAL_POD_PHASES = ("Failed", "Succeeded")
|
|
24
30
|
# Set by stop_pods() on every Deployment it scales down, cleared by start_pods() and
|
|
25
31
|
# restore_stopped_pods(). Allows telling "stopped by us" apart from "deliberately scaled to 0".
|
|
26
32
|
STOPPED_ANNOTATION = "wiremind.io/stopped-by"
|
|
33
|
+
# Set by pause_hpa() on the HPA itself; stores the pre-pause spec.behavior as JSON ("" when
|
|
34
|
+
# absent) so resume_hpa() can restore it exactly. Presence of the annotation == "paused by us".
|
|
35
|
+
PAUSED_ANNOTATION = "wiremind.io/pre-pause-scale-behavior"
|
|
27
36
|
|
|
28
37
|
|
|
29
38
|
def load_kubernetes_config(use_kubeconfig: bool | None = None) -> None:
|
|
@@ -177,7 +186,7 @@ class KubernetesDeploymentManager:
|
|
|
177
186
|
|
|
178
187
|
current_scale = 0
|
|
179
188
|
for pod in pod_list:
|
|
180
|
-
if pod.status.phase not in
|
|
189
|
+
if pod.status.phase not in TERMINAL_POD_PHASES:
|
|
181
190
|
current_scale += 1
|
|
182
191
|
|
|
183
192
|
if current_scale > 0:
|
|
@@ -254,14 +263,51 @@ class KubernetesDeploymentManager:
|
|
|
254
263
|
return eds_dict
|
|
255
264
|
|
|
256
265
|
@retry_kubernetes_request
|
|
257
|
-
def
|
|
266
|
+
def pause_hpa(self, *, deployment_name: str) -> None:
|
|
267
|
+
"""
|
|
268
|
+
Forbid the Deployment's HPAs from scaling up (spec.behavior.scaleUp.selectPolicy:
|
|
269
|
+
Disabled) so they can't start pods mid-migration. Unlike breaking the scaleTargetRef,
|
|
270
|
+
the HPA stays Healthy: no FailedGetScale events, no Degraded ArgoCD application.
|
|
271
|
+
|
|
272
|
+
The pre-pause spec.behavior is saved in PAUSED_ANNOTATION so resume_hpa() can restore
|
|
273
|
+
it exactly. Idempotent: an already-paused HPA is left alone (never overwrite the
|
|
274
|
+
saved original with a paused state).
|
|
275
|
+
"""
|
|
258
276
|
for hpa in self.get_deployment_hpa(deployment_name=deployment_name):
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
self.
|
|
277
|
+
if PAUSED_ANNOTATION in (hpa.metadata.annotations or {}):
|
|
278
|
+
continue
|
|
279
|
+
original_behavior = self.client_autoscalingv2_api.api_client.sanitize_for_serialization(hpa.spec.behavior)
|
|
280
|
+
saved = json.dumps(original_behavior) if original_behavior else ""
|
|
281
|
+
body = {
|
|
282
|
+
"metadata": {"annotations": {PAUSED_ANNOTATION: saved}},
|
|
283
|
+
"spec": {"behavior": {"scaleUp": {"selectPolicy": "Disabled"}}},
|
|
284
|
+
}
|
|
285
|
+
self.patch_deployment_hpa(hpa_name=hpa.metadata.name, body=body)
|
|
286
|
+
|
|
287
|
+
@retry_kubernetes_request
|
|
288
|
+
def resume_hpa(self, *, deployment_name: str) -> None:
|
|
289
|
+
"""
|
|
290
|
+
Undo pause_hpa(): restore the exact pre-pause spec.behavior (or clear it when there
|
|
291
|
+
was none) and drop PAUSED_ANNOTATION. No-op on HPAs that don't carry the annotation,
|
|
292
|
+
so it is always safe to call.
|
|
293
|
+
"""
|
|
294
|
+
for hpa in self.get_deployment_hpa(deployment_name=deployment_name):
|
|
295
|
+
saved = (hpa.metadata.annotations or {}).get(PAUSED_ANNOTATION)
|
|
296
|
+
if saved is None:
|
|
297
|
+
continue
|
|
298
|
+
body = {
|
|
299
|
+
"metadata": {"annotations": {PAUSED_ANNOTATION: None}},
|
|
300
|
+
"spec": {"behavior": json.loads(saved) if saved else None},
|
|
301
|
+
}
|
|
302
|
+
self.patch_deployment_hpa(hpa_name=hpa.metadata.name, body=body)
|
|
262
303
|
|
|
263
304
|
@retry_kubernetes_request
|
|
264
305
|
def re_enable_hpa(self, *, deployment_name: str) -> None:
|
|
306
|
+
"""
|
|
307
|
+
Repair an HPA left pointing at a wm--disabled--kube- target by a pre-7.0.1 chartreuse
|
|
308
|
+
whose run crashed before re-enabling. 7.0.1+ never creates such renames.
|
|
309
|
+
TODO: remove in 8.0.
|
|
310
|
+
"""
|
|
265
311
|
for hpa in self.get_deployment_hpa(deployment_name=f"{HPA_ID_PREFIX}-{deployment_name}"):
|
|
266
312
|
hpa.spec.scale_target_ref.name = deployment_name
|
|
267
313
|
self.patch_deployment_hpa(hpa_name=hpa.metadata.name, body=hpa)
|
|
@@ -278,9 +324,9 @@ class KubernetesDeploymentManager:
|
|
|
278
324
|
"""
|
|
279
325
|
for deployment_name in deployment_dict:
|
|
280
326
|
self.annotate_deployment(deployment_name, {STOPPED_ANNOTATION: self.release_name})
|
|
327
|
+
self.pause_hpa(deployment_name=deployment_name)
|
|
281
328
|
for _ in range(self.SCALE_DOWN_MAX_WAIT_TIME):
|
|
282
329
|
for deployment_name in deployment_dict:
|
|
283
|
-
self.disable_hpa(deployment_name=deployment_name)
|
|
284
330
|
self.scale_down_deployment(deployment_name)
|
|
285
331
|
if self._are_deployments_stopped(deployment_dict):
|
|
286
332
|
break
|
|
@@ -324,6 +370,7 @@ class KubernetesDeploymentManager:
|
|
|
324
370
|
if len(priority_dict):
|
|
325
371
|
scaled = True
|
|
326
372
|
for name, expected_scale in priority_dict.items():
|
|
373
|
+
self.resume_hpa(deployment_name=name)
|
|
327
374
|
self.re_enable_hpa(deployment_name=name)
|
|
328
375
|
self.scale_up_deployment(name, expected_scale)
|
|
329
376
|
self.annotate_deployment(name, {STOPPED_ANNOTATION: None})
|
|
@@ -336,8 +383,11 @@ class KubernetesDeploymentManager:
|
|
|
336
383
|
"""
|
|
337
384
|
Restore Deployments that stop_pods() scaled down and that nothing scaled back up since.
|
|
338
385
|
|
|
339
|
-
Only
|
|
340
|
-
|
|
386
|
+
Only the replica restore is gated on STOPPED_ANNOTATION, so a Deployment deliberately
|
|
387
|
+
scaled to zero outside of stop_pods() is left alone. HPA resume/repair on the other
|
|
388
|
+
hand runs for EVERY tracked Deployment: a pause left behind by a crashed run is not
|
|
389
|
+
self-healing (ArgoCD ignores the live-only spec.behavior field), and resuming an
|
|
390
|
+
unpaused HPA is a no-op.
|
|
341
391
|
|
|
342
392
|
Meant to run after the deployment that follows a stop_pods() without start_pods()
|
|
343
393
|
(CHARTREUSE_UPGRADE_BEFORE_DEPLOYMENT): that deployment restores spec.replicas of
|
|
@@ -351,6 +401,8 @@ class KubernetesDeploymentManager:
|
|
|
351
401
|
restored: list[str] = []
|
|
352
402
|
for priority in sorted(expected_deployment_scale_dict):
|
|
353
403
|
for name, expected_scale in expected_deployment_scale_dict[priority].items():
|
|
404
|
+
self.resume_hpa(deployment_name=name)
|
|
405
|
+
self.re_enable_hpa(deployment_name=name)
|
|
354
406
|
try:
|
|
355
407
|
deployment = self.client_appsv1_api.read_namespaced_deployment(name, self.namespace)
|
|
356
408
|
except kubernetes.client.rest.ApiException as e:
|
|
@@ -359,7 +411,6 @@ class KubernetesDeploymentManager:
|
|
|
359
411
|
raise
|
|
360
412
|
if STOPPED_ANNOTATION not in (deployment.metadata.annotations or {}):
|
|
361
413
|
continue
|
|
362
|
-
self.re_enable_hpa(deployment_name=name)
|
|
363
414
|
if not deployment.spec.replicas:
|
|
364
415
|
target_scale = expected_scale
|
|
365
416
|
if target_scale < 1 and any(self.get_deployment_hpa(deployment_name=name)):
|
chartreuse-7.0.0.dev1/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
7.0.0.dev1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|