This page is READ-ONLY. It is generated from the old site.
All timestamps are relative to 2013 (when this page is generated).
If you are looking for TeX support, please go to VietTUG.org

Puppet: Ruby is not Ruby (part 1)

Damn it, Puppet!
Added by ruby 9 months ago  »  Votes: 3/3

Introduction

There are two important things in Puppet: Puppet DSL and Puppet Ruby DSL.

If you don't know iterate over a hash, an array, or you don't know how to support complex exceptions, you may think of Puppet Ruby DSL which allows you to do a bit of Ruby in your recipe.

Unfortunately, Ruby in Puppet Ruby DSL isn't Ruby. In short, Ruby is not Ruby. I will show some some stupid and very stupid things to remember

Default values

In Ruby, you can specify default value for optional parameters easily and quickly. As below

1 # This is pure Ruby code
2 def my_method(param1, param2 = "default")
3   # my stuff
4 end

This easy way is impossible in Puppet Ruby DSL. Actually, there is a bug in Puppet as shown in http://projects.puppetlabs.com/issues/5237 (found about 1 year ago, and not fixed at the time I write this post. I am using Puppet 2.7.12). The following recipe won't work as expected

1 # This is in Puppet recipe
2 define 'my_resource', :uid, :magic => "", :path => "/test" do
3   # my stuff
4 end

When this resource is executed, it will generate very strange error

Puppet::Parser::Compiler failed with error NoMethodError:
   undefined method `safeevaluate' for """":String on node MyNode.com

Puppet::Parser::Compiler failed with error NoMethodError:
   undefined method `safeevaluate' for "/test":String on node MyNode.com

You have to declare your resource like this in a tricky way (see also in this ticket)

1 define 'my_resource', :uid,
2         :magic => Puppet::Parser::AST::Leaf::Undef.new({:value => ''}),
3          :path => Puppet::Parser::AST::Leaf::String.new({:value => '/test'}) do
4   # my stuff
5 end

According to the source code documentation at this, Undef is the undef value, and it's equivalent to nil. I don't think so, as when you don'd specify any value for :magic when invoking the resource my_resource

1 my_resource "testing": uid => 100, path => "/testing" 

then the actual value in your recipe is an empty string (not nil!). I meant, @magic is an String, and it's empty. It is not nil, so you can't make a test like this your definition of the resource

 1 define 'my_resource', :uid,
 2         :magic => Puppet::Parser::AST::Leaf::Undef.new({:value => ''}),
 3          :path => Puppet::Parser::AST::Leaf::String.new({:value => '/test'}) do
 4 
 5   # Purpose: Redefine the magic
 6   #   if user doesn't provide value 'magic' when invoking resource
 7   # The first line is WRONG. It's completely WRONG
 8   #
 9   wrong_real_magic = @magic ? @magic : "magic-not-specified"           # WRONG
10   right_real_magic = @magic.empty? ? "magic-not-specified" : @magic    # RIGHT
11   # other stuff
12 end

In this portion of code, the value of wrong_real_magic is always @magic, even if you don't specify magic when calling the resource. Too bad, right !? WTF?

Now, you may wonder why we don't use nil as default value.

1 define 'my_resource', :uid,
2         :magic => nil, :path => nil do
3   # my stuff
4 end

Oh no. Doing this has the same effect as do-nothing. Puppet will raise an error (on its client)

err: Could not retrieve catalog from remote server:
    Error 400 on SERVER: Must pass magic to My_resource[pi] on node MyNode.com
warning: Not using cache on failed catalog
err: Could not retrieve catalog; skipping run

Ah, you try to do a trick but you've got a sh1t from Puppet. Congratulations!

NB: The problem is still the same if you try

1 define 'my_resource', :uid,
2         :magic => Puppet::Parser::AST::Leaf::Undef.new({:value => nil}) do

Comments

Added by anhhk 3 months ago

Fucking news in Puppet 3

Ruby DSL is Deprecated

The Ruby DSL that was added in Puppet 2.6 (and then largely ignored) is deprecated. A future release of Puppet will introduce a significantly improved Ruby DSL which may break backwards compatibility with the old one.

Added by anhhk 2 months ago

another note : can't use

foobar = a ? b : c ? d: e

Added by anhhk 2 months ago

  executable = true
  executable &&= (on_host ? (on_host.include?(fqdn) or on_host.include?("any")) : false)
  executable &&= (not_on  ? (not (not_on.include?(fqdn) or not_on.include?("any"))) : tru