First, the issue manifests itself only on Debian systems. The runit
runit_service provider will write an
LSB init.d script on
Debian, rather than symlinking to
/usr/bin/sv. The problem raised in
the new ticket is that the template will follow the link and write to
/usr/bin/sv. This is bad, as it will end up in a forkbomb as
runsvdir attempts to restart sv on
all the things.
Oops! Sorry about that. Let’s get it fixed, and practice some TDD.
The runit cookbook includes support for test-kitchen, though I did
for this effort. Part of this change was adding a box for Debian in
.kitchen.yml. I set about resolving this with TDD in mind.
First, the runit cookbook includes a couple
to facilitate setting up the system with the
so the outcome can be tested to ensure the behavior is correct. I
started by adding a “failing test” in the
recipe, meaning a link resource, and a
runit_service resource that
link "/etc/init.d/cook-2867" do to "/usr/bin/sv" end runit_service "cook-2867" do default_logger true end
Then I ran
kitchen test on the Debian box. As expected, the link was
created, and then the runit service was configured. The service’s
provider will wait until the service is up. Since we’ve destroyed the
sv binary, that will never happen, so I destroyed it. I manually
confirmed the behavior too, to make sure I wasn’t seeing something
weird. Due to its very nature, this is really hard to test for
automatically, but it will happen consistently.
Next, I had to write the code to implement the fix for this bug.
Essentially, this means checking if the
is a symbolink link, and removing it.
initfile = ::File.join( '/etc', 'init.d', new_resource.service_name) ::File.unlink(initfile) if ::File.symlink?(initfile)
Simple enough. Next I tested again by destroying the existing environment and rerunning it from scratch. This takes some time, but it verifies that everything is working properly. Here’s the output on Debian:
INFO: Processing link[/etc/init.d/cook-2867] action create (runit_test::service line 147) INFO: link[/etc/init.d/cook-2867] created INFO: Processing service[cook-2867] action nothing (dynamically defined) INFO: Processing runit_service[cook-2867] action enable (runit_test::service line 151) INFO: Processing directory[/etc/sv/cook-2867] action create (dynamically defined) INFO: Processing template[/etc/sv/cook-2867/run] action create (dynamically defined) INFO: Processing directory[/etc/sv/cook-2867/log] action create (dynamically defined) INFO: Processing directory[/etc/sv/cook-2867/log/main] action create (dynamically defined) INFO: Processing directory[/var/log/cook-2867] action create (dynamically defined) INFO: Processing file[/etc/sv/cook-2867/log/run] action create (dynamically defined) INFO: Processing template[/etc/init.d/cook-2867] action create (dynamically defined) INFO: template[/etc/init.d/cook-2867] updated content INFO: template[/etc/init.d/cook-2867] owner changed to 0 INFO: template[/etc/init.d/cook-2867] group changed to 0 INFO: template[/etc/init.d/cook-2867] mode changed to 755 INFO: runit_service[cook-2867] configured INFO: Chef Run complete in 7.267132764 seconds INFO: Running report handlers
I didn’t feel I needed a specific test for this in minitest-chef, because it wouldn’t have finished converging (earlier behavior I saw in the “failing” test).
If you’re contributing to cookbooks, and they have support for test-kitchen, it’s awesome if you can open a bug report with a failing test. In this case, it was fairly easy to reproduce the bug.