Chef - template inside a provider not finding source - ruby

I have a little resource and provider for nginx sites which writes out a config file for a site.
action :start do
template "/etc/nginx/sites-enabled/my_site" do
source "nginx_site.conf.erb"
notifies :reload, "service[nginx]"
end
end
When I use it from another cookbook the template nginx_site.conf.erb is not found as chef is looking for a template where this resource is called from.
Is there a way to tell chef to look for a template inside the nginx resource & provider cookbook?

You can set cookbook value for template.
action :start do
template "/etc/nginx/sites-enabled/my_site" do
source "nginx_site.conf.erb"
notifies :reload, "service[nginx]"
cookbook 'nginx'
end
end

Related

Reuse template across different recipes

I have a Chef cookbook with many recipes that have the same code, beside other particular things.
template 'stack_file' do
local true
source File.join(base_dir, 'stack_templates/admin.yml.erb')
path File.join(base_dir, 'stacks/admin.yml')
variables(context)
end
template 'settings_file' do
sensitive true
local true
source File.join(base_dir, 'config_templates/settings_admin.yml.erb')
path File.join(base_dir, 'configs/settings_admin.yml')
variables(context)
end
Is it possible to somehow put this code in a method that I would call with my source_file, destination_file and variables?
I guess you can write a module and include it in your recipes as you do in plain Ruby.
module StackFile
...the code you want to share...
end
Then you can use:
inlclude StackFile
or
Chef::Recipe.send(:include, StackFile)
or when using *_if conditions
Chef::Resource.send(:include, StackFile)
Do this:
create new cookbook.
don't create recipes in it, but instead create a resource
you can define any parameters (inputs like source file, dest file etc like you mentioned)
add your templates to the new cookbook.
in your other cookbooks, create dependency to the previously created cookbook. This will enable you to call the resource you created there (remember that when you call resource from another cookbook and it is creating templates, it will try to take the template file from the current cookbook and not the one where the resource is defined. That is why you need to specify cookbook name when creating template (in the shared cookbook's resource) - see cookbook attribute of https://docs.chef.io/resource_template.html)
Repeat for any number of cookbooks.

Chef template resource to execute if the source template changes?

I'm deploying a package that requires a template be created in a specified directory every time a directory is unzipped.
A remote_file notifies my unzip action, that unzip action notifies the template resource, which in turn notifies other resources. This chain of notifications works as expected.
Below is my template resource:
template 'C:\\Program Files\\MyProgram\\program.yml' do
source "my_program-#{node['program']['version']}.yml.erb"
action :nothing
notifies :run, 'powershell_script[install-program]', :immediately
end
My question: Is there a way to have the template resource execute if I make a change to the source template? Right now it only executes the template resource if notified by my unzip action (due to my action :nothing).
However, it would be great to have a way for it to tell if the template itself has changed. Perhaps some kind of not_if or only_if statement?
sounds to me that you avoid all the notification chaining if you will have your resources defined in the same recipe.
back to your questions, it sounds that setting action :create, which is the default action, will do the trick. from the template resource documentation
action :create
Create a file. If a file already exists (but does not match), update that file to match.

Why chef does not execute recipe line by line?

Problem is that chef tries to install template first, and only then installs packages. If i comment template block, chef will install sphinxsearch package fine.
But if template block is not commented, sphinxsearch package is not installed, and chef fails with error
resource template[/etc/sphinxsearch/sphinx.conf] is configured to notify resource service[sphinxsearch] with action reload, but service[sphinxsearch] cannot be found in the resource collection`
Why this happens?
##
# Install system packages
##
node['website']['packages'].each do |pkg|
log 'Installing ' + pkg
package pkg
end
##
# Configure sphinx
##
template "/etc/sphinxsearch/sphinx.conf" do
source 'sphinx.erb'
owner 'root'
group 'root'
mode 00644
notifies :reload, 'service[sphinxsearch]', :delayed
end
notifies and subscribes in chef are both trying to reach out to resources that have been defined in your chef run. They will then call teh indicated action on those resources. In your case:
notifies :reload, 'service[sphinxsearch]', :delayed
is looking for a resource of type service named sphinxsearch and call the reload action on it. If, at the end of the resource gathering (compile) phase, chef cannot find a service[sphinxsearch] resource, then it throws the error. You don't see the package installed because chef never enters the execution phase. (See this answer for more on the two phase nature of chef)
As indicated by #IsabelHM, you could solve the problem by adding
service 'sphinxsearch' do
action [:enable, :start]
end
I suggest you use [:enable, :start] rather than :nothing as this will ensure that the service is always running, even if your template doesn't change. Also, please note that the service resource does not add a service config for you. So if the sphinxsearch package does not add a service config, you'll also need a cookbook_file, template, or remote_file resource to create the service config with.
Add this in your recipe.
service 'sphinxsearch' do
action :nothing
end

Chef 12.0.3: Why I can't find my own provider on Resource Collection?

I wrote my own provider which runs ok, for example:
my_custom_provider "#{node['ipaddress']}" do
my_attribute_1 node['ipaddress']
action :create
end
template '/opt/app/something.conf' do
mode '0664'
owner 'someuser'
group 'someuser'
notyfies :restart, "my_custom_provider[#{node['ipaddress']}]", :delayed
end
I receiving error (under chef-solo in Vagrant):
ERROR: resource template[/opt/app/something.conf] is configured to notify
resource my_custom_provider[10.0.2.15] with action restart, but
my_custom_provider[10.0.2.15] cannot be found in the resource collection.
template[/opt/app/something.conf] is defined in
/tmp/vagrant-chef/2a59b2477390af49acec413909d80cf5/cookbooks/project/recipes/default.rb:76:in `from_file'
But when I reverse direction of notification it works fine:
my_custom_provider "#{node['ipaddress']}" do
my_attribute_1 node['ipaddress']
action :create
subscribes :restart, 'template[/opt/app/something.conf]', :delayed
end
template '/opt/app/something.conf' do
mode '0664'
owner 'someuser'
group 'someuser'
end
I think this is a bug in chef-solo run under vagrant or maybe I can't add my custom providers as notification target ?
To get notifications working on your custom LWRPs, you might want to look at the example here in chef docs https://docs.chef.io/lwrp_custom_provider.html#updated-by-last-action

Test file initialization based off template using ChefSpec

I've got the following template file creation in my cookbook:
template "my_file" do
path "my_path"
source "my_file.erb"
owner "root"
group "root"
mode "0644"
variables(#template_variables)
notifies :restart, resources(service: "my_service")
end
and the following assertions in my ChefSpec tests:
chef_run.should create_file "my_file"
chef_run.file("my_file").should be_owned_by('root', 'root')
Which results in the following failure:
No file resource named 'my_file' with action :create found.
This is due to the fact that I am not using afile resource but a template resource. Question: How can I test for file creation off a template resource using ChefSpec?
There are two ways to solve your problem.
First, you can use the create_template matcher. This will match only "template" resources in the run context:
expect(chef_run).to create_template('my_file')
This matcher is also chainable, so you can assert attributes:
expect(chef_run).to create_template('my_file')
.with_path('my_path')
.with_owner('root')
However, this matcher won't actually render the template. So you can't check if you've setup file-specificity correctly.
There's also a top-level matcher for any kind of "file" (file, cookbook_file, and template) that actually renders the contents in memory:
expect(chef_run).to render_file('my_file').with_content(/^match me$/)
You can find more information about render_file in the README.
According to the docs (https://github.com/acrmp/chefspec) you should be able to use:
expect(chef_run).to create_file 'my_file'
I think something changed very recently (possibly the version of chefspec on rubygems), however, because tests I had passing earlier today (using the same syntax you are using) are now suddenly failing.

Resources