case statements

Like if statements, case statements choose one of several blocks of arbitrary Puppet code to execute. They take a control expression and a list of cases and code blocks, and execute the first block whose case value matches the control expression.

Behavior

Puppet compares the control expression to each of the cases, in the order they are listed (except for the top-most level default case, which always goes last). It executes the block of code associated with the first matching case, and ignores the remainder of the statement.Case statements execute a maximum of one code block. If none of the cases match, Puppet does nothing and moves on.

In addition to executing the code in a block, a case statement is also an expression that produces a value, and can be used wherever a value is allowed. The value of a case expression is the value of the last expression in the executed block. If no block was executed, the value is undef.

The control expression of a case statement can be any expression that resolves to a value. This includes:

Syntax

The general form of a case statement is:

  • The case keyword.

  • A control expression, which is any expression resolving to a value.

  • An opening curly brace.

  • Any number of possible matches, which consist of:

    • A case or comma-separated list of cases.

    • A colon.

    • A pair of curly braces containing any arbitrary Puppet code.

    • A closing curly brace case.

case $facts['os']['name'] {
  'RedHat', 'CentOS':  { include role::redhat  } # Apply the redhat class
  /^(Debian|Ubuntu)$/: { include role::debian  } # Apply the debian class
  default:             { include role::generic } # Apply the generic class
}

Case matching

A case can be any expression that resolves to a value, for example, literal values, variables and function calls. You can use a comma-separated list of cases to associate multiple cases with the same block of code. To use values from a variable as cases, use the * splat operator to convert an array of values into a comma-separated list of values.

Depending on the data type of a case's value, Puppet uses one of following behaviors to test whether the case matches:

  • Most data types, for example, strings and Booleans, are compared to the control value with the == equality operator, which is case-insensitive when comparing strings.

  • Regular expressions are compared to the control value with the =~ matching operator, which is case-sensitive. Regex cases only match strings.

  • Data types, such as Integer, are compared to the control value with the =~ matching operator. This tests whether the control value is an instance of that data type.

  • Arrays are recursively compared to the control value. First, Puppet checks whether the control and array are the same length, then each corresponding element is compared using these same case matching rules.

  • Hashes compare each key-value pair. To match, the control value and the case must have the same keys, and each corresponding value is compared using these same case matching rules.

  • The special value default matches anything, and unless nested inside an array or hash, is always tested last regardless of its position in the list.

Regex capture variables

If you use regular expression cases, any captures from parentheses in the pattern are available inside the associated code block as numbered variables (for example, $1, $2), and the entire match is available as $0:

case $trusted['hostname'] {
  /www(\d+)/: { notice("Welcome to web server number ${1}"); include role::web }
  default:   { include role::generic }
}

This example captures any digits from a hostname such as www01 and www02 and store them in the $1 variable.

Regex capture variables are different from other variables in a couple of ways:

  • The values of the numbered variables do not persist outside the code block associated with the pattern that set them.

  • In nested conditionals, each conditional has its own set of values for the set of numbered variables. At the end of an interior statement, the numbered variables are reset to their previous values for the remainder of the outside statement. This causes conditional statements to act like local scopes, but only with regard to the numbered variables.

Best practices

Case statements must have a default case:

  • If the rest of your cases are meant to be comprehensive, putting a fail('message') call in the default case makes your code more robust by protecting against mysterious failures due to behavior changes elsewhere in your manifests.

  • If your cases aren't comprehensive and you want nodes that match none to do nothing, write a default case with an empty code block (default: {}). This makes your intention obvious to the next person who maintains your code.