import logging import contextlib import tempfile from app import app __all__ = ['load'] logger = logging.getLogger(__name__) class Storage(object): """Storage is organized as follow: $ROOT/images//json $ROOT/images//layer $ROOT/repositories/// """ # Useful if we want to change those locations later without rewriting # the code which uses Storage repositories = 'repositories' images = 'images' # Set the IO buffer to 64kB buffer_size = 64 * 1024 #FIXME(samalba): Move all path resolver in each module (out of the base) def images_list_path(self, namespace, repository): return '{0}/{1}/{2}/_images_list'.format(self.repositories, namespace, repository) def image_json_path(self, namespace, repository, image_id): return '{0}/{1}/{2}/{3}/json'.format(self.images, namespace, repository, image_id) def image_mark_path(self, namespace, repository, image_id): return '{0}/{1}/{2}/{3}/_inprogress'.format(self.images, namespace, repository, image_id) def image_checksum_path(self, namespace, repository, image_id): return '{0}/{1}/{2}/{3}/_checksum'.format(self.images, namespace, repository, image_id) def image_layer_path(self, namespace, repository, image_id): return '{0}/{1}/{2}/{3}/layer'.format(self.images, namespace, repository, image_id) def image_ancestry_path(self, namespace, repository, image_id): return '{0}/{1}/{2}/{3}/ancestry'.format(self.images, namespace, repository, image_id) def tag_path(self, namespace, repository, tagname=None): if not tagname: return '{0}/{1}/{2}'.format(self.repositories, namespace, repository) return '{0}/{1}/{2}/tag_{3}'.format(self.repositories, namespace, repository, tagname) def index_images_path(self, namespace, repository): return '{0}/{1}/{2}/_index_images'.format(self.repositories, namespace, repository) def get_content(self, path): raise NotImplementedError def put_content(self, path, content): raise NotImplementedError def stream_read(self, path): raise NotImplementedError def stream_write(self, path, fp): raise NotImplementedError def list_directory(self, path=None): raise NotImplementedError def exists(self, path): raise NotImplementedError def remove(self, path): raise NotImplementedError def get_size(self, path): raise NotImplementedError @contextlib.contextmanager def store_stream(stream): """Stores the entire stream to a temporary file.""" tmpf = tempfile.TemporaryFile() while True: try: buf = stream.read(4096) if not buf: break tmpf.write(buf) except IOError: break tmpf.seek(0) yield tmpf tmpf.close() def temp_store_handler(): tmpf = tempfile.TemporaryFile() def fn(buf): try: tmpf.write(buf) except IOError: pass return tmpf, fn from local import LocalStorage from s3 import S3Storage _storage = {} def load(kind=None): """Returns the right storage class according to the configuration.""" global _storage # TODO hard code to local for now kind = app.config['STORAGE_KIND'] # if not kind: # kind = cfg.storage.lower() if kind in _storage: return _storage[kind] if kind == 's3': logger.debug('Using s3 storage.') store = S3Storage('', app.config['AWS_ACCESS_KEY'], app.config['AWS_SECRET_KEY'], app.config['REGISTRY_S3_BUCKET']) elif kind == 'local': logger.debug('Using local storage.') store = LocalStorage(app.config['LOCAL_STORAGE_DIR']) else: raise ValueError('Not supported storage \'{0}\''.format(kind)) _storage[kind] = store return store