GSoC Project: Ruby DSL for Puppet
My name is Mateusz Lenik, I study at Technical University in Wrocław, Poland. For the past two months, I’ve been working on expanding features of the Ruby DSL for Puppet. I’m a Ruby programmer and I was looking for Google Summer of Code projects in Ruby, which is how I came to Puppet Labs. My project goal is to release a version of the DSL that provides a feature parity between Puppet language and Ruby DSL. I’d also like to create a script that can (at least partially) convert manifests in Puppet language to Ruby DSL.
The previous version of DSL was introduced in 2006, but it was rarely used because it didn’t have some features that exist in the Puppet language. The new version that I’m working on tries to address these issues.
Currently, the new Ruby DSL is functional and allows writing equivalent manifests in Ruby and in Puppet language, but it still lacks some features, like loading types dynamically from Ruby manifests. The syntax has been reworked and it’s more Ruby-esque. It also allows the user to do more than the previous version. In the previous version there was no syntax defined that allowed setting defaults or overrides, referencing resources or supply a block when creating resources.
What’s new?
I’m going to do a small walkthrough of the new features of Ruby DSL. Let’s get started.
Calling functions
Improvements: Puppet functions can be called like regular Ruby functions, there is no need to put arguments into an Array now. Also functions can be called from the top level scope too.
Example:
notice "foo", "bar", "baz" |
Creating resources
Improvements: Block parameters can be specified by passing a block (see example below). When creating multiple resources they no longer need to be put into an Array. Resources can be created in top level scope now too.
Example:
file "test", "foobar", :ensure => :present # OR file "test", "foobar" do |f| f.ensure = :present end |
Creating nodes
Improvements: The name of node can be any object that responds to ‘#to_s’ method or is a ‘Regexp’. Inheritance can be set up using ‘:inherits’ option (see example below).
Example:
node "test" do # ...snip... end node "default", :inherits => "test" do # ...snip... end # OR node /foobar/i do # ...snip... end |
Creating classes
Improvements: There is no need to wrap arguments of a class into ‘Puppet::AST::String’ objects now. A new keyword ‘use’ is introduced to instantiate a class. Parameters can be accessed via ‘params’ method.
Example:
hostclass :baz do notice "baz" end hostclass :foobar, :arguments => {:myparam => 42}, :inherits => :baz do notice params[:myparam] end node "default" do use :foobar, :myparam => 43 end |
Creating definitions
Improvements: Arguments no longer have to be wrapped into ‘Puppet::AST::String’ instances. Arguments can be accessed via ‘params’ method.
Example:
define :foobarbaz, :arguments => {:msg => "Hello!"} do notify "foobarbaz-#{params[:title]}", :message => params[:msg] end foobarbaz "title" Exporting |
Improvements: New variants of supported calls to ‘export’ method. It allows passing a block, resources, resource references and strings.
Example:
export do foo "resource" end Foo.collect # OR foo "resource" export Foo["resource"] Foo.collect # OR export foo "resource" Foo.collect |
Virtualizing
Improvements: New variants of calls to ‘virtual’ method are supported. It allows passing a block, resources, resource references and strings.
Example:
virtual do foo "resource" end Foo.realize # OR foo "resource" virtual Foo["resource"] Foo.realize # OR virtual foo "resource" Foo.realize |
Resource references
Improvements: New syntax, similar to Puppet’s DSL, is added to reference a resource. It strongly depends ‘const_missing’ method. For top level constants :: operator should be used. If the constant is already defined there is a ‘type’ method that does the same thing.
Example:
foo "resource" virtual Foo["resource"] # for Ruby 1.9 File["/tmp/test"] # for Ruby 1.8 when constant is already defined elsewhere type("file")["/tmp/test"] |
Resource overrides
Improvements: New syntax is based on resource references. It supports passing both block and a hash.
Example:
notify "foo" Notify["foo"].override :message => "foobar" # or Notify["foo"].override do |o| o.message = "foobar" end |
Resource defaults
Improvements: Similarly to resource overrides, the new syntax is based on resource references. It also supports passing a block or a hash.
Example:
Notify.defaults :message => "foobar" # or Notify.defaults do |d| d.message = "foobar" end |
‘my’ method
Improvements: Ruby DSL for Puppet strongly depends on ‘method_missing’, so for Ruby 1.9 it uses ‘BasicObject’ and for Ruby 1.8 ‘Object’ with almost all methods undefined. ‘my’ method can be used to access methods from ‘Object’.
Example:
my { puts "Hello" } # => nil and a printout puts "Hello" # => NoMethodError |
Recap
That’s all when it comes to the new features. A lot of things have changed when compared to previous version of the Ruby DSL, and I think the new version addresses some of the issues in the previous version. However, there are still some things that need to be implemented, like the type loader support. The DSL is still not ready for production use. Some parts of it may change.
The current revision of the code can be found on my fork on github in ‘dsl’ branch. Annotated source code can be found in ‘lib/puppet/dsl’ directory of the project. If you have any questions or suggestions, please open an issue on GitHub.
Learn More:
6 Comments
Why Puppet has its own configuration language | Puppet Labs
[...] you’re still skeptical, you can also try out Puppet’s Ruby DSL. In fact, we currently have a Google Summer of Code project focused on this right now. Our community tends to prefer the native language, but the pure Ruby [...]
I am trying to find an equivalent to or <> that exist in the Puppet regular DSL to find virtual or exported resources matching certain tags, name, etc…
How should I write it with the ruby DSL?
How do I write for instance File with the ruby DSL?
When using with Ruby 1.9 you can write ‘File’ just by writing ‘File’. In Ruby DSL referencing types works just like in Puppet DSL. Unfortunately when using Ruby 1.8, File will return ::File, so to reference that type you should use ‘type’ function, like: ‘type(“File”)’. If you want to realize all files, you can write ‘File.realize’, or if you want to reference them by name, you can write ‘File["/foo/bar/baz"].realize’.
To take a concrete example, is there an equivalent to SshAuthorisedKey in the ruby DSL?
Damn it still doesn’t want my code. Here is what I’m trying to find a equivalent for:
SshAuthorisedKey (| tag == “developers” |)
Using parenthesis to work around the blog removing ‘greater than’ and ‘less than’ operators from comments.
Only lookup by name is currently supported, sorry. I’ll try to implement and add it to the pull request when I got some free time.