From f484f13e703c58242cdcf92ff25d480904797532 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 8 Aug 2025 11:48:01 -0700 Subject: [PATCH 1/4] (gh-35) Add a stop_service parameter to install task This is for integration with openbolt for openvoxproject/openbolt@44. Bolt was providing a stop_service parameter to the puppetlabs-puppet_agent module here: https://github.com/OpenVoxProject/openbolt/blob/b9bff5a8dfe2f41218281bf8c5e6ad901bcae460/lib/bolt/plugin.rb#L133 This was for puppetlabs/bolt#1204, and while the current linux packages, at least, install with service stopped, it's probably still good practice to ensure it for the openbolt case. (This may have been more of an issue when some packages were still SysV?) Since our install task is more general, it could be used for openvox-server/openvoxdb, but there's no particular use case for this, and I haven't bothered with acceptance tests for them. --- .../workflows/pr_testing_with_nested_vms.yml | 61 +++++++++++++++++++ files/common.sh | 40 ++++++++++++ spec/unit/bash/common_sh_spec.rb | 44 +++++++++++++ tasks/install.json | 5 ++ tasks/install_linux.sh | 7 ++- 5 files changed, 156 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr_testing_with_nested_vms.yml b/.github/workflows/pr_testing_with_nested_vms.yml index 193b51a..1089b71 100644 --- a/.github/workflows/pr_testing_with_nested_vms.yml +++ b/.github/workflows/pr_testing_with_nested_vms.yml @@ -70,3 +70,64 @@ jobs: - name: Verify openvox-agent is installed run: |- bolt task run openvox_bootstrap::check version=8 test=gt --inventory inventory.yaml --targets test-agent-1 + + # The rockylinux containers aren't set up for interacting with systemd + # so using nested_vms for this test. + test-install-and-stop: + strategy: + fail-fast: false + matrix: + os: + - [debian, '12'] + - [rocky, '9'] + - [ubuntu, '24.04'] + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - id: install-bolt + uses: ./.github/actions/bolt + with: + os-codename: jammy + - id: vm-cluster + uses: jpartlow/nested_vms@v1 + with: + os: ${{ matrix.os[0] }} + os-version: ${{ matrix.os[1] }} + os-arch: ${{ matrix.os[2] || 'x86_64' }} + image_version: ${{ matrix.os[3] }} + host-root-access: true + ruby-version: '3.3' + install-openvox: false + # Note: the cpu_mode is set to host-model for the sake of + # el-9 which expects at least x86_64-2 arch. This depends on + # the runner's architecture being sufficient, and there is + # probably a better way to get this set on the libvirt + # domain instead. + vms: |- + [ + { + "role": "agent", + "count": 1, + "cpus": 2, + "mem_mb": 4096, + "cpu_mode": "host-model" + } + ] + - name: Capture dereferenced inventory for use with openvox_bootstrap + working-directory: kvm_automation_tooling + run: |- + bolt inventory --inventory terraform/instances/inventory.test.yaml show --format json --detail | \ + jq '.inventory | with_entries(select(.key == "targets")) | del(.targets[0].groups)' | \ + yq -P > ../inventory.yaml + - name: Run openvox_bootstrap::install task on nested vm + run: |- + bolt task run openvox_bootstrap::install --inventory inventory.yaml --targets test-agent-1 stop_service=true + - name: Verify openvox-agent is installed + run: |- + bolt task run openvox_bootstrap::check version=8 test=gt --inventory inventory.yaml --targets test-agent-1 + - name: Verify service state + run: |- + set -e + bolt command run '/opt/puppetlabs/bin/puppet resource service openvox-agent' --inventory inventory.yaml --targets test-agent-1 > service.state + grep -E "ensure [ ]*=> [ ]*'stopped'" service.state + grep -E "enable [ ]*=> [ ]*'false'" service.state diff --git a/files/common.sh b/files/common.sh index 358c1ae..8aa9976 100644 --- a/files/common.sh +++ b/files/common.sh @@ -510,3 +510,43 @@ set_artifacts_package_url() { export package_url # quiets shellcheck SC2034 assigned 'package_url' } + +# Stop and disable the service for the given package. +# +# Only intended to work for openvox-agent, openvoxdb and +# openvox-server. +# +# Will fail if openvox isn't installed. +# +# (voxpupuli/puppet-openvox_bootstrap#35) +# Implemented for integration with openbolt. +stop_and_disable_service() { + local _package="$1" + # Using the full path here because openvox is installed into opt, + # and if we've just installed, the shell's PATH will not include it + # yet. + local _puppet="${2:-/opt/puppetlabs/bin/puppet}" + + case "${_package}" in + openvox-agent) + local _service='puppet' + ;; + openvoxdb) + local _service='puppetdb' + ;; + openvox-server) + local _service='puppetserver' + ;; + *) + fail "Cannot stop service. Unknown service for package: '${_package}'" + ;; + esac + + info "Stopping and disabling service '${_service}' for package '${_package}'" + + if [ -x "${_puppet}" ]; then + exec_and_capture "${_puppet}" resource service "${_service}" ensure=stopped enable=false + else + fail "Puppet executable not found at '${_puppet}'. Cannot stop and disable service '${_service}'." + fi +} diff --git a/spec/unit/bash/common_sh_spec.rb b/spec/unit/bash/common_sh_spec.rb index 8e24e74..7257c6f 100644 --- a/spec/unit/bash/common_sh_spec.rb +++ b/spec/unit/bash/common_sh_spec.rb @@ -577,4 +577,48 @@ end end end + + context 'stop_and_disable_service' do + it 'fails for an unknown package' do + output, status = test('stop_and_disable_service unknown-package') + + expect(status.success?).to be(false) + expect(output.strip).to include("Unknown service for package: 'unknown-package'") + end + + it 'fails if puppet executable not found' do + output, status = test('stop_and_disable_service openvox-agent /no/openvox') + + expect(status.success?).to be(false) + expect(output.strip).to include("Puppet executable not found at '/no/openvox'") + end + + context 'with puppet executable' do + let(:mock_puppet) { "#{tmpdir}/puppet" } + + before do + # The little BashRspec lib isn't sophisticated enough + # to deal with an absolute path, so using this instead of + # allow_script.to receive_command(mock_puppet)... + File.write(mock_puppet, <<~EOF) + #!/bin/sh + echo "Stopping ${3} service" + EOF + File.chmod(0o755, mock_puppet) + end + + [ + %w[openvox-agent puppet], + %w[openvox-server puppetserver], + %w[openvoxdb puppetdb], + ].each do |package, service| + it "stops the #{service} service for #{package}" do + output, status = test("stop_and_disable_service #{package} #{mock_puppet}") + + expect(status.success?).to be(true) + expect(output.strip).to include(service) + end + end + end + end end diff --git a/tasks/install.json b/tasks/install.json index 7752851..88644f3 100644 --- a/tasks/install.json +++ b/tasks/install.json @@ -24,6 +24,11 @@ "description": "The yum source repository to retrieve rpm packages from.", "type": "Optional[String]", "default": "https://yum.voxpupuli.org" + }, + "stop_service": { + "description": "Whether to stop the given service after install. (Requires puppet on the system.)", + "type": "Optional[Boolean]", + "default": false } }, "implementations": [ diff --git a/tasks/install_linux.sh b/tasks/install_linux.sh index 43b4b88..fbfc930 100755 --- a/tasks/install_linux.sh +++ b/tasks/install_linux.sh @@ -9,6 +9,7 @@ version=${PT_version:-latest} collection=${PT_collection:-openvox8} yum_source=${PT_yum_source:-https://yum.voxpupuli.org} apt_source=${PT_apt_source:-https://apt.voxpupuli.org} +stop_service=${PT_stop_service:-'false'} # shellcheck source=files/common.sh source "${PT__installdir}/openvox_bootstrap/files/common.sh" @@ -75,5 +76,9 @@ download "${package_url}" "${local_release_package}" # The release package has the repository metadata needed to install # packages from the collection using the platform package manager. install_release_package "${local_release_package}" -# Use the platform package manager to install openvox-agent +# Use the platform package manager to install $package install_package "${package}" "${version}" "${os_family}" "${os_full_version}" +# If a service stop is requested, stop the service now +if [[ "${stop_service}" = 'true' ]]; then + stop_and_disable_service "${package}" +fi From 28c1f7b90097b46554a3885e953a1a71c84c9f13 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 8 Aug 2025 11:55:48 -0700 Subject: [PATCH 2/4] (gh-35) Update REFERENCE for stop_service parameter --- REFERENCE.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/REFERENCE.md b/REFERENCE.md index ac0c572..9c2dd98 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -151,6 +151,12 @@ Data type: `Optional[String]` The yum source repository to retrieve rpm packages from. +##### `stop_service` + +Data type: `Optional[Boolean]` + +Whether to stop the given service after install. (Requires puppet on the system.) + ### `install_build_artifact` Downloads and installs a package directly from the openvox build artifact server. From da6828521c0d28ab9d33747f3b3865bf10700993 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 8 Aug 2025 12:28:26 -0700 Subject: [PATCH 3/4] (maint) Clear up some of the step labelling in pr_testing.yaml --- .github/workflows/pr_testing.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr_testing.yaml b/.github/workflows/pr_testing.yaml index 1364f0a..5b8222f 100644 --- a/.github/workflows/pr_testing.yaml +++ b/.github/workflows/pr_testing.yaml @@ -41,7 +41,7 @@ jobs: uses: ./.github/actions/bolt with: os-codename: ${{ matrix.os-details.codename }} - - name: Run openvox-agent install task + - name: Run install task for openvox-agent run: bolt task run openvox_bootstrap::install --targets localhost --run-as root - name: Verify openvox-agent is installed run: bolt task run openvox_bootstrap::check --targets localhost --run-as root @@ -71,7 +71,7 @@ jobs: path: openvox_bootstrap - id: prep uses: ./openvox_bootstrap/.github/actions/container_task_prep - - name: Run openvox-agent install task manually + - name: Run install task manually for openvox-agent env: PT_collection: openvox8 run: ./openvox_bootstrap/tasks/install_linux.sh @@ -104,7 +104,7 @@ jobs: path: openvox_bootstrap - id: prep uses: ./openvox_bootstrap/.github/actions/container_task_prep - - name: Run openvox-agent install task manually + - name: Run install task manually for openvox-agent env: PT_version: "8.14.0" run: ./openvox_bootstrap/tasks/install_linux.sh @@ -137,7 +137,7 @@ jobs: path: openvox_bootstrap - id: prep uses: ./openvox_bootstrap/.github/actions/container_task_prep - - name: Run openvox-agent install task manually + - name: Run install task manually for openvox-server env: PT_package: "openvox-server" run: ./openvox_bootstrap/tasks/install_linux.sh @@ -168,7 +168,7 @@ jobs: path: openvox_bootstrap - id: prep uses: ./openvox_bootstrap/.github/actions/container_task_prep - - name: Run openvox-agent install task manually + - name: Run install task manually for openvox-server env: PT_package: "openvox-server" PT_version: "8.8.0" From 6c07d342640752de3e673046389cef3704d98ed0 Mon Sep 17 00:00:00 2001 From: Josh Partlow Date: Fri, 8 Aug 2025 14:23:56 -0700 Subject: [PATCH 4/4] (gh-35) Make install.json optional parameters consistent Strictly speaking, given that the parameters have defaults, it should not be necessary to type them 'Optional' in install.json. But the install task is used as the puppet_library plugin for installing openvox via apply_prep, and something about how Bolt constructs the task there causes a failure for non optional parameters, even if they have defaults: jpartlow@archimedes:~/work/src/kvm_automation_tooling$ cat plans/test.pp plan kvm_automation_tooling::test() { apply_prep('b-alma9-ov8-agent-1') } jpartlow@archimedes:~/work/src/kvm_automation_tooling$ be bolt plan run kvm_automation_tooling::test --inventory terraform/instances/inventory.b-alma9-ov8.yaml Starting: plan kvm_automation_tooling::test Starting: install puppet and gather facts on b-alma9-ov8-agent-1 Finished: plan kvm_automation_tooling::test in 0.27 sec Failed on b-alma9-ov8-agent-1: Error executing plugin task from puppet_library: Task openvox_bootstrap::install: expects a value for parameter 'package' Failed on 1 target: b-alma9-ov8-agent-1 Ran on 1 target Some parameters will be wired into openbolt's DEFAULT_PLUGIN_HOOKS (https://github.com/OpenVoxProject/openbolt/blob/b9bff5a8dfe2f41218281bf8c5e6ad901bcae460/lib/bolt/plugin.rb#L133) and stop_service is already one of them. (See openvoxproject/openbolt#49) But to keep everything consistent, I'm setting all the parameters optional with the defaults explicit so that it's at least clear to a caller running 'bolt task show openvox_bootstrap::install' what must be provided and what the defaults are. --- REFERENCE.md | 4 ++-- tasks/install.json | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/REFERENCE.md b/REFERENCE.md index 9c2dd98..0855361 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -123,7 +123,7 @@ Installs an openvox package. By default, this will be the latest openvox-agent f ##### `package` -Data type: `String[1]` +Data type: `Optional[String]` The name of the package to install. @@ -131,7 +131,7 @@ The name of the package to install. Data type: `Optional[String]` -The version of the openvox-agent package to install. Defaults to latest. +The version of the openvox-agent package to install. ##### `collection` diff --git a/tasks/install.json b/tasks/install.json index 88644f3..1fa9226 100644 --- a/tasks/install.json +++ b/tasks/install.json @@ -3,12 +3,13 @@ "parameters": { "package": { "description": "The name of the package to install.", - "type": "String[1]", + "type": "Optional[String]", "default": "openvox-agent" }, "version": { - "description": "The version of the openvox-agent package to install. Defaults to latest.", - "type": "Optional[String]" + "description": "The version of the openvox-agent package to install.", + "type": "Optional[String]", + "default": "latest" }, "collection": { "description": "The openvox collection to install from.",