import tempfile 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_layer_path(self, storage_uuid): base_path = self.image_path(storage_uuid) return '{0}layer'.format(base_path) def image_ancestry_path(self, storage_uuid): base_path = self.image_path(storage_uuid) return '{0}ancestry'.format(base_path) 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, client): """ Called to perform any custom storage system validation. The client is an HTTP client to use for any external calls. """ 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 def copy_to(self, destination, path): raise NotImplementedError class DigestInvalidException(RuntimeError): pass class BaseStorageV2(BaseStorage): def initiate_chunked_upload(self): """ Start a new chunked upload, and return a handle with which the upload can be referenced. """ raise NotImplementedError def stream_upload_chunk(self, uuid, offset, length, in_fp): """ Upload the specified amount of data from the given file pointer to the chunked destination specified, starting at the given offset. Returns the number of bytes written. """ raise NotImplementedError def complete_chunked_upload(self, uuid, final_path, digest_to_verify): """ Complete the chunked upload and store the final results in the path indicated. """ raise NotImplementedError