@ü.li:Listing 1: Strikte Namensgebung im Modules-Verzeichnis @li:/etc/puppetlabs/code/environments # Environment Pfad /production/ # Produktions-Environment /modules/ # Modulpfad /my_apache/ # my_apache Modul /manifests/ # Modul-Klassen (Puppet-DSL-Code) /files/ # statische Konfigurationsdateien /templates/ # dynamische Konfigurationsdateien /facts.d/ # Pluginsync für statische Facter-Erweiterungen /lib/ # Pluginsync für Code-Erweiterungen /facter/ # Pluginsync für Ruby-Facter-Erweiterungen /puppet/ /functions/ # Pluginsync für Puppet-4-Funktionen /parser/ /functions/ # Pluginsync für Puppet-3-Funktionen /type/ # Pluginsync für eigene Ressourcentypen /provider/ // # Pluginsync für Provider des Ressourcetypen /spec/ # Rspec Tests /function/ # Daten Funktionen in Puppet DSL @ü.li:Listing 2: Erweiterter Namensraum eines Moduls @li:my_apache Modul /etc/puppetlabs/code/environments/production/modules/my_apache /manifests/ /init.pp # class my_apache { ... } /install.pp # class my_apache::install { ... } /server/secure.pp # class my_apache::server::secure { ... } @ü.li:Listing 3: Apache-Modul mit statischer Konfigurationsdatei @li:class my_apache { case $::facts['os']['family'] { 'Debian': { $pkgname = 'apache2' $cfgdir = '/etc/apache2' $cfgfile = 'apache2.conf' $svcname = 'apache2' } 'RedHat': { $pkgname = 'httpd' $cfgdir = '/etc/httpd' $cfgfile = 'httpd.conf' $svcname = 'httpd' } default: { fail("Das Betriebssystem ${::facts['os']['id']} wird nicht unterstützt.") } } package { 'apache': ensure => present, name => $pkgname, } file { 'apache_cfg': ensure => file, path => "${cfgdir}/${cfgfile}", owner => 'root', group => 'root', mode => '0644', source => "puppet:///modules/my_apache/${cfgfile}", require => Package['apache'], } service { 'apache': ensure => running, enable => true, name => $svcname, subscribe => File['apache_cfg'], } } @ü.li:Listing 4: Subklasse params @li:# /my_apache/manifests/params.pp class my_apache::params { case $::facts['os']['family'] { 'Debian': { $pkgname = 'apache2' $cfgdir = '/etc/apache2' $cfgfile = 'apache2.conf' $svcname = 'apache2' } 'RedHat': { $pkgname = 'httpd' $cfgdir = '/etc/httpd' $cfgfile = 'httpd.conf' $svcname = 'httpd' } default: { fail("Das Betriebssystem ${::facts['os']['id']} wird nicht unterstützt.") } } } # /manifests/init.pp class my_apache { contain my_apache::params package { 'apache': ensure => present, name => $my_apache::params::pkgname, } file { 'apache_cfg': ensure => file, path => "${my_apache::params::cfgdir}/${my_apache::params::cfgfile}", owner => 'root', group => 'root', mode => '0644', source => "puppet:///modules/my_apache/${my_apache::params::cfgfile}", require => Package['apache'], } service { 'apache': ensure => running, enable => true, name => $my_apache::params::svcname, subscribe => File['apache_cfg'], } } @ü.li:Listing 5: Betriebssystemspezifische Werte im Apache-Modul @li:class my_apache ( $pkgname_param = undef, $cfgdir_param = undef, $cfgfile_param = undef, $svcname_param = undef, ) { contain my_apache::params # Hier findet man OS-spezifische Beschreibungen $pkgname = $pkgname_param ? { undef => $my_apache::params::pkgname, default => $pkgname_param, } $cfgdir = $cfgdir_param ? { undef => $my_apache::params::cfgdir, default => $cfgdir_param, } $cfgfile = $cfgfile_param ? { undef => $my_apache::params::cfgfile, default => $cfgfile_param, } $svcname = $svcname_param ? { undef => $my_apache::params::svcname, default => $svcname_param, } # Rest Code wie oben } @ü.li:Listing 6: Vererbung @li:class my_apache ( $pkgname = $my_apache::params::pkgname, $cfgdir = $my_apache::params::cfgdir, $cfgfile = $my_apache::params::cfgfile, $svcname = $my_apache::params::svcname, ) inherits my_apache::params { package { 'apache': ensure => present, name => $pkgname, } file { 'apache_cfg': ensure => file, path => "${cfgdir}/${cfgfile}", owner => 'root', group => 'root', mode => '0644', source => "puppet:///modules/my_apache/${cfgfile}", require => Package['apache'], } service { 'apache': ensure => running, enable => true, name => $svcname, subscribe => File['apache_cfg'], } } class my_apache::params { case $::facts['os']['family'] { 'Debian': { $pkgname = 'apache2' $cfgdir = '/etc/apache2' $cfgfile = 'apache2.conf' $svcname = 'apache2' } 'RedHat': { $pkgname = 'httpd' $cfgdir = '/etc/httpd' $cfgfile = 'httpd.conf' $svcname = 'httpd' } default: { fail("Das Betriebssystem ${::facts['os']['id']} wird nicht unterstützt.") } } } @ü.li:Listing 7: Parametrisierte Klassen @li:class my_selinux { package { 'selinux': ensure => present, } file { '/etc/sysconfig/selinux': ensure => file, content => "selinux=enforced\n", require => Package['selinux'], } } class my_selinux::disable inherits my_selinux { Package['selinux'] { ensure => absent, require => File['/etc/sysconfig/selinux'], } File { '/etc/sysconfig/selinux': ensure => absent, require => undef, } } @ü.li:Listing 8: MySQL-Modul @li:# /etc/puppetlabs/code/environment/production/my_mysql/manifests/init.pp class my_mysql ( $mysql_rootpw = '', ){ contain my_mysql::params package { $my_mysql::params::package: ensure => present, } file { $my_mysql::params::my_cnf: ensure => file, owner => 'root', group => 'root', mode => '0400', content => "rootpassword = ${mysql_rootpw}\n", } service { $my_mysql::params::service: ensure => running, enable => true, } } # /etc/puppetlabs/code/environment/productoin/my_mysql/manifests/params.pp class my_mysql::params { case $::facts['os']['family'] { 'Debian', 'RedHat': { $package = 'mysql-server' $my_cnf = '/etc/mysql/my.cnf' $service = 'mysqld' } default: { fail("Your OS: ${facts['os']['family']} is not supported by module ${module_name}") } } } @ü.li:Listing 9: Dynamische Ressourcentypen mit $title @li:define my_mysql::mysqldb ( $user, $passwd, $host = 'localhost', $grant = 'ALL', ){ Exec { path => '/usr/bin:/usr/sbin:/bin:/sbin', } exec { "create db ${title}": command => "mysql -u root -p ${my_mysql::mysql_rootpw} -e 'create database ${title}": unless => "mysql -u root ${title} -e 'select 1'", } exec { "grant ${user} to ${title}": command => "mysql -u root -e 'grant ${grant} on ${title} to ${user}@${host} identified by ${passwd}": unless => "mysql -u ${user} -p ${passwd}", } @ü.li:Listing 10: Mehrere Ressourcentypen in einem Define @li:define my_usermgmt ( $group = $title, $home = "/home/${title}", $passwd = undef, ){ group { $title: ensure => present, } user { $title: ensure => present, gid => $group, home => $home, passwd => $passwd, } file { $home: ensure => directory, owner => $title, group => $group, mode => '0755', } file { "${home}/.ssh": ensure => directory, owner => $title, group => $group, mode => '0750', } file { "${home}/.bashrc_company": ensure => file, owner => $title, group => $group, mode => '0644', content => "export http_proxy=http://proxy:3128\n", } } @ü.li:Listing 11: Klassendefinition mit Automatic Data Binding @li:class my_apache ( $pkgname = $my_apache::params::pkgname, $cfgdir = $my_apache::params::cfgdir, $cfgfile = $my_apache::params::cfgfile, $svcname = $my_apache::params::svcname, ) inherits my_apache::params { # Puppet Code } class my_mysql ( $mysql_rootpw = '', ){ # Puppet Code } @ü.li:Listing 12: Nested Hash @li:class my_users ( Hash[String, Struct] $users_hash = {} ){ $users_hash.each |$user_key, $user_value| { $home = $user_value['home'] $shell = $user_value['shell'] if ! ($home =~ Pattern[/^\/.*$/]) { fail('The home config in the user hash is not an absolute path') } # ... } @ü.li:Listing 13: Datentypen im Apache- und im MySQL-Modul @li:class my_apache ( String $pkgname = $my_apache::params::pkgname, Pattern[/^\/.*$/] $cfgdir = $my_apache::params::cfgdir, Pattern[/.*\.conf$/] $cfgfile = $my_apache::params::cfgfile, String $svcname = $my_apache::params::svcname, ) inherits my_apache::params { # Puppet Code } class my_mysql ( String $mysql_rootpw = '', ){ # Puppet Code } @ü.li:Listing 14: Dispatch und Namensräume in Puppet~~3 und~~4 @li:# Prüfen der Parameter-Datentypen im Dispatch # lib/puppet/functions/resolver.rb Puppet::Functions.create_function(:resolver) do dispatch :ip_param do param 'Pattern[/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/]', :ip end # ... end # 2) Namensgleichheit bei Code und Dispatch # lib/puppet/functions/resolver.rb require 'resolv' # needed by Resolv.getname function Puppet::Functions.create_function(:resolver) do dispatch :ip_param do param 'Pattern[/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/]', :ip end def ip_param(ip) Resolv.getname(ip) end end # 3) Individuelle Namensräume für Funktionen in Puppet 4 # lib/puppet/functions/my_apache/resolver.rb require 'resolv' # needed by Resolv.getname function Puppet::Functions.create_function(:'my_apache::resolver') do dispatch :ip_param do param 'Pattern[/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/]', :ip end def ip_param(ip) Resolv.getname(ip) end end @ü.li:Listing 15: Dynamische Vorlage für Konfigurationsdatei @li:# apache_config.epp ServerRoot "<%= $my_apache::server_root %>" PidFile ${APACHE_PID_FILE} Timeout 300 KeepAlive Off User ${APACHE_RUN_USER} Group ${APACHE_RUN_GROUP} HostnameLookups Off ErrorLog ${APACHE_LOG_DIR}/error.log LogLevel warn Include ports.conf > Options Indexes FollowSymLinks AllowOverride None Require all granted IncludeOptional sites-enabled/*.conf @ü.li:Listing 16: User-SSH-Konfiguration (EPP-Template) @li:# /my_usermgmt/templates/ssh_config.epp <%- | $strict_hostkey_check, $user_known_host_file, $user_host_config, | -%> Host * StrictHostKeyCheck <%= $strict_hostkey_check %> UserKnownHostsFile <%= $user_known_host_file %> <% $user_host_config.each |$key, $value| { -%> Host <%= $key %> <%- $value.each |$subkey, $subvalue| { -%> <%= $subkey %> <%= $subvalue %> <%- } -%> <% } -%> @ü.li:Listing 17: Parameterübergabe mit EPP-Funktion und Hash-Map @li:define my_usermgmt ( Enum['on', 'off'] $strict_host_check = 'off', String $user_known_hosts = "/home/${title}/.ssh/config", Hash $user_config = {}, ) { # Puppet DSL code file { "/home/${title}/.ssh/config": ensure => file, content => epp('my_usermgmt/ssh_config.epp', { strict_hostkey_check => $strict_host_check, user_known_host_file => $user_known_hosts, user_host_config => $user_config, } ), } } @ü.li:Listing 18: my.cnf nur teilweise verwalten @li:class my_mysql ( String $mysql_rootpw = '', ){ contain my_mysql::params package { $my_mysql::params::package: ensure => present, } file_line { 'mysqlroot_password': ensure => present, path => $my_mysql::params::my_cnf, line => "root_password = ${mysql_rootpw}\n", match => 'root_password', } service { $my_mysql::params::service: ensure => running, enable => true, } } @ü.li:Listing 19: Implementierungsmodul (Profil) @li:class profile::mysql_server { $mysql_rootpasswd = hiera('mysql_root_password', 's3cr3t') class { 'my_mysql': root_password => $mysql_rootpasswd, } } class profile::wordpress::app { $wordpress_db_host = hiera('wordpress_db_host') $wordpress_db_pass = hiera('wordpress_db_pass') contain my_apache class { 'wordpress': db_user => 'wordpress', db_pass => $wordpress_db_pass, } } class profile::wordpress::db { $wordpress_db_pass = hiera('wordpress_db_pass') $wordpress_app_host = hiera('wordpress_app_host') my_mysql::mysqldb { 'wordpress': user => 'wordpress', password => $wordpress_db_pass, host => $wordpress_app_host, } }