From f4ff48d12f0e0a5cbab577b0445da8b1b1412594 Mon Sep 17 00:00:00 2001 From: Zach Huntington-Meath Date: Tue, 7 Jul 2026 09:21:24 -0400 Subject: [PATCH 01/16] Add satellite flavor for downstream deployments The satellite flavor provides a feature set matching what RPM-based Satellite ships by default, enabling proper downstream test coverage with foremanctl. Co-Authored-By: Claude Opus 4.6 --- src/features.yaml | 4 ++++ src/playbooks/deploy/metadata.obsah.yaml | 1 + src/vars/flavors/satellite.yml | 12 ++++++++++++ 3 files changed, 17 insertions(+) create mode 100644 src/vars/flavors/satellite.yml diff --git a/src/features.yaml b/src/features.yaml index 73a8f3a60..248bc85eb 100644 --- a/src/features.yaml +++ b/src/features.yaml @@ -90,3 +90,7 @@ registration: plugin_name: registration dependencies: - templates +theme-satellite: + description: Satellite theme for Foreman + foreman: + plugin_name: foreman_theme_satellite diff --git a/src/playbooks/deploy/metadata.obsah.yaml b/src/playbooks/deploy/metadata.obsah.yaml index 0fdf1a242..8c68d40e6 100644 --- a/src/playbooks/deploy/metadata.obsah.yaml +++ b/src/playbooks/deploy/metadata.obsah.yaml @@ -7,6 +7,7 @@ variables: help: Base flavor to use in this deployment. choices: - katello + - satellite external_authentication: help: External authentication method to use choices: diff --git a/src/vars/flavors/satellite.yml b/src/vars/flavors/satellite.yml new file mode 100644 index 000000000..0fb4a7c15 --- /dev/null +++ b/src/vars/flavors/satellite.yml @@ -0,0 +1,12 @@ +--- +flavor_features: + - foreman + - katello + - content/ansible + - content/container + - content/deb + - content/python + - content/rpm + - remote-execution + - rh-cloud + - theme-satellite From 4289f20279af9eeda7e5cfe7ae4c7d97c5de5262 Mon Sep 17 00:00:00 2001 From: Zach Huntington-Meath Date: Sun, 12 Jul 2026 15:05:16 -0400 Subject: [PATCH 02/16] Add satellite flavor test mapping and CI job Map the satellite flavor to run katello flavor tests via FLAVOR_TEST_DIRS in conftest.py, so satellite deployments automatically pick up katello-specific test assertions. Add a satellite-tests CI job that deploys with --flavor satellite. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/test.yml | 71 +++++++++++++++++++++++ src/features.yaml | 1 + src/roles/post_install/tasks/message.yaml | 2 +- src/vars/flavors/satellite.yml | 17 ++++++ tests/backup_test.py | 4 +- tests/conftest.py | 13 ++++- 6 files changed, 103 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 03ee040b8..6aa2773c8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -465,6 +465,76 @@ jobs: limit-access-to-actor: true wait-timeout-minutes: 5 + satellite-tests: + runs-on: ubuntu-24.04 + name: "Tests Satellite Deployment" + steps: + - uses: actions/checkout@v7 + - name: Set up Python + uses: actions/setup-python@v7 + with: + python-version: '3.12' + - name: Setup libvirt for Vagrant + uses: voxpupuli/setup-vagrant@v0 + with: + configure_dns: true + - name: Install Ansible + run: pip install --upgrade ansible-core + - name: Setup environment + run: ./setup-environment + - name: Start VMs + run: | + ./forge vms start --vms "quadlet client" + - name: Configure repositories + run: | + ./forge setup-repositories + - name: Run image pull + run: | + ./foremanctl pull-images + - name: Run deployment + run: | + ./foremanctl deploy \ + --flavor satellite \ + --initial-admin-password=changeme \ + --initial-organization "Foreman CI" \ + --initial-location "Internet" \ + --tuning development + - name: Deploy features + run: | + ./foremanctl deploy \ + --add-feature hammer \ + --add-feature foreman-proxy \ + --add-feature azure-rm \ + --add-feature google \ + --add-feature bmc + - name: Run tests + run: | + ./forge test + - name: Run smoker + run: | + ./forge smoker + - name: Archive smoker report + if: ${{ always() }} + uses: actions/upload-artifact@v7 + with: + name: smoker-satellite + path: "/home/runner/smoker/report/" + - name: Generate sos reports + if: ${{ always() }} + run: ./forge sos + - name: Archive sos reports + if: ${{ always() }} + uses: actions/upload-artifact@v7 + with: + name: sosreport-satellite + path: sos/ + - name: Setup upterm session + if: ${{ failure() }} + uses: owenthereal/action-upterm@v1 + with: + limit-access-to-actor: true + wait-timeout-minutes: 5 + # A dummy job that you can mark as a required check instead of each individual test test-suite: if: always() @@ -474,6 +544,7 @@ jobs: - upgrade - migration - foreman-proxy-content-tests + - satellite-tests - ansible-lint - python-lint runs-on: ubuntu-latest diff --git a/src/features.yaml b/src/features.yaml index 248bc85eb..6c65c2ebf 100644 --- a/src/features.yaml +++ b/src/features.yaml @@ -92,5 +92,6 @@ registration: - templates theme-satellite: description: Satellite theme for Foreman + internal: true foreman: plugin_name: foreman_theme_satellite diff --git a/src/roles/post_install/tasks/message.yaml b/src/roles/post_install/tasks/message.yaml index 581f4fba0..cfa393961 100644 --- a/src/roles/post_install/tasks/message.yaml +++ b/src/roles/post_install/tasks/message.yaml @@ -1,6 +1,6 @@ --- - name: Post install message - when: flavor in ['katello'] + when: flavor in ['katello', 'satellite'] ansible.builtin.debug: msg: - "{{ _post_install_url_msg }}" diff --git a/src/vars/flavors/satellite.yml b/src/vars/flavors/satellite.yml index 0fb4a7c15..6890e0d18 100644 --- a/src/vars/flavors/satellite.yml +++ b/src/vars/flavors/satellite.yml @@ -10,3 +10,20 @@ flavor_features: - remote-execution - rh-cloud - theme-satellite + - webhooks + +checks_to_execute: + - check_features + - check_hostname + - check_database_connection + - check_system_requirements + - check_podman_network_backend + +health_checks_to_execute: + - check_hostname + - check_database_connection + - check_services + - check_foreman_api + - check_foreman_tasks + - check_host_facts_count + - check_duplicate_permissions diff --git a/tests/backup_test.py b/tests/backup_test.py index ff76d15c8..09ef2959d 100644 --- a/tests/backup_test.py +++ b/tests/backup_test.py @@ -18,8 +18,8 @@ def expected_databases(enabled_features, flavor): """ databases = [] - # Katello flavor has foreman, candlepin, and pulp - if flavor == 'katello': + # Katello and Satellite flavors have foreman, candlepin, and pulp + if flavor in ('katello', 'satellite'): databases = ['foreman', 'candlepin', 'pulp'] # Foreman-proxy-content flavor only has pulp diff --git a/tests/conftest.py b/tests/conftest.py index b52d0f178..076f13c72 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -284,16 +284,25 @@ def pytest_configure(config): config.user_parameters = UserParameters(config) +FLAVOR_TEST_DIRS = { + 'katello': ['katello', 'satellite'], + 'foreman-proxy-content': ['foreman-proxy-content', 'capsule'], +} + + def pytest_collection_modifyitems(config, items): active_flavor = config.user_parameters.flavor - active_flavor_dir = FLAVOR_TESTS_DIR / active_flavor + allowed_dirs = set() + for test_dir, flavors in FLAVOR_TEST_DIRS.items(): + if active_flavor in flavors: + allowed_dirs.add(test_dir) deselected = [] selected = [] for item in items: test_path = py.path.local(item.fspath) if test_path.relto(FLAVOR_TESTS_DIR): - if not test_path.relto(active_flavor_dir): + if not any(test_path.relto(FLAVOR_TESTS_DIR / d) for d in allowed_dirs): deselected.append(item) continue selected.append(item) From b58c348140cb3ec0dfde233626ab6a72c52dd211 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Wed, 5 Aug 2026 13:18:52 +0200 Subject: [PATCH 03/16] add ansible, drop deb and python --- src/vars/flavors/satellite.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vars/flavors/satellite.yml b/src/vars/flavors/satellite.yml index 6890e0d18..304fddc02 100644 --- a/src/vars/flavors/satellite.yml +++ b/src/vars/flavors/satellite.yml @@ -4,9 +4,8 @@ flavor_features: - katello - content/ansible - content/container - - content/deb - - content/python - content/rpm + - ansible - remote-execution - rh-cloud - theme-satellite From 87e71f4c4af629bfc4c56cdbd75ee13c4b487f09 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Wed, 5 Aug 2026 13:23:43 +0200 Subject: [PATCH 04/16] use a boolean whether a flavor wants a message --- src/roles/post_install/tasks/message.yaml | 2 +- src/vars/flavors/katello.yml | 2 ++ src/vars/flavors/satellite.yml | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/roles/post_install/tasks/message.yaml b/src/roles/post_install/tasks/message.yaml index cfa393961..ebcc824cf 100644 --- a/src/roles/post_install/tasks/message.yaml +++ b/src/roles/post_install/tasks/message.yaml @@ -1,6 +1,6 @@ --- - name: Post install message - when: flavor in ['katello', 'satellite'] + when: post_install_message_enabled | default(false) ansible.builtin.debug: msg: - "{{ _post_install_url_msg }}" diff --git a/src/vars/flavors/katello.yml b/src/vars/flavors/katello.yml index 0b3e74cc9..9e63de142 100644 --- a/src/vars/flavors/katello.yml +++ b/src/vars/flavors/katello.yml @@ -23,3 +23,5 @@ health_checks_to_execute: - check_foreman_tasks - check_host_facts_count - check_duplicate_permissions + +post_install_message_enabled: true diff --git a/src/vars/flavors/satellite.yml b/src/vars/flavors/satellite.yml index 304fddc02..a54e75112 100644 --- a/src/vars/flavors/satellite.yml +++ b/src/vars/flavors/satellite.yml @@ -26,3 +26,5 @@ health_checks_to_execute: - check_foreman_tasks - check_host_facts_count - check_duplicate_permissions + +post_install_message_enabled: true From 45d67ddeeb19b71374ab05a53e87e8af6339e277 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Wed, 5 Aug 2026 13:29:55 +0200 Subject: [PATCH 05/16] don't hardcode flavors in backup test --- tests/backup_test.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/backup_test.py b/tests/backup_test.py index 09ef2959d..fbd38650d 100644 --- a/tests/backup_test.py +++ b/tests/backup_test.py @@ -8,9 +8,9 @@ @pytest.fixture(scope="module") -def expected_databases(enabled_features, flavor): +def expected_databases(enabled_features): """ - Determine expected databases based on flavor and enabled features. + Determine expected databases based on enabled features. Note: These are the logical 'name' values from database.yml (e.g., 'foreman', 'iop_advisor'). Dump filenames use the actual @@ -18,13 +18,14 @@ def expected_databases(enabled_features, flavor): """ databases = [] - # Katello and Satellite flavors have foreman, candlepin, and pulp - if flavor in ('katello', 'satellite'): - databases = ['foreman', 'candlepin', 'pulp'] + if 'foreman' in enabled_features: + databases.append('foreman') - # Foreman-proxy-content flavor only has pulp - elif flavor == 'foreman-proxy-content': - databases = ['pulp'] + if 'candlepin' in enabled_features: + databases.append('candlepin') + + if any([feature.startswith('content/') for feature in enabled_features]): + databases.append('pulp') # Add IOP databases if IOP feature is enabled if 'iop' in enabled_features: From 6a3d0a1b18528acf48818366cd374b00389d430b Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Wed, 5 Aug 2026 13:53:07 +0200 Subject: [PATCH 06/16] simplify flavor test dir overrides --- tests/conftest.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 076f13c72..c7df7a219 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,6 +19,9 @@ OBSAH_STATE = os.environ.get('OBSAH_STATE', '.var/lib/foremanctl') PARAMETERS_FILE = os.path.join(OBSAH_STATE, 'parameters.yaml') FLAVOR_TESTS_DIR = py.path.local(__file__).dirpath() / 'flavor' +FLAVOR_TESTS_DIR_OVERRIDES = { + 'satellite': 'katello', +} FOREMAN_PROXY_PORT = 8443 @@ -284,25 +287,17 @@ def pytest_configure(config): config.user_parameters = UserParameters(config) -FLAVOR_TEST_DIRS = { - 'katello': ['katello', 'satellite'], - 'foreman-proxy-content': ['foreman-proxy-content', 'capsule'], -} - - def pytest_collection_modifyitems(config, items): active_flavor = config.user_parameters.flavor - allowed_dirs = set() - for test_dir, flavors in FLAVOR_TEST_DIRS.items(): - if active_flavor in flavors: - allowed_dirs.add(test_dir) + # if there is an override, use that, otherwise use the flavor verbatim + active_flavor_dir = FLAVOR_TESTS_DIR / FLAVOR_TESTS_DIR_OVERRIDES.get(active_flavor, active_flavor) deselected = [] selected = [] for item in items: test_path = py.path.local(item.fspath) if test_path.relto(FLAVOR_TESTS_DIR): - if not any(test_path.relto(FLAVOR_TESTS_DIR / d) for d in allowed_dirs): + if not test_path.relto(active_flavor_dir): deselected.append(item) continue selected.append(item) From 5bd0dadf3776f807949ec86d6944bd58e91d470f Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Wed, 5 Aug 2026 14:22:46 +0200 Subject: [PATCH 07/16] enable hammer and foreman-proxy by default --- .github/workflows/test.yml | 2 -- src/vars/flavors/satellite.yml | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6aa2773c8..7fa57dc9b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -502,8 +502,6 @@ jobs: - name: Deploy features run: | ./foremanctl deploy \ - --add-feature hammer \ - --add-feature foreman-proxy \ --add-feature azure-rm \ --add-feature google \ --add-feature bmc diff --git a/src/vars/flavors/satellite.yml b/src/vars/flavors/satellite.yml index a54e75112..114285239 100644 --- a/src/vars/flavors/satellite.yml +++ b/src/vars/flavors/satellite.yml @@ -10,6 +10,8 @@ flavor_features: - rh-cloud - theme-satellite - webhooks + - hammer + - foreman-proxy checks_to_execute: - check_features From 9940cf32d38e07e6cb9ffa3ab3c5176374243ec5 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Wed, 5 Aug 2026 14:34:40 +0200 Subject: [PATCH 08/16] allow listing internal features for testing --- src/filter_plugins/foremanctl.py | 5 +++-- tests/conftest.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/filter_plugins/foremanctl.py b/src/filter_plugins/foremanctl.py index 2343be9cd..5f0f92feb 100644 --- a/src/filter_plugins/foremanctl.py +++ b/src/filter_plugins/foremanctl.py @@ -4,6 +4,7 @@ __metaclass__ = type +import os import pathlib import yaml @@ -15,7 +16,7 @@ with features_yaml.open() as features_file: FEATURE_MAP = yaml.safe_load(features_file) -# load additional feature files under features.d +# load additional feature files under features.d _features_d = _SRC_ROOT / 'features.d' if _features_d.is_dir(): for _overlay in sorted(_features_d.glob('*.yaml')): @@ -73,7 +74,7 @@ def list_all_features(enabled_features, only_enabled=False): enabled_list = [] available_list = [] for name, meta in FEATURE_MAP.items(): - if meta.get('internal', False): + if meta.get('internal', False) and not os.environ.get('FOREMANCTL_FEATURES_LIST_INTERNAL', '') == 'true': continue description = meta.get('description', '') if name in enabled_features: diff --git a/tests/conftest.py b/tests/conftest.py index c7df7a219..dd6c17536 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -35,7 +35,8 @@ def features(self): # FEATURE STATE DESCRIPTION # $feature enabled/available $description output = subprocess.check_output(['./foremanctl', 'features'], cwd=self._config.rootdir, - universal_newlines=True) + universal_newlines=True, + env=os.environ | {'FOREMANCTL_FEATURES_LIST_INTERNAL': 'true'}) lines = output.splitlines(keepends=False) # feature, status, description return [line.split(None, 2) for line in lines[1:]] From 6024c9922c4ca03bcfebd7a7f4a5eb2998bc8403 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Wed, 5 Aug 2026 15:34:48 +0200 Subject: [PATCH 09/16] add description for dynflow --- src/features.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/features.yaml b/src/features.yaml index 6c65c2ebf..f158fbeb6 100644 --- a/src/features.yaml +++ b/src/features.yaml @@ -57,6 +57,7 @@ ansible: dependencies: - remote-execution dynflow: + description: dynflow internal: true foreman_proxy: plugin_name: dynflow From 1c9b409214572453450a3ae576237e4f5e658c19 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Wed, 5 Aug 2026 16:48:43 +0200 Subject: [PATCH 10/16] force-enable pulp and candlepin features --- src/vars/flavors/foreman-proxy-content.yml | 1 + src/vars/flavors/katello.yml | 2 ++ src/vars/flavors/satellite.yml | 2 ++ 3 files changed, 5 insertions(+) diff --git a/src/vars/flavors/foreman-proxy-content.yml b/src/vars/flavors/foreman-proxy-content.yml index fbba03497..31c1cca19 100644 --- a/src/vars/flavors/foreman-proxy-content.yml +++ b/src/vars/flavors/foreman-proxy-content.yml @@ -1,5 +1,6 @@ flavor_features: - foreman-proxy + - pulp - content/rpm - content/deb - content/container diff --git a/src/vars/flavors/katello.yml b/src/vars/flavors/katello.yml index 9e63de142..f82e1bf49 100644 --- a/src/vars/flavors/katello.yml +++ b/src/vars/flavors/katello.yml @@ -2,6 +2,8 @@ flavor_features: - foreman - katello + - pulp + - candlepin - content/ansible - content/container - content/deb diff --git a/src/vars/flavors/satellite.yml b/src/vars/flavors/satellite.yml index 114285239..13b2ceca3 100644 --- a/src/vars/flavors/satellite.yml +++ b/src/vars/flavors/satellite.yml @@ -2,6 +2,8 @@ flavor_features: - foreman - katello + - pulp + - candlepin - content/ansible - content/container - content/rpm From 07eace340bde2170e1472c7dcbf46246fa93a956 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Wed, 5 Aug 2026 16:48:57 +0200 Subject: [PATCH 11/16] use pulp feature --- tests/backup_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/backup_test.py b/tests/backup_test.py index fbd38650d..ad1442275 100644 --- a/tests/backup_test.py +++ b/tests/backup_test.py @@ -24,7 +24,7 @@ def expected_databases(enabled_features): if 'candlepin' in enabled_features: databases.append('candlepin') - if any([feature.startswith('content/') for feature in enabled_features]): + if 'pulp' in enabled_features: databases.append('pulp') # Add IOP databases if IOP feature is enabled From d9672ee369ee764d6d134bc5c281dfe365192c24 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Thu, 6 Aug 2026 09:11:25 +0200 Subject: [PATCH 12/16] enable azure and google for sat --- .github/workflows/test.yml | 2 -- src/vars/flavors/satellite.yml | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7fa57dc9b..fbe5a30eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -502,8 +502,6 @@ jobs: - name: Deploy features run: | ./foremanctl deploy \ - --add-feature azure-rm \ - --add-feature google \ --add-feature bmc - name: Run tests run: | diff --git a/src/vars/flavors/satellite.yml b/src/vars/flavors/satellite.yml index 13b2ceca3..b34266b2a 100644 --- a/src/vars/flavors/satellite.yml +++ b/src/vars/flavors/satellite.yml @@ -8,6 +8,8 @@ flavor_features: - content/container - content/rpm - ansible + - azure-rm + - google - remote-execution - rh-cloud - theme-satellite From 4da5bcf8b877da14197eac94900b22fb1835eb02 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Thu, 6 Aug 2026 10:18:50 +0200 Subject: [PATCH 13/16] differentiate between user enabled and internally enabled in tests --- src/filter_plugins/foremanctl.py | 21 ++++++++++++++------- tests/conftest.py | 17 +++++++++++++---- tests/features_test.py | 4 ++-- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/filter_plugins/foremanctl.py b/src/filter_plugins/foremanctl.py index 5f0f92feb..24988fcac 100644 --- a/src/filter_plugins/foremanctl.py +++ b/src/filter_plugins/foremanctl.py @@ -73,18 +73,25 @@ def available_foreman_plugins(_value): def list_all_features(enabled_features, only_enabled=False): enabled_list = [] available_list = [] + list_internal = os.environ.get('FOREMANCTL_FEATURES_LIST_INTERNAL', '') == 'true' for name, meta in FEATURE_MAP.items(): - if meta.get('internal', False) and not os.environ.get('FOREMANCTL_FEATURES_LIST_INTERNAL', '') == 'true': + internal = meta.get('internal', False) + if internal and not list_internal: continue description = meta.get('description', '') if name in enabled_features: - enabled_list.append((name, 'enabled', description)) + enabled_list.append((name, 'enabled', internal, description)) elif not only_enabled: - available_list.append((name, 'available', description)) - - output = [f"{'FEATURE':<25} {'STATE':<12} DESCRIPTION"] - for name, state, description in enabled_list + available_list: - output.append(f"{name:<25} {state:<12} {description}") + available_list.append((name, 'available', internal, description)) + + if not list_internal: + output = [f"{'FEATURE':<25} {'STATE':<12} DESCRIPTION"] + for name, state, _internal, description in enabled_list + available_list: + output.append(f"{name:<25} {state:<12} {description}") + else: + output = [f"{'FEATURE':<25} {'STATE':<12} {'INTERNAL':<8} DESCRIPTION"] + for name, state, internal, description in enabled_list + available_list: + output.append(f"{name:<25} {state:<12} {internal:<8} {description}") return "\n".join(output) diff --git a/tests/conftest.py b/tests/conftest.py index dd6c17536..4d7bd78d5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -38,16 +38,20 @@ def features(self): universal_newlines=True, env=os.environ | {'FOREMANCTL_FEATURES_LIST_INTERNAL': 'true'}) lines = output.splitlines(keepends=False) - # feature, status, description - return [line.split(None, 2) for line in lines[1:]] + # feature, status, internal, description + return [line.split(None, 3) for line in lines[1:]] @cached_property def available_features(self): - return set(feature for feature, _status, _desc in self.features) + return set(feature for feature, _status, internal, _desc in self.features if internal == '0') @cached_property def enabled_features(self): - return set(feature for feature, status, _desc in self.features if status == 'enabled') + return set(feature for feature, status, internal, _desc in self.features if status == 'enabled') + + @cached_property + def user_enabled_features(self): + return set(feature for feature, status, internal, _desc in self.features if status == 'enabled' and internal == '0') @cached_property def flavor(self): @@ -70,6 +74,11 @@ def enabled_features(pytestconfig): return pytestconfig.user_parameters.enabled_features +@pytest.fixture(scope="module") +def user_enabled_features(pytestconfig): + return pytestconfig.user_parameters.user_enabled_features + + @pytest.fixture(scope="module") def available_features(pytestconfig): return pytestconfig.user_parameters.available_features diff --git a/tests/features_test.py b/tests/features_test.py index b4aa396c7..b0d6f7dcd 100644 --- a/tests/features_test.py +++ b/tests/features_test.py @@ -14,13 +14,13 @@ def test_foremanctl_features(available_features): assert feature in result.stdout, f"Expected feature '{feature}' in output" -def test_foremanctl_features_list_enabled(enabled_features): +def test_foremanctl_features_list_enabled(user_enabled_features): command = ['./foremanctl', 'features', '--list-enabled'] result = subprocess.run(command, capture_output=True, text=True) assert result.returncode == 0 - for feature in enabled_features: + for feature in user_enabled_features: assert feature in result.stdout, f"Expected feature '{feature}' in output" From 85435018021edea2f6518128c8df3b629bb5563c Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Thu, 6 Aug 2026 11:14:40 +0200 Subject: [PATCH 14/16] pypi-endpoint only with python feature --- tests/httpd_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/httpd_test.py b/tests/httpd_test.py index 85cf5011b..bfd4e5f29 100644 --- a/tests/httpd_test.py +++ b/tests/httpd_test.py @@ -67,6 +67,7 @@ def test_https_pulp_auth(curl_request): assert cmd.stdout == '200' +@pytest.mark.feature('content/python') def test_https_pypi_endpoint(curl_request): cmd = curl_request("pypi/test/", return_body=True) assert cmd.succeeded From f5676b4aa0550f1c6c77acf72d708220769b1193 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Thu, 6 Aug 2026 11:45:59 +0200 Subject: [PATCH 15/16] only allow flavor=satellite with an override --- .github/workflows/test.yml | 2 ++ src/playbooks/deploy/metadata.obsah.yaml | 2 +- vendor_overrides/satellite/deploy/metadata.obsah.yaml | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 vendor_overrides/satellite/deploy/metadata.obsah.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fbe5a30eb..b64ac772a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -482,6 +482,8 @@ jobs: run: pip install --upgrade ansible-core - name: Setup environment run: ./setup-environment + - name: Apply vendor overrides + run: cp -a vendor_overrides/satellite src/playbooks/_vendor_overrides - name: Start VMs run: | ./forge vms start --vms "quadlet client" diff --git a/src/playbooks/deploy/metadata.obsah.yaml b/src/playbooks/deploy/metadata.obsah.yaml index 8c68d40e6..ca9bfed5c 100644 --- a/src/playbooks/deploy/metadata.obsah.yaml +++ b/src/playbooks/deploy/metadata.obsah.yaml @@ -7,7 +7,6 @@ variables: help: Base flavor to use in this deployment. choices: - katello - - satellite external_authentication: help: External authentication method to use choices: @@ -27,3 +26,4 @@ variables: include: - _flavor_features - _flavors/katello + - _vendor_overrides/deploy diff --git a/vendor_overrides/satellite/deploy/metadata.obsah.yaml b/vendor_overrides/satellite/deploy/metadata.obsah.yaml new file mode 100644 index 000000000..332f5489b --- /dev/null +++ b/vendor_overrides/satellite/deploy/metadata.obsah.yaml @@ -0,0 +1,5 @@ +--- +variables: + flavor: + choices: + - satellite From e09da294531e2f0d1ed63ec47b8d0b93235ff096 Mon Sep 17 00:00:00 2001 From: Evgeni Golov Date: Thu, 6 Aug 2026 11:55:33 +0200 Subject: [PATCH 16/16] allow internal features in markers --- tests/conftest.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 4d7bd78d5..4802e8165 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -41,6 +41,10 @@ def features(self): # feature, status, internal, description return [line.split(None, 3) for line in lines[1:]] + @cached_property + def all_available_features(self): + return set(feature for feature, _status, _internal, _desc in self.features) + @cached_property def available_features(self): return set(feature for feature, _status, internal, _desc in self.features if internal == '0') @@ -331,7 +335,7 @@ def pytest_collection_modifyitems(config, items): def pytest_runtest_setup(item): feature_markers = set(mark.args[0] for mark in item.iter_markers(name="feature")) if feature_markers: - invalid_features = feature_markers - item.config.user_parameters.available_features + invalid_features = feature_markers - item.config.user_parameters.all_available_features if invalid_features: raise pytest.PytestConfigWarning(f"Invalid feature(s) {invalid_features!r} on {item}") missing = feature_markers - item.config.user_parameters.enabled_features