Skip to content

Commit

Permalink
feat(registry): move registry information to its own top level Config…
Browse files Browse the repository at this point in the history
… resource

ref deis#639
  • Loading branch information
helgi committed Apr 21, 2016
1 parent 0cb2f02 commit 4014d4b
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 8 deletions.
10 changes: 9 additions & 1 deletion rootfs/api/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Config(UuidAuditedModel):
memory = JSONField(default={}, blank=True)
cpu = JSONField(default={}, blank=True)
tags = JSONField(default={}, blank=True)
registry = JSONField(default={}, blank=True)

class Meta:
get_latest_by = 'created'
Expand Down Expand Up @@ -84,7 +85,7 @@ def save(self, **kwargs):
"""merge the old config with the new"""
try:
previous_config = self.app.config_set.latest()
for attr in ['cpu', 'memory', 'tags', 'values']:
for attr in ['cpu', 'memory', 'tags', 'registry', 'values']:
# Guard against migrations from older apps without fixes to
# JSONField encoding.
try:
Expand All @@ -107,6 +108,13 @@ def save(self, **kwargs):
# set any missing HEALTHCHECK_* elements
self.set_healthchecks()

# lower case all registry options for consistency
if self.registry:
registry = {}
for key, value in self.registry.items():
registry[key.lower()] = value
self.registry = registry

# verify the tags exist on any nodes as labels
if self.tags:
# Get all nodes with label selectors
Expand Down
7 changes: 4 additions & 3 deletions rootfs/api/models/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ def publish(self, source_version='latest'):
if not self.build.dockerfile and not self.build.sha:
# gather custom login information for registry if needed
auth = None
if self.config.values.get('IMAGE_AUTH_USER', None):
if self.config.registry.get('username', None):
auth = {
'username': self.config.values.get('IMAGE_AUTH_USER', None),
'password': self.config.values.get('IMAGE_AUTH_PASSWORD', None),
'username': self.config.registry.get('username', None),
'password': self.config.registry.get('password', None),
'email': self.owner.email
}

Expand Down Expand Up @@ -233,6 +233,7 @@ def _delete_release_in_scheduler(self, namespace, version):
except KubeHTTPException:
pass

# TODO: Add registry in here
def save(self, *args, **kwargs): # noqa
if not self.summary:
self.summary = ''
Expand Down
9 changes: 9 additions & 0 deletions rootfs/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class ConfigSerializer(serializers.ModelSerializer):
memory = JSONFieldSerializer(required=False, binary=True)
cpu = JSONFieldSerializer(required=False, binary=True)
tags = JSONFieldSerializer(required=False, binary=True)
registry = JSONFieldSerializer(required=False, binary=True)

class Meta:
"""Metadata options for a :class:`ConfigSerializer`."""
Expand Down Expand Up @@ -208,6 +209,14 @@ def validate_tags(self, data):

return data

def validate_registry(self, data):
for key, value in data.items():
if not re.match(CONFIGKEY_MATCH, key):
raise serializers.ValidationError(
"Config keys must start with a letter or underscore and "
"only contain [A-z0-9_]")

return data

class ReleaseSerializer(serializers.ModelSerializer):
"""Serialize a :class:`~api.models.Release` model."""
Expand Down
80 changes: 76 additions & 4 deletions rootfs/api/tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,15 @@ def test_response_data(self, mock_requests):
response = self.client.post(url, body)
for key in response.data:
self.assertIn(key, ['uuid', 'owner', 'created', 'updated', 'app', 'values', 'memory',
'cpu', 'tags'])
'cpu', 'tags', 'registry'])
expected = {
'owner': self.user.username,
'app': 'test',
'values': {'PORT': '5000'},
'memory': {},
'cpu': {},
'tags': {}
'tags': {},
'registry': {}
}
self.assertDictContainsSubset(expected, response.data)

Expand All @@ -148,14 +149,15 @@ def test_response_data_types_converted(self, mock_requests):
self.assertEqual(response.status_code, 201)
for key in response.data:
self.assertIn(key, ['uuid', 'owner', 'created', 'updated', 'app', 'values', 'memory',
'cpu', 'tags'])
'cpu', 'tags', 'registry'])
expected = {
'owner': self.user.username,
'app': 'test',
'values': {'PORT': '5000'},
'memory': {},
'cpu': {'web': "1024"},
'tags': {}
'tags': {},
'registry': {}
}
self.assertDictContainsSubset(expected, response.data)

Expand Down Expand Up @@ -535,6 +537,76 @@ def test_tags(self, mock_requests):
response = self.client.delete(url)
self.assertEqual(response.status_code, 405)

def test_registry(self, mock_requests):
"""
Test that registry information can be set on an application
"""
url = '/v2/apps'
response = self.client.post(url)
self.assertEqual(response.status_code, 201)
app_id = response.data['id']

# check default
url = '/v2/apps/{app_id}/config'.format(**locals())
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn('registry', response.data)
self.assertEqual(response.data['registry'], {})

# set some registry information
body = {'registry': json.dumps({'username': 'bob'})}
response = self.client.post(url, body)
self.assertEqual(response.status_code, 201)
registry1 = response.data

# check registry information again
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertIn('registry', response.data)
registry = response.data['registry']
self.assertIn('username', registry)
self.assertEqual(registry['username'], 'bob')

# set an additional value
# set them upper case, internally it should translate to lower
body = {'registry': json.dumps({'PASSWORD': 's3cur3pw1'})}
response = self.client.post(url, body)
self.assertEqual(response.status_code, 201)
registry2 = response.data
self.assertNotEqual(registry1['uuid'], registry2['uuid'])
registry = response.data['registry']
self.assertIn('password', registry)
self.assertEqual(registry['password'], 's3cur3pw1')
self.assertIn('username', registry)
self.assertEqual(registry['username'], 'bob')

# read the registry information again
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
registry3 = response.data
self.assertEqual(registry2, registry3)
registry = response.data['registry']
self.assertIn('password', registry)
self.assertEqual(registry['password'], 's3cur3pw1')
self.assertIn('username', registry)
self.assertEqual(registry['username'], 'bob')

# unset a value
body = {'registry': json.dumps({'password': None})}
response = self.client.post(url, body)
self.assertEqual(response.status_code, 201)
registry4 = response.data
self.assertNotEqual(registry3['uuid'], registry4['uuid'])
self.assertNotIn('password', json.dumps(response.data['registry']))

# disallow put/patch/delete
response = self.client.put(url)
self.assertEqual(response.status_code, 405)
response = self.client.patch(url)
self.assertEqual(response.status_code, 405)
response = self.client.delete(url)
self.assertEqual(response.status_code, 405)

def test_config_owner_is_requesting_user(self, mock_requests):
"""
Ensure that setting the config value is owned by the requesting user
Expand Down

0 comments on commit 4014d4b

Please sign in to comment.