236 lines
8 KiB
Python
236 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
|