in ruby/chef, what is the difference between "end if" and only_if"?

Viewed 59

In chef (therefor, ruby), I've seen two ways of declaring conditionals

resource 'foo' do
  echo "Ubuntu"
end if node['platform'] == 'ubuntu'

and

resource 'foo' do
  echo "Ubuntu"
  only_if node['platform'] == 'ubuntu'
end

Don't these effectively do the same thing? In the official docs, it seems "only_if" is the preferred way, and I can't find many examples of the "end if", but just curious if they, as they seem, do the same thing (execute the block only if the conditional is true).

Thanks!

2 Answers

Chef Resource Guard Clauses vs. Expression Post-Conditions

only_if is a guard clause that's part of the Chef DSL. However, do...end if is a Ruby modifier control expression (sometimes called a post-condition) applied to a block that functions the same way a normal Ruby if/then statement works. Note that even though the if is placed after the expression to be evaluated, the post-condition is still evaluated first.

Think of only_if as a Chef-specific resource statement. The other is just syntactic sugar supported by Ruby's interpreter, and the example you cited (assuming it works in Chef outside a Ruby resource block; I didn't bother to test it) is the same as writing the block inside a more standard if-statement like this one:

if node['platform'] == 'ubuntu'
  resource 'foo' do
    echo "Ubuntu"
  end 
end

Most Chef resources should follow the current style guides and DSL features, but post-conditions are very common in idiomatic Ruby because they emphasize the expression rather than the conditional and because they allow for more brevity of code.

There is a subtle difference in how only_if and end if behave when a node is converged (in Chef speak). In simple terms, when chef-client starts, it compiles the cookbooks and creates a collection of resources that will converge on the node.

For the sake of example, let's say we have a cookbook cookbook1 with only 1 resource in the recipe. When we run such cookbook in below scenarios:

Scenario 1:

Using do .. end if:

The resource is removed from the compilation when the condition is not matched. So there will be no resources to run. Example output from chef-client run when node['platform'] is not ubuntu.

Compiling Cookbooks...
Converging 0 resources

Scenario 2:

Using only_if guard

The resource remains in the collection, but it is skipped when node['platform'] is not ubuntu.

Compiling Cookbooks...
Converging 1 resources
Recipe: cookbook1::default
  * resource[foo] action run (skipped due to only_if)

In short, pure Ruby code, such as if conditions will run during "compile" phase itself. Chef resources run during the "converge" phase. See the Chef Infra Client documentation for details.

Related