Standalone open source puppet setup on Fedora

My goal in this post is to show how to setup puppet in standalone mode on a Fedora 23 system. This setup will allow writing puppet modules and then you can apply them on your local system and check manually and via serverspec tests that they are doing what you intend them to be doing. Obviously, a VM would be the best test environment for this, or even a container. Let's get started.

Setup

Install puppet:

# dnf -y install puppet

Setup the host name:

# cat /etc/hostname
fedora-23.node

Reboot, verify:

# facter | grep node
domain => node
fqdn => fedora-23.node

Our First manifest

Let's write our first manifest. We will place it in /etc/puppet/manifests.

Upon installation, /etc/puppet looks like:

# tree /etc/puppet/
/etc/puppet/
 ├── auth.conf
 ├── modules
 └── puppet.conf

We will create a manifests sub-directory:

# mkdir /etc/puppet/manifests

Now, we will create our first manifest /etc/puppet/manifests/nginx.pp:

node "fedora-23.node" {
   package { "nginx":
     ensure => installed
   }
}

Apply with --noop:

# puppet apply nginx.pp --noop
Notice: Compiled catalog for fedora-23.node in environment production in 0.66 seconds
Notice: /Stage[main]/Main/Node[fedora-23.node]/Package[nginx]/ensure: current_value purged, should be present (noop)
Notice: Node[fedora-23.node]: Would have triggered 'refresh' from 1 events
Notice: Class[Main]: Would have triggered 'refresh' from 1 events
Notice: Stage[main]: Would have triggered 'refresh' from 1 events
Notice: Applied catalog in 0.26 seconds

Really apply:

# puppet apply nginx.pp
Notice: Compiled catalog for fedora-23.node in environment production in 0.60 seconds
Notice: /Stage[main]/Main/Node[fedora-23.node]/Package[nginx]/ensure: created
Notice: Applied catalog in 5.67 seconds
# rpm -q nginx
nginx-1.8.0-13.fc23.x86_64

Writing serverspec tests

We will first install bundler:

dnf -y install rubygem-bundler

We will put our serverspec test in /etc/puppet/manifests/tests:

# mkdir /etc/puppet/manifests/tests
# cd /etc/puppet/manifests/tests

Create a Gemfile:

# cat Gemfile
source 'https://rubygems.org'

gem 'serverspec'
gem 'rake'

Install the gems:

# bundle  install --path ./gems/
Installing rake 10.4.2
Installing diff-lcs 1.2.5
Installing multi_json 1.11.2
Installing net-ssh 2.9.2
Installing net-scp 1.2.1
Installing net-telnet 0.1.1
Installing rspec-support 3.3.0
Installing rspec-core 3.3.2
Installing rspec-expectations 3.3.1
Installing rspec-mocks 3.3.2
Installing rspec 3.3.0
Installing rspec-its 1.2.0
Installing sfl 2.2
Installing specinfra 2.43.10
Installing serverspec 2.24.1
Using bundler 1.7.8
Your bundle is complete!
It was installed into ./gems

Initialize the serverspec directory tree:

# bundle exec serverspec-init

Select OS type:

1) UN*X
2) Windows

Select number: 1

Select a backend type:

1) SSH
2) Exec (local)

Select number: 2

+ spec/
+ spec/localhost/
+ spec/localhost/sample_spec.rb
+ spec/spec_helper.rb
+ Rakefile
+ .rspec

Time to write our test in spec/localhost/nginx_spec.rb:

require 'spec_helper'
describe package('nginx') do
    it { should be_installed }
end

Let's run our test:

# bundle exec rake spec
/usr/bin/ruby -I/etc/puppet/manifests/tests/gems/ruby/gems/rspec-core-3.3.2/lib:/etc/puppet/manifests/tests/gems   /ruby/gems/rspec-support-3.3.0/lib /etc/puppet/manifests/tests/gems/ruby/gems/rspec-core-3.3.2/exe/rspec --pattern spec/localhost/\*_spec.rb

Package "nginx"
should be installed

Finished in 0.03447 seconds (files took 0.17465 seconds to load)
1 example, 0 failures

Our first module

We will now write our first puppet module, we will name it nginx:

# tree modules/nginx/
modules/nginx/
└── manifests
   ├── config
   │   ├── config1.pp
   │   └── config.pp
   └── init.pp

Create modules/nginx/manifests/init.pp:

# modules/nginx/manifests/init.pp

class nginx {
   package { "nginx":
      ensure => installed
   }

   include nginx::config::config
}

Create modules/nginx/manifests/config/config.pp:

# modules/nginx/manifests/config/config.pp
class nginx::config::config{

file { '/etc/nginx/nginx.conf':
    ensure  => present,
 }
 include nginx::config::config1
}

Create modules/nginx/manifests/config/config1.pp:

# modules/nginx/manifests/config/config1.pp
class nginx::config::config1{
  file { '/etc/nginx/conf.d':
     ensure  => directory,
  }
}

Let's write a manifest to include this module:

# cat manifests/use-nginx-module.pp
include nginx

Remove nginx and appy the manifest above:

 # dnf remove nginx

 # puppet apply manifests/use-nginx-module.pp --noop
Notice: Compiled catalog for fedora-23.node in environment production in 0.61 seconds
Notice: /Stage[main]/Nginx/Package[nginx]/ensure: current_value purged, should be present (noop)
Notice: Class[Nginx]: Would have triggered 'refresh' from 1 events
Notice: /Stage[main]/Nginx::Config::Config/File[/etc/nginx/nginx.conf]/ensure: current_value absent, should be   present (noop)
Notice: Class[Nginx::Config::Config]: Would have triggered 'refresh' from 1 events
Notice: /Stage[main]/Nginx::Config::Config1/File[/etc/nginx/conf.d]/ensure: current_value absent, should be directory (noop)
Notice: Class[Nginx::Config::Config1]: Would have triggered 'refresh' from 1 events
Notice: Stage[main]: Would have triggered 'refresh' from 3 events
Notice: Applied catalog in 0.24 seconds

And we are done.

Miscellaneous

Use puppet parser to validate your manifest:

$ puppet parser validate nginx.pp

Print current module path:

$ puppet config print modulepath
/etc/puppet/modules