Sony Koithara
Sony Koithara

Reputation: 121

How do I check if a folder exists in Chef?

This is my code:

if !::File.exist?("#{node['iis']['home']}\\backup\\BkpB4Chef")
 windows_batch "Backup IIS Config" do
  code <<-EOH
   "#{node['iis']['home']}"\\appcmd add backup BkpB4Chef
  EOH
 end
end

It always says file exists and executes the loop.

Upvotes: 12

Views: 52518

Answers (2)

sethvargo
sethvargo

Reputation: 26997

You should use Chef guards here. Guards specify conditional execution, but still insert the resource into the resource collection. In your example and jtblin answer, the resource is never added to the collection (which I'll explain a bit further in a moment).

Here's some working code to get you started:

windows_batch "Backup IIS Config" do
  code %Q|#{node['iis']['home']}"\\appcmd add backup BkpB4Chef|
  not_if { ::File.directory?("#{node['iis']['home']}\\backup\\BkpB4Chef") }
end

Using creates

Many non-idempotent Chef resources also support a creates parameter, which explains what the resource does. In other words, what does the windows_batch "create". This can be a file, directory, or executable. So, the following code is equivalent to the former answer.

windows_batch "Backup IIS Config" do
  code %Q|#{node['iis']['home']}"\\appcmd add backup BkpB4Chef|
  creates"#{node['iis']['home']}\\backup\\BkpB4Chef"
end

Why not_if vs the conditional wrapper

Chef executes in two phases - the compilation phase and the convergence phase. During the compilation phase, the recipes are evaled and the resources are added to the resource collection. In the convergence phase, the resources in the resource collection are executed and evaluated against the target system. So, consider the following example:

if false
  service 'foo' do
    action :start
  end
end

This is a fairly straight-forward recipe that starts a service based on some conditional. However, at the end of the compilation phase, the service resource isn't added to the resource collection. Since the recipe DSL is instance_evaled, the wrapping if false conditional prevents that code from ever being read by the Ruby VM. In other words, it's like that service never exists.

It's fairly common to notify resources. Later in the recipe, you might want to restart apache because of a configuration change. The "proper" way to do this is using notifications:

template '/var/www/conf.d/my.conf.file' do
  # ...
  notifies :restart, 'service[apache2]'
end

This template cannot adequately notify the service resource, because it doesn't exist in the resource collection. So this recipe will fail. It seems like a trivial example, but if you change the conditional if false to a node attribute test:

if node['cookbook']['use_apache']
  service 'apache2' do
    action :start
  end
end

you have created a dichotomy in your cookbook where it will work 50% of the time. Unfortunately most cookbooks are much more complex than two resources, so the number of edge cases where a resource can notify a non-existent resource drastically increases with complexity. This is all solvable (and exhibits the correct behavior) using resource guards:

service 'apache2' do
  action :start
  only_if { node['cookbook']['use_apache'] }
end

Upvotes: 29

WispyCloud
WispyCloud

Reputation: 4230

Use Dir.exists?. You can also replace if ! condition by unless condition which reads a bit better.

unless Dir.exist? "#{node['iis']['home']}\\backup\\BkpB4Chef"
 windows_batch "Backup IIS Config" do
  code <<-EOH
   "#{node['iis']['home']}"\\appcmd add backup BkpB4Chef
  EOH
 end
end

Upvotes: 6

Related Questions