How to load attributes from a file to chefspec node - ruby

I have the following layout:
attributes/default.rb
recipes/my_recipe.rb
spec/unit/recipes/my_recipe_spec.rb
In the attributes files I have a lot of common settings likes
default['framework']['folder']['lib'] = '/usr/lib/fwrk'
I would like to use them in my chefspec, like
it 'install the lib if there are changes' do
lib_path = chef_run.node['framework']['folder']['lib']
puts(lib_path)
end
How can I include this file to my node from SoloRunner/ServerRunner?

Run the .converge() first and you'll see them in there. But remember that you almost ever parameterize your tests on the same inputs on both sides, that wouldn't be a useful test since it doesn't check if the value is what you expected it to be.

Related

Puppet - how to pass arguments to the command line

I am newbie to puppet and I wonder how I can pass arguments to the command line. I will explain myself:
This is the command that I'm running (puppet apply):
C:>puppet apply --environment test -l C:\Puppet_logs\log.log C:\ProgramData\PuppetLabs\code\environments\test\manifests\site.pp
Site.pp:
File { backup => false }
node default {
include 'tn'
}
It means that I am running 'tn' which is one of the modules in my puppet project.
For example,
I have these modules in my puppet project:
tn
ps
av
So to run each module I need to go to this site.pp file and change it to
include 'ps'
or
include 'av'
My question is -
How do I pass these modules as arguments to the puppet apply command?
I know that I can create 3 .pp files that each one contains one module (ps, av, tn)
And then my command will look like:
puppet apply --environment test -l C:\Puppet_logs\log.log C:\ProgramData\PuppetLabs\code\environments\test\manifests\ps.pp
puppet apply --environment test -l C:\Puppet_logs\log.log C:\ProgramData\PuppetLabs\code\environments\test\manifests\av.pp
puppet apply --environment test -l C:\Puppet_logs\log.log C:\ProgramData\PuppetLabs\code\environments\test\manifests\tn.pp
But, I think it's not a good solution..
Is there another way to pass these modules as arguments to the puppet apply?
If I didn't mention - each module is responsible for different actions.
thanks !!!
I know that I can create 3 .pp files that each one contains one module
(ps, av, tn)
[...]
But, I think it's not a good solution.
Why isn't it a good solution? It seems perfectly sensible to me that if you have three different things you want to be able to do, then you have a separate file to use to accomplish each.
Nevertheless, if your modules do not use each other, then you could probably accomplish what you describe by relying on tags. Have your site manifest include all three modules:
File { backup => false }
node default {
include 'tn'
include 'ps'
include 'av'
}
Then use the --tags option to select only one of those modules and all the other classes it brings in:
puppet apply --tags ps --environment test -l C:\Puppet_logs\log.log C:\ProgramData\PuppetLabs\code\environments\test\manifests\site.pp
A pp file is a class file not a module, a module contains the classes and anything else needed to support/test those classes, take a look at https://puppet.com/docs/puppet/5.5/modules_fundamentals.html.
Look at how modules are laid out on https://forge.puppet.com/
It’s well worth looking at the PDK https://puppet.com/docs/pdk/1.x/pdk.html as it'll build a module for you, you just need to add the classes.
In your case you probably want to create a new module (let’s call it mymodule) and in that module put all your tn.pp ps.pp and av.pp class files under the C:\ProgramData\PuppetLabs\code\environments\test\modules\mymodule\manifests directory.
Then for local testing use the examples pattern, so in your module you’ll have an examples directory and in there you might have a file called ps.pp which would contain include mymodule::ps to include that ps.pp class file.
The aim of the examples directory is to give you a method of passing in parameters for local testing.
Back in your site.pp file you’d apply is with:
Node default {
Include mymodule::ps
}
So now you want to apply different classes to the nodes and there you hit the world of node classification and there are many ways you can do that. In your case I think you’re probably doing this on a small scale so you’d have;
Node psserver.example.com {
Include mymodule::ps
}
Node tnserver.example.com {
Include mymodule::tn
}
Have a look at some of the online training https://puppet.com/learning-training/kits/puppet-language-basics

How does Chef include files generated on runtime as a template source

Using Chef recipe, I am first generating a .erb file dynamically based on inputs from a CSV file and then I want to use that .erb file as a template source. But unfortunately the changes made (in .erb file) are not considered while the recipe is converging the resources. I also tried to use lazy evaluation but not able to figure out how to use it for the template source.
Quoting the template documentation:
source Ruby Types: String, Array
The location of a template file. By default, the chef-client looks for
a template file in the /templates directory of a cookbook. When the
local property is set to true, use to specify the path to a template
on the local node. This property may also be used to distribute
specific files to specific platforms. See “File Specificity” below for
more information. Default value: the name of the resource block. See
“Syntax” section above for more information.)
And
local
Ruby Types: TrueClass, FalseClass
Load a template from a local path. By default, the chef-client loads
templates from a cookbook’s /templates directory. When this property
is set to true, use the source property to specify the path to a
template on the local node. Default value: false.
so what you can do is:
# generate the local .erb file let's say source.erb
template "/path/to/file" do
source "/path/to/source.erb"
local true
end
Your question sounds like and XY problem, reading a csv file to make a template sounds counter-productive and could probably be done with attributes and taking advantage of the variable attribute of template resource.
Assuming you know how to capture the values from the CSV file as a local variable in the recipe.
Examples:
csv_hostname
csv_fqdn
Here is what you do to create a template with lazy loading attributes. The following example creates a config file.
example.erb file
# Dynamically generated by awesome Chef so don't alter by hand.
HOSTNAME=<% #host_name %>
FQDN=<% #fqdn %>
recipe.rb file
template 'path\to\example.config' do
source 'example.erb'
variables(
lazy {
:host_name => csv_hostname,
:fqdn => csv_fqdn
})
end
If you need it to run at compile time, add the action to the block.
template 'xxx' do
# blah blah
end.run_action(:create)

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.

Why would I get "Errno::ENOENT: No such file or directory" when viewing a Sinatra form?

I am trying to follow this tutorial:
http://net.tutsplus.com/tutorials/ruby/singing-with-sinatra/
Got stuck in "We’ll also make use of a “view file”, which allows us to split the markup for a view into a separate file. "
I have my basics.rb file running fine.
And My files are stored as follows:
Desktop/RubyForm/basics.rb
Desktop/RubyForm/view/form.erb
However, now when i go to http://localhost:9393/form , I am greeted with:
Errno::EIO at /form
Input/output error - <STDERR> file: lint.rb location: write line: 398
sinatra.error
Errno::ENOENT: No such file or directory -
/Users/HelenasMac/Desktop/views/form.erb
UPDATE! : Got the form to work right after running ruby basics.rb and going to http://localhost:4567/form .
However, after I run "shotgun basics.rb" , I have to go to
http://localhost:9393/form, and that's when the form doesn't show up.
What am I doing wrong? Disclaimer: mega beginner to ruby and using the terminal.
Thanks in advance!
If you cannot get shotgun to work then the new recommended way to reload Sinatra seems to be rerun.
To use it:
> gem install rerun
> cd /Users/HelenasMac/Desktop/RubyForm
> rerun ruby basics.rb
Explicity Set a Views Directory
Unless you're using inline template for your views with enable :inline_templates, you may need to explicitly define a template directory if the default values aren't working for you. The docs describe how to set your views directory as follows:
:views - view template directory
A string specifying the directory where view templates are located. By default, this is assumed to be a directory named “views” within the application’s root directory (see the :root setting). The best way to specify an alternative directory name within the root of the application is to use a deferred value that references the :root setting:
set :views, Proc.new { File.join(root, "templates") }
You may also need to explicitly set :root, and make sure that both :root and :views make sense from your current working directory.

Setting up rake-pipeline for use with handlebars alongside Google App Engine

So here's what I'm attempting to do. I'm building an ember.js application, with a java backend running on GAE.
I'm using handlebars, but I want them divided up into separate files, not just all pasted into the index.html.
Via the ember.js irc I was turned on to rake-pipeline along with minispade
Along with the web filters and a custom handlebars filter I started building the assetfile. I don't know Ruby, or gem files, etc.
So I'm trying to figure out the best way to be able to compile my coffeescript/handlebars files on the fly, minispade them, but keep the individual files accessible while in dev mode so I can debug them. What makes that hard is that the rake pipeline is running on a different port than GAE. So I'm not sure exactly how to handle this. Do I make my index file in GAE point to individual files at the 9292 port (rakep) during development, but in production mode point to the fully concatenated version? I'm not sure.
So I was attempting to do that here: https://gist.github.com/1495740 by having only one section that was triggered by the 'build' flag. Not even sure if that works that way.
I know there's a lot of confusion here. Apologies, like I said I'm not even remotely familiar with the Ruby style of doing things.
Since you're not a Ruby person, here are the most reliable steps for getting a stock OSX environment set up with rake pipeline:
Step 1: Install bundler
# on OSX, using built-in Ruby
$ sudo gem install bundler --pre
Step 2: Create a Gemfile
# inside your app directory
$ bundle init
# will create a file named Gemfile in the root
Step 3: Add rake-pipeline to the Gemfile
# inside the Gemfile
gem "rake-pipeline-web-filters"
Step 4: Install your gems
$ bundle install --binstubs
Step 5: Set up Assetfile
However you were already doing it...
Step 6: Run Rake::Pipeline
# to run the preview server
$ bin/rakep
# to build your assets
$ bin/rakep build
Rake::Pipeline.build is the method that evaluates an Assetfile. You can imagine that your entire Assetfile is wrapped inside a Rake::Pipeline.build {} block; you shouldn't ever need to write one inside an Assetfile.
Some of the filters in the docs are hypothetical, most of those docs were written before there were any filters at all. A CoffeeScript compiler has been recently added, though.
As to your main question, I'm not sure there's a clean way to do it with the current rakep implementation. An Assetfile is just Ruby, though, so it's possible to hack something together that should work. Here's how I would write yours:
require "json"
require "rake-pipeline-web-filters"
require "rake-pipeline-web-filters/helpers"
class HandlebarsFilter < Rake::Pipeline::Filter
def initialize(&block)
block ||= proc { |input| input.sub(/\.handlebars$/, '.js') }
super(&block)
end
def generate_output(inputs, output)
inputs.each do |input|
output.write "return Ember.Handlebars.compile(#{input.read.to_json})"
end
end
end
# process all js, css and html files in app/assets
input "assets"
# processed files should be outputted to public
output "public"
# process all coffee files
match "**/*.coffee" do
# compile all CoffeeScript files. the output file
# for the compilation should be the input name
# with the .coffee extension replaced with .js
coffee_script
# The coffee_script helper is exactly equivalent to:
# filter Rake::Pipeline::Web::Filters::CoffeeScriptCompiler
end
match "**/*.js" do
minispade
if ENV['RAKEP_ENV'] == "production"
concat "application.js"
else
concat
end
end
match "**/*.handlebars" do
filter HandlebarsFilter
minispade
concat "templates.js"
end
The if ENV['RAKEP_ENV'] bit reads an environment variable to decide whether to concatenate your JS to a single file.
So now you can run RAKEP_ENV="production" rakep build for a concatenated build, or just rakep build for a development build.

Resources