github.com/amrnt/deis@v1.3.1/controller/api/serializers.py (about)

     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']