Acceptance tests run Puppet in apply mode inside a Docker container managed by PDQTest.
The Docker container breaks all the rules of Docker and runs a full version of Systemd to allow complete testing of Puppet code in the most portable way to basically treat docker as a high speed VM... This is deliberate - PDQTest exists to get results, not be a perfect Docker app.
There are three supported docker images:
- Centos 7
- Ubuntu 16.04
- Windows (microsoft/windowsservercore)
Centos is used by default, all other images are activated by the
operatingsystem_support
element of metadata.json
:
Windows
"operatingsystem_support": [
{
"operatingsystem": "windows"
}
],
Ubuntu
"operatingsystem_support": [
{
"operatingsystem": "Ubuntu"
},
],
Notes:
- Centos is great for mocking systems like AIX... if you replace OS binaries as
required in the
__setup.sh
scripts - If you have a specific container you want to use, pass
--image-name
on the command line, it accepts a comma delimited list of image names to test on - The container will only be started if at least one example file is present and contains the correct magic marker
- You can get a shell on the docker container if needed, see Debugging failed builds
- See the docker_images folder for examples
- Scan for all example files under
/examples
. Files must end in.pp
and contain the magic marker#@PDQTest
or#@PDQTestWin
to be processed - If example files are present, start the docker container
- For each example file:
- Check for a file at
/spec/acceptance/EXAMPLE_NAME__setup.(sh|ps1)
, If it exists, run it inside the container. This is a useful place to install preqrequisites, edit files, install mock services, replace OS binaries, clean up after other tests, etc. - Check for a file at
/spec/acceptance/EXAMPLE_NAME__before.(bats|pats)
, If it exists run BATS or PATS against it. This is normally used to check the system state is clean or that any test prerequisites have been setup correctly (most of the time you can skip this) - Run
puppet apply
on the example file twice (to check idempotency) - Check for a file at
/spec/acceptance/EXAMPLE_NAME.(bats|pats)
, If it exists run BATS/PATS against it. This is normally used to check the state of the system after running puppet. You can do things like check services are running, check files edited correctly, or basically anything that you can write in bash (or PowerShell)!
- Destroy the container unless we were asked to keep it with
--keep-container
Note: EXAMPLE_NAME
is the example filename minus the directory and .pp
, eg
the EXAMPLE_NAME
for examples/foo.pp
would just be foo
.
- Found inside
/examples
- Regular puppet code
- Will be executed twice (to check for idempotency) with
puppet apply
- MUST contain the magic marker
#@PDQTest
or#@PDQTestWin
on a line at the top of the file to indicate that this test should be processed by PDQTest - Basically the same as a regular puppet smoke test but run automatically and with system state tests
If you've never seen a BATS test before and have previously been writing server spec code, you'll probably kick yourself when you see how simple they are.
BATS lets you run tests anywhere you can run BASH.
Here's an example:
# Tests are really easy! just the exit status of running a command...
@test "addition using bc" {
result="$(ls /)"
[ "$?" -eq 0 ]
}
Tests are all written in BASH and the testcase passes if the stanza returns zero. This means that basically any Linux/Unix sysadmin is now empowered to write Testcases and do TDD - score!
Since our tests are written in BASH, there are of course all the usual bash quirks such as failing on incorrect spacing, bizzare variable quoting etc, but given the simplicity gained its a worthwhile tradeoff.
Consider the following (real) test:
@test "ClientAliveCountMax" {
grep "ClientAliveCountMax 1" /etc/ssh/sshd_config
}
Here, we have done exactly what it looks like we have done - checked for the
value of a particular setting in a text file. Since our tests our plain old
BASH, it means we can do things like test daemons are running with systemctl
,
test ports are open with netstat
and do complete system tests wget
and
curl
. Cool!
PATS is to testing with PowerShell
as BATS is to testing with BASH. PATS is currently experimental and lets you
write your tests using the same syntax as BATS but with the body of the tests
being executed with PowerShell. As always, the exit status of your PowerShell
fragment is the result of the test. Here's an example that tests the timezone
has been set as desired. In this case we have shelled out to run cmd.exe
but
you could just run pure PowerShell if you like:
@test "sets the timezone correctly" {
cmd /C "tzutil /g | findstr /C:`"New Zealand Standard Time`" "
}
PATS is used for testing on Windows.
At present you would need to run pdqtest
twice - once in a Windows environment
and again in Linux.
This is because Docker doesn't let us run Windows and Linux on the same platform at the same time (Because it's not magic... Actually newer Docker versions do support this on Windows by running Docker in muliple VMs...).
This probably isn't as big an issue as it looks since most projects are going to cater to one specific OS family.
Lets say you have two examples, foo.pp
and init.pp
. The files for acceptance
testing would look like this:
mycoolmodule
├── examples
│ ├── foo.pp
│ └── init.pp
├── ...
└── spec
├── acceptance
│ ├── foo__before.bats
│ ├── foo__setup.sh
│ ├── foo.bats
│ ├── init__before.bats
│ ├── init__setup.sh
│ └── init.bats
├── ...
When we run acceptance tests PDQTest will create a new container and then run:
sh /testcase/spec/acceptance/foo_setup.sh
bats /testcase/spec/acceptance/foo__before.bats
puppet apply /testcase/examples/foo.pp
puppet apply /testcase/examples/foo.pp
bats /testcase/spec/acceptance/foo.bats
sh /testcase/spec/acceptance/init_setup.sh
bats /testcase/spec/acceptance/init__before.bats
puppet apply /testcase/examples/init.pp
puppet apply /testcase/examples/init.pp
bats /testcase/spec/acceptance/init.bats
Errors at any stage of the process will cause the test suite to abort once the error is returned.
PDQTest provides two targets to debug failed builds:
make shell
/.\make.ps1 shell
- Load a new docker container, run all tests, then print a message showing how to get a shellmake shellnopuppet
/.\make.ps1 shellnopuppet
- Load a new docker container and transport into it, without running Puppet
You may also want to run PDQTest directly to use a different image (Ubuntu)
se --image-name
to use a different image (Ubuntu)
cd .pdqtest
bundle exec pdqtest --image-name declarativesystems/pdqtest-ubuntu:2018-08-29-0 shell
Inside the container:
- Your code is available at
/testcase
- Any puppet modules you have installed at
/spec/fixtures/modules
will be available (note thatpdk build
removes these) - Hiera and custom facts will be installed if configured
User is responsible for cleaning up the containers created in this mode
Sometimes you need to run in Docker privileged mode to enable tests to work -
not ideal and if anyone else has a better idea please open a ticket with the
details. When privileged mode is required, run pdqtest --privileged
and you
will have access.
WARNING: This grants the container access to the host