Puppet’s Ruby DSL¶
In addition to the external Puppet DSL, Puppet also has an internal Ruby DSL available. The Ruby DSL has been in Puppet since 2006 but hasn’t been extensively used by anyone in the community. With version 2.6.x support was added to extend Ruby DSL and allow for extra syntactical sugar to deal with the convergence of Ruby and Puppet DSL. This page documents how you can leverage this DSL.
Warning: This documentation is based on the status of the Ruby DSL at version 2.6.4. Before commencing be warned that the API for Ruby DSL still may evolve, and some of the examples even show internal calls which should be avoided in production code. As the language evolves we will attempt to update this document to reflect any changes.
Examples of Ruby DSL¶
See some Puppet Ruby DSL examples on Github for code snippets that show off the different features discussed in this article.
Further references¶
- Dan Bode wrote a blog entry which inspired the renewal of this wiki topic.
Other implementations¶
Also available is Shadow Puppet – which is RailsMachine’s implementation of a Ruby DSL for Puppet.
How to use Ruby DSL with your code¶
Puppet has support for importing and using Ruby based DSL code with similar semantics as Puppet DSL. In fact, in many cases you can use the two interchangeably as long as they are kept in different files.
As you will see with the following sections the parser within Puppet will always acknowledge content accessed with an rb suffix as Ruby DSL and a pp suffix as Puppet DSL and will parse them accordingly.
Basic imports¶
Normally in your manifests you will import other Puppet DSL content using this methodology:
import 'foo.pp'
import 'bar.pp'
You can now instead pull in Ruby DSL using the same syntax but with the rb extension:
import 'foo.rb'
import 'bar.rb'
Modules and Ruby DSL manifests¶
When including a class:
include 'foo'
include 'bar::baz'
Would normally (respectively) include the puppet manifests files:
$PUPPET_ENV_DIR/modules/foo/manifests/init.pp
$PUPPET_ENV_DIR/modules/bar/manifests/baz.pp
To replace this behavior with Ruby DSL, simply interchange the pp files with rb based ones:
$PUPPET_ENV_DIR/modules/foo/manifests/init.rb
$PUPPET_ENV_DIR/modules/bar/manifests/baz.rb
Site or entry-point manifest
Ordinarily your Puppet DSL content is initially loaded through your site.pp file. This entire file can now be replaced with Ruby DSL.
First adjust your puppet.conf [main] block’s entry as follows to point to a ruby file:
[main]
manifest=/etc/puppet/manifests/site.rb
If you are using environments, change the entry in each environments block entry as well.
[development]
manifest=/etc/puppet/development/manifests/site.rb
Now using the above example configuration place the file here:
/etc/puppet/development/manifests/site.rb
And your initial content will now use Ruby DSL instead.
Ruby DSL reference
This section tries to go through the available Puppet language constructs and provide equivalent Ruby DSL forms for them. As we have already warned about, not all of these are going to be supported in the future.
Note: Top scope is not available in Ruby DSL at this point in time. Where this applies I have made sure there is a note (much like this one) indicating so. Ensure examples of this kind are put between a node, class or resource declaration. For example:
node "default" do
scope.set
end
Resources
Defining Resources
You can define a resource this way:
define "foo", :port, :protocol => 'tcp' do
...
# And then access instance vars this way:
...
foo = @name
bar = @port
prot = @protocol
...
end
Defining Defaults
Note: Not available at top scope.
You can define defaults for resources this way:
scope.setdefaults(:type_name,
[
Puppet::Parser::Resource::Param.new(:name => "paramname1", :value => "paramvalue1"),
Puppet::Parser::Resource::Param.new(:name => "paramname2", :value => "paramvalue2")
]
)
Using Resources
Note: Not available at top scope.
Using Normal Resources
You can call them using the convenience method:
foo "namevar", :port => 12, :protocol => 'tcp'
For relationships use this:
foo "namevar", :port => 12, :protocol => 'tcp', :require => "Package[foo]"
Note: Don’t use quotation marks around qualified names in relationships, write
:require => “Class[foo::bar]” instead of
:require => “Class[‘foo::bar’]”.
You can declare an resource array similar to puppet manifest:
file { ['/tmp/a', '/tmp/a/b', '/tmp/a/b/c']:
ensure => directory,
}
Ruby DSL:
file [‘/tmp/a’,‘/tmp/a/b’,‘/tmp/a/b/c’],
:ensure => :directory
If there is a namespace clash with the name of the resource with something else in the ruby runtime (such as exec resource) you can do it like this:
create_resource :foo, "namevar", :port => 12, :protocol => 'tcp'
Exporting and Collecting Resources
You can export resources like this:
export do
foo "bar", baz => "waz"
end
And collect resources like this:
scope.compiler.add_collection(
Puppet::Parser::Collector.new(scope, "Foo", nil, nil, :exported)
)
Virtualizing and Realizing Resources
You can virtualize a resource like this:
virtual do
foo "bar", baz => "waz"
end
And realize a resource like this:
call_function(:realize,
'Foo[bar]')
scope.compiler.add_collection(
Puppet::Parser::Collector.new(scope, "Foo", nil, nil, :virtual)
)
Classes
Non-Parameterized Classes
Defining Classes
You define resources this way:
hostclass :fooclass do
...
end
Using Classes
Note: Not available at top scope.
You can use a class this way:
include "class"
There is no hostclass convenience method yet and using ‘class’ would clash with a ruby in-built keyword. Instead you can use:
create_resource :class, "fooclass"
Just like with resources.
Parameterized Classes
Defining Classes
You define resources this way:
hostclass :fooclass, :arguments => {"myparam1" => nil, "myparam2" => AST::String.new(:value => "somedefault") } do
...
end
Using Classes
Note: Not available at top scope.
You must use create_resources to do this:
create_resource :class, "fooclass", :myparam2 => "foo"
Nodes
Defining Nodes
node 'mynode' do
...
end
Using Nodes
Simply run it as the node in question.
Functions
Defining Functions
Functions are already written in ruby. See the article: Writing Your Own Functions.
Using Functions
Note: Not available at top scope.
You can call functions like below. Make sure parameters are passed as an array.
notice ["param1"]
If there is a namespace clash you can call functions this way:
call_function "notice", ["params1"]
Vars
Note: Not available at top scope.
Defining Vars
Basic:
scope.setvar("name_of_var", "value")
Appending:
scope.setvar("name_of_var", "value", :append => true)
Unsetting:
scope.unsetvar("name_of_var")
Using Vars
The functions available to access variables within ruby are much more extensive then using the Puppet DSL>
To access a single variable:
name_of_var = scope.lookupvar("name_of_var")
To access a hash of all variables:
all_vars = scope.to_hash()
Relationships
Defining Relationships with attributes
Defining resource relationships can be done using the following attribute based syntax, similar to the Puppet DSL:
foo "foo", :require => [ "Bar[bar]", "Bar[another_bar]" ]
Chaining Resources
TODO