Skip to content

Commit 123bb97

Browse files
committed
feat(config): set config to celery
1 parent 25b9a5e commit 123bb97

4 files changed

Lines changed: 64 additions & 38 deletions

File tree

rootfs/api/tasks.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,38 @@ def run_pipeline(release, *args, **kwargs):
6262
signals.request_finished.send(sender=task_id)
6363

6464

65+
@shared_task(
66+
autoretry_for=(ServiceUnavailable, ),
67+
retry_kwargs={'max_retries': None}
68+
)
69+
def run_deploy(release, config):
70+
task_id = uuid.uuid4().hex
71+
signals.request_started.send(sender=task_id)
72+
try:
73+
if release.build is not None:
74+
procfile_types = set()
75+
for field, diff in config.diff().items():
76+
if field in config.procfile_fields:
77+
for value in diff.values():
78+
procfile_types.update(value.keys())
79+
# all_diff_fields changed, deploy all.
80+
procfile_types = procfile_types if procfile_types else None
81+
config.app.deploy(release, procfile_types)
82+
release.state = "succeed"
83+
release.save()
84+
except Exception as e:
85+
release.state = "crashed"
86+
release.failed = True
87+
if release.summary:
88+
release.summary += " "
89+
release.summary += "{} deployed a config that failed".format(release.owner)
90+
# Get the exception that has occured
91+
release.exception = "error: {}".format(str(e))
92+
release.save()
93+
finally:
94+
signals.request_finished.send(sender=task_id)
95+
96+
6597
@shared_task(
6698
autoretry_for=(ServiceUnavailable, ),
6799
retry_jitter=True,

rootfs/api/tests/test_config.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,11 @@ def test_config_deploy_failure(self, mock_requests):
312312
url = f'/v2/apps/{app_id}/config'
313313
body = {'values': json.dumps({'test': "testvalue"})}
314314
response = self.client.post(url, body)
315-
self.assertEqual(response.status_code, 400)
315+
self.assertEqual(response.status_code, 201)
316+
app = App.objects.get(id=app_id)
317+
release = app.release_set.latest()
318+
self.assertEqual(release.failed, True)
319+
self.assertEqual(release.exception, "error: Boom!")
316320

317321
def test_invalid_config_keys(self, mock_requests):
318322
"""Test that invalid config keys are rejected.
@@ -416,8 +420,10 @@ def test_config_failures(self, mock_requests):
416420
url = f'/v2/apps/{app_id}/config'
417421
body = {'values': json.dumps({'test': "testvalue"})}
418422
response = self.client.post(url, body)
419-
self.assertEqual(response.status_code, 400)
423+
self.assertEqual(response.status_code, 201)
420424
self.assertEqual(app.release_set.latest().version, 4)
425+
self.assertEqual(app.release_set.latest().failed, True)
426+
self.assertEqual(app.release_set.latest().exception, "error: Boom!")
421427
self.assertEqual(app.release_set.filter(failed=False).latest().version, 3)
422428

423429
# create a build to see that the new release is created with the last successful config

rootfs/api/views.py

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
from api import monitor, models, permissions, serializers, viewsets, authentication
2929
from api.tasks import scale_app, restart_app, mount_app, downstream_model_owner, \
30-
delete_pod
30+
delete_pod, run_deploy
3131
from api.exceptions import AlreadyExists, ServiceUnavailable, DryccException
3232

3333
from django.views.decorators.cache import never_cache
@@ -394,29 +394,9 @@ def post_save(self, config):
394394
try:
395395
release = latest_release.new(
396396
self.request.user, config=config, build=latest_release.build)
397-
if release.build is not None:
398-
procfile_types = set()
399-
for field, diff in config.diff().items():
400-
if field in config.procfile_fields:
401-
for value in diff.values():
402-
procfile_types.update(value.keys())
403-
# all_diff_fields changed, deploy all.
404-
procfile_types = procfile_types if procfile_types else None
405-
config.app.deploy(release, procfile_types)
406-
release.state = "succeed"
407-
release.save()
397+
run_deploy.delay(release, config)
408398
except Exception as e:
409-
if 'release' in locals():
410-
release.state = "crashed"
411-
release.failed = True
412-
if release.summary:
413-
release.summary += " "
414-
release.summary += "{} deployed a config that failed".format(self.request.user)
415-
# Get the exception that has occured
416-
release.exception = "error: {}".format(str(e))
417-
release.save()
418-
else:
419-
config.delete()
399+
config.delete()
420400
if isinstance(e, AlreadyExists):
421401
raise
422402
raise DryccException(str(e)) from e

rootfs/scheduler/resources/pod.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -749,33 +749,41 @@ def _handle_not_ready_pods(self, namespace, labels):
749749
message = "\n".join([x.strip() for x in event['message'].split("\n")])
750750
raise KubeException(message)
751751

752+
def _get_probe_timeout(self, probe):
753+
period_seconds = probe.get('periodSeconds', 10)
754+
timeout_seconds = probe.get('timeoutSeconds', 1)
755+
failure_threshold = probe.get('failureThreshold', 3)
756+
success_threshold = probe.get('successThreshold', 1)
757+
initial_delay_seconds = probe.get('initialDelaySeconds', 0)
758+
if period_seconds > initial_delay_seconds:
759+
timeout = 0
760+
else:
761+
timeout = initial_delay_seconds
762+
timeout += (period_seconds + timeout_seconds) * failure_threshold
763+
timeout += (period_seconds + timeout_seconds) * success_threshold
764+
return int(timeout)
765+
752766
def deploy_probe_timeout(self, timeout, namespace, labels, containers):
753767
"""
754768
Added in additional timeouts based on readiness and liveness probe
755769
756770
Uses the max of the two instead of combining them as the checks are stacked.
757771
"""
758-
759772
container_name = '{}-{}'.format(labels['app'], labels['type'])
760773
container = self.pod.find_container(container_name, containers)
761774

762775
# get health info from container
763776
added_timeout = []
764777
if 'readinessProbe' in container:
765-
# If there is initial delay on the readiness check then timeout needs to be higher
766-
# this is to account for kubernetes having readiness check report as failure until
767-
# the initial delay period is up
768-
added_timeout.append(int(container['readinessProbe'].get('initialDelaySeconds', 50)))
769-
778+
added_timeout.append(self._get_probe_timeout(container['readinessProbe']))
770779
if 'livenessProbe' in container:
771-
# If there is initial delay on the readiness check then timeout needs to be higher
772-
# this is to account for kubernetes having liveness check report as failure until
773-
# the initial delay period is up
774-
added_timeout.append(int(container['livenessProbe'].get('initialDelaySeconds', 50)))
775-
780+
added_timeout.append(self._get_probe_timeout(container['livenessProbe']))
776781
if added_timeout:
777782
delay = max(added_timeout)
778-
self.log(namespace, "adding {}s on to the original {}s timeout to account for the initial delay specified in the liveness / readiness probe".format(delay, timeout)) # noqa
783+
self.log(
784+
namespace,
785+
"adding {}s on to the original {}s timeout for the probe".format(delay, timeout))
779786
timeout += delay
780-
787+
if 'startupProbe' in container:
788+
timeout += self._get_probe_timeout(container['startupProbe'])
781789
return timeout

0 commit comments

Comments
 (0)