Skip to content

Commit ab2cce5

Browse files
committed
fix(app): check if app name already exists in kubernetes during app creation
Fixes #316
1 parent 8c26ca6 commit ab2cce5

4 files changed

Lines changed: 42 additions & 4 deletions

File tree

rootfs/api/models/app.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from django.core.exceptions import ValidationError
1212
from jsonfield import JSONField
1313

14-
from api.models import UuidAuditedModel, log_event
14+
from api.models import UuidAuditedModel, log_event, AlreadyExists
1515
from api.utils import generate_app_name, app_build_type
1616
from api.models.release import Release
1717
from api.models.config import Config
@@ -71,6 +71,20 @@ def save(self, *args, **kwargs):
7171
while App.objects.filter(id=self.id).exists():
7272
self.id = generate_app_name()
7373

74+
# verify the application name doesn't exist as a k8s namespace
75+
# only check for it if there have been on releases
76+
try:
77+
self.release_set.latest()
78+
except Release.DoesNotExist:
79+
try:
80+
if self._scheduler._get_namespace(self.id).status_code == 200:
81+
# Namespace already exists
82+
err = "{} already exists as a namespace in this kuberenetes setup".format(self.id) # noqa
83+
log_event(self, err, logging.INFO)
84+
raise AlreadyExists(err)
85+
except KubeHTTPException:
86+
pass
87+
7488
application = super(App, self).save(**kwargs)
7589

7690
# create all the required resources
@@ -310,12 +324,12 @@ def _scale_pods(self, scale_types):
310324

311325
def deploy(self, user, release):
312326
"""Deploy a new release to this application"""
313-
# use create to make sure minimum resources are created
314-
self.create()
315-
316327
if release.build is None:
317328
raise EnvironmentError('No build associated with this release')
318329

330+
# use create to make sure minimum resources are created
331+
self.create()
332+
319333
if self.structure == {}:
320334
self.structure = self._default_structure(release)
321335
self.save()

rootfs/api/tests/test_app.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,17 @@ def test_app_transfer(self):
364364
self.assertEqual(response.status_code, 200)
365365
self.assertEqual(response.data['owner'], self.user.username)
366366

367+
def test_app_exists_in_kubernetes(self):
368+
"""
369+
Create an app that has the same namespace as an existing kubernetes namespace
370+
"""
371+
body = {'id': 'duplicate'}
372+
response = self.client.post('/v2/apps', body)
373+
self.assertContains(
374+
response,
375+
'duplicate already exists as a namespace in this kuberenetes setup',
376+
status_code=409
377+
)
367378

368379
FAKE_LOG_DATA = """
369380
2013-08-15 12:41:25 [33454] [INFO] Starting gunicorn 17.5

rootfs/api/views.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,14 @@ class AppViewSet(BaseDeisViewSet):
203203
def get_queryset(self, *args, **kwargs):
204204
return self.model.objects.all(*args, **kwargs)
205205

206+
def create(self, request, **kwargs):
207+
try:
208+
return super(AppViewSet, self).create(request, **kwargs)
209+
except AlreadyExists as e:
210+
return Response({'detail': str(e)}, status=status.HTTP_409_CONFLICT)
211+
except EnvironmentError as e:
212+
return Response({'detail': str(e)}, status=status.HTTP_400_BAD_REQUEST)
213+
206214
def list(self, request, *args, **kwargs):
207215
"""
208216
HACK: Instead of filtering by the queryset, we limit the queryset to list only the apps

rootfs/scheduler/mock.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,11 @@ def __init__(self):
420420
}
421421
self._create_secret('deis', 'minio-user', secrets)
422422

423+
try:
424+
self._get_namespace('duplicate')
425+
except KubeHTTPException:
426+
self._create_namespace('duplicate')
427+
423428
try:
424429
self._get_node('172.17.8.100')
425430
except KubeHTTPException:

0 commit comments

Comments
 (0)