Repository: deis/deis

Issue:
Feature request: customize controller host name
Is there a config option to set the deis controller url? (deis.example.com)
if not there should be. I am runnig in a corporate environment with strict DNS rules



Discussion:
It's not currently possible to set the "deis" prefix of the host name separately, if that's what you are asking. Whatever domain you use to access your Deis cluster, there are some hard-coded prefixes that the router mesh recognizes in order to route traffic to the proper component, and this is one of those.

there are a few edge cases which concerns me a little with changing the controller URI in the router:
1. We have to refuse API requests for creating apps with the same name
2. We have to refuse API requests for custom domains with the same endpoint

We have [those measures in place](https://github.com/deis/deis/blob/master/controller/api/models.py#L93-L96), hence the concerns. Any thoughts on this?


Resolving patch:
diff --git a/controller/api/models.py b/controller/api/models.py
--- a/controller/api/models.py
+++ b/controller/api/models.py
@@ -92,7 +92,7 @@ def validate_app_structure(value):
 
 def validate_reserved_names(value):
     """A value cannot use some reserved names."""
-    if value in ['deis']:
+    if value in settings.DEIS_RESERVED_NAMES:
         raise ValidationError('{} is a reserved name.'.format(value))
 
 
diff --git a/controller/deis/settings.py b/controller/deis/settings.py
--- a/controller/deis/settings.py
+++ b/controller/deis/settings.py
@@ -287,6 +287,9 @@
 # standard datetime format used for logging, model timestamps, etc.
 DEIS_DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%S%Z'
 
+# names which apps cannot reserve for routing
+DEIS_RESERVED_NAMES = ['deis']
+
 # default scheduler settings
 SCHEDULER_MODULE = 'scheduler.mock'
 SCHEDULER_TARGET = ''  # path to scheduler endpoint (e.g. /var/run/fleet.sock)
diff --git a/controller/templates/confd_settings.py b/controller/templates/confd_settings.py
--- a/controller/templates/confd_settings.py
+++ b/controller/templates/confd_settings.py
@@ -49,6 +49,10 @@
 {{ end }}
 UNIT_HOSTNAME = '{{ if exists "/deis/controller/unitHostname" }}{{ getv "/deis/controller/unitHostname" }}{{ else }}default{{ end }}'
 
+{{ if exists "/deis/controller/subdomain" }}
+DEIS_RESERVED_NAMES = ['{{ getv "/deis/controller/subdomain" }}']
+{{ end }}
+
 # AUTH
 # LDAP
 {{ if exists "/deis/controller/auth/ldap/endpoint" }}


Test patch:
diff --git a/controller/api/tests/test_app.py b/controller/api/tests/test_app.py
--- a/controller/api/tests/test_app.py
+++ b/controller/api/tests/test_app.py
@@ -156,10 +156,6 @@ def test_app_errors(self):
                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
         self.assertContains(response, 'App IDs can only contain [a-z0-9-]', status_code=400)
         url = '/v1/apps'
-        body = {'id': 'deis'}
-        response = self.client.post(url, json.dumps(body), content_type='application/json',
-                                    HTTP_AUTHORIZATION='token {}'.format(self.token))
-        self.assertContains(response, 'deis is a reserved name.', status_code=400)
         body = {'id': app_id}
         response = self.client.post(url, json.dumps(body), content_type='application/json',
                                     HTTP_AUTHORIZATION='token {}'.format(self.token))
@@ -175,6 +171,20 @@ def test_app_errors(self):
                                        HTTP_AUTHORIZATION='token {}'.format(self.token))
             self.assertEquals(response.status_code, 404)
 
+    def test_app_reserved_names(self):
+        """Nobody should be able to create applications with names which are reserved."""
+        url = '/v1/apps'
+        reserved_names = ['foo', 'bar']
+        with self.settings(DEIS_RESERVED_NAMES=reserved_names):
+            for name in reserved_names:
+                body = {'id': name}
+                response = self.client.post(url, json.dumps(body), content_type='application/json',
+                                            HTTP_AUTHORIZATION='token {}'.format(self.token))
+                self.assertContains(
+                    response,
+                    '{} is a reserved name.'.format(name),
+                    status_code=400)
+
     def test_app_structure_is_valid_json(self):
         """Application structures should be valid JSON objects."""
         url = '/v1/apps'