Skip to content

Commit 9fb9960

Browse files
Matthew Fishermboersma
authored andcommitted
fix(scheduler): use app name when creating services
When we eventually allow applications to be grouped together in the same namespace, we'll want to have the service names discoverable to other instances in the same namespace. Having a name like "app-1234" was created so we could get over the limitation of 24 characters for service names since applications in v1 could be as long as 64 characters in length. Now that we're working on v2, we can just restrict app names to 24 characters. The app name validation regex was not changed because the docker repository name regex is a subset of the kubernetes service name regex, so we still need to keep ourselves restricted to that name regex so we can still push those images to the internal registry.
1 parent c31b457 commit 9fb9960

3 files changed

Lines changed: 167 additions & 13 deletions

File tree

rootfs/api/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ class App(UuidAuditedModel):
153153
"""
154154

155155
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
156-
id = models.SlugField(max_length=64, unique=True, default=select_app_name,
156+
id = models.SlugField(max_length=24, unique=True, default=select_app_name,
157157
validators=[validate_id_is_docker_compatible,
158158
validate_reserved_names])
159159
structure = JSONField(default={}, blank=True, validators=[validate_app_structure])
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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+
12+
# Changing field 'App.id'
13+
db.alter_column(u'api_app', 'id', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=24))
14+
15+
def backwards(self, orm):
16+
17+
# Changing field 'App.id'
18+
db.alter_column(u'api_app', 'id', self.gf('django.db.models.fields.SlugField')(max_length=64, unique=True))
19+
20+
models = {
21+
u'api.app': {
22+
'Meta': {'object_name': 'App'},
23+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
24+
'id': ('django.db.models.fields.SlugField', [], {'default': "'unisex-mainsail'", 'unique': 'True', 'max_length': '24'}),
25+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
26+
'structure': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
27+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
28+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
29+
},
30+
u'api.build': {
31+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'uuid'),)", 'object_name': 'Build'},
32+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
33+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
34+
'dockerfile': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
35+
'image': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
36+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
37+
'procfile': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
38+
'sha': ('django.db.models.fields.CharField', [], {'max_length': '40', 'blank': 'True'}),
39+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
40+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
41+
},
42+
u'api.certificate': {
43+
'Meta': {'object_name': 'Certificate'},
44+
'certificate': ('django.db.models.fields.TextField', [], {}),
45+
'common_name': ('django.db.models.fields.TextField', [], {'unique': 'True'}),
46+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
47+
'expires': ('django.db.models.fields.DateTimeField', [], {}),
48+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
49+
'key': ('django.db.models.fields.TextField', [], {}),
50+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
51+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
52+
},
53+
u'api.config': {
54+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'uuid'),)", 'object_name': 'Config'},
55+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
56+
'cpu': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
57+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
58+
'memory': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
59+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
60+
'tags': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
61+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
62+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'}),
63+
'values': ('json_field.fields.JSONField', [], {'default': '{}', 'blank': 'True'})
64+
},
65+
u'api.container': {
66+
'Meta': {'ordering': "[u'created']", 'object_name': 'Container'},
67+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
68+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
69+
'num': ('django.db.models.fields.PositiveIntegerField', [], {}),
70+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
71+
'release': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.Release']"}),
72+
'type': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
73+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
74+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
75+
},
76+
u'api.domain': {
77+
'Meta': {'object_name': 'Domain'},
78+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
79+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
80+
'domain': ('django.db.models.fields.TextField', [], {'unique': 'True'}),
81+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
82+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
83+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
84+
},
85+
u'api.key': {
86+
'Meta': {'unique_together': "((u'owner', u'fingerprint'),)", 'object_name': 'Key'},
87+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
88+
'fingerprint': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
89+
'id': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
90+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
91+
'public': ('django.db.models.fields.TextField', [], {'unique': 'True'}),
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.push': {
96+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'uuid'),)", 'object_name': 'Push'},
97+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
98+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
99+
'fingerprint': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
100+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
101+
'receive_repo': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
102+
'receive_user': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
103+
'sha': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
104+
'ssh_connection': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
105+
'ssh_original_command': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
106+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
107+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'})
108+
},
109+
u'api.release': {
110+
'Meta': {'ordering': "[u'-created']", 'unique_together': "((u'app', u'version'),)", 'object_name': 'Release'},
111+
'app': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.App']"}),
112+
'build': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.Build']", 'null': 'True'}),
113+
'config': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.Config']"}),
114+
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
115+
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
116+
'summary': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
117+
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
118+
'uuid': ('api.fields.UuidField', [], {'unique': 'True', 'max_length': '32', 'primary_key': 'True'}),
119+
'version': ('django.db.models.fields.PositiveIntegerField', [], {})
120+
},
121+
u'auth.group': {
122+
'Meta': {'object_name': 'Group'},
123+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
124+
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
125+
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
126+
},
127+
u'auth.permission': {
128+
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
129+
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
130+
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
131+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
132+
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
133+
},
134+
u'auth.user': {
135+
'Meta': {'object_name': 'User'},
136+
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
137+
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
138+
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
139+
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
140+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
141+
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
142+
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
143+
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
144+
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
145+
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
146+
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
147+
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
148+
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
149+
},
150+
u'contenttypes.contenttype': {
151+
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
152+
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
153+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
154+
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
155+
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
156+
}
157+
}
158+
159+
complete_apps = ['api']

rootfs/scheduler/k8s.py

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import json
22
import logging
3-
import random
43
import re
54
import string
65
import time
@@ -82,7 +81,7 @@
8281
"metadata": {
8382
"name": "$name",
8483
"labels": {
85-
"app": "$label"
84+
"app": "$name"
8685
}
8786
},
8887
"spec": {
@@ -94,7 +93,7 @@
9493
}
9594
],
9695
"selector": {
97-
"app": "$label",
96+
"app": "$name",
9897
"type": "$type",
9998
"heritage": "deis"
10099
}
@@ -405,9 +404,6 @@ def _get_service(self, name, namespace):
405404
return resp.status_code, resp.text, resp.reason
406405

407406
def _create_service(self, name, app_name, app_type):
408-
random.seed(app_name)
409-
app_id = random.randint(1, 100000)
410-
appname = "app-" + str(app_id)
411407
actual_pod = {}
412408
for _ in xrange(300):
413409
status, data, reason = self._get_pods(app_name)
@@ -437,29 +433,28 @@ def _create_service(self, name, app_name, app_type):
437433

438434
l = {
439435
"version": self.apiversion,
440-
"label": app_name,
441436
"port": port,
442437
"type": app_type,
443-
"name": appname,
438+
"name": app_name,
444439
}
445440

446441
template = string.Template(SERVICE_TEMPLATE).substitute(l)
447442
url = self._api("/namespaces/{}/services", app_name)
448443
resp = self.session.post(url, json=json.loads(template))
449444
if resp.status_code == 409:
450-
status, data, reason = self._get_service(appname, app_name)
445+
status, data, reason = self._get_service(app_name, app_name)
451446
srv = json.loads(data)
452447
if srv['spec']['selector']['type'] == 'web':
453448
return
454449

455450
srv['spec']['selector']['type'] = app_type
456451
srv['spec']['ports'][0]['targetPort'] = port
457-
url = self._api("/namespaces/{}/services/{}", app_name, appname)
452+
url = self._api("/namespaces/{}/services/{}", app_name, app_name)
458453
resp2 = self.session.put(url, json=srv)
459454
if unhealthy(resp2.status_code):
460-
error(resp, 'update Service "{}" in Namespace "{}"', app_name, appname)
455+
error(resp, 'update Service "{}" in Namespace "{}"', app_name, app_name)
461456
elif unhealthy(resp.status_code):
462-
error(resp, 'create Service "{}" in Namespace "{}"', app_name, appname)
457+
error(resp, 'create Service "{}" in Namespace "{}"', app_name, app_name)
463458

464459
def start(self, name):
465460
"""Start a container."""

0 commit comments

Comments
 (0)