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/).
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.
$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
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$*