diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7572f7cb..23d75c23 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,19 +18,9 @@ jobs: uses: actions/setup-python@v5 with: python-version: "3.14" - - name: Install python tools - run: pipx install flake8 pyright black pyflakes pytest cfbs cfengine - - name: Check the status with cfbs - run: cfbs status - - name: Validate with cfbs - run: cfbs validate - - name: Check the formatting - run: cfbs --check pretty ./cfbs.json - - name: Linting python code - run: ./ci/linting.sh - - name: Run promise type tests - run: pytest promise-types/ -v - - name: Lint policy with CFEngine CLI - run: cfengine lint --strict no ./ - - name: Check formatting with CFEngine CLI - run: cfengine format --check + - name: Install CFEngine community for tests + run: | + pipx install cf-remote + cf-remote install --clients localhost --edition community + - name: Run make check for linting, formatting and tests + run: make check diff --git a/.gitignore b/.gitignore index e5e89d59..8bfd5e5e 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ node_modules/ build/ dist/ .cache +tests/deploy/cfbs.json +tests/deploy/out diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..69f1f719 --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +.PHONY: check lint format all clean tools + +all: tools clean lint format check + +tools: + sudo pipx install --global flake8 pyright black pyflakes pytest cfbs cfengine + echo "deploy test requires cf-agent installed..." + command -v cf-agent + +clean: + rm -rf tests/deploy/out + +lint: clean tools + cfbs status + cfbs validate + cfbs --check pretty ./cfbs.json + ./ci/linting.sh + cfengine lint --strict no ./ + +format: lint + cfengine format --check + +check: format + pytest promise-types/ -v + bash tests/deploy/test.sh diff --git a/cfbs.json b/cfbs.json index 9864d8b5..5f263a43 100644 --- a/cfbs.json +++ b/cfbs.json @@ -144,7 +144,7 @@ }, "delete-home-dotshosts": { "description": "Ensure that ~/.shosts files are not present, as they present a security risk.", - "subdirectory": "security/delete-home-dotshots", + "subdirectory": "security/delete-home-dotshosts", "steps": [ "copy policy/main.cf services/cfbs/delete-home-dotshots/", "policy_files services/cfbs/delete-home-dotshots/", @@ -497,10 +497,10 @@ "description": "Allows you to specify a list of packages you want uninstalled on your hosts.", "subdirectory": "security/uninstall-packages", "steps": [ - "copy uninstall-packages.cf services/cfbs/modules/uninstall-packages/uninstall-packages.cf", + "copy main.cf services/cfbs/modules/uninstall-packages/main.cf", "input uninstall-packages/input.json def.json", "bundles uninstall_packages:uninstall_packages", - "policy_files services/cfbs/modules/uninstall-packages/uninstall-packages.cf" + "policy_files services/cfbs/modules/uninstall-packages/main.cf" ], "input": [ { @@ -546,6 +546,15 @@ "bundles windows_capability" ] }, + "install-ansible": { + "description": "Optionally install ansible.", + "subdirectory": "software", + "steps": [ + "copy install-ansible.cf services/cfbs/software/install-ansible.cf", + "policy_files services/cfbs/software/install-ansible.cf", + "bundles install_ansible" + ] + }, "windows-openssh-server": { "description": "Optionally install Windows OpenSSH Server.", "subdirectory": "software/windows", diff --git a/inventory/inventory-smartctl/policy.cf b/inventory/inventory-smartctl/policy.cf index 7aae3302..2358b111 100644 --- a/inventory/inventory-smartctl/policy.cf +++ b/inventory/inventory-smartctl/policy.cf @@ -33,9 +33,12 @@ bundle agent main ); "_sdir" string => "$(sys.statedir)"; - "_cache_ttl" string => "3600"; - # 1 hour + "_cache_ttl" + string => "3600", + comment => "3600 seconds is 1 hour"; + + linux._have_smartctl:: # Enumerate drives - extract first field from each line of smartctl --scan "_scan_lines" slist => splitstring( @@ -52,7 +55,6 @@ bundle agent main "_cache[${_drives}]" string => "$(_sdir)/inventory_smartctl_${_id[${_drives}]}.json"; - linux._have_smartctl:: # Rolled-up status: OK or DEGRADED (SMARTCTL_MISSING when smartctl absent) "smartctl_status" string => "DEGRADED", diff --git a/software/install-ansible.cf b/software/install-ansible.cf new file mode 100644 index 00000000..f270da3b --- /dev/null +++ b/software/install-ansible.cf @@ -0,0 +1,58 @@ +# Steps here based on this guide: https://docs.ansible.com/projects/ansible/latest/installation_guide/intro_installation.html +# +# host specific data supported, in the data bundle context +# +# data:install_ansible: class, if defined, install ansible +# data:ansible_minimal_install: class, if defined, minimal install aka ansible-core +# data:ansible_full_install: class, if defined, full install. This is the default if neither minimal or full classes are defined. +bundle agent install_ansible +{ + methods: + linux.data:install_ansible:: + "install_ansible_main"; + + reports: + windows:: + "Use WSL to install Ansible on Windows. See https://blog.rolpdog.com/2020/03/why-no-ansible-controller-for-windows.html"; + + !linux.!windows:: + "Installing Ansible on a non-Linux/non-Windows host is not supported"; +} + +bundle agent install_ansible_main +{ + classes: + !windows:: + "ansible_installed" + expression => returnszero("command -v ansible >/dev/null", "useshell"); + + vars: + "install_ansible_command" + string => "${base_command}==${data:install_ansible.version}", + if => isvariable("data:install_ansible.version"); + + "install_ansible_command" + string => "${base_command}", + if => not(isvariable("data:install_ansible.version")); + + data:ansible_minimal_install:: + "base_command" string => "pipx install --global ansible-core"; + + data:ansible_full_install|(!data:ansible_full_install.!data:ansible_minimal_install):: + "base_command" string => "pipx install --global --include-deps ansible"; + + packages: + "pipx" classes => if_ok("pipx_installed"); + + commands: + pipx_installed.!ansible_installed:: + "${install_ansible_command}" + contain => in_shell_umask(022), + comment => "The default umask of 077 would create /opt/pipx/venvs/ansible which is inaccessible to everyone but root, so use 022 instead."; +} + +body contain in_shell_umask(umask) +{ + useshell => "true"; + umask => "${umask}"; +} diff --git a/tests/deploy/00-promise-run-no-errors.sh b/tests/deploy/00-promise-run-no-errors.sh new file mode 100755 index 00000000..53291da5 --- /dev/null +++ b/tests/deploy/00-promise-run-no-errors.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# assumes deploy.sh has already run adjacent to this file + +set -ex +sudo cf-agent -KI > log +if grep 'error:' log; then + grep 'error:' log + exit 1 +fi diff --git a/tests/deploy/01-install-ansible-full-test.sh b/tests/deploy/01-install-ansible-full-test.sh new file mode 100755 index 00000000..29acb7b5 --- /dev/null +++ b/tests/deploy/01-install-ansible-full-test.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# assumes deploy.sh has already run adjacent to this file + +set -ex + +sudo pipx uninstall --global ansible || true +sudo pipx uninstall --global ansible-core || true + +sudo cf-agent -Kd -Ddata:install_ansible -Ddata:ansible_full_install --bundle install_ansible > log +if grep 'error:' log; then + grep 'error:' log + exit 1 +fi + +ansible --version +echo "expect that ansible-core is not installed via pipx list" +if ( sudo pipx list --global --short | grep ansible-core ); then + echo "expected only ansible to be installed, but found ansible-core" + exit 1 +fi diff --git a/tests/deploy/02-install-ansible-minimal-test.sh b/tests/deploy/02-install-ansible-minimal-test.sh new file mode 100644 index 00000000..1386973d --- /dev/null +++ b/tests/deploy/02-install-ansible-minimal-test.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -ex + +sudo pipx uninstall --global ansible || true +sudo pipx uninstall --global ansible-core || true + +sudo cf-agent -Kd -Ddata:install_ansible -Ddata:ansible_minimal_install --bundle install_ansible > log +if grep 'error:' log; then + grep 'error:' log + exit 1 +fi + +ansible --version +if ! ( sudo pipx list --global --short | grep ansible-core ); then + echo "expected ansible-core to be installed but did not find that in pipx list output" +fi diff --git a/tests/deploy/03-install-ansible-versions-test.sh b/tests/deploy/03-install-ansible-versions-test.sh new file mode 100644 index 00000000..c777563b --- /dev/null +++ b/tests/deploy/03-install-ansible-versions-test.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -ex + +sudo pipx uninstall --global ansible || true +sudo pipx uninstall --global ansible-core || true + +function cleanup +{ + sudo rm -f /var/cfengine/data/host_specific.json || true # to make sure any version data is removed for other tests other runs +} + +trap cleanup EXIT + +good_version="2.21.3" + +sudo mkdir -p /var/cfengine/data +cat < log + +if grep 'error:' log; then + grep 'error:' log + exit 1 +fi + +ansible --version | grep "${good_version}" + +sudo pipx uninstall --global ansible || true +sudo pipx uninstall --global ansible-core || true + +bad_version="x.y.z" +cat < log + +if grep 'error:' log; then + echo "Expected error found due to bad version value. Test passes." + grep 'error:' log + exit 0 +fi + +echo "Expected errors when installing ansible with bad version" +exit 1 diff --git a/tests/deploy/04-install-ansible-default-test.sh b/tests/deploy/04-install-ansible-default-test.sh new file mode 100755 index 00000000..9ecd442e --- /dev/null +++ b/tests/deploy/04-install-ansible-default-test.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# assumes deploy.sh has already run adjacent to this file + +set -ex + +sudo pipx uninstall --global ansible || true +sudo pipx uninstall --global ansible-core || true + +sudo cf-agent -Kd -Ddata:install_ansible --bundle install_ansible > log +if grep 'error:' log; then + grep 'error:' log + exit 1 +fi + +ansible --version +echo "expect that ansible(full) is installed and not ansible-core" +if ( sudo pipx list --global --short | grep ansible-core ); then + echo "expected ansible(full) to be installed, but found ansible-core" + exit 1 +fi diff --git a/tests/deploy/deploy.sh b/tests/deploy/deploy.sh new file mode 100755 index 00000000..9744b2b3 --- /dev/null +++ b/tests/deploy/deploy.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set -ex + +echo "WARNING/TODO: This test, $0, requires all changes to be pushed up to a branch in order to test the latest. This could be improved if cfbs add could work with the modules repo and a cfbs.json file. See ENT-14421" +if [ -n "$GITHUB_HEAD_REF" ]; then + # github case + BRANCH="$GITHUB_HEAD_REF" +else + # local case + BRANCH=$(git rev-parse --abbrev-ref HEAD) +fi + +# workaround, if git@ url then we get Error: Cannot specify more than one version of the same module +# so transform the remote url +REPO=$(git remote get-url origin | \ + sed -e 's,git@,https://,' \ + -e 's,com:,com/,' \ +) + +if [ -n "$GITHUB_HEAD_REF" ]; then + REPO="$(echo "$REPO" | sed -e "s,cfengine,$GITHUB_TRIGGERING_ACTOR,")" +fi + +thisdir="$(dirname "$0")" +cd "$thisdir" +[ -d .git ] && rm -rf .git +[ -f cfbs.json ] && rm cfbs.json +cfbs --version +cfbs init --non-interactive +cfbs --non-interactive add "$REPO@$BRANCH" +cfbs build +sudo cfbs install +sudo cf-agent -IB 127.0.0.1 > log + +if grep 'error:' log; then + grep 'error:' log + exit 1 +fi + diff --git a/tests/deploy/test.sh b/tests/deploy/test.sh new file mode 100755 index 00000000..3c8d9c4e --- /dev/null +++ b/tests/deploy/test.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -ex +thisdir="$(dirname "$0")" +bash "$thisdir"/deploy.sh +if ls "$thisdir"/0*.sh >/dev/null; then + for test in "$thisdir"/0*.sh; do + bash -ex "$test" + done +fi