Merge pull request #2447 from jzelinskie/cnr-step2

CNR Step 2
This commit is contained in:
Jimmy Zelinskie 2017-03-22 18:45:51 -04:00 committed by GitHub
commit 3ccf3c5f33
40 changed files with 790 additions and 136 deletions

View file

@ -3,14 +3,23 @@ from cachetools import lru_cache
from data.model import DataModelException
from data.database import (Repository, User, Team, TeamMember, RepositoryPermission, TeamRole,
Namespace, Visibility, ImageStorage, Image, db_for_update)
Namespace, Visibility, ImageStorage, Image, RepositoryKind,
db_for_update)
def get_existing_repository(namespace_name, repository_name, for_update=False):
def get_existing_repository(namespace_name, repository_name, for_update=False, kind_filter=None):
query = (Repository
.select(Repository, Namespace)
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
.where(Namespace.username == namespace_name, Repository.name == repository_name))
.where(Namespace.username == namespace_name,
Repository.name == repository_name))
if kind_filter:
query = (query
.switch(Repository)
.join(RepositoryKind)
.where(RepositoryKind.name == kind_filter))
if for_update:
query = db_for_update(query)
@ -27,11 +36,14 @@ def _lookup_team_role(name):
return TeamRole.get(name=name)
def filter_to_repos_for_user(query, username=None, namespace=None, include_public=True,
start_id=None):
def filter_to_repos_for_user(query, username=None, namespace=None, repo_kind='image',
include_public=True, start_id=None):
if not include_public and not username:
return Repository.select().where(Repository.id == '-1')
# Filter on the type of repository.
query = query.where(Repository.kind == Repository.kind.get_id(repo_kind))
# Add the start ID if necessary.
if start_id is not None:
query = query.where(Repository.id >= start_id)
@ -121,5 +133,3 @@ def calculate_image_aggregate_size(ancestors_str, image_size, parent_image):
return None
return ancestor_size + image_size

View file

@ -18,6 +18,11 @@ from util.itertoolrecipes import take
logger = logging.getLogger(__name__)
def get_repo_kind_name(repo):
return Repository.kind.get_name(repo.kind_id)
def get_repository_count():
return Repository.select().count()
@ -26,10 +31,11 @@ def get_public_repo_visibility():
return _basequery.get_public_repo_visibility()
def create_repository(namespace, name, creating_user, visibility='private'):
def create_repository(namespace, name, creating_user, visibility='private', repo_kind='image'):
private = Visibility.get(name=visibility)
namespace_user = User.get(username=namespace)
repo = Repository.create(name=name, visibility=private, namespace_user=namespace_user)
repo = Repository.create(name=name, visibility=private, namespace_user=namespace_user,
kind=Repository.kind.get_id(repo_kind))
admin = Role.get(name='admin')
yesterday = datetime.now() - timedelta(days=1)
@ -44,9 +50,10 @@ def create_repository(namespace, name, creating_user, visibility='private'):
return repo
def get_repository(namespace_name, repository_name):
def get_repository(namespace_name, repository_name, kind_filter=None):
try:
return _basequery.get_existing_repository(namespace_name, repository_name)
return _basequery.get_existing_repository(namespace_name, repository_name,
kind_filter=kind_filter)
except Repository.DoesNotExist:
return None
@ -103,7 +110,7 @@ def _get_gc_expiration_policies():
def get_random_gc_policy():
""" Return a single random policy from the database to use when garbage collecting.
"""
"""
return random.choice(_get_gc_expiration_policies())
@ -259,7 +266,7 @@ def unstar_repository(user, repository):
raise DataModelException('Star not found.')
def get_user_starred_repositories(user):
def get_user_starred_repositories(user, repo_kind='image'):
""" Retrieves all of the repositories a user has starred. """
query = (Repository
.select(Repository, User, Visibility, Repository.id.alias('rid'))
@ -268,7 +275,8 @@ def get_user_starred_repositories(user):
.join(User)
.switch(Repository)
.join(Visibility)
.where(Star.user == user))
.where(Star.user == user,
Repository.kind == Repository.kind.get_id(repo_kind)))
return query
@ -302,8 +310,8 @@ def get_when_last_modified(repository_ids):
return last_modified_map
def get_visible_repositories(username, namespace=None, include_public=False, start_id=None,
limit=None):
def get_visible_repositories(username, namespace=None, repo_kind='image', include_public=False,
start_id=None, limit=None):
""" Returns the repositories visible to the given user (if any).
"""
if not include_public and not username:
@ -313,7 +321,8 @@ def get_visible_repositories(username, namespace=None, include_public=False, sta
query = (Repository
.select(Repository.name, Repository.id.alias('rid'),
Repository.description, Namespace.username, Repository.visibility)
Repository.description, Namespace.username, Repository.visibility,
Repository.kind)
.switch(Repository)
.join(Namespace, on=(Repository.namespace_user == Namespace.id)))
@ -321,7 +330,7 @@ def get_visible_repositories(username, namespace=None, include_public=False, sta
# Note: We only need the permissions table if we will filter based on a user's permissions.
query = query.switch(Repository).distinct().join(RepositoryPermission, JOIN_LEFT_OUTER)
query = _basequery.filter_to_repos_for_user(query, username, namespace, include_public,
query = _basequery.filter_to_repos_for_user(query, username, namespace, repo_kind, include_public,
start_id=start_id)
if limit is not None:
@ -330,14 +339,15 @@ def get_visible_repositories(username, namespace=None, include_public=False, sta
return query
def get_filtered_matching_repositories(lookup_value, filter_username=None, offset=0, limit=25):
def get_filtered_matching_repositories(lookup_value, filter_username=None, repo_kind='image',
offset=0, limit=25):
""" Returns an iterator of all repositories matching the given lookup value, with optional
filtering to a specific user. If the user is unspecified, only public repositories will
be returned.
"""
# Build the unfiltered search query.
unfiltered_query = _get_sorted_matching_repositories(lookup_value,
unfiltered_query = _get_sorted_matching_repositories(lookup_value, repo_kind=repo_kind,
include_private=filter_username is not None)
# Add a filter to the iterator, if necessary.
@ -395,7 +405,7 @@ def _filter_repositories_visible_to_username(unfiltered_query, filter_username,
iteration_count = iteration_count + 1
def _get_sorted_matching_repositories(lookup_value, include_private=False):
def _get_sorted_matching_repositories(lookup_value, repo_kind='image', include_private=False):
""" Returns a query of repositories matching the given lookup string, with optional inclusion of
private repositories. Note that this method does *not* filter results based on visibility
to users.
@ -405,7 +415,8 @@ def _get_sorted_matching_repositories(lookup_value, include_private=False):
query = (Repository
.select(Repository, Namespace)
.join(Namespace, on=(Namespace.id == Repository.namespace_user))
.where(Repository.name.match(lookup_value) | Repository.description.match(lookup_value))
.where(Repository.name.match(lookup_value) | Repository.description.match(lookup_value),
Repository.kind == Repository.kind.get_id(repo_kind))
.group_by(Repository.id, Namespace.id))
if not include_private:
@ -438,7 +449,8 @@ def repository_is_public(namespace_name, repository_name):
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
.switch(Repository)
.join(Visibility)
.where(Namespace.username == namespace_name, Repository.name == repository_name,
.where(Namespace.username == namespace_name,
Repository.name == repository_name,
Visibility.name == 'public')
.get())
return True
@ -461,7 +473,8 @@ def get_email_authorized_for_repo(namespace, repository, email):
.select(RepositoryAuthorizedEmail, Repository, Namespace)
.join(Repository)
.join(Namespace, on=(Repository.namespace_user == Namespace.id))
.where(Namespace.username == namespace, Repository.name == repository,
.where(Namespace.username == namespace,
Repository.name == repository,
RepositoryAuthorizedEmail.email == email)
.get())
except RepositoryAuthorizedEmail.DoesNotExist:
@ -495,7 +508,7 @@ def confirm_email_authorization_for_repo(code):
return found
def list_popular_public_repos(action_count_threshold, time_span):
def list_popular_public_repos(action_count_threshold, time_span, repo_kind='image'):
cutoff = datetime.now() - time_span
return (Repository
.select(Namespace.username, Repository.name)
@ -503,7 +516,8 @@ def list_popular_public_repos(action_count_threshold, time_span):
.switch(Repository)
.join(RepositoryActionCount)
.where(RepositoryActionCount.date >= cutoff,
Repository.visibility == get_public_repo_visibility())
Repository.visibility == get_public_repo_visibility(),
Repository.kind == Repository.kind.get_id(repo_kind))
.group_by(RepositoryActionCount.repository, Repository.name, Namespace.username)
.having(fn.Sum(RepositoryActionCount.count) >= action_count_threshold)
.tuples())

View file

@ -0,0 +1,14 @@
import pytest
from peewee import IntegrityError
from endpoints.test.fixtures import database_uri, init_db_path, sqlitedb_file
from data.model.repository import create_repository
def test_duplicate_repository_different_kinds(database_uri):
# Create an image repo.
create_repository('devtable', 'somenewrepo', None, repo_kind='image')
# Try to create an app repo with the same name, which should fail.
with pytest.raises(IntegrityError):
create_repository('devtable', 'somenewrepo', None, repo_kind='application')