Discover a versioned container image tag
This change implements a discover_image_tag method to discover the versioned tag for a container image. The current approach is to expect a label to be set on the image which contains the expected tag value. For example the RDO image building pipeline sets a label rdo_version whose value also matches a tag. This change will be used by the python-tripleoclient change which actually implements the command "openstack overcloud container image tag discover". Change-Id: I27ea031287604d70032fb5392aecbce313d4b096 Partial-Bug: #1708967
This commit is contained in:
parent
c58ac826df
commit
39f5716c04
@ -15,6 +15,7 @@
|
||||
|
||||
|
||||
import abc
|
||||
import json
|
||||
import logging
|
||||
import netifaces
|
||||
import six
|
||||
@ -38,6 +39,11 @@ class ImageUploadManager(BaseImageManager):
|
||||
def __init__(self, config_files, verbose=False, debug=False):
|
||||
super(ImageUploadManager, self).__init__(config_files)
|
||||
|
||||
def discover_image_tag(self, image, tag_from_label=None):
|
||||
uploader = ImageUploader.get_uploader('docker')
|
||||
return uploader.discover_image_tag(
|
||||
image, tag_from_label=tag_from_label)
|
||||
|
||||
def upload(self):
|
||||
"""Start the upload process"""
|
||||
|
||||
@ -89,6 +95,11 @@ class ImageUploader(object):
|
||||
"""Upload a disk image"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def discover_image_tag(self, image, tag_from_label=None):
|
||||
"""Discover a versioned tag for an image"""
|
||||
pass
|
||||
|
||||
|
||||
class DockerImageUploader(ImageUploader):
|
||||
"""Upload images using docker push"""
|
||||
@ -108,9 +119,7 @@ class DockerImageUploader(ImageUploader):
|
||||
else:
|
||||
repo = image
|
||||
|
||||
response = [line for line in dockerc.pull(repo,
|
||||
tag=tag, stream=True)]
|
||||
self.logger.debug(response)
|
||||
self._pull(dockerc, repo, tag=tag)
|
||||
|
||||
full_image = repo + ':' + tag
|
||||
new_repo = push_destination + '/' + repo.partition('/')[2]
|
||||
@ -123,3 +132,44 @@ class DockerImageUploader(ImageUploader):
|
||||
self.logger.debug(response)
|
||||
|
||||
self.logger.info('Completed upload for docker image %s' % image_name)
|
||||
|
||||
def _pull(self, dockerc, image, tag=None):
|
||||
self.logger.debug('Pulling %s' % image)
|
||||
for line in dockerc.pull(image, tag=tag, stream=True):
|
||||
status = json.loads(line)
|
||||
if 'error' in status:
|
||||
raise ImageUploaderException('Could not pull image: %s' %
|
||||
status['error'])
|
||||
self.logger.debug(status.get('status'))
|
||||
|
||||
def discover_image_tag(self, image, tag_from_label=None):
|
||||
dockerc = Client(base_url='unix://var/run/docker.sock', version='auto')
|
||||
|
||||
image_name, colon, tag = image.partition(':')
|
||||
if not tag:
|
||||
tag = 'latest'
|
||||
image = '%s:%s' % (image_name, tag)
|
||||
|
||||
self._pull(dockerc, image)
|
||||
i = dockerc.inspect_image(image)
|
||||
labels = i['Config']['Labels']
|
||||
|
||||
label_keys = ', '.join(labels.keys())
|
||||
|
||||
if not tag_from_label:
|
||||
raise ImageUploaderException(
|
||||
'No label specified. Available labels: %s' % label_keys
|
||||
)
|
||||
|
||||
tag_label = labels.get(tag_from_label)
|
||||
if tag_label is None:
|
||||
raise ImageUploaderException(
|
||||
'Image %s has no label %s. Available labels: %s' %
|
||||
(image, tag_from_label, label_keys)
|
||||
)
|
||||
|
||||
# confirm the tag exists by pulling it, which should be fast
|
||||
# because that image has just been pulled
|
||||
versioned_image = '%s:%s' % (image_name, tag_label)
|
||||
self._pull(dockerc, versioned_image)
|
||||
return tag_label
|
||||
|
@ -173,3 +173,87 @@ class TestDockerImageUploader(base.TestCase):
|
||||
self.dockermock.return_value.push(
|
||||
push_destination + '/' + image,
|
||||
tag=expected_tag, stream=True)
|
||||
|
||||
def test_discover_image_tag(self):
|
||||
image = 'docker.io/tripleoupstream/heat-docker-agents-centos:latest'
|
||||
vimage = 'docker.io/tripleoupstream/heat-docker-agents-centos:1.2.3'
|
||||
|
||||
dockerc = self.dockermock.return_value
|
||||
dockerc.pull.return_value = ['{"status": "done"}']
|
||||
dockerc.inspect_image.return_value = {
|
||||
'Config': {'Labels': {'image-version': '1.2.3'}}
|
||||
}
|
||||
result = self.uploader.discover_image_tag(image, 'image-version')
|
||||
self.assertEqual('1.2.3', result)
|
||||
|
||||
self.dockermock.assert_called_once_with(
|
||||
base_url='unix://var/run/docker.sock', version='auto')
|
||||
|
||||
dockerc.pull.assert_has_calls([
|
||||
mock.call(image, tag=None, stream=True),
|
||||
mock.call(vimage, tag=None, stream=True),
|
||||
])
|
||||
|
||||
def test_discover_image_tag_no_latest(self):
|
||||
image = 'docker.io/tripleoupstream/heat-docker-agents-centos'
|
||||
limage = image + ':latest'
|
||||
vimage = image + ':1.2.3'
|
||||
|
||||
dockerc = self.dockermock.return_value
|
||||
dockerc.pull.return_value = ['{"status": "done"}']
|
||||
dockerc.inspect_image.return_value = {
|
||||
'Config': {'Labels': {'image-version': '1.2.3'}}
|
||||
}
|
||||
result = self.uploader.discover_image_tag(image, 'image-version')
|
||||
self.assertEqual('1.2.3', result)
|
||||
|
||||
dockerc.pull.assert_has_calls([
|
||||
mock.call(limage, tag=None, stream=True),
|
||||
mock.call(vimage, tag=None, stream=True),
|
||||
])
|
||||
|
||||
def test_discover_image_tag_no_tag_from_image(self):
|
||||
image = 'docker.io/tripleoupstream/heat-docker-agents-centos:latest'
|
||||
|
||||
dockerc = self.dockermock.return_value
|
||||
dockerc.pull.return_value = ['{"status": "done"}']
|
||||
dockerc.inspect_image.return_value = {
|
||||
'Config': {'Labels': {'image-version': '1.2.3'}}
|
||||
}
|
||||
self.assertRaises(ImageUploaderException,
|
||||
self.uploader.discover_image_tag, image)
|
||||
|
||||
def test_discover_image_tag_missing_label(self):
|
||||
image = 'docker.io/tripleoupstream/heat-docker-agents-centos:latest'
|
||||
|
||||
dockerc = self.dockermock.return_value
|
||||
dockerc.pull.return_value = ['{"status": "done"}']
|
||||
dockerc.inspect_image.return_value = {
|
||||
'Config': {'Labels': {'image-version': '1.2.3'}}
|
||||
}
|
||||
self.assertRaises(ImageUploaderException,
|
||||
self.uploader.discover_image_tag, image, 'foo')
|
||||
|
||||
def test_discover_image_tag_missing_tag(self):
|
||||
image = 'docker.io/tripleoupstream/heat-docker-agents-centos:latest'
|
||||
vimage = 'docker.io/tripleoupstream/heat-docker-agents-centos:1.2.3'
|
||||
|
||||
dockerc = self.dockermock.return_value
|
||||
dockerc.pull.side_effect = [
|
||||
['{"status": "done"}'], # First pull, :latest
|
||||
['{"error": "ouch"}'], # Second pull, :1.2.3
|
||||
]
|
||||
dockerc.inspect_image.return_value = {
|
||||
'Config': {'Labels': {'image-version': '1.2.3'}}
|
||||
}
|
||||
self.assertRaises(ImageUploaderException,
|
||||
self.uploader.discover_image_tag, image,
|
||||
'image-version')
|
||||
|
||||
self.dockermock.assert_called_once_with(
|
||||
base_url='unix://var/run/docker.sock', version='auto')
|
||||
|
||||
dockerc.pull.assert_has_calls([
|
||||
mock.call(image, tag=None, stream=True),
|
||||
mock.call(vimage, tag=None, stream=True),
|
||||
])
|
||||
|
Loading…
Reference in New Issue
Block a user