1

I would like to use chefspec to test the idempotency of my recipe.

Let's say I have a recipe that includes these two resource statements:

file '/etc/app.config' do
  action :create
  notifies :restart, "service[app]"
end

service 'app' do
  action :enable
end

How can I write a chefspec example that proves, given the file /etc/app.config already exists, then the app service will not be notified to restart?

Is there some mocking I can do so the 'file' resource thinks the file already exists? Can I run the ChefSpec::ServerRunner twice, keeping the state from the first run (I assume not, since the file won't have really been created)? Or will I be forced to use test-kitchen and Vagrant to make things happen for realz?

(Note: My actual cookbook has a custom LWRP that builds the configuration file. It gets info from a chef-server, which is why I'm using ServerRunner)

Kief
  • 4,383
  • 4
  • 26
  • 21
  • 1
    Given that ChefSpec never attempts to converge your resources, I'm fairly certain that you cannot test for idempotency with ChefSpec. It is basically there to make sure you declare the proper resources with the proper actions and attributes, but doesn't really test that those resources work properly. But I'm hoping someone can prove me wrong on that. – Tejay Cardon May 01 '15 at 13:30
  • @TejayCardon, you're right, we're not going to be able to test idempotency at the level of a full converge (with chefspec). I was at least hoping to be able to test different starting conditions for a single resource, though. Unfortunately it doesn't look feasible off the shelf. I'll have to test it with test-kitchen, I think. – Kief May 05 '15 at 10:24

3 Answers3

1

test-kitchen since version 1.18.0 has working support for idempotence check for chef_* provisioners (it was introduced earlier but than broken and only fixed in latest release). It's not documented but you can find some details here.

Basically, what you can do is add this two lines to your provisioner definition:

  provisioner:
    name: chef_zero
    ...
    enforce_idempotency: true # force idempotence check
    multiple_converge: 2 # how many times to run your provisioners
    ...

This will run your cookbook multiple times and check that the last run has 0 updated resources. It also gives you a nice list of resources that were updated on a second run.

Artem Yarmoliuk
  • 306
  • 1
  • 5
0

Warning This is untested.

I assume you can mock ::File.exist? to return true in your spec for this file with.

allow_any_instance_of(File).to receive('exists?').with('/etc/app.config').and_return(true)

According to the file provider this is the method called in the provider to get the current state.

This would work cleanly for a file resource like the one you described in your question.

However if the action is :create and the resource is defined with a checksum attribute or a content attribute you'll have to manage to mock the content of the file or the checksum like this

allow_any_instance_of(Chef::Digester).to receive('generate_checksum').with('/etc/app.config').and_return('The checksum of the file, do a sha256sum on your file to get this')

After that you'll have to follow ChefSpec documentation on notifications here

resource = chef_run.file('/etc/app.config')
expect(resource).to_not notify('service[app]').to(:restart)

As far as I know, Test-Kitchen aimed to test a one-shot run to ensure the cookbooks are correct, multiples chef-run in the same test are not handled and you end up having to decide what you wish to use for multiples run test.

There's a talk about this on the chef-mailing list about infrastructure testing, see the tag wiki for links to the ML (the talk is not a final answer and does not fit on SO).

Tensibai
  • 15,557
  • 1
  • 37
  • 57
  • Poke @Tejaycardon .You're right, there's no convergence, it's a matter of mocking the state to test a behavior, but that will probably be error prone with time. – Tensibai May 04 '15 at 08:23
  • mocking the File.exist? check doesn't work. The file provider doesn't actually call this when run with Chefspec, which makes sense given that without having converged, the file won't have been created, so would fail. – Kief May 05 '15 at 10:21
  • @Kief The idea is to provide a fake state where the file is present and contain the correct content (so the run should not update the resource). You're right the provider is totaly obfuscated, you'll have to write around [this matcher](https://github.com/sethvargo/chefspec/blob/ab7a07d60d6df446a3c2063b8825ea8c7bce51b6/lib/chefspec/matchers/render_file_matcher.rb#L107) – Tensibai May 05 '15 at 12:58
0

It looks like different starting conditions can only be practically tested by actually converging. If so, the correct answer to my question is to use something like test-kitchen and Vagrant, rather than ChefSpec.

Kief
  • 4,383
  • 4
  • 26
  • 21