How do i define variable within a template cookbook - ruby

I am trying to edit the existing user_management cookbook on the supermarket to include sudoers. I seem to be having problems properly defining the sudoers_groups variable within the template.
Link to default cookbook
https://github.com/FFIN/user_management/blob/master/recipes/default.rb
Here is what my vault looks like.
knife vault show testusers john
action: create
comment: John Smith
dbpass: secret
gid: john
id: john
password: $1$d$xKNtrFrifo6f7tLFW1xh750
shell: /bin/bash
sudo_pwdless: true
sudoer: false
sudoer_group:
command: ALL
name: admin
sudo_pwdless: false
command: ALL
name: wheel
sudo_pwdless: false
command: ALL
name: sysadmin
sudo_pwdless: true
uid: 1002
username: john`
Here is the template section of my recipe
sudoer_users = Array.new()
if user['sudoer']
command = user['command'] ? user['command'] : 'ALL'
hash = { :uname => user['username'], :command => command, :sudo_pwdless => user['sudo_pwdless'] }
sudoer_users.push(hash)
end
template "/etc/sudoers" do
source 'sudoers.erb'
mode '0440'
owner 'root'
group node['root_group']
variables(
:sudoers_users => sudoer_users,
:sudoers_groups => node[:testcookbook][:testusers][:sudoer_group]
)
only_if { sudoer_users }
end
When i run the recipe, i get the following error
Recipe Compile Error in /var/chef/cache/cookbooks/newuser/recipes/default.rb ============================================. ==================================== NoMethodError
-------------`
undefined method [] for nil:NilClass
template "/etc/sudoers" do
61: source 'sudoers.erb'
62: mode '0440'
63: owner 'root'
64: group node['root_group']
65: variables(
66: :sudoers_users => sudoer_users,
67>> :sudoers_groups => node[ :testcookbook][ :testusers][ :sudoer_group]
68: )
69: only_if { sudoer_users }
70: end
My question is how do i go about defining the sudoers_group variable so that it only iterates the sudoer_group section within the vault?

Unfortunately Ruby doesn't give us enough info to check which is undefined, but either node[:testcookbook] or node[:testcookbook][:testusers] is unset/undefined. Double check where you are setting the sudoer_group value because it is likely either misformatted or not uploaded to the Chef Server.

Here is what i did to finally resolve the issue.
I added the following as part of my variables in attributes/default.rb.
default['testcookbook']['testusers']['sudoer_group'] = [
{"name" => "admin", "sudo_pwdless" => false, "command" => "ALL"},
{"name" => "wheel", "sudo_pwdless" => false, "command" => "ALL"},
{"name" => "sysadmin", "sudo_pwdless" => true, "command" => "ALL"}
]`

Related

From ruby array to json in bash

In my chef environment I have a variable like this:
"repl_set_members": ["tmongo01", "tmongo02", "tmongo03"]
I need to create JSON to be sent to a Mongo instance and build the replica set.
I created a bash file from a template with:
template "/opt/create_repl_set.sh" do
source "create_repl_set.sh.erb"
owner 'root'
group 'root'
mode "0755"
variables(
:repl_set_name => node['mongodb']['shardname'],
:members => node['mongodb']['repl_set_members']
)
end
And in the bash template I would have something like this:
config = "{_id: '<%= #repl_set_name %>', members: [
<% #members.each_with_index do |name,number| %>
{_id: <%= "#{number}" %>, host: '<%= "#{name}" %>:27017'},
<% end %>
] }"
mongo localhost:27091 --eval "JSON.stringify(db.adminCommand({'replSetInitiate' : $config}))"
But the resulting JSON includes a last comma which I don't know how to get rid of.
Does anyone have a better idea?
A quick and dirty way to remove your last comma would be to use bash string manipulation and call your script as follow :
mongo localhost:27091 --eval "JSON.stringify(db.adminCommand({'replSetInitiate' : ${config/%,/}))"
Note that this will always delte the last comma in your JSON value, so it will only work if your resulting JSON always has an extra comma (i.e. even when your JSON is empty)
What you probably want is an execute resource an .to_json
mongo_cmd = {
'replSetInitiate' => {
'_id' => node['mongodb']['shardname'],
'members' => node['mongodb']['repl_set_members'].each_with_index.map { |name, number|
{'_id' => number, 'host' => "#{name}:27017"}
},
},
}
execute "mongo localhost:27091 --eval 'JSON.stringify(db.adminCommand(#{mongo_cmd.to_json}))'"

Ruby code for Choice build parameter within Jenkins job (configured via Chef)

I need to make a drop down / Choice selection for one of my automated builds.
Here's the finished XML that I can observe when I configure the job on-the-fly from the front end (I need to translate this into the Chef recipe that I am creating):
<property>
<parameterDefinition>
<defaultParameterValue>
<name>DATA_BAG_NAME</name>
<value>X</value>
</defaultParameterValue>
<description>Select the data bag that contains the job above.</description>
<name>DATA_BAG_NAME</name>
<type>ChoiceParameterDefinition</type>
<choice>X</choice>
<choice>Y</choice>
<choice>Z</choice>
</parameterDefinition>
</property>
I have also attached a screenshot of what the manual configuration looks like.
Finally,
Here is the non working code I have written up for my Chef Recipe:
:build_params => [
{ 'name' => 'JENKINS_ID_TO_ADD', 'type' => 'String', 'default' => '',
'description' => ' Enter the JenkinsID you want to add, example: PRCalculator' },
{ 'name' => 'DATA_BAG_NAME', 'type' => 'Choice', 'choices' =>
'description' => ' Select the data bag that contains the job above.' }
],
Please help me determine what type of Ruby Syntax I need to make the drop down list actually contain values, instead of "'choices' => " which is what I have right now. Any permutation I try results in failure to configure, or success with 0 elements in the dropdown.
~EDIT: Adding in further code to help troubleshoot:
config_name = 'free-style'
job_name = "flag-chef-add-jenkins-id"
job_config = File.join(Chef::Config[:file_cache_path], "#{job_name}-config.xml")
template job_config do
source File.join('jenkins',"job-#{config_name}-config.xml.erb")
variables :job_name => job_name,
:max_builds => '15',
:build_params => [
{ 'name' => 'JENKINS_ID_TO_ADD', 'type' => 'String', 'default' => '',
'description' => ' Enter the JenkinsID you want to add, example: MyJobID' },
{ 'name' => 'DATA_BAG_NAME', 'type' => 'Choice', 'choice' => '1', 'choice' => '2',
'description' => ' Select the data bag that contains the job above.' }
],
:command => "
if [ \"$JENKINS_ID_TO_ADD\" != \"\" ]; then
# run gimmicky update that wont work!
echo \"Jenkins ID to add or update: \"
echo \"Running Jenkins_ID_To_Add job for the following ID: $JENKINS_ID_TO_ADD .'\n\" >> jenkinsIDTest.txt
echo recipe['jenkins::master'],recipe[\"flag_utils::data_bags::promo_lps::$JENKINS_ID_TO_ADD\"] >> jenkinsIDTest.txt
sudo -u root -i chef-client -o recipe['jenkins::master'],recipe[\"flag_utils::data_bags::promo_lps::$JENKINS_ID_TO_ADD\"] --force-formatter >> jenkinsIDTest.txt
fi
",
:email_release_subject => 'Flag Utils Chef add Jenkins ID run! $JENKINS_ID_TO_ADD',
:admin_emails => admin_emails,
:notification_emails => notification_emails
end
jenkins_job job_name do
config job_config
end
Using Chef you can generate the jobs's config.xml file using a chef template resource.
Better still I recommend using the Jenkins cookbook which has a jenkins_job resource that takes a template as a parameter.
Update - Cookbook example
I have created the following "demo" cookbook to illustrate how to install Jenkins and configure a parametrized job.
├── Berksfile
├── metadata.rb
├── recipes
│   └── default.rb
├── templates
│ └── default
│ └── choice-job.xml.erb
└── attributes
├── java.rb
└── jenkins.rb
recipes/default.rb
#
# Cookbook Name:: demo
# Recipe:: default
#
# Copyright (c) 2016 The Authors, All Rights Reserved.
#
include_recipe "apt"
include_recipe "java"
include_recipe "jenkins::master"
#
# Jenkins job
#
jobxml = File.join(Chef::Config[:file_cache_path], 'choice-job.xml')
template jobxml do
source "choice-job.xml.erb"
variables :choices => ["X", "Y", "Z"]
end
jenkins_job "Choice Demo" do
config jobxml
end
Note:
An xml file is generated by the template and passed to the Jenkins job resource.
Passing in 3 parameters that will appear as choices in the Jenkins UI
templates/default/choice-job.xml.erb
<?xml version='1.0' encoding='UTF-8'?>
<project>
<actions/>
<description>Demo job</description>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.model.ParametersDefinitionProperty>
<parameterDefinitions>
<hudson.model.ChoiceParameterDefinition>
<name>DATA_BAG_NAME</name>
<description></description>
<choices class="java.util.Arrays$ArrayList">
<a class="string-array">
<% #choices.each do |choice| -%>
<string><%= choice %></string>
<% end -%>
</a>
</choices>
</hudson.model.ChoiceParameterDefinition>
</parameterDefinitions>
</hudson.model.ParametersDefinitionProperty>
</properties>
<scm class="hudson.scm.NullSCM"/>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers/>
<concurrentBuild>false</concurrentBuild>
<builders>
<hudson.tasks.Shell>
<command>env</command>
</hudson.tasks.Shell>
</builders>
<publishers/>
<buildWrappers/>
</project>
Note the following fragment:
<% #choices.each do |choice| -%>
<string><%= choice %></string>
<% end -%>
The "choices" are passed into the template
metadata.rb
name 'demo'
maintainer 'The Authors'
maintainer_email 'you#example.com'
license 'all_rights'
description 'Installs/Configures demo'
long_description 'Installs/Configures demo'
version '0.1.0'
depends "apt"
depends "java"
depends "jenkins"
Berksfile
source 'https://supermarket.chef.io'
metadata
Note:
Dependencies pulled from supermarket by Berkshelf
attributes/java.rb
normal['java']['jdk_version'] = '7'
Note:
Jenkins needs at least Java7
attributes/jenkins.rb
normal['jenkins']['master']['install_method'] = "war"
normal['jenkins']['master']['version'] = "1.655"
normal['jenkins']['master']['checksum'] = "0cee889af697c115961ce50229cc5e39d1b798c0a0a689687b745c0a938c8547"
Note:
Specifying the version and checksum avoids the need to check for the latest version
xml = File.join(Chef::Config[:file_cache_path], 'rconfig.xml')
template xml do
source 'jconfig.xml.erb'
end
jenkins_job 'project' do
config xml
action :create
end
This is the code example. This might help. "jconfig.xml" is my config.xml file for jenkins job

Write users to .htpasswd in chef recipe

In a chef recipe invoked by chef-solo / vagrant I'm trying to write a .htpasswd file from an object of users.
I specify the users in vagrantfile like this...
chef.json = {
:apache => {
...
:my_users => {
:john => "test",
:matt => "test2"
}
...
My chef recipe looks like this at the moment:
file "/etc/apache2/.htpasswd" do
content "john:n5MfEoHOIQkKg"
owner "#{node['apache']['user']}"
group "#{node['apache']['group']}"
mode '0644'
action :create
end
As you can see I have hard coded John's credentials in there - however, I'm not a Ruby dev and I'm missing some very basic knowledge here...
How can I write all user credentials in the node['apache']['my_users'] attribute (defined in the chef.json) in a loop into the file while creating the password hash for each clear text password?
Note: I'm trying to avoid using a template for this simple file.
I got this working using the LWRP Charlie suggested.
First step is to modify the definition of users to be a proper array:
chef.json = {
:apache => {
...
:my_users => [
{ :username => "john", :password => "test1" },
{ :username => "matt", :password => "test2" }
]
...
I include the htpasswd dependency to metadata and bershelf.
Then in my recipe I create the users in a loop using the htpasswd call:
node[:apache][:my_users].each do |user|
htpasswd "/etc/apache2/.htpasswd" do
user user['username']
password user['password']
end
end
The htpasswd man page looks like it uses MD5 hashing on the passwords.
Perhaps you can generate md5 hashes in your recipe's Ruby code?
You can do it the native way, it requires htpasswd to be installed:
execute 'set password' do
sensitive true
command "htpasswd -cb /etc/htpasswd.users #{user} #{password}"
creates '/etc/htpasswd.users'
end
file '/etc/htpasswd.users' do
owner 'www-data'
group 'www-data'
mode 0o600
end

Chef template and using it in a loop

How to write this in a loop.
I am very new to ruby so struggling with the approach.
I am trying to create a response file and then update the fixpack for IHS (IBM HTTP Server)
#Install the fix pack for IHS
template "/tmp/ihs-fixpack-response1.txt" do
source "ihs-fixpack-response.erb"
mode 0755
owner "root"
group "root"
variables({
:fixpack => "7.0.0-WS-IHS-LinuxX32-FP0000019.pak",
:product_path => node[:websphere][:ihs][:ihs_path]
})
end
# code for installing Fixpack
bash "ihs/was-updateinstaller" do
user "root"
code %(#{node[:websphere][:ihs][:ihs_updi_path]}/update.sh -options "/tmp/ihs-fixpack-response1.txt" -silent)
end
#Install the fix pack for the plugin.
template "/tmp/ihs-fixpack-response2.txt" do
source "ihs-fixpack-response.erb"
mode 0755
owner "root"
group "root"
variables({
:fixpack => "7.0.0-WS-PLG-LinuxX32-FP0000019.pak",
:product_path => node[:websphere][:ihs][:ihs_wasPluginPath]
})
end
# code for installing Fixpack
bash "ihs/was-updateinstaller" do
user "root"
code %(#{node[:websphere][:ihs][:ihs_updi_path]}/update.sh -options "/tmp/ihs-fixpack-response2.txt" -silent)
end
I believe this will do what you want:
[ [ "7.0.0-WS-IHS-LinuxX32-FP0000019.pak", node[:websphere][:ihs][:ihs_path] ],
[ "7.0.0-WS-PLG-LinuxX32-FP0000019.pak", node[:websphere][:ihs][:ihs_wasPluginPath] ]
].zip(1..2).each do |vars, i|
template "/tmp/ihs-fixpack-response#{i}.txt" do
source "ihs-fixpack-response.erb"
mode 0755
owner "root"
group "root"
variables({
:fixpack => vars.first,
:product_path => vars.last
})
end
bash "ihs/was-updateinstaller" do
user "root"
code %(#{node[:websphere][:ihs][:ihs_updi_path]}/update.sh -options "/tmp/ihs-fixpack-response#{i}.txt" -silent)
end
end

How to set an environment variable on a server with puppet?

I'm starting to use puppet in my current project and I'm having some issues.
I'm using a recipe to install jruby, but I want to set a environment variable (in this case, JRUBY_HOME and modify the PATH to include JRUBY_HOME/bin) after it finishes installing jruby.
Here's the recipe:
class jruby {
$jruby_home = "/opt/jruby"
exec { "download_jruby":
command => "wget http://jruby.org.s3.amazonaws.com/downloads/1.7.0.RC2/jruby-bin-1.7.0.RC2.tar.gz",
path => $path,
timeout => 0,
unless => "ls /opt | grep jruby-1.7.0",
require => Package["openjdk-7-jre-headless"]
}
exec { "unpack_jruby" :
command => "tar -zxf jruby-bin-1.7.0.RC2.tar.gz -C /opt",
path => $path,
creates => "${jruby_home}-1.7.0.RC2",
require => Exec["download_jruby"]
}
file { $jruby_home:
ensure => link,
target => "${jruby_home}-1.7.0.RC2",
require => Exec["unpack_jruby"]
}
}
So, what's the best way to add /opt/jruby as JRUBY_HOME and then add JRUBY_HOME/bin to PATH?
Solved it:
# init.pp
$jruby_sh = "/etc/profile.d/jruby.sh"
file { $jruby_sh:
ensure => present,
source => "puppet:///modules/jruby/jruby.sh",
owner => "root",
group => "root",
mode => 644,
require => File[$jruby_home]
}
# jruby.sh
export JRUBY_HOME=/opt/jruby
export PATH=$PATH:$JRUBY_HOME/bin

Resources