Allow tags to be marked as hidden. Create a hidden tag on every image during a push to prevent them from getting GCed.

This commit is contained in:
Jake Moshenko 2015-02-18 16:37:38 -05:00
parent 04b06547b8
commit 41108a0856
6 changed files with 63 additions and 14 deletions

View file

@ -194,4 +194,7 @@ class DefaultConfig(object):
SYSTEM_SERVICES_PATH = "conf/init/" SYSTEM_SERVICES_PATH = "conf/init/"
# Services that should not be shown in the logs view. # Services that should not be shown in the logs view.
SYSTEM_SERVICE_BLACKLIST = [] SYSTEM_SERVICE_BLACKLIST = []
# Temporary tag expiration in seconds, this may actually be longer based on GC policy
PUSH_TEMP_TAG_EXPIRATION_S = 60 * 60

View file

@ -469,6 +469,7 @@ class RepositoryTag(BaseModel):
repository = ForeignKeyField(Repository) repository = ForeignKeyField(Repository)
lifetime_start_ts = IntegerField(default=_get_epoch_timestamp) lifetime_start_ts = IntegerField(default=_get_epoch_timestamp)
lifetime_end_ts = IntegerField(null=True, index=True) lifetime_end_ts = IntegerField(null=True, index=True)
hidden = BooleanField(default=False)
class Meta: class Meta:
database = db database = db

View file

@ -0,0 +1,26 @@
"""Allow tags to be marked as hidden.
Revision ID: 4ef04c61fcf9
Revises: 509d2857566f
Create Date: 2015-02-18 16:34:16.586129
"""
# revision identifiers, used by Alembic.
revision = '4ef04c61fcf9'
down_revision = '509d2857566f'
from alembic import op
import sqlalchemy as sa
def upgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.add_column('repositorytag', sa.Column('hidden', sa.Boolean(), nullable=False, server_default=sa.sql.expression.false()))
### end Alembic commands ###
def downgrade(tables):
### commands auto generated by Alembic - please adjust! ###
op.drop_column('repositorytag', 'hidden')
### end Alembic commands ###

View file

@ -5,6 +5,7 @@ import json
import time import time
from datetime import datetime, timedelta, date from datetime import datetime, timedelta, date
from uuid import uuid4
from data.database import (User, Repository, Image, AccessToken, Role, RepositoryPermission, from data.database import (User, Repository, Image, AccessToken, Role, RepositoryPermission,
Visibility, RepositoryTag, EmailConfirmation, FederatedLogin, Visibility, RepositoryTag, EmailConfirmation, FederatedLogin,
@ -1561,15 +1562,20 @@ def _tag_alive(query):
(RepositoryTag.lifetime_end_ts > int(time.time()))) (RepositoryTag.lifetime_end_ts > int(time.time())))
def list_repository_tags(namespace_name, repository_name): def list_repository_tags(namespace_name, repository_name, include_hidden=False):
return _tag_alive(RepositoryTag query = _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, .where(Repository.name == repository_name,
Namespace.username == namespace_name)) Namespace.username == namespace_name))
if not include_hidden:
query = query.where(RepositoryTag.hidden == False)
return query
def _garbage_collect_tags(namespace_name, repository_name): def _garbage_collect_tags(namespace_name, repository_name):
@ -1786,10 +1792,8 @@ def create_or_update_tag(namespace_name, repository_name, tag_name,
# No tag that needs to be ended # No tag that needs to be ended
pass pass
tag = RepositoryTag.create(repository=repo, image=image, name=tag_name, return RepositoryTag.create(repository=repo, image=image, name=tag_name,
lifetime_start_ts=now_ts) lifetime_start_ts=now_ts)
return tag
def delete_tag(namespace_name, repository_name, tag_name): def delete_tag(namespace_name, repository_name, tag_name):
@ -1812,6 +1816,17 @@ def delete_tag(namespace_name, repository_name, tag_name):
found.save() found.save()
def create_temporary_hidden_tag(repo, image, expiration_s):
""" Create a tag with a defined timeline, that will not appear in the UI or CLI. Returns the name
of the temporary tag. """
now_ts = int(time.time())
expire_ts = now_ts + expiration_s
tag_name = str(uuid4())
RepositoryTag.create(repository=repo, image=image, name=tag_name, lifetime_start_ts=now_ts,
lifetime_end_ts=expire_ts, hidden=True)
return tag_name
def purge_all_repository_tags(namespace_name, repository_name): def purge_all_repository_tags(namespace_name, repository_name):
""" Immediately purge all repository tags without respecting the lifeline procedure """ """ Immediately purge all repository tags without respecting the lifeline procedure """
try: try:

View file

@ -467,6 +467,10 @@ def put_image_json(namespace, repository, image_id):
repo_image = model.find_create_or_link_image(image_id, repo, username, {}, repo_image = model.find_create_or_link_image(image_id, repo, username, {},
store.preferred_locations[0]) store.preferred_locations[0])
# Create a temporary tag to prevent this image from getting garbage collected while the push
# is in progress.
model.create_temporary_hidden_tag(repo, repo_image, app.config['PUSH_TEMP_TAG_EXPIRATION_S'])
uuid = repo_image.storage.uuid uuid = repo_image.storage.uuid
if image_id != data['id']: if image_id != data['id']:

Binary file not shown.