Skip to content

Commit 50b6cca

Browse files
committed
fix(limits): always set default
1 parent 7391edf commit 50b6cca

4 files changed

Lines changed: 71 additions & 25 deletions

File tree

rootfs/api/models/app.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -738,19 +738,27 @@ def _scale_pods(self, scale_types, release, app_settings):
738738
self.log(err, logging.ERROR)
739739
raise ServiceUnavailable(err) from e
740740

741-
def _set_default_config(self, config=None, procfile_types=None):
742-
procfile_types = self.procfile_types if procfile_types is None else procfile_types
741+
def _set_default_limit(self, config, procfile_type):
742+
if procfile_type not in config.limits:
743+
plan = LimitPlan.get_default()
744+
config.limits[procfile_type] = plan.id
745+
config.save(update_fields=['limits'])
746+
return config
747+
748+
def _set_default_config(self):
743749
plan = LimitPlan.get_default()
744750
limits = {PROCFILE_TYPE_WEB: plan.id, PROCFILE_TYPE_RUN: plan.id}
745751
try:
746-
config = self.config_set.latest() if config is None else config
747-
for procfile_type in procfile_types:
748-
limits[procfile_type] = config.limits.get(procfile_type, plan.id)
749-
if limits != config.limits:
750-
config.limits = limits
751-
config.save(update_fields=['limits'])
752+
config = self.config_set.latest()
753+
limits[PROCFILE_TYPE_WEB] = config.limits.get(PROCFILE_TYPE_WEB, plan.id)
754+
limits[PROCFILE_TYPE_RUN] = config.limits.get(PROCFILE_TYPE_RUN, plan.id)
752755
except Config.DoesNotExist:
753756
config = Config.objects.create(owner=self.owner, app=self, limits=limits)
757+
for procfile_type in self.procfile_types:
758+
limits[procfile_type] = config.limits.get(procfile_type, plan.id)
759+
if limits != config.limits:
760+
config.limits = limits
761+
config.save(update_fields=['limits'])
754762
return config
755763

756764
def _create_default_ingress(self, target_port):
@@ -993,9 +1001,8 @@ def _gather_app_settings(self, release, app_settings, procfile_type, replicas, v
9931001
"""
9941002

9951003
envs = self._build_env_vars(release)
996-
config = release.config
9971004
# Obtain a limit plan that must exist, if raise error here, it must be a bug
998-
self._set_default_config(config, procfile_types=[procfile_type])
1005+
config = self._set_default_limit(release.config, procfile_type)
9991006
limit_plan = LimitPlan.objects.get(id=config.limits.get(procfile_type))
10001007

10011008
# see if the app config has deploy batch preference, otherwise use global

rootfs/api/tests/test_config.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,6 @@ def test_unset_limits_error(self, mock_requests):
473473
# dockerfile + procfile worflow
474474
app = App.objects.get(id=app_id)
475475
user = User.objects.get(username='autotest')
476-
build = Build.objects.create(owner=user, app=app, image="qwerty")
477476
build = Build.objects.create(
478477
owner=user,
479478
app=app,
@@ -545,3 +544,45 @@ def test_measure_config(self, *args, **kwargs):
545544
out = self.call_command()
546545
self.assertIn(out, "done\n")
547546
self.assertEqual(response.status_code, 201)
547+
548+
def test_set_config_limits_run(self, *args, **kwargs):
549+
# create
550+
app_id = self.create_app()
551+
# dockerfile + procfile worflow
552+
app = App.objects.get(id=app_id)
553+
user = User.objects.get(username='autotest')
554+
build = Build.objects.create(
555+
owner=user,
556+
app=app,
557+
image="qwerty",
558+
procfile={
559+
'web': 'node server.js',
560+
'worker': 'node worker.js'
561+
},
562+
dockerfile='foo',
563+
sha='somereallylongsha'
564+
)
565+
# create an initial release
566+
release = Release.objects.create(
567+
version=3,
568+
owner=user,
569+
app=app,
570+
config=app.config_set.latest(),
571+
build=build
572+
)
573+
# deploy
574+
app.pipeline(release)
575+
body = {
576+
'values': json.dumps({'PORT': 5000}),
577+
'limits': {
578+
PROCFILE_TYPE_RUN: 'std1.large.c2m4',
579+
PROCFILE_TYPE_WEB: 'std1.large.c2m4',
580+
},
581+
}
582+
url = f"/v2/apps/{app_id}/config"
583+
response = self.client.post(url, body)
584+
url = "/v2/apps/{app_id}/config".format(**locals())
585+
response = self.client.get(url)
586+
self.assertEqual(response.status_code, 200, response.data)
587+
expect = {'run': 'std1.large.c2m4', 'web': 'std1.large.c2m4', 'worker': 'std1.large.c1m1'}
588+
self.assertEqual(expect, response.json()["limits"], response.data)

rootfs/api/views.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,8 @@ def list(self, request, *args, **kwargs):
284284

285285
def scale(self, request, **kwargs):
286286
app = self.get_object()
287-
release = app.release_set.filter(failed=False).latest()
288-
if release.build is not None and release.state == "created":
287+
latest_release = app.release_set.filter(failed=False).latest()
288+
if latest_release.build is not None and latest_release.state == "created":
289289
raise DryccException('There is an executing pipeline, please wait')
290290
scale_app.delay(self.get_object(), request.user, request.data)
291291
return Response(status=status.HTTP_204_NO_CONTENT)
@@ -385,22 +385,20 @@ class ConfigViewSet(ReleasableViewSet):
385385
serializer_class = serializers.ConfigSerializer
386386

387387
def post_save(self, config):
388-
release = config.app.release_set.filter(failed=False).latest()
389-
if release.build is not None and release.state == "created":
388+
latest_release = config.app.release_set.filter(failed=False).latest()
389+
if latest_release.build is not None and latest_release.state == "created":
390390
raise DryccException('There is an executing pipeline, please wait')
391391
# It's possible to set config values before a build
392392
latest_version = config.app.release_set.latest().version
393393
try:
394-
release = release.new(
395-
self.request.user, config=config, build=release.build, canary=release.canary)
394+
release = latest_release.new(
395+
self.request.user, config=config, build=latest_release.build,
396+
canary=latest_release.canary)
396397
if release.build is not None:
397398
config.app.deploy(release)
398399
release.state = "succeed"
399400
release.save()
400401
except Exception as e:
401-
if ('release' not in locals() and
402-
config.app.release_set.latest().version == latest_version+1):
403-
release = config.app.release_set.latest()
404402
if 'release' in locals():
405403
release.state = "crashed"
406404
release.failed = True
@@ -461,8 +459,8 @@ def _clean_canaries(self):
461459

462460
def release(self, request, *args, **kwargs):
463461
self._clean_canaries()
464-
release = self.get_app().release_set.filter(failed=False).latest()
465-
data = {'version': release.rollback(request.user, release.version).version}
462+
latest_release = self.get_app().release_set.filter(failed=False).latest()
463+
data = {'version': latest_release.rollback(request.user, latest_release.version).version}
466464
return Response(data, status=status.HTTP_201_CREATED)
467465

468466
def rollback(self, request, *args, **kwargs):
@@ -596,8 +594,8 @@ def rollback(self, request, **kwargs):
596594
Create a new release as a copy of the state of the compiled slug and config vars of a
597595
previous release.
598596
"""
599-
release = self.get_app().release_set.filter(failed=False).latest()
600-
new_release = release.rollback(request.user, request.data.get('version', None))
597+
latest_release = self.get_app().release_set.filter(failed=False).latest()
598+
new_release = latest_release.rollback(request.user, request.data.get('version', None))
601599
response = {'version': new_release.version}
602600
return Response(response, status=status.HTTP_201_CREATED)
603601

rootfs/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ docker==7.0.0
1111
gunicorn==21.2.0
1212
uvicorn[standard]==0.29.0
1313
asgiref==3.8.0
14-
idna==3.6
14+
idna==3.7
1515
jsonschema==4.21.1
1616
morph==0.1.5
1717
packaging==24.0

0 commit comments

Comments
 (0)