Uninstalling a package using Chef - amazon-ec2

I have been using Chef to manage our servers.
My roles/app.rb looks like this:
name "app"
description "App server"
run_list [
"recipe[apt]",
...,
...,
"recipe[nginx]"
...,
...,
]
Now I would like to remove the nginx package from the machine.
If I remove the nginx recipie in run_list, will it remove nginx from the nodes? If not please advise me what is the best strategy to have change-management on nodes.

If you remove nginx from the run_list that particular recipe just won't run. It won't actually remove nginx from the nodes because it doesn't know how to. I was actually pondering about this yesterday.
You can write your own recipe which undoes recipe[nginx] maybe recipe[remove_nginx] or something like that. Following that you can then remove recipe[remove_nginx].
Someone else also thinks this is a good way to do things which is at least a little reassuring:
http://community.opscode.com/questions/6
Apparently you can remove a recipe from the run_list in a ruby_block, so that saves you the hassle of using knife to remove it yourself after it is run:
https://gist.github.com/883522

Related

Ansible Expect module question - the installer makes me respond on a new line

I have an interesting issue. I'm using the expect module for ansible to install an application. All works well until the final question.
It does something like this.
Enter the default profile name [default] followed by a blank line:
<cursor starts here no at the semi colon>
and the response look looks like this if you manually do it. (yes i know its silly but its the way the binary installs - i can't control it)
Enter the default profile name [default] followed by a blank line:
default
The application is now installed message.
It errors out because the the cursor drops to the line below. Now, i tried doing something like this
expect:
'':'default'
but it errored out. Is there any other way to do this?

Install an apt package using chef.json on Vagrant solo-provisioner

I would like to install several arbitrary APT packages using Vagrants Chef solo provisioner.
chef.json seems to allow you to execute chef commands, but I'm unclear as to how to do this. Something like:
chef.json = {
apt: {
package: {'libssl-dev': {action: 'install'}}
}
?
Chef uses recipes to define resources that are executed on nodes via a chef-client.
A recipe is basically a definition of what to do (a script)
A resource is a particular element you are configuring (a file, a service, or package etc)
A node is the machine running chef-client
The json that you are setting up for chef-solo defines attributes which are like variables that your Chef can use to decide what to do.
So you have a hash of attributes for Chef to use, but you need a recipe that configures resources based on that hash to be executed on your node
In your case you need to configure the package resource
package "name" do
some_attribute "value"
action :action
end
The package resource supports lots of different package back ends, including apt so you don't need to worry about differences (except for package names).
To install the packages from your hash you can create a recipe like:
node[:apt][:package].each do |pkg,pkg_data|
package pkg do
action pkg_data[:action].to_sym
end
end
Individual recipes are then packaged up into cookbooks which is a logical grouping of like recipes. Generally a cookbook would be for a piece of software, say httpd or mysql.
As Tensibia mentions, read through the Vagrant Chef-Solo docco for where to put your recipe/cookbook and run from there.
chef.json does not execute or define commands.
It defines attributes for the node which can be used by recipes.
I would recomand reading THIS
and THIS
Some of the json content is generated by vagrant like defining the runlist attribute with the chef.add_recipe keyword in the vagrantfile.
For your use case you should have a cookbook with a recipe parsing node['apt'] and using deb_package resource.

List all the declared packages in chef

I'm working on a infrastructure where some servers don't have access to the internet, so I have to push the packages to the local repo before declaring them to be installed on Chef.
However we've been on a situation where Chef failed to install a package since the package wasn't there on some boxes and it has been successful on some other boxes.
What I want to do is to run a Ruby/RSpec test before applying Chef config on the nodes to make sure the packages declared on the recipes do actually exist on the repo.
In order to do that I need to be able to list all the packages exists in the our recipes.
My question is: Is there anyway to list all the declared packages in Chef? I had a quick look at Chef::Platform and ChefSpec but unfortunately couldn't find anything useful to my problem.
Do you have any idea where is the best place to look at?
If you use ChefSpec you can find all the packages by calling chef_run.find_resources(:package) inside some test. See the source code. Like this:
require 'chefspec'
describe 'example::default' do
let(:chef_run) { ChefSpec::Runner.new.converge(described_recipe) }
it 'does something' do
chef_run.find_resources(:package)...
end
end
You could install one or more of the community ohai plugins. For example the following will return information about installed sofware:
debian
Redhat
windows
Once the plugins are enabled they will add additional node attributes that will be searchable from chef-server.

Ruby code on Chef as a "ruby_block" not working

I have a question for the Ruby and Chef hackers.
I have very limited knowledge of Chef and even less on Ruby programming language, however, I need to implement on Chef (chef-solo) something similar to "augeas" (which works with Puppet, but here I need a solution for Chef).
I got the example code below but it's not working and I am now for a few days trying to figure out what is wrong.
Basically I need to be able to select specific strings in a text file and modify these values. I could use sed but perhaps I can do it in a more elegant way using the ruby_block from Chef.
Please let me know what can be possibly wrong with the code below. Why is my /etc/hosts not being updated with new values?
Always when I re-run chef-solo, I get the following error:
NoMethodError
-------------
undefined method `chef' for Chef::Resource::RubyBlock
Thanks for your help.
Follows my default.rb file:
ruby_block "edit etc hosts" do
block do
rc = Chef::Util::FileEdit.new("/etc/hosts")
rc.search_file_replace_line(
/^127\.0\.0\.1 localhost$/,
"127.0.0.1 #{new_fqdn} #{new_hostname} localhost"
)
rc.write_file
end
end
Add this line as the first line of your ruby block:
require 'chef/util/file_edit'
According to your case, you should use the cookbook hostsfile:
hostsfile_entry '127.0.0.1' do
hostname new_hostname
aliases [new_fqdn]
comment 'Append by Recipe X'
action :append
end
It shouldn't be too hard to get Augeas to work with Chef. Augeas is a C library, and Puppet simply uses its Ruby bindings. You just need to make use of these bindings in Chef.
There is a PoC Augeas resource provider for Chef here: https://github.com/craigtracey/augeas.
Note: http://lists.opscode.com/sympa/arc/chef/2013-02/msg00337.html mentions Augeas integration into Chef, but apparently the participants misunderstand Augeas as they mention idempotency issues and deltas. Most uses of Augeas don't lead to managing deltas, but desired states.

Make chef cookbook recipe only run once

So I use the following recipe:
include_recipe "build-essential"
node_packages = value_for_platform(
[ "debian", "ubuntu" ] => { "default" => [ "libssl-dev" ] },
[ "amazon", "centos", "fedora", "centos" ] => { "default" => [ "openssl-devel" ] },
"default" => [ "libssl-dev" ]
)
node_packages.each do |node_package|
package node_package do
action :install
end
end
bash "install-node" do
cwd Chef::Config[:file_cache_path]
code <<-EOH
tar -xzf node-v#{node["nodejs"]["version"]}.tar.gz
(cd node-v#{node["nodejs"]["version"]} && ./configure --prefix=#{node["nodejs"]["dir"]} && make && make install)
EOH
action :nothing
not_if "#{node["nodejs"]["dir"]}/bin/node --version 2>&1 | grep #{node["nodejs"]["version"]}"
end
remote_file "#{Chef::Config[:file_cache_path]}/node-v#{node["nodejs"]["version"]}.tar.gz" do
source node["nodejs"]["url"]
checksum node["nodejs"]["checksum"]
notifies :run, resources(:bash => "install-node"), :immediately
end
It successfully installed nodejs on my Vagrant VM but on restart it's getting executed again. How do I prevent this? I'm not that good in reading ruby code.
To make the remote_file resource idempotent (i.e. to not download a file already present again) you have to correctly specify the checksum of the file. You do this in your code using the node["nodejs"]["checksum"] attribute. However, this only works, if the checksum is correctly specified as the SHA256 hash of the downloaded file, no other algorithm (esp. not MD5) is supported.
If the checksum is not correct, your recipe will still work. However, on the next run, Chef will notice that the checksum of the existing file is different from the one you specified and will download the file again, thus notify the install node ressource and do the whole compile stuff.
With chef, it's important that recipes be idempotent. That means that they should be able to run over and over again without changing the outcome. Chef expects to be able to run all the recipes on a node periodically, and that should be ok.
Do you have a way of knowing which resource within that recipe is causing you problems? The remote_file one is the only one I'm suspicious of being non-idempotent, but I'm not sure offhand.
Looking at the Chef wiki, I find this:
Deprecated Behavior In Chef 0.8.x and earlier, Remote File is also
used to fetch files from the files/ directory in a cookbook. This
behavior is now provided by #Cookbook File, and use of Remote File for
this purpose is deprecated (though still valid) in Chef 0.9.0 and
later.
Anyway, the way chef tends to work, it will look to see if whatever "#{Chef::Config[:file_cache_path]}/node-v#{node["nodejs"]["version"]}.tar.gz" resolves to exists, and if it does, it should skip that resource. Is it possible that install-node deletes that file when it's finished installing? If so, chef will re-fetch it every time.
You can run a recipe only once overriding the run-list with -o modifier.
sudo chef-client -o "recipe[cookbook::recipe]"
-o RunlistItem,RunlistItem..., Replace current run list with specified items
--override-runlist
In my experience remote_file always runs when executing chef-client, even if the target file already exists. I'm not sure why (haven't dug into the Chef code to find the exact cause of the bug), though.
You can always write a not_if or only_if to control the execution of the remote_file resource, but usually it's harmless to just let it run every time.
The rest of your code looks like it's already idempotent, so there's no harm in running the client repeatedly.
There's an action you can specify for remote_file that will make it run conditionally:
remote_file 'target' do
source 'wherever'
action :create_if_missing
end
See the docs.
If you want to test whether your recipe is idempotent, you may be interested in ToASTER, a framework for systematic testing of Chef scripts.
http://cloud-toaster.github.io/
Chef recipes are executed with different configurations in isolated container environments (Docker VMs), and ToASTER reports various metrics such as system state changes, convergence properties, and idempotence issues.

Resources