Skip to content

Commit

Permalink
[6.16.z] Add test case to verify artifacts repair at the Satellite si…
Browse files Browse the repository at this point in the history
…de (#15241)

Add test case to verify artifacts repair at the Satellite side (#14976)

* Add test case to verify artifacts repair at Capsule

* Merge yum/file and docker/AC into one test case

* Add test case to verify artifacts repair at Satellite

* Let the test run through CLI to test hammer too

* Update CODEOWNERS and lint fix

Co-authored-by: Samuel Bible <[email protected]>
  • Loading branch information
vsedmik and sambible authored May 30, 2024
1 parent f7186cf commit 06f7c36
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 15 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
/tests/foreman/api/test_subscription.py @SatelliteQE/phoenix
/tests/foreman/api/test_syncplan.py @SatelliteQE/phoenix
/tests/foreman/cli/test_activationkey.py @SatelliteQE/phoenix
/tests/foreman/cli/test_artifacts.py @SatelliteQE/phoenix
/tests/foreman/cli/test_capsulecontent.py @SatelliteQE/phoenix
/tests/foreman/cli/test_contentaccess.py @SatelliteQE/phoenix
/tests/foreman/cli/test_contentcredentials.py @SatelliteQE/phoenix
Expand Down
6 changes: 6 additions & 0 deletions robottelo/cli/contentview.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ def version_republish_repositories(cls, options):
cls.command_sub = 'version republish-repositories'
return cls.execute(cls._construct_command(options), ignore_stderr=True)

@classmethod
def version_verify_checksum(cls, options):
"""Verify checksum of repository contents in the content view version."""
cls.command_sub = 'version verify-checksum'
return cls.execute(cls._construct_command(options), ignore_stderr=True)

@classmethod
def remove_from_environment(cls, options=None):
"""Remove content-view from an environment"""
Expand Down
25 changes: 10 additions & 15 deletions robottelo/cli/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
synchronize Sync a repository
update Update a product
update-proxy Updates an HTTP Proxy for a product
verify-checksum Verify checksum for one or more products
"""

from robottelo.cli.base import Base
Expand All @@ -34,22 +35,14 @@ class Product(Base):

@classmethod
def remove_sync_plan(cls, options=None):
"""
Delete assignment sync plan and product.
"""

"""Delete assignment sync plan and product."""
cls.command_sub = 'remove-sync-plan'

return cls.execute(cls._construct_command(options))

@classmethod
def set_sync_plan(cls, options=None):
"""
Assign sync plan to product.
"""

"""Assign sync plan to product."""
cls.command_sub = 'set-sync-plan'

return cls.execute(cls._construct_command(options))

@classmethod
Expand All @@ -60,10 +53,12 @@ def synchronize(cls, options=None):

@classmethod
def update_proxy(cls, options=None):
"""
Assign Http Proxy to products.
"""

"""Assign Http Proxy to products."""
cls.command_sub = 'update-proxy'

return cls.execute(cls._construct_command(options))

@classmethod
def verify_checksum(cls, options=None):
"""Verify checksum for one or more products."""
cls.command_sub = 'verify-checksum'
return cls.execute(cls._construct_command(options), ignore_stderr=True)
7 changes: 7 additions & 0 deletions robottelo/cli/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
synchronize Sync a repository
update Update a repository
upload-content Upload content into the repository
verify-checksum Verify checksum of repository contents
"""

from robottelo.cli.base import Base
Expand Down Expand Up @@ -78,3 +79,9 @@ def upload_content(cls, options):
"""Upload content to repository."""
cls.command_sub = 'upload-content'
return cls.execute(cls._construct_command(options), output_format='csv', ignore_stderr=True)

@classmethod
def verify_checksum(cls, options):
"""Verify checksum of repository contents."""
cls.command_sub = 'verify-checksum'
return cls.execute(cls._construct_command(options), ignore_stderr=True)
159 changes: 159 additions & 0 deletions tests/foreman/cli/test_artifacts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
"""Pulp artifacts related tests being run through CLI.
:Requirement: Repositories
:CaseAutomation: Automated
:CaseComponent: Repositories
:team: Phoenix-content
:CaseImportance: High
"""

from datetime import datetime
import random

from box import Box
import pytest

from robottelo.config import settings
from robottelo.constants import (
CONTAINER_REGISTRY_HUB,
CONTAINER_UPSTREAM_NAME,
)
from robottelo.constants.repos import ANSIBLE_GALAXY, CUSTOM_FILE_REPO
from robottelo.content_info import get_repo_files_urls_by_url


@pytest.fixture(scope='module')
def module_synced_content(
request,
module_target_sat,
module_org,
module_product,
):
"""
Create and sync one or more repositories and publish them in a CV.
:param request: Repo to use - dict with options to create the repo.
:return: Box with created instances and Repository sync time.
"""
repo = module_target_sat.api.Repository(product=module_product, **request.param).create()
sync_time = datetime.utcnow().replace(microsecond=0)
repo.sync()

cv = module_target_sat.api.ContentView(organization=module_org, repository=[repo]).create()
cv.publish()

return Box(prod=module_product, repo=repo, cv=cv.read(), sync_time=sync_time)


@pytest.mark.stream
@pytest.mark.parametrize('repair_type', ['repo', 'cv', 'product'])
@pytest.mark.parametrize(
'module_synced_content',
[
{'content_type': 'yum', 'url': settings.repos.yum_0.url},
{'content_type': 'file', 'url': CUSTOM_FILE_REPO},
{
'content_type': 'docker',
'docker_upstream_name': CONTAINER_UPSTREAM_NAME,
'url': CONTAINER_REGISTRY_HUB,
},
{
'content_type': 'ansible_collection',
'url': ANSIBLE_GALAXY,
'ansible_collection_requirements': '{collections: [ \
{ name: theforeman.foreman, version: "2.1.0" }, \
{ name: theforeman.operations, version: "0.1.0"} ]}',
},
],
indirect=True,
ids=['yum', 'file', 'docker', 'AC'],
)
@pytest.mark.parametrize('damage_type', ['destroy', 'corrupt'])
def test_positive_artifact_repair(
module_target_sat,
module_org,
module_lce_library,
module_synced_content,
damage_type,
repair_type,
):
"""Test the verify-checksum task repairs artifacts of each supported content type correctly
at the Satellite side for repo, CVV and product when the artifacts were removed or corrupted
before.
:id: 55c31fdc-bfa1-4af4-9adf-35c996eca974
:parametrized: yes
:setup:
1. Have a blank Satellite to avoid any artifacts already synced by other tests.
2. Per parameter, create repository of each content type, publish it in a CV.
:steps:
1. Based on the repository content type
- find and pick one artifact for particular published file, or
- pick one artifact synced recently by the `module_synced_content` fixture.
2. Cause desired type of damage to the artifact and verify the effect.
3. Trigger desired variant of the repair (verify_checksum) task.
4. Check if the artifact is back in shape.
:expectedresults:
1. Artifact is stored correctly based on the checksum. (yum and file)
2. All variants of verify_checksum task are able to repair all types of damage for all
supported content types.
"""
# Based on the repository content type
if module_synced_content.repo.content_type in ['yum', 'file']:
# Find and pick one artifact for particular published file.
sat_repo_url = module_target_sat.get_published_repo_url(
org=module_org.label,
lce=None if repair_type == 'repo' else module_lce_library.label,
cv=None if repair_type == 'repo' else module_synced_content.cv.label,
prod=module_synced_content.prod.label,
repo=module_synced_content.repo.label,
)
sat_files_urls = get_repo_files_urls_by_url(
sat_repo_url,
extension='rpm' if module_synced_content.repo.content_type == 'yum' else 'iso',
)
url = random.choice(sat_files_urls)
sum = module_target_sat.checksum_by_url(url, sum_type='sha256sum')
ai = module_target_sat.get_artifact_info(checksum=sum)
else:
# Pick one artifact synced recently by the `module_synced_content` fixture.
artifacts = module_target_sat.get_artifacts(since=module_synced_content.sync_time)
assert len(artifacts) > 0, 'No NEW artifacts found'
ai = module_target_sat.get_artifact_info(path=random.choice(artifacts))

# Cause desired type of damage to the artifact and verify the effect.
if damage_type == 'destroy':
module_target_sat.execute(f'rm -f {ai.path}')
with pytest.raises(FileNotFoundError):
module_target_sat.get_artifact_info(path=ai.path)
elif damage_type == 'corrupt':
res = module_target_sat.execute(f'truncate -s {random.randrange(1, ai.size)} {ai.path}')
assert res.status == 0, f'Artifact truncation failed: {res.stderr}'
assert module_target_sat.get_artifact_info(path=ai.path) != ai, 'Artifact corruption failed'
else:
raise ValueError(f'Unsupported damage type: {damage_type}')

# Trigger desired variant of repair (verify_checksum) task.
if repair_type == 'repo':
module_target_sat.cli.Repository.verify_checksum({'id': module_synced_content.repo.id})
elif repair_type == 'cv':
cvv_id = module_synced_content.cv.version[0].id
module_target_sat.cli.ContentView.version_verify_checksum({'id': cvv_id})
elif repair_type == 'product':
module_target_sat.cli.Product.verify_checksum({'ids': module_synced_content.prod.id})
else:
raise ValueError(f'Unsupported repair type: {repair_type}')

# Check if the artifact is back in shape.
fixed_ai = module_target_sat.get_artifact_info(path=ai.path)
assert fixed_ai == ai, f'Artifact restoration failed: {fixed_ai} != {ai}'

0 comments on commit 06f7c36

Please sign in to comment.