Skip to content
This repository was archived by the owner on Oct 3, 2020. It is now read-only.

Commit c38f566

Browse files
authored
force-uptime on namespace now accepts periods (#114)
The annotation downscaler/force-uptime now accepts a period to force the uptime of a namespace during scheduled periods. Example usage: downscaler/force-uptime: 2020-05-02T08:00:00+02:00-2020-05-02T08:30:00+02:00
1 parent 9440409 commit c38f566

File tree

3 files changed

+124
-6
lines changed

3 files changed

+124
-6
lines changed

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ The following annotations are supported on the Namespace level:
194194
* ``downscaler/downscale-period``
195195
* ``downscaler/uptime``: set "uptime" for all resources in this namespace
196196
* ``downscaler/downtime``: set "downtime" for all resources in this namespace
197-
* ``downscaler/force-uptime``: force scaling up all resources in this namespace
197+
* ``downscaler/force-uptime``: force scaling up all resources in this namespace - can be ``true``/``false`` or a period
198198
* ``downscaler/exclude``: set to ``true`` to exclude all resources in the namespace
199199
* ``downscaler/exclude-until``: temporarily exclude all resources in the namespace until the given timestamp
200200
* ``downscaler/downtime-replicas``: overwrite the default target replicas to scale down to (default: zero)

kube_downscaler/scaler.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -374,12 +374,20 @@ def autoscale_resources(
374374
downscale_period_for_namespace = namespace_obj.annotations.get(
375375
DOWNSCALE_PERIOD_ANNOTATION, downscale_period
376376
)
377-
forced_uptime_for_namespace = (
378-
str(
379-
namespace_obj.annotations.get(FORCE_UPTIME_ANNOTATION, forced_uptime)
380-
).lower()
381-
== "true"
377+
forced_uptime_value_for_namespace = str(
378+
namespace_obj.annotations.get(FORCE_UPTIME_ANNOTATION, forced_uptime)
382379
)
380+
if forced_uptime_value_for_namespace.lower() == "true":
381+
forced_uptime_for_namespace = True
382+
elif forced_uptime_value_for_namespace.lower() == "false":
383+
forced_uptime_for_namespace = False
384+
elif forced_uptime_value_for_namespace:
385+
forced_uptime_for_namespace = matches_time_spec(
386+
now, forced_uptime_value_for_namespace
387+
)
388+
else:
389+
forced_uptime_for_namespace = False
390+
383391
for resource in resources:
384392
autoscale_resource(
385393
resource,

tests/test_scaler.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,3 +1098,113 @@ def get(url, version, **kwargs):
10981098
}
10991099
assert api.patch.call_args[1]["url"] == "/deployments/deploy-1"
11001100
assert json.loads(api.patch.call_args[1]["data"]) == patch_data
1101+
1102+
1103+
def test_scaler_namespace_force_uptime_period(monkeypatch):
1104+
api = MagicMock()
1105+
monkeypatch.setattr(
1106+
"kube_downscaler.scaler.helper.get_kube_api", MagicMock(return_value=api)
1107+
)
1108+
ORIGINAL_REPLICAS = 2
1109+
1110+
def get(url, version, **kwargs):
1111+
if url == "pods":
1112+
data = {"items": []}
1113+
elif url == "deployments":
1114+
data = {
1115+
"items": [
1116+
{
1117+
"metadata": {
1118+
"name": "deploy-1",
1119+
"namespace": "ns-1",
1120+
"creationTimestamp": "2019-03-01T16:38:00Z",
1121+
"annotations": {
1122+
ORIGINAL_REPLICAS_ANNOTATION: ORIGINAL_REPLICAS,
1123+
},
1124+
},
1125+
"spec": {"replicas": 0},
1126+
},
1127+
{
1128+
"metadata": {
1129+
"name": "deploy-2",
1130+
"namespace": "ns-2",
1131+
"creationTimestamp": "2019-03-01T16:38:00Z",
1132+
"annotations": {
1133+
ORIGINAL_REPLICAS_ANNOTATION: ORIGINAL_REPLICAS,
1134+
},
1135+
},
1136+
"spec": {"replicas": 0},
1137+
},
1138+
{
1139+
"metadata": {
1140+
"name": "deploy-3",
1141+
"namespace": "ns-3",
1142+
"creationTimestamp": "2019-03-01T16:38:00Z",
1143+
"annotations": {
1144+
ORIGINAL_REPLICAS_ANNOTATION: ORIGINAL_REPLICAS,
1145+
},
1146+
},
1147+
"spec": {"replicas": 0},
1148+
},
1149+
]
1150+
}
1151+
elif url == "namespaces/ns-1":
1152+
# past period
1153+
data = {
1154+
"metadata": {
1155+
"annotations": {
1156+
"downscaler/force-uptime": "2020-04-04T16:00:00+00:00-2020-04-05T16:00:00+00:00"
1157+
}
1158+
}
1159+
}
1160+
elif url == "namespaces/ns-2":
1161+
# current period
1162+
data = {
1163+
"metadata": {
1164+
"annotations": {
1165+
"downscaler/force-uptime": "2020-04-04T16:00:00+00:00-2040-04-05T16:00:00+00:00"
1166+
}
1167+
}
1168+
}
1169+
elif url == "namespaces/ns-3":
1170+
# future period
1171+
data = {
1172+
"metadata": {
1173+
"annotations": {
1174+
"downscaler/force-uptime": "2040-04-04T16:00:00+00:00-2040-04-05T16:00:00+00:00"
1175+
}
1176+
}
1177+
}
1178+
else:
1179+
raise Exception(f"unexpected call: {url}, {version}, {kwargs}")
1180+
1181+
response = MagicMock()
1182+
response.json.return_value = data
1183+
return response
1184+
1185+
api.get = get
1186+
1187+
include_resources = frozenset(["deployments"])
1188+
scale(
1189+
namespace=None,
1190+
upscale_period="never",
1191+
downscale_period="never",
1192+
default_uptime="never",
1193+
default_downtime="always",
1194+
include_resources=include_resources,
1195+
exclude_namespaces=[],
1196+
exclude_deployments=[],
1197+
dry_run=False,
1198+
grace_period=300,
1199+
)
1200+
1201+
# make sure that deploy-2 was updated
1202+
assert api.patch.call_count == 1
1203+
assert api.patch.call_args[1]["url"] == "/deployments/deploy-2"
1204+
assert (
1205+
json.loads(api.patch.call_args[1]["data"])["spec"]["replicas"]
1206+
== ORIGINAL_REPLICAS
1207+
)
1208+
assert not json.loads(api.patch.call_args[1]["data"])["metadata"]["annotations"][
1209+
ORIGINAL_REPLICAS_ANNOTATION
1210+
]

0 commit comments

Comments
 (0)