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. |