A Puppet control repository for managing an OpenVox development environment with a Puppet master on CentOS Stream 10 and agents on CentOS Stream 9 and Ubuntu 24.04.
# Start the Vagrant environment
vagrant up
# SSH to nodes
vagrant ssh puppet # Puppet master
vagrant ssh agent01 # CentOS agent
vagrant ssh agent02 # Ubuntu agent
# Run Puppet on an agent
vagrant ssh agent01 -c "sudo /opt/puppetlabs/bin/puppet agent -t"| VM | Hostname | IP | OS |
|---|---|---|---|
| puppet | puppet.example.com | 192.168.56.10 | CentOS Stream 10 |
| agent01 | agent01.example.com | 192.168.56.11 | CentOS Stream 9 |
| agent02 | agent02.example.com | 192.168.56.12 | Ubuntu 24.04 |
See the Wiki for detailed documentation:
site-modules/
role/ # Node classification (one role per node)
profile/ # Technology-specific configurations
adhoc/ # Bolt tasks
modules/ # External Forge modules (via Puppetfile)
data/ # Hiera data
plans/ # Bolt plans
Three layers, each catching something the others can't. All three run in CI on every pull request. The tools themselves are documented upstream in the OpenVox ecosystem docs.
| Layer | Tool | Scope | Run from |
|---|---|---|---|
| Unit | rspec-puppet (via voxpupuli-test) |
Each profile class in isolation, across OS fact sets | site-modules/profile/ |
| Compilation | Onceover | Every role, against real node facts and this repo's Hiera data | repo root |
| Acceptance | Beaker (via voxpupuli-acceptance) |
A real apply on a real node, checked for idempotency | site-modules/profile/ |
Ruby is pinned to the version in .ruby-version (3.2.8, matching Puppet Enterprise); CI reads the same file. Note that the two bundles are separate: Gemfile at the root is for Onceover, and site-modules/profile/Gemfile is for rspec-puppet and Beaker.
rspec-puppet is module-scoped by design; it has no notion of a control repo.
The way around that is to stop treating site-modules/profile as "a directory
in a control repo" and start treating it as a module that happens to live in
a control repo. It gets its own
metadata.json,
Gemfile,
Rakefile,
.fixtures.yml and spec/ directory —
after which the standard Vox Pupuli module toolchain works unmodified, with no
module_path surgery in spec_helper.rb.
cd site-modules/profile
bundle install
bundle exec rake validate lint check # syntax, puppet-lint, rubocop
bundle exec rake parallel_spec # all unit tests
bundle exec rake spec SPEC=spec/classes/openvox_agent_spec.rb # just oneLint rules are the voxpupuli-test gem defaults — there is deliberately no
.puppet-lint.rc or .rubocop.yml here.
The trade-off is that dependencies are declared twice: fixtures come from
.fixtures.yml, not the control repo's Puppetfile, so adding a
module dependency to a profile means updating .fixtures.yml, metadata.json
and the Puppetfile. (The other common layout — a single top-level spec/
with c.module_path pointed at site-modules, or parsed out of
environment.conf — avoids the duplication but gives up the
per-module Rakefile, the on_supported_os matrix from metadata.json, and the
ability to reuse Vox Pupuli's shared GitHub workflows.)
Multi-OS coverage comes from rspec-puppet-facts: on_supported_os generates
fact sets from the operatingsystem_support entries in metadata.json, so a
platform is only covered once it's listed there and facterdb ships facts for
it.
If you'd rather not install Ruby at all, the voxbox container runs the same tasks (its lint rules can drift from the CI bundle, which is authoritative):
docker run --rm -v $PWD:/repo ghcr.io/voxpupuli/voxbox:8 specUnit tests prove a profile compiles in isolation with facts you supplied by
hand. They say nothing about whether role::puppet_master composes cleanly, or
whether the Hiera data in data/ actually resolves. Onceover covers
exactly that gap: it compiles every role against representative node
factsets, exercising the full role -> profile -> module chain with this
repo's own Hiera.
# From the repo root — uses the root Gemfile, not site-modules/profile
bundle install
bundle exec onceover run spec --auto_vendored
bundle exec onceover show repo # print the parsed test matrixConfiguration lives in spec/:
- spec/onceover.yaml — the
test_matrix: which roles compile on which nodes.role::puppet_masterruns only onpuppet.example.com; theprofile::base-only roles run on both agents to catch RedHat/Debian regressions. - spec/factsets/ — real facts captured from the Vagrant
VMs, not synthetic ones. Named after each certname so
nodes/%{trusted.certname}.yamlresolves correctly. See spec/factsets/README.md to regenerate. - spec/hiera.yaml — an Onceover-only Hiera config that drops the eyaml backend, so CI needs no PKCS7 keys. The production root hiera.yaml is untouched.
One wrinkle worth knowing about: core types like yumrepo and cron come from
modules that ship vendored with the OpenVox agent (yumrepo_core,
cron_core, ...). On real nodes they're on the $basemodulepath, so they are
deliberately absent from the Puppetfile. Onceover runs against a gem-installed
Puppet that lacks them, so --auto_vendored resolves them from the agent's
component manifests and injects them into Onceover's temporary Puppetfile
only. The committed cache in spec/vendored_modules/
lets that work without a GitHub API call at run time; regenerate it with
bundle exec rake generate_vendor_cache when the Puppet version changes.
Compilation is not application. Beaker provisions a real node, installs OpenVox, applies a manifest, and re-applies it to prove idempotency.
cd site-modules/profile
BUNDLE_WITHOUT=development:release bundle install
BEAKER_HYPERVISOR=docker \
BEAKER_PUPPET_COLLECTION=openvox8 \
BEAKER_SETFILE=almalinux9-64 \
bundle exec rake beakerTests live in site-modules/profile/spec/acceptance/,
and the whole harness is a require 'voxpupuli/acceptance/spec_helper_acceptance'
followed by configure_beaker — node definitions come from
voxpupuli-acceptance's built-in setfiles, selected by BEAKER_SETFILE. Set
BEAKER_destroy=no to keep the container around for debugging.
The layers above are all disposable. For end-to-end verification against the
real master/agent topology, use the Vagrant environment — the
repo is synced to /etc/puppetlabs/code/environments/production on the master,
so changes are live immediately:
vagrant ssh agent01 -c "sudo /opt/puppetlabs/bin/puppet agent -t"
./scripts/bolt-validate.sh # Bolt validation plan, from the host- OpenVox Ecosystem Documentation — the tooling around OpenVox, including the test stack used here
- OpenVox Project
- Puppet Documentation
- Voxbox Container