commit e8984427523d92767c455d6fd37ab91ab7804621 Author: Richard Megginson Date: Tue Oct 22 10:07:09 2024 -0600 Initial commit diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..a37aba5 --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,26 @@ +--- +profile: production +kinds: + - yaml: "**/meta/collection-requirements.yml" + - playbook: "**/tests/get_coverage.yml" + - yaml: "**/tests/collection-requirements.yml" + - playbook: "**/tests/tests_*.yml" + - playbook: "**/tests/setup-snapshot.yml" + - tasks: "**/tests/*.yml" + - playbook: "**/tests/playbooks/*.yml" + - tasks: "**/tests/tasks/*.yml" + - tasks: "**/tests/tasks/*/*.yml" + - vars: "**/tests/vars/*.yml" + - playbook: "**/examples/*.yml" +skip_list: + - fqcn-builtins + - var-naming[no-role-prefix] +exclude_paths: + - tests/roles/ + - .github/ + - .markdownlint.yaml + - examples/roles/ +mock_roles: + - linux-system-roles.template +supported_ansible_also: + - "2.14.0" diff --git a/.commitlintrc.js b/.commitlintrc.js new file mode 100644 index 0000000..f8a39ba --- /dev/null +++ b/.commitlintrc.js @@ -0,0 +1,141 @@ +module.exports = { + parserPreset: 'conventional-changelog-conventionalcommits', + rules: { + 'body-leading-blank': [1, 'always'], + 'body-max-line-length': [2, 'always', 100], + 'footer-leading-blank': [1, 'always'], + 'footer-max-line-length': [2, 'always', 100], + 'header-max-length': [2, 'always', 100], + 'subject-case': [ + 2, + 'never', + ['start-case', 'pascal-case', 'upper-case'], + ], + 'subject-empty': [2, 'never'], + 'subject-full-stop': [2, 'never', '.'], + 'type-case': [2, 'always', 'lower-case'], + 'type-empty': [2, 'never'], + 'type-enum': [ + 2, + 'always', + [ + 'build', + 'chore', + 'ci', + 'docs', + 'feat', + 'fix', + 'perf', + 'refactor', + 'revert', + 'style', + 'test', + 'tests', + ], + ], + }, + prompt: { + questions: { + type: { + description: "Select the type of change that you're committing", + enum: { + feat: { + description: 'A new feature', + title: 'Features', + emoji: '✨', + }, + fix: { + description: 'A bug fix', + title: 'Bug Fixes', + emoji: '🐛', + }, + docs: { + description: 'Documentation only changes', + title: 'Documentation', + emoji: '📚', + }, + style: { + description: + 'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)', + title: 'Styles', + emoji: '💎', + }, + refactor: { + description: + 'A code change that neither fixes a bug nor adds a feature', + title: 'Code Refactoring', + emoji: '📦', + }, + perf: { + description: 'A code change that improves performance', + title: 'Performance Improvements', + emoji: '🚀', + }, + test: { + description: 'Adding missing tests or correcting existing tests', + title: 'Tests', + emoji: '🚨', + }, + tests: { + description: 'Adding missing tests or correcting existing tests', + title: 'Tests', + emoji: '🚨', + }, + build: { + description: + 'Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)', + title: 'Builds', + emoji: '🛠', + }, + ci: { + description: + 'Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)', + title: 'Continuous Integrations', + emoji: '⚙️', + }, + chore: { + description: "Other changes that don't modify src or test files", + title: 'Chores', + emoji: '♻️', + }, + revert: { + description: 'Reverts a previous commit', + title: 'Reverts', + emoji: '🗑', + }, + }, + }, + scope: { + description: + 'What is the scope of this change (e.g. component or file name)', + }, + subject: { + description: + 'Write a short, imperative tense description of the change', + }, + body: { + description: 'Provide a longer description of the change', + }, + isBreaking: { + description: 'Are there any breaking changes?', + }, + breakingBody: { + description: + 'A BREAKING CHANGE commit requires a body. Please enter a longer description of the commit itself', + }, + breaking: { + description: 'Describe the breaking changes', + }, + isIssueAffected: { + description: 'Does this change affect any open issues?', + }, + issuesBody: { + description: + 'If issues are closed, the commit requires a body. Please enter a longer description of the commit itself', + }, + issues: { + description: 'Add issue references (e.g. "fix #123", "re #123".)', + }, + }, + }, +}; diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..48c0688 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# https://help.github.com/en/articles/about-code-owners +# Default reviewers for everything +* @spetrosi @nhosoi @richm diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..7212528 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +--- +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + commit-message: + prefix: ci diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..dabc3e7 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,7 @@ +Enhancement: + +Reason: + +Result: + +Issue Tracker Tickets (Jira or BZ if any): diff --git a/.github/workflows/ansible-lint.yml b/.github/workflows/ansible-lint.yml new file mode 100644 index 0000000..d0ee41b --- /dev/null +++ b/.github/workflows/ansible-lint.yml @@ -0,0 +1,49 @@ +--- +name: Ansible Lint +on: # yamllint disable-line rule:truthy + pull_request: + merge_group: + branches: + - main + types: + - checks_requested + push: + branches: + - main + workflow_dispatch: +env: + LSR_ROLE2COLL_NAMESPACE: fedora + LSR_ROLE2COLL_NAME: linux_system_roles +permissions: + contents: read +jobs: + ansible_lint: + runs-on: ubuntu-latest + steps: + - name: Update pip, git + run: | + set -euxo pipefail + sudo apt update + sudo apt install -y git + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install tox, tox-lsr + run: | + set -euxo pipefail + pip3 install "git+https://github.com/linux-system-roles/tox-lsr@3.4.0" + + - name: Convert role to collection format + run: | + set -euxo pipefail + TOXENV=collection lsr_ci_runtox + coll_dir=".tox/ansible_collections/$LSR_ROLE2COLL_NAMESPACE/$LSR_ROLE2COLL_NAME" + # ansible-lint action requires a .git directory??? + # https://github.com/ansible/ansible-lint/blob/main/action.yml#L45 + mkdir -p "$coll_dir/.git" + + - name: Run ansible-lint + uses: ansible/ansible-lint@v24 + with: + working_directory: ${{ github.workspace }}/.tox/ansible_collections/${{ env.LSR_ROLE2COLL_NAMESPACE }}/${{ env.LSR_ROLE2COLL_NAME }} diff --git a/.github/workflows/ansible-managed-var-comment.yml b/.github/workflows/ansible-managed-var-comment.yml new file mode 100644 index 0000000..0864269 --- /dev/null +++ b/.github/workflows/ansible-managed-var-comment.yml @@ -0,0 +1,38 @@ +--- +name: Check for ansible_managed variable use in comments +on: # yamllint disable-line rule:truthy + pull_request: + merge_group: + branches: + - main + types: + - checks_requested + push: + branches: + - main + workflow_dispatch: +permissions: + contents: read +jobs: + ansible_managed_var_comment: + runs-on: ubuntu-latest + steps: + - name: Update pip, git + run: | + set -euxo pipefail + python3 -m pip install --upgrade pip + sudo apt update + sudo apt install -y git + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install tox, tox-lsr + run: | + set -euxo pipefail + pip3 install "git+https://github.com/linux-system-roles/tox-lsr@3.4.0" + + - name: Run ansible-plugin-scan + run: | + set -euxo pipefail + TOXENV=ansible-managed-var-comment lsr_ci_runtox diff --git a/.github/workflows/ansible-plugin-scan.yml b/.github/workflows/ansible-plugin-scan.yml new file mode 100644 index 0000000..987448f --- /dev/null +++ b/.github/workflows/ansible-plugin-scan.yml @@ -0,0 +1,38 @@ +--- +name: Ansible Plugin Scan +on: # yamllint disable-line rule:truthy + pull_request: + merge_group: + branches: + - main + types: + - checks_requested + push: + branches: + - main + workflow_dispatch: +permissions: + contents: read +jobs: + ansible_plugin_scan: + runs-on: ubuntu-latest + steps: + - name: Update pip, git + run: | + set -euxo pipefail + python3 -m pip install --upgrade pip + sudo apt update + sudo apt install -y git + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install tox, tox-lsr + run: | + set -euxo pipefail + pip3 install "git+https://github.com/linux-system-roles/tox-lsr@3.4.0" + + - name: Run ansible-plugin-scan + run: | + set -euxo pipefail + TOXENV=ansible-plugin-scan lsr_ci_runtox diff --git a/.github/workflows/ansible-test.yml b/.github/workflows/ansible-test.yml new file mode 100644 index 0000000..4dbdaa2 --- /dev/null +++ b/.github/workflows/ansible-test.yml @@ -0,0 +1,48 @@ +--- +name: Ansible Test +on: # yamllint disable-line rule:truthy + pull_request: + merge_group: + branches: + - main + types: + - checks_requested + push: + branches: + - main + workflow_dispatch: +env: + LSR_ROLE2COLL_NAMESPACE: fedora + LSR_ROLE2COLL_NAME: linux_system_roles +permissions: + contents: read +jobs: + ansible_test: + runs-on: ubuntu-latest + steps: + - name: Update pip, git + run: | + set -euxo pipefail + python3 -m pip install --upgrade pip + sudo apt update + sudo apt install -y git + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install tox, tox-lsr + run: | + set -euxo pipefail + pip3 install "git+https://github.com/linux-system-roles/tox-lsr@3.4.0" + + - name: Convert role to collection format + run: | + set -euxo pipefail + TOXENV=collection lsr_ci_runtox + + - name: Run ansible-test + uses: ansible-community/ansible-test-gh-action@release/v1 + with: + testing-type: sanity # wokeignore:rule=sanity + ansible-core-version: stable-2.17 + collection-src-directory: ${{ github.workspace }}/.tox/ansible_collections/${{ env.LSR_ROLE2COLL_NAMESPACE }}/${{ env.LSR_ROLE2COLL_NAME }} diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml new file mode 100644 index 0000000..e08cb4b --- /dev/null +++ b/.github/workflows/build_docs.yml @@ -0,0 +1,104 @@ +--- +# yamllint disable rule:line-length +name: Convert README.md to HTML and push to docs branch +on: # yamllint disable-line rule:truthy + push: + branches: + - main + paths: + - README.md + release: + types: + - published +permissions: + contents: read +jobs: + build_docs: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Update pip, git + run: | + set -euxo pipefail + sudo apt update + sudo apt install -y git + + - name: Check out code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Ensure the docs branch + run: | + set -euxo pipefail + branch=docs + existed_in_remote=$(git ls-remote --heads origin $branch) + + if [ -z "${existed_in_remote}" ]; then + echo "Creating $branch branch" + git config --global user.name "${{ github.actor }}" + git config --global user.email "${{ github.actor }}@users.noreply.github.com" + git checkout --orphan $branch + git reset --hard + git commit --allow-empty -m "Initializing $branch branch" + git push origin $branch + echo "Created $branch branch" + else + echo "Branch $branch already exists" + fi + + - name: Checkout the docs branch + uses: actions/checkout@v4 + with: + ref: docs + + - name: Fetch README.md and .pandoc_template.html5 template from the workflow branch + uses: actions/checkout@v4 + with: + sparse-checkout: | + README.md + .pandoc_template.html5 + sparse-checkout-cone-mode: false + path: ref_branch + - name: Set RELEASE_VERSION based on whether run on release or on push + run: | + set -euxo pipefail + if [ ${{ github.event_name }} = release ]; then + echo "RELEASE_VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV + elif [ ${{ github.event_name }} = push ]; then + echo "RELEASE_VERSION=latest" >> $GITHUB_ENV + else + echo Unsupported event + exit 1 + fi + + - name: Ensure that version and docs directories exist + run: mkdir -p ${{ env.RELEASE_VERSION }} docs + + - name: Remove badges from README.md prior to converting to HTML + run: sed -i '1,8 {/^\[\!.*actions\/workflows/d}' ref_branch/README.md + + - name: Convert README.md to HTML and save to the version directory + uses: docker://pandoc/core:latest + with: + args: >- + --from gfm --to html5 --toc --shift-heading-level-by=-1 + --template ref_branch/.pandoc_template.html5 + --output ${{ env.RELEASE_VERSION }}/README.html ref_branch/README.md + + - name: Copy latest README.html to docs/index.html for GitHub pages + if: env.RELEASE_VERSION == 'latest' + run: cp ${{ env.RELEASE_VERSION }}/README.html docs/index.html + + - name: Commit changes + run: | + git config --global user.name "${{ github.actor }}" + git config --global user.email "${{ github.actor }}@users.noreply.github.com" + git add ${{ env.RELEASE_VERSION }}/README.html docs/index.html + git commit -m "Update README.html for ${{ env.RELEASE_VERSION }}" + + - name: Push changes + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + branch: docs diff --git a/.github/workflows/changelog_to_tag.yml b/.github/workflows/changelog_to_tag.yml new file mode 100644 index 0000000..71e5540 --- /dev/null +++ b/.github/workflows/changelog_to_tag.yml @@ -0,0 +1,91 @@ +--- +# yamllint disable rule:line-length +name: Tag, release, and publish role based on CHANGELOG.md push +on: # yamllint disable-line rule:truthy + push: + branches: + - main + paths: + - CHANGELOG.md +permissions: + contents: read +jobs: + tag_release_publish: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Update pip, git + run: | + set -euxo pipefail + sudo apt update + sudo apt install -y git + + - name: checkout PR + uses: actions/checkout@v4 + + - name: Get tag and message from the latest CHANGELOG.md commit + id: tag + run: | + set -euxo pipefail + print=false + while read -r line; do + if [[ "$line" =~ ^\[([0-9]+\.[0-9]+\.[0-9]+)\]\ -\ [0-9-]+ ]]; then + if [ "$print" = false ]; then + _tagname="${BASH_REMATCH[1]}" + echo "$line" + print=true + else + break + fi + elif [ "$print" = true ]; then + echo "$line" + fi + done < CHANGELOG.md > ./.tagmsg.txt + git fetch --all --tags + for t in $( git tag -l ); do + if [ "$t" = "$_tagname" ]; then + echo INFO: tag "$t" already exists + exit 1 + fi + done + # Get name of the branch that the change was pushed to + _branch="${GITHUB_REF_NAME:-}" + if [ "$_branch" = master ] || [ "$_branch" = main ]; then + echo Using branch name ["$_branch"] as push branch + else + echo WARNING: GITHUB_REF_NAME ["$_branch"] is not main or master + _branch=$( git branch -r | grep -o 'origin/HEAD -> origin/.*$' | \ + awk -F'/' '{print $3}' || : ) + fi + if [ -z "$_branch" ]; then + _branch=$( git branch --points-at HEAD --no-color --format='%(refname:short)' ) + fi + if [ -z "$_branch" ]; then + echo ERROR: unable to determine push branch + git branch -a + exit 1 + fi + echo "tagname=$_tagname" >> "$GITHUB_OUTPUT" + echo "branch=$_branch" >> "$GITHUB_OUTPUT" + - name: Create tag + uses: mathieudutour/github-tag-action@v6.2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + custom_tag: ${{ steps.tag.outputs.tagname }} + tag_prefix: '' + + - name: Create Release + id: create_release + uses: ncipollo/release-action@v1 + with: + tag: ${{ steps.tag.outputs.tagname }} + name: Version ${{ steps.tag.outputs.tagname }} + bodyFile: ./.tagmsg.txt + makeLatest: true + + - name: Publish role to Galaxy + uses: robertdebock/galaxy-action@1.2.1 + with: + galaxy_api_key: ${{ secrets.galaxy_api_key }} + git_branch: ${{ steps.tag.outputs.branch }} diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/markdownlint.yml new file mode 100644 index 0000000..3094acd --- /dev/null +++ b/.github/workflows/markdownlint.yml @@ -0,0 +1,38 @@ +--- +# yamllint disable rule:line-length +name: Markdown Lint +on: # yamllint disable-line rule:truthy + pull_request: + merge_group: + branches: + - main + types: + - checks_requested + push: + branches: + - main + workflow_dispatch: +permissions: + contents: read +jobs: + markdownlint: + runs-on: ubuntu-latest + steps: + - name: Update pip, git + run: | + set -euxo pipefail + sudo apt update + sudo apt install -y git + + - name: Check out code + uses: actions/checkout@v4 + + # CHANGELOG.md is generated automatically from PR titles and descriptions + # It might have issues but they are not critical + - name: Lint all markdown files except for CHANGELOG.md + uses: docker://avtodev/markdown-lint:master + with: + args: >- + --ignore=CHANGELOG.md + **/*.md + config: .markdownlint.yaml diff --git a/.github/workflows/pr-title-lint.yml b/.github/workflows/pr-title-lint.yml new file mode 100644 index 0000000..95b81f8 --- /dev/null +++ b/.github/workflows/pr-title-lint.yml @@ -0,0 +1,32 @@ +--- +name: PR Title Lint +on: # yamllint disable-line rule:truthy + pull_request: + types: + - opened + - synchronize + - reopened + - edited + merge_group: + branches: + - main + types: + - checks_requested +permissions: + contents: read +jobs: + commit-checks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install conventional-commit linter + run: npm install @commitlint/config-conventional @commitlint/cli + + - name: Run commitlint on PR title + env: + PR_TITLE: ${{ github.event.pull_request.title }} + # Echo from env variable to avoid bash errors with extra characters + run: echo "$PR_TITLE" | npx commitlint --verbose diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml new file mode 100644 index 0000000..3b36d47 --- /dev/null +++ b/.github/workflows/shellcheck.yml @@ -0,0 +1,34 @@ +--- +name: ShellCheck +on: # yamllint disable-line rule:truthy + pull_request: + push: + branches: + - main + workflow_dispatch: +env: + # some scripts source tox-lsr scripts - suppress that check + SHELLCHECK_OPTS: -e SC1091 +permissions: + contents: read +jobs: + shellcheck: + runs-on: ubuntu-latest + steps: + - name: Update git + run: | + set -euxo pipefail + sudo apt update + sudo apt install -y git + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Run ShellCheck + id: shellcheck_id + uses: ludeeus/action-shellcheck@master + + - name: Show file paths scanned + run: | + echo Files scanned: + echo "${{ steps.shellcheck_id.outputs.files }}" diff --git a/.github/workflows/test_converting_readme.yml b/.github/workflows/test_converting_readme.yml new file mode 100644 index 0000000..ee96211 --- /dev/null +++ b/.github/workflows/test_converting_readme.yml @@ -0,0 +1,46 @@ +--- +# yamllint disable rule:line-length +name: Test converting README.md to README.html +on: # yamllint disable-line rule:truthy + pull_request: + merge_group: + branches: + - main + types: + - checks_requested + push: + branches: + - main +permissions: + contents: read +jobs: + test_converting_readme: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Update pip, git + run: | + set -euxo pipefail + sudo apt update + sudo apt install -y git + + - name: Check out code + uses: actions/checkout@v4 + + - name: Remove badges from README.md prior to converting to HTML + run: sed -i '1,8 {/^\[\!.*actions\/workflows/d}' README.md + + - name: Convert README.md to HTML + uses: docker://pandoc/core:latest + with: + args: >- + --from gfm --to html5 --toc --shift-heading-level-by=-1 + --template .pandoc_template.html5 + --output README.html README.md + + - name: Upload README.html as an artifact + uses: actions/upload-artifact@master + with: + name: README.html + path: README.html diff --git a/.github/workflows/tft.yml b/.github/workflows/tft.yml new file mode 100644 index 0000000..ccb89b0 --- /dev/null +++ b/.github/workflows/tft.yml @@ -0,0 +1,194 @@ +--- +name: Run integration tests in Testing Farm +on: + issue_comment: + types: + - created +permissions: + contents: read + # This is required for the ability to create/update the Pull request status + statuses: write +jobs: + prepare_vars: + name: Get info from role and PR to determine if and how to test + # The concurrency key is used to prevent multiple workflows from running at the same time + concurrency: + # group name contains reponame-pr_num to allow simualteneous runs in different PRs + group: testing-farm-${{ github.event.repository.name }}-${{ github.event.issue.number }} + cancel-in-progress: true + # Let's schedule tests only on user request. NOT automatically. + # Only repository owner or member can schedule tests + if: | + github.event.issue.pull_request + && contains(github.event.comment.body, '[citest]') + && (contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR", "CONTRIBUTOR"]'), github.event.comment.author_association) + || contains('systemroller', github.event.comment.user.login)) + runs-on: ubuntu-latest + outputs: + supported_platforms: ${{ steps.supported_platforms.outputs.supported_platforms }} + head_sha: ${{ steps.head_sha.outputs.head_sha }} + memory: ${{ steps.memory.outputs.memory }} + steps: + - name: Dump github context + run: echo "$GITHUB_CONTEXT" + shell: bash + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Get head sha of the PR + id: head_sha + run: | + head_sha=$(gh api "repos/$REPO/pulls/$PR_NO" --jq '.head.sha') + echo "head_sha=$head_sha" >> $GITHUB_OUTPUT + env: + REPO: ${{ github.repository }} + PR_NO: ${{ github.event.issue.number }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Checkout PR + uses: actions/checkout@v4 + with: + ref: ${{ steps.head_sha.outputs.head_sha }} + + - name: Get memory + id: memory + run: | + if [ -d tests/provision.fmf ]; then + memory=$(grep -rPo ' m: \K(.*)' tests/provision.fmf) + fi + if [ -n "$memory" ]; then + echo "memory=$memory" >> $GITHUB_OUTPUT + else + echo "memory=2048" >> $GITHUB_OUTPUT + fi + + - name: Get supported platforms + id: supported_platforms + run: | + supported_platforms="" + meta_main=meta/main.yml + # All Fedora are supported, add latest Fedora versions to supported_platforms + if yq '.galaxy_info.galaxy_tags[]' "$meta_main" | grep -qi fedora$; then + supported_platforms+=" Fedora-39" + supported_platforms+=" Fedora-40" + fi + # Specific Fedora versions supported + if yq '.galaxy_info.galaxy_tags[]' "$meta_main" | grep -qiP 'fedora\d+$'; then + for fedora_ver in $(yq '.galaxy_info.galaxy_tags[]' "$meta_main" | grep -iPo 'fedora\K(\d+$)'); do + supported_platforms+=" Fedora-$fedora_ver" + done + fi + if yq '.galaxy_info.galaxy_tags[]' "$meta_main" | grep -qi el7; then + supported_platforms+=" CentOS-7-latest" + fi + for ver in 8 9 10; do + if yq '.galaxy_info.galaxy_tags[]' "$meta_main" | grep -qi el"$ver"; then + supported_platforms+=" CentOS-Stream-$ver" + fi + done + echo "supported_platforms=$supported_platforms" >> $GITHUB_OUTPUT + + testing-farm: + name: ${{ matrix.platform }}/ansible-${{ matrix.ansible_version }} + needs: prepare_vars + strategy: + fail-fast: false + matrix: + include: + - platform: Fedora-39 + ansible_version: 2.17 + - platform: Fedora-40 + ansible_version: 2.17 + - platform: CentOS-7-latest + ansible_version: 2.9 + - platform: CentOS-Stream-8 + ansible_version: 2.9 + # On CentOS-Stream-8, latest supported Ansible is 2.16 + - platform: CentOS-Stream-8 + ansible_version: 2.16 + - platform: CentOS-Stream-9 + ansible_version: 2.17 + - platform: CentOS-Stream-10 + ansible_version: 2.17 + runs-on: ubuntu-latest + env: + ARTIFACTS_DIR_NAME: "tf_${{ github.event.repository.name }}-${{ github.event.issue.number }}_\ + ${{ matrix.platform }}-${{ matrix.ansible_version }}_\ + ${{ needs.prepare_vars.outputs.datetime }}/artifacts" + ARTIFACT_TARGET_DIR: /srv/pub/alt/${{ vars.LINUXSYSTEMROLES_USER }}/logs + steps: + - name: Set variables with DATETIME and artifact location + id: set_vars + run: | + printf -v DATETIME '%(%Y%m%d-%H%M%S)T' -1 + ARTIFACTS_DIR_NAME="tf_${{ github.event.repository.name }}-${{ github.event.issue.number }}_\ + ${{ matrix.platform }}-${{ matrix.ansible_version }}_$DATETIME/artifacts" + ARTIFACTS_TARGET_DIR=/srv/pub/alt/${{ vars.LINUXSYSTEMROLES_USER }}/logs + ARTIFACTS_DIR=$ARTIFACTS_TARGET_DIR/$ARTIFACTS_DIR_NAME + ARTIFACTS_URL=https://dl.fedoraproject.org/pub/alt/${{ vars.LINUXSYSTEMROLES_USER }}/logs/$ARTIFACTS_DIR_NAME + echo "DATETIME=$DATETIME" >> $GITHUB_OUTPUT + echo "ARTIFACTS_DIR=$ARTIFACTS_DIR" >> $GITHUB_OUTPUT + echo "ARTIFACTS_URL=$ARTIFACTS_URL" >> $GITHUB_OUTPUT + + - name: Set commit status as pending + if: contains(needs.prepare_vars.outputs.supported_platforms, matrix.platform) + uses: myrotvorets/set-commit-status-action@master + with: + sha: ${{ needs.prepare_vars.outputs.head_sha }} + status: pending + context: ${{ matrix.platform }}|ansible-${{ matrix.ansible_version }} + description: Test started + targetUrl: "" + + - name: Set commit status as success with a description that platform is skipped + if: "!contains(needs.prepare_vars.outputs.supported_platforms, matrix.platform)" + uses: myrotvorets/set-commit-status-action@master + with: + sha: ${{ needs.prepare_vars.outputs.head_sha }} + status: success + context: ${{ matrix.platform }}|ansible-${{ matrix.ansible_version }} + description: The role does not support this platform. Skipping. + targetUrl: "" + + - name: Run test in testing farm + uses: sclorg/testing-farm-as-github-action@v3 + if: contains(needs.prepare_vars.outputs.supported_platforms, matrix.platform) + with: + git_url: https://github.com/linux-system-roles/tft-tests + git_ref: main + pipeline_settings: '{ "type": "tmt-multihost" }' + environment_settings: '{ "provisioning": { "tags": { "BusinessUnit": "system_roles" } } }' + # Keeping ARTIFACTS_URL at the bottom makes the link in logs clickable + variables: "ANSIBLE_VER=${{ matrix.ansible_version }};\ + REPO_NAME=${{ github.event.repository.name }};\ + GITHUB_ORG=${{ github.repository_owner }};\ + PR_NUM=${{ github.event.issue.number }};\ + ARTIFACTS_DIR=${{ steps.set_vars.outputs.ARTIFACTS_DIR }};\ + TEST_LOCAL_CHANGES=false;\ + LINUXSYSTEMROLES_USER=${{ vars.LINUXSYSTEMROLES_USER }};\ + ARTIFACTS_URL=${{ steps.set_vars.outputs.ARTIFACTS_URL }}" + # Note that LINUXSYSTEMROLES_SSH_KEY must be single-line, TF doesn't read multi-line variables fine. + secrets: "LINUXSYSTEMROLES_DOMAIN=${{ secrets.LINUXSYSTEMROLES_DOMAIN }};\ + LINUXSYSTEMROLES_SSH_KEY=${{ secrets.LINUXSYSTEMROLES_SSH_KEY }}" + compose: ${{ matrix.platform }} + # There are two blockers for using public ranch: + # 1. multihost is not supported in public https://github.com/teemtee/tmt/issues/2620 + # 2. Security issue that leaks long secrets - Jira TFT-2698 + tf_scope: private + api_key: ${{ secrets.TF_API_KEY_RH }} + update_pull_request_status: false + tmt_hardware: '{ "memory": ">= ${{ needs.prepare_vars.outputs.memory }} MB" }' + tmt_plan_filter: "tag:general,template" + + - name: Set final commit status + uses: myrotvorets/set-commit-status-action@master + if: always() && contains(needs.prepare_vars.outputs.supported_platforms, matrix.platform) + with: + sha: ${{ needs.prepare_vars.outputs.head_sha }} + status: ${{ job.status }} + context: ${{ matrix.platform }}|ansible-${{ matrix.ansible_version }} + description: Test finished + targetUrl: ${{ steps.set_vars.outputs.ARTIFACTS_URL }} diff --git a/.github/workflows/tft_citest_bad.yml b/.github/workflows/tft_citest_bad.yml new file mode 100644 index 0000000..85db1f4 --- /dev/null +++ b/.github/workflows/tft_citest_bad.yml @@ -0,0 +1,43 @@ +--- +name: Re-run failed testing farm tests +on: + issue_comment: + types: + - created +permissions: + contents: read +jobs: + citest_bad_rerun: + if: | + github.event.issue.pull_request + && contains(fromJson('["[citest_bad]", "[citest-bad]", "[citest bad]"]'), github.event.comment.body) + && contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR", "CONTRIBUTOR"]'), github.event.comment.author_association) + permissions: + actions: write # for re-running failed jobs: https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#re-run-a-job-from-a-workflow-run + runs-on: ubuntu-latest + steps: + - name: Wait 10s until tft.yml workflow is created and skipped because new comment don't match [citest] + run: sleep 10s + + - name: Re-run failed jobs for this PR + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + PR_TITLE: ${{ github.event.issue.title }} + run: | + PENDING_RUN=$(gh api "repos/$REPO/actions/workflows/tft.yml/runs?event=issue_comment" \ + | jq -r "[.workflow_runs[] | select( .display_title == \"$PR_TITLE\") | \ + select(.status == \"pending\" or .status == \"queued\" or .status == \"in_progress\") | .id][0]") + # if pending run don't exist, take the last run with failure state + if [ "$PENDING_RUN" != "null" ]; then + echo "The workflow $PENDING_RUN is still running, wait for it to finish to re-run" + exit 1 + fi + RUN_ID=$(gh api "repos/$REPO/actions/workflows/tft.yml/runs?event=issue_comment" \ + | jq -r "[.workflow_runs[] | select( .display_title == \"$PR_TITLE\" ) | select( .conclusion == \"failure\" ) | .id][0]") + if [ "$RUN_ID" = "null" ]; then + echo "Failed workflow not found, exitting" + exit 1 + fi + echo "Re-running workflow $RUN_ID" + gh api --method POST repos/$REPO/actions/runs/$RUN_ID/rerun-failed-jobs diff --git a/.github/workflows/weekly_ci.yml b/.github/workflows/weekly_ci.yml new file mode 100644 index 0000000..aff7f38 --- /dev/null +++ b/.github/workflows/weekly_ci.yml @@ -0,0 +1,84 @@ +--- +# yamllint disable rule:line-length +name: Weekly CI trigger +on: # yamllint disable-line rule:truthy + workflow_dispatch: + schedule: + - cron: 0 0 * * 6 +env: + BRANCH_NAME: weekly-ci + COMMIT_MESSAGE: "ci: This PR is to trigger periodic CI testing" + BODY_MESSAGE: >- + This PR is for the purpose of triggering periodic CI testing. + We don't currently have a way to trigger CI without a PR, + so this PR serves that purpose. + COMMENT: "[citest]" +permissions: + contents: read +jobs: + weekly_ci: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + contents: write + steps: + - name: Update pip, git + run: | + set -euxo pipefail + sudo apt update + sudo apt install -y git + + - name: Checkout latest code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Create or rebase commit, add dump_packages callback + run: | + set -euxo pipefail + + git config --global user.name "github-actions[bot]" + git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout ${{ env.BRANCH_NAME }} || git checkout -b ${{ env.BRANCH_NAME }} + git rebase main + if [ ! -d tests/callback_plugins ]; then + mkdir -p tests/callback_plugins + fi + curl -L -s -o tests/callback_plugins/dump_packages.py https://raw.githubusercontent.com/linux-system-roles/auto-maintenance/main/callback_plugins/dump_packages.py + git add tests/callback_plugins + git commit --allow-empty -m "${{ env.COMMIT_MESSAGE }}" + git push -f --set-upstream origin ${{ env.BRANCH_NAME }} + + - name: Create and comment pull request + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GH_PUSH_TOKEN }} + script: | + const head = [context.repo.owner, ":", "${{ env.BRANCH_NAME }}"].join(""); + const response = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + head: head, + base: context.ref, + state: "open" + }); + let pr_number = ''; + if (response.data.length === 0) { + pr_number = (await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: "${{ env.COMMIT_MESSAGE }}", + body: "${{ env.BODY_MESSAGE }}", + head: "${{ env.BRANCH_NAME }}", + base: context.ref, + draft: true + })).data.number; + } else { + pr_number = response.data[0].number; + } + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr_number, + body: "${{ env.COMMENT }}", + }); diff --git a/.github/workflows/woke.yml b/.github/workflows/woke.yml new file mode 100644 index 0000000..4cded0d --- /dev/null +++ b/.github/workflows/woke.yml @@ -0,0 +1,20 @@ +--- +# yamllint disable rule:line-length +name: Woke +on: # yamllint disable-line rule:truthy + - pull_request +jobs: + woke: + name: Detect non-inclusive language + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Run lsr-woke-action + # Originally, uses: get-woke/woke-action@v0 + uses: linux-system-roles/lsr-woke-action@main + with: + woke-args: "-c https://raw.githubusercontent.com/linux-system-roles/tox-lsr/main/src/tox_lsr/config_files/woke.yml --count-only-error-for-failure" + # Cause the check to fail on any broke rules + fail-on-error: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..10cccca --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +passes.yml +vault.yml +*.pyc +*.retry +/tests/.coverage +/tests/htmlcov* +/.tox +/venv*/ +/.venv/ +.vscode/ +artifacts/ +__pycache__/ +*~ +.pytest_cache/ diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..6bf4ccd --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,261 @@ +--- +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: null + +# MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time +MD001: true + +# MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading +MD002: + # Heading level + level: 1 + +# MD003/heading-style/header-style - Heading style +MD003: + # Heading style + style: "consistent" + +# MD004/ul-style - Unordered list style +MD004: + # List style + style: "consistent" + +# MD005/list-indent - Inconsistent indentation for list items at the same level +MD005: true + +# MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line +MD006: true + +# MD007/ul-indent - Unordered list indentation +MD007: + # Spaces for indent + indent: 2 + # Whether to indent the first level of the list + start_indented: false + # Spaces for first level indent (when start_indented is set) + start_indent: 2 + +# MD009/no-trailing-spaces - Trailing spaces +MD009: + # Spaces for line break + br_spaces: 2 + # Allow spaces for empty lines in list items + list_item_empty_lines: false + # Include unnecessary breaks + strict: false + +# MD010/no-hard-tabs - Hard tabs +MD010: + # Include code blocks + code_blocks: true + # Fenced code languages to ignore + ignore_code_languages: [] + # Number of spaces for each hard tab + spaces_per_tab: 1 + +# MD011/no-reversed-links - Reversed link syntax +MD011: true + +# MD012/no-multiple-blanks - Multiple consecutive blank lines +MD012: + # Consecutive blank lines + maximum: 1 + +# Modified for LSR +# GFM does not limit line length +# MD013/line-length - Line length +MD013: false + # # Number of characters + # # line_length: 80 + # line_length: 999 + # # Number of characters for headings + # heading_line_length: 80 + # # Number of characters for code blocks + # code_block_line_length: 80 + # # Include code blocks + # code_blocks: true + # # Include tables + # tables: true + # # Include headings + # headings: true + # # Include headings + # headers: true + # # Strict length checking + # strict: false + # # Stern length checking + # stern: false + +# MD014/commands-show-output - Dollar signs used before commands without showing output +MD014: true + +# MD018/no-missing-space-atx - No space after hash on atx style heading +MD018: true + +# MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading +MD019: true + +# MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading +MD020: true + +# MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading +MD021: true + +# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines +MD022: + # Blank lines above heading + lines_above: 1 + # Blank lines below heading + lines_below: 1 + +# MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line +MD023: true + +# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content +MD024: true + +# MD025/single-title/single-h1 - Multiple top-level headings in the same document +MD025: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD026/no-trailing-punctuation - Trailing punctuation in heading +MD026: + # Punctuation characters not allowed at end of headings + punctuation: ".,;:!。,;:!" + +# MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol +MD027: true + +# MD028/no-blanks-blockquote - Blank line inside blockquote +MD028: true + +# MD029/ol-prefix - Ordered list item prefix +MD029: + # List style + style: "one_or_ordered" + +# MD030/list-marker-space - Spaces after list markers +MD030: + # Spaces for single-line unordered list items + ul_single: 1 + # Spaces for single-line ordered list items + ol_single: 1 + # Spaces for multi-line unordered list items + ul_multi: 1 + # Spaces for multi-line ordered list items + ol_multi: 1 + +# MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines +MD031: + # Include list items + list_items: true + +# MD032/blanks-around-lists - Lists should be surrounded by blank lines +MD032: true + +# MD033/no-inline-html - Inline HTML +MD033: + # Allowed elements + allowed_elements: [] + +# MD034/no-bare-urls - Bare URL used +MD034: true + +# MD035/hr-style - Horizontal rule style +MD035: + # Horizontal rule style + style: "consistent" + +# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading +MD036: + # Punctuation characters + punctuation: ".,;:!?。,;:!?" + +# MD037/no-space-in-emphasis - Spaces inside emphasis markers +MD037: true + +# MD038/no-space-in-code - Spaces inside code span elements +MD038: true + +# MD039/no-space-in-links - Spaces inside link text +MD039: true + +# MD040/fenced-code-language - Fenced code blocks should have a language specified +MD040: + # List of languages + allowed_languages: [] + # Require language only + language_only: false + +# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading +MD041: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD042/no-empty-links - No empty links +MD042: true + +# Modified for LSR +# Disabling, we do not need this +# MD043/required-headings/required-headers - Required heading structure +MD043: false + # # List of headings + # headings: [] + # # List of headings + # headers: [] + # # Match case of headings + # match_case: false + +# MD044/proper-names - Proper names should have the correct capitalization +MD044: + # List of proper names + names: [] + # Include code blocks + code_blocks: true + # Include HTML elements + html_elements: true + +# MD045/no-alt-text - Images should have alternate text (alt text) +MD045: true + +# MD046/code-block-style - Code block style +MD046: + # Block style + style: "consistent" + +# MD047/single-trailing-newline - Files should end with a single newline character +MD047: true + +# MD048/code-fence-style - Code fence style +MD048: + # Code fence style + style: "consistent" + +# MD049/emphasis-style - Emphasis style should be consistent +MD049: + # Emphasis style should be consistent + style: "consistent" + +# MD050/strong-style - Strong style should be consistent +MD050: + # Strong style should be consistent + style: "consistent" + +# MD051/link-fragments - Link fragments should be valid +MD051: true + +# MD052/reference-links-images - Reference links and images should use a label that is defined +MD052: true + +# MD053/link-image-reference-definitions - Link and image reference definitions should be needed +MD053: + # Ignored definitions + ignored_definitions: + - "//" diff --git a/.ostree/README.md b/.ostree/README.md new file mode 100644 index 0000000..f5e6931 --- /dev/null +++ b/.ostree/README.md @@ -0,0 +1,3 @@ +*NOTE*: The `*.txt` files are used by `get_ostree_data.sh` to create the lists +of packages, and to find other system roles used by this role. DO NOT use them +directly. diff --git a/.ostree/get_ostree_data.sh b/.ostree/get_ostree_data.sh new file mode 100755 index 0000000..65830d6 --- /dev/null +++ b/.ostree/get_ostree_data.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ostree_dir="${OSTREE_DIR:-"$(dirname "$(realpath "$0")")"}" + +if [ -z "${4:-}" ] || [ "${1:-}" = help ] || [ "${1:-}" = -h ]; then + cat <&2 echo ERROR - could not find role "$role" - please use ANSIBLE_COLLECTIONS_PATH + exit 2 +} + +get_packages() { + local ostree_dir pkgtype pkgfile rolefile + ostree_dir="$1" + for pkgtype in "${pkgtypes[@]}"; do + for suff in "" "-$distro" "-${distro}-${major_ver}" "-${distro}-${ver}"; do + pkgfile="$ostree_dir/packages-${pkgtype}${suff}.txt" + if [ -f "$pkgfile" ]; then + cat "$pkgfile" + fi + done + rolefile="$ostree_dir/roles-${pkgtype}.txt" + if [ -f "$rolefile" ]; then + local roles role rolepath + roles="$(cat "$rolefile")" + for role in $roles; do + rolepath="$(get_rolepath "$ostree_dir" "$role")" + if [ -z "$rolepath" ]; then + 1>&2 echo ERROR - could not find role "$role" - please use ANSIBLE_COLLECTIONS_PATH + exit 2 + fi + get_packages "$rolepath" + done + fi + done | sort -u +} + +format_packages_json() { + local comma pkgs pkg + comma="" + pkgs="[" + while read -r pkg; do + pkgs="${pkgs}${comma}\"${pkg}\"" + comma=, + done + pkgs="${pkgs}]" + echo "$pkgs" +} + +format_packages_raw() { + cat +} + +format_packages_yaml() { + while read -r pkg; do + echo "- $pkg" + done +} + +format_packages_toml() { + while read -r pkg; do + echo "[[packages]]" + echo "name = \"$pkg\"" + echo "version = \"*\"" + done +} + +distro="${distro_ver%%-*}" +ver="${distro_ver##*-}" +if [[ "$ver" =~ ^([0-9]*) ]]; then + major_ver="${BASH_REMATCH[1]}" +else + echo ERROR: cannot parse major version number from version "$ver" + exit 1 +fi + +"get_$category" "$ostree_dir" | "format_${category}_$format" diff --git a/.pandoc_template.html5 b/.pandoc_template.html5 new file mode 100644 index 0000000..f214661 --- /dev/null +++ b/.pandoc_template.html5 @@ -0,0 +1,166 @@ +$--| GitHub HTML5 Pandoc Template" v2.2 | 2020/08/12 | pandoc v2.1.1 + + +$-------------------------------------------------------------------------> lang + + +$--============================================================================= +$-- METADATA +$--============================================================================= + + + +$-----------------------------------------------------------------------> author +$for(author-meta)$ + +$endfor$ +$-------------------------------------------------------------------------> date +$if(date-meta)$ + +$endif$ +$---------------------------------------------------------------------> keywords +$if(keywords)$ + +$endif$ +$------------------------------------------------------------------> description +$if(description)$ + +$endif$ +$------------------------------------------------------------------------> title + $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ +$--=========================================================================== +$-- CSS STYLESHEETS +$--=========================================================================== +$-- Here comes the placeholder (within double braces) that will be replaced +$-- by the CSS file in the finalized template: + +$------------------------------------------------------------------------------- + +$------------------------------------------------------------------------------- +$if(quotes)$ + +$endif$ +$-------------------------------------------------------------> highlighting-css +$if(highlighting-css)$ + +$endif$ +$--------------------------------------------------------------------------> css +$for(css)$ + +$endfor$ +$-------------------------------------------------------------------------> math +$if(math)$ + $math$ +$endif$ +$------------------------------------------------------------------------------- + +$--------------------------------------------------------------> header-includes +$for(header-includes)$ + $header-includes$ +$endfor$ +$------------------------------------------------------------------------------- + + +
+$---------------------------------------------------------------> include-before +$for(include-before)$ +$include-before$ +$endfor$ +$-->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> IF: title +$if(title)$ +
+

$title$

+$---------------------------------------------------------------------> subtitle +$if(subtitle)$ +

$subtitle$

+$endif$ +$-----------------------------------------------------------------------> author +$for(author)$ +

$author$

+$endfor$ +$-------------------------------------------------------------------------> date +$if(date)$ +

$date$

+$endif$ +$----------------------------------------------------------------------> summary +$if(summary)$ +
+$summary$ +
+$endif$ +$------------------------------------------------------------------------------- +
+$endif$ +$--<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< END IF: title +$--------------------------------------------------------------------------> toc +$if(toc)$ +
+ +
+$endif$ +$-------------------------------------------------------------------------> body +$body$ +$----------------------------------------------------------------> include-after +$for(include-after)$ +$include-after$ +$endfor$ +$------------------------------------------------------------------------------- +
+ + diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 0000000..f37db65 --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: MIT +--- +ignore: | + /.tox/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5c025d1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README-ansible.md b/README-ansible.md new file mode 100644 index 0000000..01a54eb --- /dev/null +++ b/README-ansible.md @@ -0,0 +1,5 @@ +# Introduction to Ansible for Linux System Roles + +If you are not familiar with Ansible, please see +[Introduction to Ansible for Linux System Roles](https://linux-system-roles.github.io/documentation/intro-to-ansible-for-system-roles.html), +where many useful links are presented. diff --git a/README-ostree.md b/README-ostree.md new file mode 100644 index 0000000..a9f0185 --- /dev/null +++ b/README-ostree.md @@ -0,0 +1,66 @@ +# rpm-ostree + +The role supports running on [rpm-ostree](https://coreos.github.io/rpm-ostree/) +systems. The primary issue is that the `/usr` filesystem is read-only, and the +role cannot install packages. Instead, it will just verify that the necessary +packages and any other `/usr` files are pre-installed. The role will change the +package manager to one that is compatible with `rpm-ostree` systems. + +## Building + +To build an ostree image for a particular operating system distribution and +version, use the script `.ostree/get_ostree_data.sh` to get the list of +packages. If the role uses other system roles, then the script will include the +packages for the other roles in the list it outputs. The list of packages will +be sorted in alphanumeric order. + +Usage: + +```bash +.ostree/get_ostree_data.sh packages runtime DISTRO-VERSION FORMAT +``` + +`DISTRO-VERSION` is in the format that Ansible uses for `ansible_distribution` +and `ansible_distribution_version` - for example, `Fedora-38`, `CentOS-8`, +`RedHat-9.4` + +`FORMAT` is one of `toml`, `json`, `yaml`, `raw` + +* `toml` - each package in a TOML `[[packages]]` element + +```toml +[[packages]] +name = "package-a" +version = "*" +[[packages]] +name = "package-b" +version = "*" +... +``` + +* `yaml` - a YAML list of packages + +```yaml +- package-a +- package-b +... +``` + +* `json` - a JSON list of packages + +```json +["package-a","package-b",...] +``` + +* `raw` - a plain text list of packages, one per line + +```bash +package-a +package-b +... +``` + +What format you choose depends on which image builder you are using. For +example, if you are using something based on +[osbuild-composer](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/composing_installing_and_managing_rhel_for_edge_images/index#creating-an-image-builder-blueprint-for-a-rhel-for-edge-image-using-the-command-line-interface_composing-a-rhel-for-edge-image-using-image-builder-command-line), +you will probably want to use the `toml` output format. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7229849 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# Role Name + +[![ansible-lint.yml](https://github.com/linux-system-roles/template/actions/workflows/ansible-lint.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/ansible-lint.yml) [![ansible-test.yml](https://github.com/linux-system-roles/template/actions/workflows/ansible-test.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/ansible-test.yml) [![markdownlint.yml](https://github.com/linux-system-roles/template/actions/workflows/markdownlint.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/markdownlint.yml) [![shellcheck.yml](https://github.com/linux-system-roles/template/actions/workflows/shellcheck.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/shellcheck.yml) [![tft.yml](https://github.com/linux-system-roles/template/actions/workflows/tft.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/tft.yml) [![tft_citest_bad.yml](https://github.com/linux-system-roles/template/actions/workflows/tft_citest_bad.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/tft_citest_bad.yml) [![woke.yml](https://github.com/linux-system-roles/template/actions/workflows/woke.yml/badge.svg)](https://github.com/linux-system-roles/template/actions/workflows/woke.yml) + +![template](https://github.com/linux-system-roles/template/workflows/tox/badge.svg) + +A template for an ansible role that configures some GNU/Linux subsystem or +service. A brief description of the role goes here. + +## Requirements + +Any prerequisites that may not be covered by Ansible itself or the role should +be mentioned here. This includes platform dependencies not managed by the +role, hardware requirements, external collections, etc. There should be a +distinction between *control node* requirements (like collections) and +*managed node* requirements (like special hardware, platform provisioning). + +### Collection requirements + +For instance, if the role depends on some collections and has a +`meta/collection-requirements.yml` file for installing those dependencies, and +in order to manage `rpm-ostree` systems, it should be mentioned here that the + user should run + +```bash +ansible-galaxy collection install -vv -r meta/collection-requirements.yml +``` + +on the *control node* before using the role. + +## Role Variables + +A description of all input variables (i.e. variables that are defined in +`defaults/main.yml`) for the role should go here as these form an API of the +role. Each variable should have its own section e.g. + +### template_foo + +This variable is required. It is a string that lists the foo of the role. +There is no default value. + +### template_bar + +This variable is optional. It is a boolean that tells the role to disable bar. +The default value is `true`. + +Variables that are not intended as input, like variables defined in +`vars/main.yml`, variables that are read from other roles and/or the global +scope (ie. hostvars, group vars, etc.) can be also mentioned here but keep in +mind that as these are probably not part of the role API they may change during +the lifetime. + +Example of setting the variables: + +```yaml +template_foo: "oof" +template_bar: false +``` + +## Variables Exported by the Role + +This section is optional. Some roles may export variables for playbooks to +use later. These are analogous to "return values" in Ansible modules. For +example, if a role performs some action that will require a system reboot, but +the user wants to defer the reboot, the role might set a variable like +`template_reboot_needed: true` that the playbook can use to reboot at a more +convenient time. + +Example: + +### template_reboot_needed + +Default `false` - if `true`, this means a reboot is needed to apply the changes +made by the role + +## Example Playbook + +Including an example of how to use your role (for instance, with variables +passed in as parameters) is always nice for users too: + +```yaml +- name: Manage the template subsystem + hosts: all + vars: + template_foo: "foo foo!" + template_bar: false + roles: + - linux-system-roles.template +``` + +More examples can be provided in the [`examples/`](examples) directory. These +can be useful, especially for documentation. + +## rpm-ostree + +See README-ostree.md + +## License + +Whenever possible, please prefer MIT. + +## Author Information + +An optional section for the role authors to include contact information, or a +website (HTML is not allowed). diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..44e3ad0 --- /dev/null +++ b/contributing.md @@ -0,0 +1,21 @@ +# Contributing to the template Linux System Role + +## Where to start + +The first place to go is [Contribute](https://linux-system-roles.github.io/contribute.html). +This has all of the common information that all role developers need: + +* Role structure and layout +* Development tools - How to run tests and checks +* Ansible recommended practices +* Basic git and github information +* How to create git commits and submit pull requests + +**Bugs and needed implementations** are listed on +[Github Issues](https://github.com/linux-system-roles/template/issues). +Issues labeled with +[**help wanted**](https://github.com/linux-system-roles/template/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) +are likely to be suitable for new contributors! + +**Code** is managed on [Github](https://github.com/linux-system-roles/template), using +[Pull Requests](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests). diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..6944529 --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: MIT +--- +# Here is the right place to put the role's input variables. +# This file also serves as a documentation for such a variables. + +# Examples of role input variables: +template_foo: foo +template_bar: true diff --git a/examples/simple.yml b/examples/simple.yml new file mode 100644 index 0000000..4f1456e --- /dev/null +++ b/examples/simple.yml @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: MIT +--- +- name: Example template role invocation + hosts: all + vars: + template_foo: example variable value + template_bar: false + roles: + - linux-system-roles.template diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..726022e --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +--- +- name: Handler for template to restart services + service: + name: "{{ item }}" + state: restarted + loop: "{{ __template_services }}" diff --git a/meta/collection-requirements.yml b/meta/collection-requirements.yml new file mode 100644 index 0000000..0110bbe --- /dev/null +++ b/meta/collection-requirements.yml @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: MIT +--- +collections: + - ansible.posix diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..a8a1444 --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: MIT +--- +galaxy_info: + author: John Doe + description: Basic template for Linux system roles + company: John Doe, Inc. + license: MIT + min_ansible_version: "2.9" + platforms: + - name: Fedora + versions: + - all + - name: EL + versions: + - "9" + galaxy_tags: + - el9 + - el10 + - fedora +dependencies: [] diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..6eb72a5 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: MIT +--- +- name: Set platform/version specific variables + include_tasks: tasks/set_vars.yml + +# Examples of some tasks: +- name: Ensure required packages are installed + package: + name: "{{ __template_packages }}" + state: present + use: "{{ (__template_is_ostree | d(false)) | + ternary('ansible.posix.rhel_rpm_ostree', omit) }}" + +- name: Ensure required services are enabled and started + service: + name: "{{ item }}" + state: started + enabled: true + loop: "{{ __template_services }}" + +- name: Generate /etc/{{ __template_foo_config }} + template: + src: "{{ __template_foo_config }}.j2" + dest: /etc/{{ __template_foo_config }} + backup: true + mode: "0400" + notify: Handler for template to restart services diff --git a/tasks/set_vars.yml b/tasks/set_vars.yml new file mode 100644 index 0000000..c1ef3f6 --- /dev/null +++ b/tasks/set_vars.yml @@ -0,0 +1,33 @@ +--- +- name: Ensure ansible_facts used by role + setup: + gather_subset: "{{ __template_required_facts_subsets }}" + when: __template_required_facts | + difference(ansible_facts.keys() | list) | length > 0 + +- name: Determine if system is ostree and set flag + when: not __template_is_ostree is defined + block: + - name: Check if system is ostree + stat: + path: /run/ostree-booted + register: __ostree_booted_stat + + - name: Set flag to indicate system is ostree + set_fact: + __template_is_ostree: "{{ __ostree_booted_stat.stat.exists }}" + +- name: Set platform/version specific variables + include_vars: "{{ __vars_file }}" + loop: + - "{{ ansible_facts['os_family'] }}.yml" + - "{{ ansible_facts['distribution'] }}.yml" + - >- + {{ ansible_facts['distribution'] ~ '_' ~ + ansible_facts['distribution_major_version'] }}.yml + - >- + {{ ansible_facts['distribution'] ~ '_' ~ + ansible_facts['distribution_version'] }}.yml + vars: + __vars_file: "{{ role_path }}/vars/{{ item }}" + when: __vars_file is file diff --git a/templates/foo.conf.j2 b/templates/foo.conf.j2 new file mode 100644 index 0000000..5fc204b --- /dev/null +++ b/templates/foo.conf.j2 @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: MIT +# +# Example of a template of configuration file +# +{{ ansible_managed | comment }} +{{ "system_role:template" | comment(prefix="", postfix="") }} +[foo] +foo = {{ template_foo }} +bar = {{ template_bar }} diff --git a/tests/roles/caller/tasks/main.yml b/tests/roles/caller/tasks/main.yml new file mode 100644 index 0000000..edc195d --- /dev/null +++ b/tests/roles/caller/tasks/main.yml @@ -0,0 +1,10 @@ +--- +# tasks file for caller + +- name: Include role + include_role: + name: "{{ roletoinclude }}" + +- name: Assert variable not overridden + assert: + that: not __caller_override diff --git a/tests/roles/caller/vars/main.yml b/tests/roles/caller/vars/main.yml new file mode 100644 index 0000000..fff38f0 --- /dev/null +++ b/tests/roles/caller/vars/main.yml @@ -0,0 +1,3 @@ +--- +# vars file for caller +__caller_override: false diff --git a/tests/roles/linux-system-roles.template/defaults b/tests/roles/linux-system-roles.template/defaults new file mode 120000 index 0000000..30459c3 --- /dev/null +++ b/tests/roles/linux-system-roles.template/defaults @@ -0,0 +1 @@ +../../../defaults \ No newline at end of file diff --git a/tests/roles/linux-system-roles.template/handlers b/tests/roles/linux-system-roles.template/handlers new file mode 120000 index 0000000..3b11237 --- /dev/null +++ b/tests/roles/linux-system-roles.template/handlers @@ -0,0 +1 @@ +../../../handlers \ No newline at end of file diff --git a/tests/roles/linux-system-roles.template/meta b/tests/roles/linux-system-roles.template/meta new file mode 120000 index 0000000..8df72eb --- /dev/null +++ b/tests/roles/linux-system-roles.template/meta @@ -0,0 +1 @@ +../../../meta \ No newline at end of file diff --git a/tests/roles/linux-system-roles.template/tasks b/tests/roles/linux-system-roles.template/tasks new file mode 120000 index 0000000..d97297b --- /dev/null +++ b/tests/roles/linux-system-roles.template/tasks @@ -0,0 +1 @@ +../../../tasks \ No newline at end of file diff --git a/tests/roles/linux-system-roles.template/templates b/tests/roles/linux-system-roles.template/templates new file mode 120000 index 0000000..0e4c94f --- /dev/null +++ b/tests/roles/linux-system-roles.template/templates @@ -0,0 +1 @@ +../../../templates \ No newline at end of file diff --git a/tests/roles/linux-system-roles.template/vars b/tests/roles/linux-system-roles.template/vars new file mode 120000 index 0000000..663079d --- /dev/null +++ b/tests/roles/linux-system-roles.template/vars @@ -0,0 +1 @@ +../../../vars \ No newline at end of file diff --git a/tests/setup-snapshot.yml b/tests/setup-snapshot.yml new file mode 100644 index 0000000..a7704df --- /dev/null +++ b/tests/setup-snapshot.yml @@ -0,0 +1,14 @@ +--- +- name: Setup snapshot + hosts: all + tasks: + - name: Set platform/version specific variables + include_role: + name: linux-system-roles.template + tasks_from: set_vars.yml + public: true + + - name: Install test packages + package: + name: "{{ __template_packages }}" + state: present diff --git a/tests/tasks/check_header.yml b/tests/tasks/check_header.yml new file mode 100644 index 0000000..607320f --- /dev/null +++ b/tests/tasks/check_header.yml @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: MIT +--- +- name: Get file + slurp: + path: "{{ __file }}" + register: __content + when: not __file_content is defined + +- name: Check for presence of ansible managed header, fingerprint + assert: + that: + - ansible_managed in content + - __fingerprint in content + vars: + content: "{{ (__file_content | d(__content)).content | b64decode }}" + ansible_managed: "{{ lookup('template', 'get_ansible_managed.j2') }}" diff --git a/tests/templates/get_ansible_managed.j2 b/tests/templates/get_ansible_managed.j2 new file mode 100644 index 0000000..c690cfa --- /dev/null +++ b/tests/templates/get_ansible_managed.j2 @@ -0,0 +1 @@ +{{ ansible_managed | comment(__comment_type | d("plain")) }} diff --git a/tests/tests_default.yml b/tests/tests_default.yml new file mode 100644 index 0000000..4457422 --- /dev/null +++ b/tests/tests_default.yml @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: MIT +--- +- name: Ensure that the role runs with default parameters + hosts: all + gather_facts: false # test that role works in this case + roles: + - linux-system-roles.template + tasks: + - name: Check header for ansible_managed, fingerprint + include_tasks: tasks/check_header.yml + vars: + __file: /etc/foo.conf + __fingerprint: system_role:template diff --git a/tests/tests_include_vars_from_parent.yml b/tests/tests_include_vars_from_parent.yml new file mode 100644 index 0000000..fc795dd --- /dev/null +++ b/tests/tests_include_vars_from_parent.yml @@ -0,0 +1,55 @@ +--- +- name: Test role include variable override + hosts: all + gather_facts: true + tasks: + - name: Create var file in caller that can override the one in called role + delegate_to: localhost + copy: + # usually the fake file will cause the called role to crash of + # overriding happens, but if not, set a variable that will + # allow to detect the bug + content: "__caller_override: true" + # XXX ugly, self-modifying code - changes the "caller" role on + # the controller + dest: "{{ playbook_dir }}/roles/caller/vars/{{ item }}.yml" + mode: preserve + loop: "{{ varfiles | unique }}" + # In case the playbook is executed against multiple hosts, use + # only the first one. Otherwise the hosts would stomp on each + # other since they are changing files on the controller. + when: inventory_hostname == ansible_play_hosts_all[0] + vars: + # change to hostvars['localhost']['ansible_facts'] to use the + # information for localhost + facts: "{{ ansible_facts }}" + versions: + - "{{ facts['distribution_version'] }}" + - "{{ facts['distribution_major_version'] }}" + separators: ["-", "_"] + # create all variants like CentOS, CentOS_8.1, CentOS-8.1, + # CentOS-8, CentOS-8.1 + # more formally: + # {{ ansible_distribution }}-{{ ansible_distribution_version }} + # {{ ansible_distribution }}-{{ ansible_distribution_major_version }} + # {{ ansible_distribution }} + # {{ ansible_os_family }} + # and the same for _ as separator. + varfiles: "{{ [facts['distribution']] | product(separators) | + map('join') | product(versions) | map('join') | list + + [facts['distribution'], facts['os_family']] }}" + register: __varfiles_created + + - name: Import role + import_role: + name: caller + vars: + roletoinclude: linux-system-roles.template + + - name: Cleanup + file: + path: "{{ item.dest }}" + state: absent + loop: "{{ __varfiles_created.results }}" + delegate_to: localhost + when: inventory_hostname == ansible_play_hosts_all[0] diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..703418d --- /dev/null +++ b/tox.ini @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT +[lsr_config] +lsr_enable = true diff --git a/vars/CentOS_10.yml b/vars/CentOS_10.yml new file mode 100644 index 0000000..7331ad9 --- /dev/null +++ b/vars/CentOS_10.yml @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +--- +# Put internal variables here with CentOS 10 specific values. + +# Example: +__template_packages: [] +__template_services: [] diff --git a/vars/CentOS_7.yml b/vars/CentOS_7.yml new file mode 100644 index 0000000..24448bc --- /dev/null +++ b/vars/CentOS_7.yml @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +--- +# Put internal variables here with CentOS 7 specific values. + +# Example: +__template_packages: [] +__template_services: [] diff --git a/vars/CentOS_8.yml b/vars/CentOS_8.yml new file mode 100644 index 0000000..0fb6af8 --- /dev/null +++ b/vars/CentOS_8.yml @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +--- +# Put internal variables here with CentOS 8 specific values. + +# Example: +__template_packages: [] +__template_services: [] diff --git a/vars/CentOS_9.yml b/vars/CentOS_9.yml new file mode 100644 index 0000000..7c25b3c --- /dev/null +++ b/vars/CentOS_9.yml @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +--- +# Put internal variables here with CentOS 9 specific values. + +# Example: +__template_packages: [] +__template_services: [] diff --git a/vars/Fedora.yml b/vars/Fedora.yml new file mode 100644 index 0000000..a783f79 --- /dev/null +++ b/vars/Fedora.yml @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +--- +# Put internal variables here with Fedora specific values. + +# Example: +__template_packages: [] +__template_services: [] diff --git a/vars/RedHat_10.yml b/vars/RedHat_10.yml new file mode 100644 index 0000000..c1a73a0 --- /dev/null +++ b/vars/RedHat_10.yml @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +--- +# Put internal variables here with Red Hat Enterprise Linux 10 specific values. + +# Example: +__template_packages: [] +__template_services: [] diff --git a/vars/RedHat_7.yml b/vars/RedHat_7.yml new file mode 100644 index 0000000..3815df4 --- /dev/null +++ b/vars/RedHat_7.yml @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +--- +# Put internal variables here with Red Hat Enterprise Linux 7 specific values. + +# Example: +__template_packages: [] +__template_services: [] diff --git a/vars/RedHat_8.yml b/vars/RedHat_8.yml new file mode 100644 index 0000000..954bf90 --- /dev/null +++ b/vars/RedHat_8.yml @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +--- +# Put internal variables here with Red Hat Enterprise Linux 8 specific values. + +# Example: +__template_packages: [] +__template_services: [] diff --git a/vars/RedHat_9.yml b/vars/RedHat_9.yml new file mode 100644 index 0000000..b367bff --- /dev/null +++ b/vars/RedHat_9.yml @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: MIT +--- +# Put internal variables here with Red Hat Enterprise Linux 9 specific values. + +# Example: +__template_packages: [] +__template_services: [] diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..24f178c --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: MIT +--- +# Put the role's internal variables here that are not distribution specific. +# You can override these by defining the same variable with a different +# value in a platform/version specific file in vars/ + +# Examples of non-distribution specific (generic) internal variables: +__template_foo_config: foo.conf +__template_packages: [] +__template_services: [] +# ansible_facts required by the role +__template_required_facts: + - distribution + - distribution_major_version + - distribution_version + - os_family +# the subsets of ansible_facts that need to be gathered in case any of the +# facts in required_facts is missing; see the documentation of +# the 'gather_subset' parameter of the 'setup' module +__template_required_facts_subsets: "{{ ['!all', '!min'] + + __template_required_facts }}"