Understanding Chef Environment Attributes - ruby

Background
I am trying to understand how to correctly work with environment attributes in recipes. Note: I understand that my approach may not be Chef's best practices; however, my goal is to understand data flow here, not to necessarily use my approach in a production application.
Some details:
I am using Vagrant and Chef -- specifically, the chef-zero
provisioner (Thus I must use ruby files for my environments rather
than JSON due to a chef-vagrant limitation).
Here is my directory structure:
├── environments
├── vm.rb
├── roles
├── base.json
├── cookbooks
├── init
├── recipes
├── default.rb
├── Vagrantfile
Here are my files
Environment: vm.rb
name "vm"
description "Configuration file for the Kukla Demo VM"
default_attributes(
custom_demo: {
title: 'My demo title',
description: 'My demo description'
))
Role: base.json
{
"name": "base",
"description": "Base VM configuration",
"chef_type": "role",
"json_class": "Chef::Role",
"default_attributes": {},
"override_attributes": {},
"run_list": ["recipe[init::default]"]
}
Recipe: default.rb
#
# Cookbook:: init
# Recipe:: default
#
# Copyright:: 2020, The Authors, All Rights Reserved.
print 'I am here!'
print node[:custom_demo]
Chef provisioner in the Vagrantfile
...
chef.nodes_path = 'nodes/'
chef.environments_path = 'environments/'
chef.roles_path = 'roles/'
chef.cookbooks_path = 'cookbooks/'
# Roles
chef.add_role "base"
...
Expected Behavior
When I run the provisioner, I expect to see the custom_demo hash printed in the chef run log. Something like:
...
==> Machine: Synchronizing Cookbooks:
==> Machine: [2020-02-07T20:37:15+00:00] INFO: Storing updated cookbooks/init/recipes/default.rb in the cache.
==> Machine:
==> Machine: - init (0.1.0)
==> Machine: Installing Cookbook Gems:
==> Machine: Compiling Cookbooks...
==> Machine: I am here! custom_demo => { :title => 'My demo title', :description => 'My demo description' }
==> Machine: Converging 0 resources
==> Machine: [2020-02-07T20:37:15+00:00] INFO: Chef Infra Client Run complete in 0.118042019 seconds
...
Actual behavior
Instead, I get:
...
==> Machine: Synchronizing Cookbooks:
==> Machine: [2020-02-07T20:37:15+00:00] INFO: Storing updated cookbooks/init/recipes/default.rb in the cache.
==> Machine:
==> Machine: - init (0.1.0)
==> Machine: Installing Cookbook Gems:
==> Machine: Compiling Cookbooks...
==> Machine: I am here!
==> Machine: Converging 0 resources
==> Machine: [2020-02-07T20:37:15+00:00] INFO: Chef Infra Client Run complete in 0.118042019 seconds
...
My Question(s):
Based on the result, I'm left with the following questions:
Is my understanding of environment attributes (and therefore) my expectation correct?
If not, what am I missing? Can environment attributes be used in this fashion?
What is the proper way to debug whether an environment is being used by chef or not?
What is the proper way to reference environment attributes in a recipe so that I achieve the expected result?

You are doing everything right and your expectations are correct. The only thing, you are missing, is that you do not assign the environment to the vm. For that you need to add a line in your Vagrantfile:
...
chef.nodes_path = 'nodes/'
chef.environments_path = 'environments/'
chef.roles_path = 'roles/'
chef.cookbooks_path = 'cookbooks/'
chef.environment = 'vm' # the added line
# Roles
chef.add_role "base"
...
To understand, if the node has the right environment set, you can just print out in your recipe:
p node.chef_environment
# or
p node.environment
Concerning your last question, you actually cannot reference from a recipe environment attributes. Chef collects attributes from different sources (recipe, environment, node etc.) and combines them into 1 attribute hash using precedence, so in the end you can only reference the attribute with highest precedence or with some particular precedence, but not by the attribute source (environment, node or recipe).
node[:custom_demo] # highest precedence
node.default[:custom_demo] # default precedence, ignoring override and normal

Related

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

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.

confused with berkshelf, chef and vagrant

I've took a look on BerkShelf documentation. I've been able to figure out it stands for managing cookbook dependencies. So, guess I'd like to build a machine with java. I've first generated my custom cookbook:
chef generate cookbook custom-java
My Berksfile is:
source 'https://supermarket.chef.io'
metadata
cookbook 'java'
and my metadata.rb is:
name 'custom-java'
...
version '0.1.0'
After that, I've simply run berks install, so all dependencies have been resolved and located under ~\.berkshelf\cookbooks.
Nevertheless, I don't quite figure out how to use my custom-java into my vagrant configuration. What do I need to do in order for vagrant to provistion this cookbook into my machines?
My vagrant structure is:
VagrantFile
├───chef
│ ├───cookbooks
│ │ ├───(((1))) <<<<<<<<<<<<<<<<<<<<<<
│ ├───roles
│ │ ├───java-dev-workstation.rb
Vagrantfile content is:
Vagrant.configure("2") do |config|
config.vm.box = "centos/7"
config.vm.provision "chef_solo" do |chef|
chef.roles_path = "chef/roles"
chef.cookbooks_path = "chef/cookbooks"
chef.add_role "java-dev-workstation"
end
end
And java-dev-workstation.rb:
name "java-dev-workstation"
default_attributes(
# Because it's Oracle, we need to agree to the terms and conditions.
:custom-java => { >>>>>>>>> (((2)))
:install_flavor => 'oracle',
:jdk_version => '8',
:accept_license_agreement => true,
:oracle => { "accept_oracle_download_terms" => true }
}
)
run_list(
"recipe[java]"
)
I'm using Chef 12.18.31.
On (((1))): Do I need to "import" my custom-java cookbook, how? Where is it located?
On (((2))): How should I configure java?
EDIT
I've set chef.cookbooks_path:
config.vm.provision "chef_solo" do |chef|
chef.roles_path = "chef/roles"
chef.cookbooks_path = 'D:\projects\living\vagrant\production\berk\custom-java'
chef.add_role "java-dev-workstation"
end
EDIT2
Nevertheless, custom-java dependencies are not resolved now:
================================================================================
Error Resolving Cookbooks for Run List:
================================================================================
Missing Cookbooks:
------------------
No such cookbook: yum
Expanded Run List:
------------------
* custom-java
Platform:
---------
x86_64-linux
My metadata.rb content is:
name 'berk'
...
version '0.1.0'
supports 'centos'
depends 'yum'
Currently, all dependencies are located in ~/.berkshelf/cookbooks. It seems shef-solo is not looking for in it...
You did take it backward for a wrapper cookbook pattern, your custom_java should depends on java and change its default behavior (overriding node attributes).
Your custom-java metadata.rb should contain a line like this, what is in the berksfile will never be interpreted by the chef-client run, it's an outside dependency resolver to create coherent bundles.
depends 'java', '~> 1.47'
And the default.rb a line
include_recipe 'java'
Then your Berksfile can omit the cookbook line (unless you're pulling a version elsewhere than the source at top)
Next your role should use custom-java as recipe and not java, then the chef-client run (the vagrant part before edit sounds ok and should pull both cookbooks) will compute attributes from the java coobooks, overwrite those defined in custom-java and you should end up with the desired behavior.
Alternatively, you can just set the runlist to custom-java and avoid the role altogether, that will works.
I highly recommend you to go through all the tutorials on https://learn.chef.io to get a better overview.

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.

Vagrant not able to find home/vagrant/hiera.yaml

I have inherited a python app that uses Puppet, Vagrant and VirtualBox to provision a local Centos test machine.
This app was written on Mac and I'm developing on Windows.
When I run vagrant up I see a large list of command line errors, the most relevant of which is:
Running Puppet with site.pp..
Warning: Config file /home/vagrant/hiera.yaml not found, using Hiera defaults
WARN: Fri Apr 25 16:32:24 +0100 2014: Not using Hiera::Puppet_logger. It does not report itself to b
e suitable.
I know what Hiera is, and why it's important, but I'm not sure how to fix this.
The file hiera.yaml is present in the repo but it's not found at home/vagrant/hiera.yaml, instead it's found at ./puppet/manifests/hiera.yaml.... Similarly if I ssh into the box, there is absolutely nothing whatsoever inside home/vagrant but I can find this file when I look in /tmp
So I'm confused, is vagrant looking inside the Virtual box and expecting this file to be found at home/vagrant/hiera.yaml? Or have I inherited an app that did not work properly in the first place? I'm really stuck here and I can't get in touch with the original dev.
Here are some details from my Vagrantfile:
Vagrant.configure("2") do |config|
# Base box configuration ommitted
# Forwarded ports ommitted
# Statically set hostname and internal network to match puppet env ommitted
# Enable provisioning with Puppet standalone
config.vm.provision :puppet do |puppet|
# Tell Puppet where to find the hiera config
puppet.options = "--hiera_config hiera.yaml --manifestdir /tmp/vagrant-puppet/manifests"
# Boilerplate Vagrant/Puppet configuration
puppet.module_path = "puppet/modules"
puppet.manifests_path = "puppet/manifests"
puppet.manifest_file = "site.pp"
# Custom facts provided to Puppet
puppet.facter = {
# Tells Puppet that we're running in Vagrant
"is_vagrant" => true,
}
end
# Make the client accessible
config.vm.synced_folder "marflar_client/", "/opt/marflar_client"
end
It's really strange.
There's this puppet.options line that tells Puppet where to look for hiera.yaml and currently it simply specifies the file name. Now, when you run vagrant up, it mounts the current directory (the one that has the Vagrantfile in it) into /vagrant on the guest machine. Since you're saying hiera.yaml is actually found in ./puppet/manifests/hiera.yaml, I believe you want to change the puppet.options line as follows:
puppet.options = "--hiera_config /vagrant/puppet/manifests/hiera.yaml --manifestdir /tmp/vagrant-puppet/manifests"
To solve this warning, you may first try to check from where this file is referenced, i.e.:
$ grep -Rn hiera.yaml /etc/puppet
/etc/puppet/modules/stdlib/spec/spec_helper_acceptance.rb:13: on hosts, '/bin/touch /etc/puppet/hiera.yaml'
/etc/puppet/modules/mysql/spec/spec_helper_acceptance.rb:34: shell("/bin/touch #{default['puppetpath']}/hiera.yaml")
In this case it's your vagrant file.
You may also try to find the right path to it via:
sudo updatedb && locate hiera.yaml
And either follow the #EvgenyChernyavskiy suggestion, create a symbolic link if you found the file:
sudo ln -vs /etc/hiera.yaml /etc/puppet/hiera.yaml
or create the empty file (touch /etc/puppet/hiera.yaml).
In your case you should use the absolute path to your hiera.yaml file, instead of relative.
The warning should be gone.

vagrantfile cookbook path

I want to set the cookbook path to a certain place, so that I don't need to
modify the Vagrantfile everytime(after vagrant init).
I find Vagrantfile load several places, so I decide to set my cookbook path info
in ~/.vagrant.d/Vagrantfile,(this file is the 3rd of Vagrantfile Load Order) like:
...
config.vm.provision :chef_solo do |chef|
chef.cookbooks_path = ["D:/lib/chef/cookbooks"]
chef.add_recipe "dev::default"
end
...
but when I make a new vm, and modify Vagrantfile(this file is the 4th of Vagrantfile Load Order):
...
config.vm.provision :chef_solo do |chef|
chef.add_recipe "torch"
end
...
error:
[2013-02-28T03:23:36+00:00] ERROR: Running exception handlers
[2013-02-28T03:23:36+00:00] ERROR: Exception handlers complete
[2013-02-28T03:23:36+00:00] FATAL: Stacktrace dumped to /tmp/vagrant-chef-1/chef-stacktrace.out
[2013-02-28T03:23:36+00:00] FATAL: Chef::Exceptions::CookbookNotFound: Cookbook torch not found. If
you're loading torch from another cookbook, make sure you configure the dependency in your metadata
Chef never successfully completed! Any errors should be visible in the
output above. Please fix your recipes so that they properly complete.
but I am sure the specific cookbook is under my cookbook path.
Try the following steps:
- Create a folder cookbooks in the vagrantfile's directory
- checkout the cookbooks to this directory
- add the following to your vagrantfile
chef.cookbooks_path = ["cookbooks"]
hope to have helped

Resources