Skip to content

Commit 1e90c0c

Browse files
authored
Merge pull request #904 from kmala/regis
fest(registry):Add support for private registry
2 parents 9849c5f + a1dabf3 commit 1e90c0c

6 files changed

Lines changed: 123 additions & 37 deletions

File tree

rootfs/api/models/release.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ def __str__(self):
3737

3838
@property
3939
def image(self):
40+
if (settings.REGISTRY_LOCATION != 'on-cluster'):
41+
return self.build.image
4042
# Builder pushes to internal registry, exclude SHA based images from being returned
4143
registry = self.config.registry
4244
if (
@@ -111,7 +113,7 @@ def publish(self):
111113
not self.build.sha and
112114
# hostname tells Builder where to push images
113115
not registry.get('hostname', None)
114-
):
116+
) or (settings.REGISTRY_LOCATION != 'on-cluster'):
115117
self.app.log('{} exists in the target registry. Using image for release {} of app {}'.format(self.build.image, self.version, self.app)) # noqa
116118
return
117119

@@ -148,7 +150,7 @@ def get_port(self):
148150
return 5000
149151

150152
# application has registry auth - $PORT is required
151-
if creds is not None:
153+
if (creds is not None) or (settings.REGISTRY_LOCATION != 'on-cluster'):
152154
if envs.get('PORT', None) is None:
153155
self.app.log('Private registry detected but no $PORT defined. Defaulting to $PORT 5000', logging.WARNING) # noqa
154156
return 5000

rootfs/api/settings/production.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@
283283
REGISTRY_HOST = os.environ.get('DEIS_REGISTRY_SERVICE_HOST', '127.0.0.1')
284284
REGISTRY_PORT = os.environ.get('DEIS_REGISTRY_SERVICE_PORT', 5000)
285285
REGISTRY_URL = '{}:{}'.format(REGISTRY_HOST, REGISTRY_PORT)
286+
REGISTRY_LOCATION = os.environ.get('DEIS_REGISTRY_LOCATION', 'on-cluster')
287+
REGISTRY_SECRET_PREFIX = os.environ.get('DEIS_REGISTRY_SECRET_PREFIX', 'private-registry')
286288

287289
# logger settings
288290
LOGGER_HOST = os.environ.get('DEIS_LOGGER_SERVICE_HOST', '127.0.0.1')

rootfs/api/tests/test_release.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from django.contrib.auth.models import User
1212
from django.core.cache import cache
13+
from django.conf import settings
1314
from rest_framework.test import APITransactionTestCase
1415
from unittest import mock
1516
from rest_framework.authtoken.models import Token
@@ -391,3 +392,27 @@ def test_release_get_port(self, mock_requests):
391392
self.assertEqual(release.get_port(), 8080)
392393

393394
# TODO(bacongobbler): test dockerfile ports
395+
396+
def test_release_external_registry(self, mock_requests):
397+
"""
398+
Test that get_port always returns the proper value.
399+
"""
400+
app_id = "test"
401+
body = {'id': app_id}
402+
response = self.client.post('/v2/apps', body,)
403+
self.assertEqual(response.status_code, 201, response.data)
404+
body = {'values': json.dumps({'PORT': '3000'})}
405+
config_response = self.client.post('/v2/apps/test/config', body)
406+
self.assertEqual(config_response.status_code, 201, config_response.data)
407+
408+
app = App.objects.get(id=app_id)
409+
settings.REGISTRY_LOCATION = "off-cluster"
410+
url = '/v2/apps/{app_id}/builds'.format(**locals())
411+
body = {'image': 'test/autotest/example'}
412+
response = self.client.post(url, body)
413+
self.assertEqual(response.status_code, 201, response.data)
414+
release = app.release_set.latest()
415+
416+
self.assertEqual(release.get_port(), 3000)
417+
418+
self.assertEqual(release.image, 'test/autotest/example')

rootfs/scheduler/__init__.py

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -643,15 +643,31 @@ def _set_container(self, namespace, container_name, data, **kwargs): # noqa
643643
self._default_readiness_probe(data, kwargs.get('build_type'), env.get('PORT', None))
644644

645645
def _get_private_registry_config(self, registry, image):
646-
# try to get the hostname information
647-
hostname = registry.get('hostname', None)
648-
if not hostname:
649-
hostname, _ = docker_auth.split_repo_name(image)
650-
if hostname == docker_auth.INDEX_NAME:
651-
hostname = "https://index.docker.io/v1/"
646+
secret_name = settings.REGISTRY_SECRET_PREFIX
647+
if registry:
648+
# try to get the hostname information
649+
hostname = registry.get('hostname', None)
650+
if not hostname:
651+
hostname, _ = docker_auth.split_repo_name(image)
652+
if hostname == docker_auth.INDEX_NAME:
653+
hostname = "https://index.docker.io/v1/"
654+
username = registry.get('username')
655+
password = registry.get('password')
656+
elif settings.REGISTRY_LOCATION == 'off-cluster':
657+
secret = self.get_secret('deis', 'registry-secret').json()
658+
username = secret['data']['username']
659+
password = secret['data']['password']
660+
hostname = secret['data']['hostname']
661+
if hostname == '':
662+
hostname = "https://index.docker.io/v1/"
663+
secret_name = secret_name+"-"+settings.REGISTRY_LOCATION
664+
elif settings.REGISTRY_LOCATION in ['ecr', 'gcr']:
665+
return None, secret_name+"-"+settings.REGISTRY_LOCATION, False
666+
else:
667+
return None, None, None
652668

653669
# create / update private registry secret
654-
auth = bytes('{}:{}'.format(registry.get('username'), registry.get('password')), 'UTF-8')
670+
auth = bytes('{}:{}'.format(username, password), 'UTF-8')
655671
# value has to be a base64 encoded JSON
656672
docker_config = json.dumps({
657673
"auths": {
@@ -660,36 +676,34 @@ def _get_private_registry_config(self, registry, image):
660676
}
661677
}
662678
})
663-
return docker_config
679+
return docker_config, secret_name, True
664680

665681
def _set_image_secret(self, data, namespace, **kwargs):
666682
"""
667683
Take registry information and set as an imagePullSecret for an RC / Deployment
668684
http://kubernetes.io/docs/user-guide/images/#specifying-imagepullsecrets-on-a-pod
669685
"""
670-
registry = kwargs.get('registry', {})
671-
if not registry:
686+
docker_config, secret_name, secret_create = self._get_private_registry_config(kwargs.get('registry', {}), kwargs.get('image')) # noqa
687+
if secret_create is None:
672688
return
673-
docker_config = self._get_private_registry_config(registry, kwargs.get('image')) # noqa
674-
secret_data = {'.dockerconfigjson': docker_config}
675-
676-
secret_name = 'private-registry'
677-
try:
678-
self.get_secret(namespace, secret_name)
679-
except KubeHTTPException:
680-
self.create_secret(
681-
namespace,
682-
secret_name,
683-
secret_data,
684-
secret_type='kubernetes.io/dockerconfigjson'
685-
)
686-
else:
687-
self.update_secret(
688-
namespace,
689-
secret_name,
690-
secret_data,
691-
secret_type='kubernetes.io/dockerconfigjson'
692-
)
689+
elif secret_create:
690+
secret_data = {'.dockerconfigjson': docker_config}
691+
try:
692+
self.get_secret(namespace, secret_name)
693+
except KubeHTTPException:
694+
self.create_secret(
695+
namespace,
696+
secret_name,
697+
secret_data,
698+
secret_type='kubernetes.io/dockerconfigjson'
699+
)
700+
else:
701+
self.update_secret(
702+
namespace,
703+
secret_name,
704+
secret_data,
705+
secret_type='kubernetes.io/dockerconfigjson'
706+
)
693707

694708
# apply image pull secret to a Pod spec
695709
data['imagePullSecrets'] = [{'name': secret_name}]
@@ -821,7 +835,10 @@ def create_namespace(self, namespace):
821835
"kind": "Namespace",
822836
"apiVersion": "v1",
823837
"metadata": {
824-
"name": namespace
838+
"name": namespace,
839+
"labels": {
840+
'heritage': 'deis'
841+
}
825842
}
826843
}
827844

rootfs/scheduler/mock.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,16 @@ def __init__(self):
707707
}
708708
self.create_secret('deis', 'objectstorage-keyfile', secrets)
709709

710+
try:
711+
self.get_secret('deis', 'registry-secret')
712+
except KubeHTTPException:
713+
secrets = {
714+
'username': 'test',
715+
'password': 'test',
716+
'hostname': ''
717+
}
718+
self.create_secret('deis', 'registry-secret', secrets)
719+
710720
try:
711721
self.get_namespace('duplicate')
712722
except KubeHTTPException:

rootfs/scheduler/tests/test_scheduler.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"""
66
from django.core.cache import cache
77
from django.test import TestCase
8+
from django.conf import settings
89

910
from scheduler import mock
1011
import base64
@@ -75,18 +76,47 @@ def test_get_private_registry_config(self):
7576
encAuth = base64.b64encode(auth).decode(encoding='UTF-8')
7677
image = 'test/test'
7778

78-
dockerConfig = self.scheduler_client._get_private_registry_config(registry, image)
79-
dockerConfig = json.loads(dockerConfig)
79+
docker_config, secret_name, secret_create = self.scheduler_client._get_private_registry_config(registry, image) # noqa
80+
dockerConfig = json.loads(docker_config)
8081
expected = {"https://index.docker.io/v1/": {
8182
"auth": encAuth
8283
}}
8384
self.assertEqual(dockerConfig.get('auths'), expected)
85+
self.assertEqual(secret_name, "private-registry")
86+
self.assertEqual(secret_create, True)
8487

8588
image = "quay.io/test/test"
8689

87-
dockerConfig = self.scheduler_client._get_private_registry_config(registry, image)
88-
dockerConfig = json.loads(dockerConfig)
90+
docker_config, secret_name, secret_create = self.scheduler_client._get_private_registry_config(registry, image) # noqa
91+
dockerConfig = json.loads(docker_config)
8992
expected = {"quay.io": {
9093
"auth": encAuth
9194
}}
9295
self.assertEqual(dockerConfig.get('auths'), expected)
96+
self.assertEqual(secret_name, "private-registry")
97+
self.assertEqual(secret_create, True)
98+
99+
settings.REGISTRY_LOCATION = "ecr"
100+
registry = {}
101+
image = "test.com/test/test"
102+
docker_config, secret_name, secret_create = self.scheduler_client._get_private_registry_config(registry, image) # noqa
103+
self.assertEqual(docker_config, None)
104+
self.assertEqual(secret_name, "private-registry-ecr")
105+
self.assertEqual(secret_create, False)
106+
107+
settings.REGISTRY_LOCATION = "off-cluster"
108+
docker_config, secret_name, secret_create = self.scheduler_client._get_private_registry_config(registry, image) # noqa
109+
dockerConfig = json.loads(docker_config)
110+
expected = {"https://index.docker.io/v1/": {
111+
"auth": encAuth
112+
}}
113+
self.assertEqual(dockerConfig.get('auths'), expected)
114+
self.assertEqual(secret_name, "private-registry-off-cluster")
115+
self.assertEqual(secret_create, True)
116+
117+
settings.REGISTRY_LOCATION = "ecra"
118+
image = "test.com/test/test"
119+
docker_config, secret_name, secret_create = self.scheduler_client._get_private_registry_config(registry, image) # noqa
120+
self.assertEqual(docker_config, None)
121+
self.assertEqual(secret_name, None)
122+
self.assertEqual(secret_create, None)

0 commit comments

Comments
 (0)