Skip to content

Commit

Permalink
Merge branch 'main' into chore/CleanOutdatedFiles
Browse files Browse the repository at this point in the history
  • Loading branch information
JTaeuber authored Nov 29, 2024
2 parents 7765574 + 0631d35 commit c887d6f
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 24 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -466,13 +466,14 @@ issues are observed in the logs.
`--max-retries-on-conflict`
: Optional: Specifies the maximum number of retries KubeDownscaler should perform
: Optional: Specifies the maximum number of retries KubeDownscaler should perform
when encountering a conflict error (HTTP 409). These errors occur when one of the
resources, just before being processed by Kube Downscaler, is modified by another entity,
such as an HPA, CI/CD pipeline, or manual intervention. If enabled, Kube Downscaler will
retry the update immediately, without waiting for the next iteration (default: 0). This
retry the update immediately, without waiting for the next iteration (default: 0). This
argument is strongly recommended when using the `--once` argument to process large clusters
### Constrained Mode (Limited Access Mode)
The Constrained Mode (also known as Limited Access Mode) is designed for users who do not have full cluster access.
Expand Down
17 changes: 4 additions & 13 deletions kube_downscaler/scaler.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pykube import CronJob
from pykube import CustomResourceDefinition
from pykube import DaemonSet
from pykube import HTTPClient
from pykube import Deployment
from pykube import HorizontalPodAutoscaler
from pykube import HTTPClient
Expand Down Expand Up @@ -311,12 +312,7 @@ def get_resources(kind, api, namespaces: FrozenSet[str], excluded_namespaces):


def get_resource(kind, api, namespace, resource_name: str):
try:
resource = (
kind.objects(api)
.filter(namespace=namespace)
.get_or_none(name=resource_name)
)
resource = kind.objects(api).filter(namespace=namespace).get_or_none(name=resource_name)
if resource is None:
logger.debug(f"{kind.endpoint} {namespace}/{resource_name} not found")
except requests.HTTPError as e:
Expand All @@ -335,9 +331,7 @@ def get_resource(kind, api, namespace, resource_name: str):
return resource


def scale_jobs_without_admission_controller(
plural, admission_controller, constrainted_downscaler
):
def scale_jobs_without_admission_controller(plural, admission_controller, constrainted_downscaler):
return (plural == "jobs" and admission_controller == "") or constrainted_downscaler


Expand Down Expand Up @@ -1080,10 +1074,7 @@ def autoscale_resource(
else:
resource.update()
except Exception as e:
if (
isinstance(e, HTTPError)
and "the object has been modified" in str(e).lower()
):
if isinstance(e, HTTPError) and "the object has been modified" in str(e).lower():
logger.warning(
f"Unable to process {resource.kind} {resource.namespace}/{resource.name} because it was recently modified"
)
Expand Down
23 changes: 14 additions & 9 deletions tests/test_autoscale_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import re
from datetime import datetime
from datetime import timezone
from unittest.mock import MagicMock
from unittest.mock import MagicMock, patch

import pykube
import pytest
Expand Down Expand Up @@ -33,7 +33,6 @@ def resource():
res.annotations = {}
return res


def test_swallow_exception(monkeypatch, resource, caplog):
api = MagicMock()
monkeypatch.setattr(
Expand Down Expand Up @@ -1635,14 +1634,16 @@ def test_downscale_resource_concurrently_modified(monkeypatch):
"namespace": "default",
"creationTimestamp": "2018-10-23T21:55:00Z",
},
"spec": {"template": {"spec": {}}},
},
"spec": {
"template": {
"spec": {}
}
}
}
)

# Replace update method to track calls
ds.update = MagicMock(
side_effect=[http_error, None]
) # Simulate conflict and success
ds.update = MagicMock(side_effect=[http_error, None]) # Simulate conflict and success

# Mock get_resource with MagicMock
mock_get_resource = MagicMock(return_value=ds)
Expand Down Expand Up @@ -1699,8 +1700,12 @@ def test_downscale_resource_concurrently_modified_without_retries_allowed(monkey
"namespace": "default",
"creationTimestamp": "2018-10-23T21:55:00Z",
},
"spec": {"template": {"spec": {}}},
},
"spec": {
"template": {
"spec": {}
}
}
}
)

# Mock get_resource with MagicMock
Expand Down
36 changes: 36 additions & 0 deletions tests/test_scaler.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,42 @@
from kube_downscaler.scaler import scale_down_jobs
from kube_downscaler.scaler import scale_up_jobs

def test_scale_custom_timeout(monkeypatch):
api_server_timeout = 15 # Defined by the user
api = MagicMock()
api.timeout = 15 # Expected timeout

mock_get_kube_api = MagicMock(return_value=api)
monkeypatch.setattr(
"kube_downscaler.scaler.helper.get_kube_api", mock_get_kube_api
)

scale(
namespaces=frozenset({"default"}),
upscale_period="never",
downscale_period="never",
default_uptime="never",
default_downtime="always",
upscale_target_only=False,
include_resources=frozenset(["pods"]),
exclude_namespaces=frozenset(),
exclude_deployments=frozenset(),
dry_run=False,
grace_period=300,
admission_controller="",
constrained_downscaler=False,
api_server_timeout=api_server_timeout,
max_retries_on_conflict=0,
downtime_replicas=0,
deployment_time_annotation=None,
enable_events=False,
matching_labels=frozenset(),
)

# ensure get_kube_api is called with the correct timeout value
mock_get_kube_api.assert_called_once_with(api_server_timeout)
# ensure timeout value is correctly set on the returned object
assert api.timeout == api_server_timeout

def test_scale_custom_timeout(monkeypatch):
api_server_timeout = 15 # Defined by the user
Expand Down

0 comments on commit c887d6f

Please sign in to comment.