From e6c611110e07bf3ea5082b0a323224868270d4aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20H=C3=B6sel?= Date: Sun, 23 Jul 2023 20:36:57 +0200 Subject: [PATCH] refactor testing: extend plugin tests, get rid of scripts (#305) * WIP * module test work * roles *should* run? * use devel orb for now * docker-compose => docker compose * fix "not a tty errors" * try limiting to cgroupv1 * final fixes * what the fuck * use 3 executors for module tests * lower further * aa * cleanup molecule config and galaxy.yml * update documentation --- .circleci/config.yml | 54 ++-- .ansible-lint => .config/ansible-lint.yaml | 3 +- .config/molecule/config.yml | 36 +++ .readthedocs.yml | 2 +- CONTRIBUTING.md | 299 +++++++----------- README.md | 47 +-- galaxy.yml | 20 +- requirements-molecule.txt | 6 - requirements.txt | 13 +- .../molecule/default/molecule.yml | 35 +- .../molecule/default/requirements.txt | 2 +- .../molecule/default/molecule.yml | 37 +-- .../molecule/default/requirements.txt | 2 +- roles/step_ca/molecule/default/molecule.yml | 35 -- .../step_ca/molecule/default/requirements.txt | 2 +- roles/step_cli/molecule/default/molecule.yml | 34 -- .../molecule/default/requirements.txt | 2 +- .../render_template.sh | 7 +- scripts/setup.sh | 2 +- tests/constants.sh | 8 - tests/integration/docker-compose-local.yml | 36 +++ tests/integration/docker-compose-remote.yml | 44 +++ tests/integration/docker/local-ca/Dockerfile | 27 ++ .../{step-ca-ansible => local-ca}/README.md | 0 .../docker/local-ca/step-ca.service | 16 + tests/integration/docker/local-ca/step-ca.sh | 23 ++ .../docker/step-ca-ansible/Dockerfile | 57 ---- .../docker/step-ca-ansible/pre-entry.sh | 7 - .../integration_config.yml.template-local | 14 + ...=> integration_config.yml.template-remote} | 10 +- .../targets/setup_remote_ca/tasks/main.yml | 25 ++ .../targets/step_ca_bootstrap/tasks/main.yml | 12 + .../step_ca_provisioner/tasks/main.yml | 7 +- tests/roles/requirements.txt | 4 + tests/sanity/docker-compose.yml | 15 + tests/sanity/ignore.txt | 1 + tests/test-modules | 7 - tests/test-modules-integration | 123 ------- tests/test-modules-sanity | 25 -- tests/test-roles | 60 ---- tests/util.sh | 24 -- tox.ini | 70 +++- 42 files changed, 499 insertions(+), 754 deletions(-) rename .ansible-lint => .config/ansible-lint.yaml (92%) create mode 100644 .config/molecule/config.yml delete mode 100644 requirements-molecule.txt rename tests/integration/render_config.sh => scripts/render_template.sh (51%) delete mode 100644 tests/constants.sh create mode 100644 tests/integration/docker-compose-local.yml create mode 100644 tests/integration/docker-compose-remote.yml create mode 100644 tests/integration/docker/local-ca/Dockerfile rename tests/integration/docker/{step-ca-ansible => local-ca}/README.md (100%) create mode 100644 tests/integration/docker/local-ca/step-ca.service create mode 100644 tests/integration/docker/local-ca/step-ca.sh delete mode 100644 tests/integration/docker/step-ca-ansible/Dockerfile delete mode 100644 tests/integration/docker/step-ca-ansible/pre-entry.sh create mode 100644 tests/integration/integration_config.yml.template-local rename tests/integration/{integration_config.yml.template => integration_config.yml.template-remote} (63%) create mode 100644 tests/roles/requirements.txt create mode 100644 tests/sanity/docker-compose.yml delete mode 100755 tests/test-modules delete mode 100755 tests/test-modules-integration delete mode 100755 tests/test-modules-sanity delete mode 100755 tests/test-roles delete mode 100644 tests/util.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index c4bf9eb3..70ffe0a2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,31 +1,7 @@ version: 2.1 orbs: - collection-testing: maxhoesel-ansible/ansible-collection-testing@0.3.8 - -jobs: - modules-sanity: - executor: collection-testing/default - steps: - - checkout - - collection-testing/install-podman-on-ubuntu - - collection-testing/install-requirements-txt - - run: - command: tests/test-modules-sanity - environment: - TEST_RETRIES: 3 - TEST_RETRY_DELAY: 300 - modules-integration: - executor: collection-testing/default - steps: - - checkout - - collection-testing/install-podman-on-ubuntu - - collection-testing/install-requirements-txt - - run: - command: tests/test-modules-integration - environment: - TEST_RETRIES: 3 - TEST_RETRY_DELAY: 300 + collection-testing: maxhoesel-ansible/ansible-collection-testing@0.4.0 filters: &semver-tagged tags: @@ -37,18 +13,30 @@ filters: &semver-tagged workflows: ci: jobs: - - collection-testing/pre-commit-lint - - collection-testing/antsibull-docs - - collection-testing/tox-role-scenarios: - parallelism: 16 + - collection-testing/pre-commit-lint: + name: Lint + - collection-testing/antsibull-docs: + name: Generate Docs + - collection-testing/run-tox-environments: + name: Test Modules + match-environments: \-test\- + parallelism: 3 + resource-class: medium + retries: 1 + retry-delay: 60 + - collection-testing/run-tox-environments: + name: Test Roles + match-environments: roles + # number of scenarios * ansible versions to test + parallelism: 12 resource-class: large - retries: 3 - retry-delay: 300 + retries: 1 + retry-delay: 60 - collection-testing/publish-github: + name: Publish Release to GitHub context: collection-publishing filters: *semver-tagged - collection-testing/publish-galaxy: + name: Publish to Galaxy context: collection-publishing filters: *semver-tagged - - modules-sanity - - modules-integration diff --git a/.ansible-lint b/.config/ansible-lint.yaml similarity index 92% rename from .ansible-lint rename to .config/ansible-lint.yaml index bb2a4d72..186b27c2 100644 --- a/.ansible-lint +++ b/.config/ansible-lint.yaml @@ -1,4 +1,5 @@ -# Don't automatically install roles from galaxy to prevent flaky tests +--- +# Don't automatically install roles from galaxy offline: true exclude_paths: diff --git a/.config/molecule/config.yml b/.config/molecule/config.yml new file mode 100644 index 00000000..746d631c --- /dev/null +++ b/.config/molecule/config.yml @@ -0,0 +1,36 @@ +--- +dependency: + name: galaxy + +driver: + name: podman + +provisioner: + name: ansible + env: {} + # Cannot enable pipelining for podman at this time: + # https://github.com/ansible-community/molecule-podman/issues/2 + #ANSIBLE_PIPELINING: false + inventory: + group_vars: + all: + # Versions to use, passed in from Tox + step_cli_version: ${STEP_CLI_VERSION} + step_ca_version: ${STEP_CA_VERSION} + +scenario: + test_sequence: + - destroy + - dependency + - syntax + - create + - prepare + - converge + - idempotence + - check # also run check mode in regular tests + - side_effect + - verify + - destroy + +verifier: + name: ansible diff --git a/.readthedocs.yml b/.readthedocs.yml index ed421b9a..d728a0a1 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -5,7 +5,7 @@ python: - requirements: docs/requirements.txt build: - os: "ubuntu-20.04" + os: "ubuntu-22.04" tools: python: "3.10" apt_packages: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a701d7c8..e6cd1a9e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,39 +1,44 @@ # Contribution Guide -Below you will find the information needed to contribute to this project. +This document contains the information needed to contribute to this project. +In here you will find the basic steps for getting started, as well as an overview over our testing system. Note that by contributing to this collection, you agree with the code of conduct you can find [here.](/CODE_OF_CONDUCT.md) ## Getting Started -To begin development on this collection, you need to have the following dependencies installed: +Prerequisites: -- A python version that supports the release of `ansible-core` in `requirements.txt` (see ["Control node Python" here](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#node-requirement-summary)) +- A recent version of Python supported by `ansible-core` (see [here](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#control-node-requirements)) +- For role tests: `podman` 4 or newer set up as shown [below](#setting-up-podman) (note that Docker will *not* work for role tests!) +- For plugin tests: A recent version of Docker -To get started quickly: +Steps: -1. Fork the repository and clone it to your local machine -2. Run `./scripts/setup.sh` to configure a local dev environment (virtualenv) with all required dependencies +1. Clone the repository (or your fork) to your local machine +2. Run `./scripts/setup.sh`. This will set up a virtual environment with all required dependencies for development 3. Activate the virtualenv with `source .venv/bin/activate` -4. Make your changes and commit them to a new branch -5. Run the tests locally by running `./tests/test-`. See below for more details on these testing scripts -6. Once you're done, commit your changes (make sure that you are in the venv). - Pre-commit will format your code and check for any obvious errors when you do so. +4. Make your changes +5. Run the relevant tests using the instructions in [here](#testing-changes) +6. Once you're done, commit your changes and open a PR -## Developing Modules +## Developing Content -The modules in this collection are mostly simple wrappers around the `step-cli` tool. -Feel free to add a new module if you would like to implement another subcommand. -Here are some general hints for module development: +### Plugins (and Modules) -- All modules should target Python 3.6 as the minimum supported version +The plugins in this collection are mostly simple wrappers around the `step-cli` tool. +Feel free to add a new plugin if you would like to implement another functionality. +Here are some general hints for plugin development: + +- All plugins should target Python 3.6 as the minimum supported version - Read the [Ansible module conventions](https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_best_practices.html) -- Use the pre-existing `doc_fragments` and `module_utils` python modules where applicable. Feel free to use an existing module as a base +- Use the pre-existing `doc_fragments` and `module_utils` python modules where applicable. Feel free to use an existing plugin/module as a base - Name your module according to the `step-li` command that it wraps around (Example: `step-cli ca provisioner` -> `step_ca_provisioner`). - Use the `CLIWrapper` class in [step_cli_wrapper.py](/plugins/module_utils/step_cli_wrapper.py) to run step-cli commands -- Try to make the calls idempotent where possible. Modules should always support check mode +- Try to make the calls idempotent where possible. +- Modules should always support check mode -## Developing Roles +### Roles Each role in this collection performs a complex task to bring a remote host into a desired state. If you want to write a new role, look to the existing ones for inspiration. @@ -41,58 +46,88 @@ If you want to write a new role, look to the existing ones for inspiration. Some general guidelines: - Try to support most common Linux distributions (including Ubuntu, Debian, Fedora and Rockylinux) + - You should test every supported distro in your `molecule.yml` see [here](#roles-2) - Keep the configuration for the user simple and try to provide sensible defaults where possible - Try to avoid using complex data structures as role variables/parameters, use simple values that can be composed easily instead. +- Make sure to document any role variables in both the `README.md` and in the `meta/argument_specs.yml` file. + Te latter is used to generate role documentation programmatically. + +## Testing Changes -## Testing Infrastructure +We aim to test all of the components in this collection as thoroughly as possible. +We currently test all components using the following testing matrix: -Writing tests for your contributions ensure that they continue working in the future. -In addition to the testing venv, you will also need the following: +- Ansible version: The three most recent major releases (such as 6,7,8), with a compatible host Python +- Node Python: The minimum supported Python version (see `tox.ini`) +- Step-CLI/CA version: The most recent minor release corresponding to the collections version (e.g. collection version `0.24.X` is teated with `step-cli/ca` `0.24.y`) +- For each entry in this matrix, we test all roles on all of their supported platforms, as well as all modules/plugins -- `podman` 4.0 or newer for both ansible-test and molecule (see [here](#setting-up-podman) for a setup guide). +To make testing all these permutations easier, we use `tox`to manage test scenarios. +If you set up the test environment as described in [the Getting Started guide](#getting-started), you should be able to run `tox -l` from the collection root: -We use `tox` in conjunction with `tox-ansible` to run tests against both modules and roles with multiple Ansible versions. -If you set up your environment as described [above](#getting-started), you should already have everything installed. +This should print a list of test environments (scenarios) that you can run. +To run a specific environment, just use `tox -e {environment}`. +You can also use substitution syntax to run multiple environments at once: -To run tests, use the wrapper scripts in the `tests` directory from the collection root: +```bash +# Will run the py3-ansible7-roles-step_cli environment +tox -e py3-ansible7-roles-step_cli -- [`./tests/test-modules`](./tests/test-modules) will run ansible-test against the modules in the collection and verify them - - [`test-modules-sanity`](./tests/test-modules-sanity) performs basic syntax and validation checks - - [`test-modules-integration`]((./tests/test-modules-integration)) runs the module integration [test targets](./tests/integration/targets/). -- [`./tests/test-roles`](./tests/test-roles) will use `molecule` to run tests on all roles in the collection (this might take a long time!) - - To limit the scope to a single role, add a filter parameter: `./tests/test-roles step_cli` - - Alternatively, you can use `tox -l` to list all available scenarios and then select a single one with `tox -e ` +# Will run XXX-roles-step_cli environment with ansible versions 6,7 and 8 +# Note the single quotes around the string! +tox -e 'py3-ansible{6,7,8}-roles-step_cli' +``` -### Setting up Podman +### Plugins (and Modules) -To run role or module tests, you will need the following: +Modules (and plugins in general) are verified using `ansible-test`, specifically sanity, unit (TBD) and integration tests, all run from tox. +Since many plugins in this collection need access to a step-ca server, the `tox` environments spin up additional docker containers as needed - +see [`tox.ini`](./tox.ini) and the `docker-compose.yml` files in `tests/{integration/sanity}` for details. -- `podman` version 4+ (as it comes with the new netvark networking stack) -- `aardvark-dns`, a plugin for netvark which provides DNS between containers in the same network +You should always run the plugin tests after making changes to one (sanity, unit (TBD), integration): + +```bash +tox -e 'py3-ansible215-test-{sanity,integration,#add other environments here}' +``` -On Archlinux, you can install these with this command: `sudo pacman -S podman aardvark-dns`. -Other distributions may not have these versions [available in their repositories](https://pkgs.org/search/?q=podman), you might have install things manually. +### Roles -**NOTE:** If you previously used an older (`<=3.x`) version of `podman` you will have to migrate to the new networking stack fist. This can be done with `podman system reset` +We use the `tox-ansible` plugin (v1) to integrate molecule scenarios into tox. +You can run these scenarios using `tox -e`, just like for module tests. -Finally, make sure that your user has a subuid/subgid configuration associated with them so that you can run rootless containers. -Check the `/etc/subuid` and `/etc/subgid` files for entries corresponding to your username. -If none are found, you can add them like so: `sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 ` (make sure that the range is not already taken by another user in `/etc/subuid`/`/etc/subgid`). +Molecule itself runs the subject role against several containers to verify its functionality across target systems. +Since some roles involve the management of systemd services, we need a container runtime with good systemd support, +something which `docker` sadly doesn't offer on [modern linux distros](https://gist.github.com/pinkeen/bba0a6790fec96d6c8de84bd824ad933). -Once you have applied your changes, run `podman system migrate` to force `podman` to pick up the new configuration. +Instead, we use `podman`, a daemonless, rootless container runtime developed by RedHat to be (mostly) compatible to docker, but with better support for certain features such as systemd. +See below for setup instructions. +Once podman is installed and running, you can use `tox` to run the molecule scenarios. + +#### Setting up Podman + +1. Ensure that you have the following packages installed: + - `podman` version 4+ (as it comes with the new netvark networking stack) + - `aardvark-dns`, a plugin for netvark which provides DNS between containers in the same network + - **NOTE:** If you previously used an older (`<=3.x`) version of `podman`, you will have to migrate to the new networking stack fist. This can be done with `podman system reset` +2. Ensure that your user has a subuid/subguid configuration associated with them so that you can run rootless containers + - Check the `/etc/subuid` and `/etc/subgid` files for entries corresponding to your username. + - If there is nothing, you can add them like so: `sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 ` (make sure that the range is not already taken by another user in `/etc/subuid`/`/etc/subgid`). +3. Once you have applied your changes, run `podman system migrate` to force `podman` to pick up the new configuration. That's it! Podman should now be working! To test it, you can run a container just like with docker: `podman run --rm -it ubuntu bash` -### Module Tests +## Writing Tests -The sanity tests should "just work" - they can give you valuable feedback and uncover accidental mistakes like mismatches between your module docs and argspec. +Any new new component or change to an existing one should be covered by tests to ensure that the code works, and that it keeps working into the future. +This section will help you in adding your own tests to this collection. -You can run the integration tests with the provided script. +#### Plugins -When writing module tests, you can start out by either copying an existing target such as [`step_ca_bootstrap`](./tests/integration/targets/step_ca_bootstrap/), or start out from scratch. -In either case, there is a step-ca available in the integration environment that you can use for your tests ([`test-modules-integration`](./tests/test-modules-integration) takes care of that, see [this section](#using-the-local-ca) for mode details on how this works). +Unit tests are currently not used in this collection, this section will be filled once the need for them arises. -To access it, your target structure must look like this: +For integration tests, you can get started by either copying an existing target such as [`step_ca_certificate`](./tests/integration/targets/step_ca_certificate/), or starting out from scratch. +Each target is an ansible role containing tasks that verify the functionality of a plugin. +Your targets directory structure should look like this: ``` meta/ @@ -100,67 +135,31 @@ meta/ tasks/ main.yml ``` + +Since many plugins need to connect to a CA to verify functionality, you can set up your target to connect to either a remote (network) or a local CA: + ```yaml # in meta/main.yml: -# Configure the local target to connect to the remote CA dependencies: + # Setup the target role for accessing a CA over the network, this is the default - setup_remote_ca - -# in tasks/main.yml: -- name: Get a certificate from the CA - maxhoesel.smallstep.step_ca_certificate: - # your tests go here + # Setup the target role for accessing a CA that is installed locally. + # This is needed for certain plugins/modules such as step_ca_provisioner. + # Mutually exclusive with setup_remote_ca + #- setup_local_ca ``` -See the [integration config template](./tests/integration/integration_config.yml.template) for a list of variables that you can use in your integration targets. - -#### Using the Local CA - -The CA is normally a separate docker container managed by the [`test-modules-integration`](./tests/test-modules-integration) script. -This works fine for most modules. -However, there are a few modules (such as [`step_ca_provisioner`](./plugins/modules/step_ca_provisioner.py)) that need to run on the same host that the CA is on. -If you are writing such a module, you can use a local CA by modifying your target like so: +Your target will then be set up for accessing the selected CA. +For more details, see the integration config templates for the [remote]() and the [local]() CA. ```yaml -# in meta/main.yml -dependencies: - - setup_local_ca - -# in tasks/main.yml -- block: - - name: Perform a test here - maxhoesel.smallstep.step_ca_module: - # - tags: - - local-ca +# in tasks/main.yml: +- name: Get a certificate from the remote CA + maxhoesel.smallstep.step_ca_certificate: + # parameters go here ``` -This will cause the target to get run on a host with a local CA installed, see the [integration config template](./tests/integration/integration_config.yml.template) for available values to access said CA. - -In the background, this is done by running the `local-ca` integration tests on a custom test host based on the official `step-ca` docker image, see [here](./tests/integration/docker/step-ca-ansible/README.md) for details. - - -### Role Tests - -Testing Ansible roles ensures that they do exactly what we want them to and nothing more. -We perform role tests using `molecule` with the `molecule-podman` driver with rootless containers. - ---- - -Why not just `molecule-docker`? Simple: This collection needs systemd inside its test containers to start the `ca` server among other things, and modern versions of [systemd do not work inside docker containers anymore](https://github.com/ansible-community/molecule/discussions/3108). -Meanwhile, `podman` has official support for passing through the host systemd and with the 4.0 release, podman networking is now mature enough that we can use it in our role tests. In addition, `podman` offers daemonless, rootless containers, which improves security considerably. - ---- - -Before you run any tests, make sure you follow the instructions in [the following section](#setting-up-podman). - -Once you're done, you can: - -- See all available scenarios with `tox -l` and look for the scenarios starting with `ansible-py*`. -- Run all molecule scenarios with `tests/test-roles` -- Pass a (partial) scenario name as a filter to limit execution (for example, `/tests/test-roles acme_cert` to limit molecule scenarios to the ACME role only) - -#### Writing Role Tests +#### Roles There are tons of good guides online for how to write tests using molecule. Alternatively, you can always look at the existing molecule scenarios in this collection @@ -176,7 +175,7 @@ some_role/ converge.yml molecule.yml prepare.yml - requirements.txt # --> symlink to /requirements-molecule.txt + requirements.txt # --> symlink to /tests/roles/requirements.txt verify.yml another-scenario/ ... @@ -185,96 +184,34 @@ some_role/ ``` The `requirements.txt` symlink is used by `tox-ansible` when running tests via `tox` to install a specific, known-good version of `molecule` and the `molecule-podman` driver. +That way, all roles and scenarios in this collection can use the same version of `molecule`. -Below is a shortened example `molecule.yml` that you can include in your own scenarios: - -```yaml ---- -dependency: - name: galaxy -driver: - name: podman -platforms: - # Example for a systemd-enabled test container - - name: step-cli-ubuntu-22 - # We use the images provided by geerlingguy where possible, as they provide out-of-the-box - # support for Ansible (pre_build_image=true, speeds up testing). - image: "docker.io/geerlingguy/docker-ubuntu2204-ansible" - # Grouping your hosts makes it easier to address them in your playbooks - groups: - - ubuntu - # By default, molecule overrides the container start command with a while-true loop. - # Instead, we want systemd to be the init command, which is already the case for geerlingguy images - override_command: false - # When false, this causes molecule to modify the container so that ansible can connect to it. - # This is not needed for geerlingguy containers - pre_build_image: true - # Force podman systemd integration. By default, podmans systemd detection only uses a few specific paths, - # which might get not detected on some distros - systemd: always - # Assign the container to a non-default network. This is required when you want inter-container communication. - network: molecule-ste-ca - -#... more platforms here - - # You might need a running CA for some of your role tests. - # Instead of creating your own, you can just use the official step-ca docker image and link your test containers to it. - # See the provisioner section for how the containers can access the CA - - name: step-ca - groups: - - ca - image: "docker.io/smallstep/step-ca:${STEP_CA_VERSION:-latest}" - # we don't actually use the container with ansible, leave it as is - override_command: false - pre_build_image: true - env: - DOCKER_STEPCA_INIT_NAME: "Molecule_Bootstrap_CA" - DOCKER_STEPCA_INIT_DNS_NAMES: "step-ca,localhost" - network: molecule-step-ca -provisioner: - name: ansible - env: - # This is required for podman to function: https://github.com/ansible-community/molecule-podman/issues/2 - ANSIBLE_PIPELINING: false - #ANSIBLE_VERBOSITY: 3 # enable for debugging - inventory: - group_vars: - all: - # These will be set to a concrete version if running from tox, - # fallback on the latest version if running molecule directly - step_cli_version: ${STEP_CLI_VERSION:-latest} - step_ca_version: ${STEP_CA_VERSION:-latest} - # Tell the test containers where to find the CA - step_bootstrap_ca_url: https://step-ca:9000 -scenario: - test_sequence: - - lint - - destroy - - dependency - - syntax - - create - - prepare - - converge - - idempotence - - check # also run check mode in regular tests - - side_effect - - verify - - destroy -verifier: - name: ansible -``` +The [root molecule config](./.config/molecule/config.yml) contains the basic settings for molecule, such as driver setup and the step utility versions. +In addition, your roles molecule scenario must define a set of platforms to test on, as well as any inventory configuration that you may need. +To get started you can copy the `molecule.yml` configuration from an existing role, then adjust it to suit your needs. ## Collection Docs -In addition to the `README.md`s, we use `antsibull-docs` to generate sphinx documentation for both modules and roles (from the `meta/argument_specs.yml` file). +In addition to the `README.md`s, we use `antsibull-docs` to generate sphinx documentation for both plugins and roles (from the `meta/argument_specs.yml` file). See [here](https://docs.ansible.com/ansible/latest/dev_guide/developing_collections_documenting.html) for more information about the build process. The CI also builds the docs to ensure they don't break silently. -## Misc Lifecycle Maintainer Information +## Maintainer information + +### Updating Tested Versions + +- Smallstep CLI/CA and Node Python: Bump the values in `tox.ini` (`consts` section) +- ansible-core (for plugins): Add the new core version to the following places in `tox.ini`: + - The `envlist` in `tox.ini` + - each `testenv:xxx` section header that deals with plugin tests + - Set the correct environment variable in the main `testenv` section +- ansible (for roles): Change + -- To update the smallstep cli/ca versions that are used to run the tests, bump the values in `tests/constants.sh` -- This project uses sematic versioning. Version numbers and releases/changelogs are automatically generated using [release-drafter](https://github.com/release-drafter/release-drafter), utilizing pull request labels. +- This project uses sematic versioning. the collection version stays in sync with the `step-cli`` utility version to ensure compatibility. + This means that any breaking changes can only be shipped when updates to the `step-cli` utility are released. + Version numbers and releases/changelogs are automatically generated using [release-drafter](https://github.com/release-drafter/release-drafter), utilizing pull request labels. - When merging a pull request, make sure to select an appropriate label (pr-bugfix, pr-feature, etc.). Release-drafter will automatically update the draft release changelog and the galaxy.yml version will be bumped if needed. - Once a draft release is actually published, collection packages will be added to the release and ansible-galaxy automatically. diff --git a/README.md b/README.md index b9e33a15..2fcc86cc 100644 --- a/README.md +++ b/README.md @@ -12,20 +12,6 @@ and the [CLI tool](https://github.com/smallstep/cli). Possible uses for this col - Token or certificate creation from within your Ansible playbooks - [Complete configuration of client certificates via ACME, including automatic renewal](roles/step_acme_cert/README.md) ---- -**⚠️ SUPPORTED SMALLSTEP VERSION NOTICE ⚠️** - -The smallstep tools (and this collection) are constantly changing and breaking changes may be introduced in each minor version (e.g. from `0.20` to `0.21`). -To help maintain compatibility, you should use the version of this collection that corresponds to your `step-cli` version. - -For example, if you are using `step-cli==0.20`, you should use the collection version `>=0.20,<0.21`. -Newer and older collection versions *may* work, but are not supported. - -**For step-cli versions `<0.20`:** Use the collection version `>=0.4,<0.5`. -This was the last collection version released under the old versioning scheme. - ---- - ## Components --- @@ -47,15 +33,6 @@ This was the last collection version released under the old versioning scheme. ### Modules -#### CA Modules - ---- -**NOTE** - -To learn more about the differences between Online/Offline/Local-Only Modules, see [this section](#module-usage) - ---- - | Module | Description | Remote (Online mode) | Local (Offline mode) | |---------|-------------|--------|---------------| | [`step_ca_bootstrap`](https://ansible-collection-smallstep.readthedocs.io/en/latest/collections/maxhoesel/smallstep/step_ca_bootstrap_module.html) | Initialize `step-cli` to trust a step-ca server | ✅ | ❌ | @@ -65,34 +42,34 @@ To learn more about the differences between Online/Offline/Local-Only Modules, s | [`step_ca_revoke`](https://ansible-collection-smallstep.readthedocs.io/en/latest/collections/maxhoesel/smallstep/step_ca_revoke_module.html) | Revoke a Certificate | ✅ | `offline` parameter | | [`step_ca_token`](https://ansible-collection-smallstep.readthedocs.io/en/latest/collections/maxhoesel/smallstep/step_ca_token_module.html) | Generate an OTT granting access to the CA | ✅ | `offline` parameter | -#### Standalone Modules - -None so far - ## Installation ### Dependencies -- A recent release of Ansible. This collection officially supports the 3 most recent Ansible releases. +- A recent release of Ansible. This collection is tested against the 3 most recent Ansible releases. Older versions might still work, but are not supported -- Python 3.6 or newer on the target host +- Python 3.6 or newer on the target nodes Individual roles or modules may have additional dependencies, please check their respective documentation. +### Versioning Policy and Node Requirements + +Each minor version of this collection designed to be compatible with the corresponding minor release of the `step-cli` utility. +For example, The collection releases with version `0.24.x` are compatible with the `step-cli` utility versions `0.24.x`. +This coupling is needed as newer minor versions of the `step-cli` tool may introduce breaking changes and affect this collection. + +To install the correct collection version, check your `step-cli` version (`step-cli --version`), then use that value when installing the collection. + +**For step-cli versions `<0.20`:** Use the collection version `>=0.4,<0.5`. + ### Install Via ansible-galaxy (recommended): `ansible-galaxy collection install maxhoesel.smallstep>=your-step-cli-version, /collection/tests/integration/integration_config.yml" + ANSIBLE_TEST_ARGS: "integration --color -v \ + --controller docker:default --target docker:step-ansible-local-ca:ca${STEP_CA_VERSION},python=${NODE_PYTHON_VERSION}@/usr/bin/python${NODE_PYTHON_VERSION} \ + --tags local-ca" + + # build local-ca container so that we can use it for tests. + # We don't actually use the started container, we just need the built image in the local cache + local-ca: + build: + context: ./docker/local-ca + tags: + - step-ansible-local-ca:ca${STEP_CA_VERSION} + args: + STEP_CA_VERSION: ${STEP_CA_VERSION} + ANSIBLE_VERSION: ${ANSIBLE_VERSION} diff --git a/tests/integration/docker-compose-remote.yml b/tests/integration/docker-compose-remote.yml new file mode 100644 index 00000000..dd5166b0 --- /dev/null +++ b/tests/integration/docker-compose-remote.yml @@ -0,0 +1,44 @@ +version: "3" + +services: + # Test modules that do not require a ca configured on the executing node (remote) + test-runner-remote: + image: quay.io/maxhoesel-ansible/ansible-test-collection-runner:${ANSIBLE_VERSION} + volumes: + # Pass through the docker socket + - /var/run/docker.sock:/var/run/docker.sock + # and our collection + - ../../:/collection + environment: + # versions + STEP_CA_VERSION: ${STEP_CA_VERSION} + STEP_CLI_VERSION: ${STEP_CLI_VERSION} + # remote ca settings + STEP_REMOTE_CA_URL: https://remote-ca:9000 + STEP_REMOTE_CA_PROVISIONER_NAME: ansible + STEP_REMOTE_CA_PROVISIONER_PASSWORD: ansible-module-tests-pw + # render the integration config template + PRE_COMMANDS: "/collection/scripts/render_template.sh \ + /collection/tests/integration/integration_config.yml.template-remote > /collection/tests/integration/integration_config.yml" + ANSIBLE_TEST_ARGS: "integration --color -v \ + --controller docker:default --target docker:default,python=${NODE_PYTHON_VERSION} \ + --docker-network ansible-collection-smallstep-integration-ca \ + --skip-tags local-ca" + depends_on: + remote-ca: + condition: service_healthy + + remote-ca: + image: docker.io/smallstep/step-ca:${STEP_CA_VERSION} + environment: + DOCKER_STEPCA_INIT_NAME: Ansible-Test + DOCKER_STEPCA_INIT_DNS_NAMES: localhost,remote-ca + DOCKER_STEPCA_INIT_PROVISIONER_NAME: ansible + DOCKER_STEPCA_INIT_PASSWORD: ansible-module-tests-pw + networks: + - ca + +networks: + ca: + name: ansible-collection-smallstep-integration-ca + driver: bridge diff --git a/tests/integration/docker/local-ca/Dockerfile b/tests/integration/docker/local-ca/Dockerfile new file mode 100644 index 00000000..702dad05 --- /dev/null +++ b/tests/integration/docker/local-ca/Dockerfile @@ -0,0 +1,27 @@ +ARG STEP_CA_VERSION +ARG ANSIBLE_VERSION + +FROM docker.io/smallstep/step-ca:${STEP_CA_VERSION} as ca + +# This is only a target, the exact ansible version does not matter +# as long as that image has our required python lib +FROM quay.io/ansible/default-test-container:stable-2.15 + +# Get binaries from step-ca container +COPY --from=ca /usr/local/bin/step /usr/local/bin/step +COPY --from=ca /usr/local/bin/step-ca /usr/local/bin/step-ca + +# Install and enable the systemd service +COPY step-ca.service /etc/systemd/system/step-ca.service +COPY step-ca.sh /usr/local/bin/step-ca.sh +RUN ln -s /etc/systemd/system/step-ca.service /etc/systemd/system/multi-user.target.wants/step-ca.service \ + && chmod 755 /usr/local/bin/step-ca.sh + +# Recreate step environment +ENV STEP="/home/step" +ENV STEPPATH="/home/step" +RUN apt update && apt -y -qq install libcap2-bin sudo \ + && setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/step-ca \ + && useradd -m step \ + && echo "step ALL=NOPASSWD: ALL" > /etc/sudoers.d/00_step \ + && rm -rf /var/lib/apt/lists/* diff --git a/tests/integration/docker/step-ca-ansible/README.md b/tests/integration/docker/local-ca/README.md similarity index 100% rename from tests/integration/docker/step-ca-ansible/README.md rename to tests/integration/docker/local-ca/README.md diff --git a/tests/integration/docker/local-ca/step-ca.service b/tests/integration/docker/local-ca/step-ca.service new file mode 100644 index 00000000..66dad8c9 --- /dev/null +++ b/tests/integration/docker/local-ca/step-ca.service @@ -0,0 +1,16 @@ +[Unit] +Name=Step-ca + +[Service] +Environment="STEP=/home/step" +Environment="STEPPATH=/home/step" +Environment="STEP=/home/step" +Environment="DOCKER_STEPCA_INIT_NAME=Ansible-Test" +Environment="DOCKER_STEPCA_INIT_DNS_NAMES=localhost,step-ca" + +User=step +Group=step +ExecStart=/usr/local/bin/step-ca.sh + +[Install] +WantedBy=multi-user.target diff --git a/tests/integration/docker/local-ca/step-ca.sh b/tests/integration/docker/local-ca/step-ca.sh new file mode 100644 index 00000000..24cd458b --- /dev/null +++ b/tests/integration/docker/local-ca/step-ca.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -euo pipefail + +if [ ! -f "${STEPPATH}/config/ca.json" ]; then + # Generate password + set +o pipefail + < /dev/urandom tr -dc A-Za-z0-9 | head -c40 > "$STEPPATH/password" + echo + set -o pipefail + + DOCKER_STEPCA_INIT_PROVISIONER_NAME="${DOCKER_STEPCA_INIT_PROVISIONER_NAME:-admin}" + DOCKER_STEPCA_INIT_ADMIN_SUBJECT="${DOCKER_STEPCA_INIT_ADMIN_SUBJECT:-step}" + DOCKER_STEPCA_INIT_ADDRESS="${DOCKER_STEPCA_INIT_ADDRESS:-127.0.0.1:9000}" + + step ca init --name "${DOCKER_STEPCA_INIT_NAME}" \ + --dns "${DOCKER_STEPCA_INIT_DNS_NAMES}" \ + --provisioner "${DOCKER_STEPCA_INIT_PROVISIONER_NAME}" \ + --password-file "${STEPPATH}/password" \ + --provisioner-password-file "${STEPPATH}/password" \ + --address "${DOCKER_STEPCA_INIT_ADDRESS}" +fi + +/usr/local/bin/step-ca --password-file "$STEPPATH/password" "$STEPPATH/config/ca.json" diff --git a/tests/integration/docker/step-ca-ansible/Dockerfile b/tests/integration/docker/step-ca-ansible/Dockerfile deleted file mode 100644 index 04c6ef6f..00000000 --- a/tests/integration/docker/step-ca-ansible/Dockerfile +++ /dev/null @@ -1,57 +0,0 @@ -# Dockerfile for a python-capable step-ca container that can be accessed by ansible -# Heavily modified and *highly* cursed variant of the step-ca dockerfile -# https://github.com/smallstep/certificates/blob/master/docker/Dockerfile.step-ca - -ARG STEP_CA_VERSION=latest -ARG PYTHON_VERSION=3.6 - -FROM docker.io/smallstep/step-ca:${STEP_CA_VERSION} as ca - -FROM python:${PYTHON_VERSION}-alpine - -ARG STEP_CA_USER=step - -# Get binaries from step-ca container -COPY --from=ca /usr/local/bin/step /usr/local/bin/step -COPY --from=ca /usr/local/bin/step-ca /usr/local/bin/step-ca - -# Recreate step environment -ENV STEP="/home/${STEP_CA_USER}" -ENV STEPPATH="/home/${STEP_CA_USER}" -ARG STEPUID=1000 -ARG STEPGID=1000 -# Ansible-test still connects to the container via SSH, so let's also install a server -# ipruote2 is needed for ansible_default_ipv4/6 facts -# sudo is needed for become: in ansible, su-exec is used to start step-ca, shadow to unlock the root user for ssh login -RUN apk update \ - && apk upgrade \ - && apk add --no-cache bash curl tzdata libcap openssh iproute2 sudo su-exec shadow procps\ - && setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/step-ca \ - && addgroup -g ${STEPGID} step \ - && adduser -D -u ${STEPUID} -G step ${STEP_CA_USER} \ - && chown ${STEP_CA_USER}:step /home/${STEP_CA_USER} - -# SSH setup, generate keys, permit root login, allow step to start it -RUN cd /etc/ssh \ - && ssh-keygen -A \ - && echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config \ - && usermod -p $1$pBxtBT.3$T4Vx/azTzrgdpJzAP/O121 root \ - && usermod -U root - -ENV CONFIGPATH="/home/${STEP_CA_USER}/config/ca.json" -ENV PWDPATH="/home/${STEP_CA_USER}/secrets/password" - -VOLUME ["/home/${STEP_CA_USER}"] -STOPSIGNAL SIGTERM -HEALTHCHECK CMD step ca health 2>/dev/null | grep "^ok" >/dev/null - -COPY --from=ca /entrypoint.sh /entrypoint.sh -COPY pre-entry.sh /pre-entry.sh - -# Start the container as root then switch to step for the actual -ENTRYPOINT ["/bin/bash", "/pre-entry.sh"] -CMD exec /usr/local/bin/step-ca --password-file $PWDPATH $CONFIGPATH - -# Custom overrides for our local tests, this allows the container to initialize on its own -ENV DOCKER_STEPCA_INIT_NAME=Ansible-Test -ENV DOCKER_STEPCA_INIT_DNS_NAMES=localhost,step-ca diff --git a/tests/integration/docker/step-ca-ansible/pre-entry.sh b/tests/integration/docker/step-ca-ansible/pre-entry.sh deleted file mode 100644 index ae02a91a..00000000 --- a/tests/integration/docker/step-ca-ansible/pre-entry.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -e - -/usr/sbin/sshd - -su-exec step bash /entrypoint.sh "${@}" diff --git a/tests/integration/integration_config.yml.template-local b/tests/integration/integration_config.yml.template-local new file mode 100644 index 00000000..9941c48a --- /dev/null +++ b/tests/integration/integration_config.yml.template-local @@ -0,0 +1,14 @@ +--- +# Configuration for the smallstep module tests. +# This template is rendered by render_config.sh, called from tox +# You can use these variable names in your module integration tests. + +# Currently tested versions +ca_version: ${STEP_CA_VERSION} +cli_version: ${STEP_CLI_VERSION} + +# Locally reachable CA settings - this is needed by modules like step_ca_provisioner that need FS access +# Don't use these unless your target is tagged with "local-ca" and you use "setup_local_ca" +ca_user: ${STEP_LOCAL_CA_USER} +ca_path: ${STEP_LOCAL_STEPPATH} +cli_binary: ${STEP_LOCAL_CLI_BINARY} diff --git a/tests/integration/integration_config.yml.template b/tests/integration/integration_config.yml.template-remote similarity index 63% rename from tests/integration/integration_config.yml.template rename to tests/integration/integration_config.yml.template-remote index bf256c9c..25d6664c 100644 --- a/tests/integration/integration_config.yml.template +++ b/tests/integration/integration_config.yml.template-remote @@ -6,19 +6,11 @@ # Remote CA access options - this could be a VM somewhere or a local container # Use these variables for normal module integration tests ca_url: ${STEP_REMOTE_CA_URL} -ca_fp: ${STEP_REMOTE_CA_FP} -# Use this provisioner to create certs/tokens/etc. The password file is already present after calling `setup_remote_provisioner` +# Use this provisioner to create certs/tokens/etc. The password file is already present after calling "setup_remote_provisioner" ca_provisioner: ${STEP_REMOTE_CA_PROVISIONER_NAME} ca_provisioner_password: ${STEP_REMOTE_CA_PROVISIONER_PASSWORD} ca_provisioner_password_file: /tmp/provisioner_passfile - # Currently tested versions ca_version: ${STEP_CA_VERSION} cli_version: ${STEP_CLI_VERSION} - -# Locally reachable CA settings - this is needed by modules like step_ca_provisioner that need FS access -# Don't use these unless your target is tagged with `local-ca` and you use `setup_local_ca` -ca_user: ${STEP_LOCAL_CA_USER} -ca_path: ${STEP_LOCAL_STEPPATH} -cli_binary: ${STEP_LOCAL_CLI_BINARY} diff --git a/tests/integration/targets/setup_remote_ca/tasks/main.yml b/tests/integration/targets/setup_remote_ca/tasks/main.yml index bdad7913..bad10ec4 100644 --- a/tests/integration/targets/setup_remote_ca/tasks/main.yml +++ b/tests/integration/targets/setup_remote_ca/tasks/main.yml @@ -1,11 +1,36 @@ +- name: Install JQ + ansible.builtin.apt: + name: jq # needed to get root certificate + retries: 3 + delay: 5 + register: _task + until: _task is not failed - name: Install step-cli ansible.builtin.apt: deb: "https://github.com/smallstep/cli/releases/download/v{{ cli_version }}/step-cli_{{ cli_version }}_amd64.deb" + retries: 3 + delay: 5 + register: _task + until: _task is not failed + +# the stuff of itsec nightmares +# the step-ca container genreates its root CA dynamically when started, but we retrieve its CA fingerprint to bootstrap from it. +# since there is no way to pre-seed the fingerprint and root CA cert, we instead use the internal step-ca API to retrieve all root certificates, +# then generate the fingerprint from that. +# This hack may stop working if smallstep decides to change their ca API structure in the future. but it works for now +- name: Get remote fingerprint + ansible.builtin.shell: "curl -k {{ ca_url }}/roots | jq -rj '.crts[0]' > /tmp/root_cert && step-cli certificate fingerprint /tmp/root_cert" + register: _ca_fp +- name: Set fingerprint + set_fact: + ca_fp: "{{ _ca_fp.stdout }}" + - name: Bootstrap host maxhoesel.smallstep.step_ca_bootstrap: ca_url: "{{ ca_url }}" fingerprint: "{{ ca_fp }}" install: no # Can't test system-wide install as it could affect the other test targets + - name: Provsioner password file is present copy: content: "{{ ca_provisioner_password }}" diff --git a/tests/integration/targets/step_ca_bootstrap/tasks/main.yml b/tests/integration/targets/step_ca_bootstrap/tasks/main.yml index e4a4e994..ea5ea46d 100644 --- a/tests/integration/targets/step_ca_bootstrap/tasks/main.yml +++ b/tests/integration/targets/step_ca_bootstrap/tasks/main.yml @@ -1,3 +1,15 @@ +# the stuff of itsec nightmares +# the step-ca container genreates its root CA dynamically when started, but we retrieve its CA fingerprint to bootstrap from it. +# since there is no way to pre-seed the fingerprint and root CA cert, we instead use the internal step-ca API to retrieve all root certificates, +# then generate the fingerprint from that. +# This hack may stop working if smallstep decides to change their ca API structure in the future. but it works for now +- name: Get remote fingerprint + ansible.builtin.shell: "curl -k {{ ca_url }}/roots | jq -rj '.crts[0]' > /tmp/root_cert && step-cli certificate fingerprint /tmp/root_cert" + register: _ca_fp +- name: Set fingerprint + set_fact: + ca_fp: "{{ _ca_fp.stdout }}" + - name: Bootstrap Host maxhoesel.smallstep.step_ca_bootstrap: ca_url: "{{ ca_url }}" diff --git a/tests/integration/targets/step_ca_provisioner/tasks/main.yml b/tests/integration/targets/step_ca_provisioner/tasks/main.yml index 0252d30b..4f898908 100644 --- a/tests/integration/targets/step_ca_provisioner/tasks/main.yml +++ b/tests/integration/targets/step_ca_provisioner/tasks/main.yml @@ -128,12 +128,11 @@ - ["tests-Amazon", "AWS"] - ["tests-Google", "GCP"] - - name: Get Server PID - command: pgrep -f step-ca + shell: pgrep -fa step-ca | grep -v step-ca.sh | cut -d ' ' -f 1 register: _pid - - name: Restart Server - command: "kill -1 {{ _pid.stdout }}" + - name: Reload Server + command: "kill -1 {{ _pid.stdout_lines[0] }}" become: false - name: Check server health diff --git a/tests/roles/requirements.txt b/tests/roles/requirements.txt new file mode 100644 index 00000000..03f5a357 --- /dev/null +++ b/tests/roles/requirements.txt @@ -0,0 +1,4 @@ +# Dependencies for executing the role scenarios. +# We pin molecule to prevent issues with breaking changes +molecule==5.1.0 +molecule-plugins[podman]==23.4.1 diff --git a/tests/sanity/docker-compose.yml b/tests/sanity/docker-compose.yml new file mode 100644 index 00000000..d5fbec94 --- /dev/null +++ b/tests/sanity/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3" + +services: + test-runner: + image: quay.io/maxhoesel-ansible/ansible-test-collection-runner:${ANSIBLE_VERSION} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ../../:/collection:ro + # As we only support py36+, pyupgrade is set to remove the py27 boilerplate. + # Don't check for that as we don't need it. + environment: + ANSIBLE_TEST_ARGS: "sanity --docker --color -v \ + --python ${NODE_PYTHON_VERSION} \ + --skip-test metaclass-boilerplate \ + --skip-test future-import-boilerplate" diff --git a/tests/sanity/ignore.txt b/tests/sanity/ignore.txt index 1747b1d4..66ce7979 100644 --- a/tests/sanity/ignore.txt +++ b/tests/sanity/ignore.txt @@ -1 +1,2 @@ plugins/modules/step_ca_provisioner.py validate-modules:doc-default-does-not-match-spec # Can't always rely on $STEPPATH being set +tests/integration/docker/local-ca/step-ca.sh shebang # Integration test, don't care diff --git a/tests/test-modules b/tests/test-modules deleted file mode 100755 index 69638cb3..00000000 --- a/tests/test-modules +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -eu -set -o pipefail - -source ./tests/test-modules-sanity -source ./tests/test-modules-integration diff --git a/tests/test-modules-integration b/tests/test-modules-integration deleted file mode 100755 index b51bd3af..00000000 --- a/tests/test-modules-integration +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env bash - -# Perform integration tests on the modules -# To keep retrying a failing test, you can set the following environment variables. -# This is mainly intended for CI use. -# TEST_RETRIES= - Number of times to try the test. Default: 1 -# TEST_RETRY_DELAY= - Number of seconds to wait between failing test runs. Default: 300 (5min) - -set -eu -set -o pipefail - -SCRIPT_DIRECTORY=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -# shellcheck disable=1091 -source "$SCRIPT_DIRECTORY/constants.sh" -# shellcheck disable=1091 -source "$SCRIPT_DIRECTORY/util.sh" - -TEST_RETRIES="${TEST_RETRIES:-1}" -TEST_RETRY_DELAY="${TEST_RETRY_DELAY:-300}" - -# Docker settings -export STEP_PODMAN_NETWORK="step-ansible-modules" - -# Remote CA settings -export STEP_REMOTE_CA_CT_NAME="step-ca" -export STEP_REMOTE_CA_PROVISIONER_NAME="ansible" -export STEP_REMOTE_CA_PROVISIONER_PASSWORD="ansible-module-tests-pw" -export STEP_REMOTE_CA_URL="https://$STEP_REMOTE_CA_CT_NAME:9000" -# FP can only evaluated when the remote docker ct is running -export STEP_REMOTE_CA_FP="" - -# Local CA settings -export STEP_LOCAL_CA_USER=step -export STEP_LOCAL_CLI_BINARY=step -export STEP_LOCAL_STEPPATH=/home/step # hardcoded in Dockerfile - -LOCAL_CA_TAG=local-ca - -# Cleanup function to delete any docker objects that we might have created -cleanup() { - echo "Cleaning up..." - set +e # cleanup, we don't care if some stuff doesn't exist - podman rm -f "$STEP_REMOTE_CA_CT_NAME" > /dev/null - podman network rm "$STEP_PODMAN_NETWORK" > /dev/null - echo "Done" - set -e - exit -} -trap cleanup INT ERR EXIT - -prepare() { - podman network create $STEP_PODMAN_NETWORK -} - -render_config() { - # The test config contains parameters that the module targets use to connect to the local/remote ca respectively. - # We render out this config right before starting a integration test - echo "Rendering configuration" - tests/integration/render_config.sh tests/integration/integration_config.yml.template > tests/integration/integration_config.yml -} - -remote_ca() { - echo "Starting remote CA container" - # CircleCIs systemd session is, in some way, misconfigured and that causes some issues with podman and cgroups - # Instead of fixing it, we can just fallback to cgroupfs - # See: https://github.com/containers/podman/issues/16529 - retry "$TEST_RETRIES" "$TEST_RETRY_DELAY" podman run --cgroup-manager=cgroupfs -d \ - --name $STEP_REMOTE_CA_CT_NAME --network $STEP_PODMAN_NETWORK \ - -e "DOCKER_STEPCA_INIT_NAME=Ansible-Test" \ - -e "DOCKER_STEPCA_INIT_DNS_NAMES=localhost,$STEP_REMOTE_CA_CT_NAME" \ - -e "DOCKER_STEPCA_INIT_PROVISIONER_NAME=$STEP_REMOTE_CA_PROVISIONER_NAME" \ - -e "DOCKER_STEPCA_INIT_PASSWORD=$STEP_REMOTE_CA_PROVISIONER_PASSWORD" \ - "docker.io/smallstep/step-ca:$STEP_CA_VERSION" - - - # Give the CA a little bit to initialize - echo "Waiting for CA to come online" - # TODO: Check the container status with docker instead of exec-ing - timeout 20 bash -c "while ! podman exec $STEP_REMOTE_CA_CT_NAME step ca health &> /dev/null; do sleep 1; done" - - STEP_REMOTE_CA_FP=$(podman exec $STEP_REMOTE_CA_CT_NAME step certificate fingerprint certs/root_ca.crt) - export STEP_REMOTE_CA_FP - - render_config - - retry "$TEST_RETRIES" "$TEST_RETRY_DELAY" tox -e integration -- \ - --color -v \ - --controller docker:default --target docker:default,python=3.6 \ - --docker-network $STEP_PODMAN_NETWORK \ - --skip-tags $LOCAL_CA_TAG \ - --docker-terminate=always `#Change this if you want to debug test targets` -} - -local_ca() { - local image_name="step-ca-ansible" - - echo "Building docker image for local-ca test targets: $image_name with step-ca version $STEP_CA_VERSION" - # CircleCIs systemd session is, in some way, misconfigured and that causes some issues with podman and cgroups - # Instead of fixing it, we can just fallback to cgroupfs - # See: https://github.com/containers/podman/issues/16529 - retry "$TEST_RETRIES" "$TEST_RETRY_DELAY" podman build --cgroup-manager=cgroupfs tests/integration/docker/step-ca-ansible -t $image_name \ - --build-arg "STEP_CA_VERSION=$STEP_CA_VERSION" \ - --build-arg "PYTHON_VERSION=$PYTHON_VERSION" \ - --build-arg "STEP_CA_USER=$STEP_LOCAL_CA_USER" - - render_config - - retry "$TEST_RETRIES" "$TEST_RETRY_DELAY" tox -e integration -- \ - --color -v \ - --controller docker:default --target "docker:$image_name,python=$PYTHON_VERSION@/usr/local/bin/python3" \ - --docker-network $STEP_PODMAN_NETWORK \ - --tags $LOCAL_CA_TAG \ - --docker-terminate=always `#Change this if you want to debug test targets` -} - -main() { - prepare - local_ca - remote_ca - cleanup -} - -main "$@" diff --git a/tests/test-modules-sanity b/tests/test-modules-sanity deleted file mode 100755 index d9f8bd07..00000000 --- a/tests/test-modules-sanity +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -# Perform sanity tests on the modules. -# To keep retrying a failing test, you can set the following environment variables. -# This is mainly intended for CI use. -# TEST_RETRIES= - Number of times to try the test. Default: 1 -# TEST_RETRY_DELAY= - Number of seconds to wait between failing test runs. Default: 300 (5min) - -set -eu -set -o pipefail - -SCRIPT_DIRECTORY=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -# shellcheck disable=1091 -source "$SCRIPT_DIRECTORY/constants.sh" -# shellcheck disable=1091 -source "$SCRIPT_DIRECTORY/util.sh" - -TEST_RETRIES="${TEST_RETRIES:-1}" -TEST_RETRY_DELAY="${TEST_RETRY_DELAY:-300}" - -# As we only support py36+, pyupgrade is set to remove the py27 boilerplate. Don't check for that as we don't need it -retry "$TEST_RETRIES" "$TEST_RETRY_DELAY" tox -e sanity -- \ - --docker --color -v \ - --python "$PYTHON_VERSION" \ - --skip-test metaclass-boilerplate --skip-test future-import-boilerplate diff --git a/tests/test-roles b/tests/test-roles deleted file mode 100755 index 15c3fb76..00000000 --- a/tests/test-roles +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash - -# Run tets on the roles in this collection. -# Usage: test-roles [filter] [--help, -h] -# -# Run all tox molecule role scenarios that match the given filter. -# If no filter is given, all role scenarios will be run. -# -# Example: ./test-roles step_ca -# -# To keep retrying a failing test, you can set the following environment variables. -# This is mainly intended for CI use. -# TEST_RETRIES= - Number of times to try the test. Default: 1 -# TEST_RETRY_DELAY= - Number of seconds to wait between failing test runs. Default: 300 (5min) - -set -eu -set -o pipefail - -SCRIPT_DIRECTORY=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) - -# shellcheck disable=1091 -source "$SCRIPT_DIRECTORY/constants.sh" -# shellcheck disable=1091 -source "$SCRIPT_DIRECTORY/util.sh" - -TEST_RETRIES="${TEST_RETRIES:-1}" -TEST_RETRY_DELAY="${TEST_RETRY_DELAY:-300}" - -# Hostname to use for the ca container in molecule scenarios -export STEP_MOLECULE_CA_HOSTNAME=step-ca-molecule.localdomain - -USAGE=$(cat << EOF -Usage: test-roles [filter] [--help, -h] - -Run all tox molecule role scenarios that match the given filter. -If no filter is given, all role scenarios will be run. - -Example: ./test-roles step_ca -EOF -) - -main() { - # shellcheck disable=1091 - source "$SCRIPT_DIRECTORY/constants.sh" - - set +u - if [[ $1 == "--help" || $1 == "-h" ]]; then - echo "$USAGE" - exit 1 - elif [[ -z $1 ]]; then - scenarios=$(tox -l | grep ansible | grep -v "lint" | tr '\n' ',') - else - scenarios=$(tox -l | grep ansible | grep -v "lint" | grep "$1" | tr '\n' ',') - fi - set -u - - retry "$TEST_RETRIES" "$TEST_RETRY_DELAY" tox -e "$scenarios" -} - -main "$@" diff --git a/tests/util.sh b/tests/util.sh deleted file mode 100644 index 7fe9f8b5..00000000 --- a/tests/util.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -# Retry a given command a number of times. -# Usage: retry command with parameters just like normal -retry() { - local retries=$1 - local delay=$2 - shift - shift - - local count=0 - until "$@"; do - exit=$? - count=$((count + 1)) - if [ $count -lt "$retries" ]; then - echo "Retry $count/$retries exited $exit, retrying in $delay seconds..." - sleep "$delay" - else - echo "Retry $count/$retries exited $exit, no more retries left." - return $exit - fi - done - return 0 -} diff --git a/tox.ini b/tox.ini index a27ebe14..ad002f04 100644 --- a/tox.ini +++ b/tox.ini @@ -1,30 +1,74 @@ +[consts] +node_min_python = 3.6 +step_ca_version = 0.24.1 +step_cli_version = 0.24.3 + [tox] -envlist=lint +# plugin tests: run plugin tests with these ansible versions +envlist = py3-ansible{213,214,215}-test-{sanity,integration} requires = tox-ansible>=1.8,<2 tox<4 skipsdist = True [ansible] -ansible_test_platform=posargs -# only used in role scenario tests +# Role tests config: we test roles with these ansible/py versions ansible = 6,7,8 python = 3 +scenario_format = $path-$parent-$nondefault_name [testenv] passenv = HOME - # Testing versions - STEP_CLI_VERSION - STEP_CA_VERSION - # Hostname of the remote CA container used in Moleucle - STEP_MOLECULE_CA_HOSTNAME setenv = + # Set the ansible version for plugin tests + ansible213: ANSIBLE_VERSION = 2.13 + ansible214: ANSIBLE_VERSION = 2.14 + ansible215: ANSIBLE_VERSION = 2.15 + # Make testing versions available to everyone + STEP_CLI_VERSION = {[consts]step_cli_version} + STEP_CA_VERSION = {[consts]step_ca_version} + NODE_PYTHON_VERSION = {[consts]node_min_python} + # Visualization PY_COLORS = 1 ANSIBLE_FORCE_COLOR = 1 - # Enable podman for ansible-test instead of docker - ANSIBLE_TEST_PREFER_PODMAN = 1 -[testenv:sanity] -# Pin ansible-test/core version to latest stable release, same as used for development -deps = -rrequirements.txt +[testenv:py3-ansible{213,214,215}-test-sanity] +whitelist_externals = + docker +changedir = tests/sanity +commands_pre = + docker compose down +commands = + docker compose run --no-TTY --rm test-runner +commands_post = + # cleanup networks + docker compose down --remove-orphans + +#[testenv:py3-ansible{213,214,215}-test-units] +#whitelist_externals = +# docker +#changedir = tests/unit +#commands_pre = +# docker compose down +#commands = +# docker compose run --no-TTY --rm test-runner +#commands_post = +# # cleanup networks +# docker compose down --remove-orphans + +[testenv:py3-ansible{213,214,215}-test-integration] +whitelist_externals = + docker +changedir = tests/integration +commands_pre = + docker compose -f docker-compose-local.yml down + docker compose -f docker-compose-remote.yml down +commands = + docker compose -f docker-compose-local.yml build + docker compose -f docker-compose-local.yml run --no-TTY --rm test-runner-local + docker compose -f docker-compose-remote.yml run --no-TTY --rm test-runner-remote +commands_post = + # cleanup networks + docker compose -f docker-compose-local.yml down --remove-orphans + docker compose -f docker-compose-remote.yml down --remove-orphans