diff --git a/.config/dictionary.txt b/.config/dictionary.txt index f161e5ed..1a2affcc 100644 --- a/.config/dictionary.txt +++ b/.config/dictionary.txt @@ -27,9 +27,11 @@ myuser netcommon nilashish notesdir +PYCMD pydoclint rulebook rulebooks +sshpass sysargs templated templating diff --git a/src/ansible_creator/arg_parser.py b/src/ansible_creator/arg_parser.py index bd09d685..e3c4afaa 100644 --- a/src/ansible_creator/arg_parser.py +++ b/src/ansible_creator/arg_parser.py @@ -233,6 +233,7 @@ def _add_resource(self, subparser: SubParser[ArgumentParser]) -> None: self._add_resource_devcontainer(subparser=subparser) self._add_resource_devfile(subparser=subparser) self._add_resource_role(subparser=subparser) + self._add_resource_execution_env(subparser=subparser) def _add_resource_devcontainer(self, subparser: SubParser[ArgumentParser]) -> None: """Add devcontainer files to an existing Ansible project. @@ -312,6 +313,29 @@ def _add_resource_role(self, subparser: SubParser[ArgumentParser]) -> None: ) self._add_args_common(parser) + def _add_resource_execution_env(self, subparser: SubParser[ArgumentParser]) -> None: + """Add execution environment sample file to an existing path. + + Args: + subparser: The subparser to add execution environment file to + """ + parser = subparser.add_parser( + "execution-environment", + help="Add a sample execution-environment.yml file to an existing path.", + formatter_class=CustomHelpFormatter, + ) + + parser.add_argument( + "path", + default="./", + metavar="path", + help="The destination directory for the execution environment file. " + "The default is the current working directory.", + ) + + self._add_overwrite(parser) + self._add_args_common(parser) + def _add_plugin(self, subparser: SubParser[ArgumentParser]) -> None: """Add a plugin to an Ansible project. diff --git a/src/ansible_creator/constants.py b/src/ansible_creator/constants.py index b8c9b049..95b95f01 100644 --- a/src/ansible_creator/constants.py +++ b/src/ansible_creator/constants.py @@ -14,6 +14,7 @@ ), "DEV_FILE_IMAGE": "ghcr.io/ansible/ansible-workspace-env-reference:latest", "RECOMMENDED_EXTENSIONS": ["redhat.ansible", "redhat.vscode-redhat-account"], + "EXECUTION_ENVIRONMENT_DEFAULT_IMAGE": "quay.io/fedora/fedora:41", } MIN_COLLECTION_NAME_LEN = 2 diff --git a/src/ansible_creator/resources/common/execution-environment/execution-environment.yml.j2 b/src/ansible_creator/resources/common/execution-environment/execution-environment.yml.j2 new file mode 100644 index 00000000..3082343e --- /dev/null +++ b/src/ansible_creator/resources/common/execution-environment/execution-environment.yml.j2 @@ -0,0 +1,34 @@ +--- +version: 3 + +images: + base_image: + name: {{ execution_environment_image }} + +dependencies: + ansible_core: + package_pip: ansible-core + + ansible_runner: + package_pip: ansible-runner + + system: + - openssh-clients + - sshpass + + python: + - requests + - boto3 + + galaxy: + collections: + - name: ansible.posix + - name: ansible.utils + +additional_build_steps: + append_base: + - RUN $PYCMD -m pip install -U pip + +options: + tags: + - ansible_sample_ee diff --git a/src/ansible_creator/subcommands/add.py b/src/ansible_creator/subcommands/add.py index 153041b0..32a32fe0 100644 --- a/src/ansible_creator/subcommands/add.py +++ b/src/ansible_creator/subcommands/add.py @@ -105,7 +105,8 @@ def _resource_scaffold(self) -> None: template_data = self._get_devfile_template_data() elif self._resource_type == "devcontainer": template_data = self._get_devcontainer_template_data() - + elif self._resource_type == "execution-environment": + template_data = self._get_ee_template_data() else: msg = f"Unsupported resource type: {self._resource_type}" raise CreatorError(msg) @@ -281,3 +282,14 @@ def _get_plugin_template_data(self) -> TemplateData: plugin_name=self._plugin_name, creator_version=self._creator_version, ) + + def _get_ee_template_data(self) -> TemplateData: + """Get the template data for lookup plugin. + + Returns: + TemplateData: Data required for templating the lookup plugin. + """ + return TemplateData( + resource_type=self._resource_type, + creator_version=self._creator_version, + ) diff --git a/src/ansible_creator/types.py b/src/ansible_creator/types.py index 38e64ff8..ce26d4fe 100644 --- a/src/ansible_creator/types.py +++ b/src/ansible_creator/types.py @@ -27,6 +27,7 @@ class TemplateData: dev_file_image: The devfile image. dev_file_name: The unique name entry in devfile. namespace: The namespace of the collection. + execution_environment_image: The execution environment image. recommended_extensions: A list of recommended VsCode extensions. """ @@ -40,6 +41,9 @@ class TemplateData: dev_file_image: Sequence[str] = GLOBAL_TEMPLATE_VARS["DEV_FILE_IMAGE"] dev_file_name: str = "" namespace: str = "" + execution_environment_image: Sequence[str] = GLOBAL_TEMPLATE_VARS[ + "EXECUTION_ENVIRONMENT_DEFAULT_IMAGE" + ] recommended_extensions: Sequence[str] = field( default_factory=lambda: GLOBAL_TEMPLATE_VARS["RECOMMENDED_EXTENSIONS"], ) diff --git a/tests/fixtures/common/execution-environment/execution-environment.yml b/tests/fixtures/common/execution-environment/execution-environment.yml new file mode 100644 index 00000000..12f70694 --- /dev/null +++ b/tests/fixtures/common/execution-environment/execution-environment.yml @@ -0,0 +1,34 @@ +--- +version: 3 + +images: + base_image: + name: quay.io/fedora/fedora:41 + +dependencies: + ansible_core: + package_pip: ansible-core + + ansible_runner: + package_pip: ansible-runner + + system: + - openssh-clients + - sshpass + + python: + - requests + - boto3 + + galaxy: + collections: + - name: ansible.posix + - name: ansible.utils + +additional_build_steps: + append_base: + - RUN $PYCMD -m pip install -U pip + +options: + tags: + - ansible_sample_ee diff --git a/tests/units/test_add.py b/tests/units/test_add.py index 52a98ed5..59d36032 100644 --- a/tests/units/test_add.py +++ b/tests/units/test_add.py @@ -696,3 +696,68 @@ def mock_check_collection_path() -> None: with pytest.raises(CreatorError) as exc_info: add.run() assert "Unsupported plugin type: unsupported_type" in str(exc_info.value) + + +def test_run_success_add_execution_env( + capsys: pytest.CaptureFixture[str], + tmp_path: Path, + cli_args: ConfigDict, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test Add.run() for adding a execution-environment sample file. + + Successfully adds execution-environment.yml sample file to path. + + Args: + capsys: Pytest fixture to capture stdout and stderr. + tmp_path: Temporary directory path. + cli_args: Dictionary, partial Add class object. + monkeypatch: Pytest monkeypatch fixture. + """ + # Set the resource_type to execution-environment + cli_args["resource_type"] = "execution-environment" + add = Add( + Config(**cli_args), + ) + add.run() + result = capsys.readouterr().out + assert re.search("Note: Resource added to", result) is not None + + # Verify the generated execution-environment file match the expected structure + expected_ee_file = tmp_path / "execution-environment.yml" + effective_ee_file = ( + FIXTURES_DIR / "common" / "execution-environment" / "execution-environment.yml" + ) + + cmp_result = cmp(expected_ee_file, effective_ee_file, shallow=False) + assert cmp_result + + # Test for overwrite prompt and failure with no overwrite option + conflict_file = tmp_path / "execution-environment.yml" + conflict_file.write_text('{ "version": "1" }') + + # expect a CreatorError when the response to overwrite is no. + monkeypatch.setattr("builtins.input", lambda _: "n") + fail_msg = ( + "The destination directory contains files that will be overwritten." + " Please re-run ansible-creator with --overwrite to continue." + ) + with pytest.raises( + CreatorError, + match=fail_msg, + ): + add.run() + + # expect a warning followed by execution-environment resource creation msg + # when response to overwrite is yes. + monkeypatch.setattr("builtins.input", lambda _: "y") + add.run() + result = capsys.readouterr().out + assert ( + re.search( + "already exists", + result, + ) + is not None + ), result + assert re.search("Note: Resource added to", result) is not None