├── .flake8 ├── .github ├── FUNDING.yml ├── labels.yml ├── release-drafter.yml └── workflows │ ├── release-drafter.yml │ └── tox.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .yamllint ├── LICENSE ├── MANIFEST.in ├── README.rst ├── bindep.txt ├── conftest.py ├── molecule_azure ├── __init__.py ├── cookiecutter │ ├── cookiecutter.json │ └── {{cookiecutter.molecule_directory}} │ │ └── {{cookiecutter.scenario_name}} │ │ ├── INSTALL.rst │ │ ├── converge.yml │ │ ├── create.yml │ │ └── destroy.yml ├── driver.py └── test │ ├── __init__.py │ ├── functional │ ├── __init__.py │ ├── conftest.py │ └── test_azure.py │ ├── scenarios │ └── driver │ │ └── azure │ │ └── molecule │ │ ├── default │ │ ├── converge.yml │ │ ├── molecule.yml │ │ └── tests │ │ │ └── test_default.py │ │ └── multi-node │ │ ├── converge.yml │ │ ├── molecule.yml │ │ ├── prepare.yml │ │ └── tests │ │ ├── __init__.py │ │ └── test_default.py │ └── test_driver.py ├── pyproject.toml ├── pytest.ini ├── setup.cfg ├── setup.py ├── tools └── test-setup.sh └── tox.ini /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | # do not add excludes for files in repo 3 | exclude = .venv/,.tox/,dist/,build/,.eggs/ 4 | format = pylint 5 | # E203: https://github.com/python/black/issues/315 6 | ignore = E741,W503,W504,H,E501,E203 7 | # 88 is official black default: 8 | max-line-length = 88 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ssbarnea 4 | -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | # Format and labels used aim to match those used by Ansible project 2 | # https://github.com/marketplace/actions/github-labeler 3 | - name: bug 4 | color: "fbca04" 5 | description: "This issue/PR relates to a bug." 6 | - name: deprecated 7 | color: "fef2c0" 8 | description: "This issue/PR relates to a deprecated module." 9 | - name: docs 10 | color: "4071a5" 11 | description: "This issue/PR relates to or includes documentation." 12 | - name: enhancement 13 | color: "ededed" 14 | description: "This issue/PR relates to a feature request." 15 | - name: feature 16 | color: "006b75" 17 | description: "This issue/PR relates to a feature request." 18 | - name: major 19 | color: "c6476b" 20 | description: "Marks an important and likely breaking change." 21 | - name: packaging 22 | color: "4071a5" 23 | description: "Packaging category" 24 | - name: performance 25 | color: "555555" 26 | description: "Relates to product or testing performance." 27 | - name: skip-changelog 28 | color: "eeeeee" 29 | description: "Can be missed from the changelog." 30 | - name: stale 31 | color: "eeeeee" 32 | description: "Not updated in long time, will be closed soon." 33 | - name: wontfix 34 | color: "eeeeee" 35 | description: "This will not be worked on" 36 | - name: test 37 | color: "0e8a16" 38 | description: "This PR relates to tests, QA, CI." 39 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | categories: 2 | - title: 'Features' 3 | labels: 4 | - 'feature' 5 | - 'enhancement' 6 | - title: 'Bug Fixes' 7 | labels: 8 | - 'fix' 9 | - 'bugfix' 10 | - 'bug' 11 | - title: 'Maintenance' 12 | label: 'chore' 13 | exclude-labels: 14 | - 'skip-changelog' 15 | template: | 16 | ## Changes 17 | 18 | $CHANGES 19 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | - 'releases/**' 9 | - 'stable/**' 10 | 11 | jobs: 12 | update_release_draft: 13 | runs-on: ubuntu-latest 14 | steps: 15 | # Drafts your next Release notes as Pull Requests are merged into "master" 16 | - uses: release-drafter/release-drafter@v5 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.github/workflows/tox.yml: -------------------------------------------------------------------------------- 1 | name: tox 2 | 3 | on: 4 | create: # is used for publishing to PyPI and TestPyPI 5 | tags: # any tag regardless of its name, no branches 6 | - "**" 7 | push: # only publishes pushes to the main branch to TestPyPI 8 | branches: # any integration branch but not tag 9 | - "master" 10 | pull_request: 11 | release: 12 | types: 13 | - published # It seems that you can publish directly without creating 14 | schedule: 15 | - cron: 1 0 * * * # Run daily at 0:01 UTC 16 | 17 | jobs: 18 | build: 19 | name: ${{ matrix.tox_env }} 20 | runs-on: ubuntu-latest 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | include: 25 | - tox_env: lint 26 | - tox_env: py36 27 | PREFIX: PYTEST_REQPASS=2 28 | - tox_env: py36-devel 29 | PREFIX: PYTEST_REQPASS=2 30 | - tox_env: py37 31 | PREFIX: PYTEST_REQPASS=2 32 | - tox_env: py38 33 | PREFIX: PYTEST_REQPASS=2 34 | - tox_env: py39 35 | PREFIX: PYTEST_REQPASS=2 36 | - tox_env: py39-devel 37 | PREFIX: PYTEST_REQPASS=2 38 | - tox_env: packaging 39 | steps: 40 | - uses: actions/checkout@v1 41 | - name: Find python version 42 | id: py_ver 43 | shell: python 44 | if: ${{ contains(matrix.tox_env, 'py') }} 45 | run: | 46 | v = '${{ matrix.tox_env }}'.split('-')[0].lstrip('py') 47 | print('::set-output name=version::{0}.{1}'.format(v[0],v[1:])) 48 | # Even our lint and other envs need access to tox 49 | - name: Install a default Python 50 | uses: actions/setup-python@v2 51 | if: ${{ ! contains(matrix.tox_env, 'py') }} 52 | # Be sure to install the version of python needed by a specific test, if necessary 53 | - name: Set up Python version 54 | uses: actions/setup-python@v2 55 | if: ${{ contains(matrix.tox_env, 'py') }} 56 | with: 57 | python-version: ${{ steps.py_ver.outputs.version }} 58 | - name: Install dependencies 59 | run: | 60 | python -m pip install -U pip 61 | pip install tox 62 | - name: Run tox -e ${{ matrix.tox_env }} 63 | run: | 64 | echo "${{ matrix.PREFIX }} tox -e ${{ matrix.tox_env }}" 65 | ${{ matrix.PREFIX }} tox -e ${{ matrix.tox_env }} 66 | 67 | publish: 68 | name: Publish to PyPI registry 69 | needs: 70 | - build 71 | runs-on: ubuntu-latest 72 | 73 | env: 74 | PY_COLORS: 1 75 | TOXENV: packaging 76 | 77 | steps: 78 | - name: Switch to using Python 3.6 by default 79 | uses: actions/setup-python@v2 80 | with: 81 | python-version: 3.6 82 | - name: Install tox 83 | run: python -m pip install --user tox 84 | - name: Check out src from Git 85 | uses: actions/checkout@v2 86 | with: 87 | # Get shallow Git history (default) for release events 88 | # but have a complete clone for any other workflows. 89 | # Both options fetch tags but since we're going to remove 90 | # one from HEAD in non-create-tag workflows, we need full 91 | # history for them. 92 | fetch-depth: >- 93 | ${{ 94 | ( 95 | ( 96 | github.event_name == 'create' && 97 | github.event.ref_type == 'tag' 98 | ) || 99 | github.event_name == 'release' 100 | ) && 101 | 1 || 0 102 | }} 103 | - name: Drop Git tags from HEAD for non-tag-create and non-release events 104 | if: >- 105 | ( 106 | github.event_name != 'create' || 107 | github.event.ref_type != 'tag' 108 | ) && 109 | github.event_name != 'release' 110 | run: >- 111 | git tag --points-at HEAD 112 | | 113 | xargs git tag --delete 114 | - name: Build dists 115 | run: python -m tox 116 | - name: Publish to test.pypi.org 117 | if: >- 118 | ( 119 | github.event_name == 'push' && 120 | github.ref == format( 121 | 'refs/heads/{0}', github.event.repository.default_branch 122 | ) 123 | ) || 124 | ( 125 | github.event_name == 'create' && 126 | github.event.ref_type == 'tag' 127 | ) 128 | uses: pypa/gh-action-pypi-publish@master 129 | with: 130 | password: ${{ secrets.testpypi_password }} 131 | repository_url: https://test.pypi.org/legacy/ 132 | - name: Publish to pypi.org 133 | if: >- # "create" workflows run separately from "push" & "pull_request" 134 | github.event_name == 'release' 135 | uses: pypa/gh-action-pypi-publish@master 136 | with: 137 | password: ${{ secrets.pypi_password }} 138 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | pip-wheel-metadata 106 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | default_language_version: 3 | python: python3 4 | minimum_pre_commit_version: "1.14.0" 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v3.2.0 8 | hooks: 9 | - id: end-of-file-fixer 10 | - id: trailing-whitespace 11 | - id: mixed-line-ending 12 | - id: check-byte-order-marker 13 | - id: check-executables-have-shebangs 14 | - id: check-merge-conflict 15 | - id: debug-statements 16 | - id: check-yaml 17 | files: .*\.(yaml|yml)$ 18 | exclude: "cookiecutter.*" 19 | # https://github.com/pre-commit/pre-commit-hooks/issues/273 20 | args: ["--unsafe"] 21 | - repo: https://github.com/PyCQA/doc8.git 22 | rev: 0.9.0a1 23 | hooks: 24 | - id: doc8 25 | - repo: https://github.com/python/black.git 26 | rev: 20.8b1 27 | hooks: 28 | - id: black 29 | language_version: python3 30 | - repo: https://gitlab.com/pycqa/flake8.git 31 | rev: 3.8.3 32 | hooks: 33 | - id: flake8 34 | additional_dependencies: 35 | - flake8-black 36 | - repo: https://github.com/codespell-project/codespell.git 37 | rev: v1.17.1 38 | hooks: 39 | - id: codespell 40 | name: codespell 41 | description: Checks for common misspellings in text files. 42 | entry: codespell 43 | language: python 44 | types: [text] 45 | args: [] 46 | require_serial: false 47 | additional_dependencies: [] 48 | - repo: https://github.com/PyCQA/flake8.git 49 | rev: 3.8.3 50 | hooks: 51 | - id: flake8 52 | - repo: https://github.com/adrienverge/yamllint.git 53 | rev: v1.25.0 54 | hooks: 55 | - id: yamllint 56 | files: \.(yaml|yml)$ 57 | types: [file, yaml] 58 | entry: yamllint --strict -f parsable 59 | - repo: https://github.com/openstack-dev/bashate.git 60 | rev: 2.0.0 61 | hooks: 62 | - id: bashate 63 | entry: bashate --error . --ignore=E006,E040 64 | # Run bashate check for all bash scripts 65 | # Ignores the following rules: 66 | # E006: Line longer than 79 columns (as many scripts use jinja 67 | # templating, this is very difficult) 68 | # E040: Syntax error determined using `bash -n` (as many scripts 69 | # use jinja templating, this will often fail and the syntax 70 | # error will be discovered in execution anyway) 71 | - repo: https://github.com/ansible/ansible-lint.git 72 | rev: v4.3.5 73 | hooks: 74 | - id: ansible-lint 75 | always_run: true 76 | pass_filenames: false 77 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | extends: default 2 | ignore: | 3 | */cookiecutter/ 4 | .github/workflows/ 5 | .tox 6 | 7 | rules: 8 | braces: 9 | max-spaces-inside: 1 10 | level: error 11 | brackets: 12 | max-spaces-inside: 1 13 | level: error 14 | document-start: disable 15 | line-length: disable 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2019 Sorin Sbarnea 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.rst 3 | 4 | recursive-exclude * __pycache__ 5 | recursive-exclude * *.py[co] 6 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ********************* 2 | Molecule Azure Plugin 3 | ********************* 4 | 5 | .. image:: https://badge.fury.io/py/molecule-azure.svg 6 | :target: https://badge.fury.io/py/molecule-azure 7 | :alt: PyPI Package 8 | 9 | .. image:: https://github.com/ansible-community/molecule-azure/workflows/tox/badge.svg 10 | :target: https://github.com/ansible-community/molecule-azure/actions 11 | 12 | .. image:: https://img.shields.io/badge/code%20style-black-000000.svg 13 | :target: https://github.com/python/black 14 | :alt: Python Black Code Style 15 | 16 | .. image:: https://img.shields.io/badge/Code%20of%20Conduct-silver.svg 17 | :target: https://docs.ansible.com/ansible/latest/community/code_of_conduct.html 18 | :alt: Ansible Code of Conduct 19 | 20 | .. image:: https://img.shields.io/badge/Mailing%20lists-silver.svg 21 | :target: https://docs.ansible.com/ansible/latest/community/communication.html#mailing-list-information 22 | :alt: Ansible mailing lists 23 | 24 | .. image:: https://img.shields.io/badge/license-MIT-brightgreen.svg 25 | :target: LICENSE 26 | :alt: Repository License 27 | 28 | Molecule Azure is designed to allow use of Azure Cloud for provisioning test 29 | resources. 30 | 31 | Documentation 32 | ============= 33 | 34 | Read the documentation and more at `molecule.readthedocs.io`_. 35 | 36 | .. _get-involved: 37 | 38 | Get Involved 39 | ============ 40 | 41 | * Join us in the ``#ansible-molecule`` channel on `Freenode`_. 42 | * Join the discussion in `molecule-users Forum`_. 43 | * Join the community working group by checking the `wiki`_. 44 | * Want to know about releases, subscribe to `ansible-announce list`_. 45 | * For the full list of Ansible email Lists, IRC channels see the 46 | `communication page`_. 47 | 48 | .. _`molecule.readthedocs.io`: https://molecule.readthedocs.io/ 49 | .. _`Freenode`: https://freenode.net 50 | .. _`molecule-users Forum`: https://groups.google.com/forum/#!forum/molecule-users 51 | .. _`wiki`: https://github.com/ansible/community/wiki/Molecule 52 | .. _`ansible-announce list`: https://groups.google.com/group/ansible-announce 53 | .. _`communication page`: https://docs.ansible.com/ansible/latest/community/communication.html 54 | 55 | .. _authors: 56 | 57 | Authors 58 | ======= 59 | 60 | Molecule Azure Plugin was created by Sorin Sbarnea based on code from Molecule. 61 | 62 | .. _license: 63 | 64 | License 65 | ======= 66 | 67 | The `MIT`_ License. 68 | 69 | .. _`MIT`: https://github.com/ansible-community/molecule/blob/master/LICENSE 70 | 71 | The logo is licensed under the `Creative Commons NoDerivatives 4.0 License`_. 72 | 73 | If you have some other use in mind, contact us. 74 | 75 | .. _`Creative Commons NoDerivatives 4.0 License`: https://creativecommons.org/licenses/by-nd/4.0/ 76 | -------------------------------------------------------------------------------- /bindep.txt: -------------------------------------------------------------------------------- 1 | # This is a cross-platform list tracking distribution packages needed by tests; 2 | # see https://docs.openstack.org/infra/bindep/ for additional information. 3 | 4 | build-dep [platform:dpkg] 5 | gcc-c++ [test platform:rpm] 6 | gcc [test platform:rpm] 7 | libselinux-python [platform:centos-7] 8 | make [platform:centos-7] 9 | pkg-config [platform:dpkg] 10 | python36 [test !platform:centos-7 !platform:fedora-28] 11 | python3-devel [test !platform:centos-7 platform:rpm] 12 | python3-libselinux [platform:fedora] 13 | python3-netifaces [test !platform:centos-7 platform:rpm] 14 | python3 [test !platform:centos-7 platform:rpm] 15 | -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansible-community/molecule-azure/18acf4d8359975af42d6b344f415b02b8ba7db97/conftest.py -------------------------------------------------------------------------------- /molecule_azure/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansible-community/molecule-azure/18acf4d8359975af42d6b344f415b02b8ba7db97/molecule_azure/__init__.py -------------------------------------------------------------------------------- /molecule_azure/cookiecutter/cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "molecule_directory": "molecule", 3 | "role_name": "OVERRIDDEN", 4 | "scenario_name": "OVERRIDDEN" 5 | } 6 | -------------------------------------------------------------------------------- /molecule_azure/cookiecutter/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/INSTALL.rst: -------------------------------------------------------------------------------- 1 | ******************************* 2 | Azure driver installation guide 3 | ******************************* 4 | 5 | Requirements 6 | ============ 7 | 8 | * An Azure credentials rc file 9 | 10 | Install 11 | ======= 12 | 13 | Please refer to the `Virtual environment`_ documentation for installation best 14 | practices. If not using a virtual environment, please consider passing the 15 | widely recommended `'--user' flag`_ when invoking ``pip``. 16 | 17 | .. _Virtual environment: https://virtualenv.pypa.io/en/latest/ 18 | .. _'--user' flag: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site 19 | 20 | .. code-block:: bash 21 | 22 | $ pip install 'molecule[azure]' 23 | -------------------------------------------------------------------------------- /molecule_azure/cookiecutter/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | tasks: 5 | 6 | - name: Include tested role 7 | include_role: 8 | name: "{{ cookiecutter.role_name }}" 9 | -------------------------------------------------------------------------------- /molecule_azure/cookiecutter/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/create.yml: -------------------------------------------------------------------------------- 1 | --- 2 | {% raw -%} 3 | - name: Create 4 | hosts: localhost 5 | connection: local 6 | gather_facts: false 7 | no_log: "{{ molecule_no_log }}" 8 | vars: 9 | resource_group_name: molecule 10 | location: "{{ lookup('env', 'AZURE_REGION') or 'westus' }}" 11 | ssh_user: molecule 12 | ssh_port: 22 13 | virtual_network_name: molecule_vnet 14 | subnet_name: molecule_subnet 15 | keypair_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" 16 | tasks: 17 | - name: Create resource group 18 | azure_rm_resourcegroup: 19 | name: "{{ resource_group_name }}" 20 | location: "{{ location }}" 21 | 22 | - name: Create virtual network 23 | azure_rm_virtualnetwork: 24 | resource_group: "{{ resource_group_name }}" 25 | name: "{{ virtual_network_name }}" 26 | address_prefixes: "10.10.0.0/16" 27 | 28 | - name: Create subnet 29 | azure_rm_subnet: 30 | resource_group: "{{ resource_group_name }}" 31 | name: "{{ subnet_name }}" 32 | address_prefix_cidr: 10.10.1.0/24 33 | virtual_network_name: "{{ virtual_network_name }}" 34 | 35 | - name: Create key pair 36 | user: 37 | name: "{{ lookup('env', 'USER') }}" 38 | generate_ssh_key: true 39 | ssh_key_file: "{{ keypair_path }}" 40 | register: key_pair 41 | 42 | - name: Create molecule instance(s) 43 | azure_rm_virtualmachine: 44 | resource_group: "{{ resource_group_name }}" 45 | name: "{{ item.name }}" 46 | vm_size: Standard_A0 47 | admin_username: "{{ ssh_user }}" 48 | public_ip_allocation_method: Dynamic 49 | ssh_password_enabled: false 50 | ssh_public_keys: 51 | - path: "/home/{{ ssh_user }}/.ssh/authorized_keys" 52 | key_data: "{{ key_pair.ssh_public_key }}" 53 | image: 54 | offer: CentOS 55 | publisher: OpenLogic 56 | sku: '7.4' 57 | version: latest 58 | register: server 59 | with_items: "{{ molecule_yml.platforms }}" 60 | async: 7200 61 | poll: 0 62 | 63 | - name: Wait for instance(s) creation to complete 64 | async_status: 65 | jid: "{{ item.ansible_job_id }}" 66 | register: azure_jobs 67 | until: azure_jobs.finished 68 | retries: 300 69 | with_items: "{{ server.results }}" 70 | 71 | 72 | # Mandatory configuration for Molecule to function. 73 | 74 | - name: Populate instance config dict 75 | set_fact: 76 | instance_conf_dict: { 77 | 'instance': "{{ item.ansible_facts.azure_vm.name }}", 78 | 'address': "{{ item.ansible_facts.azure_vm.properties.networkProfile.networkInterfaces[0].properties.ipConfigurations[0].properties.publicIPAddress.properties.ipAddress }}", 79 | 'user': "{{ ssh_user }}", 80 | 'port': "{{ ssh_port }}", 81 | 'identity_file': "{{ keypair_path }}", } 82 | with_items: "{{ azure_jobs.results }}" 83 | register: instance_config_dict 84 | when: server.changed | bool 85 | 86 | - name: Convert instance config dict to a list 87 | set_fact: 88 | instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" 89 | when: server.changed | bool 90 | 91 | - name: Dump instance config 92 | copy: 93 | content: "{{ instance_conf | to_json | from_json | molecule_to_yaml | molecule_header }}" 94 | dest: "{{ molecule_instance_config }}" 95 | when: server.changed | bool 96 | 97 | - name: Wait for SSH 98 | wait_for: 99 | port: "{{ ssh_port }}" 100 | host: "{{ item.address }}" 101 | search_regex: SSH 102 | delay: 10 103 | with_items: "{{ lookup('file', molecule_instance_config) | molecule_from_yaml }}" 104 | {%- endraw %} 105 | -------------------------------------------------------------------------------- /molecule_azure/cookiecutter/{{cookiecutter.molecule_directory}}/{{cookiecutter.scenario_name}}/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | {% raw -%} 3 | - name: Destroy 4 | hosts: localhost 5 | connection: local 6 | gather_facts: false 7 | no_log: "{{ molecule_no_log }}" 8 | vars: 9 | resource_group_name: molecule 10 | virtual_network_name: molecule_vnet 11 | subnet_name: molecule_subnet 12 | tasks: 13 | - name: Destroy resource group and all associated resources 14 | azure_rm_resourcegroup: 15 | name: "{{ resource_group_name }}" 16 | state: absent 17 | force_delete_nonempty: true 18 | register: rg 19 | 20 | # Mandatory configuration for Molecule to function. 21 | 22 | - name: Populate instance config 23 | set_fact: 24 | instance_conf: {} 25 | 26 | - name: Dump instance config 27 | copy: 28 | content: "{{ instance_conf | to_json | from_json | molecule_to_yaml | molecule_header }}" 29 | dest: "{{ molecule_instance_config }}" 30 | when: rg.changed | bool 31 | {%- endraw %} 32 | -------------------------------------------------------------------------------- /molecule_azure/driver.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015-2018 Cisco Systems, Inc. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to 5 | # deal in the Software without restriction, including without limitation the 6 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | # sell copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | import os 21 | from molecule import logger 22 | from molecule.api import Driver 23 | 24 | from molecule import util 25 | 26 | LOG = logger.get_logger(__name__) 27 | 28 | 29 | class Azure(Driver): 30 | """ 31 | The class responsible for managing `Azure`_ instances. `Azure`_ 32 | is ``not`` the default driver used in Molecule. 33 | 34 | Molecule leverages Ansible's `azure_module`_, by mapping variables 35 | from ``molecule.yml`` into ``create.yml`` and ``destroy.yml``. 36 | 37 | .. _`azure_module`: https://docs.ansible.com/ansible/latest/guide_azure.html 38 | 39 | .. code-block:: yaml 40 | 41 | driver: 42 | name: azure 43 | platforms: 44 | - name: instance 45 | 46 | .. code-block:: bash 47 | 48 | $ pip install 'molecule-azure' 49 | 50 | Change the options passed to the ssh client. 51 | 52 | .. code-block:: yaml 53 | 54 | driver: 55 | name: azure 56 | ssh_connection_options: 57 | - '-o ControlPath=~/.ansible/cp/%r@%h-%p' 58 | 59 | .. important:: 60 | 61 | Molecule does not merge lists, when overriding the developer must 62 | provide all options. 63 | 64 | Provide a list of files Molecule will preserve, relative to the scenario 65 | ephemeral directory, after any ``destroy`` subcommand execution. 66 | 67 | .. code-block:: yaml 68 | 69 | driver: 70 | name: azure 71 | safe_files: 72 | - foo 73 | 74 | .. _`Azure`: https://azure.microsoft.com 75 | """ # noqa 76 | 77 | def __init__(self, config=None): 78 | super(Azure, self).__init__(config) 79 | self._name = "azure" 80 | 81 | @property 82 | def name(self): 83 | return self._name 84 | 85 | @name.setter 86 | def name(self, value): 87 | self._name = value 88 | 89 | @property 90 | def login_cmd_template(self): 91 | connection_options = " ".join(self.ssh_connection_options) 92 | 93 | return ( 94 | "ssh {{address}} " 95 | "-l {{user}} " 96 | "-p {{port}} " 97 | "-i {{identity_file}} " 98 | "{}" 99 | ).format(connection_options) 100 | 101 | @property 102 | def default_safe_files(self): 103 | return [self.instance_config] 104 | 105 | @property 106 | def default_ssh_connection_options(self): 107 | return self._get_ssh_connection_options() 108 | 109 | def login_options(self, instance_name): 110 | d = {"instance": instance_name} 111 | 112 | return util.merge_dicts(d, self._get_instance_config(instance_name)) 113 | 114 | def ansible_connection_options(self, instance_name): 115 | try: 116 | d = self._get_instance_config(instance_name) 117 | 118 | return { 119 | "ansible_user": d["user"], 120 | "ansible_host": d["address"], 121 | "ansible_port": d["port"], 122 | "ansible_private_key_file": d["identity_file"], 123 | "connection": "ssh", 124 | "ansible_ssh_common_args": " ".join(self.ssh_connection_options), 125 | } 126 | except StopIteration: 127 | return {} 128 | except IOError: 129 | # Instance has yet to be provisioned , therefore the 130 | # instance_config is not on disk. 131 | return {} 132 | 133 | def _get_instance_config(self, instance_name): 134 | instance_config_dict = util.safe_load_file(self._config.driver.instance_config) 135 | 136 | return next( 137 | item for item in instance_config_dict if item["instance"] == instance_name 138 | ) 139 | 140 | def sanity_checks(self): 141 | # FIXME(decentral1se): Implement sanity checks 142 | pass 143 | 144 | def template_dir(self): 145 | """Return path to its own cookiecutterm templates. It is used by init 146 | command in order to figure out where to load the templates from. 147 | """ 148 | return os.path.join(os.path.dirname(__file__), "cookiecutter") 149 | -------------------------------------------------------------------------------- /molecule_azure/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansible-community/molecule-azure/18acf4d8359975af42d6b344f415b02b8ba7db97/molecule_azure/test/__init__.py -------------------------------------------------------------------------------- /molecule_azure/test/functional/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansible-community/molecule-azure/18acf4d8359975af42d6b344f415b02b8ba7db97/molecule_azure/test/functional/__init__.py -------------------------------------------------------------------------------- /molecule_azure/test/functional/conftest.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015-2018 Cisco Systems, Inc. 2 | # Copyright (c) 2018 Red Hat, Inc. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to 6 | # deal in the Software without restriction, including without limitation the 7 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | # sell copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | # DEALINGS IN THE SOFTWARE. 21 | 22 | 23 | from molecule.test.conftest import * # noqa 24 | -------------------------------------------------------------------------------- /molecule_azure/test/functional/test_azure.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015-2018 Cisco Systems, Inc. 2 | # Copyright (c) 2018 Red Hat, Inc. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to 6 | # deal in the Software without restriction, including without limitation the 7 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | # sell copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | # DEALINGS IN THE SOFTWARE. 21 | 22 | import pytest 23 | import os 24 | 25 | from molecule import logger 26 | from molecule.util import run_command 27 | from molecule.test.conftest import change_dir_to 28 | 29 | # import change_dir_to, temp_dir 30 | 31 | LOG = logger.get_logger(__name__) 32 | 33 | 34 | def test_command_init_scenario(temp_dir): 35 | role_directory = os.path.join(temp_dir.strpath, "test-init") 36 | cmd = ["molecule", "init", "role", "test-init"] 37 | result = run_command(cmd) 38 | assert result.returncode == 0 39 | 40 | with change_dir_to(role_directory): 41 | molecule_directory = pytest.helpers.molecule_directory() 42 | scenario_directory = os.path.join(molecule_directory, "test-scenario") 43 | cmd = [ 44 | "molecule", 45 | "init", 46 | "scenario", 47 | "test-scenario", 48 | "--role-name", 49 | "test-init", 50 | "--driver-name", 51 | "azure", 52 | ] 53 | result = run_command(cmd) 54 | assert result.returncode == 0 55 | 56 | assert os.path.isdir(scenario_directory) 57 | 58 | # temporary trick to pass on CI/CD 59 | if "AZURE_SECRET" in os.environ: 60 | cmd = ["molecule", "test", "-s", "test-scenario"] 61 | result = run_command(cmd) 62 | assert result.returncode == 0 63 | -------------------------------------------------------------------------------- /molecule_azure/test/scenarios/driver/azure/molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | gather_facts: false 5 | become: true 6 | roles: 7 | - molecule 8 | -------------------------------------------------------------------------------- /molecule_azure/test/scenarios/driver/azure/molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: azure 6 | platforms: 7 | - name: instance 8 | provisioner: 9 | name: ansible 10 | config_options: 11 | defaults: 12 | callback_whitelist: profile_roles,profile_tasks,timer 13 | playbooks: 14 | create: ../../../../../resources/playbooks/azure/create.yml 15 | destroy: ../../../../../resources/playbooks/azure/destroy.yml 16 | env: 17 | ANSIBLE_ROLES_PATH: ../../../../../resources/roles/ 18 | verifier: 19 | name: testinfra 20 | -------------------------------------------------------------------------------- /molecule_azure/test/scenarios/driver/azure/molecule/default/tests/test_default.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import testinfra.utils.ansible_runner 4 | 5 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 6 | os.environ["MOLECULE_INVENTORY_FILE"] 7 | ).get_hosts("all") 8 | 9 | 10 | def test_hostname(host): 11 | assert "instance" == host.check_output("hostname -s") 12 | 13 | 14 | def test_etc_molecule_directory(host): 15 | f = host.file("/etc/molecule") 16 | 17 | assert f.is_directory 18 | assert f.user == "root" 19 | assert f.group == "root" 20 | assert f.mode == 0o755 21 | 22 | 23 | def test_etc_molecule_ansible_hostname_file(host): 24 | f = host.file("/etc/molecule/instance") 25 | 26 | assert f.is_file 27 | assert f.user == "root" 28 | assert f.group == "root" 29 | assert f.mode == 0o644 30 | -------------------------------------------------------------------------------- /molecule_azure/test/scenarios/driver/azure/molecule/multi-node/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | gather_facts: false 5 | become: true 6 | roles: 7 | - molecule 8 | 9 | - name: Converge 10 | hosts: bar 11 | gather_facts: false 12 | become: true 13 | roles: 14 | - molecule 15 | 16 | - name: Converge 17 | hosts: foo 18 | gather_facts: false 19 | become: true 20 | roles: 21 | - molecule 22 | 23 | - name: Converge 24 | hosts: baz 25 | gather_facts: false 26 | become: true 27 | roles: 28 | - molecule 29 | -------------------------------------------------------------------------------- /molecule_azure/test/scenarios/driver/azure/molecule/multi-node/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: azure 6 | platforms: 7 | - name: instance-1 8 | groups: 9 | - foo 10 | - bar 11 | - name: instance-2 12 | groups: 13 | - foo 14 | - baz 15 | provisioner: 16 | name: ansible 17 | config_options: 18 | defaults: 19 | callback_whitelist: profile_roles,profile_tasks,timer 20 | playbooks: 21 | create: ../../../../../resources/playbooks/azure/create.yml 22 | destroy: ../../../../../resources/playbooks/azure/destroy.yml 23 | inventory: 24 | group_vars: 25 | all: 26 | resource_group_name: molecule 27 | location: "{{ lookup('env', 'AZURE_REGION') or 'westus' }}" 28 | ssh_user: molecule 29 | ssh_port: 22 30 | virtual_network_name: molecule_vnet 31 | subnet_name: molecule_subnet 32 | keypair_path: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" 33 | env: 34 | ANSIBLE_ROLES_PATH: ../../../../../resources/roles/ 35 | verifier: 36 | name: testinfra 37 | -------------------------------------------------------------------------------- /molecule_azure/test/scenarios/driver/azure/molecule/multi-node/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: all 4 | gather_facts: false 5 | tasks: 6 | - name: Install python for Ansible 7 | raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal python-zipstream) 8 | become: true 9 | changed_when: false 10 | -------------------------------------------------------------------------------- /molecule_azure/test/scenarios/driver/azure/molecule/multi-node/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ansible-community/molecule-azure/18acf4d8359975af42d6b344f415b02b8ba7db97/molecule_azure/test/scenarios/driver/azure/molecule/multi-node/tests/__init__.py -------------------------------------------------------------------------------- /molecule_azure/test/scenarios/driver/azure/molecule/multi-node/tests/test_default.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | import testinfra.utils.ansible_runner 5 | 6 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 7 | os.environ["MOLECULE_INVENTORY_FILE"] 8 | ).get_hosts("all") 9 | 10 | 11 | def test_hostname(host): 12 | assert re.search(r"instance-[12].*", host.check_output("hostname -s")) 13 | 14 | 15 | def test_etc_molecule_directory(host): 16 | f = host.file("/etc/molecule") 17 | 18 | assert f.is_directory 19 | assert f.user == "root" 20 | assert f.group == "root" 21 | assert f.mode == 0o755 22 | 23 | 24 | def test_etc_molecule_ansible_hostname_file(host): 25 | filename = "/etc/molecule/{}".format(host.check_output("hostname -s")) 26 | f = host.file(filename) 27 | 28 | assert f.is_file 29 | assert f.user == "root" 30 | assert f.group == "root" 31 | assert f.mode == 0o644 32 | -------------------------------------------------------------------------------- /molecule_azure/test/test_driver.py: -------------------------------------------------------------------------------- 1 | from molecule import api 2 | 3 | 4 | def test_driver_is_detected(): 5 | assert "azure" in [str(d) for d in api.drivers()] 6 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools >= 41.0.0", 4 | "setuptools_scm >= 1.15.0", 5 | "setuptools_scm_git_archive >= 1.0", 6 | "wheel", 7 | ] 8 | build-backend = "setuptools.build_meta" 9 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = -v -rxXs --doctest-modules --durations 10 --cov=molecule_azure --cov-report term-missing:skip-covered --no-cov-on-fail 3 | doctest_optionflags = ALLOW_UNICODE ELLIPSIS 4 | junit_suite_name = molecule_test_suite 5 | norecursedirs = dist doc build .tox .eggs test/scenarios test/resources 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | dists = clean --all sdist bdist_wheel 3 | 4 | [metadata] 5 | name = molecule-azure 6 | url = https://github.com/ansible-community/molecule-azure 7 | project_urls = 8 | Bug Tracker = https://github.com/ansible-community/molecule-azure/issues 9 | Release Management = https://github.com/ansible-community/molecule-azure/releases 10 | CI = https://github.com/ansible-community/molecule-azure/actions 11 | 12 | Documentation = https://molecule.readthedocs.io 13 | Mailing lists = https://docs.ansible.com/ansible/latest/community/communication.html#mailing-list-information 14 | Source Code = https://github.com/ansible-community/molecule-azure 15 | description = Azure Molecule Plugin :: run molecule tests on Azure 16 | long_description = file: README.rst 17 | long_description_content_type = text/x-rst 18 | author = Sorin Sbarnea 19 | author_email = sorin.sbarnea@gmail.com 20 | maintainer = Sorin Sbarnea 21 | maintainer_email = sorin.sbarnea@gmail.com 22 | license = MIT 23 | license_file = LICENSE 24 | classifiers = 25 | Development Status :: 5 - Production/Stable 26 | 27 | Environment :: Console 28 | Framework :: Pytest 29 | Intended Audience :: Developers 30 | Intended Audience :: Information Technology 31 | Intended Audience :: System Administrators 32 | License :: OSI Approved :: MIT License 33 | Natural Language :: English 34 | Operating System :: OS Independent 35 | Programming Language :: Python :: 3 36 | Programming Language :: Python :: 3.6 37 | Programming Language :: Python :: 3.7 38 | Programming Language :: Python :: 3.8 39 | Programming Language :: Python :: 3.9 40 | 41 | Topic :: System :: Systems Administration 42 | Topic :: Utilities 43 | 44 | keywords = 45 | ansible 46 | roles 47 | testing 48 | molecule 49 | plugin 50 | azure 51 | 52 | [options] 53 | use_scm_version = True 54 | python_requires = >=3.6 55 | packages = find: 56 | include_package_data = True 57 | zip_safe = False 58 | 59 | # These are required during `setup.py` run: 60 | setup_requires = 61 | setuptools_scm >= 1.15.0 62 | setuptools_scm_git_archive >= 1.0 63 | 64 | # These are required in actual runtime: 65 | install_requires = 66 | # molecule plugins are not allowed to mention Ansible as a direct dependency 67 | molecule >= 3.2.0a0 68 | 69 | [options.extras_require] 70 | test = 71 | molecule[ansible,test] 72 | 73 | [options.entry_points] 74 | molecule.driver = 75 | azure = molecule_azure.driver:Azure 76 | 77 | [options.packages.find] 78 | where = . 79 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python 2 | import setuptools 3 | 4 | 5 | if __name__ == "__main__": 6 | setuptools.setup(use_scm_version=True, setup_requires=["setuptools_scm"]) 7 | -------------------------------------------------------------------------------- /tools/test-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euxo pipefail 3 | # Used by Zuul CI to perform extra bootstrapping 4 | 5 | # Bumping system tox because version from CentOS 7 is too old 6 | # We are not using pip --user due to few bugs in tox role which does not allow 7 | # us to override how is called. Once these are addressed we will switch back 8 | # non-sudo mode. 9 | PYTHON=$(command -v python3 python | head -n1) 10 | 11 | sudo $PYTHON -m pip install -U tox "zipp<0.6.0;python_version=='2.7'" 12 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # For more information about tox, see https://tox.readthedocs.io/en/latest/ 2 | [tox] 3 | minversion = 3.9.0 4 | envlist = 5 | lint 6 | docs 7 | packaging 8 | py{36,37,38,39} 9 | py{36,37,38,39}-{devel} 10 | 11 | # do not enable skip missing to avoid CI false positives 12 | skip_missing_interpreters = False 13 | isolated_build = True 14 | 15 | [testenv] 16 | description = 17 | Unit testing 18 | usedevelop = True 19 | commands = 20 | pytest --collect-only 21 | pytest --color=yes {tty:-s} 22 | setenv = 23 | ANSIBLE_FORCE_COLOR={env:ANSIBLE_FORCE_COLOR:1} 24 | ANSIBLE_INVENTORY={toxinidir}/tests/hosts.ini 25 | ANSIBLE_CONFIG={toxinidir}/ansible.cfg 26 | ANSIBLE_NOCOWS=1 27 | ANSIBLE_RETRY_FILES_ENABLED=0 28 | ANSIBLE_STDOUT_CALLBACK={env:ANSIBLE_STDOUT_CALLBACK:debug} 29 | ANSIBLE_VERBOSITY={env:ANSIBLE_VERBOSITY:0} 30 | PIP_DISABLE_PIP_VERSION_CHECK=1 31 | PY_COLORS={env:PY_COLORS:1} 32 | # pip: Avoid 2020-01-01 warnings: https://github.com/pypa/pip/issues/6207 33 | PYTHONWARNINGS=ignore:DEPRECATION::pip._internal.cli.base_command 34 | PYTHONDONTWRITEBYTECODE=1 35 | # This should pass these args to molecule, no effect here as this is the default 36 | # but it validates that it accepts extra params. 37 | MOLECULE_OPTS=--destroy always 38 | MOLECULE_NO_LOG="false" 39 | _EXTRAS=-l --html={envlogdir}/reports.html --self-contained-html 40 | PYTEST_ADDOPTS={env:_EXTRAS} {env:PYTEST_ADDOPTS:} 41 | passenv = 42 | AZURE* 43 | CI 44 | CURL_CA_BUNDLE 45 | DOCKER_* 46 | HOME 47 | PYTEST_OPTIONS 48 | REQUESTS_CA_BUNDLE 49 | SSH_AUTH_SOCK 50 | SSL_CERT_FILE 51 | TOXENV 52 | TRAVIS 53 | TRAVIS_* 54 | TWINE_* 55 | USER 56 | deps = 57 | py{36,37,38,39}: molecule[ansible,test] 58 | py{36,37,38,39}-{devel}: git+https://github.com/ansible-community/molecule.git@master#egg=molecule[ansible,test] 59 | extras = 60 | ansible 61 | test 62 | whitelist_externals = 63 | bash 64 | twine 65 | pytest 66 | pre-commit 67 | rm 68 | 69 | [testenv:packaging] 70 | usedevelop = false 71 | skip_install = true 72 | deps = 73 | collective.checkdocs >= 0.2 74 | pep517 >= 0.5.0 75 | twine >= 2.0.0 76 | commands = 77 | bash -c "rm -rf {toxinidir}/dist/ {toxinidir}/build/ && mkdir -p {toxinidir}/dist/" 78 | python -m pep517.build \ 79 | --source \ 80 | --binary \ 81 | --out-dir {toxinidir}/dist/ \ 82 | {toxinidir} 83 | twine check dist/* 84 | 85 | [testenv:devel] 86 | description= Unit testing using master branches of molecule and ansible 87 | extras = test 88 | commands = 89 | {[testenv]commands} 90 | deps = 91 | git+https://github.com/ansible/ansible.git#egg=ansible 92 | git+https://github.com/ansible-community/molecule#egg=molecule 93 | 94 | [testenv:lint] 95 | description = Performs linting, style checks 96 | skip_install = true 97 | deps = 98 | pre-commit 99 | commands = 100 | pre-commit run -a 101 | 102 | [testenv:upload] 103 | description = Builds the packages and uploads them to https://pypi.org 104 | envdir={toxworkdir}/dist 105 | deps= 106 | {[testenv:packaging]deps} 107 | commands = 108 | {[testenv:packaging]commands} 109 | twine upload --verbose dist/* 110 | --------------------------------------------------------------------------------