Ruby DSL
Starting in Puppet 2.6.x, manifests can be written in Ruby.
Motivations:
- Developers can generate puppet resources without having to learn the Puppet DSL.
- Possible to overcome any limitations of the Puppet DSL.
- Ruby already has well known unit testing frameworks.
Methods:
Puppet’s Ruby DSL introduces two types of methods:
Methods that create scopes:
- Allows the user to implement a defined resource type in Ruby.
define "services::port", :port, :protocol => 'tcp' do ... end
port and protocols are accessible within the block as instance variables.
- Creates a class.
hostclass :fooclass, :foo do end
- Creates a node scope which can be used to associate resources with a certain node.
node 'mynode' do include apache end
Each of these methods accepts a block in which resources can be declared. Resources can only be declared in one of these blocks.
Methods that create resources
#/etc/puppet/manifests/blah.rb hostclass :blah do file '/tmp/blah', :content => 'bar' end node 'default' do include 'blah' end
Now I can run puppet apply on the Ruby file to create the file:
# puppet /etc/puppet/manifests/blah.rb notice: /Stage[main]/Blah/File[/tmp/blah]/ensure: defined content as '{md5}37b51d194a7513e45b56f6524f2d51f2'
Attributes passed to a defined resource or hostclass will be available as instance variables within the block.
define :foo, :bar do notify @name, :message => @bar end
Examples
One of the oldest open tickets in Puppet requests mkdir_p functionality (It’s #86 :) ). We can easily code this with the Ruby DSL since Ruby can iterate over all directory subpaths to create File resources.
define :mkdir_p do name = @name until name == '/' file name, :ensure => 'directory' name = File.dirname(name) end end node 'default' do mkdir_p '/tmp/foo/bar/baz' end
Now puppet will create all directories in this path!
$ puppet mkdir_p.rb notice: /Stage[main]//Node[default]/Mkdir_p[/tmp/foo/bar/baz]/File[/tmp/foo]/ensure: ensure changed 'file' to 'directory' notice: /Stage[main]//Node[default]/Mkdir_p[/tmp/foo/bar/baz]/File[/tmp/foo/bar]/ensure: created notice: /Stage[main]//Node[default]/Mkdir_p[/tmp/foo/bar/baz]/File[/tmp/foo/bar/baz]/ensure: created
Now, we could use this defined resource type in a function to create a file with its parent directories:
define :with_parents do mkdir_p File.dirname(@name) file @name, :ensure => 'present' end define :mkdir_p do name = @name until name == '/' file name, :ensure => 'directory' name = File.dirname(name) end end node 'default' do with_parents '/tmp/foo/bar/baz/myfile' end
7 Comments
This is awesome guys, it’s going to remove a lot of the hoops we have to jump through to get the right behaviour using the Puppet DSL.
Many thanks.
Interesting stuff. I’d be really keen to see an example test driven workflow for puppet using Ruby manifests – is that something you would consider?
Thank you, thank you, thank you!
Up until now, our only reasons for upgrading Puppet have had nothing to do with Puppet itself. This changes that, significantly. Oh, happy day!
Ciao,
Sheldon.
Puppet & Ruby DSL – Only four minutes | Ruy Rocha
[...] 2.6.x introduces a ruby dsl, so manifests and resources can be written in ruby right now. It’s time to rewrite everything [...]
Good stuff. However, one of the things I had to find out by trying, is the way to include pure-Ruby manifests in your Puppet catalog.
It appears, you can just put something like:
import “classes/*.rb”
in your site.pp, and include the classes defined there like any other.
Best regards,
Martijn.
Is there some way to make available the mkdir_p and with_parents functions to a puppet manifest. I really don’t want to have to convert all of my .pp files to .rb files.
Tom – you could easily write a Puppet define or a function to do the same thing.