diff --git a/documentation/documentation/structures.page b/documentation/documentation/structures.page index 8b3ba8cbd..6ee99607f 100644 --- a/documentation/documentation/structures.page +++ b/documentation/documentation/structures.page @@ -1,357 +1,364 @@ --- inMenu: true orderInfo: 10 --- This is a brief overview of the language structures available for making site configurations in Puppet. For futher documentation, visit the [Puppet homepage](/projects/puppet/).

Types

The basic unit of configuration in Puppet are ``types``. Types model objects on the computer being managed, and each builtin type has attributes that determine the final type configuration: file { "/etc/passwd": owner => root, mode => 644 } package { apache: install => true } Puppet also provides facilities for defining new types as collections of existing types (see Components below), but there is no syntactic difference between using builtin types like ``file`` and ``package`` and using defined types. Any operation or syntax that succeeds for builtin types should also work for defined types. See the [Type Reference](typedocs.html) for the documentation for the Puppet Library's primitive types.

Assignment

$variable = value Variables available in the current scope are referenced by preceding them with the ``$`` character. Once assigned, variables cannot be reassigned. However, within a sub-scope a new assignment can be made for a variable name for that sub-scope and any further sub-scopes created within it: $x = foo $y = bar $z = "$x$y" # Bringing Config files together import "filename" Starts the parsing of the file specified and creates any specified definitions and classes at the current scope. Currently files are only searched for within the same directory as the file doing the importing. Files can also be imported using globbing, as implemented by Ruby's ``Dir.glob`` method: import "classes/*" import "packages/[a-z]*" # Scope Generally speaking, any language structure that involves curly braces creates a new scope inside those braces. This currently includes server and class definitions and if/then/else structures. Each file should also introduce its own scope but currently does not. Once assigned, variables cannot be reassigned within the same scope. However, within a sub-scope a new assignment can be made for a variable name for that sub-scope and any further scopes created within it: $var = value # override $var define testing { $var = othervalue } Service and class definitions are scoped just as variable assignments are. Functions defined and Classes created within a scope will not be available outside the scope in which they are created: define testing { file { "/etc/passwd": owner => root } } class osx { # override the existing testing definition define testing { file { "/etc/other": owner => root } } } The evaluation by Puppet of following example would be result in the copying of ``/file_repository/test-httpd.conf`` to ``/etc/httpd/conf/httpd.conf``: $filename = "/etc/apache/httpd.conf" class webserver { $filename = "/etc/httpd/conf/httpd.conf" define httpd_service (config_file) { file { $filename : source => $config_file } } httpd_service { "test_httpd" : config_file => "/file_repository/test-httpd.conf" } } webserver {} # Components define (,,...) {...} Definition of fuctions allows the composition of lower level types into higher level types. Parameters of defined functions can be referenced within the definition scope, similarly to variables, by preceding their names with the ``$`` character: define svnserve($source, $path, $user = false, $password = false) { file { $path: create => directory, owner => root, group => root } $svncmd = $user ? { false => "/usr/bin/svn co --non-interactive $source/$name .", default => "/usr/bin/svn co --non-interactive --username $user --password '$password' $source/$name ." } exec { $svncmd: cwd => $path, require => file[$path], creates => "$path/.svn" } } svnserve { dist: source => "https://reductivelabs.com/svn", path => "/dist", user => "puppet", password => "password" } svnserve { "dist/config/apps/puppet": source => "https://reductivelabs.com/svn", path => "/etc/puppet", user => "puppet", password => "password" } As of 0.19.0, arguments in definition prototypes must have '$' attached to them. Note that calling components results in a unique instance of all contained objects. In the above case, each of the calls to ``svnserver`` results in an ``exec`` and a ``file`` instance. So, it's important that all of your components are written that they support this. A good rule of thumb is that you should only include statements in your components that have variables in their names. If a statement doesn't have a variable in the name, then you are likely to result in a situation where multiple components will try to manage the same instance, which will result in an error at run time. ## $name Within a component, the name used in the component is available via the ``$name`` variable. This is very similar to the concept of ``self`` in many OO languages, but it's just a simple text string, not a reference to an object or something. # Server Classes class [inherits ] { ... } Class definitions allow the specification of a hierarchy of server classes; a host that is a member of a subclass will apply the configuration from the subclass and all parent classes. The primary difference between classes and components is that classes are singletons -- there will only ever be a single instance of a given class on a given server. Thus, if you have a server which is a member of three different classes, each of which share the same parent class, then you will get one instance of the parent class and one instance of each of the subclasses. # really simple example class solaris { file { "/etc/passwd": owner => root, group => root, mode => 644; "/etc/shadow": owner => root, group => root, mode => 440 } } class solworkstation inherits solaris { file { "/etc/sudoers": owner => root, group => root, mode => 440; "/bin/sudo": owner => root, group => root, mode => 4111 } } include solworkstation Because ``include`` is a function, any normal value can be used, including variables and selectors: include $operatingsystem, $hostname ? { myhost => classA, default => classB } ## Classes vs. Components Classes and components are defined similarly (although classes currently do not accept parameters), but they are meant to be used very differently. Components are used to define reusable objects which will have multiple instances on a given host, so they cannot include any elements that will only have one instance, such as a package or a root-level service. Classes, on the other hand, are guaranteed to be singletons -- you can include them as many times as you want and you'll only ever get one copy of the elements -- so they are exactly meant to include these singleton objects. Most often, services will be defined in a class, where the service's package, configuration files, and running service will all be defined in the class, because there will normally be one copy of each on a given host. Components would be used to manage elements like virtual hosts, of which you can have many, or to encode some simple information in a reusable wrapper to save typing. Every installation I've done of Puppet so far has a ``remotefile`` component that encodes where Puppet clients should get their configuration files from: define remotefile(source, owner = root, group = root, recurse = true) { file { $name: source => "http://puppet.$domain/dist/$source", owner => $owner, group => $group, recurse => $recurse } } ## Subclassing The primary benefit of using subclasses instead of just including the parent class is that the subclass can override elements in the parent class: class unix { file { "/etc/sudoers": owner => root, group => root, mode => 440 } } class bsd inherits unix { file { "/etc/sudoers": group => wheel } } Including the ``unix`` class sets the group to ``root``, but including the ``bsd`` class overrides the vale to ``wheel``. ## Using Classes Outside of Puppet This isn't really a "language" thing, but it seemed the best place to document this. All classes set on a Puppet client are stored in an external file (usually ``/etc/puppet/classes.txt``, but can be modified with the ``classfile`` argument or setting). This means other tools can easily read in the classes that Puppet sets and use them for their own logic. There is also (as of 0.15.4) a new command to set arbitrary classes that do not have any code associated with them: class freebsd { tag unix, bsd } class redhat { tag unix, sysv } These classes will then be written to the classes.txt file like all others, even though there is no code associated with them. The syntax is just like ``include``, so you can use variables, also: tag $operatingsystem

Nodes

node { ... } Node definitions specify the configuration to apply to a specific node. By default they are looked for by ``puppetmasterd`` but not by ``puppet``. See the documentation for each to enable or disable them. Any code outside of a node definition will be applied to all nodes, while any code inside will only apply to the specified node or nodes: class webserver { ... } class dbserver { ... } file { "/etc/sudoers": mode => 440 } # apply to everyone node host1, host2 { include webserver } node host3, host4 { include dbserver } Nodes can also inherit from other nodes, so it's easy to apply defaults: node base { include $operatingsystem } node kirby inherits base { include webserver } +You can specify fully-qualified node names, but you have to single-quote the +names: + + node 'host.domain.com' { + ... + } + # Conditionals Puppet currently supports two types of conditionals: in-statement and around statements. We call the in-statement conditionals ``selectors``, as they are essentially a select-style operator, which support the use of ``default`` to specify a default value: define testing(os) { owner = $os ? { sunos => adm, redhat => bin, default => root } file { "/some/file": owner => $owner } } The ``case`` statement provides the ability to conditionally apply types: case $operatingsystem { sunos: { solaris {} } # apply the solaris class redhat: { redhat {} } # apply the redhat class default: { generic {} } # apply the generic class } # Reserved words Generally, any word that the syntax uses for special meaning is probably also a reserved word, meaning you cannot use it for variable or type names. Thus, words like ``true``, ``define``, ``inherits``, and ``class`` are all reserved. # Comments Puppet supports sh-style comments; they can either be on their own line or at the end of a line (see the Conditionals example above). *$Id$*