--- inMenu: true --- # Introduction Puppet is a system configuration tool. It has a library for managing the system, a language for specifying the configuration you want, and a set of clients and servers for communicating the configuration and other information. The library is entirely responsible for all action, and the language is entirely responsible for expressing configuration choices. Everything is developed so that the language operations can take place centrally on a single server (or bank of servers), and all library operations will take place on each individual client. Thus, there is a clear demarcation between language operations and library operations, as this document will mention. # Setup The vast majority of Puppet architectures will look like a star, with a central server running `puppetmasterd`, and each client node running `puppetd`, contacting that central server. Your central manifest, which contains the configuration for all of your nodes, needs to be on the central server, most likely at `/etc/puppet/manifests/site.pp`. Start the `puppetmasterd` daemon, and then tell your clients to contact that server by specifying `-s ` as arguments to `puppetd`, replacing "" with the name of the server. Alternatively, `puppetd` defaults to looking for a server named "puppet", so you can just create a CNAME for your server, so that it answers to "puppet". It is a good idea to run both the server and client in verbose mode, enabled with the `-v` flag, until you are sure everything is working. As each new client connects, you will need to run `puppetca --list` to list the certificates waiting to be signed, and then `puppetca --sign `, replacing "" with the name of the client whose certificate you want to sign. You can turn on autosigning by creating `/etc/puppet/autosign.conf` and put the hosts, domains, or IP addresses or ranges that you want to sign in there. # Language The language is declarative, and is modeled somewhat after cfengine's concept of actions, action instances, and instance parameters. Here is how a file element would look in puppet: file { "/etc/passwd": owner => root, group => root, mode => 644 } Each instance of a low-level element like ``file`` must have a name parameter defined, because that is how you uniquely refer to the element you are managing. Any string that doesn't match ``\w+`` or ``\d+`` needs to be quoted (e.g., file names). There are two ways for types to be available in the language: They'll either be ``primitive types`` included in the Puppet framework itself, or they'll be ``defined types`` resulting from definitions or server classes. ## Definitions Because experience with cfengine showed that objects of different types are often related, puppet focuses on making creation of associations between objects of any type easier than making many objects of the same type. The fundamental unit of association is a 'definition': # currently, the variables don't have '$' attached in the definition prototype # this might change define sudo(source,group) { package { sudo: version => "1.6.7" } file { "/etc/sudoers": source => $source, # parameterization; copy the file from where? mode => 440 # the 'file' type converts this appropriately to # octal } file { "/usr/sbin/sudo": owner => root, group => $group } } A definition has its own scope, so variables defined within it are only available within the definition itself. This definition can now be treated as a type: sudo { group => root, source => "http://fileserver/files/sudoers?v=production" } One significant difference between definitions and types is that types generally have many associated parameters, most of which are optional, while a given definition will (at this point) always require all parameters. You can manage as little or as much of a file as you want, for instance, but once you wrap it in a definition you have to pass every specified parameter to instances of the definition. It is appropriate to think of definitions as declarative functions; referring to the name of a definition results in the associated declarative code being executed with the provided parameters. I will likely soon add the ability to provide defaults to definition parameters; any other feature requests are welcome. ## Classes In addition to definitions, the language supports server classes. Classes are different from definitions in two fundamental ways: They do not (currently) support parameterization, and sub classes can override base classes. Classes can be called using the same syntax as definitions, although always without arguments and usually without names, but they are generally called with the ``include`` keyword, which is more flexible in that it supports variable interpolation and conditional expressions: class base { include sudo } class server inherits base { include ssh } case $hostname { myhost: { include server } default: { include base } } include $operatingsystem ## Variables Because it is assumed that strings will be used far more than variables, simple strings don't have be quoted or otherwise marked, but variables must have the ``$`` attached: $group = "root" file { "/etc/sudoers": group => $group } Strings and booleans (``true`` and ``false``) are the only data types; even numbers are converted to strings. Arrays are supported, although their behaviour has not been characterized for all cases. One particular use of arrays is for implicit iteration: $files = ["/etc/passwd","/etc/group","/etc/fstab"] file { $files: owner => root, group => root } This implicitly iterates across the file list and performs all of the appropriate checks. Currently, ``puppet`` collects as much information as it can (using the ``Facter`` library) and sets all of it as top-level variables. So, you can expect variables like ``$operatingsystem`` and ``$ipaddress``. Also, because ``puppet`` is a declarative language, reassigning a variable within the same scope is currently an error. The language is written such that this could be disabled (please let me know if you need this feature), but it is currently always enabled. You can override defaults in a lower scope: $var = default define test { $var = override ... } It is expected that parameter passing to definitions will be the primary mechanism for variable assignment, so it seems unlikely that this limitation will cause much trouble. ## Importing Files can be imported using the ``import`` command: import "filename" There is currently no search path or anything; files are looked for in the same directory as the file doing the importing. Each file should have its own lexical scope, but I haven't yet figured out how to do that. ## Control Structures There are currently two basic control structures, one meant to return a value and the other just a normal 'if/elsif/else' structure. ### Selectors One of the primary goals of Puppet is to simplify building a single configuration that works across multiple machines and machine classes. One mechanism for this is currently called a 'selector'; it is similar to the trinary operator ``:?``: $value = $variable ? { value1 => setvalue1, value2 => setvalue2, default => other } This sets the variable ``$value`` depending on the value of ``$variable``. If it is ``value1``, then ``$value`` gets set to ``setvalue1``, else the value gets set to ``other``. The brackets can be in either part of the expression, or not at all: $value = $variable ? "value1" => "setvalue1" A selector that doesn't match a value is a compile error. These structures are useful for simplistic abstraction across platforms: file { "/etc/sudoers": owner => root, group => $operatingsystem ? { SunOS => root, Linux => root, FreeBSD => wheel } } Selectors can specify default values using the ``default`` keyword. ### Case Statements Puppet currently supports a normal Case structure similar to so many other languages: case $operatingsystem { solaris: { include sunos } debian: { include linux } default: { include $operatingsystem } } As you can see, case statements also support defaults. The difference between the case stateements and the selectors is that the selectors just return values while the case statements execute arbitrary Puppet code. # Library This section discusses some aspects of the internals of the Puppet library. This information can be useful but is not critical for use and understanding of Puppet. The library is composed of two fundamental types of objects: Types and states (yes, the terminology is atrocious). States are things that can be configured to change one aspect of an object (e.g., a file's owner), types are essentially named collections of states. So, there is a File type, and it is a collection of all of the states available to modify files. In addition to states, which necessarily modify 'the bits on disk', as it were, types can also have non-state parameters which modify how that type instance behaves: file { "/bin": owner => bin, recurse => true } The ``recurse`` parameter to ``file`` does not modify the system itself, it modifies how the ``file`` type manages ``/bin``. Not all types as expressed in the language are types defined in the library; some language types are actually definitions or server classes. Types that are defined in the language itself are termed ``primitive types`` or just ``primitives``, and types that generate other types are termed ``metatypes``. At this point, all types have a unique name (within the current scope, but it probably should actually be global). This name is set using a class instance variable: : # in the library Puppet::Type.newtype(:mytype) ... end # in the language mytype { "yay": ... } The states similarly have unique names, although this uniqueness is only per-type. When a type is defined, all states and parameters are added using class methods. Here is the File type declaration: newstate(:ensure) do ... end newstate(:owner) do ... end newstate(:group) do ... end ... newparam(:path) do ... end Lastly, each type must either provide a state or parameter of ':name', or it must mark a parameter as the namevar so that the system knows what is considered the name: newparam(:path) do isnamevar end With this declaration, ``file { "/tmp/file": }`` is basically equivalent to ``file { path => "/tmp/file" }``. See [Creating a Puppet Type](creating-a-puppet-type.html) for more information.