Skip to content

Commit e3fab08

Browse files
committed
feat(images): use imagePullSecret when pulling a private image
use a k8s Secret to encode a '.docker/config.json' to completely bypass the Controller when it comes to images if a private registry is being used Handles the various possible errors coming from k8s and bubble that up through to the end user, similar to how the deis dockerclient can do. k8s adds more information on top of what docker gives so an error message bubbled up from this compared to when the Controller was handling the Docker interaction ref #639
1 parent 94323f6 commit e3fab08

4 files changed

Lines changed: 219 additions & 54 deletions

File tree

rootfs/api/models/app.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,8 @@ def _scale_pods(self, scale_types):
338338
'memory': release.config.memory,
339339
'cpu': release.config.cpu,
340340
'tags': release.config.tags,
341-
'envs': envs,
341+
'envs': release.config.values,
342+
'registry': release.config.registry,
342343
'version': "v{}".format(release.version),
343344
'replicas': replicas,
344345
'app_type': scale_type,
@@ -390,7 +391,8 @@ def deploy(self, release):
390391
'memory': release.config.memory,
391392
'cpu': release.config.cpu,
392393
'tags': release.config.tags,
393-
'envs': envs,
394+
'envs': release.config.values,
395+
'registry': release.config.registry,
394396
# only used if there is no previous RC
395397
'replicas': replicas,
396398
'version': "v{}".format(release.version),
@@ -583,6 +585,7 @@ def pod_name(size=5, chars=string.ascii_lowercase + string.digits):
583585
'cpu': release.config.cpu,
584586
'tags': release.config.tags,
585587
'envs': release.config.values,
588+
'registry': release.config.registry,
586589
'version': "v{}".format(release.version),
587590
'build_type': release.build.type,
588591
}

rootfs/api/models/release.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,19 @@ def __str__(self):
4040

4141
@property
4242
def image(self):
43-
# return image if it is already in the registry, test host and then host + port
43+
# Builder pushes to internal registry, exclude SHA based images from being returned
44+
registry = self.config.registry
45+
if (
46+
registry.get('username', None) and
47+
registry.get('password', None) and
48+
# SHA means it came from a git push (builder)
49+
not self.build.sha and
50+
# hostname tells Builder where to push images
51+
not registry.get('hostname', None)
52+
):
53+
return self.build.image
54+
55+
# return image if it is already in a registry, test host and then host + port
4456
if (
4557
self.build.image.startswith(settings.REGISTRY_HOST) or
4658
self.build.image.startswith(settings.REGISTRY_URL)
@@ -95,16 +107,29 @@ def publish(self):
95107
if self.build.source_based:
96108
return
97109

98-
source_image = self.build.image
110+
# Builder pushes to internal registry, exclude SHA based images from being returned early
111+
registry = self.config.registry
112+
if (
113+
registry.get('username', None) and
114+
registry.get('password', None) and
115+
# SHA means it came from a git push (builder)
116+
not self.build.sha and
117+
# hostname tells Builder where to push images
118+
not registry.get('hostname', None)
119+
):
120+
log_event(self.app, '{} exists in the target registry. Using image for release {} of app {}'.format(self.build.image, self.version, self.app)) # noqa
121+
return
122+
99123
# return image if it is already in the registry, test host and then host + port
100124
if (
101-
source_image.startswith(settings.REGISTRY_HOST) or
102-
source_image.startswith(settings.REGISTRY_URL)
125+
self.build.image.startswith(settings.REGISTRY_HOST) or
126+
self.build.image.startswith(settings.REGISTRY_URL)
103127
):
104-
log_event(self.app, '{} already exists in the target registry. Using this image for release {} of app {}'.format(source_image, self.version, self.app)) # noqa
128+
log_event(self.app, '{} exists in the target registry. Using image for release {} of app {}'.format(self.build.image, self.version, self.app)) # noqa
105129
return
106130

107131
# add tag if it was not provided
132+
source_image = self.build.image
108133
if ':' not in source_image:
109134
source_image = "{}:{}".format(source_image, self.build.version)
110135

rootfs/api/tests/test_config.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,36 @@ def test_registry(self, mock_requests):
665665
response = self.client.delete(url)
666666
self.assertEqual(response.status_code, 405, response.data)
667667

668+
def test_registry_deploy(self, mock_requests):
669+
"""
670+
Test that registry information can be applied
671+
"""
672+
url = '/v2/apps'
673+
response = self.client.post(url)
674+
self.assertEqual(response.status_code, 201, response.data)
675+
app_id = response.data['id']
676+
677+
# Set healthcheck URL to get defaults set
678+
body = {'registry': json.dumps({
679+
'username': 'bob',
680+
'password': 's3cur3pw1'
681+
})}
682+
resp = self.client.post(
683+
'/v2/apps/{app_id}/config'.format(**locals()),
684+
body
685+
)
686+
self.assertEqual(resp.status_code, 201, response.data)
687+
self.assertIn('username', resp.data['registry'])
688+
self.assertIn('password', resp.data['registry'])
689+
self.assertEqual(resp.data['registry']['username'], 'bob')
690+
self.assertEqual(resp.data['registry']['password'], 's3cur3pw1')
691+
692+
# post a new build
693+
url = "/v2/apps/{app_id}/builds".format(**locals())
694+
body = {'image': 'autotest/example'}
695+
response = self.client.post(url, body)
696+
self.assertEqual(response.status_code, 201, response.data)
697+
668698
def test_config_owner_is_requesting_user(self, mock_requests):
669699
"""
670700
Ensure that setting the config value is owned by the requesting user
@@ -724,6 +754,6 @@ def test_healthchecks(self, mock_requests):
724754

725755
# post a new build
726756
url = "/v2/apps/{app_id}/builds".format(**locals())
727-
body = {'image': 'autotest/example'}
757+
body = {'image': 'quay.io/autotest/example'}
728758
response = self.client.post(url, body)
729759
self.assertEqual(response.status_code, 201, response.data)

0 commit comments

Comments
 (0)