简体   繁体   中英

Setting Chef variable via a ruby_block not being executed

I'm trying to implement a system where, because applications need the same user ID across multiple nodes, we do a lookup on a small mysql table which has the application name and corresponding ID stored in an autoincrememnting table. If the user ID doesn't already exist, we create it and then return it. Because of issues I've encountered with the 'database' cookbook, I decided to implement the Mysql query in a bash script which returns the UID to Stdout and have Chef use that in the user creation. As an argument to the Mysql script, we feed in the node attribute for the apllication name that we're releasing and this is the entry searched for in the DB.

The issue I'm having is that I'm attempting to set the variable via a ruby_block and this doesn't seem to be being processed. The Chef log shows that the variable isn't being set and when I've removed the mysql script from the equation completely, it still isn't being set. As I understand it, it's fairly simple syntax so can see why this is failing. Here's the code:

define :create_application_ids do

  # Assign variable names to the parameters passed.
  application = params[:application_name]

  # Set the fail flag to zero.
  node.default[:deploy][application][:fail] = 0

  # Deploy our mysql query script to get (or create, if required) a user ID for the application
  template "query_mysql.bash" do
        path "/root/query_mysql.bash"
        source "query_mysql.bash.erb"
        owner "root"
        group "root"
        mode "0750"
  end

  ruby_block "set_app_id" do
  block do
     id = `/root/query_mysql.bash #{node[:deploy][application]}`
  end
  action :create
  end

  Chef::Log.info "MY-DEPLOY: The ID variable is #{id}"

  # If a user and group ID has been specified in the JSON string for the
  # application, then create them both.

  if (defined?(id))
    directory node[:deploy][application][:home_dir] do
      owner "root"
      group "root"
      mode 0777
      recursive true
      action :create
    end

    group "#{node[:deploy][application][:group_name]}" do
      gid id
      only_if { `cat /etc/group | grep -E '^#{node[:deploy][application][:group_name]}:'` }
    end

    user "#{node[:deploy][application][:user_name]}" do
      uid id
      gid node[:deploy][application][:group_name]
      home "#{node[:deploy][application][:home_dir]}"
      shell "/sbin/nologin"
      only_if { `cat /etc/passwd | grep -E '^#{node[:deploy][application][:user_name]}:'` }
    end

    # Create any standard directories for the user.
    create_application_dirs do
      application_name application
    end

  else

        Chef::Log.info "MY: #{node[:deploy][application]}"
    # Since this failed, set the fail flag to 1.
    node.default[:deploy][application][:fail] = 1

    log "message" do
      message "MY-DEPLOY: Cannot deploy application without unique user and group ID"
      level :fatal
    end

  end

end

and I'm seeing this in the Chef output:

Recipe Compile Error in /var/lib/aws/opsworks/cache.stage2/cookbooks/my-deploy/recipes/repository.rb
================================================================================


NameError
---------
No resource, method, or local variable named `id' for `Chef::Recipe "repository"'


Cookbook Trace:
---------------
/var/lib/aws/opsworks/cache.stage2/cookbooks/my-deploy/definitions/create_application_ids.rb:25:in `block in from_file'
/var/lib/aws/opsworks/cache.stage2/cookbooks/my-deploy/recipes/repository.rb:15:in `block in from_file'
/var/lib/aws/opsworks/cache.stage2/cookbooks/my-deploy/recipes/repository.rb:2:in `each'
/var/lib/aws/opsworks/cache.stage2/cookbooks/my-deploy/recipes/repository.rb:2:in `from_file'


Relevant File Content:
----------------------
/var/lib/aws/opsworks/cache.stage2/cookbooks/my-deploy/definitions/create_application_ids.rb:

18:    ruby_block "set_app_id" do
19:    block do
20:       id = `/root/query_mysql.bash #{node[:deploy][application]}`
21:    end
22:    action :create
23:    end
24: 
25>>   Chef::Log.info "MY-DEPLOY: The ID variable is #{id}"
26: 
27:    # If a user and group ID has been specified in the JSON string for the
28:    # application, then create them both.
29: 
30:    if (defined?(id))
31:      directory node[:deploy][application][:home_dir] do
32:        owner "root"
33:        group "root"
34:        mode 0777



[2014-10-08T00:55:49+11:00] ERROR: Running exception handlers
[2014-10-08T00:55:49+11:00] ERROR: Exception handlers complete
[2014-10-08T00:55:49+11:00] FATAL: Stacktrace dumped to /var/lib/aws/opsworks/cache.stage2/chef-stacktrace.out
[2014-10-08T00:55:50+11:00] ERROR: No resource, method, or local variable named `id' for `Chef::Recipe "repository"'
[2014-10-08T00:55:50+11:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)

You're hitting the two phases of a chef run problem. See the documentation about it here

First pass compile recipes/definitions and make a 'stack' of resources.

Lwrp would solve your problem by being evaluated when the resource is called (at converge time) with correct params.

Here your definition is evaluated, creating a resource of type ruby_block which will be executed at converge time.

Just after defining this resource you test if id is defined, but your ruby_block has not be run at this time.

You have to change your code, stripping the if (defined?(id)) . The resources you use below are already idempotent (no need for the only_if in user and group resources).

However you'll have to use the lazy evaluation for id in thoose resources to avoid having them defined with nil in place of your id.

ie

group "#{node[:deploy][application][:group_name]}" do
  gid lazy { id }
  only_if { `cat /etc/group | grep -E '^#{node[:deploy][application][:group_name]}:'` }
end

If you really want to catch errors and print a custom message you may use a begin/rescue block, as a group or user resource with nil id would throw an exception IIRC.

Tensibai's answer is excellent, and I'd suggest you accept it. That said, my personal flavor would be to take a slightly different approach.

One of the great benefits of Chef-server is the ability to search. Therefore, I would put the id in a node attribute, rather than a standalone attribute, and then use that with lazy evaluation .

define :create_application_ids do

  # Assign variable names to the parameters passed.
  application = params[:application_name]

  # Set the fail flag to zero.
  node.default[:deploy][application][:fail] = 0

  # Deploy our mysql query script to get (or create, if required) a user ID for the application
  template "query_mysql.bash" do
        path "/root/query_mysql.bash"
        source "query_mysql.bash.erb"
        owner "root"
        group "root"
        mode "0750"
  end

  ruby_block "set_app_id" do
    block do
       node[application][:id] = `/root/query_mysql.bash #{node[:deploy][application]}`
    end
    action :create
    not_if node[application] && node[application][:id]
  end

  Chef::Log.info "MY-DEPLOY: The ID variable is #{id}"

  # If a user and group ID has been specified in the JSON string for the
  # application, then create them both.

  directory node[:deploy][application][:home_dir] do
    owner "root"
    group "root"
    mode 0777
    recursive true
    action :create
  end

  group "#{node[:deploy][application][:group_name]}" do
    gid lazy{ node[application][:id]  }
    only_if { `cat /etc/group | grep -E '^#{node[:deploy][application][:group_name]}:'` }
  end

  user "#{node[:deploy][application][:user_name]}" do
    uid lazy { node[application][:id] }
    gid node[:deploy][application][:group_name]
    home "#{node[:deploy][application][:home_dir]}"
    shell "/sbin/nologin"
    only_if { `cat /etc/passwd | grep -E '^#{node[:deploy][application][:user_name]}:'` }
  end

  # Create any standard directories for the user.
  create_application_dirs do
    application_name application
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM