Skip to content

Commit

Permalink
Capsule N-Minus testing (#12939)
Browse files Browse the repository at this point in the history
(cherry picked from commit 6426265)
  • Loading branch information
jyejare authored and web-flow committed Mar 11, 2024
1 parent d2a8ef1 commit 286887b
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 15 deletions.
2 changes: 2 additions & 0 deletions conf/capsule.yaml.template
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
CAPSULE:
# Capsule hostname for N-minus testing
HOSTNAME:
VERSION:
# The full release version (6.9.2)
RELEASE: # populate with capsule version
Expand Down
6 changes: 3 additions & 3 deletions conf/dynaconf_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ def get_ohsnap_repos(settings):
settings,
repo='capsule',
product='capsule',
release=settings.server.version.release,
os_release=settings.server.version.rhel_version,
snap=settings.server.version.snap,
release=settings.capsule.version.release,
os_release=settings.capsule.version.rhel_version,
snap=settings.capsule.version.snap,
)

data['SATELLITE_REPO'] = get_ohsnap_repo_url(
Expand Down
1 change: 1 addition & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
'pytest_plugins.requirements.update_requirements',
'pytest_plugins.sanity_plugin',
'pytest_plugins.video_cleanup',
'pytest_plugins.capsule_n-minus',
# Fixtures
'pytest_fixtures.core.broker',
'pytest_fixtures.core.sat_cap_factory',
Expand Down
40 changes: 31 additions & 9 deletions pytest_fixtures/core/sat_cap_factory.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from contextlib import contextmanager
from functools import lru_cache

from broker import Broker
from packaging.version import Version
Expand Down Expand Up @@ -37,13 +38,29 @@ def _target_satellite_host(request, satellite_factory):
yield


@lru_cache
def cached_capsule_cdn_register(hostname=None):
cap = Capsule.get_host_by_hostname(hostname=hostname)
cap.enable_capsule_downstream_repos()


@contextmanager
def _target_capsule_host(request, capsule_factory):
if 'sanity' not in request.config.option.markexpr:
if 'sanity' not in request.config.option.markexpr and not request.config.option.n_minus:
new_cap = capsule_factory()
yield new_cap
new_cap.teardown()
Broker(hosts=[new_cap]).checkin()
elif request.config.option.n_minus:
if not settings.capsule.hostname:
hosts = Capsule.get_hosts_from_inventory(filter="'cap' in @inv.name")
settings.capsule.hostname = hosts[0].hostname
cap = hosts[0]
else:
cap = Capsule.get_host_by_hostname(settings.capsule.hostname)
# Capsule needs RHEL contents for some tests
cached_capsule_cdn_register(hostname=settings.capsule.hostname)
yield cap
else:
yield

Expand Down Expand Up @@ -162,9 +179,10 @@ def session_capsule_host(request, capsule_factory):


@pytest.fixture
def capsule_configured(capsule_host, target_sat):
def capsule_configured(request, capsule_host, target_sat):
"""Configure the capsule instance with the satellite from settings.server.hostname"""
capsule_host.capsule_setup(sat_host=target_sat)
if not request.config.option.n_minus:
capsule_host.capsule_setup(sat_host=target_sat)
return capsule_host


Expand All @@ -176,21 +194,23 @@ def large_capsule_configured(large_capsule_host, target_sat):


@pytest.fixture(scope='module')
def module_capsule_configured(module_capsule_host, module_target_sat):
def module_capsule_configured(request, module_capsule_host, module_target_sat):
"""Configure the capsule instance with the satellite from settings.server.hostname"""
module_capsule_host.capsule_setup(sat_host=module_target_sat)
if not request.config.option.n_minus:
module_capsule_host.capsule_setup(sat_host=module_target_sat)
return module_capsule_host


@pytest.fixture(scope='session')
def session_capsule_configured(session_capsule_host, session_target_sat):
def session_capsule_configured(request, session_capsule_host, session_target_sat):
"""Configure the capsule instance with the satellite from settings.server.hostname"""
session_capsule_host.capsule_setup(sat_host=session_target_sat)
if not request.config.option.n_minus:
session_capsule_host.capsule_setup(sat_host=session_target_sat)
return session_capsule_host


@pytest.fixture(scope='module')
def module_capsule_configured_mqtt(module_capsule_configured):
def module_capsule_configured_mqtt(request, module_capsule_configured):
"""Configure the capsule instance with the satellite from settings.server.hostname,
enable MQTT broker"""
module_capsule_configured.set_rex_script_mode_provider('pull-mqtt')
Expand All @@ -201,7 +221,9 @@ def module_capsule_configured_mqtt(module_capsule_configured):
result = module_capsule_configured.execute('firewall-cmd --permanent --add-port="1883/tcp"')
assert result.status == 0, 'Failed to open mqtt port on capsule'
module_capsule_configured.execute('firewall-cmd --reload')
return module_capsule_configured
yield module_capsule_configured
if request.config.option.n_minus:
raise TypeError('The teardown is missed for MQTT configuration undo for nminus testing')


@pytest.fixture(scope='module')
Expand Down
55 changes: 55 additions & 0 deletions pytest_plugins/capsule_n-minus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Collection of Capsule Factory fixture tests
# No destructive tests
# Adjust capsule host and capsule_configured host behavior for n_minus testing
# Calculate capsule hostname from inventory just as we do in xDist.py
from robottelo.config import settings
from robottelo.hosts import Capsule


def pytest_addoption(parser):
"""Add options for pytest to collect tests based on fixtures its using"""
help_text = '''
Collects tests based on capsule fixtures used by tests and uncollect destructive tests
Usage: --n-minus
example: pytest --n-minus tests/foreman
'''
parser.addoption("--n-minus", action='store_true', default=False, help=help_text)


def pytest_collection_modifyitems(items, config):

if not config.getoption('n_minus', False):
return

selected = []
deselected = []

for item in items:
is_destructive = item.get_closest_marker('destructive')
# Deselect Destructive tests and tests without capsule_factory fixture
if 'capsule_factory' not in item.fixturenames or is_destructive:
deselected.append(item)
continue
# Ignoring all puppet tests as they are destructive in nature
# and needs its own satellite for verification
if 'session_puppet_enabled_sat' in item.fixturenames:
deselected.append(item)
continue
# Ignoring all satellite maintain tests as they are destructive in nature
# Also dont need them in nminus testing as its not integration testing
if 'sat_maintain' in item.fixturenames and 'satellite' in item.callspec.params.values():
deselected.append(item)
continue
selected.append(item)

config.hook.pytest_deselected(items=deselected)
items[:] = selected


def pytest_sessionfinish(session, exitstatus):
# Unregister the capsule from CDN after all tests
if session.config.option.n_minus and not session.config.option.collectonly:
caps = Capsule.get_host_by_hostname(hostname=settings.capsule.hostname)
caps.unregister()
6 changes: 6 additions & 0 deletions robottelo/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,12 @@ class CLIError(Exception):
"""Indicates that a CLI command could not be run."""


class CapsuleHostError(Exception):
"""Indicates error in capsule configuration etc"""

pass


class CLIBaseError(Exception):
"""Indicates that a CLI command has finished with return code different
from zero.
Expand Down
20 changes: 19 additions & 1 deletion robottelo/hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,21 @@ def get_features(self):
"""Get capsule features"""
return requests.get(f'https://{self.hostname}:9090/features', verify=False).text

def enable_capsule_downstream_repos(self):
"""Enable CDN repos and capsule downstream repos on Capsule Host"""
# CDN Repos
self.register_to_cdn()
for repo in getattr(constants, f"OHSNAP_RHEL{self.os_version.major}_REPOS"):
result = self.enable_repo(repo, force=True)
if result.status:
raise CapsuleHostError(f'Repo enable at capsule host failed\n{result.stdout}')
# Downstream Capsule specific Repos
self.download_repofile(
product='capsule',
release=settings.capsule.version.release,
snap=settings.capsule.version.snap,
)

def capsule_setup(self, sat_host=None, capsule_cert_opts=None, **installer_kwargs):
"""Prepare the host and run the capsule installer"""
self._satellite = sat_host or Satellite()
Expand Down Expand Up @@ -1701,7 +1716,10 @@ def run_installer_arg(self, *args, timeout='20m'):
timeout=timeout,
)
if result.status != 0:
raise SatelliteHostError(f'Failed to execute with argument: {result.stderr}')
raise SatelliteHostError(
f'Failed to execute with arguments: {installer_args} and,'
f' the stderr is {result.stderr}'
)

def set_mqtt_resend_interval(self, value):
"""Set the time interval in seconds at which the notification should be
Expand Down
15 changes: 13 additions & 2 deletions tests/foreman/api/test_capsule.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
@pytest.mark.e2e
@pytest.mark.upgrade
@pytest.mark.tier1
def test_positive_update_capsule(target_sat, module_capsule_configured):
def test_positive_update_capsule(request, pytestconfig, target_sat, module_capsule_configured):
"""Update various capsule properties
:id: a3d3eaa9-ed8d-42e6-9c83-20251e5ca9af
Expand All @@ -39,7 +39,7 @@ def test_positive_update_capsule(target_sat, module_capsule_configured):
:customerscenario: true
"""
new_name = f'{gen_string("alpha")}-{module_capsule_configured.name}'
new_name = f'{gen_string("alpha")}-{module_capsule_configured.hostname}'
capsule = target_sat.api.SmartProxy().search(
query={'search': f'name = {module_capsule_configured.hostname}'}
)[0]
Expand Down Expand Up @@ -68,6 +68,17 @@ def test_positive_update_capsule(target_sat, module_capsule_configured):
capsule = capsule.update(['name'])
assert capsule.name == new_name

@request.addfinalizer
def _finalize():
# Updating the hostname back
if (
cap := target_sat.api.SmartProxy().search(query={'search': f'name = {new_name}'})
and pytestconfig.option.n_minus
):
cap = cap[0]
cap.name = module_capsule_configured.hostname
cap.update(['name'])

# serching for non-default capsule BZ#2077824
capsules = target_sat.api.SmartProxy().search(query={'search': 'id != 1'})
assert len(capsules) > 0
Expand Down
3 changes: 3 additions & 0 deletions tests/foreman/api/test_capsulecontent.py
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,7 @@ def test_positive_capsule_sync_status_persists(
def test_positive_remove_capsule_orphans(
self,
target_sat,
pytestconfig,
capsule_configured,
function_entitlement_manifest_org,
function_lce_library,
Expand Down Expand Up @@ -1288,6 +1289,8 @@ def test_positive_remove_capsule_orphans(
:BZ: 22043089, 2211962
"""
if not pytestconfig.option.n_minus:
pytest.skip('Test cannot be run on n-minus setups session-scoped capsule')
# Enable RHST repo and sync it to the Library LCE.
repo_id = target_sat.api_factory.enable_rhrepo_and_fetchid(
basearch='x86_64',
Expand Down
2 changes: 2 additions & 0 deletions tests/foreman/destructive/test_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

from robottelo.config import settings

pytestmark = pytest.mark.destructive


@pytest.mark.tier3
@pytest.mark.no_containers
Expand Down

0 comments on commit 286887b

Please sign in to comment.