diff --git a/.config/molecule/config.yml b/.config/molecule/config.yml new file mode 100644 index 00000000..4d78a1b9 --- /dev/null +++ b/.config/molecule/config.yml @@ -0,0 +1,19 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +provisioner: + name: ansible + config_options: + defaults: + collections_path: ${ANSIBLE_COLLECTIONS_PATH} diff --git a/TESTING.md b/TESTING.md index 656b497e..a4402a2e 100644 --- a/TESTING.md +++ b/TESTING.md @@ -1,25 +1,195 @@ # Testing cloudera.exe -The collection uses `ansible-test` for unit and integration testing. +The collection is migrating from `ansible-test` to `pytest` and `molecule` for unit and integration testing. In addition, we use `pre-commit` to handle linting and formatting both for `git` commits and for various Github Action workflows in the repository. + +## Setup + +To set up a development and test environment for the collection, you need to: + +1. Set up the Ansible Collection and Role paths +1. Install Ansible and the Python dependencies +1. Install the collection and its dependencies +1. Configure the PYTHONPATH to use the correct location of the collections code +1. Install the Molecule driver dependencies + +### Ansible Collection and Role Paths + +You have to install your Ansible collections, both the collection under test and its dependencies, into the `ansible_collections//` folder structure. For the collection under test, run the following _in the parent directory of your choosing_: + +```bash +git clone https://github.com/cloudera-labs/cloudera.exe.git ansible_collections/cloudera/exe +``` + +Then create the `roles` directory in the _parent directory_: + +```bash +mkdir roles +``` + +Lastly, set the Ansible [COLLECTION](https://docs.ansible.com/ansible/latest/reference_appendices/config.html#envvar-ANSIBLE_COLLECTIONS_PATH) and [ROLE](https://docs.ansible.com/ansible/latest/reference_appendices/config.html#envvar-ANSIBLE_ROLES_PATH) configurations for these two locations: + +```bash +export ANSIBLE_COLLECTIONS_PATH=$(pwd) +export ANSIBLE_ROLES_PATH="$(pwd)/roles" +``` + +### Set the PYTHONPATH + +Include the `ANSIBLE_COLLECTIONS_PATH` variable to the `PYTHONPATH` to allow module imports. + +```bash +export PYTHONPATH="${ANSIBLE_COLLECTIONS_PATH}":"${PYTHONPATH}" +``` + +### Ansible + +Set up a development `virtualenv` and install `ansible-core~=2.16.0` and `ansible-navigator`. + +```bash +pip install ansible-core~=2.16.0 ansible-navigator +``` + +> [!warning] +> Installing `>=2.17` will require that the target hosts run Python 3.7. This requirement extends to RHEL 8.x and its `platform-python`, which means that `2.17` will not work on these platforms. + +### Python Dependencies + +Install the development and collection Python requirements from the `requirements-dev.txt` and `requirements.txt` files respectively in the project root. + +```bash +pip install -r ansible_collections/cloudera/exe/requirements.txt +pip install -r ansible_collections/cloudera/exe/requirements-dev.txt +``` + +### Collection Dependencies + +You also need to install the collection's dependencies and install them into the `ANSIBLE_COLLECTIONS_PATH`: + +```bash +ansible-galaxy collection install -r ansible_collections/cloudera/exe/requirements.yml -p "${ANSIBLE_COLLECTIONS_PATH}" +``` + +And install any role dependencies as well into the `ANSIBLE_ROLES_PATH`: + +```bash +ansible-galaxy role install -r ansible_collections/cloudera/exe/requirements.yml -p "${ANSIBLE_ROLES_PATH}" +``` + +If the collection has any system requirements, run `bindep` on its requirements file: ```bash -# Sanity tests -ansible-test sanity --docker --python 3.9 +bindep -f ansible_collections/cloudera/exe/bindep.txt +``` + +### Molecule + +Running the `molecule` tests requires `podman` as the container engine, so you will need to install that service on your test machine. Read more about [Podman](https://podman.io/) or [Podman Desktop](https://podman-desktop.io/). + +## Testing + +You can either run standalone `molecule`, for roles and more advanced integration testing, or `pytest`. The latter is set up to run any and all tests, including `molecule` scenarios. + +### Running standalone `molecule` tests + +Currently, `molecule` scenarios are located in the `extensions/molecule` directory of the collection. To run a scenario, change to `extensions` as your current working directory and then run `molecule`. For example: + +| Command | Description | +| --- | --- | +| `molecule test -s rdbms_server_postgresql_14_tls` | Execute the full test lifecyle for the PostgreSQL 14 server role with TLS | +| `molecule create -s rdbms_server_postgresql_14_tls` | Create the `platforms`, i.e. the inventory, that are the target hosts of the role testing | +| `molecule prepare -s rdbms_server_postgresql_14_tls` | Prep the target hosts for testing the roles | +| `molecule converge -s rdbms_server_postgresql_14_tls` | Run the testing playbook, i.e. converge the test code, on the target hosts | +| `molecule side-effect -s rdbms_server_postgresql_14_tls` | Prep the target hosts, post-`converge`, for any additional setup prior to verification or idempotency testing | +| `molecule verify -s rdbms_server_postgresql_14_tls` | Verify the target hosts | +| `molecule cleanup -s rdbms_server_postgresql_14_tls` | Clean up any resources, for example, temporary files created on the controller | +| `molecule destroy -s rdbms_server_postgresql_14_tls` | Destroy the `platform` hosts | + +You can limit testing to a `platform` within a scenario by using the `-p/--platform-name` parameter (or via the `MOLECULE_PLATFORM_NAME` environment variable): + +```bash +molecule test -s rdbms_server_postgresql_14_tls -p rhel9.4 +``` + +To stop tests from destroying the platforms after encountering an error (or at all, even on a successful test), pass the `--destroy=never` parameter: + +```bash +molecule test -s rdbms_server_postgresql_14_tls -p rhel9.4 --destroy=never +``` + +You can log into a running platform via the `login` subcommand and the `-h/--host` parameter: + +```bash +molecule login -s rdbms_server_postgresql_14_tls -h rhel9.4 +``` + +As well as pass extra parameters to the underlying playbook (`converge` command only!): -# Unit tests -ansible-test units --docker --python 3.9 +```bash +molecule converge -s rdbms_server_postgresql_14_tls -- -vvv -t tls_config +``` + +### Running `pytest` tests + +We use the `ansible-pytest` plugin to run unit and integration tests for the collection. + +To see what tests (unit and integration) are available, run the following from the `ANSIBLE_COLLECTIONS_PATH` directory: + +```bash +pytest ansible_collections/cloudera/exe --collect-only +``` + +You should see something like: + +``` +platform darwin -- Python 3.12.4, pytest-8.3.3, pluggy-1.5.0 +ansible: 2.16.11 +rootdir: /Users/wmudge/Devel/ansible_collections/cloudera/exe +configfile: pyproject.toml +testpaths: tests +plugins: ansible-24.9.0, xdist-3.6.1 +collected 8 items -# Integration tests -ansible-test integration --docker + + + + + + + + + + + + + + + + + ``` -To run the _integration_ tests, you first need to have a running virtual environment configured with Ansible (`core`) and the required collections. When you run `ansible-test`, the program will bootstrap the [requirements|tests/integration/requirements.txt] in the Docker container and mount the `ANSIBLE_COLLECTION_PATHS`. +To run a selected test, execute with a regex: ```bash -# In your favorite VENV... -pip3 install ansible-core -ansible-galaxy collection install -p collections -r galaxy.yml -export ANSIBLE_COLLECTION_PATHS="$(pwd)/collections" +pytest ansible_collections/cloudera/exe -k "postgresql_14_tls" ``` -You also need to provide AWS credentials and test configuration in the `integration_config.yml` file. This file is *not* included in the project, as it will contain sensitive data, but there is a template -- `integration_config.yml.template` -- that you can copy and update as needed. +To run a Molecule scenario on a selected platform, i.e. target host, set the platform via the environment variable: + +```bash +MOLECULE_PLATFORM_NAME="rhel9.4" pytest ansible_collections/cloudera/exe -k "postgresql_14_tls" +``` + +> [!warning] +> If you run `pytest` in the root of the collection, `pytest` will copies the current collection into the required Ansible collection path structure within the working directory. That is, running `pytest` at the root of the **collection** creates a `collection/ansible_collections//` **within** the collection. +> Thus, our recommendation is that you can run `pytest` in at the root of the **Ansible collections path**. That is, run `pytest ansible_collections// ...` so that `pytest` doesn't have to bootstrap the collections path. + +## Linting and Commits + +The `pre-commit` Python application is used to manage linting and other housekeeping functions. The application is installed as a `git` hook and as a Github workflow check. + +Commits and pull requests will fail if your contributions do not pass the `pre-commit` checks. You can check your work-in-progress code by running the following: + +```bash +pre-commit run -a +``` diff --git a/extensions/molecule/default/Dockerfile-rhel.j2 b/extensions/molecule/default/Dockerfile-rhel.j2 new file mode 100644 index 00000000..32db3dc1 --- /dev/null +++ b/extensions/molecule/default/Dockerfile-rhel.j2 @@ -0,0 +1,40 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Derived from molecule-plugins[podman] + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +{% if item.env is defined %} +{% for var, value in item.env.items() %} +{% if value %} +ENV {{ var }} {{ value }} +{% endif %} +{% endfor %} +{% endif %} + +RUN dnf makecache && dnf --assumeyes install \ + python3 \ + python3-devel \ + python3-dnf \ + sudo \ + bash \ + iproute \ + && dnf clean all + +CMD ["/sbin/init"] diff --git a/extensions/molecule/default/Dockerfile-ubuntu.j2 b/extensions/molecule/default/Dockerfile-ubuntu.j2 new file mode 100644 index 00000000..bd834cbe --- /dev/null +++ b/extensions/molecule/default/Dockerfile-ubuntu.j2 @@ -0,0 +1,50 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Derived from molecule-plugins[podman] + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +{% if item.env is defined %} +{% for var, value in item.env.items() %} +{% if value %} +ENV {{ var }} {{ value }} +{% endif %} +{% endfor %} +{% endif %} + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 \ + python3-pip \ + python3-apt \ + python3-dev \ + sudo \ + bash \ + gpgv2 \ + systemd \ + systemd-cron \ + xmlsec1 \ + libxmlsec1-openssl \ + libpq-dev \ + && apt-get clean \ + && rm -Rf /usr/share/doc && rm -Rf /usr/share/man \ + && rm -rf /var/lib/apt/lists/* + +CMD ["/lib/systemd/systemd"] diff --git a/extensions/molecule/default/converge.yml b/extensions/molecule/default/converge.yml new file mode 100644 index 00000000..4ebf68fe --- /dev/null +++ b/extensions/molecule/default/converge.yml @@ -0,0 +1,20 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Default test + hosts: all + gather_facts: yes + tasks: + - name: Heartbeat + ansible.builtin.ping: diff --git a/extensions/molecule/default/molecule.yml b/extensions/molecule/default/molecule.yml new file mode 100644 index 00000000..2e704f0f --- /dev/null +++ b/extensions/molecule/default/molecule.yml @@ -0,0 +1,38 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +driver: + name: podman +dependency: + name: galaxy + options: + requirements-file: "${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml" + role-file: "${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml" +platforms: + - name: rhel9.4 + image: registry.access.redhat.com/ubi9/ubi:9.4 + dockerfile: Dockerfile-rhel.j2 + command: /sbin/init + - name: rhel8.10 + image: registry.access.redhat.com/ubi8/ubi:8.10 + dockerfile: Dockerfile-rhel.j2 + command: /sbin/init + - name: ubuntu22.04 + image: ubuntu:22.04 + dockerfile: Dockerfile-ubuntu.j2 + command: /lib/systemd/systemd + - name: ubuntu20.04 + image: ubuntu:20.04 + dockerfile: Dockerfile-ubuntu.j2 + command: /lib/systemd/systemd diff --git a/extensions/molecule/default/requirements.yml b/extensions/molecule/default/requirements.yml new file mode 100644 index 00000000..48342277 --- /dev/null +++ b/extensions/molecule/default/requirements.yml @@ -0,0 +1,17 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +collections: + - containers.podman + - ansible.posix diff --git a/extensions/molecule/rdbms_server_postgresql_14/molecule.yml b/extensions/molecule/rdbms_server_postgresql_14/molecule.yml new file mode 100644 index 00000000..b8045b50 --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_14/molecule.yml @@ -0,0 +1,81 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +driver: + name: podman +platforms: + - name: rhel9.4 + image: registry.access.redhat.com/ubi9/ubi:9.4 + dockerfile: ../default/Dockerfile-rhel.j2 + command: /sbin/init + published_ports: + - 0.0.0.0:6432:5432/tcp + - name: rhel8.10 + image: registry.access.redhat.com/ubi8/ubi:8.10 + dockerfile: ../default/Dockerfile-rhel.j2 + command: /sbin/init + published_ports: + - 0.0.0.0:7432:5432/tcp + - name: ubuntu22.04 + image: ubuntu:22.04 + dockerfile: ../default/Dockerfile-ubuntu.j2 + command: /lib/systemd/systemd + published_ports: + - 0.0.0.0:8432:5432/tcp + - name: ubuntu20.04 + image: ubuntu:20.04 + dockerfile: ../default/Dockerfile-ubuntu.j2 + command: /lib/systemd/systemd + published_ports: + - 0.0.0.0:9432:5432/tcp +dependency: + name: galaxy + options: + requirements-file: "${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml" + role-file: "${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml" +provisioner: + playbooks: + converge: ../rdbms_server_postgresql_default/converge.yml + prepare: ../rdbms_server_postgresql_default/prepare.yml + verify: ../rdbms_server_postgresql_default/verify.yml + side_effect: ../rdbms_server_postgresql_default/side_effect.yml + inventory: + group_vars: + all: + database_type: postgresql + database_version: 14 + database_tls: no + host_vars: + rhel9.4: + psql_port: 6432 + rhel8.10: + psql_port: 7432 + ubuntu22.04: + psql_port: 8432 + ubuntu20.04: + psql_port: 9432 +scenario: + test_sequence: + - dependency + - cleanup + - destroy + - syntax + - create + - prepare + - converge + # - idempotence # geerlinguy.postgresql has non-ideopotent tasks, too. + - side_effect + - verify + - cleanup + - destroy diff --git a/extensions/molecule/rdbms_server_postgresql_14/requirements.yml b/extensions/molecule/rdbms_server_postgresql_14/requirements.yml new file mode 100644 index 00000000..b5f72813 --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_14/requirements.yml @@ -0,0 +1,20 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +collections: + - containers.podman + - community.postgresql + +roles: + - geerlingguy.postgresql diff --git a/extensions/molecule/rdbms_server_postgresql_14_tls/cleanup.yml b/extensions/molecule/rdbms_server_postgresql_14_tls/cleanup.yml new file mode 100644 index 00000000..2624b09a --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_14_tls/cleanup.yml @@ -0,0 +1,28 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Remove CA certificate + hosts: localhost + connection: local + gather_facts: no + tasks: + - name: Remove CA private key + ansible.builtin.file: + path: ca-certificate.key + state: absent + + - name: Remove self-signed CA certificate + ansible.builtin.file: + path: ca-certificate.pem + state: absent diff --git a/extensions/molecule/rdbms_server_postgresql_14_tls/molecule.yml b/extensions/molecule/rdbms_server_postgresql_14_tls/molecule.yml new file mode 100644 index 00000000..e38f6d59 --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_14_tls/molecule.yml @@ -0,0 +1,87 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- + +driver: + name: podman +platforms: + - name: rhel9.4 + image: registry.access.redhat.com/ubi9/ubi:9.4 + dockerfile: ../default/Dockerfile-rhel.j2 + command: /sbin/init + published_ports: + - 0.0.0.0:6432:5432/tcp + - name: rhel8.10 + image: registry.access.redhat.com/ubi8/ubi:8.10 + dockerfile: ../default/Dockerfile-rhel.j2 + command: /sbin/init + published_ports: + - 0.0.0.0:7432:5432/tcp + - name: ubuntu22.04 + image: ubuntu:22.04 + dockerfile: ../default/Dockerfile-ubuntu.j2 + command: /lib/systemd/systemd + published_ports: + - 0.0.0.0:8432:5432/tcp + - name: ubuntu20.04 + image: ubuntu:20.04 + dockerfile: ../default/Dockerfile-ubuntu.j2 + command: /lib/systemd/systemd + published_ports: + - 0.0.0.0:9432:5432/tcp +dependency: + name: galaxy + options: + requirements-file: "${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml" + role-file: "${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml" +provisioner: + name: ansible + playbooks: + converge: ../rdbms_server_postgresql_default/converge.yml + prepare: prepare.yml + verify: verify.yml + side_effect: ../rdbms_server_postgresql_default/side_effect.yml + inventory: + group_vars: + all: + database_type: postgresql + database_version: 14 + database_tls: yes + tls_cert_path_generic: /opt/security/pki/host.pem + tls_key_path_plaintext_generic: /opt/security/pki/host.key.unenc + tls_chain_path: /opt/security/pki/ca-certificate.pem + host_vars: + rhel9.4: + psql_port: 6432 + rhel8.10: + psql_port: 7432 + ubuntu22.04: + psql_port: 8432 + ubuntu20.04: + psql_port: 9432 +scenario: + test_sequence: + - dependency + - cleanup + - destroy + - syntax + - create + - prepare + - converge + # - idempotence # geerlinguy.postgresql has non-idempotent tasks, too. + - side_effect + - verify + - cleanup + - destroy diff --git a/extensions/molecule/rdbms_server_postgresql_14_tls/prepare.yml b/extensions/molecule/rdbms_server_postgresql_14_tls/prepare.yml new file mode 100644 index 00000000..ea317ad0 --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_14_tls/prepare.yml @@ -0,0 +1,125 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Establish CA certificate + hosts: localhost + connection: local + tasks: + - name: Create CA private key + community.crypto.openssl_privatekey: + path: ./ca-certificate.key + mode: 0600 + + - name: Create CSR for CA certificate + community.crypto.openssl_csr_pipe: + privatekey_path: ca-certificate.key + common_name: Molecule CA + use_common_name_for_san: false + basic_constraints: + - 'CA:TRUE' + basic_constraints_critical: true + key_usage: + - keyCertSign + key_usage_critical: true + register: __ca_csr + + - name: Create self-signed CA certificate from CSR + community.crypto.x509_certificate: + path: ./ca-certificate.pem + csr_content: "{{ __ca_csr.csr }}" + privatekey_path: ca-certificate.key + provider: selfsigned + mode: 0644 + +- name: Set up TLS certificates + hosts: all + gather_facts: yes + tasks: + - name: Update pip + ansible.builtin.pip: + name: pip + state: latest + + - name: Install Rust (RHEL 8) + when: + - ansible_os_family == "RedHat" + - ansible_distribution_major_version == 8 + ansible.builtin.package: + name: rust + + - name: Install Python Rust setup tools + when: + - ansible_os_family == "RedHat" + - ansible_distribution_major_version == 8 + ansible.builtin.pip: + name: setup_rust + + - name: Install cryptography Python library + ansible.builtin.pip: + name: cryptography + + - name: Establish postgres group + ansible.builtin.group: + name: postgres + + - name: Establish PKI directory + ansible.builtin.file: + path: /opt/security/pki + state: directory + mode: 0755 + + - name: Copy CA certificate from controller + ansible.builtin.copy: + src: ca-certificate.pem + dest: /opt/security/pki/ca-certificate.pem + mode: 0644 + + - name: Create host private key + community.crypto.openssl_privatekey: + path: /opt/security/pki/host.key + return_content: yes + group: postgres + mode: 0640 + register: __key + + - name: Write unencrypted host private key + ansible.builtin.copy: + dest: /opt/security/pki/host.key.unenc + content: "{{ __key.privatekey }}" + group: postgres + mode: 0640 + + - name: Create CSR for host certificate + community.crypto.openssl_csr_pipe: + privatekey_path: /opt/security/pki/host.key + use_common_name_for_san: false + register: __csr + + - name: Sign host certificate with self-signed CA certificate + community.crypto.x509_certificate_pipe: + csr_content: "{{ __csr.csr }}" + provider: ownca + ownca_path: ca-certificate.pem + ownca_privatekey_path: ca-certificate.key + ownca_not_after: +1d + ownca_not_before: "-1d" + delegate_to: localhost + register: __cert + + - name: Write host certificate + ansible.builtin.copy: + dest: /opt/security/pki/host.pem + content: "{{ __cert.certificate }}" + mode: 0640 + group: postgres diff --git a/extensions/molecule/rdbms_server_postgresql_14_tls/requirements.yml b/extensions/molecule/rdbms_server_postgresql_14_tls/requirements.yml new file mode 100644 index 00000000..b5f72813 --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_14_tls/requirements.yml @@ -0,0 +1,20 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +collections: + - containers.podman + - community.postgresql + +roles: + - geerlingguy.postgresql diff --git a/extensions/molecule/rdbms_server_postgresql_14_tls/verify.yml b/extensions/molecule/rdbms_server_postgresql_14_tls/verify.yml new file mode 100644 index 00000000..f6fb87c0 --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_14_tls/verify.yml @@ -0,0 +1,54 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Confirm local database access + hosts: all + gather_facts: no + tasks: + - name: Ping database locally + community.postgresql.postgresql_ping: + become: yes + become_user: postgres + register: postgres_user + failed_when: not postgres_user.is_available + +- name: Confirm external database access + hosts: all + gather_facts: no + tasks: + - name: Ping database as authorized user + community.postgresql.postgresql_ping: + login_host: "0.0.0.0" + port: "{{ psql_port }}" + db: test + login_user: user_one + login_password: authorized + ssl_mode: verify-ca + ca_cert: ./ca-certificate.pem + delegate_to: localhost + register: authorized_user + failed_when: not authorized_user.is_available + + - name: Ping database as non-authorized user + community.postgresql.postgresql_ping: + login_host: "0.0.0.0" + port: "{{ psql_port }}" + db: test + login_user: user_two + login_password: no_password_is_set + ssl_mode: verify-ca + ca_cert: ./ca-certificate.pem + delegate_to: localhost + register: not_authorized_user + failed_when: not_authorized_user.is_available diff --git a/extensions/molecule/rdbms_server_postgresql_default/converge.yml b/extensions/molecule/rdbms_server_postgresql_default/converge.yml new file mode 100644 index 00000000..649235b4 --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_default/converge.yml @@ -0,0 +1,21 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Converge + hosts: all + gather_facts: true + tasks: + - name: Install the RDBMS server + ansible.builtin.include_role: + name: cloudera.exe.rdbms_server diff --git a/extensions/molecule/rdbms_server_postgresql_default/molecule.yml b/extensions/molecule/rdbms_server_postgresql_default/molecule.yml new file mode 100644 index 00000000..f3863887 --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_default/molecule.yml @@ -0,0 +1,74 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- + +driver: + name: podman +platforms: + - name: rhel9.4 + image: registry.access.redhat.com/ubi9/ubi:9.4 + dockerfile: ../default/Dockerfile-rhel.j2 + command: /sbin/init + published_ports: + - 0.0.0.0:6432:5432/tcp + - name: rhel8.10 + image: registry.access.redhat.com/ubi8/ubi:8.10 + dockerfile: ../default/Dockerfile-rhel.j2 + command: /sbin/init + published_ports: + - 0.0.0.0:7432:5432/tcp + - name: ubuntu22.04 + image: ubuntu:22.04 + dockerfile: ../default/Dockerfile-ubuntu.j2 + command: /lib/systemd/systemd + published_ports: + - 0.0.0.0:8432:5432/tcp + - name: ubuntu20.04 + image: ubuntu:20.04 + dockerfile: ../default/Dockerfile-ubuntu.j2 + command: /lib/systemd/systemd + published_ports: + - 0.0.0.0:9432:5432/tcp +dependency: + name: galaxy + options: + requirements-file: "${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml" + role-file: "${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml" +provisioner: + name: ansible + inventory: + host_vars: + rhel9.4: + psql_port: 6432 + rhel8.10: + psql_port: 7432 + ubuntu22.04: + psql_port: 8432 + ubuntu20.04: + psql_port: 9432 +scenario: + test_sequence: + - dependency + - cleanup + - destroy + - syntax + - create + - prepare + - converge + # - idempotence # geerlinguy.postgresql has non-idempotent tasks, too. + - side_effect + - verify + - cleanup + - destroy diff --git a/extensions/molecule/rdbms_server_postgresql_default/prepare.yml b/extensions/molecule/rdbms_server_postgresql_default/prepare.yml new file mode 100644 index 00000000..0dc02c41 --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_default/prepare.yml @@ -0,0 +1,18 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Set up TLS certificates + hosts: all + gather_facts: yes + tasks: diff --git a/extensions/molecule/rdbms_server_postgresql_default/requirements.yml b/extensions/molecule/rdbms_server_postgresql_default/requirements.yml new file mode 100644 index 00000000..74f1f387 --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_default/requirements.yml @@ -0,0 +1,19 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +collections: + - containers.podman + - community.postgresql +roles: + - geerlingguy.postgresql diff --git a/extensions/molecule/rdbms_server_postgresql_default/side_effect.yml b/extensions/molecule/rdbms_server_postgresql_default/side_effect.yml new file mode 100644 index 00000000..c4772ba4 --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_default/side_effect.yml @@ -0,0 +1,39 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +- name: Add test users to PostgreSQL + hosts: all + gather_facts: yes + become: yes + tasks: + - name: Create test database + community.postgresql.postgresql_db: + name: test + become_user: postgres + + - name: Add authorized user + community.postgresql.postgresql_user: + db: test + name: user_one + password: authorized + comment: Authorized User + become_user: postgres + + - name: Add non-authorized user + community.postgresql.postgresql_user: + db: test + name: user_two + password: "" + comment: Unauthorized User + become_user: postgres diff --git a/extensions/molecule/rdbms_server_postgresql_default/verify.yml b/extensions/molecule/rdbms_server_postgresql_default/verify.yml new file mode 100644 index 00000000..9703ea0a --- /dev/null +++ b/extensions/molecule/rdbms_server_postgresql_default/verify.yml @@ -0,0 +1,58 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# - name: Set up required packages +# hosts: all +# gather_facts: no +# tasks: +# - name: Add psycopg2 Python library +# ansible.builtin.pip: +# name: psycopg2-binary + +- name: Confirm local database access + hosts: all + gather_facts: no + tasks: + - name: Ping database locally + community.postgresql.postgresql_ping: + become: yes + become_user: postgres + register: postgres_user + failed_when: not postgres_user.is_available + +- name: Confirm external database access + hosts: all + gather_facts: no + tasks: + - name: Ping database as authorized user + community.postgresql.postgresql_ping: + login_host: "0.0.0.0" + port: "{{ psql_port }}" + db: test + login_user: user_one + login_password: authorized + delegate_to: localhost + register: authorized_user + failed_when: not authorized_user.is_available + + - name: Ping database as non-authorized user + community.postgresql.postgresql_ping: + login_host: "0.0.0.0" + port: "{{ psql_port }}" + db: test + login_user: user_two + login_password: no_password_is_set + delegate_to: localhost + register: not_authorized_user + failed_when: not_authorized_user.is_available diff --git a/pyproject.toml b/pyproject.toml index eee1de66..878d2118 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,15 @@ dependencies = [ ] installer = "uv" +[tool.pytest.ini_options] +testpaths = [ + "tests", +] +filterwarnings = [ + "ignore:AnsibleCollectionFinder has already been configured", + "ignore:'crypt' is deprecated and slated for removal in Python 3.13:DeprecationWarning", +] + [build-system] requires = ["hatchling"] build-backend = "hatchling.build" diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 00000000..fbebe191 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,23 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +molecule +molecule[lint] +molecule-plugins +molecule-plugins[podman] +tox-ansible +pre-commit + +# For the Molecule scenarios for rdbms_server role +psycopg2-binary diff --git a/requirements.txt b/requirements.txt index 2eaa8cf9..5ecc8933 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,10 @@ -# Copyright 2023 Cloudera, Inc. All Rights Reserved. +# Copyright 2024 Cloudera, Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, @@ -13,5 +13,5 @@ # limitations under the License. netaddr - -molecule[lint]==3.4 # Pinned due to https://github.com/ansible-community/molecule/issues/3243 +psycopg2-binary +cryptography diff --git a/requirements.yml b/requirements.yml index af582d25..374c0267 100644 --- a/requirements.yml +++ b/requirements.yml @@ -42,8 +42,9 @@ collections: version: 1.0.2 - name: ansible.posix version: 1.3.0 - - name: community.crypto - version: 2.17.1 + # Is included via cloudera.cluster + # - name: community.crypto + # version: 2.2.1 - name: community.mysql version: 3.8.0 - name: community.postgresql @@ -51,4 +52,4 @@ collections: - name: freeipa.ansible_freeipa version: 1.11.1 - name: cloud.terraform - version: 1.11.0 + version: 3.0.0 diff --git a/roles/rdbms/server/tasks/main.yml b/roles/rdbms/server/tasks/main.yml index 4417562a..387c8f59 100644 --- a/roles/rdbms/server/tasks/main.yml +++ b/roles/rdbms/server/tasks/main.yml @@ -17,7 +17,7 @@ - name: DEPRECATION WARNING ansible.builtin.debug: msg: - - This role has been moved to M(cloudera.exe.rdbms_server). + - This role has been moved to cloudera.exe.rdbms_server. - Please update your playbooks and roles accordingly. - name: Include database type variables diff --git a/roles/rdbms_server/files/utf8-template.sql b/roles/rdbms_server/files/utf8-template.sql new file mode 100644 index 00000000..7fc16312 --- /dev/null +++ b/roles/rdbms_server/files/utf8-template.sql @@ -0,0 +1,22 @@ +-- Copyright 2024 Cloudera, Inc. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +update pg_database set datallowconn = TRUE where datname = 'template0'; +\c template0 +update pg_database set datistemplate = FALSE where datname = 'template1'; +drop database template1; +create database template1 with template = template0 encoding = 'UTF8'; +update pg_database set datistemplate = TRUE where datname = 'template1'; +\c template1 +update pg_database set datallowconn = FALSE where datname = 'template0'; diff --git a/roles/rdbms_server/handlers/main.yml b/roles/rdbms_server/handlers/main.yml index 034c8b04..2f942fed 100644 --- a/roles/rdbms_server/handlers/main.yml +++ b/roles/rdbms_server/handlers/main.yml @@ -16,3 +16,6 @@ - name: yum clean metadata ansible.builtin.command: yum clean metadata + +- name: dnf clean metadata + ansible.builtin.command: dnf clean metadata diff --git a/roles/rdbms_server/tasks/postgresql/Debian.yml b/roles/rdbms_server/tasks/postgresql/Debian.yml index 5bdca9d7..ed20cda0 100644 --- a/roles/rdbms_server/tasks/postgresql/Debian.yml +++ b/roles/rdbms_server/tasks/postgresql/Debian.yml @@ -25,7 +25,7 @@ - name: Install PostgreSQL repository ansible.builtin.apt_repository: - repo: "deb [arch=amd64 signed-by=/etc/apt/keyrings/postgresql.asc] https://apt.postgresql.org/pub/repos/apt {{ ansible_distribution_release }}-pgdg main" + repo: "deb [signed-by=/etc/apt/keyrings/postgresql.asc] https://apt.postgresql.org/pub/repos/apt {{ ansible_distribution_release }}-pgdg main" state: present when: not skip_rdbms_repo_setup diff --git a/roles/rdbms_server/tasks/postgresql/RedHat.yml b/roles/rdbms_server/tasks/postgresql/RedHat.yml index 404d96a6..d457c1e1 100644 --- a/roles/rdbms_server/tasks/postgresql/RedHat.yml +++ b/roles/rdbms_server/tasks/postgresql/RedHat.yml @@ -16,20 +16,20 @@ ansible.builtin.yum_repository: name: pgdg-common description: PostgreSQL common for RHEL/CentOS - baseurl: https://download.postgresql.org/pub/repos/yum/common/redhat/rhel-$releasever-$basearch - gpgkey: https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-RHEL + baseurl: "https://download.postgresql.org/pub/repos/yum/common/redhat/rhel-$releasever-$basearch" + gpgkey: "https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-{{ (ansible_architecture in ['aarch64', 'arm64']) | ternary('AARCH64-', '') }}RHEL" when: not skip_rdbms_repo_setup - name: Install PostgreSQL version repository ansible.builtin.yum_repository: name: pgdg description: PostgreSQL {{ postgresql_version }} for RHEL/CentOS - baseurl: https://download.postgresql.org/pub/repos/yum/{{ postgresql_version }}/redhat/rhel-$releasever-$basearch - gpgkey: https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-RHEL + baseurl: "https://download.postgresql.org/pub/repos/yum/{{ postgresql_version }}/redhat/rhel-$releasever-$basearch" + gpgkey: "https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-{{ (ansible_architecture in ['aarch64', 'arm64']) | ternary('AARCH64-', '') }}RHEL" when: not skip_rdbms_repo_setup - name: Disable default Postgres module in RHEL 8 or greater - ansible.builtin.command: dnf module disable -y postgresql + ansible.builtin.command: dnf module disable -qy postgresql register: __postgres_module_result changed_when: - '"Disabling modules" in __postgres_module_result.stdout' @@ -49,10 +49,24 @@ - name: Remove repositories and clean metadata ansible.builtin.yum_repository: - name: "{{ item }}" + name: "{{ repo }}" state: absent - with_items: + loop: - pgdg-common - pgdg + loop_control: + loop_var: repo when: not skip_rdbms_repo_setup notify: yum clean metadata + +- name: Install psycopg2 build prerequisites + when: ansible_distribution_major_version == "8" + ansible.builtin.package: + name: + - gcc + - libpq-devel + +# This fails if the PSQL repositories are not removed +- name: Install psycopg2 library + ansible.builtin.pip: + name: psycopg2-binary diff --git a/roles/rdbms_server/tasks/postgresql/template_fix.yml b/roles/rdbms_server/tasks/postgresql/template_fix.yml index 6be8d86c..82a0468e 100644 --- a/roles/rdbms_server/tasks/postgresql/template_fix.yml +++ b/roles/rdbms_server/tasks/postgresql/template_fix.yml @@ -26,7 +26,7 @@ - name: Copy SQL to change template to UTF-8 ansible.builtin.copy: - src: files/utf8-template.sql + src: utf8-template.sql dest: "{{ __sql.path }}/utf8-template.sql" owner: postgres group: postgres diff --git a/tests/integration/test_molecule_integration.py b/tests/integration/test_molecule_integration.py new file mode 100644 index 00000000..70ce55fa --- /dev/null +++ b/tests/integration/test_molecule_integration.py @@ -0,0 +1,26 @@ +# Copyright 2024 Cloudera, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import, division, print_function + +from pytest_ansible.molecule import MoleculeScenario + + +def test_integration(molecule_scenario: MoleculeScenario) -> None: + """Run molecule for each scenario. + + :param molecule_scenario: The molecule scenario object + """ + proc = molecule_scenario.test() + assert proc.returncode == 0 diff --git a/tox-ansible.ini b/tox-ansible.ini new file mode 100644 index 00000000..9dca3959 --- /dev/null +++ b/tox-ansible.ini @@ -0,0 +1,19 @@ +; Copyright 2024 Cloudera, Inc. +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; https://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, +; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +; See the License for the specific language governing permissions and +; limitations under the License. + +[ansible] +skip = + 2.17 + devel + milestone