chartreuse 7.0.0.dev1__tar.gz → 7.0.1__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.
Files changed (24) hide show
  1. {chartreuse-7.0.0.dev1/src/chartreuse.egg-info → chartreuse-7.0.1}/PKG-INFO +1 -1
  2. chartreuse-7.0.1/VERSION +1 -0
  3. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse/chartreuse_restore.py +2 -0
  4. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse/kubernetes_helper.py +57 -8
  5. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1/src/chartreuse.egg-info}/PKG-INFO +1 -1
  6. chartreuse-7.0.0.dev1/VERSION +0 -1
  7. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/LICENCE.md +0 -0
  8. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/MANIFEST.in +0 -0
  9. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/README.md +0 -0
  10. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/pyproject.toml +0 -0
  11. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/setup.cfg +0 -0
  12. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse/__init__.py +0 -0
  13. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse/chartreuse.py +0 -0
  14. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse/chartreuse_upgrade.py +0 -0
  15. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse/config_loader.py +0 -0
  16. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse/utils/__init__.py +0 -0
  17. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse/utils/alembic_migration_helper.py +0 -0
  18. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse/utils/command.py +0 -0
  19. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse.egg-info/SOURCES.txt +0 -0
  20. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse.egg-info/dependency_links.txt +0 -0
  21. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse.egg-info/entry_points.txt +0 -0
  22. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse.egg-info/requires.txt +0 -0
  23. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse.egg-info/top_level.txt +0 -0
  24. {chartreuse-7.0.0.dev1 → chartreuse-7.0.1}/src/chartreuse.egg-info/zip-safe +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chartreuse
3
- Version: 7.0.0.dev1
3
+ Version: 7.0.1
4
4
  Summary: Helper for Alembic migrations within Kubernetes.
5
5
  Author-email: wiremind <dev@wiremind.io>
6
6
  License-Expression: LGPL-3.0-or-later
@@ -0,0 +1 @@
1
+ 7.0.1
@@ -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,16 @@ 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"
24
28
  # Set by stop_pods() on every Deployment it scales down, cleared by start_pods() and
25
29
  # restore_stopped_pods(). Allows telling "stopped by us" apart from "deliberately scaled to 0".
26
30
  STOPPED_ANNOTATION = "wiremind.io/stopped-by"
31
+ # Set by pause_hpa() on the HPA itself; stores the pre-pause spec.behavior as JSON ("" when
32
+ # absent) so resume_hpa() can restore it exactly. Presence of the annotation == "paused by us".
33
+ PAUSED_ANNOTATION = "wiremind.io/pre-pause-scale-behavior"
27
34
 
28
35
 
29
36
  def load_kubernetes_config(use_kubeconfig: bool | None = None) -> None:
@@ -254,14 +261,51 @@ class KubernetesDeploymentManager:
254
261
  return eds_dict
255
262
 
256
263
  @retry_kubernetes_request
257
- def disable_hpa(self, *, deployment_name: str) -> None:
264
+ def pause_hpa(self, *, deployment_name: str) -> None:
265
+ """
266
+ Forbid the Deployment's HPAs from scaling up (spec.behavior.scaleUp.selectPolicy:
267
+ Disabled) so they can't start pods mid-migration. Unlike breaking the scaleTargetRef,
268
+ the HPA stays Healthy: no FailedGetScale events, no Degraded ArgoCD application.
269
+
270
+ The pre-pause spec.behavior is saved in PAUSED_ANNOTATION so resume_hpa() can restore
271
+ it exactly. Idempotent: an already-paused HPA is left alone (never overwrite the
272
+ saved original with a paused state).
273
+ """
258
274
  for hpa in self.get_deployment_hpa(deployment_name=deployment_name):
259
- # Tell the hpa to manage a non-existing Deployment
260
- hpa.spec.scale_target_ref.name = f"{HPA_ID_PREFIX}-{deployment_name}"
261
- self.patch_deployment_hpa(hpa_name=hpa.metadata.name, body=hpa)
275
+ if PAUSED_ANNOTATION in (hpa.metadata.annotations or {}):
276
+ continue
277
+ original_behavior = self.client_autoscalingv2_api.api_client.sanitize_for_serialization(hpa.spec.behavior)
278
+ saved = json.dumps(original_behavior) if original_behavior else ""
279
+ body = {
280
+ "metadata": {"annotations": {PAUSED_ANNOTATION: saved}},
281
+ "spec": {"behavior": {"scaleUp": {"selectPolicy": "Disabled"}}},
282
+ }
283
+ self.patch_deployment_hpa(hpa_name=hpa.metadata.name, body=body)
284
+
285
+ @retry_kubernetes_request
286
+ def resume_hpa(self, *, deployment_name: str) -> None:
287
+ """
288
+ Undo pause_hpa(): restore the exact pre-pause spec.behavior (or clear it when there
289
+ was none) and drop PAUSED_ANNOTATION. No-op on HPAs that don't carry the annotation,
290
+ so it is always safe to call.
291
+ """
292
+ for hpa in self.get_deployment_hpa(deployment_name=deployment_name):
293
+ saved = (hpa.metadata.annotations or {}).get(PAUSED_ANNOTATION)
294
+ if saved is None:
295
+ continue
296
+ body = {
297
+ "metadata": {"annotations": {PAUSED_ANNOTATION: None}},
298
+ "spec": {"behavior": json.loads(saved) if saved else None},
299
+ }
300
+ self.patch_deployment_hpa(hpa_name=hpa.metadata.name, body=body)
262
301
 
263
302
  @retry_kubernetes_request
264
303
  def re_enable_hpa(self, *, deployment_name: str) -> None:
304
+ """
305
+ Repair an HPA left pointing at a wm--disabled--kube- target by a pre-7.0.1 chartreuse
306
+ whose run crashed before re-enabling. 7.0.1+ never creates such renames.
307
+ TODO: remove in 8.0.
308
+ """
265
309
  for hpa in self.get_deployment_hpa(deployment_name=f"{HPA_ID_PREFIX}-{deployment_name}"):
266
310
  hpa.spec.scale_target_ref.name = deployment_name
267
311
  self.patch_deployment_hpa(hpa_name=hpa.metadata.name, body=hpa)
@@ -278,9 +322,9 @@ class KubernetesDeploymentManager:
278
322
  """
279
323
  for deployment_name in deployment_dict:
280
324
  self.annotate_deployment(deployment_name, {STOPPED_ANNOTATION: self.release_name})
325
+ self.pause_hpa(deployment_name=deployment_name)
281
326
  for _ in range(self.SCALE_DOWN_MAX_WAIT_TIME):
282
327
  for deployment_name in deployment_dict:
283
- self.disable_hpa(deployment_name=deployment_name)
284
328
  self.scale_down_deployment(deployment_name)
285
329
  if self._are_deployments_stopped(deployment_dict):
286
330
  break
@@ -324,6 +368,7 @@ class KubernetesDeploymentManager:
324
368
  if len(priority_dict):
325
369
  scaled = True
326
370
  for name, expected_scale in priority_dict.items():
371
+ self.resume_hpa(deployment_name=name)
327
372
  self.re_enable_hpa(deployment_name=name)
328
373
  self.scale_up_deployment(name, expected_scale)
329
374
  self.annotate_deployment(name, {STOPPED_ANNOTATION: None})
@@ -336,8 +381,11 @@ class KubernetesDeploymentManager:
336
381
  """
337
382
  Restore Deployments that stop_pods() scaled down and that nothing scaled back up since.
338
383
 
339
- Only Deployments still carrying STOPPED_ANNOTATION are touched, so a Deployment
340
- deliberately scaled to zero outside of stop_pods() is left alone.
384
+ Only the replica restore is gated on STOPPED_ANNOTATION, so a Deployment deliberately
385
+ scaled to zero outside of stop_pods() is left alone. HPA resume/repair on the other
386
+ hand runs for EVERY tracked Deployment: a pause left behind by a crashed run is not
387
+ self-healing (ArgoCD ignores the live-only spec.behavior field), and resuming an
388
+ unpaused HPA is a no-op.
341
389
 
342
390
  Meant to run after the deployment that follows a stop_pods() without start_pods()
343
391
  (CHARTREUSE_UPGRADE_BEFORE_DEPLOYMENT): that deployment restores spec.replicas of
@@ -351,6 +399,8 @@ class KubernetesDeploymentManager:
351
399
  restored: list[str] = []
352
400
  for priority in sorted(expected_deployment_scale_dict):
353
401
  for name, expected_scale in expected_deployment_scale_dict[priority].items():
402
+ self.resume_hpa(deployment_name=name)
403
+ self.re_enable_hpa(deployment_name=name)
354
404
  try:
355
405
  deployment = self.client_appsv1_api.read_namespaced_deployment(name, self.namespace)
356
406
  except kubernetes.client.rest.ApiException as e:
@@ -359,7 +409,6 @@ class KubernetesDeploymentManager:
359
409
  raise
360
410
  if STOPPED_ANNOTATION not in (deployment.metadata.annotations or {}):
361
411
  continue
362
- self.re_enable_hpa(deployment_name=name)
363
412
  if not deployment.spec.replicas:
364
413
  target_scale = expected_scale
365
414
  if target_scale < 1 and any(self.get_deployment_hpa(deployment_name=name)):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chartreuse
3
- Version: 7.0.0.dev1
3
+ Version: 7.0.1
4
4
  Summary: Helper for Alembic migrations within Kubernetes.
5
5
  Author-email: wiremind <dev@wiremind.io>
6
6
  License-Expression: LGPL-3.0-or-later
@@ -1 +0,0 @@
1
- 7.0.0.dev1
File without changes
File without changes
File without changes
File without changes