Search code examples
rubypuppet

Using Ruby to parse and write Puppet node definitions


I am writing a helper API in Ruby to automatically create and manipulate node definitions. My code is working; it can read and write the node defs successfully, however, it is a bit clunky.

Ruby is not my main language, so I'm sure there is a cleaner, and more rubyesque solution. I would appreciate some advice or suggestions.

Each host has its own file in manifests/nodes containing just the node definition. e.g.

node 'testnode' {
  class {'firstclass': }
  class {'secondclass': enabled => false }
}

The classes all are either enabled (default) or disabled elements. In the Ruby code, I store these as an instance variable hash @elements.

The read method looks like this:

  def read()
    data = File.readlines(@filepath)
    for line in data do
      if line.include? 'class'
        element = line[/.*\{'([^\']*)':/, 1]
        if @elements.include? element.to_sym
          if not line.include? 'enabled => false'
            @elements[element.to_sym] = true
          else
            @elements[element.to_sym] = false
          end
        end
      end
    end
  end

And the write method looks like this:

  def write()
    data = "node #{@hostname} {\n"
    for element in @elements do
      if element[1]
        line = "    class {'#{element[0]}': }\n"
      else
        line = "    class {'#{element[0]}': enabled => false}\n"
      end

      data += line
    end

    data += "}\n"

    file = File.open(@filepath, 'w')
    file.write(data)
    file.close()
  end

One thing to add is that these systems will be isolated from the internet. So I'd prefer to avoid large number of dependency libraries as I'll need to install / maintain them manually.


Solution

  • Ruby has some pretty good methods to iterate over data structures. See below for an example of how to rubify your code a little bit. I am by no means an expert on the subject, and have not tested the code. :)

    def read
      data = File.readlines(@filepath)
      data.each_line do |line|
        element = line[/.*\{'([^\']*)':/, 1].to_sym
        if @elements.include?(element)
          @elements[element] = line.include?('enabled => false') ? false : true
        end
      end
    end
    
    def write
      File.open(@filepath, 'w') do |file|
        file.puts "node #{@hostname} {"
        @elements.each do |element|
          if element[1]
            file.puts "    class {'#{element[0]}': }"
          else
            file.puts "    class {'#{element[0]}': enabled => false }"
          end
        end
        file.puts '}'
      end
    end
    

    Hope this points you in the right direction.