Skip to content

Commit 67811f5

Browse files
committed
Move the routable from config object to app settings
1 parent 3e988ea commit 67811f5

12 files changed

Lines changed: 100 additions & 85 deletions

rootfs/api/migrations/0012_auto_20160810_1603.py renamed to rootfs/api/migrations/0011_auto_20160810_1603.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
class Migration(migrations.Migration):
1010

1111
dependencies = [
12-
('api', '0011_config_routable'),
12+
('api', '0010_config_healthcheck'),
1313
]
1414

1515
operations = [

rootfs/api/migrations/0011_config_routable.py

Lines changed: 0 additions & 21 deletions
This file was deleted.

rootfs/api/migrations/0013_auto_20160812_1905.py renamed to rootfs/api/migrations/0012_auto_20160816_1934.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
# Generated by Django 1.9.8 on 2016-08-12 19:05
2+
# Generated by Django 1.9.8 on 2016-08-16 19:34
33
from __future__ import unicode_literals
44

55
from django.conf import settings
@@ -12,7 +12,7 @@ class Migration(migrations.Migration):
1212

1313
dependencies = [
1414
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15-
('api', '0012_auto_20160810_1603'),
15+
('api', '0011_auto_20160810_1603'),
1616
]
1717

1818
operations = [
@@ -22,7 +22,8 @@ class Migration(migrations.Migration):
2222
('uuid', models.UUIDField(auto_created=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True, verbose_name='UUID')),
2323
('created', models.DateTimeField(auto_now_add=True)),
2424
('updated', models.DateTimeField(auto_now=True)),
25-
('maintenance', models.BooleanField(default=False)),
25+
('maintenance', models.NullBooleanField(default=None)),
26+
('routable', models.NullBooleanField(default=None)),
2627
('app', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.App')),
2728
('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
2829
],

rootfs/api/models/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ def _fetch_service_config(self, app):
6161
default = {'metadata': {'annotations': {}}}
6262
svc = dict_merge(svc, default)
6363

64+
if 'labels' not in svc['metadata']:
65+
default = {'metadata': {'labels': {}}}
66+
svc = dict_merge(svc, default)
67+
6468
return svc
6569

6670
def _load_service_config(self, app, component):

rootfs/api/models/app.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ def _scale_pods(self, scale_types):
409409
for scale_type, replicas in scale_types.items():
410410
# only web / cmd are routable
411411
# http://docs.deis.io/en/latest/using_deis/process-types/#web-vs-cmd-process-types
412-
routable = True if scale_type in ['web', 'cmd'] and release.config.routable else False
412+
routable = True if scale_type in ['web', 'cmd'] and app_settings.routable else False
413413
# fetch application port and inject into ENV Vars as needed
414414
port = release.get_port()
415415
if port:
@@ -430,7 +430,7 @@ def _scale_pods(self, scale_types):
430430
'app_type': scale_type,
431431
'build_type': release.build.type,
432432
'healthcheck': healthcheck,
433-
'annotations': {'maintenance': app_settings.maintenance},
433+
'service_annotations': {'maintenance': app_settings.maintenance},
434434
'routable': routable,
435435
'deploy_batches': batches,
436436
'deploy_timeout': deploy_timeout,
@@ -495,7 +495,7 @@ def deploy(self, release, force_deploy=False):
495495
for scale_type, replicas in self.structure.items():
496496
# only web / cmd are routable
497497
# http://docs.deis.io/en/latest/using_deis/process-types/#web-vs-cmd-process-types
498-
routable = True if scale_type in ['web', 'cmd'] and release.config.routable else False
498+
routable = True if scale_type in ['web', 'cmd'] and app_settings.routable else False
499499
# fetch application port and inject into ENV vars as needed
500500
port = release.get_port()
501501
if port:
@@ -518,7 +518,7 @@ def deploy(self, release, force_deploy=False):
518518
'build_type': release.build.type,
519519
'healthcheck': healthcheck,
520520
'routable': routable,
521-
'annotations': {'maintenance': app_settings.maintenance},
521+
'service_annotations': {'maintenance': app_settings.maintenance},
522522
'deploy_batches': batches,
523523
'deploy_timeout': deploy_timeout,
524524
'deployment_history_limit': deployment_history,
@@ -607,7 +607,8 @@ def verify_application_health(self, **kwargs):
607607
"""
608608
# Bail out early if the application is not routable
609609
release = self.release_set.latest()
610-
if not kwargs.get('routable', False) and release.config.routable:
610+
app_settings = self.appsettings_set.latest()
611+
if not kwargs.get('routable', False) and app_settings.routable:
611612
return
612613

613614
app_type = kwargs.get('app_type')

rootfs/api/models/appsettings.py

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class AppSettings(UuidAuditedModel):
1414

1515
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
1616
app = models.ForeignKey('App', on_delete=models.CASCADE)
17-
maintenance = models.BooleanField(default=False)
17+
maintenance = models.NullBooleanField(default=None)
18+
routable = models.NullBooleanField(default=None)
1819

1920
class Meta:
2021
get_latest_by = 'created'
@@ -36,30 +37,66 @@ def set_maintenance(self, maintenance):
3637
self._scheduler.update_service(namespace, namespace, data=old_service)
3738
raise KubeException(str(e)) from e
3839

40+
def set_routable(self, routable):
41+
namespace = self.app.id
42+
service = self._fetch_service_config(namespace)
43+
old_service = service.copy() # in case anything fails for rollback
44+
45+
try:
46+
service['metadata']['labels']['router.deis.io/routable'] = str(routable).lower()
47+
self._scheduler.update_service(namespace, namespace, data=service)
48+
except Exception as e:
49+
self._scheduler.update_service(namespace, namespace, data=old_service)
50+
raise KubeException(str(e)) from e
51+
52+
def update_maintenance(self, previous_settings):
53+
prev_maintenance = getattr(previous_settings, 'maintenance', None)
54+
new_maintenance = getattr(self, 'maintenance', None)
55+
# If no previous settings, assume this is first timeout
56+
# and set the default maintenance as false
57+
if not previous_settings:
58+
setattr(self, 'maintenance', False)
59+
self.set_maintenance(False)
60+
# if nothing changed copy the settings from previous
61+
elif new_maintenance is None and prev_maintenance is not None:
62+
setattr(self, 'maintenance', prev_maintenance)
63+
elif prev_maintenance != new_maintenance:
64+
self.set_maintenance(new_maintenance)
65+
self.summary += "{} changed maintenance mode from {} to {}".format(self.owner, prev_maintenance, new_maintenance) # noqa
66+
67+
def update_routable(self, previous_settings):
68+
old_routable = getattr(previous_settings, 'routable', None)
69+
new_routable = getattr(self, 'routable', None)
70+
# If no previous settings, assume this is first timeout
71+
# and set the default maintenance as true
72+
if not previous_settings:
73+
setattr(self, 'routable', True)
74+
self.set_routable(True)
75+
# if nothing changed copy the settings from previous
76+
elif new_routable is None and old_routable is not None:
77+
setattr(self, 'routable', old_routable)
78+
elif old_routable != new_routable:
79+
self.set_routable(new_routable)
80+
self.summary += "{} changed routablity from {} to {}".format(self.owner, old_routable, new_routable) # noqa
81+
3982
def save(self, *args, **kwargs):
40-
summary = ''
83+
self.summary = ''
4184
previous_settings = None
4285
try:
4386
previous_settings = self.app.appsettings_set.latest()
4487
except AppSettings.DoesNotExist:
4588
pass
4689

47-
prev_maintenance = getattr(previous_settings, 'maintenance', None)
48-
new_maintenance = getattr(self, 'maintenance')
49-
5090
try:
51-
if new_maintenance is None and prev_maintenance is not None:
52-
setattr(self, 'maintenance', prev_maintenance)
53-
elif prev_maintenance != new_maintenance:
54-
self.set_maintenance(new_maintenance)
55-
summary += "{} changed maintenance mode from {} to {}".format(self.owner, prev_maintenance, new_maintenance) # noqa
91+
self.update_maintenance(previous_settings)
92+
self.update_routable(previous_settings)
5693
except Exception as e:
5794
self.delete()
5895
raise DeisException(str(e)) from e
5996

60-
if not summary and previous_settings:
97+
if not self.summary and previous_settings:
6198
self.delete()
6299
raise AlreadyExists("{} changed nothing".format(self.owner))
63-
self.app.log('summary of app setting changes: {}'.format(summary), logging.DEBUG)
100+
self.app.log('summary of app setting changes: {}'.format(self.summary), logging.DEBUG)
64101

65102
return super(AppSettings, self).save(**kwargs)

rootfs/api/models/config.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ class Config(UuidAuditedModel):
2121
tags = JSONField(default={}, blank=True)
2222
registry = JSONField(default={}, blank=True)
2323
healthcheck = JSONField(default={}, blank=True)
24-
routable = models.BooleanField(default=True)
2524

2625
class Meta:
2726
get_latest_by = 'created'

rootfs/api/models/release.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -472,17 +472,6 @@ def save(self, *args, **kwargs): # noqa
472472
self.summary += ' and '
473473
self.summary += "{} {}".format(self.config.owner, changes)
474474

475-
# if the routable flag changed, log that too
476-
changes = []
477-
old_routable = old_config.routable if old_config else True
478-
enabled = "enabled routing" if self.config.routable and not old_routable else ''
479-
disabled = "disabled routing" if not self.config.routable and old_routable else ''
480-
changes = ', '.join(i for i in (enabled, disabled) if i)
481-
if changes:
482-
if self.summary:
483-
self.summary += ' and '
484-
self.summary += "{} {}".format(self.config.owner, changes)
485-
486475
if not self.summary:
487476
if self.version == 1:
488477
self.summary = "{} created the initial release".format(self.owner)

rootfs/api/tests/test_app_settings.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,21 @@ def test_settings_maintenance(self, mock_requests):
3838
self.assertTrue(response.data['maintenance'])
3939
self.assertTrue(app.appsettings_set.latest().maintenance)
4040

41-
settings['maintenance'] = False
41+
settings = {'routable': False}
4242
response = self.client.post(
4343
'/v2/apps/{app_id}/settings'.format(**locals()),
4444
settings)
4545
self.assertEqual(response.status_code, 201, response.data)
46-
self.assertFalse(response.data['maintenance'])
47-
self.assertFalse(app.appsettings_set.latest().maintenance)
46+
self.assertTrue(app.appsettings_set.latest().maintenance)
4847

48+
settings['maintenance'] = False
4949
response = self.client.post(
5050
'/v2/apps/{app_id}/settings'.format(**locals()),
5151
settings)
52-
self.assertEqual(response.status_code, 409, response.data)
52+
self.assertEqual(response.status_code, 201, response.data)
53+
self.assertFalse(response.data['maintenance'])
5354
self.assertFalse(app.appsettings_set.latest().maintenance)
5455

55-
settings = {}
5656
response = self.client.post(
5757
'/v2/apps/{app_id}/settings'.format(**locals()),
5858
settings)
@@ -64,3 +64,28 @@ def test_settings_maintenance(self, mock_requests):
6464
'/v2/apps/{app_id}/settings'.format(**locals()),
6565
settings)
6666
self.assertEqual(response.status_code, 400, response.data)
67+
68+
def test_settings_routable(self, mock_requests):
69+
"""
70+
Create an application with the routable flag turned on or off
71+
"""
72+
# create app, expecting routable to be true
73+
body = {'id': 'myid'}
74+
response = self.client.post('/v2/apps', body)
75+
self.assertEqual(response.status_code, 201, response.data)
76+
app = App.objects.get(id='myid')
77+
self.assertTrue(app.appsettings_set.latest().routable)
78+
# Set routable to false
79+
response = self.client.post(
80+
'/v2/apps/{app.id}/settings'.format(**locals()),
81+
{'routable': False}
82+
)
83+
self.assertEqual(response.status_code, 201, response.data)
84+
self.assertFalse(app.appsettings_set.latest().routable)
85+
86+
settings = {'maintenance': True}
87+
response = self.client.post(
88+
'/v2/apps/{app.id}/settings'.format(**locals()),
89+
settings)
90+
self.assertEqual(response.status_code, 201, response.data)
91+
self.assertFalse(app.appsettings_set.latest().routable)

rootfs/api/tests/test_config.py

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def test_response_data(self, mock_requests):
131131
response = self.client.post(url, body)
132132
for key in response.data:
133133
self.assertIn(key, ['uuid', 'owner', 'created', 'updated', 'app', 'values', 'memory',
134-
'cpu', 'tags', 'registry', 'healthcheck', 'routable'])
134+
'cpu', 'tags', 'registry', 'healthcheck'])
135135
expected = {
136136
'owner': self.user.username,
137137
'app': app_id,
@@ -154,7 +154,7 @@ def test_response_data_types_converted(self, mock_requests):
154154
self.assertEqual(response.status_code, 201, response.data)
155155
for key in response.data:
156156
self.assertIn(key, ['uuid', 'owner', 'created', 'updated', 'app', 'values', 'memory',
157-
'cpu', 'tags', 'registry', 'healthcheck', 'routable'])
157+
'cpu', 'tags', 'registry', 'healthcheck'])
158158
expected = {
159159
'owner': self.user.username,
160160
'app': app_id,
@@ -325,21 +325,3 @@ def test_unauthorized_user_cannot_modify_config(self, mock_requests):
325325
body = {'values': {'FOO': 'bar'}}
326326
response = self.client.post(url, body)
327327
self.assertEqual(response.status_code, 403)
328-
329-
def test_config_routable(self, mock_requests):
330-
"""
331-
Create an application with the routable flag turned on or off
332-
"""
333-
# create app, expecting routable to be true
334-
body = {'id': 'myid'}
335-
response = self.client.post('/v2/apps', body)
336-
self.assertEqual(response.status_code, 201, response.data)
337-
app = App.objects.get(id='myid')
338-
self.assertTrue(app.config_set.latest().routable)
339-
# Set routable to false
340-
response = self.client.post(
341-
'/v2/apps/{app.id}/config'.format(**locals()),
342-
{'routable': False}
343-
)
344-
self.assertEqual(response.status_code, 201, response.data)
345-
self.assertFalse(app.config_set.latest().routable)

0 commit comments

Comments
 (0)