This repository has been archived on 2020-03-24. You can view files and clone it, but cannot push or open issues or pull requests.
quay/data/model/test/test_repo_mirroring.py
2019-11-12 11:09:47 -05:00

235 lines
8 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import absolute_import
from jsonschema import ValidationError
from data.database import RepoMirrorConfig, RepoMirrorStatus, User
from data import model
from data.model.repo_mirror import (create_mirroring_rule, get_eligible_mirrors, update_sync_status_to_cancel,
MAX_SYNC_RETRIES, release_mirror)
from test.fixtures import *
def create_mirror_repo_robot(rules, repo_name="repo"):
try:
user = User.get(User.username == "mirror")
except User.DoesNotExist:
user = create_user_noverify("mirror", "mirror@example.com", email_required=False)
try:
robot = lookup_robot("mirror+robot")
except model.InvalidRobotException:
robot, _ = create_robot("robot", user)
repo = create_repository("mirror", repo_name, None, repo_kind="image", visibility="public")
repo.save()
rule = model.repo_mirror.create_mirroring_rule(repo, rules)
mirror_kwargs = {
"repository": repo,
"root_rule": rule,
"internal_robot": robot,
"external_reference": "registry.example.com/namespace/repository",
"sync_interval": timedelta(days=1).total_seconds()
}
mirror = enable_mirroring_for_repository(**mirror_kwargs)
mirror.sync_status = RepoMirrorStatus.NEVER_RUN
mirror.sync_start_date = datetime.utcnow() - timedelta(days=1)
mirror.sync_retries_remaining = 3
mirror.save()
return (mirror, repo)
def disable_existing_mirrors():
mirrors = RepoMirrorConfig.select().execute()
for mirror in mirrors:
mirror.is_enabled = False
mirror.save()
def test_eligible_oldest_first(initialized_db):
"""
Eligible mirror candidates should be returned with the oldest (earliest created) first.
"""
disable_existing_mirrors()
mirror_first, repo_first = create_mirror_repo_robot(["updated", "created"], repo_name="first")
mirror_second, repo_second = create_mirror_repo_robot(["updated", "created"], repo_name="second")
mirror_third, repo_third = create_mirror_repo_robot(["updated", "created"], repo_name="third")
candidates = get_eligible_mirrors()
assert len(candidates) == 3
assert candidates[0] == mirror_first
assert candidates[1] == mirror_second
assert candidates[2] == mirror_third
def test_eligible_includes_expired_syncing(initialized_db):
"""
Mirrors that have an end time in the past are eligible even if their state indicates still syncing.
"""
disable_existing_mirrors()
mirror_first, repo_first = create_mirror_repo_robot(["updated", "created"], repo_name="first")
mirror_second, repo_second = create_mirror_repo_robot(["updated", "created"], repo_name="second")
mirror_third, repo_third = create_mirror_repo_robot(["updated", "created"], repo_name="third")
mirror_fourth, repo_third = create_mirror_repo_robot(["updated", "created"], repo_name="fourth")
mirror_second.sync_expiration_date = datetime.utcnow() - timedelta(hours=1)
mirror_second.sync_status = RepoMirrorStatus.SYNCING
mirror_second.save()
mirror_fourth.sync_expiration_date = datetime.utcnow() + timedelta(hours=1)
mirror_fourth.sync_status = RepoMirrorStatus.SYNCING
mirror_fourth.save()
candidates = get_eligible_mirrors()
assert len(candidates) == 3
assert candidates[0] == mirror_first
assert candidates[1] == mirror_second
assert candidates[2] == mirror_third
def test_eligible_includes_immediate(initialized_db):
"""
Mirrors that are SYNC_NOW, regardless of starting time
"""
disable_existing_mirrors()
mirror_first, repo_first = create_mirror_repo_robot(["updated", "created"], repo_name="first")
mirror_second, repo_second = create_mirror_repo_robot(["updated", "created"], repo_name="second")
mirror_third, repo_third = create_mirror_repo_robot(["updated", "created"], repo_name="third")
mirror_fourth, repo_third = create_mirror_repo_robot(["updated", "created"], repo_name="fourth")
mirror_future, _ = create_mirror_repo_robot(["updated", "created"], repo_name="future")
mirror_past, _ = create_mirror_repo_robot(["updated", "created"], repo_name="past")
mirror_future.sync_start_date = datetime.utcnow() + timedelta(hours=6)
mirror_future.sync_status = RepoMirrorStatus.SYNC_NOW
mirror_future.save()
mirror_past.sync_start_date = datetime.utcnow() - timedelta(hours=6)
mirror_past.sync_status = RepoMirrorStatus.SYNC_NOW
mirror_past.save()
mirror_fourth.sync_expiration_date = datetime.utcnow() + timedelta(hours=1)
mirror_fourth.sync_status = RepoMirrorStatus.SYNCING
mirror_fourth.save()
candidates = get_eligible_mirrors()
assert len(candidates) == 5
assert candidates[0] == mirror_first
assert candidates[1] == mirror_second
assert candidates[2] == mirror_third
assert candidates[3] == mirror_past
assert candidates[4] == mirror_future
def test_create_rule_validations(initialized_db):
mirror, repo = create_mirror_repo_robot(["updated", "created"], repo_name="first")
with pytest.raises(ValidationError):
create_mirroring_rule(repo, None)
with pytest.raises(ValidationError):
create_mirroring_rule(repo, "['tag1', 'tag2']")
with pytest.raises(ValidationError):
create_mirroring_rule(repo, ['tag1', 'tag2'], rule_type=None)
def test_long_registry_passwords(initialized_db):
"""
Verify that long passwords, such as Base64 JWT used by Redhat's Registry, work as expected.
"""
MAX_PASSWORD_LENGTH = 1024
username = ''.join('a' for _ in range(MAX_PASSWORD_LENGTH))
password = ''.join('b' for _ in range(MAX_PASSWORD_LENGTH))
assert len(username) == MAX_PASSWORD_LENGTH
assert len(password) == MAX_PASSWORD_LENGTH
repo = model.repository.get_repository('devtable', 'mirrored')
assert repo
existing_mirror_conf = model.repo_mirror.get_mirror(repo)
assert existing_mirror_conf
assert model.repo_mirror.change_credentials(repo, username, password)
updated_mirror_conf = model.repo_mirror.get_mirror(repo)
assert updated_mirror_conf
assert updated_mirror_conf.external_registry_username.decrypt() == username
assert updated_mirror_conf.external_registry_password.decrypt() == password
def test_sync_status_to_cancel(initialized_db):
"""
SYNCING and SYNC_NOW mirrors may be canceled, ending in NEVER_RUN
"""
disable_existing_mirrors()
mirror, repo = create_mirror_repo_robot(["updated", "created"], repo_name="cancel")
mirror.sync_status = RepoMirrorStatus.SYNCING
mirror.save()
updated = update_sync_status_to_cancel(mirror)
assert updated is not None
assert updated.sync_status == RepoMirrorStatus.NEVER_RUN
mirror.sync_status = RepoMirrorStatus.SYNC_NOW
mirror.save()
updated = update_sync_status_to_cancel(mirror)
assert updated is not None
assert updated.sync_status == RepoMirrorStatus.NEVER_RUN
mirror.sync_status = RepoMirrorStatus.FAIL
mirror.save()
updated = update_sync_status_to_cancel(mirror)
assert updated is None
mirror.sync_status = RepoMirrorStatus.NEVER_RUN
mirror.save()
updated = update_sync_status_to_cancel(mirror)
assert updated is None
mirror.sync_status = RepoMirrorStatus.SUCCESS
mirror.save()
updated = update_sync_status_to_cancel(mirror)
assert updated is None
def test_release_mirror(initialized_db):
"""
Mirrors that are SYNC_NOW, regardless of starting time
"""
disable_existing_mirrors()
mirror, repo = create_mirror_repo_robot(["updated", "created"], repo_name="first")
# mysql rounds the milliseconds on update so force that to happen now
query = (RepoMirrorConfig
.update(sync_start_date=mirror.sync_start_date)
.where(RepoMirrorConfig.id == mirror.id))
query.execute()
mirror = RepoMirrorConfig.get_by_id(mirror.id)
original_sync_start_date = mirror.sync_start_date
assert mirror.sync_retries_remaining == 3
mirror = release_mirror(mirror, RepoMirrorStatus.FAIL)
assert mirror.sync_retries_remaining == 2
assert mirror.sync_start_date == original_sync_start_date
mirror = release_mirror(mirror, RepoMirrorStatus.FAIL)
assert mirror.sync_retries_remaining == 1
assert mirror.sync_start_date == original_sync_start_date
mirror = release_mirror(mirror, RepoMirrorStatus.FAIL)
assert mirror.sync_retries_remaining == 3
assert mirror.sync_start_date > original_sync_start_date