diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7572f7c..7f82d6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: with: python-version: "3.14" - name: Install python tools - run: pipx install flake8 pyright black pyflakes pytest cfbs cfengine + run: sudo pipx install --global flake8 pyright black pyflakes pytest cfbs cfengine - name: Check the status with cfbs run: cfbs status - name: Validate with cfbs @@ -28,9 +28,13 @@ jobs: 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 tests + run: make check diff --git a/.gitignore b/.gitignore index e5e89d5..8bfd5e5 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 0000000..41bbe68 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +.PHONY: check + +check: +# pytest promise-types/ -v + bash tests/deploy/test.sh diff --git a/cfbs.json b/cfbs.json index 9864d8b..82d8da1 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,24 @@ "bundles windows_capability" ] }, + "install-pipx": { + "description": "Optionally install pipx.", + "subdirectory": "software", + "steps": [ + "copy install-pipx.cf services/cfbs/software/install-pipx.cf", + "policy_files services/cfbs/software/install-pipx.cf", + "bundles install_pipx" + ] + }, + "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 7aae330..6626593 100644 --- a/inventory/inventory-smartctl/policy.cf +++ b/inventory/inventory-smartctl/policy.cf @@ -35,7 +35,8 @@ bundle agent main "_sdir" string => "$(sys.statedir)"; "_cache_ttl" string => "3600"; - # 1 hour + # 1 hour + linux._have_smartctl:: # Enumerate drives - extract first field from each line of smartctl --scan "_scan_lines" slist => splitstring( @@ -52,7 +53,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/i b/software/i new file mode 100755 index 0000000..f386594 --- /dev/null +++ b/software/i @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -ex +command -v ansible && sudo apk del ansible | tee log +sudo apk add pipx +if command -v pipx >/dev/null; then + if [ -x $(command -v pipx) ]; then + sudo pipx uninstall --global ansible || true + sudo pipx uninstall --global ansible-core || true + fi +fi +command -v pipx && sudo apk del pipx | tee -a log +OPTS="--show-evaluated-classes --show-evaluated-vars" +sudo cf-agent -Kvf ./install-ansible.cf -Ddata:install_ansible -Ddata:ansible_minimal_install --bundle install_ansible | tee -a log +#sudo cf-agent -KIf ./linux-install-ansible.cf -Ddata:install_ansible -Ddata:ansible_full_install --bundle linux_install_ansible | tee -a log +command -v pipx || true +command -v ansible || true diff --git a/software/install-ansible.cf b/software/install-ansible.cf new file mode 100644 index 0000000..f4348f9 --- /dev/null +++ b/software/install-ansible.cf @@ -0,0 +1,57 @@ +# 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 +# +# ansible_installed: class, if defined, installed ansible +# ansible_minimal_install: class, if defined, minimal install aka ansible-core +# 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:: + "Installing Ansible on a non-Linux host is not currently supported"; +} + +bundle agent install_ansible_main +{ + classes: + "ansible_installed" + expression => returnszero("command -v ansible >/dev/null", "useshell"); + + vars: + "ansible_version" + string => "${data:install_ansible.version}", + if => isvariable("data:install_ansible.version"); + + "install_ansible_command" + string => "${base_command}==${ansible_version}", + if => isvariable(ansible_version); + + "install_ansible_command" + string => "${base_command}", + if => not(isvariable(ansible_version)); + + data:ansible_minimal_install:: + "base_command" string => "pipx install --global ansible-core"; + + data:ansible_full_install|!data:ansible_minimal_install:: + "base_command" string => "pipx install --global --include-deps ansible"; + + methods: + "install_pipx_main" classes => if_ok("pipx_installed"); + + commands: + pipx_installed.!ansible_installed:: + "${install_ansible_command}" contain => in_shell; + + reports: + ansible_installed:: + "ansible_installed"; +} diff --git a/software/install-pipx.cf b/software/install-pipx.cf new file mode 100644 index 0000000..3b99334 --- /dev/null +++ b/software/install-pipx.cf @@ -0,0 +1,35 @@ +bundle agent install_pipx +{ + methods: + linux.data:install_pipx:: + "install_pipx_main"; + + reports: + windows:: + "Use WSL to install software"; + + !linux:: + "Installing Ansible on a non-Linux host is not currently supported"; +} + +bundle agent install_pipx_main +{ + classes: + "pipx_installed" + expression => returnszero("command -v pipx >/dev/null", "useshell"); + + packages: + "pipx" classes => if_ok("pipx_installed"); + + files: + pipx_installed:: + "/opt/pipx/." + perms => mog("755", "root", "root"), + comment => "/opt/pipx in some cases will be 700 which prevents non-root users from running installed commands."; + + "/opt/pipx/venvs" + perms => mog("755", "root", "root"), + comment => "/opt/pipx/venvs in some cases will be 700 which prevents non-root users from running installed commands."; + + reports: +} diff --git a/tests/deploy/test.sh b/tests/deploy/test.sh new file mode 100755 index 0000000..ebbf6f9 --- /dev/null +++ b/tests/deploy/test.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -ex + +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 + +sudo cf-agent -KI -Ddata:install_ansible -Ddata:ansible_minimal_install --bundle install_ansible > log +if grep -qiP '(err|fail|notkept)' log; then + cat log + exit 1 +fi + +# test for minimal versus full install +ansible --version # will fail test if not available or not executable diff --git a/tests/install-ansible.sh b/tests/install-ansible.sh new file mode 100755 index 0000000..933be03 --- /dev/null +++ b/tests/install-ansible.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -ex +thisdir="$(dirname "$0")" +thispath="$(realpath "$thisdir")" + +# todo, confirm minimal and full installs somehow by checking commands or modules or ??? +sudo cf-agent -Kvf "$thispath"/../software/install-ansible.cf -Ddata:install_ansible -Ddata:ansible_minimal_install --bundle install_ansible | tee "$0.log" +grep -E '(err|fail)' "$0.log" + +command -v pipx +command -v ansible