We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
deis blocks domain creation under improper circumstances
`$ deis domains:add django.paas-sandbox`
`Adding django.paas-sandbox to rubber-undertow... 400 BAD REQUEST`
`{u'domain': [u'Hostname does not look like a valid hostname. Only lowercase characters are allowed.']}`

deis will not let me create a domain where the top of the domain is "paas-sandbox", as displayed above, however this is not a top level domain nor is it intended as one. Our corporation uses an internal domain that is set as the dns search on all corporate computers. For example, if someone types `http://www/` in their browser, it will come up because their dns searches `corp.example.com` for non qualifying domains. They are really going to the site `www.corp.example.com` but all they type and all the host header on the server sees is "www". deis requires the domain to exist how the user will display it in the browser in order to render the page. Our DNS system is almost identical to Active Directory so anyone using Active Directory can relate but this is not a MS / Active Directory specific scenario. This can be done using only dhcp and most ISP's will do this from their dhcp as well.

Anyways, please fix. Thank you.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `controller/api/serializers.py`
Content:
```
1 """
2 Classes to serialize the RESTful representation of Deis API models.
3 """
4 
5 from __future__ import unicode_literals
6 
7 import json
8 import re
9 
10 from django.conf import settings
11 from django.contrib.auth.models import User
12 from django.utils import timezone
13 from rest_framework import serializers
14 from rest_framework.validators import UniqueTogetherValidator
15 
16 from api import models
17 
18 
19 PROCTYPE_MATCH = re.compile(r'^(?P<type>[a-z]+)')
20 MEMLIMIT_MATCH = re.compile(r'^(?P<mem>[0-9]+[BbKkMmGg])$')
21 CPUSHARE_MATCH = re.compile(r'^(?P<cpu>[0-9]+)$')
22 TAGKEY_MATCH = re.compile(r'^[a-z]+$')
23 TAGVAL_MATCH = re.compile(r'^\w+$')
24 
25 
26 class JSONFieldSerializer(serializers.Field):
27     def to_representation(self, obj):
28         return obj
29 
30     def to_internal_value(self, data):
31         try:
32             val = json.loads(data)
33         except TypeError:
34             val = data
35         return val
36 
37 
38 class ModelSerializer(serializers.ModelSerializer):
39 
40     uuid = serializers.ReadOnlyField()
41 
42     def get_validators(self):
43         """
44         Hack to remove DRF's UniqueTogetherValidator when it concerns the UUID.
45 
46         See https://github.com/deis/deis/pull/2898#discussion_r23105147
47         """
48         validators = super(ModelSerializer, self).get_validators()
49         for v in validators:
50             if isinstance(v, UniqueTogetherValidator) and 'uuid' in v.fields:
51                 validators.remove(v)
52         return validators
53 
54 
55 class UserSerializer(serializers.ModelSerializer):
56     class Meta:
57         model = User
58         fields = ['email', 'username', 'password', 'first_name', 'last_name', 'is_superuser',
59                   'is_staff', 'groups', 'user_permissions', 'last_login', 'date_joined',
60                   'is_active']
61         read_only_fields = ['is_superuser', 'is_staff', 'groups',
62                             'user_permissions', 'last_login', 'date_joined', 'is_active']
63         extra_kwargs = {'password': {'write_only': True}}
64 
65     def create(self, validated_data):
66         now = timezone.now()
67         user = User(
68             email=validated_data.get('email'),
69             username=validated_data.get('username'),
70             last_login=now,
71             date_joined=now,
72             is_active=True
73         )
74         if validated_data.get('first_name'):
75             user.first_name = validated_data['first_name']
76         if validated_data.get('last_name'):
77             user.last_name = validated_data['last_name']
78         user.set_password(validated_data['password'])
79         # Make the first signup an admin / superuser
80         if not User.objects.filter(is_superuser=True).exists():
81             user.is_superuser = user.is_staff = True
82         user.save()
83         return user
84 
85 
86 class AdminUserSerializer(serializers.ModelSerializer):
87     """Serialize admin status for a User model."""
88 
89     class Meta:
90         model = User
91         fields = ['username', 'is_superuser']
92         read_only_fields = ['username']
93 
94 
95 class AppSerializer(ModelSerializer):
96     """Serialize a :class:`~api.models.App` model."""
97 
98     owner = serializers.ReadOnlyField(source='owner.username')
99     structure = JSONFieldSerializer(required=False)
100     created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
101     updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
102 
103     class Meta:
104         """Metadata options for a :class:`AppSerializer`."""
105         model = models.App
106         fields = ['uuid', 'id', 'owner', 'url', 'structure', 'created', 'updated']
107         read_only_fields = ['uuid']
108 
109 
110 class BuildSerializer(ModelSerializer):
111     """Serialize a :class:`~api.models.Build` model."""
112 
113     app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())
114     owner = serializers.ReadOnlyField(source='owner.username')
115     procfile = JSONFieldSerializer(required=False)
116     created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
117     updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
118 
119     class Meta:
120         """Metadata options for a :class:`BuildSerializer`."""
121         model = models.Build
122         fields = ['owner', 'app', 'image', 'sha', 'procfile', 'dockerfile', 'created',
123                   'updated', 'uuid']
124         read_only_fields = ['uuid']
125 
126 
127 class ConfigSerializer(ModelSerializer):
128     """Serialize a :class:`~api.models.Config` model."""
129 
130     app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())
131     owner = serializers.ReadOnlyField(source='owner.username')
132     values = JSONFieldSerializer(required=False)
133     memory = JSONFieldSerializer(required=False)
134     cpu = JSONFieldSerializer(required=False)
135     tags = JSONFieldSerializer(required=False)
136     created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
137     updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
138 
139     class Meta:
140         """Metadata options for a :class:`ConfigSerializer`."""
141         model = models.Config
142 
143     def validate_memory(self, value):
144         for k, v in value.items():
145             if v is None:  # use NoneType to unset a value
146                 continue
147             if not re.match(PROCTYPE_MATCH, k):
148                 raise serializers.ValidationError("Process types can only contain [a-z]")
149             if not re.match(MEMLIMIT_MATCH, str(v)):
150                 raise serializers.ValidationError(
151                     "Limit format: <number><unit>, where unit = B, K, M or G")
152         return value
153 
154     def validate_cpu(self, value):
155         for k, v in value.items():
156             if v is None:  # use NoneType to unset a value
157                 continue
158             if not re.match(PROCTYPE_MATCH, k):
159                 raise serializers.ValidationError("Process types can only contain [a-z]")
160             shares = re.match(CPUSHARE_MATCH, str(v))
161             if not shares:
162                 raise serializers.ValidationError("CPU shares must be an integer")
163             for v in shares.groupdict().values():
164                 try:
165                     i = int(v)
166                 except ValueError:
167                     raise serializers.ValidationError("CPU shares must be an integer")
168                 if i > 1024 or i < 0:
169                     raise serializers.ValidationError("CPU shares must be between 0 and 1024")
170         return value
171 
172     def validate_tags(self, value):
173         for k, v in value.items():
174             if v is None:  # use NoneType to unset a value
175                 continue
176             if not re.match(TAGKEY_MATCH, k):
177                 raise serializers.ValidationError("Tag keys can only contain [a-z]")
178             if not re.match(TAGVAL_MATCH, str(v)):
179                 raise serializers.ValidationError("Invalid tag value")
180         return value
181 
182 
183 class ReleaseSerializer(ModelSerializer):
184     """Serialize a :class:`~api.models.Release` model."""
185 
186     app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())
187     owner = serializers.ReadOnlyField(source='owner.username')
188     created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
189     updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
190 
191     class Meta:
192         """Metadata options for a :class:`ReleaseSerializer`."""
193         model = models.Release
194 
195 
196 class ContainerSerializer(ModelSerializer):
197     """Serialize a :class:`~api.models.Container` model."""
198 
199     app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())
200     owner = serializers.ReadOnlyField(source='owner.username')
201     created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
202     updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
203     release = serializers.SerializerMethodField()
204 
205     class Meta:
206         """Metadata options for a :class:`ContainerSerializer`."""
207         model = models.Container
208 
209     def get_release(self, obj):
210         return "v{}".format(obj.release.version)
211 
212 
213 class KeySerializer(ModelSerializer):
214     """Serialize a :class:`~api.models.Key` model."""
215 
216     owner = serializers.ReadOnlyField(source='owner.username')
217     created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
218     updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
219 
220     class Meta:
221         """Metadata options for a KeySerializer."""
222         model = models.Key
223 
224 
225 class DomainSerializer(ModelSerializer):
226     """Serialize a :class:`~api.models.Domain` model."""
227 
228     app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())
229     owner = serializers.ReadOnlyField(source='owner.username')
230     created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
231     updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
232 
233     class Meta:
234         """Metadata options for a :class:`DomainSerializer`."""
235         model = models.Domain
236         fields = ['uuid', 'owner', 'created', 'updated', 'app', 'domain']
237 
238     def validate_domain(self, value):
239         """
240         Check that the hostname is valid
241         """
242         match = re.match(
243             r'^(\*\.)?(' + settings.APP_URL_REGEX + r'\.)*([a-z0-9-]+)\.([a-z0-9]{2,})$',
244             value)
245         if not match:
246             raise serializers.ValidationError(
247                 "Hostname does not look like a valid hostname. "
248                 "Only lowercase characters are allowed.")
249 
250         if models.Domain.objects.filter(domain=value).exists():
251             raise serializers.ValidationError(
252                 "The domain {} is already in use by another app".format(value))
253 
254         domain_parts = value.split('.')
255         if domain_parts[0] == '*':
256             raise serializers.ValidationError(
257                 "Adding a wildcard subdomain is currently not supported".format(value))
258 
259         return value
260 
261 
262 class PushSerializer(ModelSerializer):
263     """Serialize a :class:`~api.models.Push` model."""
264 
265     app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())
266     owner = serializers.ReadOnlyField(source='owner.username')
267     created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
268     updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
269 
270     class Meta:
271         """Metadata options for a :class:`PushSerializer`."""
272         model = models.Push
273         fields = ['uuid', 'owner', 'app', 'sha', 'fingerprint', 'receive_user', 'receive_repo',
274                   'ssh_connection', 'ssh_original_command', 'created', 'updated']
275
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/controller/api/serializers.py b/controller/api/serializers.py
--- a/controller/api/serializers.py
+++ b/controller/api/serializers.py
@@ -239,23 +239,22 @@
         """
         Check that the hostname is valid
         """
-        match = re.match(
-            r'^(\*\.)?(' + settings.APP_URL_REGEX + r'\.)*([a-z0-9-]+)\.([a-z0-9]{2,})$',
-            value)
-        if not match:
+        if len(value) > 255:
+            raise serializers.ValidationError('Hostname must be 255 characters or less.')
+        if value[-1:] == ".":
+            value = value[:-1]  # strip exactly one dot from the right, if present
+        labels = value.split('.')
+        if labels[0] == '*':
             raise serializers.ValidationError(
-                "Hostname does not look like a valid hostname. "
-                "Only lowercase characters are allowed.")
-
+                'Adding a wildcard subdomain is currently not supported.')
+        allowed = re.compile("^(?!-)[a-z0-9-]{1,63}(?<!-)$", re.IGNORECASE)
+        for label in labels:
+            match = allowed.match(label)
+            if not match or '--' in label or label[-1].isdigit() or label.isdigit():
+                raise serializers.ValidationError('Hostname does not look valid.')
         if models.Domain.objects.filter(domain=value).exists():
             raise serializers.ValidationError(
                 "The domain {} is already in use by another app".format(value))
-
-        domain_parts = value.split('.')
-        if domain_parts[0] == '*':
-            raise serializers.ValidationError(
-                "Adding a wildcard subdomain is currently not supported".format(value))
-
         return value

{"golden_diff": "diff --git a/controller/api/serializers.py b/controller/api/serializers.py\n--- a/controller/api/serializers.py\n+++ b/controller/api/serializers.py\n@@ -239,23 +239,22 @@\n         \"\"\"\n         Check that the hostname is valid\n         \"\"\"\n-        match = re.match(\n-            r'^(\\*\\.)?(' + settings.APP_URL_REGEX + r'\\.)*([a-z0-9-]+)\\.([a-z0-9]{2,})$',\n-            value)\n-        if not match:\n+        if len(value) > 255:\n+            raise serializers.ValidationError('Hostname must be 255 characters or less.')\n+        if value[-1:] == \".\":\n+            value = value[:-1]  # strip exactly one dot from the right, if present\n+        labels = value.split('.')\n+        if labels[0] == '*':\n             raise serializers.ValidationError(\n-                \"Hostname does not look like a valid hostname. \"\n-                \"Only lowercase characters are allowed.\")\n-\n+                'Adding a wildcard subdomain is currently not supported.')\n+        allowed = re.compile(\"^(?!-)[a-z0-9-]{1,63}(?<!-)$\", re.IGNORECASE)\n+        for label in labels:\n+            match = allowed.match(label)\n+            if not match or '--' in label or label[-1].isdigit() or label.isdigit():\n+                raise serializers.ValidationError('Hostname does not look valid.')\n         if models.Domain.objects.filter(domain=value).exists():\n             raise serializers.ValidationError(\n                 \"The domain {} is already in use by another app\".format(value))\n-\n-        domain_parts = value.split('.')\n-        if domain_parts[0] == '*':\n-            raise serializers.ValidationError(\n-                \"Adding a wildcard subdomain is currently not supported\".format(value))\n-\n         return value\n", "issue": "deis blocks domain creation under improper circumstances\n`$ deis domains:add django.paas-sandbox`\n`Adding django.paas-sandbox to rubber-undertow... 400 BAD REQUEST`\n`{u'domain': [u'Hostname does not look like a valid hostname. Only lowercase characters are allowed.']}`\n\ndeis will not let me create a domain where the top of the domain is \"paas-sandbox\", as displayed above, however this is not a top level domain nor is it intended as one. Our corporation uses an internal domain that is set as the dns search on all corporate computers. For example, if someone types `http://www/` in their browser, it will come up because their dns searches `corp.example.com` for non qualifying domains. They are really going to the site `www.corp.example.com` but all they type and all the host header on the server sees is \"www\". deis requires the domain to exist how the user will display it in the browser in order to render the page. Our DNS system is almost identical to Active Directory so anyone using Active Directory can relate but this is not a MS / Active Directory specific scenario. This can be done using only dhcp and most ISP's will do this from their dhcp as well.\n\nAnyways, please fix. Thank you.\n\n", "before_files": [{"content": "\"\"\"\nClasses to serialize the RESTful representation of Deis API models.\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport json\nimport re\n\nfrom django.conf import settings\nfrom django.contrib.auth.models import User\nfrom django.utils import timezone\nfrom rest_framework import serializers\nfrom rest_framework.validators import UniqueTogetherValidator\n\nfrom api import models\n\n\nPROCTYPE_MATCH = re.compile(r'^(?P<type>[a-z]+)')\nMEMLIMIT_MATCH = re.compile(r'^(?P<mem>[0-9]+[BbKkMmGg])$')\nCPUSHARE_MATCH = re.compile(r'^(?P<cpu>[0-9]+)$')\nTAGKEY_MATCH = re.compile(r'^[a-z]+$')\nTAGVAL_MATCH = re.compile(r'^\\w+$')\n\n\nclass JSONFieldSerializer(serializers.Field):\n    def to_representation(self, obj):\n        return obj\n\n    def to_internal_value(self, data):\n        try:\n            val = json.loads(data)\n        except TypeError:\n            val = data\n        return val\n\n\nclass ModelSerializer(serializers.ModelSerializer):\n\n    uuid = serializers.ReadOnlyField()\n\n    def get_validators(self):\n        \"\"\"\n        Hack to remove DRF's UniqueTogetherValidator when it concerns the UUID.\n\n        See https://github.com/deis/deis/pull/2898#discussion_r23105147\n        \"\"\"\n        validators = super(ModelSerializer, self).get_validators()\n        for v in validators:\n            if isinstance(v, UniqueTogetherValidator) and 'uuid' in v.fields:\n                validators.remove(v)\n        return validators\n\n\nclass UserSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = User\n        fields = ['email', 'username', 'password', 'first_name', 'last_name', 'is_superuser',\n                  'is_staff', 'groups', 'user_permissions', 'last_login', 'date_joined',\n                  'is_active']\n        read_only_fields = ['is_superuser', 'is_staff', 'groups',\n                            'user_permissions', 'last_login', 'date_joined', 'is_active']\n        extra_kwargs = {'password': {'write_only': True}}\n\n    def create(self, validated_data):\n        now = timezone.now()\n        user = User(\n            email=validated_data.get('email'),\n            username=validated_data.get('username'),\n            last_login=now,\n            date_joined=now,\n            is_active=True\n        )\n        if validated_data.get('first_name'):\n            user.first_name = validated_data['first_name']\n        if validated_data.get('last_name'):\n            user.last_name = validated_data['last_name']\n        user.set_password(validated_data['password'])\n        # Make the first signup an admin / superuser\n        if not User.objects.filter(is_superuser=True).exists():\n            user.is_superuser = user.is_staff = True\n        user.save()\n        return user\n\n\nclass AdminUserSerializer(serializers.ModelSerializer):\n    \"\"\"Serialize admin status for a User model.\"\"\"\n\n    class Meta:\n        model = User\n        fields = ['username', 'is_superuser']\n        read_only_fields = ['username']\n\n\nclass AppSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.App` model.\"\"\"\n\n    owner = serializers.ReadOnlyField(source='owner.username')\n    structure = JSONFieldSerializer(required=False)\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`AppSerializer`.\"\"\"\n        model = models.App\n        fields = ['uuid', 'id', 'owner', 'url', 'structure', 'created', 'updated']\n        read_only_fields = ['uuid']\n\n\nclass BuildSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Build` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    procfile = JSONFieldSerializer(required=False)\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`BuildSerializer`.\"\"\"\n        model = models.Build\n        fields = ['owner', 'app', 'image', 'sha', 'procfile', 'dockerfile', 'created',\n                  'updated', 'uuid']\n        read_only_fields = ['uuid']\n\n\nclass ConfigSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Config` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    values = JSONFieldSerializer(required=False)\n    memory = JSONFieldSerializer(required=False)\n    cpu = JSONFieldSerializer(required=False)\n    tags = JSONFieldSerializer(required=False)\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`ConfigSerializer`.\"\"\"\n        model = models.Config\n\n    def validate_memory(self, value):\n        for k, v in value.items():\n            if v is None:  # use NoneType to unset a value\n                continue\n            if not re.match(PROCTYPE_MATCH, k):\n                raise serializers.ValidationError(\"Process types can only contain [a-z]\")\n            if not re.match(MEMLIMIT_MATCH, str(v)):\n                raise serializers.ValidationError(\n                    \"Limit format: <number><unit>, where unit = B, K, M or G\")\n        return value\n\n    def validate_cpu(self, value):\n        for k, v in value.items():\n            if v is None:  # use NoneType to unset a value\n                continue\n            if not re.match(PROCTYPE_MATCH, k):\n                raise serializers.ValidationError(\"Process types can only contain [a-z]\")\n            shares = re.match(CPUSHARE_MATCH, str(v))\n            if not shares:\n                raise serializers.ValidationError(\"CPU shares must be an integer\")\n            for v in shares.groupdict().values():\n                try:\n                    i = int(v)\n                except ValueError:\n                    raise serializers.ValidationError(\"CPU shares must be an integer\")\n                if i > 1024 or i < 0:\n                    raise serializers.ValidationError(\"CPU shares must be between 0 and 1024\")\n        return value\n\n    def validate_tags(self, value):\n        for k, v in value.items():\n            if v is None:  # use NoneType to unset a value\n                continue\n            if not re.match(TAGKEY_MATCH, k):\n                raise serializers.ValidationError(\"Tag keys can only contain [a-z]\")\n            if not re.match(TAGVAL_MATCH, str(v)):\n                raise serializers.ValidationError(\"Invalid tag value\")\n        return value\n\n\nclass ReleaseSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Release` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`ReleaseSerializer`.\"\"\"\n        model = models.Release\n\n\nclass ContainerSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Container` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    release = serializers.SerializerMethodField()\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`ContainerSerializer`.\"\"\"\n        model = models.Container\n\n    def get_release(self, obj):\n        return \"v{}\".format(obj.release.version)\n\n\nclass KeySerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Key` model.\"\"\"\n\n    owner = serializers.ReadOnlyField(source='owner.username')\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a KeySerializer.\"\"\"\n        model = models.Key\n\n\nclass DomainSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Domain` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`DomainSerializer`.\"\"\"\n        model = models.Domain\n        fields = ['uuid', 'owner', 'created', 'updated', 'app', 'domain']\n\n    def validate_domain(self, value):\n        \"\"\"\n        Check that the hostname is valid\n        \"\"\"\n        match = re.match(\n            r'^(\\*\\.)?(' + settings.APP_URL_REGEX + r'\\.)*([a-z0-9-]+)\\.([a-z0-9]{2,})$',\n            value)\n        if not match:\n            raise serializers.ValidationError(\n                \"Hostname does not look like a valid hostname. \"\n                \"Only lowercase characters are allowed.\")\n\n        if models.Domain.objects.filter(domain=value).exists():\n            raise serializers.ValidationError(\n                \"The domain {} is already in use by another app\".format(value))\n\n        domain_parts = value.split('.')\n        if domain_parts[0] == '*':\n            raise serializers.ValidationError(\n                \"Adding a wildcard subdomain is currently not supported\".format(value))\n\n        return value\n\n\nclass PushSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Push` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`PushSerializer`.\"\"\"\n        model = models.Push\n        fields = ['uuid', 'owner', 'app', 'sha', 'fingerprint', 'receive_user', 'receive_repo',\n                  'ssh_connection', 'ssh_original_command', 'created', 'updated']\n", "path": "controller/api/serializers.py"}], "after_files": [{"content": "\"\"\"\nClasses to serialize the RESTful representation of Deis API models.\n\"\"\"\n\nfrom __future__ import unicode_literals\n\nimport json\nimport re\n\nfrom django.conf import settings\nfrom django.contrib.auth.models import User\nfrom django.utils import timezone\nfrom rest_framework import serializers\nfrom rest_framework.validators import UniqueTogetherValidator\n\nfrom api import models\n\n\nPROCTYPE_MATCH = re.compile(r'^(?P<type>[a-z]+)')\nMEMLIMIT_MATCH = re.compile(r'^(?P<mem>[0-9]+[BbKkMmGg])$')\nCPUSHARE_MATCH = re.compile(r'^(?P<cpu>[0-9]+)$')\nTAGKEY_MATCH = re.compile(r'^[a-z]+$')\nTAGVAL_MATCH = re.compile(r'^\\w+$')\n\n\nclass JSONFieldSerializer(serializers.Field):\n    def to_representation(self, obj):\n        return obj\n\n    def to_internal_value(self, data):\n        try:\n            val = json.loads(data)\n        except TypeError:\n            val = data\n        return val\n\n\nclass ModelSerializer(serializers.ModelSerializer):\n\n    uuid = serializers.ReadOnlyField()\n\n    def get_validators(self):\n        \"\"\"\n        Hack to remove DRF's UniqueTogetherValidator when it concerns the UUID.\n\n        See https://github.com/deis/deis/pull/2898#discussion_r23105147\n        \"\"\"\n        validators = super(ModelSerializer, self).get_validators()\n        for v in validators:\n            if isinstance(v, UniqueTogetherValidator) and 'uuid' in v.fields:\n                validators.remove(v)\n        return validators\n\n\nclass UserSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = User\n        fields = ['email', 'username', 'password', 'first_name', 'last_name', 'is_superuser',\n                  'is_staff', 'groups', 'user_permissions', 'last_login', 'date_joined',\n                  'is_active']\n        read_only_fields = ['is_superuser', 'is_staff', 'groups',\n                            'user_permissions', 'last_login', 'date_joined', 'is_active']\n        extra_kwargs = {'password': {'write_only': True}}\n\n    def create(self, validated_data):\n        now = timezone.now()\n        user = User(\n            email=validated_data.get('email'),\n            username=validated_data.get('username'),\n            last_login=now,\n            date_joined=now,\n            is_active=True\n        )\n        if validated_data.get('first_name'):\n            user.first_name = validated_data['first_name']\n        if validated_data.get('last_name'):\n            user.last_name = validated_data['last_name']\n        user.set_password(validated_data['password'])\n        # Make the first signup an admin / superuser\n        if not User.objects.filter(is_superuser=True).exists():\n            user.is_superuser = user.is_staff = True\n        user.save()\n        return user\n\n\nclass AdminUserSerializer(serializers.ModelSerializer):\n    \"\"\"Serialize admin status for a User model.\"\"\"\n\n    class Meta:\n        model = User\n        fields = ['username', 'is_superuser']\n        read_only_fields = ['username']\n\n\nclass AppSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.App` model.\"\"\"\n\n    owner = serializers.ReadOnlyField(source='owner.username')\n    structure = JSONFieldSerializer(required=False)\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`AppSerializer`.\"\"\"\n        model = models.App\n        fields = ['uuid', 'id', 'owner', 'url', 'structure', 'created', 'updated']\n        read_only_fields = ['uuid']\n\n\nclass BuildSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Build` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    procfile = JSONFieldSerializer(required=False)\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`BuildSerializer`.\"\"\"\n        model = models.Build\n        fields = ['owner', 'app', 'image', 'sha', 'procfile', 'dockerfile', 'created',\n                  'updated', 'uuid']\n        read_only_fields = ['uuid']\n\n\nclass ConfigSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Config` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    values = JSONFieldSerializer(required=False)\n    memory = JSONFieldSerializer(required=False)\n    cpu = JSONFieldSerializer(required=False)\n    tags = JSONFieldSerializer(required=False)\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`ConfigSerializer`.\"\"\"\n        model = models.Config\n\n    def validate_memory(self, value):\n        for k, v in value.items():\n            if v is None:  # use NoneType to unset a value\n                continue\n            if not re.match(PROCTYPE_MATCH, k):\n                raise serializers.ValidationError(\"Process types can only contain [a-z]\")\n            if not re.match(MEMLIMIT_MATCH, str(v)):\n                raise serializers.ValidationError(\n                    \"Limit format: <number><unit>, where unit = B, K, M or G\")\n        return value\n\n    def validate_cpu(self, value):\n        for k, v in value.items():\n            if v is None:  # use NoneType to unset a value\n                continue\n            if not re.match(PROCTYPE_MATCH, k):\n                raise serializers.ValidationError(\"Process types can only contain [a-z]\")\n            shares = re.match(CPUSHARE_MATCH, str(v))\n            if not shares:\n                raise serializers.ValidationError(\"CPU shares must be an integer\")\n            for v in shares.groupdict().values():\n                try:\n                    i = int(v)\n                except ValueError:\n                    raise serializers.ValidationError(\"CPU shares must be an integer\")\n                if i > 1024 or i < 0:\n                    raise serializers.ValidationError(\"CPU shares must be between 0 and 1024\")\n        return value\n\n    def validate_tags(self, value):\n        for k, v in value.items():\n            if v is None:  # use NoneType to unset a value\n                continue\n            if not re.match(TAGKEY_MATCH, k):\n                raise serializers.ValidationError(\"Tag keys can only contain [a-z]\")\n            if not re.match(TAGVAL_MATCH, str(v)):\n                raise serializers.ValidationError(\"Invalid tag value\")\n        return value\n\n\nclass ReleaseSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Release` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`ReleaseSerializer`.\"\"\"\n        model = models.Release\n\n\nclass ContainerSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Container` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    release = serializers.SerializerMethodField()\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`ContainerSerializer`.\"\"\"\n        model = models.Container\n\n    def get_release(self, obj):\n        return \"v{}\".format(obj.release.version)\n\n\nclass KeySerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Key` model.\"\"\"\n\n    owner = serializers.ReadOnlyField(source='owner.username')\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a KeySerializer.\"\"\"\n        model = models.Key\n\n\nclass DomainSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Domain` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`DomainSerializer`.\"\"\"\n        model = models.Domain\n        fields = ['uuid', 'owner', 'created', 'updated', 'app', 'domain']\n\n    def validate_domain(self, value):\n        \"\"\"\n        Check that the hostname is valid\n        \"\"\"\n        if len(value) > 255:\n            raise serializers.ValidationError('Hostname must be 255 characters or less.')\n        if value[-1:] == \".\":\n            value = value[:-1]  # strip exactly one dot from the right, if present\n        labels = value.split('.')\n        if labels[0] == '*':\n            raise serializers.ValidationError(\n                'Adding a wildcard subdomain is currently not supported.')\n        allowed = re.compile(\"^(?!-)[a-z0-9-]{1,63}(?<!-)$\", re.IGNORECASE)\n        for label in labels:\n            match = allowed.match(label)\n            if not match or '--' in label or label[-1].isdigit() or label.isdigit():\n                raise serializers.ValidationError('Hostname does not look valid.')\n        if models.Domain.objects.filter(domain=value).exists():\n            raise serializers.ValidationError(\n                \"The domain {} is already in use by another app\".format(value))\n        return value\n\n\nclass PushSerializer(ModelSerializer):\n    \"\"\"Serialize a :class:`~api.models.Push` model.\"\"\"\n\n    app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())\n    owner = serializers.ReadOnlyField(source='owner.username')\n    created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n    updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)\n\n    class Meta:\n        \"\"\"Metadata options for a :class:`PushSerializer`.\"\"\"\n        model = models.Push\n        fields = ['uuid', 'owner', 'app', 'sha', 'fingerprint', 'receive_user', 'receive_repo',\n                  'ssh_connection', 'ssh_original_command', 'created', 'updated']\n", "path": "controller/api/serializers.py"}]}