stanri
stanri

Reputation: 2972

How do I change this case statement to an if statement?

I would like to check for the value of a node attribute. This case statement is what I have so far, and it works:

case node[:languages][:ruby][:host_cpu]
   when "x86_64"
   ...
   when "i686"
   ...
end

What I would like to do is use an if statement instead. This is what I tried:

if node[:languages][:ruby][:host_cpu]?("X86_64")
   ...
end

This is based on the following, Which worked.

if platform?("ubuntu")
    ...
end

However, my try didn't work. it gave a syntax error on the if line saying that there was an unexpected \n and $end was expected.

I found that there are two kinds of ways of performing an if. The first being the one I demonstrated above, which (apparently) only works with resources, and if_only, which works with nodes. like so

if_only {node[:languages]} 

which seems to work only for checking the presence of nodes, and within a do context.

How do I check the value of a node using an if statement? One method does check values, but only of resources, the other checks nodes, but only for their presence, and not their values.

Upvotes: 3

Views: 10906

Answers (1)

Holger Just
Holger Just

Reputation: 55758

You are mixing up way to many different variants for conditionals, most of which are part of Chef, not Ruby. Let me try to describe the different options one by one.

Generally, a case is roughly comparable to a series of if and elsif statements. Your case above

case node[:languages][:ruby][:host_cpu]
   when "x86_64"
   ...
   when "i686"
   ...
end

is thus roughly equivalent to

if node[:languages][:ruby][:host_cpu] == "x86_64"
  ...
elsif node[:languages][:ruby][:host_cpu] == "i686"
  ...
end

As a side remark, case actually uses the === operator which is often not commutative but more powerful. For simple comparisons it works the same as == though. Both these variants are part of the Ruby language, in which you write your cookbooks.

The other options you mentioned are actually part of the API which Chef defined on top of Ruby. This is often called the Chef DSL (which stands for Domain Specific Language, i.e. an extension or adaption of a language, in this case Ruby for a specific usage domain, in this case configuration management.)

The platform? method is a method defined by Chef that checks whether the curent platform is one of the passed values. You can read more about that (and similar methods, e.g. the now recommended platform_family? method at the Chef docs for recipes in general and some often used ruby idioms.

As a side-remark: you might be surprised by the fact that Ruby allows the ? and ! characters to appear at the end of method names, which makes Ruby rather unique among similar languages in this regard. These characters are simply part of the method name and have no special meaning to the language. They are only used by convention to programmers to better identify the purpose of a method. If a method has a ? at the end, it is to be used to check some condition and is expected to return either a truthy or falsy value. Methods with a ! at the end often perform some potentially dangerous operation, e.g. change object in place, delete stuff, ... Again, this is only a convention and is not interpreted by the language.

The last option you mentioned, the only_if and by extension not_if are used to define conditionals on Chef resources to make sure they are only executed when a certain condition is true (or when using not_if, if it is false). As these attributes are used on Chef resources only, they are naturally also defined by Chef.

To understand why they are useful it is necessary to understand how a Chef run works. The details can be found at the description of the Anatomy of a Chef Run. What is important there is, that you basically have two execution phases: Resource Compilation and Convergence. In the first step, the actual code to define the resources is executed. Here, also the code in your case statement would be run. After all the recipes have been loaded and all the resources have been defined, Chef enters the second phase, the Convergence phase. There, the actual implementation of the resources which performs the changes (create files and directories, in stall packages, ...) is run. Only in this phase, the only_if and not_if conditions are checked.

In fact, you can observe the difference between

file "/tmp/helloworld"
  action :create
  content "hello world"
end   

if File.exist?("/tmp/helloworld")
  file "/tmp/foobar"
    action :create
    content "foobar"
  end
end

and

file "/tmp/helloworld"
  action :create
  content "hello world"
end   

file "/tmp/foobar"
  action :create
  content "foobar"
  only_if{ File.exist?("/tmp/helloworld") }
end

In the first variant, the condition whether /tmp/foobar exists is checked during resource compilation. At this time, the code to actually create the /tmp/helloworld file has not been run, as it does that only in the Conversion step. Thus, during your first run, the /tmp/foobar file would not be created.

In the second variant however, the check is done with only_if which is evaluated during conversion. Here you will notice that both files get created in the first run.

If you want to read a bit more on how the definition of the conditionals works in terms of Ruby (and you definitely should), you can read about Ruby Blocks which are more or less pieces of code that can be passed around for later execution.

Upvotes: 8

Related Questions