Always convert to docker media format
Previously if the container config mediaType was unset or set to
something other than a docker format, we would just pass that on. This
caused problem with stricter checking of the metadata performed by some
versions of Podman. Since we're always converting the layer metadata
information to docker, we should ensure that the config media type is
properly set to a valid docker distribution format. This change ensure
that when we push the image config, that the mediaType is set to match
the contents type.
Change-Id: I6b6a8231f6881fdb53084a437ee24fd852ebdd55
Closes-Bug: #186585
(cherry picked from commit e7d1e59283
)
This commit is contained in:
parent
467f89e3c5
commit
82ebd0680c
|
@ -82,6 +82,8 @@ MEDIA_TYPES = (
|
|||
MEDIA_MANIFEST_V1_SIGNED,
|
||||
MEDIA_MANIFEST_V2,
|
||||
MEDIA_MANIFEST_V2_LIST,
|
||||
MEDIA_OCI_MANIFEST_V1,
|
||||
MEDIA_OCI_INDEX_V1,
|
||||
MEDIA_CONFIG,
|
||||
MEDIA_BLOB,
|
||||
MEDIA_BLOB_COMPRESSED
|
||||
|
@ -90,6 +92,8 @@ MEDIA_TYPES = (
|
|||
'application/vnd.docker.distribution.manifest.v1+prettyjws',
|
||||
'application/vnd.docker.distribution.manifest.v2+json',
|
||||
'application/vnd.docker.distribution.manifest.list.v2+json',
|
||||
'application/vnd.oci.image.manifest.v1+json',
|
||||
'application/vnd.oci.image.index.v1+json',
|
||||
'application/vnd.docker.container.image.v1+json',
|
||||
'application/vnd.docker.image.rootfs.diff.tar',
|
||||
'application/vnd.docker.image.rootfs.diff.tar.gzip'
|
||||
|
@ -1753,8 +1757,16 @@ class PythonImageUploader(BaseImageUploader):
|
|||
else:
|
||||
manifest_type = MEDIA_MANIFEST_V1
|
||||
else:
|
||||
manifest_type = manifest.get(
|
||||
'mediaType', MEDIA_MANIFEST_V2)
|
||||
# NOTE(mwhahaha): always force docker media format if not set or
|
||||
# is explicitly OCI because buildah uses OCI by default but we
|
||||
# convert the metadata to Docker format in the uploader.
|
||||
# See LP#1860585
|
||||
manifest_type = manifest.get('mediaType', False)
|
||||
if not manifest_type or manifest_type == MEDIA_OCI_MANIFEST_V1:
|
||||
manifest_type = MEDIA_MANIFEST_V2
|
||||
elif manifest_type == MEDIA_OCI_INDEX_V1:
|
||||
manifest_type = MEDIA_MANIFEST_V2_LIST
|
||||
manifest['mediaType'] = manifest_type
|
||||
manifest_str = json.dumps(manifest, indent=3)
|
||||
|
||||
export = target_url.netloc in cls.export_registries
|
||||
|
|
|
@ -2034,6 +2034,172 @@ class TestPythonImageUploader(base.TestCase):
|
|||
)
|
||||
self.assertEqual(target_manifest, put_manifest)
|
||||
|
||||
@mock.patch('tripleo_common.image.image_uploader.'
|
||||
'BaseImageUploader.check_status')
|
||||
@mock.patch('tripleo_common.image.image_uploader.'
|
||||
'BaseImageUploader._build_url')
|
||||
@mock.patch('tripleo_common.image.image_uploader.'
|
||||
'BaseImageUploader._image_tag_from_url')
|
||||
def test_copy_manifest_config_to_registry(self, image_tag_mock,
|
||||
build_url_mock, status_mock):
|
||||
|
||||
target_url = urlparse('docker://192.168.2.1:5000/t/nova-api:latest')
|
||||
|
||||
image_tag_mock.return_value = ('t/nova-api', 'latest')
|
||||
|
||||
build_url = 'https://192.168.2.1:5000/v2/t/nova-api'
|
||||
build_url_mock.return_value = build_url
|
||||
target_session = mock.Mock()
|
||||
target_put = mock.Mock()
|
||||
target_put.return_value.text = '{}'
|
||||
target_session.put = target_put
|
||||
|
||||
config_str = None
|
||||
|
||||
manifest_str = json.dumps({
|
||||
'config': {
|
||||
'digest': 'sha256:1234',
|
||||
'size': 2,
|
||||
'mediaType': image_uploader.MEDIA_CONFIG
|
||||
},
|
||||
'layers': [
|
||||
{'digest': 'sha256:aaaa'},
|
||||
{'digest': 'sha256:bbbb'},
|
||||
],
|
||||
'mediaType': image_uploader.MEDIA_MANIFEST_V2
|
||||
})
|
||||
expected_manifest_str = json.dumps({
|
||||
'config': {
|
||||
'digest': 'sha256:1234',
|
||||
'size': 2,
|
||||
'mediaType': image_uploader.MEDIA_CONFIG
|
||||
},
|
||||
'layers': [
|
||||
{'digest': 'sha256:aaaa'},
|
||||
{'digest': 'sha256:bbbb'},
|
||||
],
|
||||
'mediaType': image_uploader.MEDIA_MANIFEST_V2
|
||||
}, indent=3)
|
||||
|
||||
expected_headers = {
|
||||
'Content-Type': image_uploader.MEDIA_MANIFEST_V2
|
||||
}
|
||||
self.uploader._copy_manifest_config_to_registry(
|
||||
target_url, manifest_str, config_str,
|
||||
target_session=target_session
|
||||
)
|
||||
|
||||
calls = [mock.call(build_url,
|
||||
data=expected_manifest_str.encode('utf-8'),
|
||||
headers=expected_headers,
|
||||
timeout=30)]
|
||||
target_put.assert_has_calls(calls)
|
||||
|
||||
@mock.patch('tripleo_common.image.image_export.export_manifest_config')
|
||||
def test_copy_manifest_config_to_registry_export(self, export_mock):
|
||||
|
||||
target_url = urlparse('docker://192.168.2.1:5000/t/nova-api:latest')
|
||||
self.uploader.export_registries.add('192.168.2.1:5000')
|
||||
target_session = mock.Mock()
|
||||
config_str = None
|
||||
|
||||
manifest_str = json.dumps({
|
||||
'config': {
|
||||
'digest': 'sha256:1234',
|
||||
'size': 2,
|
||||
'mediaType': image_uploader.MEDIA_CONFIG
|
||||
},
|
||||
'layers': [
|
||||
{'digest': 'sha256:aaaa'},
|
||||
{'digest': 'sha256:bbbb'},
|
||||
],
|
||||
})
|
||||
expected_manifest_str = json.dumps({
|
||||
'config': {
|
||||
'digest': 'sha256:1234',
|
||||
'size': 2,
|
||||
'mediaType': image_uploader.MEDIA_CONFIG
|
||||
},
|
||||
'layers': [
|
||||
{'digest': 'sha256:aaaa'},
|
||||
{'digest': 'sha256:bbbb'},
|
||||
],
|
||||
'mediaType': image_uploader.MEDIA_MANIFEST_V2
|
||||
}, indent=3)
|
||||
|
||||
self.uploader._copy_manifest_config_to_registry(
|
||||
target_url, manifest_str, config_str,
|
||||
target_session=target_session
|
||||
)
|
||||
|
||||
calls = [mock.call(target_url,
|
||||
expected_manifest_str,
|
||||
image_uploader.MEDIA_MANIFEST_V2,
|
||||
config_str,
|
||||
multi_arch=False)]
|
||||
export_mock.assert_has_calls(calls)
|
||||
|
||||
@mock.patch('tripleo_common.image.image_uploader.'
|
||||
'BaseImageUploader.check_status')
|
||||
@mock.patch('tripleo_common.image.image_uploader.'
|
||||
'BaseImageUploader._build_url')
|
||||
@mock.patch('tripleo_common.image.image_uploader.'
|
||||
'BaseImageUploader._image_tag_from_url')
|
||||
def test_copy_manifest_config_to_registry_oci(self, image_tag_mock,
|
||||
build_url_mock, status_mock):
|
||||
|
||||
target_url = urlparse('docker://192.168.2.1:5000/t/nova-api:latest')
|
||||
|
||||
image_tag_mock.return_value = ('t/nova-api', 'latest')
|
||||
|
||||
build_url = 'https://192.168.2.1:5000/v2/t/nova-api'
|
||||
build_url_mock.return_value = build_url
|
||||
target_session = mock.Mock()
|
||||
target_put = mock.Mock()
|
||||
target_put.return_value.text = '{}'
|
||||
target_session.put = target_put
|
||||
|
||||
config_str = None
|
||||
|
||||
manifest_str = json.dumps({
|
||||
'config': {
|
||||
'digest': 'sha256:1234',
|
||||
'size': 2,
|
||||
'mediaType': image_uploader.MEDIA_CONFIG
|
||||
},
|
||||
'layers': [
|
||||
{'digest': 'sha256:aaaa'},
|
||||
{'digest': 'sha256:bbbb'},
|
||||
],
|
||||
'mediaType': image_uploader.MEDIA_OCI_MANIFEST_V1
|
||||
})
|
||||
expected_manifest_str = json.dumps({
|
||||
'config': {
|
||||
'digest': 'sha256:1234',
|
||||
'size': 2,
|
||||
'mediaType': image_uploader.MEDIA_CONFIG
|
||||
},
|
||||
'layers': [
|
||||
{'digest': 'sha256:aaaa'},
|
||||
{'digest': 'sha256:bbbb'},
|
||||
],
|
||||
'mediaType': image_uploader.MEDIA_MANIFEST_V2
|
||||
}, indent=3)
|
||||
|
||||
expected_headers = {
|
||||
'Content-Type': image_uploader.MEDIA_MANIFEST_V2
|
||||
}
|
||||
self.uploader._copy_manifest_config_to_registry(
|
||||
target_url, manifest_str, config_str,
|
||||
target_session=target_session
|
||||
)
|
||||
|
||||
calls = [mock.call(build_url,
|
||||
data=expected_manifest_str.encode('utf-8'),
|
||||
headers=expected_headers,
|
||||
timeout=30)]
|
||||
target_put.assert_has_calls(calls)
|
||||
|
||||
@mock.patch('os.environ')
|
||||
@mock.patch('subprocess.Popen')
|
||||
def test_copy_registry_to_local(self, mock_popen, mock_environ):
|
||||
|
|
Loading…
Reference in New Issue