From 4ac5fee4ebbc1bb654e1960481767fa8266925d6 Mon Sep 17 00:00:00 2001 From: Samuel <40698384+samuel-esp@users.noreply.github.com> Date: Sun, 4 Aug 2024 15:31:12 +0200 Subject: [PATCH 1/4] added --upscale-target-only argument, refactored tests to reflect changes --- kube_downscaler/cmd.py | 5 + kube_downscaler/main.py | 3 + kube_downscaler/scaler.py | 21 +++- tests/test_autoscale_resource.py | 39 +++++++ tests/test_scaler.py | 170 ++++++++++++++++++++++++++++++- 5 files changed, 235 insertions(+), 3 deletions(-) diff --git a/kube_downscaler/cmd.py b/kube_downscaler/cmd.py index 994540e..cfb54cd 100644 --- a/kube_downscaler/cmd.py +++ b/kube_downscaler/cmd.py @@ -44,6 +44,11 @@ def get_parser(): parser.add_argument( "--interval", type=int, help="Loop interval (default: 30s)", default=30 ) + parser.add_argument( + "--upscale-target-only", + help="Upscale only resource in target when waking up namespaces", + action="store_true" + ) parser.add_argument( "--namespace", help="Namespace", diff --git a/kube_downscaler/main.py b/kube_downscaler/main.py index 79aecef..6bd4f28 100755 --- a/kube_downscaler/main.py +++ b/kube_downscaler/main.py @@ -40,6 +40,7 @@ def main(args=None): args.exclude_deployments, args.grace_period, args.interval, + args.upscale_target_only, args.dry_run, args.downtime_replicas, args.deployment_time_annotation, @@ -61,6 +62,7 @@ def run_loop( exclude_deployments, grace_period, interval, + upscale_target_only, dry_run, downtime_replicas, deployment_time_annotation=None, @@ -86,6 +88,7 @@ def run_loop( downscale_period, default_uptime, default_downtime, + upscale_target_only, include_resources=frozenset(include_resources.split(",")), exclude_namespaces=frozenset( re.compile(pattern) for pattern in exclude_namespaces.split(",") diff --git a/kube_downscaler/scaler.py b/kube_downscaler/scaler.py index 1e0612f..d509d74 100644 --- a/kube_downscaler/scaler.py +++ b/kube_downscaler/scaler.py @@ -81,6 +81,18 @@ def parse_time(timestamp: str) -> datetime.datetime: f"time data '{timestamp}' does not match any format ({', '.join(TIMESTAMP_FORMATS)})" ) + +# If the argument --upscale-target-only is present, resources from namespaces not in target won't be processed. +# Otherwise all resources from all namespaces will be processed for scaling if the have original_replicas annotation +def define_scope(exclude, original_replicas, upscale_target_only): + if upscale_target_only: + exclude_condition = exclude + else: + exclude_condition = exclude and not original_replicas + + return exclude_condition + + def is_grace_period_annotation_integer(value): try: int(value) # Attempt to convert the string to an integer @@ -834,6 +846,7 @@ def autoscale_resource( default_downtime: str, forced_uptime: bool, forced_downtime: bool, + upscale_target_only: bool, dry_run: bool, now: datetime.datetime, grace_period: int = 0, @@ -858,7 +871,9 @@ def autoscale_resource( if downtime_replicas_from_annotation is not None: downtime_replicas = downtime_replicas_from_annotation - if exclude: + exclude_condition = define_scope(exclude, original_replicas, upscale_target_only) + + if exclude_condition: logger.debug( f"{resource.kind} {resource.namespace}/{resource.name} was excluded" ) @@ -973,6 +988,7 @@ def autoscale_resources( default_uptime: str, default_downtime: str, forced_uptime: bool, + upscale_target_only: bool, constrained_downscaler: bool, dry_run: bool, now: datetime.datetime, @@ -1080,6 +1096,7 @@ def autoscale_resources( default_downtime_for_namespace, forced_uptime_for_namespace, forced_downtime_for_namespace, + upscale_target_only, dry_run, now, grace_period, @@ -1308,6 +1325,7 @@ def scale( downscale_period: str, default_uptime: str, default_downtime: str, + upscale_target_only: bool, include_resources: FrozenSet[str], exclude_namespaces: FrozenSet[Pattern], exclude_deployments: FrozenSet[str], @@ -1341,6 +1359,7 @@ def scale( default_uptime, default_downtime, forced_uptime, + upscale_target_only, constrained_downscaler, dry_run, now, diff --git a/tests/test_autoscale_resource.py b/tests/test_autoscale_resource.py index 74b38cb..20c809c 100644 --- a/tests/test_autoscale_resource.py +++ b/tests/test_autoscale_resource.py @@ -40,6 +40,7 @@ def test_swallow_exception(monkeypatch, resource, caplog): resource.metadata = {"creationTimestamp": "invalid-timestamp!"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -70,6 +71,7 @@ def test_swallow_exception_with_event(monkeypatch, resource, caplog): resource.metadata = {"creationTimestamp": "invalid-timestamp!"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -97,6 +99,7 @@ def test_exclude(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -122,6 +125,7 @@ def test_exclude_until_invalid_time(resource, caplog): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -151,6 +155,7 @@ def test_dry_run(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -179,6 +184,7 @@ def test_grace_period(resource): # resource was only created 1 minute ago, grace period is 5 minutes autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -204,6 +210,7 @@ def test_downtime_always(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -228,6 +235,7 @@ def test_downtime_interval(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="Mon-Fri 07:30-20:30 Europe/Berlin", @@ -252,6 +260,7 @@ def test_forced_uptime(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="Mon-Fri 07:30-20:30 Europe/Berlin", @@ -275,6 +284,7 @@ def test_forced_downtime(resource): resource.metadata = {"creationTimestamp": "2018-10-23T14:59:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="Mon-Fri 07:30-20:30 Europe/Berlin", @@ -295,6 +305,7 @@ def test_autoscale_bad_resource(): ) try: autoscale_resource( + upscale_target_only=False, resource=None, upscale_period="never", downscale_period="never", @@ -323,6 +334,7 @@ def test_scale_up(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="Mon-Fri 07:30-20:30 Europe/Berlin", @@ -350,6 +362,7 @@ def test_scale_up_downtime_replicas_annotation(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="Mon-Fri 07:30-20:30 Europe/Berlin", @@ -374,6 +387,7 @@ def test_downtime_replicas_annotation_invalid(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -397,6 +411,7 @@ def test_downtime_replicas_annotation_valid(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -420,6 +435,7 @@ def test_downtime_replicas_invalid(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -443,6 +459,7 @@ def test_downtime_replicas_valid(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -474,6 +491,7 @@ def test_set_annotation(): ) autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -506,6 +524,7 @@ def test_downscale_always(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="always", default_uptime="always", @@ -530,6 +549,7 @@ def test_downscale_period(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="Mon-Fri 20:30-24:00 Europe/Berlin", default_uptime="always", @@ -554,6 +574,7 @@ def test_downscale_period_overlaps(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="Mon-Fri 20:30-24:00 Europe/Berlin", downscale_period="Mon-Fri 20:30-24:00 Europe/Berlin", default_uptime="always", @@ -577,6 +598,7 @@ def test_downscale_period_not_match(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="Mon-Fri 07:30-10:00 Europe/Berlin", default_uptime="always", @@ -602,6 +624,7 @@ def test_downscale_period_resource_overrides_never(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="always", @@ -627,6 +650,7 @@ def test_downscale_period_resource_overrides_namespace(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="always", @@ -653,6 +677,7 @@ def test_upscale_period_resource_overrides_never(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="always", @@ -679,6 +704,7 @@ def test_upscale_period_resource_overrides_namespace(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="Mon-Fri 22:00-24:00 Europe/Berlin", downscale_period="never", default_uptime="always", @@ -711,6 +737,7 @@ def test_downscale_stack_deployment_ignored(): ) autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -735,6 +762,7 @@ def test_downscale_replicas_not_zero(resource): resource.metadata = {"creationTimestamp": "2018-10-23T21:55:00Z"} autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -750,6 +778,7 @@ def test_downscale_replicas_not_zero(resource): assert resource.annotations[ORIGINAL_REPLICAS_ANNOTATION] == "3" autoscale_resource( resource, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -785,6 +814,7 @@ def test_downscale_stack_with_autoscaling(): assert stack.replicas == 4 autoscale_resource( stack, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -818,6 +848,7 @@ def test_upscale_stack_with_autoscaling(): assert stack.replicas == 0 autoscale_resource( stack, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="always", @@ -852,6 +883,7 @@ def test_downscale_hpa_with_autoscaling(): ) autoscale_resource( hpa, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -887,6 +919,7 @@ def test_upscale_hpa_with_autoscaling(): ) autoscale_resource( hpa, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="always", @@ -920,6 +953,7 @@ def test_downscale_pdb_minavailable_with_autoscaling(): ) autoscale_resource( pdb, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -954,6 +988,7 @@ def test_upscale_pdb_minavailable_with_autoscaling(): ) autoscale_resource( pdb, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="always", @@ -984,6 +1019,7 @@ def test_downscale_pdb_maxunavailable_with_autoscaling(): ) autoscale_resource( pdb, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -1018,6 +1054,7 @@ def test_upscale_pdb_maxunavailable_with_autoscaling(): ) autoscale_resource( pdb, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="always", @@ -1052,6 +1089,7 @@ def test_downscale_daemonset_with_autoscaling(): ) autoscale_resource( ds, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="never", @@ -1093,6 +1131,7 @@ def test_upscale_daemonset_with_autoscaling(): ) autoscale_resource( ds, + upscale_target_only=False, upscale_period="never", downscale_period="never", default_uptime="always", diff --git a/tests/test_scaler.py b/tests/test_scaler.py index c5d8f93..46b7e30 100644 --- a/tests/test_scaler.py +++ b/tests/test_scaler.py @@ -56,6 +56,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="always", default_downtime="never", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -119,6 +120,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -203,6 +205,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[re.compile("system-ns")], exclude_deployments=[], @@ -279,6 +282,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[ re.compile("foo.*"), @@ -361,6 +365,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -434,6 +439,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -500,6 +506,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="always", default_downtime="never", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -518,7 +525,7 @@ def get(url, version, **kwargs): ORIGINAL_REPLICAS_ANNOTATION ] -def test_scaler_no_upscale_on_exclude(monkeypatch): +def test_scaler_no_upscale_on_exclude_with_upscale_target_only(monkeypatch): api = MagicMock() monkeypatch.setattr( "kube_downscaler.scaler.helper.get_kube_api", MagicMock(return_value=api) @@ -563,6 +570,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=True, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -576,7 +584,7 @@ def get(url, version, **kwargs): assert api.patch.call_count == 0 -def test_scaler_no_upscale_on_exclude_namespace(monkeypatch): +def test_scaler_no_upscale_on_exclude_namespace_with_upscale_target_only(monkeypatch): api = MagicMock() monkeypatch.setattr( "kube_downscaler.scaler.helper.get_kube_api", MagicMock(return_value=api) @@ -620,6 +628,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=True, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -633,6 +642,139 @@ def get(url, version, **kwargs): assert api.patch.call_count == 0 +def test_scaler_no_upscale_on_exclude_without_upscale_target_only(monkeypatch): + api = MagicMock() + monkeypatch.setattr( + "kube_downscaler.scaler.helper.get_kube_api", MagicMock(return_value=api) + ) + ORIGINAL_REPLICAS = 2 + + def get(url, version, **kwargs): + if url == "pods": + data = {"items": []} + elif url == "deployments": + data = { + "items": [ + { + "metadata": { + "name": "deploy-1", + "namespace": "default", + "annotations": { + EXCLUDE_ANNOTATION: "true", + ORIGINAL_REPLICAS_ANNOTATION: ORIGINAL_REPLICAS, + }, + }, + "spec": {"replicas": 0}, + }, + ] + } + elif url == "namespaces/default": + data = {"metadata": {}} + else: + raise Exception(f"unexpected call: {url}, {version}, {kwargs}") + + response = MagicMock() + response.json.return_value = data + return response + + api.get = get + + include_resources = frozenset(["deployments"]) + scale( + constrained_downscaler=False, + namespaces=[], + upscale_period="never", + downscale_period="never", + default_uptime="never", + default_downtime="always", + upscale_target_only=False, + include_resources=include_resources, + exclude_namespaces=[], + exclude_deployments=[], + matching_labels=frozenset([re.compile("")]), + dry_run=False, + grace_period=300, + admission_controller="", + downtime_replicas=0, + enable_events=False, + ) + + assert api.patch.call_count == 1 + assert api.patch.call_args[1]["url"] == "/deployments/deploy-1" + assert ( + json.loads(api.patch.call_args[1]["data"])["spec"]["replicas"] + == ORIGINAL_REPLICAS + ) + assert not json.loads(api.patch.call_args[1]["data"])["metadata"]["annotations"][ + ORIGINAL_REPLICAS_ANNOTATION + ] + +def test_scaler_no_upscale_on_exclude_namespace_without_upscale_target_only(monkeypatch): + api = MagicMock() + monkeypatch.setattr( + "kube_downscaler.scaler.helper.get_kube_api", MagicMock(return_value=api) + ) + ORIGINAL_REPLICAS = 2 + + def get(url, version, **kwargs): + if url == "pods": + data = {"items": []} + elif url == "deployments": + data = { + "items": [ + { + "metadata": { + "name": "deploy-1", + "namespace": "default", + "annotations": { + ORIGINAL_REPLICAS_ANNOTATION: ORIGINAL_REPLICAS, + }, + }, + "spec": {"replicas": 0}, + }, + ] + } + elif url == "namespaces/default": + data = {"metadata": {"annotations": {EXCLUDE_ANNOTATION: "true"}}} + else: + raise Exception(f"unexpected call: {url}, {version}, {kwargs}") + + response = MagicMock() + response.json.return_value = data + return response + + api.get = get + + include_resources = frozenset(["deployments"]) + scale( + constrained_downscaler=False, + namespaces=[], + upscale_period="never", + downscale_period="never", + default_uptime="never", + default_downtime="always", + upscale_target_only=False, + include_resources=include_resources, + exclude_namespaces=[], + exclude_deployments=[], + matching_labels=frozenset([re.compile("")]), + dry_run=False, + grace_period=300, + admission_controller="", + downtime_replicas=0, + enable_events=False, + ) + + assert api.patch.call_count == 1 + assert api.patch.call_args[1]["url"] == "/deployments/deploy-1" + assert ( + json.loads(api.patch.call_args[1]["data"])["spec"]["replicas"] + == ORIGINAL_REPLICAS + ) + assert not json.loads(api.patch.call_args[1]["data"])["metadata"]["annotations"][ + ORIGINAL_REPLICAS_ANNOTATION + ] + def test_scaler_always_upscale(monkeypatch): api = MagicMock() monkeypatch.setattr( @@ -674,6 +816,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -733,6 +876,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -797,6 +941,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -890,6 +1035,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -969,6 +1115,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1048,6 +1195,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1118,6 +1266,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1189,6 +1338,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1255,6 +1405,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1322,6 +1473,7 @@ def get(url, version, **kwargs): downscale_period="Mon-Tue 19:00-19:00 UTC", default_uptime="always", default_downtime="never", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1404,6 +1556,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1487,6 +1640,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1562,6 +1716,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=["sysdep-1"], @@ -1628,6 +1783,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1681,6 +1837,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1798,6 +1955,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1860,6 +2018,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="always", default_downtime="never", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1913,6 +2072,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="always", default_downtime="never", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -1973,6 +2133,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="always", default_downtime="never", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -2067,6 +2228,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="always", default_downtime="never", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -2341,6 +2503,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -2418,6 +2581,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -2487,6 +2651,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], @@ -2564,6 +2729,7 @@ def get(url, version, **kwargs): downscale_period="never", default_uptime="never", default_downtime="always", + upscale_target_only=False, include_resources=include_resources, exclude_namespaces=[], exclude_deployments=[], From 1f3a0fc4f2c76eac452f676387f833fb5fc911e4 Mon Sep 17 00:00:00 2001 From: Samuel <40698384+samuel-esp@users.noreply.github.com> Date: Sun, 4 Aug 2024 15:45:13 +0200 Subject: [PATCH 2/4] refactored docs to explain how to use --upscale-target-only --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index a9f063c..0eee933 100644 --- a/README.md +++ b/README.md @@ -379,6 +379,15 @@ Available command line options: configured via environment variable `DEFAULT_DOWNTIME` or via the annotation `downscaler/downtime` on each deployment +`--upscale-target-only` + +: Only namespaces currently targeted by the downscaler will be upscaled + during "wake up" times. For example, if your downscaler initially targets + namespaces A, B, and C, but later you reconfigure it to target only B and C, + namespace A will not be upscaled if it is downscaled at the exact time + of reconfiguration. This is an advanced configuration that most users + may not need. + `--exclude-namespaces` : Exclude namespaces from downscaling (list of regex patterns, From 2a8a7eff3ef12d479f3592e00637ffe1a19acf8d Mon Sep 17 00:00:00 2001 From: Samuel <40698384+samuel-esp@users.noreply.github.com> Date: Sun, 4 Aug 2024 16:01:50 +0200 Subject: [PATCH 3/4] refactored docs to better explain the argument --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0eee933..17ca630 100644 --- a/README.md +++ b/README.md @@ -381,12 +381,14 @@ Available command line options: `--upscale-target-only` -: Only namespaces currently targeted by the downscaler will be upscaled - during "wake up" times. For example, if your downscaler initially targets - namespaces A, B, and C, but later you reconfigure it to target only B and C, - namespace A will not be upscaled if it is downscaled at the exact time - of reconfiguration. This is an advanced configuration that most users - may not need. +: When this optional argument is used, only the namespaces currently + targeted by the downscaler will be upscaled during wake-up times. + For instance, if your downscaler initially manages namespaces + A, B, and C, but is later reconfigured to target only namespaces + B and C, namespace A will remain downscaled if it was downscaled + at the time of reconfiguration. If the parameter is not used, + all previously downscaled namespaces may be upscaled, even if + they are no longer targeted by the downscaler. `--exclude-namespaces` From 84ed31a5efc7803ceaa9f00f322101c492acaa43 Mon Sep 17 00:00:00 2001 From: Samuel <40698384+samuel-esp@users.noreply.github.com> Date: Sun, 4 Aug 2024 21:14:32 +0200 Subject: [PATCH 4/4] added new log message --- kube_downscaler/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kube_downscaler/main.py b/kube_downscaler/main.py index 6bd4f28..e3dcbd6 100755 --- a/kube_downscaler/main.py +++ b/kube_downscaler/main.py @@ -77,6 +77,7 @@ def run_loop( if len(namespaces) >= 1: constrained_downscaler = True + logging.info("Namespace argument is not empty, the downscaler will run in constrained mode") else: constrained_downscaler = False