Address torrent feature review comments.
This commit is contained in:
parent
8a924aae4a
commit
1ae101c917
7 changed files with 31 additions and 29 deletions
|
@ -275,7 +275,7 @@ class DefaultConfig(object):
|
||||||
}
|
}
|
||||||
|
|
||||||
# Torrent management flags
|
# Torrent management flags
|
||||||
FEATURE_BITTORRENT = True
|
FEATURE_BITTORRENT = False
|
||||||
TORRENT_PIECE_SIZE = 512 * 1024
|
TORRENT_PIECE_SIZE = 512 * 1024
|
||||||
TORRENT_ANNOUNCE_URL = 'https://localhost:6881/announce'
|
TORRENT_ANNOUNCE_URL = 'https://localhost:6881/announce'
|
||||||
TORRENT_NAMING_SALT = '3ae93fef-c30a-427e-9ba0-eea0fd710419'
|
TORRENT_NAMING_SALT = '3ae93fef-c30a-427e-9ba0-eea0fd710419'
|
||||||
|
|
|
@ -810,7 +810,7 @@ class BlobUpload(BaseModel):
|
||||||
uncompressed_byte_count = IntegerField(null=True)
|
uncompressed_byte_count = IntegerField(null=True)
|
||||||
created = DateTimeField(default=datetime.now, index=True)
|
created = DateTimeField(default=datetime.now, index=True)
|
||||||
piece_sha_state = ResumableSHA1Field(null=True, default=resumablehashlib.sha1)
|
piece_sha_state = ResumableSHA1Field(null=True, default=resumablehashlib.sha1)
|
||||||
piece_hashes = Base64BinaryField(default='')
|
piece_hashes = Base64BinaryField(null=True, default='')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
database = db
|
database = db
|
||||||
|
|
|
@ -27,7 +27,7 @@ def upgrade(tables):
|
||||||
)
|
)
|
||||||
op.create_index('torrentinfo_storage_id', 'torrentinfo', ['storage_id'], unique=False)
|
op.create_index('torrentinfo_storage_id', 'torrentinfo', ['storage_id'], unique=False)
|
||||||
op.create_index('torrentinfo_storage_id_piece_length', 'torrentinfo', ['storage_id', 'piece_length'], unique=True)
|
op.create_index('torrentinfo_storage_id_piece_length', 'torrentinfo', ['storage_id', 'piece_length'], unique=True)
|
||||||
op.add_column(u'blobupload', sa.Column('piece_hashes', UTF8LongText(), nullable=False))
|
op.add_column(u'blobupload', sa.Column('piece_hashes', UTF8LongText(), nullable=True))
|
||||||
op.add_column(u'blobupload', sa.Column('piece_sha_state', UTF8LongText(), nullable=True))
|
op.add_column(u'blobupload', sa.Column('piece_sha_state', UTF8LongText(), nullable=True))
|
||||||
### end Alembic commands ###
|
### end Alembic commands ###
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ from data.model import (DataModelException, db_transaction, _basequery, storage,
|
||||||
InvalidImageException, config)
|
InvalidImageException, config)
|
||||||
from data.database import (Image, Repository, ImageStoragePlacement, Namespace, ImageStorage,
|
from data.database import (Image, Repository, ImageStoragePlacement, Namespace, ImageStorage,
|
||||||
ImageStorageLocation, RepositoryPermission, DerivedStorageForImage,
|
ImageStorageLocation, RepositoryPermission, DerivedStorageForImage,
|
||||||
ImageStorageTransformation, db_random_func, db_for_update)
|
ImageStorageTransformation, db_random_func)
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -299,17 +299,16 @@ def set_image_metadata(docker_image_id, namespace_name, repository_name, created
|
||||||
""" Sets metadata that is specific to how a binary piece of storage fits into the layer tree.
|
""" Sets metadata that is specific to how a binary piece of storage fits into the layer tree.
|
||||||
"""
|
"""
|
||||||
with db_transaction():
|
with db_transaction():
|
||||||
query = (Image
|
try:
|
||||||
|
fetched = (Image
|
||||||
.select(Image, ImageStorage)
|
.select(Image, ImageStorage)
|
||||||
.join(Repository)
|
.join(Repository)
|
||||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||||
.switch(Image)
|
.switch(Image)
|
||||||
.join(ImageStorage)
|
.join(ImageStorage)
|
||||||
.where(Repository.name == repository_name, Namespace.username == namespace_name,
|
.where(Repository.name == repository_name, Namespace.username == namespace_name,
|
||||||
Image.docker_image_id == docker_image_id))
|
Image.docker_image_id == docker_image_id)
|
||||||
|
.get())
|
||||||
try:
|
|
||||||
fetched = db_for_update(query).get()
|
|
||||||
except Image.DoesNotExist:
|
except Image.DoesNotExist:
|
||||||
raise DataModelException('No image with specified id and repository')
|
raise DataModelException('No image with specified id and repository')
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@ from data.model import (config, db_transaction, InvalidImageException, TorrentIn
|
||||||
DataModelException, _basequery)
|
DataModelException, _basequery)
|
||||||
from data.database import (ImageStorage, Image, ImageStoragePlacement, ImageStorageLocation,
|
from data.database import (ImageStorage, Image, ImageStoragePlacement, ImageStorageLocation,
|
||||||
ImageStorageTransformation, ImageStorageSignature,
|
ImageStorageTransformation, ImageStorageSignature,
|
||||||
ImageStorageSignatureKind, Repository, Namespace, TorrentInfo,
|
ImageStorageSignatureKind, Repository, Namespace, TorrentInfo)
|
||||||
db_for_update)
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -213,17 +212,16 @@ def set_image_storage_metadata(docker_image_id, namespace_name, repository_name,
|
||||||
if image_size is None:
|
if image_size is None:
|
||||||
raise DataModelException('Empty image size field')
|
raise DataModelException('Empty image size field')
|
||||||
|
|
||||||
query = (Image
|
try:
|
||||||
|
image = (Image
|
||||||
.select(Image, ImageStorage)
|
.select(Image, ImageStorage)
|
||||||
.join(Repository)
|
.join(Repository)
|
||||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||||
.switch(Image)
|
.switch(Image)
|
||||||
.join(ImageStorage)
|
.join(ImageStorage)
|
||||||
.where(Repository.name == repository_name, Namespace.username == namespace_name,
|
.where(Repository.name == repository_name, Namespace.username == namespace_name,
|
||||||
Image.docker_image_id == docker_image_id))
|
Image.docker_image_id == docker_image_id)
|
||||||
|
.get())
|
||||||
try:
|
|
||||||
image = db_for_update(query).get()
|
|
||||||
except ImageStorage.DoesNotExist:
|
except ImageStorage.DoesNotExist:
|
||||||
raise InvalidImageException('No image with specified id and repository')
|
raise InvalidImageException('No image with specified id and repository')
|
||||||
|
|
||||||
|
@ -257,8 +255,7 @@ def get_torrent_info(blob):
|
||||||
try:
|
try:
|
||||||
return (TorrentInfo
|
return (TorrentInfo
|
||||||
.select()
|
.select()
|
||||||
.join(ImageStorage)
|
.where(blob == TorrentInfo.storage)
|
||||||
.where(blob.id == ImageStorage.id)
|
|
||||||
.get())
|
.get())
|
||||||
except TorrentInfo.DoesNotExist:
|
except TorrentInfo.DoesNotExist:
|
||||||
raise TorrentInfoDoesNotExist
|
raise TorrentInfoDoesNotExist
|
||||||
|
|
|
@ -309,6 +309,7 @@ def get_tag_torrent(namespace, repo_name, digest):
|
||||||
|
|
||||||
user = get_authenticated_user()
|
user = get_authenticated_user()
|
||||||
if user is None and not public_repo:
|
if user is None and not public_repo:
|
||||||
|
# We can not generate a private torrent cluster without a user uuid (e.g. token auth)
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -320,7 +321,7 @@ def get_tag_torrent(namespace, repo_name, digest):
|
||||||
webseed = storage.get_direct_download_url(blob.locations, path)
|
webseed = storage.get_direct_download_url(blob.locations, path)
|
||||||
if webseed is None:
|
if webseed is None:
|
||||||
# We cannot support webseeds for storages that cannot provide direct downloads.
|
# We cannot support webseeds for storages that cannot provide direct downloads.
|
||||||
abort(501)
|
abort(make_response('Storage engine does not support seeding.', 501))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
torrent_info = model.storage.get_torrent_info(blob)
|
torrent_info = model.storage.get_torrent_info(blob)
|
||||||
|
@ -331,6 +332,7 @@ def get_tag_torrent(namespace, repo_name, digest):
|
||||||
name = public_torrent_filename(blob.uuid)
|
name = public_torrent_filename(blob.uuid)
|
||||||
else:
|
else:
|
||||||
name = per_user_torrent_filename(user.uuid, blob.uuid)
|
name = per_user_torrent_filename(user.uuid, blob.uuid)
|
||||||
|
|
||||||
torrent_file = make_torrent(name, webseed, blob.image_size,
|
torrent_file = make_torrent(name, webseed, blob.image_size,
|
||||||
torrent_info.piece_length, torrent_info.pieces)
|
torrent_info.piece_length, torrent_info.pieces)
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,10 @@ def per_user_torrent_filename(user_uuid, blob_uuid):
|
||||||
|
|
||||||
|
|
||||||
class PieceHasher(object):
|
class PieceHasher(object):
|
||||||
|
""" Utility for computing torrent piece hashes as the data flows through the update
|
||||||
|
method of this class. Users should get the final value by calling final_piece_hashes
|
||||||
|
since new chunks are allocated lazily.
|
||||||
|
"""
|
||||||
def __init__(self, piece_size, starting_offset=0, starting_piece_hash_bytes='',
|
def __init__(self, piece_size, starting_offset=0, starting_piece_hash_bytes='',
|
||||||
hash_fragment_to_resume=None):
|
hash_fragment_to_resume=None):
|
||||||
if not isinstance(starting_offset, (int, long)):
|
if not isinstance(starting_offset, (int, long)):
|
||||||
|
|
Reference in a new issue