summaryrefslogtreecommitdiffstats
path: root/documentation/languagetutorial.page
blob: 413176b5504324f6590fefe8044738460f4c1b58 (plain)
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
---
inMenu: true
title: Language Tutorial
---

# Elements and Organization

The entire purpose of the Puppet language is to result in Puppet elements that
directly model elements on each client being managed.  Because different
clients have different lists of elements, and some clients vary in the
configuration of specific elements, Puppet's language supports various
abstraction, selection, and overriding capabilities.

# Naming

For simple elements, we just specify the element type, name, and parameters:

    file { "/etc/passwd":
        owner => root,
        group => root,
        mode => 644
    }

Any machine on which this snippet is executed will use it to verify that the
``passwd`` file is configured as specified.  The Puppet language treats the
field before the colon as the name of the object, so in this case the name is
``/etc/passwd``, which works fine for this file since both the path and the
details are essentially always the same on all systems.

For files that vary across systems, it makes sense to create a *symbolic name*
for your element:

    file { sshdconfig:
        path => $operatingsystem ? {
            solaris => "/usr/local/etc/ssh/sshd_config",
            default => "/etc/ssh/sshd_config"
        },
        owner => root,
        group => root,
        mode => 644
    }

Here we create a symbolic name instead of using the path as the name, and now
we can refer to that element by name elsewhere in the manifest:

    service { sshd:
        subscribe => file[sshdconfig]
    }

This will cause the ``sshd`` service to get restarted when the ``sshdconfig``
file changes.

It's important to note here that the language doesn't actually know anything
about the objects it is managing; it knows the name and the type, and it has a
list of parameters, but those parameters mean nothing in the language.  If you
provide a symbolic name to a given element, you should always use that single
name, because otherwise the language will assume you are managing separate
elements, which might result in an error in the library:

    file { sshdconfig:
        path => "/usr/local/etc/ssh/sshd_config",
        owner => root
    }

    file { "/usr/local/etc/ssh/sshd_config":
        owner => sshd
    }

The Puppet parser does not know that these element specifications will attempt
to manage the same element, because they have different names.  This will
result in an error on the client when the second element tries to instantiate
and it is found to conflict with the first element.

# Assignment

Puppet has a declarative language, which means that its scoping and assignment
rules are somewhat different than a normal imperative language.  The primary
difference is that you cannot change the value of a variable within a single
scope, because that would rely on file order to determine the value of the
variable.  This will result in an error:

    $user = root
    file { "/etc/passwd":
        owner => $user
    }
    $user = bin
    file { "/bin":
        owner => $user,
        recurse => true
    }

You will almost always find that you can avoid resetting variable values using
the builtin [conditionals](structures.html#conditionals):

    $group = $operatingsystem ? {
        solaris => sysadmin,
        default => wheel
    }

This is only one assignment, so no errors.

# Overriding

Puppet provides a separate scope for every class and component, and scope
trees are created as a manifest is parsed.  Take the following configuration:

    class base {
        file { "/etc/issue":
            source => "puppet://server/module/common/issue"
        }
    }

    class sub inherits base {
        file { "/etc/issue":
            source => "puppet://server/module/mysite/issue"
        }
    }

This creates two distinct scopes, one for each class.  The ``sub`` class
overrides the specification of ``/etc/issue``, because it's in a lower scope,
and when this manifest is applied, the latter location is used as the source
for the file.  This allows you to subclass a class and slightly change its
behaviour.

# Defaults

Puppet elements are normally specified using a lower-case element type.  You
can specify default values for a given type using the capitalized form of the
element type, though:

    Exec { path => "/usr/bin:/bin:/usr/sbin:/sbin" }

    import "classes/*.pp"

    node mynode {
        include $operatingsystem
    }

The first statement in this snippet provides a default value for 'exec'
elements, which require either fully qualified paths or a path in which to
look for the executable.  This way you can specify a single default path for
your entire configuration, and then override that value as necessary.

This can be used for any element type.  For instance, CentOS defaults to using
``rpm`` for its package type, but you can easily change that to ``yum``:

    case $operatingsystem {
        centos: {
            Package { type => yum }
        }
    }

    ...
*$Id$*