Skip to content

Commit

Permalink
Scaffold lookup plugin through add subcommand (#327)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
abhikdps and pre-commit-ci[bot] authored Nov 19, 2024
1 parent 7f2e436 commit 6f0c674
Show file tree
Hide file tree
Showing 10 changed files with 498 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/ansible_creator/arg_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
"add resource role",
"add plugin action",
"add plugin filter",
"add plugin lookup",
)


Expand Down Expand Up @@ -366,6 +365,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:
Expand Down
6 changes: 5 additions & 1 deletion src/ansible_creator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ 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.
"""
Expand All @@ -47,8 +49,10 @@ class Config:
collection_name: str | None = None
namespace: str = ""
resource_type: str = ""
plugin_name: str = ""
plugin_type: str = ""
type: str = ""
path: str = ""
path: str | Path = "./"

def __post_init__(self) -> None:
"""Post process config values."""
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
{# 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 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

display = Display()

DOCUMENTATION = """
name: {{ lookup_name }}
author: {{ author }}
version_added: "1.0.0"
short_description: {{ description }}
description:
- This is a custom lookup plugin to provide lookup functionality.
options:
_terms:
description: Terms to lookup
required: True
notes:
- This is a scaffold template. Customize the plugin to fit your needs.
"""

EXAMPLES = """
- name: Example usage of {{ lookup_name }}
{%- raw %}
ansible.builtin.debug:
msg: "{{ lookup('{%- endraw %}{{ lookup_name }}', 'example_term') }}"
"""

RETURN = """
_list:
description: The list of values found by the lookup
type: list
"""


class LookupModule(LookupBase): # type: ignore[misc]
"""
Custom Ansible lookup plugin: hello_world
A custom lookup plugin for Ansible.
"""

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.

Args:
terms: A list of terms to lookup.
variables: Additional variables.
**kwargs: Additional keyword arguments.

Returns:
list: A list of processed results.

Raises:
AnsibleError: If the 'terms' parameter is not a list.
"""
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}")
122 changes: 114 additions & 8 deletions src/ansible_creator/subcommands/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ 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._plugin_name: str = config.plugin_name
self._add_path: Path = Path(config.path)
self._force = config.force
self._overwrite = config.overwrite
Expand All @@ -43,12 +46,17 @@ def __init__(

def run(self) -> None:
"""Start scaffolding the resource file."""
self._check_add_path()
self._check_path_exists()
self.output.debug(msg=f"final collection path set to {self._add_path}")

self._scaffold()

def _check_add_path(self) -> None:
if self._resource_type:
self._resource_scaffold()
elif self._plugin_type:
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_path_exists(self) -> None:
"""Validate the provided add path.
Raises:
Expand All @@ -58,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 the root path of a valid ansible collection."
)
raise CreatorError(msg)

def unique_name_in_devfile(self) -> str:
"""Use project specific name in devfile.
Expand All @@ -68,7 +90,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:
Expand All @@ -84,9 +106,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:
Expand Down Expand Up @@ -136,6 +158,78 @@ def _perform_scaffold(self, template_data: TemplateData) -> None:

self.output.note(f"Resource added to {self._add_path}")

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.
"""
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, plugin_path)

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
destination directory contains files that will be overwritten.
"""
walker = Walker(
resources=(f"collection_project.plugins.{self._plugin_type}",),
resource_id=self._plugin_id,
dest=plugin_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 {plugin_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 {plugin_path}")

def _get_devfile_template_data(self) -> TemplateData:
"""Get the template data for devfile resources.
Expand All @@ -147,3 +241,15 @@ 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,
plugin_name=self._plugin_name,
creator_version=self._creator_version,
)
4 changes: 4 additions & 0 deletions src/ansible_creator/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ 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.
Expand All @@ -29,6 +31,8 @@ class TemplateData:
"""

resource_type: str = ""
plugin_type: str = ""
plugin_name: str = "hello_world"
additions: dict[str, dict[str, dict[str, str | bool]]] = field(default_factory=dict)
collection_name: str = ""
creator_version: str = ""
Expand Down
1 change: 1 addition & 0 deletions src/ansible_creator/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
PATH_REPLACERS = {
"project_org": "namespace",
"project_repo": "collection_name",
"hello_world": "plugin_name",
}


Expand Down
Empty file.
Loading

0 comments on commit 6f0c674

Please sign in to comment.