Overriding resource parameters

Both exported and virtual resources are declared once, and are then collected in different contexts. The syntax is very similar, as are the concepts.

Sometimes, a central definition of a resource cannot be safely realized on all of your nodes, though; for example, consider the set of all your user resources. You will most likely wish to manage the user ID that is assigned to each account in order to make them consistent across your networks.

Tip

This is often solved through LDAP or similar directories, but that is not possible for some sites.

Even if all accounts on almost all machines will be able to use their designated ID, there are likely to be some exceptions. On a few older machines, some IDs are probably being used for other purposes already, which cannot be changed easily. On such machines, creating users with these IDs will fail.

Tip

The accounts can be created if duplicate IDs are allowed, but that is not a solution to this problem—duplicates are usually not desirable.

Fortunately, Puppet has a convenient way to express such exceptions. To give the nonstandard UID, 2066, to the user, felix, realize the resource with an attribute value specification:

User<| title == 'felix' |> {
  uid => '2066'
}

You can pass any property, parameter, or metaparameter that applies to the resource type in question. A value that you specify this way is final and cannot be overridden again.

This language feature is more powerful than the preceding example lets on. This is because the override is not limited to virtual and exported resources. You can override any resource from anywhere in your manifest. This allows for some remarkable constructs and shortcuts.

Consider, for example, the Cacti module that you created during the previous chapter. It declares a package resource in order to make sure that the software is installed. To that end, it specifies ensure => installed. If any user of your module needs Puppet to keep their packages up to date, this is not adequate though. The clean solution for this case is to add some parameters to the module's classes, which allow the user to choose the ensure property value for the package and other resources. However, this is not really practical. Complex modules can manage hundreds of properties, and exposing them all through parameters would form a horribly confusing interface.

The override syntax can provide a simple and elegant workaround here. The manifest that achieves the desired result is very straightforward:

include cacti
Package<| title == 'cacti' |> { ensure => 'latest' }

For all its simplicity, this manifest will be hard to decipher for collaborators who are not familiar with the collector/override syntax. This is not the only problem with overrides. You cannot override the same attribute multiple times. This is actually a good thing, because any rules that resolve such conflicting overrides make it extremely difficult to predict the actual semantics of a manifest that contains multiple overrides of this kind.

Relying on this override syntax too much will make your manifests prone to conflicts. Combining the wrong classes will make the compiler stop creating the catalog. Even if you manage to avoid all conflicts, the manifests will become rather chaotic. It can be difficult to locate all active overrides for a given node. The resulting behavior of any class or define becomes hard to predict.

All things considered, it's safest to use overrides very sparingly.

Note

Please note that collectors are especially dangerous when used without a selector expression:

Package<| |> { before => Exec['send-software-list'] }

Not only will it realize all virtual resources of the given type. It will also force surprising attribute values on both virtual and regular resources of the same type.

Saving redundancy using resource defaults

The final language construct that this chapter introduces can save you quite some typing, or rather, it saves you from copying and pasting. Writing a long, repetitive manifest is not what costs you lots of time, of course. However, a briefer manifest is often more readable, and hence, more maintainable. You achieve this by defining resource defaults—attribute values that are used for resources that don't choose their own:

Mysql_grant { options => ['GRANT'], 
  privileges => ['ALL'], 
  tables     => '*.*', 
}
mysql_grant { 'root': 
  ensure => 'present', 
  user   => 'root@localhost', 
}
mysql_grant { 'apache': 
  ensure => 'present', 
  user   => '[email protected].%', 
  tables => 'application.*', 
}
mysql_grant { 'wordpress': 
  ensure => 'present', 
  user   => '[email protected]', 
  tables => 'wordpress.*', 
}
mysql_grant { 'backup':
  ensure     => 'present',
  user       => 'backup@localhost',
  privileges => [ 'SELECT', 'LOCK TABLE' ],
}

By default, each grant should apply to all databases and comprise all privileges. This allows you to define each actual mysql_grant resource quite sparsely. Otherwise, you will have to specify the privileges property for all resources. The options attribute will be especially repetitive, because they are identical for all grants in this example.

Note that the ensure property is repetitive as well, but it was not included. It is considered good practice to exempt this attribute from resource defaults.

Note

The mysql_grant resource type is not available in core Puppet. It's part of the puppetlabs-mysql module on the Forge.

Despite the convenience that this approach offers, it should not be used at each apparent opportunity. It has some downsides that you should keep in mind:

  • The defaults can be surprising if they apply to resources that are declared at a lexical distance from the defaults' definition (such as several screens further down the manifest file)
  • The defaults transcend the inclusion of classes and instantiation of defines

These two aspects form a dangerous combination. Defaults from a composite class can affect very distant parts of a manifest:

class webserver { 
  File { owner => 'www-data' } 
  include apache, nginx, firewall, logging_client 
  file { 
    ... 
  }
}

Files declared in the webserver class should belong to a default user. However, this default takes effect recursively in the included classes as well. The owner attribute is a property: a resource that defines no value for it just ignores its current state. A value that is specified in the manifest will be enforced by the agent. Often, you do not care about the owner of a managed file:

file { '/etc/motd': content => '...' }

However, because of the default owner attribute, Puppet will now mandate that this file belongs to www-data. To avoid this, you will have to unset the default by overwriting it with undef, which is Puppet's analog to the nil value:

File { owner => undef }

This can also be done in individual resources:

file { '/etc/motd': content => '...', owner => undef }

However, doing this constantly is hardly feasible. The latter option is especially unattractive, because it leads to more complexity in the manifest code instead of simplifying it. After all, not defining a default owner attribute will be the cleaner way here.

Note

The semantics that make defaults take effect in so many manifest areas is known as dynamic scoping. It used to apply to variable values as well and is generally considered harmful. One of the most decisive changes in Puppet 3.0 was the removal of dynamic variable scoping, in fact. Resource defaults still use it, but it is expected that this will change in a future release as well.

Resource defaults should be used with consideration and care. For some properties such as file mode, owner, and group, they should usually be avoided.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.167.87