diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 03ee040b8..b64ac772a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -465,6 +465,74 @@ 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: Apply vendor overrides + run: cp -a vendor_overrides/satellite src/playbooks/_vendor_overrides + - 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 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 +542,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 73a8f3a60..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 @@ -90,3 +91,8 @@ registration: plugin_name: registration dependencies: - templates +theme-satellite: + description: Satellite theme for Foreman + internal: true + foreman: + plugin_name: foreman_theme_satellite diff --git a/src/filter_plugins/foremanctl.py b/src/filter_plugins/foremanctl.py index 2343be9cd..24988fcac 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')): @@ -72,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): + 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/src/playbooks/deploy/metadata.obsah.yaml b/src/playbooks/deploy/metadata.obsah.yaml index 0fdf1a242..ca9bfed5c 100644 --- a/src/playbooks/deploy/metadata.obsah.yaml +++ b/src/playbooks/deploy/metadata.obsah.yaml @@ -26,3 +26,4 @@ variables: include: - _flavor_features - _flavors/katello + - _vendor_overrides/deploy diff --git a/src/roles/post_install/tasks/message.yaml b/src/roles/post_install/tasks/message.yaml index 581f4fba0..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'] + when: post_install_message_enabled | default(false) ansible.builtin.debug: msg: - "{{ _post_install_url_msg }}" 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 0b3e74cc9..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 @@ -23,3 +25,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 new file mode 100644 index 000000000..b34266b2a --- /dev/null +++ b/src/vars/flavors/satellite.yml @@ -0,0 +1,36 @@ +--- +flavor_features: + - foreman + - katello + - pulp + - candlepin + - content/ansible + - content/container + - content/rpm + - ansible + - azure-rm + - google + - remote-execution + - rh-cloud + - theme-satellite + - webhooks + - hammer + - foreman-proxy + +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 + +post_install_message_enabled: true diff --git a/tests/backup_test.py b/tests/backup_test.py index ff76d15c8..ad1442275 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 flavor has foreman, candlepin, and pulp - if flavor == 'katello': - 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 'pulp' in enabled_features: + databases.append('pulp') # Add IOP databases if IOP feature is enabled if 'iop' in enabled_features: diff --git a/tests/conftest.py b/tests/conftest.py index b52d0f178..4802e8165 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 @@ -32,18 +35,27 @@ 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:]] + # 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, _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): @@ -66,6 +78,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 @@ -286,7 +303,8 @@ def pytest_configure(config): def pytest_collection_modifyitems(config, items): active_flavor = config.user_parameters.flavor - active_flavor_dir = FLAVOR_TESTS_DIR / active_flavor + # 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 = [] @@ -317,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 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" 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 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