Problems with chef recipe - ruby

Sorry about my English.
Here is a ./recipes/default.rb
node['client-name'].each do |crontab|
item = data_bag_item('bag_name', "#{crontab}")
node.default[:client_timezone] = "#{item['timezone']}"
node.default[:client_name] = "#{crontab}"
template "crontab" do
path "/etc/cron.d/#{deploy}"
source "default.erb"
owner "root"
group "root"
mode "0644"
end
end
The ./attributes/default.rb looks like this:
default['version'] = "1.0.0"
default['client-name'] = ['company_1','company_2']
The templates/crontab.erb looks like this:
30 04 * * * java -Duser.timezone=<%= node[:timezone] %> -jar /var/www/app-<%= node[:version] %>.jar /var/www/<%= node[:client_name] %>/config/spring/job.xml
My recipe puts two similar crontab files (company_1 and company_2) to /etc/cron.d/, but only for the last one attributes' value (company_2). Can you please tell me, where am i wrong?

You need to use the variables section of the template resource to assign the client_name and the timezone explicitly. The recipe might look like this:
node['client-name'].each do |client_name|
item = data_bag_item('bag_name', "#{client_name}")
template "crontab" do
path "/etc/cron.d/#{deploy}"
source "default.erb"
owner "root"
group "root"
mode "0644"
variables ({
:client_name => item['client_name'], # Don't you have this already?
:client_timezone => item['client_timezone']
})
end
end
Your crontab.erb should look like this:
30 04 * * * java -Duser.timezone=<%= #client_timezone %> -jar /var/www/app-<%= node[:version] %>.jar /var/www/<%= #client_name %>/config/spring/job.xml

Related

How to use local variable of whitespace array in chef template resource

I am trying to use whitespace arrays in chef template, like below and when I run the chef-client to execute the recipe getting an error saying: option variables must be a kind of [Hash]! below is recipe file
abc = node['abc']
def1 = node['def']
abc_sit = abc['sit']
def_sit = def1['sit']
%w{abc_sit def_sit}.each do | client |
template "/etc/#{client}.sh" do
source 'tunnel.erb'
owner 'root'
group 'root'
variables ("#{client}") --> At this line I am getting error
end
end
The error I am getting when I run the chef-client:
option variables must be a kind of [Hash]! You passed "abc_sit"
As it says, you have to pass in a Hash. Perhaps something like variables myclient: client and then <%= #myclient %> in the template.

Why does puppet think my custom fact is a string?

I am trying to create a custom fact I can use as the value for a class parameter in a hiera yaml file.
I am using the openstack/puppet-keystone module and I want to use fernet-keys.
According to the comments in the module I can use this parameter.
# [*fernet_keys*]
# (Optional) Hash of Keystone fernet keys
# If you enable this parameter, make sure enable_fernet_setup is set to True.
# Example of valid value:
# fernet_keys:
# /etc/keystone/fernet-keys/0:
# content: c_aJfy6At9y-toNS9SF1NQMTSkSzQ-OBYeYulTqKsWU=
# /etc/keystone/fernet-keys/1:
# content: zx0hNG7CStxFz5KXZRsf7sE4lju0dLYvXdGDIKGcd7k=
# Puppet will create a file per key in $fernet_key_repository.
# Note: defaults to false so keystone-manage fernet_setup will be executed.
# Otherwise Puppet will manage keys with File resource.
# Defaults to false
So wrote this custom fact ...
[root#puppetmaster modules]# cat keystone_fernet/lib/facter/fernet_keys.rb
Facter.add(:fernet_keys) do
setcode do
fernet_keys = {}
puts ( 'Debug keyrepo is /etc/keystone/fernet-keys' )
Dir.glob('/etc/keystone/fernet-keys/*').each do |fernet_file|
data = File.read(fernet_file)
if data
content = {}
puts ( "Debug Key file #{fernet_file} contains #{data}" )
fernet_keys[fernet_file] = { 'content' => data }
end
end
fernet_keys
end
end
Then in my keystone.yaml file I have this line:
keystone::fernet_keys: '%{::fernet_keys}'
But when I run puppet agent -t on my node I get this error:
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Evaluation Error: Error while evaluating a Function Call, "{\"/etc/keystone/fernet-keys/1\"=>{\"content\"=>\"xxxxxxxxxxxxxxxxxxxx=\"}, \"/etc/keystone/fernet-keys/0\"=>{\"content\"=>\"xxxxxxxxxxxxxxxxxxxx=\"}}" is not a Hash. It looks to be a String at /etc/puppetlabs/code/environments/production/modules/keystone/manifests/init.pp:1144:7 on node mgmt-01
I had assumed that I had formatted the hash correctly because facter -p fernet_keys output this on the agent:
{
/etc/keystone/fernet-keys/1 => {
content => "xxxxxxxxxxxxxxxxxxxx="
},
/etc/keystone/fernet-keys/0 => {
content => "xxxxxxxxxxxxxxxxxxxx="
}
}
The code in the keystone module looks like this (with line numbers)
1142
1143 if $fernet_keys {
1144 validate_hash($fernet_keys)
1145 create_resources('file', $fernet_keys, {
1146 'owner' => $keystone_user,
1147 'group' => $keystone_group,
1148 'subscribe' => 'Anchor[keystone::install::end]',
1149 }
1150 )
1151 } else {
Puppet does not necessarily think your fact value is a string -- it might do, if the client is set to stringify facts, but that's actually beside the point. The bottom line is that Hiera interpolation tokens don't work the way you think. Specifically:
Hiera can interpolate values of any of Puppet’s data types, but the
value will be converted to a string.
(Emphasis added.)

Chef shell_out just giving hex values

I'm creating the Chef recipe below. However, my shell_out calls are just returning hex values for the expanded variable. How to get the value of the find command in the variable, and not a junk hex value?
Here is the relevant code snipet from my recipe::
so = nil
bash "getPath" do
Chef::Resource::RubyBlock.send(:include, Chef::Mixin::ShellOut)
command = "find \/ -path \\*ohai\/plugins"
so = shell_out(command)
end
listOfFiles = ["#{so}\cert.rb","#{so}\key.rb","#{so}\perms.rb","#{so}\propertiesFiles.rb","#{so}\warFiles.rb","#{so}\yum.rb"]
templates = { 1 => "cert.rb", 2 => "key.rb", 3 => "perms.rb", 4 => "propertiesFiles.rb", 5 => "warFiles.rb", 6 => "yum.rb" }
i = 1
listOfFiles.each do |remote|
template "#{remote}" do
source 'cert.rb' #this is going to be a variable once I figure out how to call it from the "templates" hash. Just doing a static file for troubleshooting purposes now
owner 'root'
group 'root'
mode '0644'
action :create
end
end
Here is part of the chef-client run output:
- create new file #<Mixlib::ShellOut:0x00000004dd88b8>yum.rb
- update content in file #<Mixlib::ShellOut:0x00000004dd88b8>yum.rb from none to fccd51
--- #<Mixlib::ShellOut:0x00000004dd88b8>yum.rb 2016-10-31 11:52:40.652276263 -0400
+++ ./.chef-#<Mixlib::ShellOut:0x00000004dd88b8>yum20161031-8408-1svdruo.rb 2016-10-31 11:52:40.652276263 -0400
## -1 +1,17 ##
+#
+# Cookbook Name:: inventory
+# Recipe:: default
+#
+# Copyright (c) 2016 me, All Rights Reserved.
+
+Ohai.plugin(:Cert) do
+ provides 'certFiles'
+
+ collect_data(:linux) do
+ certFiles Mash.new
+ so = shell_out("find \/ -name \"*.crt\"")
+ certFiles[:values] = so.stdout
+ end
+end
+
- change mode from '' to '0644'
- change owner from '' to 'root'
- change group from '' to 'root'
- restore selinux security context
* ohai[reload] action reloadPuTTYPuTTYPuTTYPuTTYPuTTY
- re-run ohai and merge results into node attributes
Running handlers:
Running handlers complete
Chef Client finished, 8/20 resources updated in 05 seconds
[root#host plugins]# PuTTYPuTTYPuTTYPuTTYPuTTY^C
Almost there!
so = shell_out(command) # Returns a Mixlib::ShellOut object
output = so.stdout # Returns stdout of your command, as String

Setting chef vault variable in template.erb

I have a vault item defined as the following in my recipe
item = ChefVault::Item.load("user","password")
How do i call this this in my template.erb? I tried the following which isn't working
ROOTPASSWORD= <%= #node["testcookbook"]["user"]["password"] %>
My vault item looks like this:
$ knife vault show user password
id: password
pass: xxxxxxxxxx
username: chefuser
I generally do something like this within a recipe
ROOTPASSWORD #{item['pass']}
however I don't think that would work within a template.
There are two options to solve that problem though the second one should be preferred as that keeps your sensitive data private.
Suppose, if your vault look like this:
knife vault show user password
id: password
pass: xxxxxxxxxx
username: chefuser
Then, you can approach like following:
Save as Node Attribute
First, if you want to set the password on node object and make it visible, then
you can do something like below:
In recipe:
node.default["testcookbook"]["user"]["password"] = ChefVault::Item.load("user","password")['pass']
template '/tmp/template' do
source 'template.erb'
owner 'root'
group 'root'
mode '0644'
end
In Template:
ROOTPASSWORD= <%= node["testcookbook"]["user"]["password"] %>
Pass Data to the Template using variables
Second, if you don't want to set the password on node object and let it visible in chef run logs, then you can do something like below:-
template '/tmp/template' do
source 'template.erb'
owner 'root'
group 'root'
mode '0644'
sensitive true
variables( {:password => ChefVault::Item.load("user","password")['pass']})
end
In Template:
ROOTPASSWORD= <%= #password %>

Chef Recipe to configure multiple mpd instances

I try to create a chef cookbook to launch multiple mpd instances in my vagrant virtual box (using chef-solo).
I want to configure each instance in my Vagrantfile like this:
mpd: {
channels: {
mix: {
name: 'mpd_mix',
bind: '0.0.0.0',
socket: '/home/vagrant/.mpd/socket/mix',
port: '6600'
},
tech: {
name: 'mpd_tech',
bind: '0.0.0.0',
socket: '/home/vagrant/.mpd/socket/tech',
port: '6601'
}
}
}
So the recipe should take these settings and loop through them (creating an mpd instance for each channel).
This is what I currently have as a recipe:
package "mpd"
node.normal[:mpd][:channels].each_value do |channel|
# create socket
file channel[:socket] do
action :touch
end
# trying to set the attributes for the config file and the service
node.set[:mpd][:port] = channel[:port]
node.set[:mpd][:db_file] = "/var/lib/mpd/tag_cache_" + channel[:name]
node.set[:mpd][:bind_2] = channel[:socket]
node.set[:mpd][:icecast_mountpoint] = "/" + channel[:name] + ".mp3"
node.set[:mpd][:channel_name] = channel[:name]
# create service
service channel[:name] do
service_name "mpd" # linux service command
action :enable
end
# create the corresponding config file
config_filename = "/etc/" + channel[:name] + ".conf"
template config_filename do
source "mpd.conf.erb"
mode "0644"
notifies :restart, resources(:service => channel[:name])
end
end
I have several Problems with this:
Ist does not create a system service for each mpd instance, so I can do sudo service mpd_mix start. Why?
It does not use the /etc/mpd_mix.conf config file when launching mpd, because it still calls /etc/init.d/mpd start which uses /etc/mpd.conf. How can I change that, so it uses the correct config file for each mpd instance?
Adjusting the attributes for the creation of the config files does not work as expected (see the node.set part in the code above). Both config files, /etc/mpd_tech.conf and /etc/mpd_mix.conf use the tech channel attributes. Looks like the mix settings get overwritten somehow? How can I fix that?
I'd really appreciate some help on this as I am quite new to chef cookbooks.
I figured out how to do it. Here is the relevant code part:
node[:mpd][:channels].each_value do |channel|
# create socket
file channel[:socket] do
action :touch
end
# create init file
init_filename = "/etc/init.d/" + channel[:name]
template init_filename do
variables :channel => channel
source "mpd.init.erb"
mode "0755"
end
# create service
service channel[:name] do
service_name channel[:name] # linux service command
action :enable
end
# create config file
config_filename = "/etc/" + channel[:name] + ".conf"
template config_filename do
variables :channel => channel
source "mpd.conf.erb"
mode "0644"
notifies :restart, resources(:service => channel[:name])
end
end
If you want to take a closer look, take a look at the complete cookbook repository on github: https://github.com/i42n/chef-cookbook-mpd/blob/master/recipes/default.rb

Resources