Skip to content

Commit 2693a7f

Browse files
committed
fix(controller): store limit fields on config
Having a separate "Limit" domain object was getting in our way, when really it represents just an extension of a Config. This moves the two JSON fields "cpu" and "memory" to Config and does away with Limit.
1 parent 3df69f8 commit 2693a7f

8 files changed

Lines changed: 369 additions & 292 deletions

File tree

api/models.py

Lines changed: 9 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,8 @@ def _command_announceable(self):
308308
@transition(field=state, source=INITIALIZED, target=CREATED)
309309
def create(self):
310310
image = self.release.image
311-
kwargs = {}
312-
if self.release.config.limit is not None:
313-
kwargs = {'memory': self.release.config.limit.memory,
314-
'cpu': self.release.config.limit.cpu}
311+
kwargs = {'memory': self.release.config.memory,
312+
'cpu': self.release.config.cpu}
315313
self._scheduler.create(name=self._job_id,
316314
image=image,
317315
command=self._command,
@@ -339,10 +337,8 @@ def deploy(self, release):
339337
new_job_id = self._job_id
340338
image = self.release.image
341339
c_type = self.type
342-
kwargs = {}
343-
if self.release.config.limit is not None:
344-
kwargs = {'memory': self.release.config.limit.memory,
345-
'cpu': self.release.config.limit.cpu}
340+
kwargs = {'memory': self.release.config.memory,
341+
'cpu': self.release.config.cpu}
346342
self._scheduler.create(name=new_job_id,
347343
image=image,
348344
command=self._command.format(**locals()),
@@ -433,26 +429,6 @@ class Config(UuidAuditedModel):
433429
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
434430
app = models.ForeignKey('App')
435431
values = fields.JSONField(default='{}', blank=True)
436-
limit = models.ForeignKey('Limit', null=True)
437-
438-
class Meta:
439-
get_latest_by = 'created'
440-
ordering = ['-created']
441-
unique_together = (('app', 'uuid'),)
442-
443-
def __str__(self):
444-
return "{}-{}".format(self.app.id, self.uuid[:7])
445-
446-
447-
@python_2_unicode_compatible
448-
class Limit(UuidAuditedModel):
449-
"""
450-
Set of resource limits applied by the scheduler
451-
during runtime execution of the Application.
452-
"""
453-
454-
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
455-
app = models.ForeignKey('App')
456432
memory = fields.JSONField(default='{}', blank=True)
457433
cpu = fields.JSONField(default='{}', blank=True)
458434

@@ -553,7 +529,6 @@ def save(self, *args, **kwargs): # noqa
553529
# compare this build to the previous build
554530
old_build = prev_release.build if prev_release else None
555531
old_config = prev_release.config if prev_release else None
556-
old_limit = prev_release.config.limit if prev_release else None
557532
# if the build changed, log it and who pushed it
558533
if self.version == 1:
559534
self.summary += "{} created initial release".format(self.app.owner)
@@ -579,15 +554,14 @@ def save(self, *args, **kwargs): # noqa
579554
if self.summary:
580555
self.summary += ' and '
581556
self.summary += "{} {}".format(self.config.owner, changes)
582-
# if the limit changes, log the dict diff
583-
if self.config.limit != old_limit:
557+
# if the limits changed (memory or cpu), log the dict diff
584558
changes = []
585-
old_mem = old_limit.memory if old_limit else {}
586-
diff = dict_diff(self.config.limit.memory, old_mem)
559+
old_mem = old_config.memory if old_config else {}
560+
diff = dict_diff(self.config.memory, old_mem)
587561
if diff.get('added') or diff.get('changed') or diff.get('deleted'):
588562
changes.append('memory')
589-
old_cpu = old_limit.cpu if old_limit else {}
590-
diff = dict_diff(self.config.limit.cpu, old_cpu)
563+
old_cpu = old_config.cpu if old_config else {}
564+
diff = dict_diff(self.config.cpu, old_cpu)
591565
if diff.get('added') or diff.get('changed') or diff.get('deleted'):
592566
changes.append('cpu')
593567
if changes:

api/serializers.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -100,26 +100,14 @@ class ConfigSerializer(serializers.ModelSerializer):
100100
app = serializers.SlugRelatedField(slug_field='id')
101101
values = serializers.ModelField(
102102
model_field=models.Config()._meta.get_field('values'), required=False)
103-
104-
class Meta:
105-
"""Metadata options for a :class:`ConfigSerializer`."""
106-
model = models.Config
107-
read_only_fields = ('uuid', 'created', 'updated')
108-
109-
110-
class LimitSerializer(serializers.ModelSerializer):
111-
"""Serialize a :class:`~api.models.Limit` model."""
112-
113-
owner = serializers.Field(source='owner.username')
114-
app = serializers.SlugRelatedField(slug_field='id')
115103
memory = serializers.ModelField(
116-
model_field=models.Limit()._meta.get_field('memory'), required=False)
104+
model_field=models.Config()._meta.get_field('memory'), required=False)
117105
cpu = serializers.ModelField(
118-
model_field=models.Limit()._meta.get_field('cpu'), required=False)
106+
model_field=models.Config()._meta.get_field('cpu'), required=False)
119107

120108
class Meta:
121-
"""Metadata options for a :class:`LimitSerializer`."""
122-
model = models.Limit
109+
"""Metadata options for a :class:`ConfigSerializer`."""
110+
model = models.Config
123111
read_only_fields = ('uuid', 'created', 'updated')
124112

125113
def validate_memory(self, attrs, source):
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# -*- coding: utf-8 -*-
2+
from south.utils import datetime_utils as datetime
3+
from south.db import db
4+
from south.v2 import SchemaMigration
5+
from django.db import models
6+
7+
8+
class Migration(SchemaMigration):
9+
10+
def forwards(self, orm):
11+
# Removing unique constraint on 'Limit', fields ['app', 'uuid']
12+
db.delete_unique(u'api_limit', ['app_id', 'uuid'])
13+
14+
# Deleting model 'Limit'
15+
db.delete_table(u'api_limit')
16+
17+
# Deleting field 'Config.limit'
18+
db.delete_column(u'api_config', 'limit_id')
19+
20+
# Adding field 'Config.memory'
21+
db.add_column(u'api_config', 'memory',
22+
self.gf('json_field.fields.JSONField')(default=u'{}', blank=True),
23+
keep_default=False)
24+
25+
# Adding field 'Config.cpu'
26+
db.add_column(u'api_config', 'cpu',
27+
self.gf('json_field.fields.JSONField')(default=u'{}', blank=True),
28+
keep_default=False)
29+
30+
31+
def backwards(self, orm):
32+
# Adding model 'Limit'
33+
db.create_table(u'api_limit', (
34+
('updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
35+
('uuid', self.gf('api.fields.UuidField')(max_length=32, unique=True, primary_key=True)),
36+
('created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
37+
('owner', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
38+
('app', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['api.App'])),
39+
('memory', self.gf('json_field.fields.JSONField')(default=u'{}', blank=True)),
40+
('cpu', self.gf('json_field.fields.JSONField')(default=u'{}', blank=True)),
41+
))
42+
db.send_create_signal(u'api', ['Limit'])
43+
44+
# Adding unique constraint on 'Limit', fields ['app', 'uuid']
45+
db.create_unique(u'api_limit', ['app_id', 'uuid'])
46+
47+
# Adding field 'Config.limit'
48+
db.add_column(u'api_config', 'limit',
49+
self.gf('django.db.models.fields.related.ForeignKey')(to=orm['api.Limit'], null=True),
50+
keep_default=False)
51+
52+
# Deleting field 'Config.memory'
53+
db.delete_column(u'api_config', 'memory')
54+
55+
# Deleting field 'Config.cpu'
56+
db.delete_column(u'api_config', 'cpu')
57+
58+
59+
models = {
60+
u'api.app': {
61+
'Meta': {'object_name': 'App'},
62+
'cluster': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.Cluster']"}),
63+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
64+
'id': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '64'}),
65+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
66+
'structure': ('json_field.fields.JSONField', [], {'default': "u'{}'", 'blank': 'True'}),
67+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
68+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
69+
},
70+
u'api.build': {
71+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'uuid'),)", 'object_name': 'Build'},
72+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
73+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
74+
'dockerfile': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
75+
'image': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
76+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
77+
'procfile': ('json_field.fields.JSONField', [], {'default': "u'{}'", 'blank': 'True'}),
78+
'sha': ('django.db.models.fields.CharField', [], {'max_length': '40', 'blank': 'True'}),
79+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
80+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
81+
},
82+
u'api.cluster': {
83+
'Meta': {'object_name': 'Cluster'},
84+
'auth': ('django.db.models.fields.TextField', [], {}),
85+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
86+
'domain': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
87+
'hosts': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
88+
'id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
89+
'options': ('json_field.fields.JSONField', [], {'default': "u'{}'", 'blank': 'True'}),
90+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
91+
'type': ('django.db.models.fields.CharField', [], {'default': "u'coreos'", 'max_length': '16'}),
92+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
93+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
94+
},
95+
u'api.config': {
96+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'uuid'),)", 'object_name': 'Config'},
97+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
98+
'cpu': ('json_field.fields.JSONField', [], {'default': "u'{}'", 'blank': 'True'}),
99+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
100+
'memory': ('json_field.fields.JSONField', [], {'default': "u'{}'", 'blank': 'True'}),
101+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
102+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
103+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'}),
104+
'values': ('json_field.fields.JSONField', [], {'default': "u'{}'", 'blank': 'True'})
105+
},
106+
u'api.container': {
107+
'Meta': {'ordering': "[u'created']", 'object_name': 'Container'},
108+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
109+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
110+
'num': ('django.db.models.fields.PositiveIntegerField', [], {}),
111+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
112+
'release': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.Release']"}),
113+
'state': ('django_fsm.FSMField', [], {'default': "u'initialized'", 'max_length': '50'}),
114+
'type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
115+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
116+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
117+
},
118+
u'api.domain': {
119+
'Meta': {'object_name': 'Domain'},
120+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
121+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
122+
'domain': ('django.db.models.fields.TextField', [], {'unique': 'True'}),
123+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
124+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
125+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
126+
},
127+
u'api.key': {
128+
'Meta': {'unique_together': "((u'owner', u'id'),)", 'object_name': 'Key'},
129+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
130+
'id': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
131+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
132+
'public': ('django.db.models.fields.TextField', [], {'unique': 'True'}),
133+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
134+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
135+
},
136+
u'api.push': {
137+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'uuid'),)", 'object_name': 'Push'},
138+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
139+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
140+
'fingerprint': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
141+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
142+
'receive_repo': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
143+
'receive_user': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
144+
'sha': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
145+
'ssh_connection': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
146+
'ssh_original_command': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
147+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
148+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
149+
},
150+
u'api.release': {
151+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'version'),)", 'object_name': 'Release'},
152+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
153+
'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.Build']"}),
154+
'config': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.Config']"}),
155+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
156+
'image': ('django.db.models.fields.CharField', [], {'default': "u'deis/helloworld'", 'max_length': '256'}),
157+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
158+
'summary': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
159+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
160+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'}),
161+
'version': ('django.db.models.fields.PositiveIntegerField', [], {})
162+
},
163+
u'auth.group': {
164+
'Meta': {'object_name': 'Group'},
165+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
166+
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
167+
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
168+
},
169+
u'auth.permission': {
170+
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
171+
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
172+
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
173+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
174+
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
175+
},
176+
u'auth.user': {
177+
'Meta': {'object_name': 'User'},
178+
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
179+
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
180+
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
181+
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
182+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
183+
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
184+
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
185+
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
186+
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
187+
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
188+
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
189+
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
190+
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
191+
},
192+
u'contenttypes.contenttype': {
193+
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
194+
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
195+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
196+
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
197+
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
198+
}
199+
}
200+
201+
complete_apps = ['api']

api/tests/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ def run_tests(self, test_labels, extra_tests=None, **kwargs):
5050
from .test_domain import * # noqa
5151
from .test_container import * # noqa
5252
from .test_hooks import * # noqa
53-
from .test_limit import * # noqa
5453
from .test_key import * # noqa
5554
from .test_perm import * # noqa
5655
from .test_release import * # noqa

0 commit comments

Comments
 (0)