Chef::Exceptions::CookbookNotFound: Cookbook sudo not found and Chef-Solo “undefined method `[]' for nil:NilClass” - ruby

I'm trying to make a cookbook that has some dependencies, but it doesn't work
/recipe
ls
default.rb
Recipe:
include_recipe 'sudo::default'
include_recipe 'user'
def will_create_user?(username, user)
return false if node['etc']['passwd'].key?(username)
return true if user['action'] == 'create' || !user.key?('action')
false
end
node['authorization']['sudo']['groups'].each do |group|
group group
end
node['vms']['users'].each do |username, user|
send_notification = will_create_user? username, user
user_account username do
comment user['comment'] if user['comment']
ssh_keys user['ssh_keys']
groups user['groups'] if user['groups']
if send_notification
notifies :run, "execute[reset_password_#{username}]", :delayed
end
end
execute "reset_password_#{username}" do
command "passwd -d #{username} && chage -d 0 #{username}"
action :nothing
end
end
Metadata.rb
...
version '0.1.0'
chef_version '>= 14.0'
depends "sudo"
depends "user"
Berksfile.lock
DEPENDENCIES
vms-users
path: .
metadata: true
GRAPH
sudo (5.4.5)
user (0.7.0)
vms-users (0.1.0)
sudo (>= 0.0.0)
user (>= 0.0.0)
Attributes/default.rb
{
"vms": {
"users": {
'magrini' => {
'comment' => 'Bruna Magrini',
'groups' => ['sysadmin'],
'ssh_keys' => ['chave ssh'],
},
}
}
}
I'm executing using chef-client --local-mode default.rb
Error: Chef::Exceptions::CookbookNotFound: Cookbook sudo not found
Recipe `sudo::default` is not in the run_list, and cookbook 'sudo'
is not a dependency of any cookbook in the run_list. To load this recipe,
first add a dependency on cookbook 'sudo' in the cookbook you're
including it from in that cookbook's metadata.
Running handlers:
[2019-12-19T20:42:12+00:00] ERROR: Running exception handlers
Running handlers complete
[2019-12-19T20:42:12+00:00] ERROR: Exception handlers complete
Chef Infra Client failed. 0 resources updated in 01 seconds
[2019-12-19T20:42:12+00:00] FATAL: Stacktrace dumped to /home/chef-repo/.chef/local-mode-cache/cache/chef-stacktrace.out
[2019-12-19T20:42:12+00:00] FATAL: Please provide the contents of the stacktrace.out file if you file a bug report
[2019-12-19T20:42:12+00:00] FATAL: Chef::Exceptions::CookbookNotFound: Cookbook sudo not found. If you're loading sudo from another cookbook, make sure you configure the dependency in your metadata

i have a feeling that you are confusing something, since the title of your question mentions chef-solo when you are really using chef-zero (--local-mode).
you should definitely favor chef-zero over chef-solo (but i won't get into the reasons why).
in both cases (using chef-zero or chef-solo), you will have to download all the cookbooks and make verify chef-client knows where are the cookbooks located.
if you ase using chef-zero, here are some references:
Local mode does not require a configuration file, instead it will look for a directory named /cookbooks and will set chef_repo_path to be just above that. (Local mode will honor the settings in a configuration file, if desired.) If the client.rb file is not found and no configuration file is specified, local mode will search for a config.rb file.
client.rb settings:
chef_repo_path: The path to the chef-repo containing cookbooks and other files, such as environments or data bags, when running Chef Infra Client in local mode.
cookbook_path: The sub-directory for Chef Infra Client cookbooks. This value can be a string or an array of file system locations, processed in the specified order. The last cookbook is considered to override local modifications.
since i see that you are using berkshelf, you can use vendor sub-command to download all the cookbooks dependencies and place them in the same directory. then, have a custom configuration for chef-client, that sets the value of cookbook_path to the same directory which you used in conjunction with berks vendor, and finally execute chef-client.

Related

Chef metadata.rb dependency needs to depend on itself

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.

Adding to the Chef Gem Source?

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

Can you mix chef-zero chef-metal, chef-metal-vagrant (Vagrant) and berkshelf?

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.

Chef undefined method `use_etag' for Chef::Resource::RemoteFile

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.

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