Further fixes for unicode handling in manifests

We were occasionally trying to compute schema 2 version 1 signatures on the *unicode* representation, which was failing the signature check. This PR adds a new wrapper type called `Bytes`, which all manifests must take in, and which handles the unicodes vs encoded utf-8 stuff in a central location. This PR also adds a test for the manifest that was breaking in production.
This commit is contained in:
Joseph Schorr 2019-01-08 20:49:00 -05:00
parent 05fa2bcbe0
commit 171c7e5238
28 changed files with 275 additions and 106 deletions

View file

@ -12,6 +12,7 @@ from image.docker.schema2.list import DOCKER_SCHEMA2_MANIFESTLIST_CONTENT_TYPE
from image.docker.schemas import parse_manifest_from_bytes
from test.registry.protocols import (RegistryProtocol, Failures, ProtocolOptions, PushResult,
PullResult)
from util.bytes import Bytes
@unique
@ -168,7 +169,8 @@ class V2Protocol(RegistryProtocol):
# Parse the returned manifest list and ensure it matches.
assert response.headers['Content-Type'] == DOCKER_SCHEMA2_MANIFESTLIST_CONTENT_TYPE
retrieved = parse_manifest_from_bytes(response.text, response.headers['Content-Type'])
retrieved = parse_manifest_from_bytes(Bytes.for_string_or_unicode(response.text),
response.headers['Content-Type'])
assert retrieved.schema_version == 2
assert retrieved.is_manifest_list
assert retrieved.digest == manifestlist.digest
@ -184,7 +186,8 @@ class V2Protocol(RegistryProtocol):
if expected_failure is not None:
return None
manifest = parse_manifest_from_bytes(response.text, response.headers['Content-Type'])
manifest = parse_manifest_from_bytes(Bytes.for_string_or_unicode(response.text),
response.headers['Content-Type'])
assert not manifest.is_manifest_list
assert manifest.digest == manifest_digest
@ -221,7 +224,7 @@ class V2Protocol(RegistryProtocol):
self.conduct(session, 'PUT',
'/v2/%s/manifests/%s' % (self.repo_name(namespace, repo_name), manifest.digest),
data=manifest.bytes,
data=manifest.bytes.as_encoded_str(),
expected_status=(202, expected_failure, V2ProtocolSteps.PUT_MANIFEST),
headers=manifest_headers)
@ -235,7 +238,7 @@ class V2Protocol(RegistryProtocol):
self.conduct(session, 'PUT',
'/v2/%s/manifests/%s' % (self.repo_name(namespace, repo_name), tag_name),
data=manifestlist.bytes,
data=manifestlist.bytes.as_encoded_str(),
expected_status=(202, expected_failure, V2ProtocolSteps.PUT_MANIFEST_LIST),
headers=manifest_headers)
@ -282,10 +285,10 @@ class V2Protocol(RegistryProtocol):
config['config'] = images[-1].config
config_json = json.dumps(config, ensure_ascii=options.ensure_ascii)
schema2_config = DockerSchema2Config(config_json)
schema2_config = DockerSchema2Config(Bytes.for_string_or_unicode(config_json))
builder.set_config(schema2_config)
blobs[schema2_config.digest] = schema2_config.bytes.encode('utf-8')
blobs[schema2_config.digest] = schema2_config.bytes.as_encoded_str()
return builder.build(ensure_ascii=options.ensure_ascii)
def build_schema1(self, namespace, repo_name, tag_name, images, blobs, options):
@ -372,7 +375,7 @@ class V2Protocol(RegistryProtocol):
tag_or_digest = tag_name if not options.push_by_manifest_digest else manifest.digest
self.conduct(session, 'PUT',
'/v2/%s/manifests/%s' % (self.repo_name(namespace, repo_name), tag_or_digest),
data=manifest.bytes.encode('utf-8'),
data=manifest.bytes.as_encoded_str(),
expected_status=(put_code, expected_failure, V2ProtocolSteps.PUT_MANIFEST),
headers=manifest_headers)
@ -546,7 +549,8 @@ class V2Protocol(RegistryProtocol):
if not self.schema2:
assert response.headers['Content-Type'] in DOCKER_SCHEMA1_CONTENT_TYPES
manifest = parse_manifest_from_bytes(response.text, response.headers['Content-Type'])
manifest = parse_manifest_from_bytes(Bytes.for_string_or_unicode(response.text),
response.headers['Content-Type'])
manifests[tag_name] = manifest
if manifest.schema_version == 1: