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

Commit

Permalink
Grace period for new deployments (#9)
Browse files Browse the repository at this point in the history
* Deployments API version apps/v1

* Python 3.7 not supported by Travis yet

* v0.2

* grace period for new deployments
  • Loading branch information
hjacobs committed Aug 5, 2018
1 parent 07afab2 commit 2f76098
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ scm-source.json
.coverage
*.bak
htmlcov/
.pytest_cache/
6 changes: 3 additions & 3 deletions deploy/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ kind: Deployment
metadata:
labels:
application: kube-downscaler
version: v0.1
version: v0.2
name: kube-downscaler
spec:
replicas: 1
Expand All @@ -14,13 +14,13 @@ spec:
metadata:
labels:
application: kube-downscaler
version: v0.1
version: v0.2
spec:
serviceAccountName: kube-downscaler
containers:
- name: downscaler
# see https://github.com/hjacobs/kube-downscaler/releases
image: hjacobs/kube-downscaler:0.1
image: hjacobs/kube-downscaler:0.2
args:
# dry run by default, remove to perform downscaling
- --dry-run
Expand Down
29 changes: 21 additions & 8 deletions kube_downscaler/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,15 @@ def get_kube_api():
return api


def autoscale(namespace: str, default_uptime: str, default_downtime: str, exclude_namespaces: set, exclude_deployments: set, dry_run: bool=False):
def within_grace_period(deploy, grace_period: int):
creation_time = datetime.datetime.strptime(deploy.metadata['creationTimestamp'], '%Y-%m-%dT%H:%M:%SZ')
now = datetime.datetime.utcnow()
delta = now - creation_time
return delta.total_seconds() <= grace_period


def autoscale(namespace: str, default_uptime: str, default_downtime: str, exclude_namespaces: set, exclude_deployments: set, dry_run: bool=False,
grace_period: int=0):
api = get_kube_api()

now = datetime.datetime.utcnow()
Expand Down Expand Up @@ -96,12 +104,15 @@ def autoscale(namespace: str, default_uptime: str, default_downtime: str, exclud
deploy.obj['spec']['replicas'] = int(original_replicas)
update_needed = True
elif not is_uptime and replicas > 0:
target_replicas = 0
logger.info('Scaling down deployment %s/%s from %s to %s replicas (uptime: %s, downtime: %s)',
deploy.namespace, deploy.name, replicas, target_replicas, uptime, downtime)
deploy.annotations['downscaler/original-replicas'] = str(replicas)
deploy.obj['spec']['replicas'] = target_replicas
update_needed = True
if within_grace_period(deploy, grace_period):
logger.info('Deployment %s/%s within grace period (%ds), not scaling down (yet)', deploy.namespace, deploy.name, grace_period)
else:
target_replicas = 0
logger.info('Scaling down deployment %s/%s from %s to %s replicas (uptime: %s, downtime: %s)',
deploy.namespace, deploy.name, replicas, target_replicas, uptime, downtime)
deploy.annotations['downscaler/original-replicas'] = str(replicas)
deploy.obj['spec']['replicas'] = target_replicas
update_needed = True
if update_needed:
if dry_run:
logger.info('**DRY-RUN**: would update deployment %s/%s', deploy.namespace, deploy.name)
Expand Down Expand Up @@ -139,6 +150,7 @@ def main():
parser.add_argument('--once', help='Run loop only once and exit', action='store_true')
parser.add_argument('--interval', type=int, help='Loop interval (default: 300s)', default=300)
parser.add_argument('--namespace', help='Namespace')
parser.add_argument('--grace-period', type=int, help='Grace period in seconds for deployments before scaling down (default: 15min)', default=900)
parser.add_argument('--default-uptime', help='Default time range to scale up for (default: always)',
default=os.getenv('DEFAULT_UPTIME', 'always'))
parser.add_argument('--default-downtime', help='Default time range to scale down for (default: never)',
Expand All @@ -161,7 +173,8 @@ def main():
while True:
try:
autoscale(args.namespace, args.default_uptime, args.default_downtime,
args.exclude_namespaces.split(','), args.exclude_deployments.split(','), dry_run=args.dry_run)
args.exclude_namespaces.split(','), args.exclude_deployments.split(','), dry_run=args.dry_run,
grace_period=args.grace_period)
except Exception:
logger.exception('Failed to autoscale')
if args.once or handler.shutdown_now:
Expand Down
10 changes: 10 additions & 0 deletions tests/test_grace_period.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from datetime import datetime, timedelta
from kube_downscaler.main import Deployment, within_grace_period


def test_within_grace_period():
now = datetime.utcnow()
ts = now - timedelta(minutes=5)
deploy = Deployment(None, {'metadata': {'creationTimestamp': ts.strftime('%Y-%m-%dT%H:%M:%SZ')}})
assert within_grace_period(deploy, 900)
assert not within_grace_period(deploy, 180)

0 comments on commit 2f76098

Please sign in to comment.