import tempfile from digest.digest_tools import content_path class StoragePaths(object): shared_images = 'sharedimages' @staticmethod def temp_store_handler(): tmpf = tempfile.TemporaryFile() def fn(buf): try: tmpf.write(buf) except IOError: pass return tmpf, fn def image_path(self, storage_uuid): return '{0}/{1}/'.format(self.shared_images, storage_uuid) def image_json_path(self, storage_uuid): base_path = self.image_path(storage_uuid) return '{0}json'.format(base_path) def v1_image_layer_path(self, storage_uuid): base_path = self.image_path(storage_uuid) return '{0}layer'.format(base_path) def blob_path(self, digest_str): return content_path(digest_str) def image_file_trie_path(self, storage_uuid): base_path = self.image_path(storage_uuid) return '{0}files.trie'.format(base_path) def image_file_diffs_path(self, storage_uuid): base_path = self.image_path(storage_uuid) return '{0}diffs.json'.format(base_path) class BaseStorage(StoragePaths): """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 def setup(self): """ Called to perform any storage system setup. """ pass def validate(self): """ Called to perform any custom storage system validation. """ pass def get_direct_download_url(self, path, expires_in=60, requires_cors=False): return None def get_direct_upload_url(self, path, mime_type, requires_cors=True): return None def get_supports_resumable_downloads(self): return False def get_content(self, path): raise NotImplementedError def put_content(self, path, content): raise NotImplementedError def stream_read(self, path): raise NotImplementedError def stream_read_file(self, path): raise NotImplementedError def stream_write(self, path, fp, content_type=None, content_encoding=None): raise NotImplementedError def list_directory(self, path=None): raise NotImplementedError def exists(self, path): raise NotImplementedError def remove(self, path): raise NotImplementedError def get_checksum(self, path): raise NotImplementedError class InvalidChunkException(RuntimeError): pass class BaseStorageV2(BaseStorage): def initiate_chunked_upload(self, upload_uuid): """ Start a new chunked upload """ raise NotImplementedError def stream_upload_chunk(self, uuid, offset, length, in_fp, hash_obj): """ Upload the specified amount of data from the given file pointer to the chunked destination specified, starting at the given offset. Raises InvalidChunkException if the offset or length can not be accepted. """ raise NotImplementedError def complete_chunked_upload(self, uuid, final_path): """ Complete the chunked upload and store the final results in the path indicated. """ raise NotImplementedError def cancel_chunked_upload(self, uuid): """ Cancel the chunked upload and clean up any outstanding partially uploaded data. """ raise NotImplementedError