Search code examples
rubychef-infralwrp

variable scope in chef LWRPs


I have the following problem and I think its because I do not understand the variable scopes in Chef LWRPs.

You can look at the cookbook at https://github.com/jkleinlercher/chef_problems and simply test the behaviour with kitchen.

Even though I define the following very similar resources, I recognized, that the attribute 'colors' inherits from the previous defined resources. The attribute 'colors' is defaulted to "['blue']" and in the provider the element 'magenta' is added to the array. However, the second and third resource inherit the whole array from the previous resource. That is quite weird to me ...

Resource definition in recipes/default.rb:

chef_problems_problem1 "test1" do
     address "myaddress1"
     action :install
end

chef_problems_problem1 "test2" do
     address "myaddress2"
     action :install
end

chef_problems_problem1 "test3" do
    address "myaddress3"
    action :install
end

In the output of the kitchen converge you see that the variable new_resource.colors inherits the values of the previous resources:

     * chef_problems_problem1[test1] action install
   new_resource.colors at the beginning: ["blue"]

   Values of local variables:
   address: myaddress1
   colors: ["blue"]

   adding magenta to local variable colors

   colors after adding magenta: ["blue", "magenta"]
   new_resource.colors at the end: ["blue", "magenta"]


     * chef_problems_problem1[test2] action install
   new_resource.colors at the beginning: ["blue", "magenta"]

   Values of local variables:
   address: myaddress2
   colors: ["blue", "magenta"]

   adding magenta to local variable colors

   colors after adding magenta: ["blue", "magenta", "magenta"]
   new_resource.colors at the end: ["blue", "magenta", "magenta"]


     * chef_problems_problem1[test3] action install
   new_resource.colors at the beginning: ["blue", "magenta", "magenta"]

   Values of local variables:
   address: myaddress3
   colors: ["blue", "magenta", "magenta"]

   adding magenta to local variable colors

   colors after adding magenta: ["blue", "magenta", "magenta", "magenta"]
   new_resource.colors at the end: ["blue", "magenta", "magenta", "magenta"]

Maybe you can help me to find where the problem is here.


Solution

  • It would appear that the default value is passed to each new resource, but not cloned for each new resource. As a result, the array (not it's contents) is the default value. If you add things to this array, it would be expected that each provider using the default attribute value would have the full array.

    Personally, it think that's somewhat unexpected behaviour. I would think you would pass each new resource a clone of the default, but that doesn't appear to be the case here. Now, if you passed a new array to the color attribute in your resource definition, then you would not see the same effect.

    This is not, however, a scope issue. It's a matter of how the default gets passed to each new instance of the resource.

    Update from Johannes:

    It turned out that this problem was already discussed in https://tickets.opscode.com/browse/CHEF-4608 but not fixed. You can use the workaround described in this ticket or simply create a new array in the provider action like

    colors = Array.new(new_resource.colors)
    

    and then work with the new array and don't touch the original attribute.