From ef68982728a670652d6d95736c90c275064d51ec Mon Sep 17 00:00:00 2001 From: yackob03 Date: Thu, 6 Feb 2014 14:13:35 -0500 Subject: [PATCH] Garbage collect repositories on push and on tag deletion. --- data/model.py | 33 +++++++++++++++++++++++++++++++++ endpoints/index.py | 5 ++++- endpoints/tags.py | 16 +--------------- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/data/model.py b/data/model.py index c047e1602..336fc1f32 100644 --- a/data/model.py +++ b/data/model.py @@ -973,6 +973,39 @@ def delete_tag_and_images(namespace_name, repository_name, tag_name): store.remove(repository_path) +def garbage_collect_repository(namespace_name, repository_name): + # Get a list of all images used by tags in the repository + tag_query = (RepositoryTag + .select(RepositoryTag, Image) + .join(Image) + .switch(RepositoryTag) + .join(Repository) + .where(Repository.name == repository_name, + Repository.namespace == namespace_name)) + + referenced_anscestors = set() + for tag in tag_query: + ancestor_list = [int(img) for img in tag.image.ancestors.split('/')[1:-1]] + referenced_anscestors = referenced_anscestors.union(set(ancestor_list)) + referenced_anscestors.add(tag.image.id) + + all_repo_images = get_repository_images(namespace_name, repository_name) + all_images = {int(img.id):img for img in all_repo_images} + to_remove = set(all_images.keys()).difference(referenced_anscestors) + + logger.info('Cleaning up unreferenced images: %s', to_remove) + + for image_id_to_remove in to_remove: + image_to_remove = all_images[image_id_to_remove] + image_path = store.image_path(namespace_name, repository_name, + image_to_remove.docker_image_id) + logger.debug('Recursively deleting image path: %s' % image_path) + image_to_remove.delete_instance() + store.remove(image_path) + + return len(to_remove) + + def get_tag_image(namespace_name, repository_name, tag_name): joined = Image.select().join(RepositoryTag).join(Repository) fetched = list(joined.where(Repository.name == repository_name, diff --git a/endpoints/index.py b/endpoints/index.py index 998a0f94d..41064d805 100644 --- a/endpoints/index.py +++ b/endpoints/index.py @@ -222,6 +222,8 @@ def update_images(namespace, repository): updated_tags[image['Tag']] = image['id'] model.set_image_checksum(image['id'], repo, image['checksum']) + num_removed = model.garbage_collect_repository(namespace, repository) + # Generate a job for each webhook that has been added to this repo webhooks = model.list_webhooks(namespace, repository) for webhook in webhooks: @@ -237,7 +239,8 @@ def update_images(namespace, repository): 'homepage': 'https://quay.io/repository/%s' % repo_string, 'visibility': repo.visibility.name, 'updated_tags': updated_tags, - 'pushed_image_count': len(image_with_checksums), + 'pushed_image_count': len(image_with_checksums), + 'pruned_image_count': num_removed, } webhook_queue.put(json.dumps(webhook_data)) diff --git a/endpoints/tags.py b/endpoints/tags.py index f6b0e1163..cbfefda9f 100644 --- a/endpoints/tags.py +++ b/endpoints/tags.py @@ -73,21 +73,7 @@ def delete_tag(namespace, repository, tag): if permission.can(): model.delete_tag(namespace, repository, tag) - - return make_response('Deleted', 204) - - abort(403) - - -@tags.route('/repositories//tags', - methods=['DELETE']) -@process_auth -@parse_repository_name -def delete_repository_tags(namespace, repository): - permission = ModifyRepositoryPermission(namespace, repository) - - if permission.can(): - model.delete_all_repository_tags(namespace, repository) + model.garbage_collect_repository(namespace, repository) return make_response('Deleted', 204)