| 25 | | * modules: modules are abstracted collections of manifests, templates and files. Usually, they are intended to be re-usable by others (we have one exception, the mayfirst module, which contains most code specific to us. |
| | 28 | * modules: modules are abstracted collections of manifests, templates and files. All of our puppet code is in the mayfirst module. |
| | 29 | |
| | 30 | = Puppet Resources = |
| | 31 | |
| | 32 | Puppet is [http://docs.puppetlabs.com/ fully documented]. |
| | 33 | |
| | 34 | However, we only use a small subset of puppet features. Below is a very brief overview. |
| | 35 | |
| | 36 | == files == |
| | 37 | |
| | 38 | A file resources defines a file that should be created on the server: |
| | 39 | |
| | 40 | {{{ |
| | 41 | file { "/etc/aliases": |
| | 42 | ensure => present, |
| | 43 | content => template("mayfirst/postfix/aliases.erb"), |
| | 44 | } |
| | 45 | }}} |
| | 46 | |
| | 47 | This file resource ensures that a file located at /etc/aliases on the target server will be created. The content of the file will be based on the template located in the puppet repository in modules/mayfirst/postfix/aliases.erb (this template contains variables that will be dynamically filled in). |
| | 48 | |
| | 49 | == execs == |
| | 50 | |
| | 51 | An exec resources defines a command that will be run everytime puppet is run: |
| | 52 | |
| | 53 | {{{ |
| | 54 | exec { "gpg-ssh-genkey-$user": |
| | 55 | command => "ssh-keygen -t rsa -b '$length' -f '$homedir/.ssh/id_rsa' -N ''", |
| | 56 | environment => "HOME=$homedir", |
| | 57 | require => [ Package["gnupg"], Package["openssh-client"] ], |
| | 58 | user => $user, |
| | 59 | unless => "test -f '$homedir/.ssh/id_rsa'" |
| | 60 | } |
| | 61 | |
| | 62 | The unless clause provides a test to see if the command should be run. |
| | 63 | |
| | 64 | == defines == |
| | 65 | |
| | 66 | A define statement is like a function. It allows you to define a set of things to happen every time it is called. |
| | 67 | |
| | 68 | Here's how a define is, well, defined: |
| | 69 | {{{ |
| | 70 | define m_gpg::publish_user_key ( $keyserver = '' ){ |
| | 71 | $user = $title |
| | 72 | |
| | 73 | $keyserver_arg = $keyserver ? { |
| | 74 | '' => '', |
| | 75 | default => "--keyserver $keyserver" |
| | 76 | } |
| | 77 | |
| | 78 | exec { "gpg-send-key-$user": |
| | 79 | command => "gpg $keyserver_arg --send-key $(gpg --list-secret-key --with-colons | grep ^sec | cut -d: -f5)", |
| | 80 | require => [ Package["gnupg"], Exec["gpg-pem2openpgp-$user" ] ], |
| | 81 | user => $user, |
| | 82 | } |
| | 83 | }}} |
| | 84 | |
| | 85 | And here's how it is called: |
| | 86 | {{{ |
| | 87 | m_gpg::publish_user_key { "root": keyserver => $mfpl_keyserver } |
| | 88 | }}} |
| | 89 | |
| | 90 | You can repeatedly call the same define as many times as you want. |
| | 91 | |
| | 92 | == classes == |
| | 93 | |
| | 94 | A class is a bigger collection of resources that can only be called once per node. |
| | 95 | |
| | 96 | Here's an example of a simple class: |
| | 97 | |
| | 98 | {{{ |
| | 99 | class m_resolv ( $caching_dns_ips = "", $location ) { |
| | 100 | $ips = $caching_dns_ips ? { |
| | 101 | "" => $location ? { |
| | 102 | telehouse => [ "216.66.23.46", "216.66.23.36" ], |
| | 103 | xo => [ "209.234.253.239", "209.234.253.186" ], |
| | 104 | sunsetpark => [ "216.27.145.117", "216.27.145.30" ], |
| | 105 | default => [ "216.66.23.46", "209.234.253.239" ] |
| | 106 | }, |
| | 107 | default => $caching_dns_ips |
| | 108 | } |
| | 109 | file { "/etc/resolv.conf": |
| | 110 | ensure => present, |
| | 111 | content => template("mayfirst/resolv/resolv.conf.erb") |
| | 112 | } |
| | 113 | } |
| | 114 | }}} |
| | 115 | |
| | 116 | And here is the class being called: |
| | 117 | |
| | 118 | {{{ |
| | 119 | class { "m_resolv": |
| | 120 | caching_dns_ips => [ "216.66.23.46", "216.66.23.36" ], |
| | 121 | location => "telehouse" |
| | 122 | } |
| | 123 | |
| | 124 | == nodes == |
| | 125 | |
| | 126 | The node is the basic building block for a server. Each individual server has a corresponding node. The node invokes everything, pulling it all together: |
| | 127 | |
| | 128 | {{{ |
| | 129 | node "ali.mayfirst.org" { |
| | 130 | |
| | 131 | $namesake = "https://secure.wikimedia.org/wikipedia/en/wiki/Muhammed_Ali" |
| | 132 | $purpose = "Offsite backup server for both MFPL rack machines and members who backup their office server; web interface to backup files" |
| | 133 | |
| | 134 | class { "m_sshd::default": } |
| | 135 | class { "m_minimal": |
| | 136 | location => "sunsetpark", |
| | 137 | backup_includes => [ "/var/log", "/etc", "/root", "/usr/local" ], |
| | 138 | backup_rsync_target => "robideau.mayfirst.org", |
| | 139 | caching_dns_ips => [ "216.27.145.30", "216.27.145.117" ] |
| | 140 | } |
| | 141 | class { "m_esmtp": } |
| | 142 | m_monkeysphere::publish_server_keys { ali: } |
| | 143 | m_gpg::publish_user_key { "root": keyserver => $mfpl_keyserver } |
| | 144 | |
| | 145 | class { "m_backupninja::server": } |
| | 146 | } |
| | 147 | }}} |
| | 148 | |
| | 149 | $namesake and $purpose are not used by puppet - they are there for human readability. |
| | 150 | |
| | 151 | = Variable Scoping = |
| | 152 | |
| | 153 | Global variables are defined in manifests/globals.pp and have an mfpl_ prefix. They are accessible anywhere in the code, provided you use the double colon syntax: $::, e.g. $::mfpl_admin_user_ids. |
| | 154 | |
| | 155 | All other variables should be properly scoped. |