I am getting a foodcritic error when I try to include a recipie within the default.rb for the apache cookbook lesson:
#
# Cookbook:: apache
# Recipe:: default
#
# Copyright:: 2017, The Authors, All Rights Reserved.
# Install apached package
package 'apache2' do
package_name 'httpd'
action :install
end
service 'apache2' do
service_name 'httpd'
action [:start, :enable]
end
include_recipe 'apache::websites'
websites.rb
file 'default www' do
path '/var/ww/html/index.html'
content 'Hello world!'
end
When I run foodcritic on default.rb
FC007: Ensure recipe dependencies are reflected in cookbook metadata:
default.rb:17
When I follow the recomendation and add the dependency in the metadata.rb and run knife cookbook upload apache I get this:
ERROR: RuntimeError: Cookbook depends on itself in cookbook apache, please
remove the this unnecessary self-dependency
Below is my metadeta.rb
name 'apache'
maintainer 'xxxxx'
maintainer_email 'xxxxx'
license 'All Rights Reserved'
description 'Installs/Configures apache'
long_description 'Installs/Configures apache'
version '0.1.1'
chef_version '>= 12.1' if respond_to?(:chef_version)
depends 'apache'
#The `issues_url` points to the location where issues for this cookbook are
# tracked. A `View Issues` link will be displayed on this cookbook's page
when
# uploaded to a Supermarket.
#
# issues_url 'https://github.com/<insert_org_here>/apache/issues'
# The `source_url` points to the development repository for this cookbook.
A
# `View Source` link will be displayed on this cookbook's page when uploaded
to
# a Supermarket.
#
# source_url 'https://github.com/<insert_org_here>/apache'
New to chef and ruby, first question on Stack Overflow.
Am I adding the dependency wrong?
Does anything jump out that would cause this issue?
The FC007 activation is wrong. Are you sure you're running foodcritic correctly? Just run foodcritic . on the base of the cookbook folder (i.e. the same place as the metadata.rb is). That should detect the name of the cookbook and not flag the include.
My organization works entirely within the firewall (i.e, no machines have internet access). Whenever chef cookbooks are update and require gems the recipes fail due to the fact they cannot download and install gems from rubygems.org.
We self host copies of required gems - how can we add a custom internal source to chef, so that we don't have to deal with failures?
You can manage the gemrc for the embedded Ruby with Chef Client.
chef_etc_dir = Chef::Util::PathHelper.join(Chef::Config.embedded_dir, 'etc')
chef_gemrc = Chef::Util::PathHelper.join(chef_etc_dir, 'gemrc')
directory chef_etc_dir do
owner "root"
group "root"
mode "0755"
end.run_action(:create)
file chef_gemrc do
owner "root"
group "root"
mode "0644"
content <<EOF
---
:sources:
- https://path.to.internal.repo/
- https://rubygems.org
:update_sources: true
EOF
end.run_action(:create)
The PathHelper methods should give appropriate paths for either Windows or Linux.
The ".run_action(:create)" addition executes the resources at compile time, not converge time, to ensure subsequent chef_gem resources will have access to the newly-managed gemrc at converge time.
Update: Chef appears to recommend the rubygems cookbook to perform this functionality.
gemrc :global do
values(
sources: %w{ https://path.to.internal.repo https://rubygems.org }
)
end
I want to leverage chef-metal and chef-zero with my existing cookbooks and chef-repo (already leveraging berkshelf and vagrant for dev)
I started with the example provided at https://github.com/opscode/chef-metal#vagrant
I've got a vagrant_linux.rb
require 'chef_metal_vagrant'
vagrant_box 'CentOS-6.4-x86_64' do
url 'http://developer.nrel.gov/downloads/vagrant-boxes/CentOS-6.4-x86_64-v20130427.box'
end
with_machine_options :vagrant_options => {
'vm.box' => 'CentOS-6.4-x86_64'
}
I also have dev_server.rb
require 'chef_metal'
with_chef_local_server :chef_repo_path => '~/workspace/git/my-chef-repo'
machine 'dev_server' do
tag 'dev_server'
recipe 'myapp'
converge true
end
If I put my myapp cookbook under ~/workspace/git/my-chef-repo/cookbooks, the above works fine
using the following command, I've got a vagrant managed vm named dev_server converging (applying myapp recipe)
chef-client -z vagrant_linux.rb dev_server.rb
But now, I'd like to keep my cookbooks folder empty and use berkshelf,
It does not look supported by chef-zero at the moment, is it ?
How could I do that ?
You can pass :cookbook_path that contains multiple paths as an Array like so: https://github.com/opscode/ec-metal/blob/master/cookbooks/ec-harness/recipes/vagrant.rb#L12-L13
with_chef_local_server :chef_repo_path => repo_path,
:cookbook_path => [ File.join(repo_path, 'cookbooks'),
File.join(repo_path, 'vendor', 'cookbooks') ]
Then you can use berks to vendor upstream cookbooks into a different path (vendor/cookbooks/), while putting your own cookbooks into cookbooks/ like so: https://github.com/opscode/ec-metal/blob/master/Rakefile#L114
berks vendor vendor/cookbooks/
The "berks vendor" command is how I generally do that--use "berks vendor" and add the vendored path to your cookbook path.
When I run my cookbook on a node I get a compile error: "undefined method `use_etag' for Chef::Resource::RemoteFile" on my remote_file resource:
remote_file to.deb do
mode 0644
source 'https://path.com/to.deb'
use_etag true
use_conditional_get true
end
According to Chef doc, use_etag is a attribute of remote_file.
use_etag was not introduced until later versions of Chef (11.6.0 if I recall correctly). You may need to upgrade your Chef version.
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.