diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..29bf497 --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,25 @@ +# Ansible-lint configuration file +# +# References: +# +# * Configuration - Ansible Lint Documentation +# https://ansible.readthedocs.io/projects/lint/configuring/ +# +# Copyright 2024 林博仁(Buo-ren Lin) +# SPDX-License-Identifier: CC-BY-SA-4.0 + +# Profiles - Ansible Lint Documentation +# https://ansible.readthedocs.io/projects/lint/profiles/ +profile: production + +exclude_paths: + # Implicit unless exclude_paths is defined in config + - .cache/ + + # Don't check external Ansible resources + - playbooks/roles/*/ + - playbooks/collections/*/ + +skip_list: + # FALSE_POSITIVE: The proposed style isn't better than name-rescue-block + - key-order[task] diff --git a/.editorconfig b/.editorconfig index 77da454..d55c5b8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -64,3 +64,6 @@ indent_size = 2 [/LICENSES/*] indent_size = unset indent_style = unset + +[/.ansible-lint] +indent_size = 2 diff --git a/.gitignore b/.gitignore index b26cd36..ffb4b20 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,14 @@ # Don't track continuous integration virtual environments /continuous-integration/venv/ + +# Ignore roles which were acquire externally +/playbooks/roles/* +!/playbooks/roles/README.md + +# Ignore collections which were acquire externally +/playbooks/collections/* +!/playbooks/collections/README.md + +# Do track configuration file of Ansible-lint +!/.ansible-lint diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4bf1d9c..4deda6f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -73,3 +73,8 @@ repos: (?ix)^( LICENSES/.* )$ + + - repo: https://github.com/ansible/ansible-lint + rev: v24.7.0 + hooks: + - id: ansible-lint diff --git a/.yamllint b/.yamllint index 2c9c514..38c217c 100644 --- a/.yamllint +++ b/.yamllint @@ -12,14 +12,14 @@ # This file is based on The Unofficial yamllint Configuration Templates # https://github.com/Lin-Buo-Ren/yamllint-configuration-templates # -# Copyright 2021 林博仁(Buo-ren, Lin) +# Copyright 2024 林博仁(Buo-ren Lin) # SPDX-License-Identifier: CC-BY-SA-4.0 rules: # Use this rule to control the number of spaces inside braces (`{` and `}`). # https://yamllint.readthedocs.io/en/stable/rules.html#module-yamllint.rules.braces braces: min-spaces-inside: 0 - max-spaces-inside: 0 + max-spaces-inside: 1 min-spaces-inside-empty: -1 max-spaces-inside-empty: -1 @@ -123,15 +123,13 @@ rules: # Use this rule to prevent values with octal numbers. In YAML, numbers that start with `0` are interpreted as octal, but this is not always wanted. For instance `010` is the city code of Beijing, and should not be converted to `8`. # https://yamllint.readthedocs.io/en/stable/rules.html#module-yamllint.rules.octal_values octal-values: - forbid-implicit-octal: false - forbid-explicit-octal: false + forbid-implicit-octal: true + forbid-explicit-octal: true # Use this rule to forbid trailing spaces at the end of lines. # https://yamllint.readthedocs.io/en/stable/rules.html#module-yamllint.rules.trailing_spaces trailing-spaces: enable - # Use this rule to forbid non-explictly typed truthy values other than `true` and `false`, for example `YES`, `False` and `off`. - # https://yamllint.readthedocs.io/en/stable/rules.html#module-yamllint.rules.truthy - truthy: - level: warning + # Pre-YAML 1.2 boolean expressions are commonly used in Ansible + truthy: disable ... diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..9baa750 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,26 @@ +# Configuration file for Ansible +# +# References: +# +# * Ansible Configuration Settings — Ansible Documentation +# https://docs.ansible.com/ansible/latest/reference_appendices/config.html +# +# Copyright 2024 The Common Ansible Project Template Contributors +# SPDX-License-Identifier: CC-BY-SA-4.0 + +# General Settings # +[defaults] +# Specify inventory path +inventory = inventory/production + +# Specify in-project roles path to avoid contamination +roles_path = playbooks/roles + +# Specify in-project collections path to avoid contamination +collections_path = playbooks/collections + +# Disable host key checking for convenience of not requiring manually editing known_hosts over security +host_key_checking = False + +# Improve error output presentation +stdout_callback = community.general.yaml diff --git a/continuous-integration/generate-build-artifacts.install-system-deps.sh b/continuous-integration/generate-build-artifacts.install-system-deps.sh index da29dc5..fe22460 100755 --- a/continuous-integration/generate-build-artifacts.install-system-deps.sh +++ b/continuous-integration/generate-build-artifacts.install-system-deps.sh @@ -173,6 +173,9 @@ if ! test -v CI; then fi runtime_dependency_pkgs=( + # For fetching and injecting external Ansible assets + ansible + # project archive compression dependencies #bzip2 gzip diff --git a/continuous-integration/generate-build-artifacts.sh b/continuous-integration/generate-build-artifacts.sh index f9f9c0e..93af69b 100755 --- a/continuous-integration/generate-build-artifacts.sh +++ b/continuous-integration/generate-build-artifacts.sh @@ -7,6 +7,12 @@ set \ -o errexit \ -o nounset +if ! shopt -s nullglob; then + printf \ + 'Unable to set the nullglob shell option.\n' \ + 1>&2 +fi + script="${BASH_SOURCE[0]}" if ! script="$( realpath \ @@ -76,6 +82,7 @@ printf \ 'Info: Generating the project archive...\n' project_id="${CI_PROJECT_NAME:-"${project_id}"}" release_id="${project_id}-${project_version}" +uncompressed_project_archive="${release_id}.tar" git_archive_all_opts=( # Add an additional layer of folder for containing the archive # contents @@ -84,12 +91,100 @@ git_archive_all_opts=( if ! \ git-archive-all \ "${git_archive_all_opts[@]}" \ - "${release_id}.tar.gz"; then + "${uncompressed_project_archive}"; then printf \ 'Error: Unable to generate the project archive.\n' \ 1>&2 exit 2 fi +printf \ + 'Info: Fetching external Ansible assets...\n' +ansible_galaxy_opts=( + # Specify requirements file to fetch external assets from + -r requirements.yml +) +if ! ansible-galaxy install "${ansible_galaxy_opts[@]}"; then + printf \ + 'Error: Unable to fetch external Ansible assets.\n' + exit 2 +fi + +printf \ + 'Info: Injecting external Ansible resources...\n' +for role in playbooks/roles/*/; do + role_dir="${role%/}" + role_name="${role_dir##*/}" + printf \ + 'Info: Injecting the %s role to the release archive...\n' \ + "${role_name}" + tar_opts=( + # Add files to an existing archive + --append + + # Transform member names to fit into release prefix + --transform="flags=rSH;s@^@${release_id}/@x" + + # Show namae transformation results + --show-transformed-names + + # Specify archive to operate on + --file="${uncompressed_project_archive}" + + # Print names appended in the tar archive + --verbose + ) + if ! tar "${tar_opts[@]}" "${role_dir}"; then + printf \ + 'Error: Unable to inject the %s role to the release archive...\n' \ + "${role_name}" + exit 2 + fi +done + +for collection_dir in playbooks/collections/ansible_collections/*/; do + collection_dir="${collection_dir%/}" + printf \ + 'Info: Injecting the %s collection directory to the release archive...\n' \ + "${collection_dir}" + tar_opts=( + # Add files to an existing archive + --append + + # Transform member names to fit into release prefix + --transform="flags=rSH;s@^@${release_id}/@x" + + # Show namae transformation results + --show-transformed-names + + # Specify archive to operate on + --file="${uncompressed_project_archive}" + + # Print names appended in the tar archive + --verbose + ) + if ! tar "${tar_opts[@]}" "${collection_dir}"; then + printf \ + 'Error: Unable to inject the %s collection directory to the release archive...\n' \ + "${collection_dir}" + exit 2 + fi +done + +printf \ + 'Info: Compressing the release archive...\n' +gzip_opts=( + # Overwrite existing files + --force + + --verbose +) +if ! gzip "${gzip_opts[@]}" "${uncompressed_project_archive}"; then + printf \ + 'Error: Unable to compress the release archive.\n' \ + 1>&2 + exit 2 +fi + printf \ 'Info: Operation completed without errors.\n' diff --git a/inventory/README.md b/inventory/README.md new file mode 100644 index 0000000..a81dc95 --- /dev/null +++ b/inventory/README.md @@ -0,0 +1,3 @@ +# inventory + +Info regarding the managed nodes diff --git a/inventory/production/README.md b/inventory/production/README.md new file mode 100644 index 0000000..830dbcb --- /dev/null +++ b/inventory/production/README.md @@ -0,0 +1,3 @@ +# production + +The inventory regarding managed nodes in the production environment diff --git a/inventory/production/group_vars/web_servers b/inventory/production/group_vars/web_servers new file mode 100644 index 0000000..c6ffb19 --- /dev/null +++ b/inventory/production/group_vars/web_servers @@ -0,0 +1,10 @@ +# Group variable file for web_servers +# +# References: +# - Assigning a variable to many machines: group variables — How to build your inventory — Ansible Documentation +# https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#assigning-a-variable-to-many-machines-group-variables +# +# Copyright 2021 The Common Ansible Project Template Contributors +# SPDX-License-Identifier: CC-BY-SA-4.0 +api_server: api.example.com +database_server: db.example.com diff --git a/inventory/production/host_vars/example.com b/inventory/production/host_vars/example.com new file mode 100644 index 0000000..836d355 --- /dev/null +++ b/inventory/production/host_vars/example.com @@ -0,0 +1,7 @@ +# host variable file for example.com +# +# Copyright 2021 The Common Ansible Project Template Contributors +# SPDX-License-Identifier: CC-BY-SA-4.0 +--- +api_server: api.example.com +database_server: db.example.com diff --git a/inventory/production/main.yaml b/inventory/production/main.yaml new file mode 100644 index 0000000..6f585a9 --- /dev/null +++ b/inventory/production/main.yaml @@ -0,0 +1,88 @@ +# Inventory file for the production environment +# +# References: +# - How to build your inventory — Ansible Documentation +# https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html +# +# Copyright 2021 The Common Ansible Project Template Contributors +# SPDX-License-Identifier: CC-BY-SA-4.0 +all: + hosts: + example.com: + ansible_host: 1.2.3.4 + ansible_port: 12345 + ansible_user: root + ansible_password: Passw@rd + + localhost: + ansible_connection: local + + children: + web_servers: + hosts: + web1.example.com: + web2.example.com: + web3.example.com: + + web1.staging.example.com: + web2.staging.example.com: + + test.example.com: + + db_servers: + hosts: + db1.example.com: + db2.example.com: + + db1.staging.example.com: + db2.staging.example.com: + + test.example.com: + + api_servers: + hosts: + api1.example.com: + api2.example.com: + + api1.staging.example.com: + api2.staging.example.com: + + test.example.com: + + rproxy_servers: + hosts: + rproxy1.example.com: + rproxy2.example.com: + + rproxy1.staging.example.com: + rproxy2.staging.example.com: + + production: + hosts: + web1.example.com: + web2.example.com: + web3.example.com: + db1.example.com: + db2.example.com: + api1.example.com: + api2.example.com: + + vars: + api_server: api.example.com + + staging: + hosts: + web1.staging.example.com: + web2.staging.example.com: + db1.staging.example.com: + db2.staging.example.com: + + vars: + api_server: api.staging.example.com + + test: + hosts: + test.example.com: + + vars: + api_server: test.example.com diff --git a/playbooks/README.md b/playbooks/README.md new file mode 100644 index 0000000..40599d9 --- /dev/null +++ b/playbooks/README.md @@ -0,0 +1,14 @@ +# playbooks + +A [playbook](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Playbooks) contains a collection of [plays](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Plays) that can be executed by Ansible as a whole. + +A [play](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Plays) defines a list of [tasks](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Tasks) that needs to be run on the specified [hosts](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Host)/managed nodes. + +## References + +The following materials are referenced during the writing of this README: + +* [Using Ansible playbooks — Ansible Community Documentation](https://docs.ansible.com/ansible/latest/playbook_guide/) + Explains the basic information of Ansible playbooks. +* [Glossary — Ansible Community Documentation](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html) + Explains the usage of each Ansible terms. diff --git a/playbooks/collections/README.md b/playbooks/collections/README.md new file mode 100644 index 0000000..bcab4c9 --- /dev/null +++ b/playbooks/collections/README.md @@ -0,0 +1,12 @@ +# collections + +Ansible collections are a distribution format that can include [playbooks](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Playbooks), [roles](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Roles), [modules](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Modules), and [plugins](https://docs.ansible.com/ansible/latest/plugins/plugins.html). Like [roles](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Roles), collections can also be shared via [Ansible Galaxy]. + +## References + +The following materials are referenced during the writing of this README: + +* [Using Ansible collections — Ansible Community Documentation](https://docs.ansible.com/ansible/latest/collections_guide/index.html) + Explains the basic concepts of Ansible collections. +* [Collection — Glossary — Ansible Community Documentation](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Collection) + Explains the usage of the "collection" Ansible term. diff --git a/playbooks/ping.yml b/playbooks/ping.yml new file mode 100644 index 0000000..3b6667c --- /dev/null +++ b/playbooks/ping.yml @@ -0,0 +1,8 @@ +# Copyright 2024 林博仁(Buo-ren Lin) +# SPDX-License-Identifier: CC-BY-SA-4.0 +- name: Ensure that the managed nodes are accessible + hosts: all + gather_facts: false + tasks: + - name: Ensure that the managed nodes are accessible + ansible.builtin.ping: diff --git a/playbooks/roles/README.md b/playbooks/roles/README.md new file mode 100644 index 0000000..abb3ce1 --- /dev/null +++ b/playbooks/roles/README.md @@ -0,0 +1,16 @@ +# roles + +Define automation that can be reused in multiple [plays](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Plays) and even shared for others to use on [Ansible Galaxy](https://galaxy.ansible.com/). + +A role can define [variables](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Vars-Variables) with defaults that can be overrided by the consumers to customize its behaviors. + +## References + +The following materials are referenced during the writing of this README: + +* [Roles — Ansible Community Documentation](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html) + Explains the basic information of Roles in Ansible. +* [Re-using Ansible artifacts — Ansible Community Documentation](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse.html) + Explains how to reuse Ansible artifacts in general. +* [Roles — Glossary — Ansible Community Documentation](https://docs.ansible.com/ansible/latest/reference_appendices/glossary.html#term-Roles) + Explains the usage of the "role" Ansible term. diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..a1d244e --- /dev/null +++ b/requirements.yml @@ -0,0 +1,28 @@ +# Requirements of this Ansible project +# +# This file defines required roles and/or collections of this Ansible +# project that can be installed using the following command: +# +# ansible-galaxy install -r requirements.yml +# +# References: +# +# * Installing multiple roles from a file — Galaxy User Guide — Ansible +# Community Documentation +# https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#installing-multiple-roles-from-a-file +# * Installing roles and collections from the same requirements.yml file +# — Galaxy User Guide — Ansible Community Documentation +# https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#installing-roles-and-collections-from-the-same-requirements-yml-file +# +# Copyright 2024 林博仁(Buo-ren Lin) +# SPDX-License-Identifier: CC-BY-SA-4.0 +--- +roles: [] + # Install a role from Ansible Galaxy. + #- name: geerlingguy.java + # version: "1.9.6" + +collections: + # Dependency of the community.general.yaml callback plugin + - name: community.general + version: ">=9.0.0"