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.