updating file using 'file' chef-solo resource - ruby

i am trying to install java using chef-solo. The problem is to set the JAVA_HOME and PATH variables in /etc/profile file. I tried using 'file' resource provided by chef. here is some of my code:
java_home = "export JAVA_HOME=/usr/lib/java/jdk1.7.0_05"
path = "export PATH=$PATH:/usr/lib/java/jdk1.7.0_05/bin"
execute "make_dir" do
cwd "/usr/lib/"
user "root"
command "mkdir java"
end
execute "copy" do
cwd "/usr/lib/java"
user "root"
command "cp -r /home/user/Downloads/jdk1* /usr/lib/java"
end
file "/etc/profile" do
owner "root"
group "root"
action :touch
content JAVA_HOME
content PATH
end
but the problem is content command overrides all the content of file, is there any way to UPDATE the file while using chef-solo resources. Thanks!
UPDATE: i have found some code from chef-recipe, but i am not sure what it does exactly, the code is..
ruby_block "set-env-java-home" do
block do
ENV["JAVA_HOME"] = java_home
end
end
Does it set JAVA_HOME variable for only that instance or permanently? Can anybody help?

Use Chef::Util::FileEdit. Below is an example how I modify .bashrc. The idea here is that I just add:
# Include user specific settings
if [ -f ~/.bashrc_user ]; then . ~/.bashrc_user; fi
to the end of default .bashrcand all other modifications take place in .bashrc_user that is part of my cookbook.
cookbook_file "#{ENV['HOME']}/.bashrc_user" do
user "user"
group "user"
mode 00644
end
ruby_block "include-bashrc-user" do
block do
file = Chef::Util::FileEdit.new("#{ENV['HOME']}/.bashrc")
file.insert_line_if_no_match(
"# Include user specific settings",
"\n# Include user specific settings\nif [ -f ~/.bashrc_user ]; then . ~/.bashrc_user; fi"
)
file.write_file
end
end

As #user272735 's suggestion, a clean way to modify .bashrc is:
write all your modification in a .bashrc_local file,
include your specific settings to .bashrc.
For step 1, we can use template resource.
For step 2, I prefer use line cookbook.
Sample codes as below,
templates/bashrc_local.erb
export JAVA_HOME=/usr/lib/java/jdk1.7.0_05
export PATH=$PATH:/usr/lib/java/jdk1.7.0_05/bin
recipes/default.rb
# add bashrc_local
template "#{ENV['HOME']}/.bashrc_local" do
source 'bashrc_local.erb'
mode 00644
end
# update bashrc
append_if_no_line "add bashrc_local" do
path "#{ENV['HOME']}/.bashrc"
line "if [ -f ~/.bashrc_local ]; then . ~/.bashrc_local; fi"
end

You can fix this by either using a template resource instead of a file resource, or if you are just appending those two variables, try doing this:
content "#{java_home}\n#{path}"
The second content line is overriding the first, as you have already discovered. You also don't need the action :touch.

Related

Chef - use env variable with package resource what created with bash resource

I would get the changed project from git commit and install the package based on this.
Here is my code
bash 'get_project' do
code <<-EOH
filelist=$(git diff-tree --no-commit-id --name-only -r $1)
for file in ${filelist[#]}; do
project=$(echo $file | cut -d "/" -f1)
projectList+=($project)
done
for changedProject in $(echo "${projectList[#]}" | sort | uniq); do
INSTALLABLE_RPM=application-$changedProject
done
EOH
environment 'INSTALLABLE_RPM' => '$INSTALLABLE_RPM'
end
zypper_package ENV['INSTALLABLE_RPM']
My idea is to generate the INSTALLABLE_RPM variable with bash and install the package with zypper. Unfortunately it doesn't work. The zypper_package resource cant recognize.
I ran out of ideas :-(
The environment property of the bash resource is to supply existing environment variables to execute the bash command(s).
(These variables must exist for a command to be run successfully.)
Specifying environment variables here will not set them in the shell. Also from within the code block you will not be able to access the Ruby's ENV hash.
There may not be a straight-forward way to do this. One of the options is to write this package (list?) to a file. Then we can read the file contents into variable, and use it with zypper_package resource.
Example:
Since you have used for loop in Shell, I believe you get a list of packages, so I am considering pkg_list as Array. I've set compile_time to true as the variable assignment below bash resource will only run during compile time.
bash 'get_project' do
code <<-EOH
# your code as-it-is
for changedProject in $(echo "${projectList[#]}" | sort | uniq); do
echo "application-$changedProject" >> /tmp/rpm_packages
done
EOH
compile_time true
end
pkg_list = File.read('/tmp/rpm_packages').split
zypper_package pkg_list
# remove the file for good measure :)
file '/tmp/rpm_packages' do
action :delete
end

Use Chef to ensure a user environment variable is deleted but not a system variable on Windows

I'm writing a chef script to install nvm for windows. I've just had a problem where the system ends up with a User and a System variable for NVM_HOME
env 'NVM_HOME' do
action :delete
end
the above would presumable remove both
How can I amend my chef script to ensure the system variable is kept but the user variable is removed if present?
If the user environment variable is the user running chef and not another user you can use the registry_key resource to ensure the values are deleted (untested example):
registry_key "HKCU\\Environment" do
values [{:name => 'NVM_HOME'},{:name => 'NVM_SYMLINK'}]
action :delete
end
If you wish to change another user variable you can have a look at this answer
Did this in the end
[
'NVM_HOME',
'NVM_SYMLINK'
].each do |key|
execute "ensure nvm has not added #{key} user variables" do
command "REG delete HKCU\\Environment /F /V #{key}"
only_if { registry_key_exists? "HKCU\\Environment\\#{key}" }
end
end
probably not the best way but it ran :)

Set an env var for only the provisioner

I need an environment variable added to the front of $PATH that:
Doesn't last beyond the provisioning run.
Is dependent i.e. something will be installed earlier in the run that is then is available via $PATH, so I can't set it globally as this cookbook says to.
I tried the answer here:
Exec { environment => [ "foo=$bar" ] }
but I get the error Error: All resource specifications require names. When I add a name I get other errors about syntax, for which my fiddling around to fix just gives me other errors (the error Syntax error at '}'; expected '}' is my favourite!)
I've tried using export to set it, but I see: Error: Could not find command 'export'
I've tried using set and setenv too, with similar results. There must be a straightforward way to do this, but I can't find it.
Edit
Just to add, these are the available shells:
$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
/bin/zsh
/usr/bin/zsh
zsh is part of the provisioning, but it could be a requirement of the answer, if needs be.
Added to the front of your path, you want to add your resource default like this I believe:
Exec { environment => "PATH=value:$PATH", }
This could be incorrect, but I do know that it will replace the variables you set, not append to them by default. More details at https://docs.puppetlabs.com/puppet/latest/reference/type.html#exec-attribute-environment
I tried a few ways for this, but the best I found was to use Hiera. I read quite a few blogs on how to set this up with Vagrant too, but this was the best I found.
My project directory layout
Vagrantfile
pp/
manifests/
modules/
data/
hiera.yml
common.yml
Vagrantfile
The relevant part of the Vagrantfile:
config.vm.provision "puppet" do |puppet|
puppet.manifests_path = "pp/manifests"
puppet.module_path = "pp/modules/custom"
puppet.manifest_file = "default.pp"
puppet.hiera_config_path = "pp/data/hiera.yaml"
end
I've no idea yet why there needs to be a hiera.yaml which points to a common.yaml, but that's the way it is.
hiera.yaml
---
:backends:
- yaml
:hierarchy:
- "common"
:yaml:
:datadir: '/vagrant/pp/data'
common.yaml
---
ruby_version: "2.3.0"
ruby_prefix: "/opt/rubies"
...
Then in a manifest
$ruby_version = hiera("ruby_version")
$ruby_prefix = hiera("ruby_prefix")
$ruby_dir_fullpath = "${ruby_prefix}/ruby-${ruby_version}"
Seems like a lot of effort to me, but again, that's the way it is.

Opsworks Custom Chef Recipies

I have successfully deployed my application using AWS OpsWorks, now I am trying to implement a custom Chef Cookbook that will allow me to set the bash environment variables. I have setup the Git repo the cookbook is being updated with OpsWorks. I generated the cookbook using the knife command on my dev box which is really just the directory structure with a recipes/default.rb file containing a few lines of code.
When I try to do something like the following I seem to keep getting errors
node[:deploy].each do |application, deploy|
deploy = node[:deploy][application]
command "ls -la"
end
(Note: ls -la is just for testing i know this will not set the environment variables)
I get the following error: ERROR: Caught exception during execution of custom recipe: xyz-enviroment: NoMethodError - undefined method command' for #<Chef::Recipe:0x7feb59200c00> - /opt/aws/opsworks/releases/20130328224322_109/vendor/bundle/ruby/1.8/gems/chef-0.9.15.5/bin/../lib/chef/mixin/recipe_definition_dsl_core.rb:56:in method_missing
Also if I try something like
execute "setting up the enviroment" do
# TODO: Add code that does something here
end
I get the following error:
execute[setting up the enviroment] (/opt/aws/opsworks/current/site-cookbooks/xyz-enviroment/recipes/default.rb:18:in `from_file') had an error:
No such file or directory - setting up the enviroment
I'm new to Chef so I'm sure there is something simple I'm doing wrong I just haven't been able to figure it out. Thanks in advance for the help.
I already solved my issue before seeing the responses below, they may have worked to solve the issue but I don't have the time to go back and try them now.
My solution was to use a Chef template to create an initializer file to set the variables when rails boots up the application.
# deafult.rb
node[:deploy].each do |application, deploy|
deploy = node[:deploy][application]
execute "restart Rails app #{application}" do
cwd deploy[:current_path]
command node[:opsworks][:rails_stack][:restart_command]
action :nothing
end
template "#{deploy[:deploy_to]}/current/config/initializers/dev_enviroment.rb" do
source "dev_enviroment.erb"
cookbook 'dev-enviroment'
group deploy[:group]
owner deploy[:user]
variables(:dev_env => deploy[:dev_env])
notifies :run, resources(:execute => "restart Rails app #{application}")
only_if do
File.exists?("#{deploy[:deploy_to]}") && File.exists?("#{deploy[:deploy_to]}/current/config/")
end
end
end
dev_enviroment.erb
ENV['VAR1'] = "<%= #dev_env[:VAR1] %>"
ENV['VAR2'] = "<%= #dev_env[:VAR2] %>"
The custom Chef JSON used in the Opsworks stack layer:
{
"deploy": {
"myapp": {
"dev_env": {
"VAR1": "INFO1",
"VAR2": "INFO2",
}
}
}
}
You didn't specify what command to run, so it's actually trying to run setting up the environment, which isn't a valid command.
Try instead specifying the command attribute inside the block:
execute "setting up the enviroment" do
command "/path/to/command --flags"
end
Alternatively, set the resource name to the command itself:
execute "/path/to/command --flags" do
# TODO: Add code that does something here
end
Your second question was correctly answered by clb. As for your first, 'command' is not a valid chef resource, you want something like:
node[:deploy].each do |application, deploy|
deploy = node[:deploy][application]
execute "running a command for #{application}" do
command "ls -la"
end
end
OpsWorks has a new feature, where you can add Environmentvariables on the App
You can access them via
node[:deploy]['appshortname'][:environment_variables][:variable_name]
( see http://docs.aws.amazon.com/opsworks/latest/userguide/attributes-json-deploy.html#attributes-json-deploy-app-environment )
So you can directly set them for your chef context like this:
node[:deploy]['magento']['environment_variables'].each {|key, value| ENV[key]=value }
And you can "dump" that into a shell script for example like this:
file "#{release_path}/environment.sh" do
content ENV.reduce("#!/bin/bash\n") { |a, e| a + "export #{e[0]}=\"#{e[1]}\"\n" }
mode '0775'
end
We do something similar, and solved it by adding our environment variables to the json config file in Opsworks dashboard. In chef, we write the file on deploy with this template: https://github.com/octocall/opsworks-cookbooks/blob/34e60267a4d3b5b9cf84e91829cc80c98c26f8ed/deploy/definitions/opsworks_rails.rb#L26, and then we symlink it using the "symlink_before_migrate" property of the json file, like this:
{
"deploy": {
"APPNAME": {
"symlink_before_migrate": {
"config/.env": ".env"
}
}
}
}

Setting environment variables with puppet

I'm trying to work out the best way to set some environment variables with puppet.
I could use exec and just do export VAR=blah. However, that would only last for the current session. I also thought about just adding it onto the end of a file such as bashrc. However then I don't think there is a reliable method to check if it is all ready there; so it would end up getting added with every run of puppet.
I would take a look at this related question.
*.sh scripts in /etc/profile.d are read at user-login time (as the post says, at the same time /etc/profile is sourced)
Variables export-ed in any script placed in /etc/profile.d will therefore be available to your users.
You can then use a file resource to ensure this action is idempotent. For example:
file { "/etc/profile.d/my_test.sh":
content => 'export MYVAR="123"'
}
Or an alternate means to an indempotent result:
Example
if [[ ! grep PINTO_HOME /root/.bashrc | wc -l > 0 ]] ; then
echo "export PINTO_HOME=/opt/local/pinto" >> /root/.bashrc ;
fi
This option permits this environmental variable to be set when the presence of the
pinto application makes it warrented rather than having to compose a user's
.bash_profile regardless of what applications may wind up on the box.
If you add it to your bashrc you can check that it's in the ENV hash by doing
ENV[VAR]
Which will return => "blah"
If you take a look at Github's Boxen they source a script (/opt/boxen/env.sh) from ~/.profile. This script runs a bunch of stuff including:
for f in $BOXEN_HOME/env.d/*.sh ; do
if [ -f $f ] ; then
source $f
fi
done
These scripts, in turn, set environment variables for their respective modules.
If you want the variables to affect all users /etc/profile.d is the way to go.
However, if you want them for a specific user, something like .bashrc makes more sense.
In response to "I don't think there is a reliable method to check if it is all ready there; so it would end up getting added with every run of puppet," there is now a file_line resource available from the puppetlabs stdlib module:
"Ensures that a given line is contained within a file. The implementation matches the full line, including whitespace at the beginning and end. If the line is not contained in the given file, Puppet appends the line to the end of the file to ensure the desired state. Multiple resources can be declared to manage multiple lines in the same file."
Example:
file_line { 'sudo_rule':
path => '/etc/sudoers',
line => '%sudo ALL=(ALL) ALL',
}
file_line { 'sudo_rule_nopw':
path => '/etc/sudoers',
line => '%sudonopw ALL=(ALL) NOPASSWD: ALL',
}

Resources