Puppet

From neil.tappsville.com
Jump to navigationJump to search

Puppet

https://puppet.com/

https://forge.puppet.com/ <-- Where to get Modules

https://www.puppetcookbook.com/

Puppet code is a declarative language, which means that you describe only the desired state for your systems, not the steps needed to get there

One of Puppet's core concepts is the resource abstraction layer. For Puppet, each aspect of the system you want to manage (such as a user, file, service, or package) is represented in code as a unit called a resource. The puppet resource tool lets you view and modify these resources directly.

Puppet Master --> [Catalog] --> Node --> [Facts] --> Puppet Master

  • Facts = data about its state
  • Catalog = Facts are used to compile a catalog that specifies how the node should be configured


Puppet agent uses providers to check for difference between current and desired state. The Providers help puppet implement whatever changes are necessary

Disable

Because sometimes you want to make things work before they are automated

sudo puppet agent --disable

Types

See what Puppet knows about a resource

sudo puppet resource file /tmp/
file { '/tmp/':
  ensure => 'directory',
  ctime  => '2020-03-28 19:15:40 +0000',
  group  => 0,
  mode   => '1777',
  mtime  => '2020-03-28 19:15:40 +0000',
  owner  => 0,
  type   => 'directory',
}

sudo puppet resource package httpd ensure=present Installs a package using the default package manager

Manifests – puppet code saves as .pp

Run a manifest directly (without puppet master involvement)

sudo puppet apply /tmp/hello.pp 

Class – named block of Puppet Code – Class brings together resoures that manage one logical component of a system

Module – directory structure


Data Types

---
profile::module::con_array:
  - item1
  - item2

profile::module::con_hash:
  key1: value1
  key2: value2

profile::module::con_string: 'short string'

Data type Conversions

Hash of Hashes to Array of Hashes

myhash:
  device:
     somekey: somevalue

$myarray = $myhash.reduce([]) |$memo,$data| {
	$memo + [
		$data[1] + {
			"name" => $data[0]
		}
	]
}

myarray
  - name: device
    somekey: somevalue 


Array of Hashes to Hash of Hashes

myarray
  - name: device
    somekey: somevalue 


$myhash= $myarray.reduce({}) |$memo,$data| {
	$memo + {
		$data['name'] => $data.delete('name')
	}
}
myhash:
  device:
    somekey: somevalue

Template Files (Embedded Ruby .erb)

https://puppet.com/docs/puppet/latest/lang_template_erb.html

Not required, but nice to have, an initial block that shows what variables are expected to be passed into the template

<%- | $port,
      $default_character,
      $default_message,
| -%>
# This file is managed by Puppet. Please do not make manual changes.
---
:default_character: <%= $default_character %>
:default_message:   <%= $default_message %>
:sinatra_settings:
  :port: <%= $port %>

Simple Validator

puppet parser validate pasture/manifests/init.pp

Puppet-Lint Style validator http://puppet-lint.com/

puppet-lint /etc/puppet/modules
puppet-lint --fix /etc/puppet/modules

There is also PDK https://puppet.com/docs/pdk/1.x/pdk.html


  1. Ruby Ternary Operator
CHECK                            ? TRUE                            : FALSE
scope['processors']['count'] > 1 ? scope['processors']['count'] -1 : 1

Normally to update the host (instead of waiting for the timer)

sudo puppet agent --test

Run and make no changes

sudo puppet agent -t --noop


Relationship metaparameters tell Puppet about ordering relationships among your resources.

  • before => File['/etc/pasture_config.yaml'], <-- create my resource before this other resource (config file) is generated
  • notify => Service['pasture'], <- on change tell this other resource

When a host stops reporting, it will not fetch anything

Manifests

Using the default parameters

include module_class

Passing in parameters

  class { 'pasture':
    default_character => 'cow',
  }

duplicated declarations - Packages

Two ways to include packages - if there are multiple modules that require packages they must use the same inclusion method as each other - especially if there is a dependency for the package to be installed before another one.


package['wget']

If another module also does this there will be a duplicate declaration, so use:

  if !defined(Package['wget']) {
    package { 'wget':
      ensure => installed,
    }
  }

This allows there to be conditions applied to the package aka install before x by multiple modules.

ensure_packages(['wget'])

No additional checks are required, but if one module has a condition (aka this is required before x), ensure_packages will see this as a different instance thus become a duplicate declaration.


Profiles and Roles

Using roles and profiles is a design pattern, not something written into the Puppet source code.

A profile is a class that declares one or more related component modules and sets their parameters as needed. The set of profiles on a system defines and configures the technology stack it needs to fulfill its business role.

A role is a class that combines one or more profiles to define the desired state for a whole system. A role should correspond to the business purpose of a server.

A role itself should only compose profiles and set their parameters—it should not have any parameters itself.

A role should consist of only include statements to pull in the list of profile classes that make up the role. A role should not directly declare non-profile classes or individual resources.

Facts

https://ragnarkon.com/puppet/accessing-puppet-facts/

$facts['os']['family']

Classic way to access $::osfamily

facter -p os
{
  architecture => "x86_64",
  family => "RedHat",
  hardware => "x86_64",
  name => "CentOS",
  release => {
    full => "7.6.1810",
    major => "7",
    minor => "6"
  },
  selinux => {
    enabled => false
  }
}


Can find the resources as node has by looking at the cache

/opt/puppetlabs/puppet/cache/state/resources.txt

Combining and Inheritance

Usually the attribute closest to the object wins, aka hieracommon-dev.yaml over-writes hieracommon.yaml, should you want to use all values set against the object there are two ways 1. In the Manifest

 $allattributes = lookup('package::attribute', Array[String], 'unique')

2. Specify in common.yaml

lookup_options:
  package::attribute:
    merge: unique

Stdlib

a standard library of resources for Puppet modules. such as Stdlib::IP::Address::V4

https://forge.puppet.com/puppetlabs/stdlib

puppet encrypt

Encrypting variables

ssh-keygen -f hostname -C hostname -t ed25519
sudo /opt/puppetlabs/puppet/bin/eyaml encrypt -f hostname
sudo /opt/puppetlabs/puppet/bin/eyaml decrypt -s "ENC[PKCS7,MII......]"

A better way is to use stdin

sudo /opt/puppetlabs/puppet/bin/eyaml encrypt --stdin
<paste a special string> <enter>
ctrl-d

Forman Integration

If the branch that the environment is using gets merged/deleted the nodes stop updating as puppet cant find a class = branch. puppet only needs to find a branch thats 'alive' then it will get told the right information from forman. so

sudo puppet agent -t --environment develop

Instead of --->

Usually when someone has stopped puppet - done lots of changes, potentially deleted the branch it was configured with.

Ensure that the /etc/puppetlabs/puppetpuppet.conf files environment is correct

[main]
vardir = /opt/puppetlabs/puppet/cache
logdir = /var/log/puppetlabs/puppet
rundir = /var/run/puppetlabs
ssldir = /etc/puppetlabs/puppet/ssl

[agent]
pluginsync      = true
report          = true
ignoreschedules = true
ca_server       = puppetmaster.domain
certname        = thishost.domain
environment     = develop
server          = puppetmaster.domain
splay = true

Yaml

ruby -e "require 'yaml';require 'pp';pp YAML.load_file('./hiera.yaml')"

Syntax check a yaml file

ruby -ryaml -e "p YAML.load(STDIN.read)" < data.yaml


Control-Repo

Testing Control-Repo

OnceOVer

gem install onceover onceover-codequality
(in control-repo) onceonver init
onceover run codequality

Code Quality

https://dev.to/camptocamp-ops/cleaning-up-puppet-code-4da2

vscode

https://puppet-vscode.github.io/


docker

https://code.visualstudio.com/docs/remote/containers For module development so `pdk validate` runs against docker and not the local OS

requires

  • .devcontainer/.devcontainer.json
  • Dockerfile (containing)
FROM puppet/pdk:latest
  • F1
  • Remote-Containers: Open Folder in Container..

On Mac - start docker, then launch vscode. Command-shift-P --> Remote Containers: Open folder in container

pdk validate
pdk test unit. --> requires tests in /spec/classes/[module]_spec.rb, external modules to be defined in /.fixtures.yml


To add ssh credentials to the container, modify `.devcontainer/.devcontainer.json`

	"mounts": [
		"source/path_to_credentials/.ssh,target=/root/.ssh,type=bind,consistency=cached",
	],

Snippets

Override module hiera attributes

Module: puppet-conserver/data/osfamily/RedHat/7/default.yaml

---
conserver::server_init_config_file: /usr/lib/systemd/system/conserver.service
conserver::server_init_config_tpl: conserver/server/init_config_file_systemd.erb
conserver::server_init_config_hash:
  Unit:
    Description: Conserver Serial-Port Console Daemon
    After: network.target
  Service:
    Type: forking
    ExecStart: /usr/sbin/conserver -d
    User: root
  Install:
    WantedBy: multi-user.target
conserver::has_systemd: true

Profile: conserver.pp

class { 'conserver':
  server_init_config_hash => 
    'Unit' => {
      'Description' => 'Conserver Serial-Port Console Daemon',
      'After' => 'network.target',
    },
    'Service' => {
      'Type' => 'forking',
      'ExecStart' => '/usr/sbin/conserver -d',
      'ExecReload' => '/bin/kill -HUP $MAINPID',
      'User' => 'conserver',
    },
    'Install' => {
      'WantedBy' => 'multi-user.target',
    },
}

systemd dropin file

modifies the behaviour of the systemd file delivered by a package - systemd::dropin_file { 'conserver.conf':

 unit    => 'conserver.service',
 content => @(END),
   [Service]
   User=conserver
   ExecReload=/bin/kill -HUP $MAINPID
   |END
 before  => Service['conserver'],

}