Use a consistent concept of tag liveness everywhere. Fix the tests.
This commit is contained in:
parent
90c0a9c1e0
commit
f32bd748e4
2 changed files with 58 additions and 54 deletions
|
@ -1530,15 +1530,20 @@ def get_repository_images(namespace_name, repository_name):
|
||||||
return _get_repository_images_base(namespace_name, repository_name, lambda q: q)
|
return _get_repository_images_base(namespace_name, repository_name, lambda q: q)
|
||||||
|
|
||||||
|
|
||||||
|
def _tag_alive(query):
|
||||||
|
return query.where((RepositoryTag.lifetime_end >> None) |
|
||||||
|
(RepositoryTag.lifetime_end > datetime.utcnow()))
|
||||||
|
|
||||||
|
|
||||||
def list_repository_tags(namespace_name, repository_name):
|
def list_repository_tags(namespace_name, repository_name):
|
||||||
return (RepositoryTag
|
return _tag_alive(RepositoryTag
|
||||||
.select(RepositoryTag, Image)
|
.select(RepositoryTag, Image)
|
||||||
.join(Repository)
|
.join(Repository)
|
||||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||||
.switch(RepositoryTag)
|
.switch(RepositoryTag)
|
||||||
.join(Image)
|
.join(Image)
|
||||||
.where(Repository.name == repository_name, Namespace.username == namespace_name,
|
.where(Repository.name == repository_name,
|
||||||
RepositoryTag.lifetime_end >> None))
|
Namespace.username == namespace_name))
|
||||||
|
|
||||||
|
|
||||||
def _garbage_collect_tags(namespace_name, repository_name):
|
def _garbage_collect_tags(namespace_name, repository_name):
|
||||||
|
@ -1688,7 +1693,7 @@ def _garbage_collect_storage(storage_id_whitelist):
|
||||||
|
|
||||||
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):
|
||||||
return (query
|
return _tag_alive(query
|
||||||
.switch(Image)
|
.switch(Image)
|
||||||
.join(RepositoryTag)
|
.join(RepositoryTag)
|
||||||
.where(RepositoryTag.name == tag_name))
|
.where(RepositoryTag.name == tag_name))
|
||||||
|
@ -1744,8 +1749,10 @@ def create_or_update_tag(namespace_name, repository_name, tag_name,
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# When we move a tag, we really end the timeline of the old one and create a new one
|
# When we move a tag, we really end the timeline of the old one and create a new one
|
||||||
tag = RepositoryTag.get(RepositoryTag.repository == repo, RepositoryTag.name == tag_name,
|
query = _tag_alive(RepositoryTag
|
||||||
RepositoryTag.lifetime_end >> None)
|
.select()
|
||||||
|
.where(RepositoryTag.repository == repo, RepositoryTag.name == tag_name))
|
||||||
|
tag = query.get()
|
||||||
tag.lifetime_end = now
|
tag.lifetime_end = now
|
||||||
tag.save()
|
tag.save()
|
||||||
except RepositoryTag.DoesNotExist:
|
except RepositoryTag.DoesNotExist:
|
||||||
|
@ -1760,12 +1767,13 @@ def create_or_update_tag(namespace_name, repository_name, tag_name,
|
||||||
def delete_tag(namespace_name, repository_name, tag_name):
|
def delete_tag(namespace_name, repository_name, tag_name):
|
||||||
with config.app_config['DB_TRANSACTION_FACTORY'](db):
|
with config.app_config['DB_TRANSACTION_FACTORY'](db):
|
||||||
try:
|
try:
|
||||||
query = (RepositoryTag
|
query = _tag_alive(RepositoryTag
|
||||||
.select(RepositoryTag, Repository)
|
.select(RepositoryTag, Repository)
|
||||||
.join(Repository)
|
.join(Repository)
|
||||||
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
|
||||||
.where(Repository.name == repository_name, Namespace.username == namespace_name,
|
.where(Repository.name == repository_name,
|
||||||
RepositoryTag.name == tag_name, RepositoryTag.lifetime_end >> None))
|
Namespace.username == namespace_name,
|
||||||
|
RepositoryTag.name == tag_name))
|
||||||
found = db_for_update(query).get()
|
found = db_for_update(query).get()
|
||||||
|
|
||||||
except RepositoryTag.DoesNotExist:
|
except RepositoryTag.DoesNotExist:
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import unittest
|
import unittest
|
||||||
import json as py_json
|
|
||||||
|
|
||||||
from flask import url_for
|
|
||||||
from endpoints.api import api
|
|
||||||
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
|
||||||
|
@ -14,12 +11,16 @@ REPO = 'somerepo'
|
||||||
|
|
||||||
class TestGarbageColection(unittest.TestCase):
|
class TestGarbageColection(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
self._old_tm_expiration = app.config['TIME_MACHINE_DELTA_SECONDS']
|
||||||
|
app.config['TIME_MACHINE_DELTA_SECONDS'] = 0
|
||||||
|
|
||||||
setup_database_for_testing(self)
|
setup_database_for_testing(self)
|
||||||
self.app = app.test_client()
|
self.app = app.test_client()
|
||||||
self.ctx = app.test_request_context()
|
self.ctx = app.test_request_context()
|
||||||
self.ctx.__enter__()
|
self.ctx.__enter__()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
app.config['TIME_MACHINE_DELTA_SECONDS'] = self._old_tm_expiration
|
||||||
finished_database_for_testing(self)
|
finished_database_for_testing(self)
|
||||||
self.ctx.__exit__(True, None, None)
|
self.ctx.__exit__(True, None, None)
|
||||||
|
|
||||||
|
@ -77,19 +78,21 @@ class TestGarbageColection(unittest.TestCase):
|
||||||
model.garbage_collect_repository(repository.namespace_user.username, repository.name)
|
model.garbage_collect_repository(repository.namespace_user.username, repository.name)
|
||||||
|
|
||||||
def moveTag(self, repository, tag, docker_image_id):
|
def moveTag(self, repository, tag, docker_image_id):
|
||||||
model.create_or_update_tag(repository.namespace_user.username, repository.name, tag, docker_image_id)
|
model.create_or_update_tag(repository.namespace_user.username, repository.name, tag,
|
||||||
|
docker_image_id)
|
||||||
model.garbage_collect_repository(repository.namespace_user.username, repository.name)
|
model.garbage_collect_repository(repository.namespace_user.username, repository.name)
|
||||||
|
|
||||||
def assertNotDeleted(self, repository, *args):
|
def assertNotDeleted(self, repository, *args):
|
||||||
for docker_image_id in args:
|
for docker_image_id in args:
|
||||||
self.assertTrue(bool(model.get_image_by_id(repository.namespace_user.username, repository.name, docker_image_id)))
|
self.assertTrue(bool(model.get_image_by_id(repository.namespace_user.username,
|
||||||
|
repository.name, docker_image_id)))
|
||||||
|
|
||||||
def assertDeleted(self, repository, *args):
|
def assertDeleted(self, repository, *args):
|
||||||
for docker_image_id in args:
|
for docker_image_id in args:
|
||||||
try:
|
try:
|
||||||
# Verify the image is missing when accessed by the repository.
|
# Verify the image is missing when accessed by the repository.
|
||||||
model.get_image_by_id(repository.namespace_user.username, repository.name, docker_image_id)
|
model.get_image_by_id(repository.namespace_user.username, repository.name, docker_image_id)
|
||||||
except model.DataModelException as ex:
|
except model.DataModelException:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.fail('Expected image %s to be deleted' % docker_image_id)
|
self.fail('Expected image %s to be deleted' % docker_image_id)
|
||||||
|
@ -158,8 +161,7 @@ class TestGarbageColection(unittest.TestCase):
|
||||||
""" Repository has multiple tags with shared images. Selectively deleting the tags, and
|
""" Repository has multiple tags with shared images. Selectively deleting the tags, and
|
||||||
verifying at each step.
|
verifying at each step.
|
||||||
"""
|
"""
|
||||||
repository = self.createRepository(
|
repository = self.createRepository(latest=['i1', 'i2', 'i3'],
|
||||||
latest = ['i1', 'i2', 'i3'],
|
|
||||||
other=['i1', 'f1', 'f2'],
|
other=['i1', 'f1', 'f2'],
|
||||||
third=['t1', 't2', 't3'],
|
third=['t1', 't2', 't3'],
|
||||||
fourth=['i1', 'f1'])
|
fourth=['i1', 'f1'])
|
||||||
|
@ -197,17 +199,11 @@ class TestGarbageColection(unittest.TestCase):
|
||||||
self.assertDeleted(repository, 'i1')
|
self.assertDeleted(repository, 'i1')
|
||||||
|
|
||||||
def test_empty_gc(self):
|
def test_empty_gc(self):
|
||||||
repository = self.createRepository(
|
repository = self.createRepository(latest=['i1', 'i2', 'i3'], other=['i1', 'f1', 'f2'],
|
||||||
latest = ['i1', 'i2', 'i3'],
|
third=['t1', 't2', 't3'], fourth=['i1', 'f1'])
|
||||||
other = ['i1', 'f1', 'f2'],
|
|
||||||
third = ['t1', 't2', 't3'],
|
|
||||||
fourth = ['i1', 'f1'])
|
|
||||||
|
|
||||||
self.gcNow(repository)
|
self.gcNow(repository)
|
||||||
self.assertNotDeleted(repository, 'i1', 'i2', 'i3', 't1', 't2', 't3', 'f1', 'f2')
|
self.assertNotDeleted(repository, 'i1', 'i2', 'i3', 't1', 't2', 't3', 'f1', 'f2')
|
||||||
|
|
||||||
def test_gc_storage_empty(self):
|
|
||||||
model._garbage_collect_storage(set())
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Reference in a new issue