Ruby DSL

Starting in Puppet 2.6.x, manifests can be written in Ruby.

Motivations:

  1. Developers can generate puppet resources without having to learn the Puppet DSL.
  2. Possible to overcome any limitations of the Puppet DSL.
  3. Ruby already has well known unit testing frameworks.

Methods:

Puppet's Ruby DSL introduces two types of methods:

Methods that create scopes:

  • define
    • 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.

  • hostclass
    • Creates a class.



      hostclass :fooclass, :foo do
      
      end
      


  • node
    • 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

  • Resources are create by calling a method with the same name as the resource.



    #/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
    


  • Comments

    Sheldon Hearn

    Sheldon Hearn

    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.

    malclocke

    malclocke

    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.

    Darren

    Darren

    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?

    Martijn G.

    Martijn G.

    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.

    Tom

    Tom

    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.

    James Turnbull

    James Turnbull

    Tom - you could easily write a Puppet define or a function to do the same thing.

    Muppet Pastor

    Muppet Pastor

    Does not appear the comment posted posted properly. The code within the inline_template was ignored.

    Muppet Pastor

    Muppet Pastor

    # For masterless this works.
    define mkdir_p($path="") {
    if $path == "" {
    $_path = $title
    } else {
    $_path = $path
    }
    file { $_path:
    ensure => directory,
    content => inline_template(""),
    }
    }

    mkdir_p {'/tmp/1/2/3/4/5':
    path => '/tmp/5/4/3/2/1',
    }
    mkdir_p {'/tmp/A/B/C': }

    Leave a comment