From 768713aadca5b20b5293d8dc05a683e6f548d1ad Mon Sep 17 00:00:00 2001 From: Sebastian Tobie Date: Sun, 11 Feb 2024 20:32:38 +0100 Subject: [PATCH] created an ansible module with module utils --- CHANGELOG.rst | 14 ++++ Makefile | 21 +++++ README.md | 19 +---- changelogs/.plugin-cache.yaml | 19 +++++ changelogs/changelog.yaml | 8 ++ changelogs/config.yaml | 32 ++++++++ galaxy.yml | 34 +++++++++ meta/runtime.yml | 4 + plugins/README.md | 31 ++++++++ .../module_utils}/generic.py | 0 .../module_utils}/module.py | 2 +- plugins/module_utils/py.typed | 0 pyproject.toml | 22 ++++-- src/ansible_module/__init__.py | 2 +- src/ansible_module/__main__.py | 2 - src/ansible_module/generic.pyi | 76 ------------------- src/ansible_module/module.pyi | 60 --------------- src/ansible_module/py.typed | 0 tests/__init__.py | 3 - tests/test_generic.py | 57 -------------- tests/test_modules.py | 71 ----------------- upload.sh | 13 ++++ 22 files changed, 194 insertions(+), 296 deletions(-) create mode 100644 CHANGELOG.rst create mode 100644 Makefile create mode 100644 changelogs/.plugin-cache.yaml create mode 100644 changelogs/changelog.yaml create mode 100644 changelogs/config.yaml create mode 100644 galaxy.yml create mode 100644 meta/runtime.yml create mode 100644 plugins/README.md rename {src/ansible_module => plugins/module_utils}/generic.py (100%) rename {src/ansible_module => plugins/module_utils}/module.py (99%) create mode 100644 plugins/module_utils/py.typed delete mode 100644 src/ansible_module/generic.pyi delete mode 100644 src/ansible_module/module.pyi create mode 100644 src/ansible_module/py.typed delete mode 100644 tests/__init__.py delete mode 100644 tests/test_generic.py delete mode 100644 tests/test_modules.py create mode 100755 upload.sh diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..ab94aa0 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,14 @@ +============================ +Sebastian.Base Release Notes +============================ + +.. contents:: Topics + + +v0.2.0 +====== + +Release Summary +--------------- + +change the module to an ansible module diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2f3f480 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +format: + black . + isort . + +changelog: + antsibull-changelog generate + +docs: format + update-doc + +clean-dist: + rm -rf dist + +hatch-release: + hatch build + +galaxy-release: clean-dist changelog + ansible-galaxy collection build --output-path dist + +upload: galaxy-release hatch-release + ./upload.sh diff --git a/README.md b/README.md index 44dd763..e7505f9 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,3 @@ -# Ansible module +# Ansible Collection - sebastian.base ------ - -**Table of Contents** - -- [Installation](#installation) -- [License](#license) - -## Installation - -```console -pip install ansible-module -``` - -## License - -`ansible-module` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. +Documentation for the collection. diff --git a/changelogs/.plugin-cache.yaml b/changelogs/.plugin-cache.yaml new file mode 100644 index 0000000..cacc114 --- /dev/null +++ b/changelogs/.plugin-cache.yaml @@ -0,0 +1,19 @@ +objects: + role: {} +plugins: + become: {} + cache: {} + callback: {} + cliconf: {} + connection: {} + filter: {} + httpapi: {} + inventory: {} + lookup: {} + module: {} + netconf: {} + shell: {} + strategy: {} + test: {} + vars: {} +version: 0.2.0 diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml new file mode 100644 index 0000000..12aa3be --- /dev/null +++ b/changelogs/changelog.yaml @@ -0,0 +1,8 @@ +ancestor: null +releases: + 0.2.0: + changes: + release_summary: change the module to an ansible module + fragments: + - base_release.yml + release_date: '2024-02-11' diff --git a/changelogs/config.yaml b/changelogs/config.yaml new file mode 100644 index 0000000..df49c96 --- /dev/null +++ b/changelogs/config.yaml @@ -0,0 +1,32 @@ +changelog_filename_template: ../CHANGELOG.rst +changelog_filename_version_depth: 0 +changes_file: changelog.yaml +changes_format: combined +ignore_other_fragment_extensions: true +keep_fragments: false +mention_ancestor: true +new_plugins_after_name: removed_features +notesdir: fragments +prelude_section_name: release_summary +prelude_section_title: Release Summary +sanitize_changelog: true +sections: + - - major_changes + - Major Changes + - - minor_changes + - Minor Changes + - - breaking_changes + - Breaking Changes / Porting Guide + - - deprecated_features + - Deprecated Features + - - removed_features + - Removed Features (previously deprecated) + - - security_fixes + - Security Fixes + - - bugfixes + - Bugfixes + - - known_issues + - Known Issues +title: Sebastian.Base +trivial_section_name: trivial +use_fqcn: true diff --git a/galaxy.yml b/galaxy.yml new file mode 100644 index 0000000..5bc454b --- /dev/null +++ b/galaxy.yml @@ -0,0 +1,34 @@ +--- +namespace: sebastian +name: base +version: 0.2.0 + +readme: README.md + +authors: + - Sebastian Tobie +description: > + The base of my ansible collections. It provides the nessesary tools for my modules +license_file: LICENSE +tags: + - linux + - systemd +dependencies: {} + +repository: https://gitea.sebastian-tobie.de/ansible/ansible-module.git +# documentation: https://gitea.sebastian-tobie.de/ansible/ansible-module +homepage: https://gitea.sebastian-tobie.de/ansible/ansible-module +issues: https://gitea.sebastian-tobie.de/ansible/ansible-module/issues + +build_ignore: + - "*.gz" + - ".*" + - Makefile + - pyproject.toml + - upload.sh + - htmlcov + - changelogs + - docs + - src + - "coverage.*" + - dist diff --git a/meta/runtime.yml b/meta/runtime.yml new file mode 100644 index 0000000..24b6abe --- /dev/null +++ b/meta/runtime.yml @@ -0,0 +1,4 @@ +--- +# Collections must specify a minimum required ansible version to upload +# to galaxy +requires_ansible: '>=2.9.10' diff --git a/plugins/README.md b/plugins/README.md new file mode 100644 index 0000000..67a66d4 --- /dev/null +++ b/plugins/README.md @@ -0,0 +1,31 @@ +# Collections Plugins Directory + +This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that +is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that +would contain module utils and modules respectively. + +Here is an example directory of the majority of plugins currently supported by Ansible: + +``` +└── plugins + ├── action + ├── become + ├── cache + ├── callback + ├── cliconf + ├── connection + ├── filter + ├── httpapi + ├── inventory + ├── lookup + ├── module_utils + ├── modules + ├── netconf + ├── shell + ├── strategy + ├── terminal + ├── test + └── vars +``` + +A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible-core/2.16/plugins/plugins.html). diff --git a/src/ansible_module/generic.py b/plugins/module_utils/generic.py similarity index 100% rename from src/ansible_module/generic.py rename to plugins/module_utils/generic.py diff --git a/src/ansible_module/module.py b/plugins/module_utils/module.py similarity index 99% rename from src/ansible_module/module.py rename to plugins/module_utils/module.py index f0e4954..b98ebab 100644 --- a/src/ansible_module/module.py +++ b/plugins/module_utils/module.py @@ -5,7 +5,7 @@ from typing import (Any, Callable, ClassVar, Dict, NoReturn, Optional, Type, import ansible.module_utils.basic as basic -from ansible_module.generic import AnsibleParameter, Types, systemdbool +from .generic import AnsibleParameter, Types, systemdbool __all__ = ( "AnsibleModule", diff --git a/plugins/module_utils/py.typed b/plugins/module_utils/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index 99e3932..75cac91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,9 @@ Source = "https://github.com/unknown/ansible-module" [project.scripts] update-doc = "ansible_module.update_doc:main" +[tool.hatch.build.targets.wheel.force-include] +"plugins/module_utils" = "ansible_module/module_utils" + [tool.hatch.version] path = "src/ansible_module/__init__.py" @@ -135,7 +138,12 @@ ansible_module = ["src/ansible_module", "*/ansible-module/src/ansible_module"] tests = ["tests", "*/ansible-module/tests"] [tool.coverage.report] -exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "pragma: nocover"] +exclude_lines = [ + "no cov", + "if __name__ == .__main__.:", + "if TYPE_CHECKING:", + "pragma: nocover", +] [tool.mypy] python_version = "3.11" @@ -148,13 +156,11 @@ ignore_missing_imports = true [tool.pytest.ini_options] addopts = [ - "--cov-report", "json", - "--cov-report", "term-missing:skip-covered", + "--cov-report", + "json", + "--cov-report", + "term-missing:skip-covered", "--cov", ] pythonpath = ["src"] -required_plugins = [ - "pytest-cov", - "pytest-isort", - "pytest-mypy" -] +required_plugins = ["pytest-cov", "pytest-isort", "pytest-mypy"] diff --git a/src/ansible_module/__init__.py b/src/ansible_module/__init__.py index ae73625..d3ec452 100644 --- a/src/ansible_module/__init__.py +++ b/src/ansible_module/__init__.py @@ -1 +1 @@ -__version__ = "0.1.3" +__version__ = "0.2.0" diff --git a/src/ansible_module/__main__.py b/src/ansible_module/__main__.py index 3da37ce..df98759 100644 --- a/src/ansible_module/__main__.py +++ b/src/ansible_module/__main__.py @@ -1,6 +1,4 @@ from . import update_doc - - if __name__ == "__main__": update_doc.main() diff --git a/src/ansible_module/generic.pyi b/src/ansible_module/generic.pyi deleted file mode 100644 index 7dcdf9d..0000000 --- a/src/ansible_module/generic.pyi +++ /dev/null @@ -1,76 +0,0 @@ -from typing import Any, Dict, Optional, Type, Union - -from _typeshed import Incomplete - -SYSTEMD_CONFIG_ROOT: Incomplete -SYSTEMD_NETWORK_CONFIG: Incomplete -SYSTEMD_SERVICE_CONFIG: Incomplete -AnsibleParameter = Dict[str, Any] - -class Types: - def list( - elements: Union[Type[object], str, AnsibleParameter], required: bool = False, help: Optional[str] = None - ) -> AnsibleParameter: ... - def dict(required: bool = False, help: Optional[str] = None, **options: dict) -> AnsibleParameter: ... - def str( - required: bool = False, - help: Optional[str] = None, - choices: Optional[Sequence] = None, - default: Optional[Any] = None, - ) -> AnsibleParameter: ... - def bool( - required: bool = False, - help: Optional[str] = None, - choices: Optional[Sequence] = None, - default: Optional[Any] = None, - ) -> AnsibleParameter: ... - def int( - required: bool = False, - help: Optional[str] = None, - choices: Optional[Sequence] = None, - default: Optional[Any] = None, - ) -> AnsibleParameter: ... - def float( - required: bool = False, - help: Optional[str] = None, - choices: Optional[Sequence] = None, - default: Optional[Any] = None, - ) -> AnsibleParameter: ... - def path( - required: bool = False, - help: Optional[str] = None, - choices: Optional[Sequence] = None, - default: Optional[Any] = None, - ) -> AnsibleParameter: ... - def raw( - required: bool = False, - help: Optional[str] = None, - choices: Optional[Sequence] = None, - default: Optional[Any] = None, - ) -> AnsibleParameter: ... - def jsonarg( - required: bool = False, - help: Optional[str] = None, - choices: Optional[Sequence] = None, - default: Optional[Any] = None, - ) -> AnsibleParameter: ... - def json( - required: bool = False, - help: Optional[str] = None, - choices: Optional[Sequence] = None, - default: Optional[Any] = None, - ) -> AnsibleParameter: ... - def bytes( - required: bool = False, - help: Optional[str] = None, - choices: Optional[Sequence] = None, - default: Optional[Any] = None, - ) -> AnsibleParameter: ... - def bits( - required: bool = False, - help: Optional[str] = None, - choices: Optional[Sequence] = None, - default: Optional[Any] = None, - ) -> AnsibleParameter: ... - -def systemdbool(b: Union[bool, str]) -> str: ... diff --git a/src/ansible_module/module.pyi b/src/ansible_module/module.pyi deleted file mode 100644 index 650460d..0000000 --- a/src/ansible_module/module.pyi +++ /dev/null @@ -1,60 +0,0 @@ -import pathlib -from typing import (Any, Callable, ClassVar, Dict, NoReturn, Optional, Type, - TypeVar, overload) - -import ansible.module_utils.basic as basic -from _typeshed import Incomplete - -T = TypeVar('T') - -class AnsibleModule: - name: ClassVar[str] - module: basic.AnsibleModule - result: dict - module_spec: ClassVar[dict] - @property - def params(self) -> Dict[str, Any]: ... - tmpdir: Incomplete - def __init__(self) -> None: ... - def set(self, key: str, value): ... - @overload - def diff(self, diff: Dict[str, str]): ... - @overload - def diff( - self, - before: Optional[str] = ..., - after: Optional[str] = ..., - before_header: Optional[str] = ..., - after_header: Optional[str] = ..., - ): ... - def get(self, key: str, default: Optional[T] = ...) -> T: ... - def changed_get(self): ... - def changed_set(self, value) -> None: ... - changed: Incomplete - def prepare(self) -> None: ... - def check(self) -> None: ... - def run(self) -> None: ... - def __call__(self) -> NoReturn: ... - @classmethod - def doc(cls) -> str: ... - -class SystemdUnitModule(AnsibleModule): - unitfile: pathlib.Path - post: Optional[Callable[[], None]] - install: ClassVar[Optional[Callable[[SystemdUnitModule], str]]] - def unit(self) -> str: ... - def header(self) -> str: ... - def map_param(self, **parammap: str): ... - changed: Incomplete - def unitfile_gen(self) -> None: ... - def check(self) -> None: ... - def run(self) -> None: ... - -def installable(_class: Type[SystemdUnitModule]): ... - -class SystemdReloadMixin: - module: basic.AnsibleModule - unitfile: pathlib.Path - restartable: bool - changed: bool - def post(self) -> None: ... diff --git a/src/ansible_module/py.typed b/src/ansible_module/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 1f1acc5..0000000 --- a/tests/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-FileCopyrightText: 2023-present Sebastian Tobie -# -# SPDX-License-Identifier: MIT diff --git a/tests/test_generic.py b/tests/test_generic.py deleted file mode 100644 index 7596abe..0000000 --- a/tests/test_generic.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import unittest - -from ansible_module.generic import Types, joindict, systemdbool - - -class TestTypes(unittest.TestCase): - """tests the Types class""" - - def testsimpletype(self): - """this tests if an simple type is correctly build""" - output = Types.str(required=True, help="test", choices=("a", "1"), default="1") - self.assertIn("type", output) - self.assertIn("required", output) - self.assertIn("default", output) - self.assertIn("choices", output) - self.assertEqual(output["type"], "str") - self.assertEqual(Types.str.__name__, "str") - self.assertEqual(output["required"], True) - self.assertEqual(output["default"], "1") - self.assertTupleEqual(output["choices"], ("a", "1")) - Types.str() - - def testlisttype(self): - """this tests if the special type list is correctly build""" - output = Types.list(str) - Types.list("str") - self.assertIn("type", output) - self.assertIn("elements", output) - self.assertIn("required", output) - self.assertEqual(output["type"], "list") - self.assertEqual(output["required"], False) - self.assertEqual(output["elements"], "str") - Types.list(Types.dict(a=Types.str(help="")), help="") - Types.list("str") - - def testdicttype(self): - output = Types.dict(help="HILFE") - self.assertIn("type", output) - self.assertIn("required", output) - self.assertIn("description", output) - self.assertIn("option", output) - - -class TestFuncs(unittest.TestCase): - def testsystemdbool(self): - self.assertEqual("Text", systemdbool("Text")) - self.assertEqual("no", systemdbool(False)) - self.assertEqual("yes", systemdbool(True)) - - def testjoindict(self): - dicts = ( - dict(a=1, b=2), - dict(b=3, c=4), - ) - output = dict(a=1, b=3, c=4) - self.assertDictEqual(output, joindict(*dicts)) diff --git a/tests/test_modules.py b/tests/test_modules.py deleted file mode 100644 index 905df29..0000000 --- a/tests/test_modules.py +++ /dev/null @@ -1,71 +0,0 @@ -import sys -import unittest - -from ansible_module.generic import modspec -from ansible_module.module import SystemdUnitModule, docify - - -class ModuleMock(SystemdUnitModule): - name = "mock" - module_spec = modspec({}) - - -class TestSystemdUnitModule(unittest.TestCase): - def setUp(self): - sys.argv = [sys.argv[0], '{"ANSIBLE_MODULE_ARGS":{}}'] - self.mod = ModuleMock() - - def test_header(self): - self.assertEqual("[Unit]\n", self.mod.header()) - - def test_doc(self): - ModuleMock.doc() - - def test_params(self): - self.mod.module.params = dict(ok="ok", none=None, true=True, false=False, list=("item1", "item2")) - self.assertEqual("ok", self.mod.get("ok")) - self.assertEqual("default", self.mod.get("none", "default")) - self.assertRaises(KeyError, self.mod.get, "none", None) - self.assertListEqual( - ['TRUE=yes\n', "FALSE=no\n", "OK=ok\n", "LIST=item1\n", "LIST=item2\n"], - self.mod.map_param(true="TRUE", false="FALSE", ok="OK", list="LIST"), - ) - self.assertRaises(TypeError, self.mod.diff, diff=dict(), before=True, after=False) - self.mod.diff(before="a", after="b", before_header="a", after_header="b") - self.mod.diff(dict(before="c", after="d")) - - def test_result(self): - self.assertFalse(self.mod.changed) - self.mod.changed = 5 - self.assertTrue(self.mod.changed) - self.mod.set("key", "value") - self.assertDictEqual( - dict( - changed=True, - key="value", - ), - self.mod.result, - ) - - -class TestFunctions(unittest.TestCase): - def test_docify(self): - input = dict( - item1=dict(type="str", description=""), - item2=dict(required=True, type="int", default=5), - item3=dict(type="list", required=False, elements="int"), - item4=dict(type="list", required=True, elements="int"), - item5=dict(type="str", choices=["a", "b", "c"]), - item6=dict(type="dict", options=dict(a=dict(type="str"))), - ) - expected = dict( - item1=dict(required=False, type="str", description=[""]), - item2=dict(required=True, type="int", default=5), - item3=dict(type="list", required=False, elements="int", default=[]), - item4=dict(type="list", required=True, elements="int"), - item5=dict(type="str", required=False, choices=("a", "b", "c")), - item6=dict(type="dict", required=False, options=dict(a=dict(type="str", required=False))), - ) - output = docify(input) - for key, value in input.items(): - self.assertDictEqual(expected[key], output[key]) diff --git a/upload.sh b/upload.sh new file mode 100755 index 0000000..74d1185 --- /dev/null +++ b/upload.sh @@ -0,0 +1,13 @@ +#!/bin/bash +user=$(yq -r .namespace galaxy.yml) +package=$(yq -r .name galaxy.yml) +version=$(yq -r .version galaxy.yml) +printf "Namespace: %s\nPackage: %s\nVersion: %s\n" $user $package $version +upload(){ + name=$(basename "$1") + printf "uploading: %s as %s\n" "$1" "$name" + curl -u sebastian --upload-file "$1" "https://gitea.sebastian-tobie.de/api/packages/ansible/generic/${package}/${version}/$name" +} +for file in dist/* ; do + upload "$file" +done