Fix garbage collection when manifests may reference tags
This commit is contained in:
parent
bb934e04af
commit
7b53797677
2 changed files with 39 additions and 10 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from data.model import (image, db_transaction, DataModelException, _basequery,
|
from data.model import (image, db_transaction, DataModelException, _basequery,
|
||||||
|
@ -6,6 +8,9 @@ from data.database import (RepositoryTag, Repository, Image, ImageStorage, Names
|
||||||
RepositoryNotification, get_epoch_timestamp, db_for_update)
|
RepositoryNotification, get_epoch_timestamp, db_for_update)
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _tag_alive(query, now_ts=None):
|
def _tag_alive(query, now_ts=None):
|
||||||
if now_ts is None:
|
if now_ts is None:
|
||||||
now_ts = get_epoch_timestamp()
|
now_ts = get_epoch_timestamp()
|
||||||
|
@ -19,11 +24,11 @@ def get_matching_tags(docker_image_id, storage_uuid, *args):
|
||||||
image_query = image.get_repository_image_and_deriving(docker_image_id, storage_uuid)
|
image_query = image.get_repository_image_and_deriving(docker_image_id, storage_uuid)
|
||||||
|
|
||||||
return _tag_alive(RepositoryTag
|
return _tag_alive(RepositoryTag
|
||||||
.select(*args)
|
.select(*args)
|
||||||
.distinct()
|
.distinct()
|
||||||
.join(Image)
|
.join(Image)
|
||||||
.join(ImageStorage)
|
.join(ImageStorage)
|
||||||
.where(Image.id << image_query, RepositoryTag.hidden == False))
|
.where(Image.id << image_query, RepositoryTag.hidden == False))
|
||||||
|
|
||||||
|
|
||||||
def get_tags_for_image(image_id, *args):
|
def get_tags_for_image(image_id, *args):
|
||||||
|
@ -135,12 +140,25 @@ def garbage_collect_tags(repo):
|
||||||
~(RepositoryTag.lifetime_end_ts >> None),
|
~(RepositoryTag.lifetime_end_ts >> None),
|
||||||
(RepositoryTag.lifetime_end_ts <= expired_time))
|
(RepositoryTag.lifetime_end_ts <= expired_time))
|
||||||
.order_by(RepositoryTag.id))
|
.order_by(RepositoryTag.id))
|
||||||
if len(tags_to_delete) > 0:
|
|
||||||
(RepositoryTag
|
|
||||||
.delete()
|
|
||||||
.where(RepositoryTag.id << tags_to_delete)
|
|
||||||
.execute())
|
|
||||||
|
|
||||||
|
if len(tags_to_delete) > 0:
|
||||||
|
with db_transaction():
|
||||||
|
manifests_to_delete = (TagManifest
|
||||||
|
.select(TagManifest.id)
|
||||||
|
.join(RepositoryTag)
|
||||||
|
.where(RepositoryTag.id << tags_to_delete))
|
||||||
|
|
||||||
|
num_deleted_manifests = (TagManifest
|
||||||
|
.delete()
|
||||||
|
.where(TagManifest.id << manifests_to_delete)
|
||||||
|
.execute())
|
||||||
|
|
||||||
|
num_deleted_tags = (RepositoryTag
|
||||||
|
.delete()
|
||||||
|
.where(RepositoryTag.id << tags_to_delete)
|
||||||
|
.execute())
|
||||||
|
|
||||||
|
logger.debug('Removed %s tags with %s manifests', num_deleted_tags, num_deleted_manifests)
|
||||||
|
|
||||||
def get_tag_image(namespace_name, repository_name, tag_name):
|
def get_tag_image(namespace_name, repository_name, tag_name):
|
||||||
def limit_to_tag(query):
|
def limit_to_tag(query):
|
||||||
|
|
|
@ -4,6 +4,8 @@ import time
|
||||||
from app import app, storage
|
from app import app, storage
|
||||||
from initdb import setup_database_for_testing, finished_database_for_testing
|
from initdb import setup_database_for_testing, finished_database_for_testing
|
||||||
from data import model, database
|
from data import model, database
|
||||||
|
from endpoints.v2.manifest import _generate_and_store_manifest
|
||||||
|
|
||||||
|
|
||||||
ADMIN_ACCESS_USER = 'devtable'
|
ADMIN_ACCESS_USER = 'devtable'
|
||||||
PUBLIC_USER = 'public'
|
PUBLIC_USER = 'public'
|
||||||
|
@ -288,6 +290,15 @@ class TestGarbageCollection(unittest.TestCase):
|
||||||
self.assertDeleted(repository, 'i2', 'i3')
|
self.assertDeleted(repository, 'i2', 'i3')
|
||||||
self.assertNotDeleted(repository, 'i1', 'f1')
|
self.assertNotDeleted(repository, 'i1', 'f1')
|
||||||
|
|
||||||
|
def test_manifest_gc(self):
|
||||||
|
repository = self.createRepository(latest=['i1', 'i2', 'i3'], other=['i1', 'f1'])
|
||||||
|
_generate_and_store_manifest(ADMIN_ACCESS_USER, REPO, 'latest')
|
||||||
|
|
||||||
|
self._set_tag_expiration_policy(repository.namespace_user.username, 0)
|
||||||
|
|
||||||
|
self.deleteTag(repository, 'latest')
|
||||||
|
self.assertDeleted(repository, 'i2', 'i3')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Reference in a new issue