1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
|
---
inMenu: true
---
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 <name>(<param1>,<param2>,...) {...}
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"
}
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.
# Server Classes
class <class_name> [inherits <super_class_name>] { ... }
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
}
## 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 <hostname> { ... }
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
}
# 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$*
|