NoMethodError for LWRP within a ruby_block?

Hiya,

How do I make an LWRP that I created available in a recipe within a ruby_block?
It’s not working for me.

[2013-12-04T00:13:14+00:00] DEBUG: Re-raising exception: NoMethodError - ruby_block[do stuff] (cloner::grants line 30) had an error: NoMethodError: undefined method cloner_db_svc_mysqlacl' for Chef::Resource::RubyBlock /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/resource.rb:294:inmethod_missing’
/var/cache/chef/cookbooks/cloner/recipes/grants.rb:67:in block (2 levels) in from_file' /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/ruby_block.rb:33:incall’
/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/ruby_block.rb:33:in block in action_run' /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/mixin/why_run.rb:52:incall’
/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/mixin/why_run.rb:52:in add_action' /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider.rb:149:inconverge_by’
/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/ruby_block.rb:32:in action_run' /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider.rb:114:inrun_action’
/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/resource.rb:625:in `run_action’

Afaict, the recipe code needs to execute during the convergence phase of the
chef-client run. In my case, the code needs to (1) first start mysql, (2) wait
for mysql to complete its startup (wait for innodb recovery to complete) and (3)
then query a value from a table. (4) And then do another action depending on the
value returned.

I tried doing the above outside a ruby_block my first time through, then
realized #3 was executing before #2 had completed. IOW #3 was executing at the
compile phase. So now I’m trying to put all of this into a ruby_block, but the
call to my LWRP is failing. (Is using a ruby_block for this the right answer?)

The LWRP lives in ‘cloner-db-svc’ cookbook. The cookbook::recipe that calls it
is ‘cloner::grants’. The cloner cookbook depends on cloner-db-svc. When the code
runs outside of a ruby_block, the recipe finds the LWRP method just fine, but
the behavior I need doesn’t occur.

::Chef::Recipe.send(:include, ::Cloner::Chef::DbSvc::Helper)

ruby_block “do stuff” do
block do
quux_ip = search(:node, ‘roles:quux’).map {|server| server[‘ipaddress’] }.join(",")
cli_password = ::Cloner::Chef::DbSvc::Helper.fetchsecret( “secrets”, “#{node[‘cloner’][‘environment’]}”, “rootpass” )
current_ip = %x( mysql --skip-column-names -hlocalhost -uroot -p#{cli_password} -e “SELECT Host FROM mysql.user WHERE User=‘cloner_admin’” )

if current_ip.empty?
  Chef::Log.debug("HIGGS-BOSON: current_ip is empty, applying grants.")
  cloner_admin_pass = ::Cloner::Chef::DbSvc::Helper.fetchsecret( "secrets", "#{node['cloner']['environment']}", "cloner_admin_pass" )
  cloner_proc_pass = ::Cloner::Chef::DbSvc::Helper.fetchsecret( "secrets", "#{node['cloner']['environment']}", "cloner_proc_pass" )

  cloner_db_svc_mysqlacl "mysqlgrants" do
    action :create_acl
    ipv4_address quux_ip
    cli_password cli_password
    cloner_admin_pass cloner_admin_pass
    cloner_proc_pass cloner_proc_pass
  end
  current_ip = %x( mysql --skip-column-names -hlocalhost -uroot -p#{cli_password} -e \"SELECT Host FROM mysql.user WHERE User='cloner_admin'\" )
  Chef::Log.debug("HIGGS-BOSON: After applying mysqlgrants, quux_ip #{quux_ip}; current_ip #{current_ip}")

else # snipped for brevity
end

end
action :create
end

Thanks!
kallen

Hi Callen,

When inside a ruby_block the chef DSL doesn't work. You need to use pure
ruby. Here is an example of how you can execute a resource from a
ruby_block. The cookbook is called 'test' and the lwrp is called 'mine'.

ruby_block 'dd' do
block do
mine = Chef::Resource::TestMine.new('bb', run_context)
mine.run_action(:create)
end
action :create
end

You will need something like the following. The lwrp name is turned into
camel case.

mysqlacl = Chef::Resource::ClonerDbSvcMysqlacl("mysqlgrants", run_context)
mysqlacl.ipv4_address(quux_ip)
Š

mysqlacl.run_action(:create_acl)

Regarding whether its the right thing to do, I try to avoid them if
possible. This is because it is difficult to make assertions about the
actions executed within a ruby_block using chefspec (unit testing for
chef).

Thanks,

Brian

On 04/12/2013 19:06, "kallen@groknaut.net" kallen@groknaut.net wrote:

Hiya,

How do I make an LWRP that I created available in a recipe within a
ruby_block?
It's not working for me.

[2013-12-04T00:13:14+00:00] DEBUG: Re-raising exception: NoMethodError -
ruby_block[do stuff] (cloner::grants line 30) had an error:
NoMethodError: undefined method cloner_db_svc_mysqlacl' for Chef::Resource::RubyBlock /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/resource. rb:294:in method_missing'
/var/cache/chef/cookbooks/cloner/recipes/grants.rb:67:in `block (2
levels) in from_file'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/
ruby_block.rb:33:in `call'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/
ruby_block.rb:33:in `block in action_run'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/mixin/why
_run.rb:52:in `call'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/mixin/why
_run.rb:52:in `add_action'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider.
rb:149:in `converge_by'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/
ruby_block.rb:32:in `action_run'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider.
rb:114:in `run_action'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/resource.
rb:625:in `run_action'
...

Afaict, the recipe code needs to execute during the convergence phase of
the
chef-client run. In my case, the code needs to (1) first start mysql, (2)
wait
for mysql to complete its startup (wait for innodb recovery to complete)
and (3)
then query a value from a table. (4) And then do another action depending
on the
value returned.

I tried doing the above outside a ruby_block my first time through, then
realized #3 was executing before #2 had completed. IOW #3 was executing
at the
compile phase. So now I'm trying to put all of this into a ruby_block,
but the
call to my LWRP is failing. (Is using a ruby_block for this the right
answer?)

The LWRP lives in 'cloner-db-svc' cookbook. The cookbook::recipe that
calls it
is 'cloner::grants'. The cloner cookbook depends on cloner-db-svc. When
the code
runs outside of a ruby_block, the recipe finds the LWRP method just fine,
but
the behavior I need doesn't occur.

::Chef::Recipe.send(:include, ::Cloner::Chef::DbSvc::Helper)

ruby_block "do stuff" do
block do
quux_ip = search(:node, 'roles:quux').map {|server|
server['ipaddress'] }.join(",")
cli_password = ::Cloner::Chef::DbSvc::Helper.fetchsecret( "secrets",
"#{node['cloner']['environment']}", "rootpass" )
current_ip = %x( mysql --skip-column-names -hlocalhost -uroot
-p#{cli_password} -e "SELECT Host FROM mysql.user WHERE
User='cloner_admin'" )

if current_ip.empty?
Chef::Log.debug("HIGGS-BOSON: current_ip is empty, applying
grants.")
cloner_admin_pass = ::Cloner::Chef::DbSvc::Helper.fetchsecret(
"secrets", "#{node['cloner']['environment']}", "cloner_admin_pass" )
cloner_proc_pass = ::Cloner::Chef::DbSvc::Helper.fetchsecret(
"secrets", "#{node['cloner']['environment']}", "cloner_proc_pass" )

 cloner_db_svc_mysqlacl "mysqlgrants" do
   action :create_acl
   ipv4_address quux_ip
   cli_password cli_password
   cloner_admin_pass cloner_admin_pass
   cloner_proc_pass cloner_proc_pass
 end
 current_ip = %x( mysql --skip-column-names -hlocalhost -uroot

-p#{cli_password} -e "SELECT Host FROM mysql.user WHERE
User='cloner_admin'" )
Chef::Log.debug("HIGGS-BOSON: After applying mysqlgrants, quux_ip
#{quux_ip}; current_ip #{current_ip}")

else # snipped for brevity
end
end
action :create
end

Thanks!
kallen

Thanks for the response. I forgot chef DSL isn't available within a ruby_block.
Basically, I'm trying to get my recipe actions executing in the right order,
using the chef tools as I understand them so far. I'll probably try run_context
today.

Given my cookbook, the run order I desire is:

  1. Start mysql
  2. Wait for innodb recovery to complete (i.e. wait for mysql to respond to queries).
  3. Query a value from mysql
  4. Take action on that value using my LWRP

But the run order that actually happened before I tried putting 3 and 4 into
a ruby_block was:

  1. Query a value from mysql (got executed at compile phase)
  2. Take action on that value using my LWRP (got executed at compile phase)
  3. Start mysql (got executed at convergence phase)
  4. Wait for innodb recovery to complete (i.e. wait for mysql to respond to queries). (got executed at convergence phase)

Items executing at compile phase rather than convergence phase is working
against me, I feel. I'll keep trying.

thx,
kallen

On Wed, 04 Dec 2013, Brian Fletcher wrote:

Hi Callen,

When inside a ruby_block the chef DSL doesn't work. You need to use pure
ruby. Here is an example of how you can execute a resource from a
ruby_block. The cookbook is called 'test' and the lwrp is called 'mine'.

ruby_block 'dd' do
block do
mine = Chef::Resource::TestMine.new('bb', run_context)
mine.run_action(:create)
end
action :create
end

You will need something like the following. The lwrp name is turned into
camel case.

mysqlacl = Chef::Resource::ClonerDbSvcMysqlacl("mysqlgrants", run_context)
mysqlacl.ipv4_address(quux_ip)
?

mysqlacl.run_action(:create_acl)

Regarding whether its the right thing to do, I try to avoid them if
possible. This is because it is difficult to make assertions about the
actions executed within a ruby_block using chefspec (unit testing for
chef).

Thanks,

Brian

On 04/12/2013 19:06, "kallen@groknaut.net" kallen@groknaut.net wrote:

Hiya,

How do I make an LWRP that I created available in a recipe within a
ruby_block?
It's not working for me.

[2013-12-04T00:13:14+00:00] DEBUG: Re-raising exception: NoMethodError -
ruby_block[do stuff] (cloner::grants line 30) had an error:
NoMethodError: undefined method cloner_db_svc_mysqlacl' for Chef::Resource::RubyBlock /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/resource. rb:294:in method_missing'
/var/cache/chef/cookbooks/cloner/recipes/grants.rb:67:in `block (2
levels) in from_file'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/
ruby_block.rb:33:in `call'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/
ruby_block.rb:33:in `block in action_run'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/mixin/why
_run.rb:52:in `call'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/mixin/why
_run.rb:52:in `add_action'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider.
rb:149:in `converge_by'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/
ruby_block.rb:32:in `action_run'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider.
rb:114:in `run_action'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/resource.
rb:625:in `run_action'
...

Afaict, the recipe code needs to execute during the convergence phase of
the
chef-client run. In my case, the code needs to (1) first start mysql, (2)
wait
for mysql to complete its startup (wait for innodb recovery to complete)
and (3)
then query a value from a table. (4) And then do another action depending
on the
value returned.

I tried doing the above outside a ruby_block my first time through, then
realized #3 was executing before #2 had completed. IOW #3 was executing
at the
compile phase. So now I'm trying to put all of this into a ruby_block,
but the
call to my LWRP is failing. (Is using a ruby_block for this the right
answer?)

The LWRP lives in 'cloner-db-svc' cookbook. The cookbook::recipe that
calls it
is 'cloner::grants'. The cloner cookbook depends on cloner-db-svc. When
the code
runs outside of a ruby_block, the recipe finds the LWRP method just fine,
but
the behavior I need doesn't occur.

::Chef::Recipe.send(:include, ::Cloner::Chef::DbSvc::Helper)

ruby_block "do stuff" do
block do
quux_ip = search(:node, 'roles:quux').map {|server|
server['ipaddress'] }.join(",")
cli_password = ::Cloner::Chef::DbSvc::Helper.fetchsecret( "secrets",
"#{node['cloner']['environment']}", "rootpass" )
current_ip = %x( mysql --skip-column-names -hlocalhost -uroot
-p#{cli_password} -e "SELECT Host FROM mysql.user WHERE
User='cloner_admin'" )

if current_ip.empty?
Chef::Log.debug("HIGGS-BOSON: current_ip is empty, applying
grants.")
cloner_admin_pass = ::Cloner::Chef::DbSvc::Helper.fetchsecret(
"secrets", "#{node['cloner']['environment']}", "cloner_admin_pass" )
cloner_proc_pass = ::Cloner::Chef::DbSvc::Helper.fetchsecret(
"secrets", "#{node['cloner']['environment']}", "cloner_proc_pass" )

 cloner_db_svc_mysqlacl "mysqlgrants" do
   action :create_acl
   ipv4_address quux_ip
   cli_password cli_password
   cloner_admin_pass cloner_admin_pass
   cloner_proc_pass cloner_proc_pass
 end
 current_ip = %x( mysql --skip-column-names -hlocalhost -uroot

-p#{cli_password} -e "SELECT Host FROM mysql.user WHERE
User='cloner_admin'" )
Chef::Log.debug("HIGGS-BOSON: After applying mysqlgrants, quux_ip
#{quux_ip}; current_ip #{current_ip}")

else # snipped for brevity
end
end
action :create
end

Thanks!
kallen

If your able to now get access to your LWRP and hitting the problem of things not running in
the converge, have a look at converge_by
http://docs.opscode.com/lwrp_custom_provider_ruby.html#converge-by

Here's an example from my app cookbook How to use converge_by in Chef LWRP's. This is from an app cookbook I forked from the main opscode application cookbook but stripped way down. · GitHub

On Thursday, December 5, 2013 at 2:17 PM, kallen@groknaut.net wrote:

Thanks for the response. I forgot chef DSL isn't available within a ruby_block.
Basically, I'm trying to get my recipe actions executing in the right order,
using the chef tools as I understand them so far. I'll probably try run_context
today.

Given my cookbook, the run order I desire is:

  1. Start mysql
  2. Wait for innodb recovery to complete (i.e. wait for mysql to respond to queries).
  3. Query a value from mysql
  4. Take action on that value using my LWRP

But the run order that actually happened before I tried putting 3 and 4 into
a ruby_block was:

  1. Query a value from mysql (got executed at compile phase)
  2. Take action on that value using my LWRP (got executed at compile phase)
  3. Start mysql (got executed at convergence phase)
  4. Wait for innodb recovery to complete (i.e. wait for mysql to respond to queries). (got executed at convergence phase)

Items executing at compile phase rather than convergence phase is working
against me, I feel. I'll keep trying.

thx,
kallen

On Wed, 04 Dec 2013, Brian Fletcher wrote:

Hi Callen,

When inside a ruby_block the chef DSL doesn't work. You need to use pure
ruby. Here is an example of how you can execute a resource from a
ruby_block. The cookbook is called 'test' and the lwrp is called 'mine'.

ruby_block 'dd' do
block do
mine = Chef::Resource::TestMine.new('bb', run_context)
mine.run_action(:create)
end
action :create
end

You will need something like the following. The lwrp name is turned into
camel case.

mysqlacl = Chef::Resource::ClonerDbSvcMysqlacl("mysqlgrants", run_context)
mysqlacl.ipv4_address(quux_ip)
?

mysqlacl.run_action(:create_acl)

Regarding whether its the right thing to do, I try to avoid them if
possible. This is because it is difficult to make assertions about the
actions executed within a ruby_block using chefspec (unit testing for
chef).

Thanks,

Brian

On 04/12/2013 19:06, "kallen@groknaut.net (mailto:kallen@groknaut.net)" <kallen@groknaut.net (mailto:kallen@groknaut.net)> wrote:

Hiya,

How do I make an LWRP that I created available in a recipe within a
ruby_block?
It's not working for me.

[2013-12-04T00:13:14+00:00] DEBUG: Re-raising exception: NoMethodError -
ruby_block[do stuff] (cloner::grants line 30) had an error:
NoMethodError: undefined method cloner_db_svc_mysqlacl' for Chef::Resource::RubyBlock /opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/resource. rb:294:in method_missing'
/var/cache/chef/cookbooks/cloner/recipes/grants.rb:67:in `block (2
levels) in from_file'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/
ruby_block.rb:33:in `call'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/
ruby_block.rb:33:in `block in action_run'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/mixin/why
_run.rb:52:in `call'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/mixin/why
_run.rb:52:in `add_action'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider.
rb:149:in `converge_by'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider/
ruby_block.rb:32:in `action_run'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/provider.
rb:114:in `run_action'

/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.2/lib/chef/resource.
rb:625:in `run_action'
...

Afaict, the recipe code needs to execute during the convergence phase of
the
chef-client run. In my case, the code needs to (1) first start mysql, (2)
wait
for mysql to complete its startup (wait for innodb recovery to complete)
and (3)
then query a value from a table. (4) And then do another action depending
on the
value returned.

I tried doing the above outside a ruby_block my first time through, then
realized #3 was executing before #2 had completed. IOW #3 was executing
at the
compile phase. So now I'm trying to put all of this into a ruby_block,
but the
call to my LWRP is failing. (Is using a ruby_block for this the right
answer?)

The LWRP lives in 'cloner-db-svc' cookbook. The cookbook::recipe that
calls it
is 'cloner::grants'. The cloner cookbook depends on cloner-db-svc. When
the code
runs outside of a ruby_block, the recipe finds the LWRP method just fine,
but
the behavior I need doesn't occur.

::Chef::Recipe.send(:include, ::Cloner::Chef::DbSvc::Helper)

ruby_block "do stuff" do
block do
quux_ip = search(:node, 'roles:quux').map {|server|
server['ipaddress'] }.join(",")
cli_password = ::Cloner::Chef::DbSvc::Helper.fetchsecret( "secrets",
"#{node['cloner']['environment']}", "rootpass" )
current_ip = %x( mysql --skip-column-names -hlocalhost -uroot
-p#{cli_password} -e "SELECT Host FROM mysql.user WHERE
User='cloner_admin'" )

if current_ip.empty?
Chef::Log.debug("HIGGS-BOSON: current_ip is empty, applying
grants.")
cloner_admin_pass = ::Cloner::Chef::DbSvc::Helper.fetchsecret(
"secrets", "#{node['cloner']['environment']}", "cloner_admin_pass" )
cloner_proc_pass = ::Cloner::Chef::DbSvc::Helper.fetchsecret(
"secrets", "#{node['cloner']['environment']}", "cloner_proc_pass" )

cloner_db_svc_mysqlacl "mysqlgrants" do
action :create_acl
ipv4_address quux_ip
cli_password cli_password
cloner_admin_pass cloner_admin_pass
cloner_proc_pass cloner_proc_pass
end
current_ip = %x( mysql --skip-column-names -hlocalhost -uroot
-p#{cli_password} -e "SELECT Host FROM mysql.user WHERE
User='cloner_admin'" )
Chef::Log.debug("HIGGS-BOSON: After applying mysqlgrants, quux_ip
#{quux_ip}; current_ip #{current_ip}")

else # snipped for brevity
end
end
action :create
end

Thanks!
kallen