Maro_QA
Maro_QA

Reputation: 104

Chef notification within each method

I have a recipe that iterates a hash containing SQL scripts in an each method and -- in case the script changed from the previous run -- the cookbook_file resource notifies the execute resource to run. The issue is that it seems it always runs the execute using the last element of the hash.

Following the attributes file

default['sql_scripts_dir'] = 'C:\\DBScripts'
default['script_runner']['scripts'] = [
    { 'name' => 'test', 'hostname' => 'local' },
    { 'name' => 'test2', 'hostname' => 'local' },
    { 'name' => 'test3', 'hostname' => 'local' },
    { 'name' => 'test4', 'hostname' => 'local' },
]

And the recipe

directory node['sql_scripts_dir'] do
  recursive true
end

node['script_runner']['scripts'].each do |script|
    cookbook_file "#{node['sql_scripts_dir']}\\#{script['name']}.sql" do
      source "#{script['name']}.sql"
      action :create
      notifies :run, 'execute[Create_scripts]', :immediately
    end
  
    execute 'Create_scripts' do
      command "sqlcmd -S \"#{script['hostname']}\" -i \"#{node['sql_scripts_dir']}\\#{script['name']}.sql\""
      action :nothing
    end
end
  

And it produces the following output:

Recipe: test_runner::default
         * directory[C:\DBScripts] action create
           - create new directory C:\DBScripts
         * cookbook_file[C:\DBScripts\test.sql] action create
           - create new file C:\DBScripts\test.sql
           - update content in file C:\DBScripts\test.sql from none to 8c40f1
           --- C:\DBScripts\test.sql    2020-07-30 17:30:30.959220400 +0000
           +++ C:\DBScripts/chef-test20200730-1500-11bz3an.sql  2020-07-30 17:30:30.959220400 +0000
           @@ -1 +1,2 @@
           +select @@version
         * execute[Create_scripts] action run

           ================================================================================
           Error executing action `run` on resource 'execute[Create_scripts]'
           ================================================================================

           Mixlib::ShellOut::ShellCommandFailed
           ------------------------------------
           Expected process to exit with [0], but received '1'
           ---- Begin output of sqlcmd -S "local" -i "C:\DBScripts\test4.sql" ----
           STDOUT:
           STDERR: Sqlcmd: 'C:\DBScripts\test4.sql': Invalid filename.
           ---- End output of sqlcmd -S "local" -i "C:\DBScripts\test4.sql" ----
           Ran sqlcmd -S "local" -i "C:\DBScripts\test4.sql" returned 1

The expected behavior is that the recipe runs sequentially the 4 scripts in the example instead of running just the last one. What am I missing for getting it done?

Upvotes: 3

Views: 306

Answers (1)

lamont
lamont

Reputation: 3974

You are creating 4 nearly identical resources all named execute[Create_scripts] and when the notification fires from the first cookbook_file resource being updated it finds the last one of them to be notified and runs against test4 (no matter which cookbook_file resource updates).

The fix is to use string interpolation to change the name of the execute resources to be unique and to notify based on that unique name:

directory node['sql_scripts_dir'] do
  recursive true
end

node['script_runner']['scripts'].each do |script|
    cookbook_file "#{node['sql_scripts_dir']}\\#{script['name']}.sql" do
      source "#{script['name']}.sql"
      action :create
      notifies :run, "execute[create #{script['name']} scripts]", :immediately
    end

    execute "create #{script['name']} scripts" do
      command "sqlcmd -S \"#{script['hostname']}\" -i \"#{node['sql_scripts_dir']}\\#{script['name']}.sql\""
      action :nothing
    end
end

Note that this is a manifestation of the same issues behind the old CHEF-3694 warning message where what would happen is that all the four execute resources would be merged into one resource via "resource cloning" with the properties of the subsequent resource being "last-writer-wins".

In Chef 13 this was changed to remove resource cloning and the warning, and in most circumstances having two resources named the same thing in the resource collection is totally harmless -- until you try to notify one of those resources. The resource notification system should really warn in this situation rather than silently picking the last resource that matches (but between notifications, subscribes, lazy resolution and now unified_mode that code is very complicated and you only want it to be firing under exactly the right conditions).

Upvotes: 2

Related Questions