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

Commit e787b0c

Browse files
Jason Gwartzhjacobs
authored andcommitted
Adds support for 'downtime-replicas' deployment annotation (#38)
* Adds support for scale-down-to pod annotation * Updates docs * Clarifies readme that annotation is on the deployment * Fixes formatting * Renames annotation to 'downtime-replicas'
1 parent 019fd08 commit e787b0c

File tree

3 files changed

+41
-3
lines changed

3 files changed

+41
-3
lines changed

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Scale down Kubernetes deployments and/or statefulsets during non-work hours.
1818

1919
Deployments are interchangeable by statefulset for this whole guide.
2020

21-
It will scale the deployment's replicas to zero if all of the following conditions are met:
21+
It will scale down the deployment's replicas if all of the following conditions are met:
2222

2323
* current time is not part of the "uptime" schedule or current time is part of the "downtime" schedule. The schedules are being evaluated in following order:
2424
* ``downscaler/downtime`` annotation on the deployment/stateful set
@@ -34,7 +34,7 @@ It will scale the deployment's replicas to zero if all of the following conditio
3434
* the deployment is not marked for exclusion (annotation ``downscaler/exclude: "true"``)
3535
* there are no active pods that force the whole cluster into uptime (annotation ``downscaler/force-uptime: "true"``)
3636

37-
37+
The deployment by default will be scaled down to zero replicas. This can be configured with a deployment annotation of ``downscaler/downtime-replicas``. (eg: ``downscaler/downtime-replicas: "1"``)
3838

3939
Example use cases:
4040

kube_downscaler/scaler.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
EXCLUDE_ANNOTATION = 'downscaler/exclude'
1414
UPTIME_ANNOTATION = 'downscaler/uptime'
1515
DOWNTIME_ANNOTATION = 'downscaler/downtime'
16+
DOWNTIME_REPLICAS_ANNOTATION = 'downscaler/downtime-replicas'
1617

1718

1819
def within_grace_period(deploy, grace_period: int, now: datetime.datetime):
@@ -68,7 +69,7 @@ def autoscale_resource(resource: pykube.objects.NamespacedAPIObject,
6869
logger.info('%s %s/%s within grace period (%ds), not scaling down (yet)',
6970
resource.kind, resource.namespace, resource.name, grace_period)
7071
else:
71-
target_replicas = 0
72+
target_replicas = resource.annotations.get(DOWNTIME_REPLICAS_ANNOTATION, 0)
7273
logger.info('Scaling down %s %s/%s from %s to %s replicas (uptime: %s, downtime: %s)',
7374
resource.kind, resource.namespace, resource.name, replicas, target_replicas,
7475
uptime, downtime)

tests/test_scaler.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,40 @@ def get(url, version, **kwargs):
105105
patch_data = {"metadata": {"name": "deploy-2", "namespace": "ns-2", "creationTimestamp": "2019-03-01T16:38:00Z"}, "spec": {"replicas": 0}}
106106
assert api.patch.call_args[1]['url'] == 'deployments/deploy-2'
107107
assert json.loads(api.patch.call_args[1]['data']) == patch_data
108+
109+
110+
def test_scaler_down_to(monkeypatch):
111+
api = MagicMock()
112+
monkeypatch.setattr('kube_downscaler.scaler.helper.get_kube_api', MagicMock(return_value=api))
113+
SCALE_TO = 1
114+
115+
def get(url, version, **kwargs):
116+
if url == 'pods':
117+
data = {'items': []}
118+
elif url == 'deployments':
119+
data = {'items': [
120+
{
121+
'metadata': {
122+
'name': 'deploy-1', 'namespace': 'default', 'creationTimestamp': '2019-03-01T16:38:00Z',
123+
'annotations': {'downscaler/downtime-replicas': SCALE_TO},
124+
}, 'spec': {'replicas': 5}
125+
},
126+
]}
127+
elif url == 'namespaces/default':
128+
data = {'metadata': {}}
129+
else:
130+
raise Exception(f'unexpected call: {url}, {version}, {kwargs}')
131+
132+
response = MagicMock()
133+
response.json.return_value = data
134+
return response
135+
136+
api.get = get
137+
138+
kinds = frozenset(['deployment'])
139+
scale(namespace=None, default_uptime='never', default_downtime='always', kinds=kinds,
140+
exclude_namespaces=[], exclude_deployments=[], exclude_statefulsets=[], dry_run=False, grace_period=300)
141+
142+
assert api.patch.call_count == 1
143+
assert api.patch.call_args[1]['url'] == 'deployments/deploy-1'
144+
assert json.loads(api.patch.call_args[1]['data'])["spec"]["replicas"] == SCALE_TO

0 commit comments

Comments
 (0)