Pulling Strings With Puppet - James Turnbull [26]
Figure 3-1. Node inheritance
You can see how this sort of structure can allow you to quickly design a deployment model for your nodes that selectively applies the right configuration to the right nodes and is flexible enough to allow you to change the model without having to change the definitions of all your nodes.
Node Inheritance and Variable Scope
Like class inheritance, however, there are issues with node inheritance and variable scoping. Node inheritance is generally only practical for inheriting static classes, for example, those without variables, as you can see in Listing 3-17.
In Listing 3-17, we've defined a class called master with a file resource entitled $server. We've then included this class in the definition of webserver and then created webserver2, which inherits webserver.
In webserver2, we've tried to change the value of the $server variable to "secondary". But, like class inheritance, the value of the $server variable remains defined in the scope of webserver and remains with a value of "primary".
As there is for classes, there is a workaround for this issue. The workaround is somewhat clumsy, as you can see in Listing 3-18.
In Listing 3-18, we've defined a class called master, exactly as we did in Listing 3-15. We've then created another class, called basenode, which includes the master class. We then define two nodes: webserver and webserver2. Each of these nodes includes the basenode class and a different definition of the $server variable. As each node is a separate scope, the variable is applied appropriately in each.
Default Nodes
The last node in Listing 3-15 is the default node. If no node definition is matched, the classes, resources, and definitions in this node are applied to the connecting node. In this case, we're including a class that matches the value of the $operatingsystem fact. For example, if a node connects, the $operatingsystem fact will be evaluated and the result used to include a class. If the fact returned a value of redhat, that class would be included. Lastly, we've included a resource, which ensures that the pert package is installed.
Node Conditionals
Inside nodes we can also use conditionals, as you can see in Listing 3-19.
Listing 3-19 shows a node definition for file server.testing.com. Inside the definition, we've specified a case statement. The query uses the value of the processorcount fact to determine which of a series of classes to include. If the processorcount fact returns 1, the node will include the single class, if 2, the smb class will be included. We've also specified a default value, which includes the single class, if neither 1 nor 2 is returned by the fact.
Note Node, class, and definition names must be unique. For example, if you specify a node and a class with the same name, Puppet will return an error indicating that the class can't be evaluated because the node of the same name has already been evaluated.
Virtual Resources
There is another type of resource that we need to look at: virtual resources. Virtual resources are resources that are defined but are not implemented on a client until you specifically configure them to be.
So why is this useful? Well, as we've discovered in this chapter, resources are normalized and can only be configured once. You can't create the same resource in two classes; for example, you can't configure the user james in both the webserver and dbserver classes. Most of the time this isn't an issue-a resource is generally specific to a particular class. But sometimes you have a resource that is needed in multiple unrelated classes. For example, the user