diff --git a/data/database.py b/data/database.py index cf83b50e0..13791af53 100644 --- a/data/database.py +++ b/data/database.py @@ -89,6 +89,9 @@ class Image(BaseModel): comment = CharField(null=True) repository = ForeignKeyField(Repository) + # '/' separated list of ancestory ids, e.g. /1/2/6/7/10/ + ancestors = CharField(index=True, default='/', max_length=65535) + class Meta: database = db indexes = ( diff --git a/data/model.py b/data/model.py index b3edfac08..c70013b9b 100644 --- a/data/model.py +++ b/data/model.py @@ -198,7 +198,7 @@ def set_image_checksum(image_id, repository, checksum): def set_image_metadata(image_id, namespace_name, repository_name, - created_date_str, comment): + created_date_str, comment, parent=None): joined = Image.select().join(Repository) image_list = list(joined.where(Repository.name == repository_name, Repository.namespace == namespace_name, @@ -210,6 +210,10 @@ def set_image_metadata(image_id, namespace_name, repository_name, fetched = image_list[0] fetched.created = dateutil.parser.parse(created_date_str) fetched.comment = comment + + if parent: + fetched.ancestors = '%s%s/' % (parent.ancestors, parent.id) + fetched.save() return fetched @@ -220,15 +224,6 @@ def get_repository_images(namespace_name, repository_name): Repository.namespace == namespace_name) -def get_tag_images(namespace_name, repository_name, tag_name): - joined = Image.select().join(RepositoryTag).join(Repository) - fetched = list(joined.where(Repository.name == repository_name, - Repository.namespace == namespace_name, - RepositoryTag.name == tag_name)) - - return fetched - - def list_repository_tags(namespace_name, repository_name): select = RepositoryTag.select(RepositoryTag, Image) with_repo = select.join(Repository) @@ -238,13 +233,33 @@ def list_repository_tags(namespace_name, repository_name): def get_tag_image(namespace_name, repository_name, tag_name): - fetched = get_tag_images(namespace_name, repository_name, tag_name) + joined = Image.select().join(RepositoryTag).join(Repository) + fetched = joined.where(Repository.name == repository_name, + Repository.namespace == namespace_name, + RepositoryTag.name == tag_name) + if not fetched: raise DataModelException('Unable to find image for tag.') return fetched[0] +def get_image_by_id(image_id): + return Image.get(Image.image_id == image_id) + + +def get_parent_images(image_obj): + """ Returns a list of parent Image objects in chronilogical order. """ + parents = image_obj.ancestors + parent_db_ids = parents.strip('/').split('/') + + or_clauses = [(Image.id == db_id) for db_id in parent_db_ids] + parent_images = Image.select().where(reduce(operator.or_, or_clauses)) + id_to_image = {unicode(image.id): image for image in parent_images} + + return [id_to_image[parent_id] for parent_id in parent_db_ids] + + def create_or_update_tag(namespace_name, repository_name, tag_name, tag_image_id): repo = Repository.get(Repository.name == repository_name, diff --git a/endpoints/api.py b/endpoints/api.py index 67a1079d3..7c1cadc1c 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -188,10 +188,15 @@ def role_view(repo_perm_obj): def list_tag_images(namespace, repository, tag): permission = ReadRepositoryPermission(namespace, repository) if permission.can() or model.repository_is_public(namespace, repository): - images = model.get_tag_images(namespace, repository, tag) + tag_image = model.get_tag_image(namespace, repository, tag) + parent_images = model.get_parent_images(tag_image) + + parents = list(parent_images) + parents.reverse() + all_images = [tag_image] + parents return jsonify({ - 'images': [image_view(image) for image in images] + 'images': [image_view(image) for image in all_images] }) abort(403) # Permission denied diff --git a/endpoints/registry.py b/endpoints/registry.py index e1bdc6d42..2cad43ee8 100644 --- a/endpoints/registry.py +++ b/endpoints/registry.py @@ -290,8 +290,14 @@ def put_image_json(namespace, repository, image_id): # If we reach that point, it means that this is a new image or a retry # on a failed push # save the metadata + if parent_id: + parent_obj = model.get_image_by_id(parent_id) + else: + parent_obj = None + model.set_image_metadata(image_id, namespace, repository, - data.get('created'), data.get('comment')) + data.get('created'), data.get('comment'), + parent_obj) store.put_content(mark_path, 'true') store.put_content(json_path, request.data) generate_ancestry(namespace, repository, image_id, parent_id) diff --git a/test.db b/test.db index a1177a60b..de63aaddb 100644 Binary files a/test.db and b/test.db differ