From 79b19b4337c742cf37409cfc119fa3cfc5c968ac Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Thu, 8 Oct 2020 15:28:58 +0100 Subject: [PATCH] Deprecate molecule ansible filters We publish molecule filters as a collection and deprecate the old ones. Users are invited to drop them or replace with those from the new community.molecule collection. --- .github/workflows/tox.yml | 2 + collection/.gitignore | 1 + collection/Makefile | 16 +++ collection/README.md | 4 + collection/galaxy.yml | 10 ++ collection/hosts | 1 + collection/playbooks/validate.yml | 18 ++++ collection/plugins/filter/molecule_core.py | 99 +++++++++++++++++++ molecule/constants.py | 2 + .../{{cookiecutter.scenario_name}}/create.yml | 5 +- .../destroy.yml | 5 +- molecule/provisioner/ansible.py | 2 +- .../ansible/plugins/filter/molecule_core.py | 19 +++- molecule/test/unit/test_util.py | 3 +- molecule/util.py | 3 +- tox.ini | 2 + 16 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 collection/.gitignore create mode 100644 collection/Makefile create mode 100644 collection/README.md create mode 100644 collection/galaxy.yml create mode 100644 collection/hosts create mode 100755 collection/playbooks/validate.yml create mode 100644 collection/plugins/filter/molecule_core.py diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 2510b4bc7..19cc74739 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -111,6 +111,8 @@ jobs: xargs git tag --delete - name: Build dists run: python -m tox + - name: Publish collection + run: PUBLISH=1 python -m tox -e packaging - name: Publish to test.pypi.org if: >- ( diff --git a/collection/.gitignore b/collection/.gitignore new file mode 100644 index 000000000..335ec9573 --- /dev/null +++ b/collection/.gitignore @@ -0,0 +1 @@ +*.tar.gz diff --git a/collection/Makefile b/collection/Makefile new file mode 100644 index 000000000..fdfb8370a --- /dev/null +++ b/collection/Makefile @@ -0,0 +1,16 @@ +COLLECTION_NAMESPACE=community +COLLECTION_NAME=molecule + +ifndef PUBLISH + PUBLISH=@echo To publish run: +else + PUBLISH= +endif + +build: + @rm -f *.tar.gz + @rm -rf ~/.ansible/collections/ansible_collections/$(COLLECTION_NAMESPACE)/$(COLLECTION_NAME) + ansible-galaxy collection build + ansible-galaxy collection install -f *.tar.gz + ansible-playbook -i hosts playbooks/validate.yml + $(PUBLISH) ansible-galaxy collection publish *.tar.gz diff --git a/collection/README.md b/collection/README.md new file mode 100644 index 000000000..c0bc3babf --- /dev/null +++ b/collection/README.md @@ -0,0 +1,4 @@ +# Ansible Molecule Collection + +This is not molecule itself, it is only a collection of modules and filters +that can be used by Molecule test playbooks. diff --git a/collection/galaxy.yml b/collection/galaxy.yml new file mode 100644 index 000000000..e527211e5 --- /dev/null +++ b/collection/galaxy.yml @@ -0,0 +1,10 @@ +namespace: community +name: molecule +version: 3.1.0 +authors: + - Ansible +description: Ansible Molecule collection contains molecule specific modules. +license: MIT +readme: README.md +build_ignore: + - Makefile diff --git a/collection/hosts b/collection/hosts new file mode 100644 index 000000000..2fbb50c4a --- /dev/null +++ b/collection/hosts @@ -0,0 +1 @@ +localhost diff --git a/collection/playbooks/validate.yml b/collection/playbooks/validate.yml new file mode 100755 index 000000000..8dd4abcce --- /dev/null +++ b/collection/playbooks/validate.yml @@ -0,0 +1,18 @@ +#!/usr/bin/env ansible-playbook +- hosts: localhost + gather_facts: false + collections: + - community.molecule + tasks: + + - name: Check if community.molecule.header filter can be used + debug: + msg: "{{ '' | community.molecule.header }}" + + - name: Check if community.molecule.to_yaml filter can be used + debug: + msg: "{{ '' | community.molecule.to_yaml }}" + + - name: Check if community.molecule.from_yaml filter can be used + debug: + msg: "{{ '' | community.molecule.from_yaml }}" diff --git a/collection/plugins/filter/molecule_core.py b/collection/plugins/filter/molecule_core.py new file mode 100644 index 000000000..61c191f5c --- /dev/null +++ b/collection/plugins/filter/molecule_core.py @@ -0,0 +1,99 @@ +# Copyright (c) 2015-2018 Cisco Systems, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +"""Provisioner Ansible Plugins.""" + +import os + +from molecule import config, interpolation, util + + +def from_yaml(data): + """ + Interpolate the provided data and return a dict. + + Currently, this is used to reinterpolate the `molecule.yml` inside an + Ansible playbook. If there were any interpolation errors, they would + have been found and raised earlier. + + :return: dict + """ + molecule_env_file = os.environ.get("MOLECULE_ENV_FILE", None) + + env = os.environ.copy() + if molecule_env_file: + env = config.set_env_from_file(env, molecule_env_file) + + i = interpolation.Interpolator(interpolation.TemplateWithDefaults, env) + interpolated_data = i.interpolate(data) + + return util.safe_load(interpolated_data) + + +def to_yaml(data): + """Format data as YAML.""" + return str(util.safe_dump(data)) + + +def header(content): + """Return heaader to be added.""" + return util.molecule_prepender(content) + + +def get_docker_networks(data, state, labels={}): + """Get list of docker networks.""" + network_list = [] + network_names = [] + for platform in data: + if "docker_networks" in platform: + for docker_network in platform["docker_networks"]: + if "labels" not in docker_network: + docker_network["labels"] = {} + for key in labels: + docker_network["labels"][key] = labels[key] + + docker_network["state"] = state + + if "name" in docker_network: + network_list.append(docker_network) + network_names.append(docker_network["name"]) + + # If a network name is defined for a platform but is not defined in + # docker_networks, add it to the network list. + if "networks" in platform: + for network in platform["networks"]: + if "name" in network: + name = network["name"] + if name not in network_names: + network_list.append( + {"name": name, "labels": labels, "state": state} + ) + return network_list + + +class FilterModule(object): + """Core Molecule filter plugins.""" + + def filters(self): + return { + "from_yaml": from_yaml, + "to_yaml": to_yaml, + "header": header, + "get_docker_networks": get_docker_networks, + } diff --git a/molecule/constants.py b/molecule/constants.py index e35fe4abe..1ca2f1040 100644 --- a/molecule/constants.py +++ b/molecule/constants.py @@ -3,3 +3,5 @@ RC_SUCCESS = 0 RC_TIMEOUT = 3 + +MOLECULE_HEADER = "# Molecule managed" diff --git a/molecule/cookiecutter/scenario/driver/delegated/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/create.yml b/molecule/cookiecutter/scenario/driver/delegated/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/create.yml index d2ac73f38..b1efcfca6 100644 --- a/molecule/cookiecutter/scenario/driver/delegated/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/create.yml +++ b/molecule/cookiecutter/scenario/driver/delegated/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/create.yml @@ -28,6 +28,9 @@ - name: Dump instance config copy: - content: "{{ instance_conf | to_json | from_json | molecule_to_yaml | molecule_header }}" + content: | + # Molecule managed + + {{ instance_conf | to_json | from_json | to_yaml }} dest: "{{ molecule_instance_config }}" {%- endraw %} diff --git a/molecule/cookiecutter/scenario/driver/delegated/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/destroy.yml b/molecule/cookiecutter/scenario/driver/delegated/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/destroy.yml index 0f4467cb4..064546874 100644 --- a/molecule/cookiecutter/scenario/driver/delegated/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/destroy.yml +++ b/molecule/cookiecutter/scenario/driver/delegated/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/destroy.yml @@ -16,7 +16,10 @@ - name: Dump instance config copy: - content: "{{ instance_conf | to_json | from_json | molecule_to_yaml | molecule_header }}" + content: | + # Molecule managed + + {{ instance_conf | to_json | from_json | to_yaml }} dest: "{{ molecule_instance_config }}" when: server.changed | default(false) | bool {%- endraw %} diff --git a/molecule/provisioner/ansible.py b/molecule/provisioner/ansible.py index babc0b05a..2a1e26d48 100644 --- a/molecule/provisioner/ansible.py +++ b/molecule/provisioner/ansible.py @@ -596,7 +596,7 @@ def inventory(self): "molecule_file": "{{ lookup('env', 'MOLECULE_FILE') }}", "molecule_ephemeral_directory": "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}", "molecule_scenario_directory": "{{ lookup('env', 'MOLECULE_SCENARIO_DIRECTORY') }}", - "molecule_yml": "{{ lookup('file', molecule_file) | molecule_from_yaml }}", + "molecule_yml": "{{ lookup('file', molecule_file) | community.molecule.from_yaml }}", "molecule_instance_config": "{{ lookup('env', 'MOLECULE_INSTANCE_CONFIG') }}", "molecule_no_log": "{{ lookup('env', 'MOLECULE_NO_LOG') or not " "molecule_yml.provisioner.log|default(False) | bool }}", diff --git a/molecule/provisioner/ansible/plugins/filter/molecule_core.py b/molecule/provisioner/ansible/plugins/filter/molecule_core.py index c02c2323f..e9aff2faf 100644 --- a/molecule/provisioner/ansible/plugins/filter/molecule_core.py +++ b/molecule/provisioner/ansible/plugins/filter/molecule_core.py @@ -17,9 +17,10 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -"""Provisioner Ansible Plugins.""" +"""[[DEPRECARTED]] Provisioner Ansible Plugins.""" import os +import warnings from molecule import config, interpolation, util @@ -34,6 +35,10 @@ def from_yaml(data): :return: dict """ + warnings.warn( + "Deprecated, remove it or replace it with community.molecule.from_yaml", + category=RuntimeWarning, + ) molecule_env_file = os.environ["MOLECULE_ENV_FILE"] env = os.environ.copy() @@ -47,16 +52,28 @@ def from_yaml(data): def to_yaml(data): """Format data as YAML.""" + warnings.warn( + "Deprecated, remove it or replace it with community.molecule.to_yaml", + category=RuntimeWarning, + ) return str(util.safe_dump(data)) def header(content): """Return heaader to be added.""" + warnings.warn( + "Deprecated, remove it or replace it with community.molecule.header", + category=RuntimeWarning, + ) return util.molecule_prepender(content) def get_docker_networks(data, state, labels={}): """Get list of docker networks.""" + warnings.warn( + "Deprecated, remove it or replace it with community.molecule.get_docker_networks", + category=RuntimeWarning, + ) network_list = [] network_names = [] for platform in data: diff --git a/molecule/test/unit/test_util.py b/molecule/test/unit/test_util.py index fa3daf86d..462ed4571 100644 --- a/molecule/test/unit/test_util.py +++ b/molecule/test/unit/test_util.py @@ -29,6 +29,7 @@ import sh from molecule import util +from molecule.constants import MOLECULE_HEADER colorama.init(autoreset=True) @@ -233,7 +234,7 @@ def test_write_file(temp_dir): def molecule_prepender(content): - x = "# Molecule managed\n\nfoo bar" + x = f"{MOLECULE_HEADER}\nfoo bar" assert x == util.file_prepender("foo bar") diff --git a/molecule/util.py b/molecule/util.py index 7821de90b..f8c4341ff 100644 --- a/molecule/util.py +++ b/molecule/util.py @@ -33,6 +33,7 @@ import jinja2 import yaml +from molecule.constants import MOLECULE_HEADER from molecule.logger import get_logger LOG = get_logger(__name__) @@ -164,7 +165,7 @@ def write_file(filename, content): def molecule_prepender(content): """Return molecule identification header.""" - return "# Molecule managed\n\n" + content + return MOLECULE_HEADER + "\n\n" + content def file_prepender(filename): diff --git a/tox.ini b/tox.ini index 1abcdaf58..e78cdb483 100644 --- a/tox.ini +++ b/tox.ini @@ -171,6 +171,8 @@ commands = {toxinidir} # metadata validation sh -c "python -m twine check {toxinidir}//dist/*" + # validate collection building + sh -c "cd collection && make" [testenv:snap] description = Builds a snap package