2014-09-16 04:18:57 +00:00
|
|
|
import logging
|
|
|
|
import json
|
|
|
|
|
2014-09-18 21:26:40 +00:00
|
|
|
from flask import redirect, Blueprint, abort, send_file
|
2014-09-16 04:18:57 +00:00
|
|
|
|
|
|
|
from app import storage as store, app
|
|
|
|
from auth.auth import process_auth
|
|
|
|
from auth.permissions import ReadRepositoryPermission
|
|
|
|
from data import model
|
2014-09-18 21:26:40 +00:00
|
|
|
from data import database
|
2014-09-16 04:18:57 +00:00
|
|
|
|
2014-09-17 02:43:19 +00:00
|
|
|
from util.queuefile import QueueFile
|
|
|
|
from util.queueprocess import QueueProcess
|
2014-09-16 15:53:54 +00:00
|
|
|
from util.gzipwrap import GzipWrap
|
|
|
|
from util.streamlayerformat import StreamLayerMerger
|
2014-09-16 04:18:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
verbs = Blueprint('verbs', __name__)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2014-09-18 21:26:40 +00:00
|
|
|
|
2014-09-17 02:43:19 +00:00
|
|
|
def _open_stream(namespace, repository, image_list):
|
|
|
|
def get_next_layer():
|
|
|
|
for current_image_id in image_list:
|
|
|
|
current_image_entry = model.get_repo_image(namespace, repository, current_image_id)
|
|
|
|
current_image_path = store.image_layer_path(current_image_entry.storage.uuid)
|
|
|
|
current_image_stream = store.stream_read_file(current_image_entry.storage.locations,
|
|
|
|
current_image_path)
|
|
|
|
|
|
|
|
logger.debug('Returning image layer %s: %s' % (current_image_id, current_image_path))
|
|
|
|
yield current_image_stream
|
|
|
|
|
2014-09-18 21:26:40 +00:00
|
|
|
database.configure(app.config)
|
2014-09-17 02:43:19 +00:00
|
|
|
stream = GzipWrap(StreamLayerMerger(get_next_layer).get_generator())
|
|
|
|
return stream.read
|
|
|
|
|
2014-09-18 21:26:40 +00:00
|
|
|
|
|
|
|
def _write_synthetic_image_to_storage(linked_storage_uuid, linked_locations, queue_file):
|
|
|
|
image_path = store.image_layer_path(linked_storage_uuid)
|
|
|
|
store.stream_write(linked_locations, image_path, queue_file)
|
2014-09-17 02:43:19 +00:00
|
|
|
queue_file.close()
|
2014-09-16 04:18:57 +00:00
|
|
|
|
2014-09-18 21:26:40 +00:00
|
|
|
database.configure(app.config)
|
|
|
|
done_uploading = model.get_storage_by_uuid(linked_storage_uuid)
|
|
|
|
done_uploading.uploading = False
|
|
|
|
done_uploading.save()
|
|
|
|
|
|
|
|
|
2014-09-16 04:18:57 +00:00
|
|
|
@verbs.route('/<namespace>/<repository>/<tag>/squash', methods=['GET'])
|
|
|
|
@process_auth
|
2014-09-18 21:26:40 +00:00
|
|
|
def get_squashed_tag(namespace, repository, tag):
|
2014-09-16 04:18:57 +00:00
|
|
|
permission = ReadRepositoryPermission(namespace, repository)
|
|
|
|
if permission.can() or model.repository_is_public(namespace, repository):
|
|
|
|
# Lookup the requested tag.
|
|
|
|
tag_image = model.get_tag_image(namespace, repository, tag)
|
|
|
|
if not tag_image:
|
|
|
|
abort(404)
|
|
|
|
|
|
|
|
# Lookup the tag's image and storage.
|
|
|
|
repo_image = model.get_repo_image(namespace, repository, tag_image.docker_image_id)
|
|
|
|
if not repo_image:
|
|
|
|
abort(404)
|
2014-09-18 21:26:40 +00:00
|
|
|
|
|
|
|
derived = model.find_or_create_derived_storage(repo_image.storage, 'squash',
|
|
|
|
store.preferred_locations[0])
|
|
|
|
if not derived.uploading:
|
|
|
|
logger.debug('Derived image %s exists in storage', derived.uuid)
|
|
|
|
derived_layer_path = store.image_layer_path(derived.uuid)
|
|
|
|
download_url = store.get_direct_download_url(derived.locations, derived_layer_path)
|
2014-09-17 02:43:19 +00:00
|
|
|
if download_url:
|
2014-09-18 21:26:40 +00:00
|
|
|
logger.debug('Redirecting to download URL for derived image %s', derived.uuid)
|
|
|
|
return redirect(download_url)
|
2014-09-17 02:43:19 +00:00
|
|
|
|
2014-09-18 21:26:40 +00:00
|
|
|
logger.debug('Sending cached derived image %s', derived.uuid)
|
|
|
|
return send_file(store.stream_read_file(derived.locations, derived_layer_path))
|
2014-09-17 02:43:19 +00:00
|
|
|
|
2014-09-16 04:18:57 +00:00
|
|
|
# Load the ancestry for the image.
|
2014-09-18 21:26:40 +00:00
|
|
|
logger.debug('Building and returning derived image %s', derived.uuid)
|
2014-09-16 04:18:57 +00:00
|
|
|
uuid = repo_image.storage.uuid
|
|
|
|
ancestry_data = store.get_content(repo_image.storage.locations, store.image_ancestry_path(uuid))
|
|
|
|
full_image_list = json.loads(ancestry_data)
|
2014-09-18 21:26:40 +00:00
|
|
|
|
2014-09-17 02:43:19 +00:00
|
|
|
# Create a queue process to generate the data. The queue files will read from the process
|
|
|
|
# and send the results to the client and storage.
|
|
|
|
args = (namespace, repository, full_image_list)
|
|
|
|
queue_process = QueueProcess(_open_stream, 8 * 1024, 10 * 1024 * 1024, args) # 8K/10M chunk/max
|
|
|
|
|
|
|
|
client_queue_file = QueueFile(queue_process.create_queue(), 'client')
|
|
|
|
storage_queue_file = QueueFile(queue_process.create_queue(), 'storage')
|
2014-09-18 21:26:40 +00:00
|
|
|
|
2014-09-17 02:43:19 +00:00
|
|
|
# Start building.
|
|
|
|
queue_process.run()
|
|
|
|
|
|
|
|
# Start the storage saving.
|
2014-09-18 21:26:40 +00:00
|
|
|
storage_args = (derived.uuid, derived.locations, storage_queue_file)
|
2014-09-17 02:44:45 +00:00
|
|
|
QueueProcess.run_process(_write_synthetic_image_to_storage, storage_args)
|
2014-09-16 04:18:57 +00:00
|
|
|
|
2014-09-17 02:43:19 +00:00
|
|
|
# Return the client's data.
|
|
|
|
return send_file(client_queue_file)
|
2014-09-16 04:18:57 +00:00
|
|
|
|
|
|
|
abort(403)
|