Add UI for viewing and changing the expiration of tags
This commit is contained in:
parent
977539bf08
commit
99d7fde8ee
13 changed files with 329 additions and 26 deletions
|
@ -0,0 +1,25 @@
|
|||
"""Add change_tag_expiration log type
|
||||
|
||||
Revision ID: d8989249f8f6
|
||||
Revises: dc4af11a5f90
|
||||
Create Date: 2017-06-21 21:18:25.948689
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd8989249f8f6'
|
||||
down_revision = 'dc4af11a5f90'
|
||||
|
||||
from alembic import op
|
||||
|
||||
def upgrade(tables):
|
||||
op.bulk_insert(tables.logentrykind, [
|
||||
{'name': 'change_tag_expiration'},
|
||||
])
|
||||
|
||||
|
||||
def downgrade(tables):
|
||||
op.execute(tables
|
||||
.logentrykind
|
||||
.delete()
|
||||
.where(tables.logentrykind.c.name == op.inline_literal('change_tag_expiration')))
|
|
@ -1,13 +1,17 @@
|
|||
import logging
|
||||
import time
|
||||
|
||||
from calendar import timegm
|
||||
from uuid import uuid4
|
||||
|
||||
from peewee import IntegrityError, JOIN_LEFT_OUTER, fn
|
||||
from data.model import (image, db_transaction, DataModelException, _basequery,
|
||||
InvalidManifestException, TagAlreadyCreatedException, StaleTagException)
|
||||
InvalidManifestException, TagAlreadyCreatedException, StaleTagException,
|
||||
config)
|
||||
from data.database import (RepositoryTag, Repository, Image, ImageStorage, Namespace, TagManifest,
|
||||
RepositoryNotification, Label, TagManifestLabel, get_epoch_timestamp,
|
||||
db_for_update)
|
||||
from util.timedeltastring import convert_to_timedelta
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -570,3 +574,41 @@ def _load_repo_manifests(namespace, repo_name):
|
|||
.join(Repository)
|
||||
.join(Namespace, on=(Namespace.id == Repository.namespace_user))
|
||||
.where(Repository.name == repo_name, Namespace.username == namespace))
|
||||
|
||||
|
||||
def change_repository_tag_expiration(namespace_name, repo_name, tag_name, expiration_date):
|
||||
""" Changes the expiration of the tag with the given name to the given expiration datetime. If
|
||||
the expiration datetime is None, then the tag is marked as not expiring.
|
||||
"""
|
||||
try:
|
||||
tag = get_active_tag(namespace_name, repo_name, tag_name)
|
||||
return change_tag_expiration(tag, expiration_date)
|
||||
except RepositoryTag.DoesNotExist:
|
||||
return (None, False)
|
||||
|
||||
|
||||
def change_tag_expiration(tag, expiration_date):
|
||||
""" Changes the expiration of the given tag to the given expiration datetime. If
|
||||
the expiration datetime is None, then the tag is marked as not expiring.
|
||||
"""
|
||||
end_ts = None
|
||||
min_expire_sec = convert_to_timedelta(config.app_config.get('LABELED_EXPIRATION_MINIMUM', '1h'))
|
||||
max_expire_sec = convert_to_timedelta(config.app_config.get('LABELED_EXPIRATION_MAXIMUM', '104w'))
|
||||
|
||||
if expiration_date is not None:
|
||||
offset = timegm(expiration_date.utctimetuple()) - tag.lifetime_start_ts
|
||||
offset = min(max(offset, min_expire_sec.total_seconds()), max_expire_sec.total_seconds())
|
||||
end_ts = tag.lifetime_start_ts + offset
|
||||
|
||||
if end_ts == tag.lifetime_end_ts:
|
||||
return (None, True)
|
||||
|
||||
# Note: We check not just the ID of the tag but also its lifetime_end_ts, to ensure that it has
|
||||
# not changed while we were updatings it expiration.
|
||||
result = (RepositoryTag
|
||||
.update(lifetime_end_ts=end_ts)
|
||||
.where(RepositoryTag.id == tag.id,
|
||||
RepositoryTag.lifetime_end_ts == tag.lifetime_end_ts)
|
||||
.execute())
|
||||
|
||||
return (tag.lifetime_end_ts, result > 0)
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import pytest
|
||||
|
||||
from datetime import datetime
|
||||
from mock import patch
|
||||
from time import time
|
||||
|
||||
from data.database import Image, RepositoryTag, ImageStorage, Repository
|
||||
from data.model.repository import create_repository
|
||||
from data.model.tag import (list_active_repo_tags, create_or_update_tag, delete_tag,
|
||||
get_matching_tags, _tag_alive, get_matching_tags_for_images)
|
||||
get_matching_tags, _tag_alive, get_matching_tags_for_images,
|
||||
change_tag_expiration, get_active_tag)
|
||||
from data.model.image import find_create_or_link_image
|
||||
from util.timedeltastring import convert_to_timedelta
|
||||
|
||||
from test.fixtures import *
|
||||
|
||||
|
@ -177,3 +180,34 @@ def test_list_active_tags(initialized_db):
|
|||
# "Move" foo by updating it and make sure we don't get duplicates.
|
||||
create_or_update_tag('devtable', 'somenewrepo', 'foo', image2.docker_image_id)
|
||||
assert_tags(repository, 'foo', 'bar')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('expiration_offset, expected_offset', [
|
||||
(None, None),
|
||||
('0s', '1h'),
|
||||
('30m', '1h'),
|
||||
('2h', '2h'),
|
||||
('2w', '2w'),
|
||||
('200w', '104w'),
|
||||
])
|
||||
def test_change_tag_expiration(expiration_offset, expected_offset, initialized_db):
|
||||
repository = create_repository('devtable', 'somenewrepo', None)
|
||||
image1 = find_create_or_link_image('foobarimage1', repository, None, {}, 'local_us')
|
||||
footag = create_or_update_tag('devtable', 'somenewrepo', 'foo', image1.docker_image_id)
|
||||
|
||||
expiration_date = None
|
||||
if expiration_offset is not None:
|
||||
expiration_date = datetime.utcnow() + convert_to_timedelta(expiration_offset)
|
||||
|
||||
assert change_tag_expiration(footag, expiration_date)
|
||||
|
||||
# Lookup the tag again.
|
||||
footag_updated = get_active_tag('devtable', 'somenewrepo', 'foo')
|
||||
|
||||
if expected_offset is None:
|
||||
assert footag_updated.lifetime_end_ts is None
|
||||
else:
|
||||
start_date = datetime.utcfromtimestamp(footag_updated.lifetime_start_ts)
|
||||
end_date = datetime.utcfromtimestamp(footag_updated.lifetime_end_ts)
|
||||
expected_end_date = start_date + convert_to_timedelta(expected_offset)
|
||||
assert end_date == expected_end_date
|
||||
|
|
Reference in a new issue