From 182d0fe9d71422d3c9396f4d1cb03c12c1116b68 Mon Sep 17 00:00:00 2001 From: abhikdps Date: Wed, 13 Nov 2024 17:08:21 +0530 Subject: [PATCH 01/15] Scaffold lookup plugin through add subcommand --- src/ansible_creator/arg_parser.py | 3 +- src/ansible_creator/config.py | 2 + .../plugins/lookup/__init__.py.j2 | 0 .../plugins/lookup/hello_world.py.j2 | 43 +++++++++ src/ansible_creator/subcommands/add.py | 92 ++++++++++++++++++- src/ansible_creator/types.py | 2 + 6 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 src/ansible_creator/resources/collection_project/plugins/lookup/__init__.py.j2 create mode 100644 src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 diff --git a/src/ansible_creator/arg_parser.py b/src/ansible_creator/arg_parser.py index 7483c8dd..af9ac149 100644 --- a/src/ansible_creator/arg_parser.py +++ b/src/ansible_creator/arg_parser.py @@ -39,7 +39,7 @@ "add resource role", "add plugin action", "add plugin filter", - "add plugin lookup", + # "add plugin lookup", ) @@ -366,6 +366,7 @@ def _add_plugin_lookup(self, subparser: SubParser[ArgumentParser]) -> None: formatter_class=CustomHelpFormatter, ) self._add_args_common(parser) + self._add_overwrite(parser) self._add_args_plugin_common(parser) def _add_overwrite(self, parser: ArgumentParser) -> None: diff --git a/src/ansible_creator/config.py b/src/ansible_creator/config.py index f0874baf..b0ac40f8 100644 --- a/src/ansible_creator/config.py +++ b/src/ansible_creator/config.py @@ -31,6 +31,7 @@ class Config: collection_name: The name of the collection. namespace: The namespace for the collection. resource_type: The type of resource to be scaffolded. + plugin_type: The type of plugin to be scaffolded. type: The type of the project for which the resource is being scaffolded. path: The file path where the resource should be added. """ @@ -47,6 +48,7 @@ class Config: collection_name: str | None = None namespace: str = "" resource_type: str = "" + plugin_type: str = "" type: str = "" path: str = "" diff --git a/src/ansible_creator/resources/collection_project/plugins/lookup/__init__.py.j2 b/src/ansible_creator/resources/collection_project/plugins/lookup/__init__.py.j2 new file mode 100644 index 00000000..e69de29b diff --git a/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 new file mode 100644 index 00000000..584eec2e --- /dev/null +++ b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 @@ -0,0 +1,43 @@ +"""A hello-world lookup plugin in {{ namespace }}.{{ collection_name }}.""" + +from __future__ import absolute_import, annotations, division, print_function + + +__metaclass__ = type # pylint: disable=C0103 + +DOCUMENTATION = """ + name: hello_world + author: {{ namespace | capitalize }} {{ collection_name | capitalize }} + version_added: "1.0.0" + short_description: Demo lookup plugin that returns a Hello message. + description: + - This is a demo lookup plugin designed to return Hello message. + options: + name: + description: Value specified here is appended to the Hello message. + type: str +""" + +EXAMPLES = """ +# hello_world lookup example +{% raw %} +- name: Display a hello message + ansible.builtin.debug: + msg: "{{ lookup('{{ namespace }}.{{ collection_name }}.hello_world') }}" +""" + +RETURN = """ +_raw: + description: Returns a Hello message with the specified name + type: list + elements: string + sample: ["Hello, World!"] +""" + +from ansible.errors import AnsibleLookupError +from ansible.plugins.lookup import LookupBase + +class LookupModule(LookupBase): + def run(self, terms, variables=None, **kwargs): + # Return a simple Hello, World message + return ["Hello, World!"] \ No newline at end of file diff --git a/src/ansible_creator/subcommands/add.py b/src/ansible_creator/subcommands/add.py index b5f48b9e..8f2cb3ab 100644 --- a/src/ansible_creator/subcommands/add.py +++ b/src/ansible_creator/subcommands/add.py @@ -31,7 +31,9 @@ def __init__( config: App configuration object. """ self._resource_type: str = config.resource_type + self._plugin_type: str = config.plugin_type self._resource_id: str = f"common.{self._resource_type}" + self._plugin_id: str = f"collection_project.plugins.{self._plugin_type}" self._add_path: Path = Path(config.path) self._force = config.force self._overwrite = config.overwrite @@ -45,8 +47,10 @@ def run(self) -> None: """Start scaffolding the resource file.""" self._check_add_path() self.output.debug(msg=f"final collection path set to {self._add_path}") - - self._scaffold() + if self._resource_type: + self._resource_scaffold() + elif self._plugin_type: + self._plugin_scaffold() def _check_add_path(self) -> None: """Validate the provided add path. @@ -68,7 +72,7 @@ def unique_name_in_devfile(self) -> str: final_uuid = str(uuid.uuid4())[:8] return f"{final_name}-{final_uuid}" - def _scaffold(self) -> None: + def _resource_scaffold(self) -> None: """Scaffold the specified resource file based on the resource type. Raises: @@ -84,9 +88,9 @@ def _scaffold(self) -> None: msg = f"Unsupported resource type: {self._resource_type}" raise CreatorError(msg) - self._perform_scaffold(template_data) + self._perform_resource_scaffold(template_data) - def _perform_scaffold(self, template_data: TemplateData) -> None: + def _perform_resource_scaffold(self, template_data: TemplateData) -> None: """Perform the actual scaffolding process using the provided template data. Args: @@ -136,6 +140,74 @@ def _perform_scaffold(self, template_data: TemplateData) -> None: self.output.note(f"Resource added to {self._add_path}") + def _plugin_scaffold(self) -> None: + """Scaffold the specified plugin file based on the plugin type. + + Raises: + CreatorError: If unsupported plugin type is given. + """ + self.output.debug(f"Started copying {self._project} plugin to destination") + + # Call the appropriate scaffolding function based on the plugin type + if self._plugin_type == "lookup": + template_data = self._get_lookup_plugin_template_data() + + else: + msg = f"Unsupported plugin type: {self._plugin_type}" + raise CreatorError(msg) + + self._perform_plugin_scaffold(template_data) + + def _perform_plugin_scaffold(self, template_data: TemplateData) -> None: + """Perform the actual scaffolding process using the provided template data. + + Args: + template_data: TemplateData + + Raises: + CreatorError: If there are conflicts and overwriting is not allowed, or if the + destination directory contains files that will be overwritten. + """ + walker = Walker( + resources=(f"collection_project.plugins.{self._plugin_type}",), + resource_id=self._plugin_id, + dest=self._add_path, + output=self.output, + template_data=template_data, + templar=self.templar, + ) + paths = walker.collect_paths() + copier = Copier(output=self.output) + + if self._no_overwrite: + msg = "The flag `--no-overwrite` restricts overwriting." + if paths.has_conflicts(): + msg += ( + "\nThe destination directory contains files that can be overwritten." + "\nPlease re-run ansible-creator with --overwrite to continue." + ) + raise CreatorError(msg) + + if not paths.has_conflicts() or self._force or self._overwrite: + copier.copy_containers(paths) + self.output.note(f"Plugin added to {self._add_path}") + return + + if not self._overwrite: + question = ( + "Files in the destination directory will be overwritten. Do you want to proceed?" + ) + if ask_yes_no(question): + copier.copy_containers(paths) + else: + msg = ( + "The destination directory contains files that will be overwritten." + " Please re-run ansible-creator with --overwrite to continue." + ) + raise CreatorError(msg) + + self.output.note(f"Plugin added to {self._add_path}") + def _get_devfile_template_data(self) -> TemplateData: """Get the template data for devfile resources. @@ -147,3 +219,13 @@ def _get_devfile_template_data(self) -> TemplateData: creator_version=self._creator_version, dev_file_name=self.unique_name_in_devfile(), ) + + def _get_lookup_plugin_template_data(self) -> TemplateData: + """Get the template data for lookup plugin. + + Returns: + TemplateData: Data required for templating the lookup plugin. + """ + return TemplateData( + plugin_type=self._plugin_type, + ) diff --git a/src/ansible_creator/types.py b/src/ansible_creator/types.py index bbe9691d..79e00677 100644 --- a/src/ansible_creator/types.py +++ b/src/ansible_creator/types.py @@ -18,6 +18,7 @@ class TemplateData: Attributes: resource_type: The type of resource to be scaffolded. + plugin_type: The type of plugin to be scaffolded. additions: A dictionary containing additional data to add to the gitignore. collection_name: The name of the collection. creator_version: The version of the creator. @@ -29,6 +30,7 @@ class TemplateData: """ resource_type: str = "" + plugin_type: str = "" additions: dict[str, dict[str, dict[str, str | bool]]] = field(default_factory=dict) collection_name: str = "" creator_version: str = "" From 417daa27333364676daab26ebd91c8a9dc3468b7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:40:30 +0000 Subject: [PATCH 02/15] chore: auto fixes from pre-commit.com hooks --- .../collection_project/plugins/lookup/hello_world.py.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 index 584eec2e..cf615964 100644 --- a/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 +++ b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 @@ -40,4 +40,4 @@ from ansible.plugins.lookup import LookupBase class LookupModule(LookupBase): def run(self, terms, variables=None, **kwargs): # Return a simple Hello, World message - return ["Hello, World!"] \ No newline at end of file + return ["Hello, World!"] From 9cf866cca099dbc322a8f7e46522d61504f56689 Mon Sep 17 00:00:00 2001 From: abhikdps Date: Wed, 13 Nov 2024 17:28:20 +0530 Subject: [PATCH 03/15] Missing end of directive --- .../collection_project/plugins/lookup/hello_world.py.j2 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 index cf615964..9b12b891 100644 --- a/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 +++ b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 @@ -2,7 +2,6 @@ from __future__ import absolute_import, annotations, division, print_function - __metaclass__ = type # pylint: disable=C0103 DOCUMENTATION = """ @@ -39,5 +38,9 @@ from ansible.plugins.lookup import LookupBase class LookupModule(LookupBase): def run(self, terms, variables=None, **kwargs): - # Return a simple Hello, World message + """Returns a simple Hello, World message. + + Returns: + list: The Hello message as a list. + """ return ["Hello, World!"] From 3703cefed21aa286abc76e65c062936b26ff0963 Mon Sep 17 00:00:00 2001 From: abhikdps Date: Wed, 13 Nov 2024 17:47:41 +0530 Subject: [PATCH 04/15] Missing end of raw directive --- .../collection_project/plugins/lookup/hello_world.py.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 index 9b12b891..82d8e4a6 100644 --- a/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 +++ b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 @@ -22,7 +22,7 @@ EXAMPLES = """ {% raw %} - name: Display a hello message ansible.builtin.debug: - msg: "{{ lookup('{{ namespace }}.{{ collection_name }}.hello_world') }}" + msg: "{{ lookup('{%- endraw %}{{ namespace }}.{{ collection_name }}.hello_world') }}" """ RETURN = """ From 028da1d49b51be389ef21831999295118cb8344d Mon Sep 17 00:00:00 2001 From: abhikdps Date: Wed, 13 Nov 2024 18:56:41 +0530 Subject: [PATCH 05/15] Fix CI errors --- .../plugins/lookup/hello_world.py.j2 | 12 +++-- .../testcol/plugins/lookup/__init__.py | 0 .../testcol/plugins/lookup/hello_world.py | 52 +++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 tests/fixtures/collection/testorg/testcol/plugins/lookup/__init__.py create mode 100644 tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py diff --git a/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 index 82d8e4a6..a33ffb68 100644 --- a/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 +++ b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 @@ -1,6 +1,7 @@ """A hello-world lookup plugin in {{ namespace }}.{{ collection_name }}.""" from __future__ import absolute_import, annotations, division, print_function +from ansible.plugins.lookup import LookupBase __metaclass__ = type # pylint: disable=C0103 @@ -33,13 +34,18 @@ _raw: sample: ["Hello, World!"] """ -from ansible.errors import AnsibleLookupError -from ansible.plugins.lookup import LookupBase class LookupModule(LookupBase): - def run(self, terms, variables=None, **kwargs): + """lookup plugin.""" + + def run(self, terms: list, variables: dict = None, **kwargs) -> list: """Returns a simple Hello, World message. + Parameters: + terms: A list of terms passed to the function. + variables: Additional variables for processing. + **kwargs: Arbitrary keyword arguments. + Returns: list: The Hello message as a list. """ diff --git a/tests/fixtures/collection/testorg/testcol/plugins/lookup/__init__.py b/tests/fixtures/collection/testorg/testcol/plugins/lookup/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py b/tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py new file mode 100644 index 00000000..1a408747 --- /dev/null +++ b/tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py @@ -0,0 +1,52 @@ +"""A hello-world lookup plugin in testorg.testcol.""" + +from __future__ import absolute_import, annotations, division, print_function +from ansible.plugins.lookup import LookupBase + +__metaclass__ = type # pylint: disable=C0103 + +DOCUMENTATION = """ + name: hello_world + author: Testorg Testcol + version_added: "1.0.0" + short_description: Demo lookup plugin that returns a Hello message. + description: + - This is a demo lookup plugin designed to return Hello message. + options: + name: + description: Value specified here is appended to the Hello message. + type: str +""" + +EXAMPLES = """ +# hello_world lookup example + +- name: Display a hello message + ansible.builtin.debug: + msg: "{{ lookup('testorg.testcol.hello_world') }}" +""" + +RETURN = """ +_raw: + description: Returns a Hello message with the specified name + type: list + elements: string + sample: ["Hello, World!"] +""" + + +class LookupModule(LookupBase): + """lookup plugin.""" + + def run(self, terms: list, variables: dict = None, **kwargs) -> list: + """Returns a simple Hello, World message. + + Parameters: + terms: A list of terms passed to the function. + variables: Additional variables for processing. + **kwargs: Arbitrary keyword arguments. + + Returns: + list: The Hello message as a list. + """ + return ["Hello, World!"] From 03300562eb1bc20c690a57adc31ebe764301f44f Mon Sep 17 00:00:00 2001 From: abhikdps Date: Thu, 14 Nov 2024 13:56:31 +0530 Subject: [PATCH 06/15] Template changes --- src/ansible_creator/config.py | 2 + .../plugins/lookup/hello_world.py.j2 | 92 +++++++++++++------ src/ansible_creator/subcommands/add.py | 3 + src/ansible_creator/types.py | 2 + .../testcol/plugins/lookup/hello_world.py | 84 +++++++++++------ 5 files changed, 126 insertions(+), 57 deletions(-) diff --git a/src/ansible_creator/config.py b/src/ansible_creator/config.py index b0ac40f8..5aaff1e4 100644 --- a/src/ansible_creator/config.py +++ b/src/ansible_creator/config.py @@ -31,6 +31,7 @@ class Config: collection_name: The name of the collection. namespace: The namespace for the collection. resource_type: The type of resource to be scaffolded. + plugin_name: The name of plugin to be scaffolded. plugin_type: The type of plugin to be scaffolded. type: The type of the project for which the resource is being scaffolded. path: The file path where the resource should be added. @@ -48,6 +49,7 @@ class Config: collection_name: str | None = None namespace: str = "" resource_type: str = "" + plugin_name: str = "" plugin_type: str = "" type: str = "" path: str = "" diff --git a/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 index a33ffb68..f0e2e5f8 100644 --- a/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 +++ b/src/ansible_creator/resources/collection_project/plugins/lookup/hello_world.py.j2 @@ -1,52 +1,86 @@ -"""A hello-world lookup plugin in {{ namespace }}.{{ collection_name }}.""" +{# lookup_plugin_template.j2 #} +{%- set lookup_name = plugin_name | default("hello_world") -%} +{%- set author = author | default("Your Name") -%} +{%- set description = description | default("A custom lookup plugin for Ansible.") -%} +{%- set license = license | default("GPL-3.0-or-later") -%} +# pylint: disable=E0401 +# {{ lookup_name }}.py - {{ description }} +# Author: {{ author }} +# License: {{ license }} -from __future__ import absolute_import, annotations, division, print_function -from ansible.plugins.lookup import LookupBase +from ansible.plugins.lookup import LookupBase # type: ignore +from ansible.errors import AnsibleError # type: ignore +from ansible.utils.display import Display # type: ignore +from typing import Any, Optional, Dict, List -__metaclass__ = type # pylint: disable=C0103 +display = Display() DOCUMENTATION = """ - name: hello_world - author: {{ namespace | capitalize }} {{ collection_name | capitalize }} + name: {{ lookup_name }} + author: {{ author }} version_added: "1.0.0" - short_description: Demo lookup plugin that returns a Hello message. + short_description: {{ description }} description: - - This is a demo lookup plugin designed to return Hello message. + - This is a custom lookup plugin to provide lookup functionality. options: - name: - description: Value specified here is appended to the Hello message. - type: str + _terms: + description: Terms to lookup + required: True + notes: + - This is a scaffold template. Customize the plugin to fit your needs. """ EXAMPLES = """ -# hello_world lookup example -{% raw %} -- name: Display a hello message +- name: Example usage of {{ lookup_name }} +{%- raw %} ansible.builtin.debug: - msg: "{{ lookup('{%- endraw %}{{ namespace }}.{{ collection_name }}.hello_world') }}" + msg: "{{ lookup('{%- endraw %}{{ lookup_name }}', 'example_term') }}" """ RETURN = """ -_raw: - description: Returns a Hello message with the specified name +_list: + description: The list of values found by the lookup type: list - elements: string - sample: ["Hello, World!"] """ -class LookupModule(LookupBase): - """lookup plugin.""" +class LookupModule(LookupBase): # type: ignore[misc] + """ + Custom Ansible lookup plugin: hello_world + A custom lookup plugin for Ansible. + """ - def run(self, terms: list, variables: dict = None, **kwargs) -> list: - """Returns a simple Hello, World message. + def run( + self, + terms: List[str], + variables: Optional[Dict[str, Any]] = None, + **kwargs: Dict[str, Any], + ) -> list[str]: + """ + Run the lookup with the specified terms. - Parameters: - terms: A list of terms passed to the function. - variables: Additional variables for processing. - **kwargs: Arbitrary keyword arguments. + Args: + terms: A list of terms to lookup. + variables: Additional variables. + **kwargs: Additional keyword arguments. Returns: - list: The Hello message as a list. + list: A list of processed results. + + Raises: + AnsibleError: If the 'terms' parameter is not a list. """ - return ["Hello, World!"] + if not isinstance(terms, list): + raise AnsibleError("The 'terms' parameter must be a list.") + + display.vvv(f"Running hello_world lookup plugin with terms: {terms}") + + try: + # Example processing logic - Replace this with actual lookup code + result = [term.upper() for term in terms] + + display.vvv(f"Result from hello_world lookup: {result}") + return result + + except Exception as e: + raise AnsibleError(f"Error in hello_world plugin: {e}") diff --git a/src/ansible_creator/subcommands/add.py b/src/ansible_creator/subcommands/add.py index 8f2cb3ab..e74f4343 100644 --- a/src/ansible_creator/subcommands/add.py +++ b/src/ansible_creator/subcommands/add.py @@ -34,6 +34,7 @@ def __init__( self._plugin_type: str = config.plugin_type self._resource_id: str = f"common.{self._resource_type}" self._plugin_id: str = f"collection_project.plugins.{self._plugin_type}" + self._plugin_name: str = config.plugin_name self._add_path: Path = Path(config.path) self._force = config.force self._overwrite = config.overwrite @@ -228,4 +229,6 @@ def _get_lookup_plugin_template_data(self) -> TemplateData: """ return TemplateData( plugin_type=self._plugin_type, + plugin_name=self._plugin_name, + creator_version=self._creator_version, ) diff --git a/src/ansible_creator/types.py b/src/ansible_creator/types.py index 79e00677..40dec1bd 100644 --- a/src/ansible_creator/types.py +++ b/src/ansible_creator/types.py @@ -19,6 +19,7 @@ class TemplateData: Attributes: resource_type: The type of resource to be scaffolded. plugin_type: The type of plugin to be scaffolded. + plugin_name: The name of the plugin to be scaffolded. additions: A dictionary containing additional data to add to the gitignore. collection_name: The name of the collection. creator_version: The version of the creator. @@ -31,6 +32,7 @@ class TemplateData: resource_type: str = "" plugin_type: str = "" + plugin_name: str = "" additions: dict[str, dict[str, dict[str, str | bool]]] = field(default_factory=dict) collection_name: str = "" creator_version: str = "" diff --git a/tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py b/tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py index 1a408747..eb2499a2 100644 --- a/tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py +++ b/tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py @@ -1,52 +1,80 @@ -"""A hello-world lookup plugin in testorg.testcol.""" +# pylint: disable=E0401 +# hello_world.py - A custom lookup plugin for Ansible. +# Author: Your Name +# License: GPL-3.0-or-later -from __future__ import absolute_import, annotations, division, print_function -from ansible.plugins.lookup import LookupBase +from ansible.plugins.lookup import LookupBase # type: ignore +from ansible.errors import AnsibleError # type: ignore +from ansible.utils.display import Display # type: ignore +from typing import Any, Optional, Dict, List -__metaclass__ = type # pylint: disable=C0103 +display = Display() DOCUMENTATION = """ name: hello_world - author: Testorg Testcol + author: Your Name version_added: "1.0.0" - short_description: Demo lookup plugin that returns a Hello message. + short_description: A custom lookup plugin for Ansible. description: - - This is a demo lookup plugin designed to return Hello message. + - This is a custom lookup plugin to provide lookup functionality. options: - name: - description: Value specified here is appended to the Hello message. - type: str + _terms: + description: Terms to lookup + required: True + notes: + - This is a scaffold template. Customize the plugin to fit your needs. """ EXAMPLES = """ -# hello_world lookup example - -- name: Display a hello message +- name: Example usage of hello_world ansible.builtin.debug: - msg: "{{ lookup('testorg.testcol.hello_world') }}" + msg: "{{ lookup('hello_world', 'example_term') }}" """ RETURN = """ -_raw: - description: Returns a Hello message with the specified name +_list: + description: The list of values found by the lookup type: list - elements: string - sample: ["Hello, World!"] """ -class LookupModule(LookupBase): - """lookup plugin.""" +class LookupModule(LookupBase): # type: ignore[misc] + """ + Custom Ansible lookup plugin: hello_world + A custom lookup plugin for Ansible. + """ - def run(self, terms: list, variables: dict = None, **kwargs) -> list: - """Returns a simple Hello, World message. + def run( + self, + terms: List[str], + variables: Optional[Dict[str, Any]] = None, + **kwargs: Dict[str, Any], + ) -> list[str]: + """ + Run the lookup with the specified terms. - Parameters: - terms: A list of terms passed to the function. - variables: Additional variables for processing. - **kwargs: Arbitrary keyword arguments. + Args: + terms: A list of terms to lookup. + variables: Additional variables. + **kwargs: Additional keyword arguments. Returns: - list: The Hello message as a list. + list: A list of processed results. + + Raises: + AnsibleError: If the 'terms' parameter is not a list. """ - return ["Hello, World!"] + if not isinstance(terms, list): + raise AnsibleError("The 'terms' parameter must be a list.") + + display.vvv(f"Running hello_world lookup plugin with terms: {terms}") + + try: + # Example processing logic - Replace this with actual lookup code + result = [term.upper() for term in terms] + + display.vvv(f"Result from hello_world lookup: {result}") + return result + + except Exception as e: + raise AnsibleError(f"Error in hello_world plugin: {e}") From 241ca3371f609fdfe47a62b98100f76134c8ce35 Mon Sep 17 00:00:00 2001 From: abhikdps Date: Thu, 14 Nov 2024 19:11:16 +0530 Subject: [PATCH 07/15] Create path if not present and replace paths --- src/ansible_creator/subcommands/add.py | 5 +++-- src/ansible_creator/types.py | 2 +- src/ansible_creator/utils.py | 1 + .../molecule.yml | 0 .../plugins/filter/{hello_world.py => plugin_example.py} | 0 .../plugins/lookup/{hello_world.py => plugin_example.py} | 8 ++++---- .../{hello_world => plugin_example}/tasks/main.yml | 0 7 files changed, 9 insertions(+), 7 deletions(-) rename tests/fixtures/collection/testorg/testcol/extensions/molecule/{integration_hello_world => integration_plugin_example}/molecule.yml (100%) rename tests/fixtures/collection/testorg/testcol/plugins/filter/{hello_world.py => plugin_example.py} (100%) rename tests/fixtures/collection/testorg/testcol/plugins/lookup/{hello_world.py => plugin_example.py} (91%) rename tests/fixtures/collection/testorg/testcol/tests/integration/targets/{hello_world => plugin_example}/tasks/main.yml (100%) diff --git a/src/ansible_creator/subcommands/add.py b/src/ansible_creator/subcommands/add.py index e74f4343..916c96a4 100644 --- a/src/ansible_creator/subcommands/add.py +++ b/src/ansible_creator/subcommands/add.py @@ -46,11 +46,12 @@ def __init__( def run(self) -> None: """Start scaffolding the resource file.""" - self._check_add_path() - self.output.debug(msg=f"final collection path set to {self._add_path}") if self._resource_type: + self._check_add_path() + self.output.debug(msg=f"final collection path set to {self._add_path}") self._resource_scaffold() elif self._plugin_type: + self._add_path.mkdir(parents=True, exist_ok=True) self._plugin_scaffold() def _check_add_path(self) -> None: diff --git a/src/ansible_creator/types.py b/src/ansible_creator/types.py index 40dec1bd..72265e19 100644 --- a/src/ansible_creator/types.py +++ b/src/ansible_creator/types.py @@ -32,7 +32,7 @@ class TemplateData: resource_type: str = "" plugin_type: str = "" - plugin_name: str = "" + plugin_name: str = "plugin_example" additions: dict[str, dict[str, dict[str, str | bool]]] = field(default_factory=dict) collection_name: str = "" creator_version: str = "" diff --git a/src/ansible_creator/utils.py b/src/ansible_creator/utils.py index fca0d26f..7637f75c 100644 --- a/src/ansible_creator/utils.py +++ b/src/ansible_creator/utils.py @@ -28,6 +28,7 @@ PATH_REPLACERS = { "project_org": "namespace", "project_repo": "collection_name", + "hello_world": "plugin_name", } diff --git a/tests/fixtures/collection/testorg/testcol/extensions/molecule/integration_hello_world/molecule.yml b/tests/fixtures/collection/testorg/testcol/extensions/molecule/integration_plugin_example/molecule.yml similarity index 100% rename from tests/fixtures/collection/testorg/testcol/extensions/molecule/integration_hello_world/molecule.yml rename to tests/fixtures/collection/testorg/testcol/extensions/molecule/integration_plugin_example/molecule.yml diff --git a/tests/fixtures/collection/testorg/testcol/plugins/filter/hello_world.py b/tests/fixtures/collection/testorg/testcol/plugins/filter/plugin_example.py similarity index 100% rename from tests/fixtures/collection/testorg/testcol/plugins/filter/hello_world.py rename to tests/fixtures/collection/testorg/testcol/plugins/filter/plugin_example.py diff --git a/tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py b/tests/fixtures/collection/testorg/testcol/plugins/lookup/plugin_example.py similarity index 91% rename from tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py rename to tests/fixtures/collection/testorg/testcol/plugins/lookup/plugin_example.py index eb2499a2..fc9562dc 100644 --- a/tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py +++ b/tests/fixtures/collection/testorg/testcol/plugins/lookup/plugin_example.py @@ -1,5 +1,5 @@ # pylint: disable=E0401 -# hello_world.py - A custom lookup plugin for Ansible. +# plugin_example.py - A custom lookup plugin for Ansible. # Author: Your Name # License: GPL-3.0-or-later @@ -11,7 +11,7 @@ display = Display() DOCUMENTATION = """ - name: hello_world + name: plugin_example author: Your Name version_added: "1.0.0" short_description: A custom lookup plugin for Ansible. @@ -26,9 +26,9 @@ """ EXAMPLES = """ -- name: Example usage of hello_world +- name: Example usage of plugin_example ansible.builtin.debug: - msg: "{{ lookup('hello_world', 'example_term') }}" + msg: "{{ lookup('plugin_example', 'example_term') }}" """ RETURN = """ diff --git a/tests/fixtures/collection/testorg/testcol/tests/integration/targets/hello_world/tasks/main.yml b/tests/fixtures/collection/testorg/testcol/tests/integration/targets/plugin_example/tasks/main.yml similarity index 100% rename from tests/fixtures/collection/testorg/testcol/tests/integration/targets/hello_world/tasks/main.yml rename to tests/fixtures/collection/testorg/testcol/tests/integration/targets/plugin_example/tasks/main.yml From f0157fa740f98cc222285d3f61ad66051b459d00 Mon Sep 17 00:00:00 2001 From: abhikdps Date: Fri, 15 Nov 2024 12:18:08 +0530 Subject: [PATCH 08/15] Change default value for plugin_name --- src/ansible_creator/types.py | 2 +- .../molecule.yml | 0 .../plugins/filter/{plugin_example.py => hello_world.py} | 0 .../plugins/lookup/{plugin_example.py => hello_world.py} | 8 ++++---- .../{plugin_example => hello_world}/tasks/main.yml | 0 5 files changed, 5 insertions(+), 5 deletions(-) rename tests/fixtures/collection/testorg/testcol/extensions/molecule/{integration_plugin_example => integration_hello_world}/molecule.yml (100%) rename tests/fixtures/collection/testorg/testcol/plugins/filter/{plugin_example.py => hello_world.py} (100%) rename tests/fixtures/collection/testorg/testcol/plugins/lookup/{plugin_example.py => hello_world.py} (91%) rename tests/fixtures/collection/testorg/testcol/tests/integration/targets/{plugin_example => hello_world}/tasks/main.yml (100%) diff --git a/src/ansible_creator/types.py b/src/ansible_creator/types.py index 72265e19..38e64ff8 100644 --- a/src/ansible_creator/types.py +++ b/src/ansible_creator/types.py @@ -32,7 +32,7 @@ class TemplateData: resource_type: str = "" plugin_type: str = "" - plugin_name: str = "plugin_example" + plugin_name: str = "hello_world" additions: dict[str, dict[str, dict[str, str | bool]]] = field(default_factory=dict) collection_name: str = "" creator_version: str = "" diff --git a/tests/fixtures/collection/testorg/testcol/extensions/molecule/integration_plugin_example/molecule.yml b/tests/fixtures/collection/testorg/testcol/extensions/molecule/integration_hello_world/molecule.yml similarity index 100% rename from tests/fixtures/collection/testorg/testcol/extensions/molecule/integration_plugin_example/molecule.yml rename to tests/fixtures/collection/testorg/testcol/extensions/molecule/integration_hello_world/molecule.yml diff --git a/tests/fixtures/collection/testorg/testcol/plugins/filter/plugin_example.py b/tests/fixtures/collection/testorg/testcol/plugins/filter/hello_world.py similarity index 100% rename from tests/fixtures/collection/testorg/testcol/plugins/filter/plugin_example.py rename to tests/fixtures/collection/testorg/testcol/plugins/filter/hello_world.py diff --git a/tests/fixtures/collection/testorg/testcol/plugins/lookup/plugin_example.py b/tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py similarity index 91% rename from tests/fixtures/collection/testorg/testcol/plugins/lookup/plugin_example.py rename to tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py index fc9562dc..eb2499a2 100644 --- a/tests/fixtures/collection/testorg/testcol/plugins/lookup/plugin_example.py +++ b/tests/fixtures/collection/testorg/testcol/plugins/lookup/hello_world.py @@ -1,5 +1,5 @@ # pylint: disable=E0401 -# plugin_example.py - A custom lookup plugin for Ansible. +# hello_world.py - A custom lookup plugin for Ansible. # Author: Your Name # License: GPL-3.0-or-later @@ -11,7 +11,7 @@ display = Display() DOCUMENTATION = """ - name: plugin_example + name: hello_world author: Your Name version_added: "1.0.0" short_description: A custom lookup plugin for Ansible. @@ -26,9 +26,9 @@ """ EXAMPLES = """ -- name: Example usage of plugin_example +- name: Example usage of hello_world ansible.builtin.debug: - msg: "{{ lookup('plugin_example', 'example_term') }}" + msg: "{{ lookup('hello_world', 'example_term') }}" """ RETURN = """ diff --git a/tests/fixtures/collection/testorg/testcol/tests/integration/targets/plugin_example/tasks/main.yml b/tests/fixtures/collection/testorg/testcol/tests/integration/targets/hello_world/tasks/main.yml similarity index 100% rename from tests/fixtures/collection/testorg/testcol/tests/integration/targets/plugin_example/tasks/main.yml rename to tests/fixtures/collection/testorg/testcol/tests/integration/targets/hello_world/tasks/main.yml From 1bf220a160edc1536bc15767d732e529551508a2 Mon Sep 17 00:00:00 2001 From: abhikdps Date: Mon, 18 Nov 2024 19:27:06 +0530 Subject: [PATCH 09/15] Tests --- .vscode/launch.json | 7 +- src/ansible_creator/arg_parser.py | 1 - tests/units/test_add.py | 152 +++++++++++++++++++++++++++++- 3 files changed, 155 insertions(+), 5 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index a6a60f98..2925ee71 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -31,9 +31,10 @@ "module": "ansible_creator", "args": [ "add", - "resource", - "devcontainer", - "/home/user/..path/to/your/existing_project" + "plugin", + "lookup", + "validate", + "/Users/abanand/test", ], "cwd": "${workspaceFolder}/src", "justMyCode": false diff --git a/src/ansible_creator/arg_parser.py b/src/ansible_creator/arg_parser.py index af9ac149..5e65ee91 100644 --- a/src/ansible_creator/arg_parser.py +++ b/src/ansible_creator/arg_parser.py @@ -39,7 +39,6 @@ "add resource role", "add plugin action", "add plugin filter", - # "add plugin lookup", ) diff --git a/tests/units/test_add.py b/tests/units/test_add.py index d9890084..a984d48e 100644 --- a/tests/units/test_add.py +++ b/tests/units/test_add.py @@ -42,6 +42,8 @@ class ConfigDict(TypedDict): output: Output subcommand: str resource_type: str + plugin_type: str + plugin_name: str type: str path: str force: bool @@ -65,7 +67,9 @@ def fixture_cli_args(tmp_path: Path, output: Output) -> ConfigDict: "output": output, "subcommand": "add", "type": "resource", - "resource_type": "devfile", + "resource_type": "", + "plugin_type": "", + "plugin_name": "hello_world", "path": str(tmp_path), "force": False, "overwrite": False, @@ -109,6 +113,7 @@ def test_run_success_add_devfile( cli_args: Dictionary, partial Add class object. monkeypatch: Pytest monkeypatch fixture. """ + cli_args["resource_type"] = "devfile" add = Add( Config(**cli_args), ) @@ -182,6 +187,7 @@ def test_run_error_no_overwrite( tmp_path: Temporary directory path. cli_args: Dictionary, partial Add class object. """ + cli_args["resource_type"] = "devfile" add = Add( Config(**cli_args), ) @@ -233,6 +239,7 @@ def test_error_invalid_path( Args: cli_args: Dictionary, partial Add class object. """ + cli_args["resource_type"] = "devfile" cli_args["path"] = "/invalid" add = Add( Config(**cli_args), @@ -256,6 +263,7 @@ def test_run_error_unsupported_resource_type( cli_args: Dictionary, partial Add class object. monkeypatch: Pytest monkeypatch fixture. """ + cli_args["resource_type"] = "devfile" add = Add( Config(**cli_args), ) @@ -266,3 +274,145 @@ def test_run_error_unsupported_resource_type( with pytest.raises(CreatorError) as exc_info: add.run() assert "Unsupported resource type: unsupported_type" in str(exc_info.value) + + +def test_run_success_add_plugin_lookup( + capsys: pytest.CaptureFixture[str], + tmp_path: Path, + cli_args: ConfigDict, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test Add.run(). + + Successfully add plugin 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. + """ + cli_args["plugin_type"] = "lookup" + add = Add( + Config(**cli_args), + ) + + add.run() + result = capsys.readouterr().out + assert re.search("Note: Plugin added to", result) is not None + + expected_devfile = tmp_path / "hello_world.py" + effective_devfile = ( + FIXTURES_DIR + / "collection" + / "testorg" + / "testcol" + / "plugins" + / "lookup" + / "hello_world.py" + ) + cmp_result = cmp(expected_devfile, effective_devfile, shallow=False) + assert cmp_result + + conflict_file = tmp_path / "hello_world.py" + conflict_file.write_text("Author: Your Name") + + # 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 playbook project 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: Plugin added to", result) is not None + + +def test_run_error_plugin_no_overwrite( + capsys: pytest.CaptureFixture[str], + tmp_path: Path, + cli_args: ConfigDict, +) -> None: + """Test Add.run(). + + Successfully add devfile to path + + Args: + capsys: Pytest fixture to capture stdout and stderr. + tmp_path: Temporary directory path. + cli_args: Dictionary, partial Add class object. + """ + cli_args["plugin_type"] = "lookup" + add = Add( + Config(**cli_args), + ) + + add.run() + result = capsys.readouterr().out + assert re.search("Note: Plugin added to", result) is not None + + expected_devfile = tmp_path / "hello_world.py" + effective_devfile = ( + FIXTURES_DIR + / "collection" + / "testorg" + / "testcol" + / "plugins" + / "lookup" + / "hello_world.py" + ) + cmp_result = cmp(expected_devfile, effective_devfile, shallow=False) + assert cmp_result + + conflict_file = tmp_path / "hello_world.py" + conflict_file.write_text("name: Your Name") + + cli_args["no_overwrite"] = True + add = Add( + Config(**cli_args), + ) + with pytest.raises(CreatorError) as exc_info: + add.run() + assert "Please re-run ansible-creator with --overwrite to continue." in str(exc_info.value) + + +def test_run_error_unsupported_plugin_type( + cli_args: ConfigDict, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Test Add.run() with an unsupported plugin type. + + This test checks if the CreatorError is raised when an unsupported + resource type is provided. + + Args: + cli_args: Dictionary, partial Add class object. + monkeypatch: Pytest monkeypatch fixture. + """ + cli_args["plugin_type"] = "lookup" + add = Add( + Config(**cli_args), + ) + # Mock the _plugin_type to bypass the validation step + monkeypatch.setattr(add, "_plugin_type", "unsupported_type") + + # Expect a CreatorError with the appropriate message + with pytest.raises(CreatorError) as exc_info: + add.run() + assert "Unsupported plugin type: unsupported_type" in str(exc_info.value) From b5b3784a20ac784d282af634417c047a4c6f1b71 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:57:44 +0000 Subject: [PATCH 10/15] chore: auto fixes from pre-commit.com hooks --- .vscode/launch.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 2925ee71..d0c54efb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -29,13 +29,7 @@ "type": "debugpy", "request": "launch", "module": "ansible_creator", - "args": [ - "add", - "plugin", - "lookup", - "validate", - "/Users/abanand/test", - ], + "args": ["add", "plugin", "lookup", "validate", "/Users/abanand/test"], "cwd": "${workspaceFolder}/src", "justMyCode": false } From 4243ada4ef2cca51ee247f8af85e688981490118 Mon Sep 17 00:00:00 2001 From: abhikdps Date: Mon, 18 Nov 2024 19:37:25 +0530 Subject: [PATCH 11/15] Fix linting --- tests/units/test_add.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/units/test_add.py b/tests/units/test_add.py index a984d48e..b0a2c6c5 100644 --- a/tests/units/test_add.py +++ b/tests/units/test_add.py @@ -31,6 +31,8 @@ class ConfigDict(TypedDict): output: The output object to use for logging. subcommand: The subcommand to execute. resource_type: The type of resource to be scaffolded. + plugin_type: The type of the plugin to be scaffolded. + plugin_name: The name of the plugin to be scaffolded. type: The type of the project for which the resource is being scaffolded. path: The file path where the resource should be added. force: Force overwrite of existing directory. From cc8420675689bc85b3ded6c33e4e6c39b704aef5 Mon Sep 17 00:00:00 2001 From: abhikdps Date: Tue, 19 Nov 2024 14:08:09 +0530 Subject: [PATCH 12/15] Check if path is collection & tests --- src/ansible_creator/config.py | 2 +- src/ansible_creator/subcommands/add.py | 42 +++++++++++++----- tests/units/test_add.py | 61 +++++++++++++++++++++++--- 3 files changed, 88 insertions(+), 17 deletions(-) diff --git a/src/ansible_creator/config.py b/src/ansible_creator/config.py index 5aaff1e4..494ba928 100644 --- a/src/ansible_creator/config.py +++ b/src/ansible_creator/config.py @@ -52,7 +52,7 @@ class Config: plugin_name: str = "" plugin_type: str = "" type: str = "" - path: str = "" + path: str | Path = "./" def __post_init__(self) -> None: """Post process config values.""" diff --git a/src/ansible_creator/subcommands/add.py b/src/ansible_creator/subcommands/add.py index 916c96a4..948c18fb 100644 --- a/src/ansible_creator/subcommands/add.py +++ b/src/ansible_creator/subcommands/add.py @@ -46,15 +46,17 @@ def __init__( def run(self) -> None: """Start scaffolding the resource file.""" + self._check_path_exists() + self.output.debug(msg=f"final collection path set to {self._add_path}") if self._resource_type: - self._check_add_path() - self.output.debug(msg=f"final collection path set to {self._add_path}") self._resource_scaffold() elif self._plugin_type: - self._add_path.mkdir(parents=True, exist_ok=True) - self._plugin_scaffold() + self._check_collection_path() + plugin_path = self._add_path / "plugins" / self._plugin_type + plugin_path.mkdir(parents=True, exist_ok=True) + self._plugin_scaffold(plugin_path) - def _check_add_path(self) -> None: + def _check_path_exists(self) -> None: """Validate the provided add path. Raises: @@ -64,6 +66,20 @@ def _check_add_path(self) -> None: msg = f"The path {self._add_path} does not exist. Please provide an existing directory." raise CreatorError(msg) + def _check_collection_path(self) -> None: + """Validates if the provided path is an Ansible collection. + + Raises: + CreatorError: If the path is not a collection path. + """ + galaxy_file_path = self._add_path / "galaxy.yml" + if not Path.is_file(galaxy_file_path): + msg = ( + f"The path {self._add_path} is not a valid Ansible collection path. " + "Please provide a valid collection path." + ) + raise CreatorError(msg) + def unique_name_in_devfile(self) -> str: """Use project specific name in devfile. @@ -142,9 +158,12 @@ def _perform_resource_scaffold(self, template_data: TemplateData) -> None: self.output.note(f"Resource added to {self._add_path}") - def _plugin_scaffold(self) -> None: + def _plugin_scaffold(self, plugin_path: Path) -> None: """Scaffold the specified plugin file based on the plugin type. + Args: + plugin_path: Path where the plugin will be scaffolded. + Raises: CreatorError: If unsupported plugin type is given. """ @@ -158,13 +177,14 @@ def _plugin_scaffold(self) -> None: msg = f"Unsupported plugin type: {self._plugin_type}" raise CreatorError(msg) - self._perform_plugin_scaffold(template_data) + self._perform_plugin_scaffold(template_data, plugin_path) - def _perform_plugin_scaffold(self, template_data: TemplateData) -> None: + def _perform_plugin_scaffold(self, template_data: TemplateData, plugin_path: Path) -> None: """Perform the actual scaffolding process using the provided template data. Args: template_data: TemplateData + plugin_path: Path where the plugin will be scaffolded. Raises: CreatorError: If there are conflicts and overwriting is not allowed, or if the @@ -173,7 +193,7 @@ def _perform_plugin_scaffold(self, template_data: TemplateData) -> None: walker = Walker( resources=(f"collection_project.plugins.{self._plugin_type}",), resource_id=self._plugin_id, - dest=self._add_path, + dest=plugin_path, output=self.output, template_data=template_data, templar=self.templar, @@ -192,7 +212,7 @@ def _perform_plugin_scaffold(self, template_data: TemplateData) -> None: if not paths.has_conflicts() or self._force or self._overwrite: copier.copy_containers(paths) - self.output.note(f"Plugin added to {self._add_path}") + self.output.note(f"Plugin added to {plugin_path}") return if not self._overwrite: @@ -208,7 +228,7 @@ def _perform_plugin_scaffold(self, template_data: TemplateData) -> None: ) raise CreatorError(msg) - self.output.note(f"Plugin added to {self._add_path}") + self.output.note(f"Plugin added to {plugin_path}") def _get_devfile_template_data(self) -> TemplateData: """Get the template data for devfile resources. diff --git a/tests/units/test_add.py b/tests/units/test_add.py index b0a2c6c5..9d64d742 100644 --- a/tests/units/test_add.py +++ b/tests/units/test_add.py @@ -252,6 +252,28 @@ def test_error_invalid_path( assert "does not exist. Please provide an existing directory" in str(exc_info.value) +def test_error_invalid_collection_path( + cli_args: ConfigDict, +) -> None: + """Test Add.run(). + + Check if collection exists. + + Args: + cli_args: Dictionary, partial Add class object. + """ + cli_args["plugin_type"] = "lookup" + add = Add( + Config(**cli_args), + ) + + with pytest.raises(CreatorError) as exc_info: + add.run() + assert "is not a valid Ansible collection path. Please provide a valid collection path." in str( + exc_info.value, + ) + + def test_run_error_unsupported_resource_type( cli_args: ConfigDict, monkeypatch: pytest.MonkeyPatch, @@ -299,11 +321,20 @@ def test_run_success_add_plugin_lookup( Config(**cli_args), ) + # Mock the "_check_collection_path" method + def mock_check_collection_path() -> None: + """Mock function to skip checking collection path.""" + + monkeypatch.setattr( + Add, + "_check_collection_path", + staticmethod(mock_check_collection_path), + ) add.run() result = capsys.readouterr().out assert re.search("Note: Plugin added to", result) is not None - expected_devfile = tmp_path / "hello_world.py" + expected_devfile = tmp_path / "plugins" / "lookup" / "hello_world.py" effective_devfile = ( FIXTURES_DIR / "collection" @@ -316,7 +347,7 @@ def test_run_success_add_plugin_lookup( cmp_result = cmp(expected_devfile, effective_devfile, shallow=False) assert cmp_result - conflict_file = tmp_path / "hello_world.py" + conflict_file = tmp_path / "plugins" / "lookup" / "hello_world.py" conflict_file.write_text("Author: Your Name") # expect a CreatorError when the response to overwrite is no. @@ -350,6 +381,7 @@ def test_run_error_plugin_no_overwrite( capsys: pytest.CaptureFixture[str], tmp_path: Path, cli_args: ConfigDict, + monkeypatch: pytest.MonkeyPatch, ) -> None: """Test Add.run(). @@ -359,17 +391,27 @@ def test_run_error_plugin_no_overwrite( capsys: Pytest fixture to capture stdout and stderr. tmp_path: Temporary directory path. cli_args: Dictionary, partial Add class object. + monkeypatch: Pytest monkeypatch fixture. """ cli_args["plugin_type"] = "lookup" add = Add( Config(**cli_args), ) + # Mock the "_check_collection_path" method + def mock_check_collection_path() -> None: + """Mock function to skip checking collection path.""" + + monkeypatch.setattr( + Add, + "_check_collection_path", + staticmethod(mock_check_collection_path), + ) add.run() result = capsys.readouterr().out assert re.search("Note: Plugin added to", result) is not None - expected_devfile = tmp_path / "hello_world.py" + expected_devfile = tmp_path / "plugins" / "lookup" / "hello_world.py" effective_devfile = ( FIXTURES_DIR / "collection" @@ -382,7 +424,7 @@ def test_run_error_plugin_no_overwrite( cmp_result = cmp(expected_devfile, effective_devfile, shallow=False) assert cmp_result - conflict_file = tmp_path / "hello_world.py" + conflict_file = tmp_path / "plugins" / "lookup" / "hello_world.py" conflict_file.write_text("name: Your Name") cli_args["no_overwrite"] = True @@ -411,7 +453,16 @@ def test_run_error_unsupported_plugin_type( add = Add( Config(**cli_args), ) - # Mock the _plugin_type to bypass the validation step + + # Mock the "_check_collection_path" method + def mock_check_collection_path() -> None: + """Mock function to skip checking collection path.""" + + monkeypatch.setattr( + Add, + "_check_collection_path", + staticmethod(mock_check_collection_path), + ) monkeypatch.setattr(add, "_plugin_type", "unsupported_type") # Expect a CreatorError with the appropriate message From ee3813604a17136d41e805ff776085d976419f5b Mon Sep 17 00:00:00 2001 From: abhikdps Date: Tue, 19 Nov 2024 14:50:11 +0530 Subject: [PATCH 13/15] Minor change --- src/ansible_creator/subcommands/add.py | 2 +- tests/units/test_add.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ansible_creator/subcommands/add.py b/src/ansible_creator/subcommands/add.py index 948c18fb..94f6a051 100644 --- a/src/ansible_creator/subcommands/add.py +++ b/src/ansible_creator/subcommands/add.py @@ -76,7 +76,7 @@ def _check_collection_path(self) -> None: if not Path.is_file(galaxy_file_path): msg = ( f"The path {self._add_path} is not a valid Ansible collection path. " - "Please provide a valid collection path." + "Please provide the root path of a valid ansible collection." ) raise CreatorError(msg) diff --git a/tests/units/test_add.py b/tests/units/test_add.py index 9d64d742..234654d6 100644 --- a/tests/units/test_add.py +++ b/tests/units/test_add.py @@ -334,8 +334,8 @@ def mock_check_collection_path() -> None: result = capsys.readouterr().out assert re.search("Note: Plugin added to", result) is not None - expected_devfile = tmp_path / "plugins" / "lookup" / "hello_world.py" - effective_devfile = ( + expected_file = tmp_path / "plugins" / "lookup" / "hello_world.py" + effective_file = ( FIXTURES_DIR / "collection" / "testorg" @@ -344,7 +344,7 @@ def mock_check_collection_path() -> None: / "lookup" / "hello_world.py" ) - cmp_result = cmp(expected_devfile, effective_devfile, shallow=False) + cmp_result = cmp(expected_file, effective_file, shallow=False) assert cmp_result conflict_file = tmp_path / "plugins" / "lookup" / "hello_world.py" @@ -411,8 +411,8 @@ def mock_check_collection_path() -> None: result = capsys.readouterr().out assert re.search("Note: Plugin added to", result) is not None - expected_devfile = tmp_path / "plugins" / "lookup" / "hello_world.py" - effective_devfile = ( + expected_file = tmp_path / "plugins" / "lookup" / "hello_world.py" + effective_file = ( FIXTURES_DIR / "collection" / "testorg" @@ -421,7 +421,7 @@ def mock_check_collection_path() -> None: / "lookup" / "hello_world.py" ) - cmp_result = cmp(expected_devfile, effective_devfile, shallow=False) + cmp_result = cmp(expected_file, effective_file, shallow=False) assert cmp_result conflict_file = tmp_path / "plugins" / "lookup" / "hello_world.py" From 867fc167237d980306874f898264044609851eca Mon Sep 17 00:00:00 2001 From: abhikdps Date: Tue, 19 Nov 2024 14:55:17 +0530 Subject: [PATCH 14/15] Minor changes --- tests/units/test_add.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/units/test_add.py b/tests/units/test_add.py index 234654d6..8a06bda9 100644 --- a/tests/units/test_add.py +++ b/tests/units/test_add.py @@ -269,7 +269,10 @@ def test_error_invalid_collection_path( with pytest.raises(CreatorError) as exc_info: add.run() - assert "is not a valid Ansible collection path. Please provide a valid collection path." in str( + assert ( + "is not a valid Ansible collection path. " + "Please provide the root path of a valid ansible collection." + ) in str( exc_info.value, ) From 2230d1ace9fd712321527bc6732e430696e895ba Mon Sep 17 00:00:00 2001 From: abhikdps Date: Tue, 19 Nov 2024 15:09:35 +0530 Subject: [PATCH 15/15] Cleanup --- .vscode/launch.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d0c54efb..a6a60f98 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -29,7 +29,12 @@ "type": "debugpy", "request": "launch", "module": "ansible_creator", - "args": ["add", "plugin", "lookup", "validate", "/Users/abanand/test"], + "args": [ + "add", + "resource", + "devcontainer", + "/home/user/..path/to/your/existing_project" + ], "cwd": "${workspaceFolder}/src", "justMyCode": false }