From 154bcff8accb821cbcad0c314014879e9bbf47e8 Mon Sep 17 00:00:00 2001 From: Arvind Jangir Date: Wed, 11 Mar 2026 13:34:05 +0530 Subject: [PATCH 1/2] add check to avoid deployment with invalid features --- src/filter_plugins/foremanctl.py | 5 +++++ src/roles/check_features/tasks/main.yaml | 15 +++++++++++++++ src/roles/checks/tasks/main.yml | 1 + 3 files changed, 21 insertions(+) create mode 100644 src/roles/check_features/tasks/main.yaml diff --git a/src/filter_plugins/foremanctl.py b/src/filter_plugins/foremanctl.py index fb88c9cac..8195ce728 100644 --- a/src/filter_plugins/foremanctl.py +++ b/src/filter_plugins/foremanctl.py @@ -73,6 +73,10 @@ def list_all_features(enabled_features, only_enabled=False): return "\n".join(output) +def invalid_features(features): + """Return a list of unknown features not defined in features.yaml.""" + return [feature for feature in features if feature not in FEATURE_MAP] + def foreman_proxy_plugins(value): dependencies = list(get_dependencies(filter_features(value))) plugins = [FEATURE_MAP.get(feature, {}).get('foreman_proxy', {}).get('plugin_name') for feature in filter_features(value + dependencies)] @@ -94,4 +98,5 @@ def filters(self): 'features_to_foreman_proxy_plugins': foreman_proxy_plugins, 'available_foreman_proxy_plugins': available_foreman_proxy_plugins, 'list_all_features': list_all_features, + 'invalid_features': invalid_features, } diff --git a/src/roles/check_features/tasks/main.yaml b/src/roles/check_features/tasks/main.yaml new file mode 100644 index 000000000..2e5d1a7a8 --- /dev/null +++ b/src/roles/check_features/tasks/main.yaml @@ -0,0 +1,15 @@ +--- +- name: Validate requested features + ansible.builtin.assert: + that: + - found_invalid_features | length == 0 + fail_msg: | + ERROR: Unknown feature(s) requested: {{ found_invalid_features | join(', ') }} + + To remove them, run: + foremanctl deploy {% for feature in found_invalid_features %}--remove-feature={{ feature }} {% endfor %} + + Run 'foremanctl features' to list all available features. + vars: + found_invalid_features: "{{ features | invalid_features }}" + when: features | length > 0 diff --git a/src/roles/checks/tasks/main.yml b/src/roles/checks/tasks/main.yml index cc1f44040..90dbf9e1d 100644 --- a/src/roles/checks/tasks/main.yml +++ b/src/roles/checks/tasks/main.yml @@ -2,6 +2,7 @@ - name: Execute checks ansible.builtin.include_tasks: execute_check.yml loop: + - check_features - check_hostname - check_database_connection - check_system_requirements From 9d54ecc6c2518c86474c6f95737ff145785afcd2 Mon Sep 17 00:00:00 2001 From: Arvind Jangir Date: Tue, 24 Mar 2026 18:02:47 +0530 Subject: [PATCH 2/2] add test for check_features role --- tests/features_test.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/features_test.py b/tests/features_test.py index 92309c950..260aa15e5 100644 --- a/tests/features_test.py +++ b/tests/features_test.py @@ -20,3 +20,13 @@ def test_foremanctl_features_list_enabled(): assert 'enabled' in result.stdout assert 'available' not in result.stdout + +def test_invalid_feature_rejected(): + command = ['./foremanctl', 'deploy', '--add-feature', 'invalid-feature'] + result = subprocess.run(command, capture_output=True, text=True) + + assert result.returncode == 2 + + assert 'Unknown feature(s) requested: invalid-feature' in result.stdout + assert '--remove-feature=invalid' in result.stdout + assert "Run 'foremanctl features' to list all available features." in result.stdout