summaryrefslogtreecommitdiffstats
path: root/documentation/introduction.rst
diff options
context:
space:
mode:
Diffstat (limited to 'documentation/introduction.rst')
-rw-r--r--documentation/introduction.rst315
1 files changed, 315 insertions, 0 deletions
diff --git a/documentation/introduction.rst b/documentation/introduction.rst
new file mode 100644
index 000000000..2614b4d46
--- /dev/null
+++ b/documentation/introduction.rst
@@ -0,0 +1,315 @@
+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 <servername>` as arguments to `puppetd`, replacing
+"<servername>" 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 <name>`,
+replacing "<name>" 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. At this
+point, the only syntactical or functional difference between the two is that
+server classes can have a parent class::
+
+ class base {
+ sudo { ... } # an existing definition
+ }
+
+ class server inherits base {
+ ssh { ... } # an existing definition
+ }
+
+ if $hostname == "myhost" {
+ server{} # the braces are mandatory at this point
+ } elsif $hostname == "otherhost" {
+ base{}
+ }
+
+The classes also support parameterization, just like definitions. Notice that
+the mechanism for associating nodes with servers is stupid. I considered the
+following syntax::
+
+ node host {
+ server {}
+ }
+
+But that doesn't seem much better. Again, recommendations are accepted.
+
+Having a parent class means that that parent's objects are all operated on
+before any of the subclass's are. So, in this case, each member of 'server'
+would first verify 'sudo' is set up correctly and then check 'ssh'.
+
+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
+ }
+ }
+
+If
+++
+Puppet currently supports a normal If/Elsif/Else structure, and each statement
+introduces a new lexical scope. Things that can be tested are currently
+extremely limited (just simple comparisons, e.g., '==', '<=', etc.); it should
+be easy to add &&, ||, and parentheticals for extended tests, but I don't yet
+know how I'll handling testing more complex expressions.
+
+I expect that this structure will go away, to be replaced with a more
+declarative structure.
+
+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
+ class Mytype < Puppet::Type
+ @name = :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 also listed as
+class instance variables; states are listed by class, and parameters with
+symbols. Here is the File type declaration: ::
+
+ @states = [
+ Puppet::State::FileCreate,
+ Puppet::State::FileUID,
+ Puppet::State::FileGroup,
+ Puppet::State::FileMode,
+ Puppet::State::FileSetUID
+ ]
+
+ @parameters = [
+ :path,
+ :recurse
+ ]
+
+Puppet::Type gets notified of a subclass, and it iterates over the states
+retrieving each name and making a hash from the name to the class.
+
+Lastly, each type must either provide a state or parameter of ':name', or it
+must set '@namevar' so that the system knows what is considered the name: ::
+
+ @namevar = :path
+
+With this declaration, ``file { "/tmp/file": }`` is basically equivalent to ``file { path => "/tmp/file" }``.