Have the layer PUT method calculate the uncompressed size in realtime, as trusting the JSON is fraught with complications
This commit is contained in:
parent
7fd3c7d31b
commit
474add0fb1
3 changed files with 48 additions and 5 deletions
|
@ -1251,7 +1251,7 @@ def set_image_size(docker_image_id, namespace_name, repository_name,
|
||||||
|
|
||||||
|
|
||||||
def set_image_metadata(docker_image_id, namespace_name, repository_name, created_date_str, comment,
|
def set_image_metadata(docker_image_id, namespace_name, repository_name, created_date_str, comment,
|
||||||
command, uncompressed_size, parent=None):
|
command, parent=None):
|
||||||
with config.app_config['DB_TRANSACTION_FACTORY'](db):
|
with config.app_config['DB_TRANSACTION_FACTORY'](db):
|
||||||
query = (Image
|
query = (Image
|
||||||
.select(Image, ImageStorage)
|
.select(Image, ImageStorage)
|
||||||
|
@ -1272,7 +1272,6 @@ def set_image_metadata(docker_image_id, namespace_name, repository_name, created
|
||||||
fetched.storage.created = dateutil.parser.parse(created_date_str).replace(tzinfo=None)
|
fetched.storage.created = dateutil.parser.parse(created_date_str).replace(tzinfo=None)
|
||||||
fetched.storage.comment = comment
|
fetched.storage.comment = comment
|
||||||
fetched.storage.command = command
|
fetched.storage.command = command
|
||||||
fetched.storage.uncompressed_size = uncompressed_size
|
|
||||||
|
|
||||||
if parent:
|
if parent:
|
||||||
fetched.ancestors = '%s%s/' % (parent.ancestors, parent.id)
|
fetched.ancestors = '%s%s/' % (parent.ancestors, parent.id)
|
||||||
|
|
|
@ -14,6 +14,7 @@ from util.http import abort, exact_abort
|
||||||
from auth.permissions import (ReadRepositoryPermission,
|
from auth.permissions import (ReadRepositoryPermission,
|
||||||
ModifyRepositoryPermission)
|
ModifyRepositoryPermission)
|
||||||
from data import model
|
from data import model
|
||||||
|
from util import gzipstream
|
||||||
|
|
||||||
|
|
||||||
registry = Blueprint('registry', __name__)
|
registry = Blueprint('registry', __name__)
|
||||||
|
@ -193,14 +194,26 @@ def put_image_layer(namespace, repository, image_id):
|
||||||
# encoding (Gunicorn)
|
# encoding (Gunicorn)
|
||||||
input_stream = request.environ['wsgi.input']
|
input_stream = request.environ['wsgi.input']
|
||||||
|
|
||||||
# compute checksums
|
# Create a socket reader to read the input stream containing the layer data.
|
||||||
csums = []
|
|
||||||
sr = SocketReader(input_stream)
|
sr = SocketReader(input_stream)
|
||||||
|
|
||||||
|
# Add a handler that store the data in storage.
|
||||||
tmp, store_hndlr = store.temp_store_handler()
|
tmp, store_hndlr = store.temp_store_handler()
|
||||||
sr.add_handler(store_hndlr)
|
sr.add_handler(store_hndlr)
|
||||||
|
|
||||||
|
# Add a handler to compute the uncompressed size of the layer.
|
||||||
|
uncompressed_size_info, size_hndlr = gzipstream.calculate_size_handler()
|
||||||
|
sr.add_handler(size_hndlr)
|
||||||
|
|
||||||
|
# Add a handler which computes the checksum.
|
||||||
h, sum_hndlr = checksums.simple_checksum_handler(json_data)
|
h, sum_hndlr = checksums.simple_checksum_handler(json_data)
|
||||||
sr.add_handler(sum_hndlr)
|
sr.add_handler(sum_hndlr)
|
||||||
|
|
||||||
|
# Stream write the data to storage.
|
||||||
store.stream_write(repo_image.storage.locations, layer_path, sr)
|
store.stream_write(repo_image.storage.locations, layer_path, sr)
|
||||||
|
|
||||||
|
# Append the computed checksum.
|
||||||
|
csums = []
|
||||||
csums.append('sha256:{0}'.format(h.hexdigest()))
|
csums.append('sha256:{0}'.format(h.hexdigest()))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -216,6 +229,12 @@ def put_image_layer(namespace, repository, image_id):
|
||||||
logger.debug('put_image_layer: Error when computing tarsum '
|
logger.debug('put_image_layer: Error when computing tarsum '
|
||||||
'{0}'.format(e))
|
'{0}'.format(e))
|
||||||
|
|
||||||
|
# Write the uncompressed image size, if any.
|
||||||
|
if uncompressed_size_info['size'] > 0:
|
||||||
|
profile.debug('Storing uncompressed layer size: %s' % uncompressed_size_info['size'])
|
||||||
|
repo_image.storage.uncompressed_size = uncompressed_size_info['size']
|
||||||
|
repo_image.storage.save()
|
||||||
|
|
||||||
if repo_image.storage.checksum is None:
|
if repo_image.storage.checksum is None:
|
||||||
# We don't have a checksum stored yet, that's fine skipping the check.
|
# We don't have a checksum stored yet, that's fine skipping the check.
|
||||||
# Not removing the mark though, image is not downloadable yet.
|
# Not removing the mark though, image is not downloadable yet.
|
||||||
|
@ -460,7 +479,7 @@ def put_image_json(namespace, repository, image_id):
|
||||||
profile.debug('Setting image metadata')
|
profile.debug('Setting image metadata')
|
||||||
model.set_image_metadata(image_id, namespace, repository,
|
model.set_image_metadata(image_id, namespace, repository,
|
||||||
data.get('created'), data.get('comment'), command,
|
data.get('created'), data.get('comment'), command,
|
||||||
data.get('Size'), parent_image)
|
parent_image)
|
||||||
|
|
||||||
profile.debug('Putting json path')
|
profile.debug('Putting json path')
|
||||||
store.put_content(repo_image.storage.locations, json_path, request.data)
|
store.put_content(repo_image.storage.locations, json_path, request.data)
|
||||||
|
|
25
util/gzipstream.py
Normal file
25
util/gzipstream.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
"""
|
||||||
|
Defines utility methods for working with gzip streams.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
# Window size for decompressing GZIP streams.
|
||||||
|
# This results in ZLIB automatically detecting the GZIP headers.
|
||||||
|
# http://stackoverflow.com/questions/3122145/zlib-error-error-3-while-decompressing-incorrect-header-check/22310760#22310760
|
||||||
|
ZLIB_GZIP_WINDOW = zlib.MAX_WBITS | 32
|
||||||
|
|
||||||
|
def calculate_size_handler():
|
||||||
|
""" Returns an object and a SocketReader handler. The handler will gunzip the data it receives,
|
||||||
|
adding the size found to the object.
|
||||||
|
"""
|
||||||
|
uncompressed_size_info = {
|
||||||
|
'size': 0
|
||||||
|
}
|
||||||
|
|
||||||
|
decompressor = zlib.decompressobj(ZLIB_GZIP_WINDOW)
|
||||||
|
|
||||||
|
def fn(buf):
|
||||||
|
uncompressed_size_info['size'] += len(decompressor.decompress(buf))
|
||||||
|
|
||||||
|
return uncompressed_size_info, fn
|
Reference in a new issue