We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Domain name validation: w3.domain.com
I tried to add a domain to my app: `deis domains:add w3.domain.com`
and got the following error :
`{u'domain': [u'Hostname does not look valid.']}`
w3.domain.com is a valid domain.


--- 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         fields = ['owner', 'app', 'release', 'type', 'num', 'state', 'created', 'updated', 'uuid']
209 
210     def get_release(self, obj):
211         return "v{}".format(obj.release.version)
212 
213 
214 class KeySerializer(ModelSerializer):
215     """Serialize a :class:`~api.models.Key` model."""
216 
217     owner = serializers.ReadOnlyField(source='owner.username')
218     created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
219     updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
220 
221     class Meta:
222         """Metadata options for a KeySerializer."""
223         model = models.Key
224 
225 
226 class DomainSerializer(ModelSerializer):
227     """Serialize a :class:`~api.models.Domain` model."""
228 
229     app = serializers.SlugRelatedField(slug_field='id', queryset=models.App.objects.all())
230     owner = serializers.ReadOnlyField(source='owner.username')
231     created = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
232     updated = serializers.DateTimeField(format=settings.DEIS_DATETIME_FORMAT, read_only=True)
233 
234     class Meta:
235         """Metadata options for a :class:`DomainSerializer`."""
236         model = models.Domain
237         fields = ['uuid', 'owner', 'created', 'updated', 'app', 'domain']
238 
239     def validate_domain(self, value):
240         """
241         Check that the hostname is valid
242         """
243         if len(value) > 255:
244             raise serializers.ValidationError('Hostname must be 255 characters or less.')
245         if value[-1:] == ".":
246             value = value[:-1]  # strip exactly one dot from the right, if present
247         labels = value.split('.')
248         if labels[0] == '*':
249             raise serializers.ValidationError(
250                 'Adding a wildcard subdomain is currently not supported.')
251         allowed = re.compile("^(?!-)[a-z0-9-]{1,63}(?<!-)$", re.IGNORECASE)
252         for label in labels:
253             match = allowed.match(label)
254             if not match or '--' in label or label[-1].isdigit() or label.isdigit():
255                 raise serializers.ValidationError('Hostname does not look valid.')
256         if models.Domain.objects.filter(domain=value).exists():
257             raise serializers.ValidationError(
258                 "The domain {} is already in use by another app".format(value))
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
@@ -251,7 +251,8 @@
         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():
+            if not match or '--' in label or label.isdigit() or \
+               len(labels) == 1 and any(char.isdigit() for char in label):
                 raise serializers.ValidationError('Hostname does not look valid.')
         if models.Domain.objects.filter(domain=value).exists():
             raise serializers.ValidationError(

{"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@@ -251,7 +251,8 @@\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+            if not match or '--' in label or label.isdigit() or \\\n+               len(labels) == 1 and any(char.isdigit() for char in label):\n                 raise serializers.ValidationError('Hostname does not look valid.')\n         if models.Domain.objects.filter(domain=value).exists():\n             raise serializers.ValidationError(\n", "issue": "Domain name validation: w3.domain.com\nI tried to add a domain to my app: `deis domains:add w3.domain.com`\nand got the following error :\n`{u'domain': [u'Hostname does not look valid.']}`\nw3.domain.com is a valid domain.\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        fields = ['owner', 'app', 'release', 'type', 'num', 'state', 'created', 'updated', 'uuid']\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"}], "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        fields = ['owner', 'app', 'release', 'type', 'num', 'state', 'created', 'updated', 'uuid']\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.isdigit() or \\\n               len(labels) == 1 and any(char.isdigit() for char in label):\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"}]}