poise / poise-derived

A Chef cookbook for defining lazily evaluated node attributes.
Apache License 2.0
22 stars 5 forks source link

Chef 13 recipe compile error. #5

Open cpjones01 opened 7 years ago

cpjones01 commented 7 years ago

Using lazy attribute with Chef 13.2.20 causes recipe compilation errors, e.g.

================================================================================
       Recipe Compile Error in /tmp/kitchen/cache/cookbooks/radian_chef_automate/recipes/default.rb
       ================================================================================

       RuntimeError
       ------------
       can't modify frozen PoiseDerived::LazyAttribute

       Cookbook Trace:
       ---------------
         /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:111:in `_evaluate'
         /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:65:in `method_missing'
         /tmp/kitchen/cache/cookbooks/radian_chef_automate/recipes/default.rb:9:in `from_file'

       Relevant File Content:
       ----------------------
       /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:

       104:      private
       105:
       106:      # Evaluate the lazy attribute.
       107:      #
       108:      # @return [Object]
       109:      def _evaluate
       110:        return @evaluate_cache if defined?(@evaluate_cache)
       111>>       @evaluate_cache = if @value.is_a?(Proc)
       112:          # Block mode, just run the block.
       113:          @node.instance_eval(&@value).to_s
       114:        else
       115:          # String mode, parse the template string and fill it in.
       116:          format_keys = @value.scan(FORMAT_STRING_REGEXP).inject({}) do |memo, (key)|
       117:            # Keys must be symbols because Ruby.
       118:            memo[key.to_sym] = key.split(/\./).inject(@node) {|n, k| n.nil? ? n : n[k] }
       119:            memo
       120:          end

Attribute code for repro:

base_pkg_url = lazy 'https://packages.chef.io/files/stable/automate/%{radian_chef_automate.version}'

case node['platform_family']
when 'rhel'
  if node['platform_version'] =~ /7\..*/
    default['radian_chef_automate']['pkg_url'] = lazy {
      "#{base_pkg_url}/el/7/automate-#{node['radian_chef_automate']['version']}-1.el7.x86_64.rpm"
    }
  elsif node['platform_version'] =~ /6\..*/
    default['radian_chef_automate']['pkg_url'] = lazy {
      "#{base_pkg_url}/el/6/automate-#{node['radian_chef_automate']['version']}-1.el6.x86_64.rpm"
    }
  else
  end
when 'amazon'
  default['radian_chef_automate']['pkg_url'] = lazy {
      "#{base_pkg_url}/el/6/automate-#{node['radian_chef_automate']['version']}-1.el6.x86_64.rpm"
  }
end
rjhornsby commented 7 years ago

Running into what seems like the same issue trying to use a template.

The source attributes are

default['sso']['hostname'] = 'www.example.org'
default['sso']['openam']['server_port'] = 8080

and the derived attribute is written in the cookbook's attributes file as

default['sso']['openam']['web']['uri'] = lazy 'http://%{sso.hostname}:%{sso.openam.server_port}'

Chef error output (truncated for brevity):

================================================================================
Error executing action `create` on resource 'template[/opt/tomcat/conf/openam_config.properties]'
================================================================================

Chef::Mixin::Template::TemplateError
------------------------------------
can't modify frozen PoiseDerived::LazyAttribute

Resource Declaration:
---------------------
# In /tmp/kitchen/cache/cookbooks/sso/recipes/opensso.rb

 9: template "#{node['sso']['tomcat']['path']}/conf/openam_config.properties" do
      ...
13:   variables(
      ...
22:     uri:                      node['sso']['openam']['web']['uri']
23:   )
24:   sensitive false
25: end
26:

Compiled Resource:
------------------
# Declared in /tmp/kitchen/cache/cookbooks/sso/recipes/opensso.rb:9:in `from_file'

template("/opt/tomcat/conf/openam_config.properties") do
 action [:create]
 default_guard_interpreter :default
 source "openam_config.properties.erb"
 variables {
    ...
    :uri=>#<PoiseDerived::LazyAttribute @value="http://%{sso.hostname}:%{sso.openam.server_port}">
 }
 declared_type :template
 cookbook_name "sso"
 recipe_name "opensso"
 ...
 path "/opt/tomcat/conf/openam_config.properties"
 verifications []
end

Template Context:
-----------------
on line #21
19: SERVER_HOST = <%= @hostname %>
20: SERVER_PORT = <%= @http_port %>
21: SERVER_URI = <%= @uri -%>/openam/config/wizard/wizard.htm
22: SERVER_URL = <%= @uri %>
23:

System Info:
------------
chef_version=13.1.31
platform=centos
platform_version=6.6
ruby=ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]
program_name=chef-client worker: ppid=8928;start=17:10:26;
executable=/opt/chef/bin/chef-client
nicutor commented 6 years ago

Got the same error. To reproduce:

# test_parent_cookbook/attributes/default.rb
default['test_child_cookbook']['work_dir'] = 'C:\WORK'

# test_parent_cookbook/recipes/default.rb
include_recipe 'test_child_cookbook'

# test_child_cookbook/attributes/default.rb
default['test_child_cookbook']['work_dir'] = 'C:\TMP'
default['test_child_cookbook']['home'] = lazy "%{test_child_cookbook.work_dir}\\somefoldername"

# test_child_cookbook/recipes/default.rb
puts "HOME dir is: #{node['test_child_cookbook']['home']}"

Error:

================================================================================
Recipe Compile Error in /tmp/kitchen/cache/cookbooks/test_parent_cookbook/recipes/default.rb
================================================================================

RuntimeError
------------
can't modify frozen PoiseDerived::LazyAttribute

Cookbook Trace:
---------------
  /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:111:in `_evaluate'
  /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:77:in `to_s'
  /tmp/kitchen/cache/cookbooks/test_child_cookbook/recipes/default.rb:7:in `from_file'
  /tmp/kitchen/cache/cookbooks/test_parent_cookbook/recipes/default.rb:7:in `from_file'

Relevant File Content:
----------------------
/tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:

104:      private
105:
106:      # Evaluate the lazy attribute.
107:      #
108:      # @return [Object]
109:      def _evaluate
110:        return @evaluate_cache if defined?(@evaluate_cache)
111>>       @evaluate_cache = if @value.is_a?(Proc)
112:          # Block mode, just run the block.
113:          @node.instance_eval(&@value).to_s
114:        else
115:          # String mode, parse the template string and fill it in.
116:          format_keys = @value.scan(FORMAT_STRING_REGEXP).inject({}) do |memo, (key)|
117:            # Keys must be symbols because Ruby.
118:            memo[key.to_sym] = key.split(/\./).inject(@node) {|n, k| n.nil? ? n : n[k] }
119:            memo
120:          end

System Info:
------------
chef_version=13.6.0
platform=centos
platform_version=7.3.1611
ruby=ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-linux]
program_name=chef-client worker: ppid=4089;start=18:22:08;
executable=/opt/chef/bin/chef-client
jeffbyrnes commented 6 years ago

I’m running into the same issue, I think, on Chef 14.2.0:

Relevant code:

# attributes/default.rb
default['ds_maps']['app_dir'] = '/opt/maps'
default['ds_maps']['config_dir'] = lazy '%{ds_maps.app_dir}/config'
default['ds_maps']['config_file'] = lazy "%{ds_maps.config_dir}/local-#{node.chef_environment}.json"

# recipes/default.rb
git node['ds_maps']['app_dir'] do
  # [snip]
  notifies :create, "file[#{node['ds_maps']['config_file']}]", :immediately

Error output:

================================================================================
  Recipe Compile Error in /tmp/kitchen/cache/cookbooks/ds_maps/recipes/default.rb
  ================================================================================

  FrozenError
  -----------
  can't modify frozen PoiseDerived::LazyAttribute

  Cookbook Trace:
  ---------------
    /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:111:in `_evaluate'
    /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:77:in `to_s'
    /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:121:in `%'
    /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:121:in `_evaluate'
    /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:77:in `to_s'
    /tmp/kitchen/cache/cookbooks/ds_maps/recipes/default.rb:55:in `block in from_file'
    /tmp/kitchen/cache/cookbooks/ds_maps/recipes/default.rb:50:in `from_file'

  Relevant File Content:
  ----------------------
  /tmp/kitchen/cache/cookbooks/poise-derived/files/halite_gem/poise_derived/lazy_attribute.rb:

  104:      private
  105:
  106:      # Evaluate the lazy attribute.
  107:      #
  108:      # @return [Object]
  109:      def _evaluate
  110:        return @evaluate_cache if defined?(@evaluate_cache)
  111>>       @evaluate_cache = if @value.is_a?(Proc)
  112:          # Block mode, just run the block.
  113:          @node.instance_eval(&@value).to_s
  114:        else
  115:          # String mode, parse the template string and fill it in.
  116:          format_keys = @value.scan(FORMAT_STRING_REGEXP).inject({}) do |memo, (key)|
  117:            # Keys must be symbols because Ruby.
  118:            memo[key.to_sym] = key.split(/\./).inject(@node) {|n, k| n.nil? ? n : n[k] }
  119:            memo
  120:          end

  Additional information:
  -----------------------
 Ruby objects are often frozen to prevent further modifications
 when they would negatively impact the process (e.g. values inside
 Ruby's ENV class) or to prevent polluting other objects when default
 values are passed by reference to many instances of an object (e.g.
 the empty Array as a Chef resource default, passed by reference
 to every instance of the resource).

 Chef uses Object#freeze to ensure the default values of properties
 inside Chef resources are not modified, so that when a new instance
 of a Chef resource is created, and Object#dup copies values by
 reference, the new resource is not receiving a default value that
 has been by a previous instance of that resource.

 Instead of modifying an object that contains a default value for all
 instances of a Chef resource, create a new object and assign it to
 the resource's parameter, e.g.:

 fruit_basket = resource(:fruit_basket, 'default')

 # BAD: modifies 'contents' object for all new fruit_basket instances
 fruit_basket.contents << 'apple'

 # GOOD: allocates new array only owned by this fruit_basket instance
 fruit_basket.contents %w(apple)

  System Info:
  ------------
  chef_version=14.2.0
  platform=ubuntu
  platform_version=16.04
  ruby=ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
  program_name=/opt/chef/bin/chef-client
  executable=/opt/chef/bin/chef-client

  Running handlers:
[2018-06-27T13:48:54+00:00] ERROR: Running exception handlers
  Running handlers complete
[2018-06-27T13:48:54+00:00] ERROR: Exception handlers complete
  Chef Client failed. 5 resources updated in 27 seconds
[2018-06-27T13:48:54+00:00] FATAL: Stacktrace dumped to /tmp/kitchen/cache/chef-stacktrace.out
[2018-06-27T13:48:54+00:00] FATAL: Please provide the contents of the stacktrace.out file if you file a bug report
[2018-06-27T13:48:54+00:00] FATAL: FrozenError: can't modify frozen PoiseDerived::LazyAttribute
jeffbyrnes commented 6 years ago

By the way, as of ba76bc8, at least, this appears to be fixed. @coderanger, any chance of shipping a new version?

gordonbondon commented 5 years ago

I would be really nice to get a release. Currently we host local version of this cookbook.

geekgogie commented 5 years ago

By the way, as of ba76bc8, at least, this appears to be fixed. @coderanger, any chance of shipping a new version?

I am not sure but it seems still an issue:

node3
node3
node3 Running handlers:
node3 [2019-05-14T03:40:36+00:00] ERROR: Running exception handlers
node3 Running handlers complete
node3 [2019-05-14T03:40:36+00:00] ERROR: Exception handlers complete
node3 Chef Client failed. 11 resources updated in 09 seconds
node3 [2019-05-14T03:40:36+00:00] FATAL: Stacktrace dumped to /var/chef/cache/chef-stacktrace.out
node3 [2019-05-14T03:40:36+00:00] FATAL: Please provide the contents of the stacktrace.out file if you file a bug report
node3 [2019-05-14T03:40:36+00:00] FATAL: FrozenError: can't modify frozen PoiseDerived::LazyAttribute
[vagrant@node2 lamp_stack]$ chef-client --version
Chef: 14.8.12

In fact, I have copies this file https://github.com/poise/poise-derived/blob/ba76bc88d61183f450b1bf8b10625920d2ff99e5/lib/poise_derived/core_ext/deep_merge.rb i.e.

[vagrant@node2 poise-derived]$ pwd
/home/vagrant/chef-repo/cookbooks/poise-derived
[vagrant@node2 poise-derived]$ ls ./files/halite_gem/poise_derived/core_ext/deep_merge.rb 
./files/halite_gem/poise_derived/core_ext/deep_merge.rb

and updated it as well but as you see, still not fix the issue at least on 14.8.12. Although, the result of the string in my defaults.rb used is correct which is using yum_repository and it provides the correct string results.