puppet apply custom function - ruby

How can I quickly run a custom function on any node with puppet apply ?
Let's say I have this file (test.pp)
file { "/root/files/f":
content => test('test'),
}
And this ruby file (test.rb) which only log and return the first agument.
require 'logger'
module Puppet::Parser::Functions
newfunction(:test, :type => :rvalue
) do |args|
log = Logger.new(STDOUT)
log.level = Logger::INFO
log.info(args[0])
args[0]
end
end
How does one call the ruby function test with a puppet apply test.pp?
My problem is that is take like 5minutes to run my entire puppet agent -t and I need to add a function to puppet so I would like to test it quickly instead of waiting 5minutes each time.

This will certainly work, but it's hard to maintain.
A more maintainable way to do this is to add your function to a module, then install that module on your master.
For example, create a new with the name of your module (eg. test_function):
mkdir -p test_function/lib/puppet/parser/functions
You should have the following tree in your test_function module
├── lib
│   └── puppet
│   ├── parser
│   │   └── functions
│   │   ├── test.rb
Add your test.rb code to the test.rb file
Copy this module to your master's module path (this is probably /etc/puppet/modules depending on your Puppet version, use puppet module list to find out)
After that, the puppet master will read its module list and dynamically add-in all functions it finds.
More documentation about it here:
https://docs.puppetlabs.com/guides/plugins_in_modules.html
https://docs.puppetlabs.com/guides/custom_functions.html

I just found out.
All you need to do is to add the test.rb file directly into /var/lib/puppet/lib/puppet/parser/functions/test.rb and the puppet apply will "see" the function.

Related

How to split Dangerfile to multiple smaller files

I am trying to split Dangerfile into multiple files and call them from the main Dangerfile.
I tried this way,
In the parent Dangerfile
puts "Branch #{github.branch_for_base} #{git.lines_of_code}"
require_relative "scripts/large_diff.rb"
warn 'Some warning'
And in the scripts/large_diff.rb
puts '---->>>>'
puts github.pr_json.inspect
puts '<<<<---->>>>'
puts github.inspect
puts '<<<<----'
But this gives this error
You used `puts` in your Dangerfile. To print out text to GitHub use `message` instead
Branch release/abc/xyz 304
---->>>>
bundler: failed to load command: danger (/Users/runner/work/my-proj/my-proj/vendor/bundle/ruby/3.0.0/bin/danger)
/Users/runner/work/my-proj/my-proj/scripts/large_diff.rb:2:in `<top (required)>': \e[31m (Danger::DSLError)
[!] Invalid `Dangerfile` file: undefined local variable or method `github' for main:Object. Updating the Danger gem might fix the issue. Your Danger version: 8.5.0, latest Danger version: 8.6.1
\e[0m
# from Dangerfile:3
# -------------------------------------------
# puts "Branch #{github.branch_for_base} #{git.lines_of_code}"
> require_relative "scripts/large_diff.rb"
# warn 'Big PR, split it into smaller ones'
# -------------------------------------------
from Dangerfile:3:in `require_relative'
from Dangerfile:3:in `eval_file'
from /Users/runner/work/bitrise-comment/bitrise-comment/vendor/bundle/ruby/3.0.0/gems/danger-8.5.0/lib/danger/danger_core/dangerfile.rb:311:in `eval'
from /Users/runner/work/bitrise-comment/bitrise-comment/vendor/bundle/ruby/3.0.0/gems/danger-8.5.0/lib/danger/danger_core/dangerfile.rb:311:in `eval_file'
...
...
...
I know this is because the github object is not avaialble in the child danger file. How can I use variables and functions (the object github functions warn, fail etc) available in Parent Dangerfile also in the child danger file?
PS: I am new to ruby :)
What you're trying to do is not so much Ruby-specific but rather a way of how things are done with Danger. One of the ways to split the file into several smaller ones would be to dedicate a folder for these smaller danger files. Here's an example of such a structure
.
├── Dangerfile
└── checks
├── large_diff
│   └── Dangerfile
└── other_check
└── Dangerfile
Then in the main Dangerfile, you can just reference the other ones like so
danger.import_dangerfile(path: 'checks/large_diff')
danger.import_dangerfile(path: 'checks/other_check')
It doesn't really matter how you name the directories, but Danger will expect to find a file named Dangerfile in that directory. Personally, I prefer this approach, but there are other methods like importing a Dangerfile from a gem or creating a Danger plugin and calling it through danger.import_plugin(...) See the reference for more info - https://danger.systems/reference.html

"The specified collections path is not part of the configured Ansible collections paths" but I'm installing into a relative directory

I'm installing the ansible.posix collection to use in my playbook like this:
ansible-galaxy collection install -r ansible/requirements.yml -p ansible/collections
However, I get this warning message that I want to get rid of:
[WARNING]: The specified collections path '/home/myuser/path/to/my/repo/ansible/collections' is not part of the
configured Ansible collections paths '/home/myuser/.ansible/collections:/usr/share/ansible/collections'. The installed collection won't be
picked up in an Ansible run.
My repo is laid out like this:
├── ansible
│   ├── playbook.yml
│   ├── files
│   │   ├── ...
│   ├── tasks
│   │   ├── ...
│   ├── requirements.yml
├── ansible.cfg
...
ansible.cfg looks like this:
[defaults]
timeout = 60
callback_whitelist = profile_tasks
Here's the output of ansible --version:
ansible 2.9.17
config file = /home/myuser/path/to/my/repo/ansible.cfg
configured module search path = ['/home/myuser/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.7/dist-packages/ansible
executable location = /usr/local/bin/ansible
python version = 3.7.3 (default, Jul 25 2020, 13:03:44) [GCC 8.3.0]
In the docs for installing collections with ansible-galaxy, they say the following:
You can also keep a collection adjacent to the current playbook, under a collections/ansible_collections/ directory structure.
play.yml
├── collections/
│ └── ansible_collections/
│ └── my_namespace/
│ └── my_collection/<collection structure lives here>
And, like the documentation suggests, I can still use the collection just fine in my play. But this warning message is quite annoying. How do I get rid of it?
I have created ansible.cfg within the ansible project I'm working on.
You could simply cp /etc/ansible/ansible.cfg .
but since the file would look like:
[defaults]
collections_paths = ./collections/ansible_collections
It is just easier to create it.
Once there, Ansible will know about your custom configuration file.
In you project folder you will:
mkdir -p ./collections/ansible_collections
And then run the install.
If your requirements.yml contains a collection like:
collections:
- community.general
You'd have to install it as:
ansible-galaxy collection install -r requirements.yml -p ./collections/
And the output would be:
[borat#mypotatopc mycoolproject]$ ansible-galaxy collection install -r requirements.yml -p ./collections/
Process install dependency map
Starting collection install process
Installing 'community.general:3.1.0' to '/home/borat/projects/mycoolproject/collections/ansible_collections/community/general'
In case you won't setup your modified ansible.cfg, the output would be:
[borat#mypotatopc mycoolproject]$ ansible-galaxy collection install -r requirements.yml -p ./
[WARNING]: The specified collections path '/home/borat/projects/mycoolproject' is not part of the configured Ansible collections paths
'/home/borat/.ansible/collections:/usr/share/ansible/collections'. The installed collection won't be picked up in an Ansible run.
Process install dependency map
Starting collection install process
Installing 'community.general:3.1.0' to '/home/borat/projects/mycoolproject/ansible_collections/community/general'
There are other methods too, but I like this one.

Puppet unable to find my .erb template but finds other files OK?

No one has been able to explain this inside my company so if you are able to solve this KUDOS to you!
Inside my puppet repo I have setup as follows:
environment/ops/modules/papertrail
├── files
│ ├── elasticsearch_log_files.yml
│ ├── log_files.yml
│ └── remote_syslog.conf
|
└── manifests
├── elasticsearch.pp
└──init.pp
└── templates
└── elasticsearch_log_files.yml.erb
MY elasticsearch.pp file contains the following:
class papertrail::elasticsearch inherits papertrail {
$source = "puppet:///modules/papertrail"
file { "/etc/log_files.yml" :
mode => 0644,
owner => root,
group => root,
ensure => present,
source => "$source/elasticsearch_log_files.yml",
}
}
Now when I try to change the last line to:
"$source/elasticsearch_log_files.yml.erb",
or
"$source/templates/elasticsearch_log_files.yml",
Puppet errors out and says that it can't locate the file:
Error: /Stage[main]/Papertrail::Elasticsearch/File[/etc/log_files.yml]: Could not evaluate: Could not retrieve information from environment ops source(s) puppet:///modules/papertrail/elasticsearch_log_files.yml.erb
What is strange is that when I use the following stanza to just include the yml file instead of erb it works fine and the file gets populated on the target:
"$source/elasticsearch_log_files.yml",
How can I include my erb? I have dynamic variables that I need to assign to the configuration file log_files.yml and I am so far unable to do so =(
This is solved. I didn't add the template directory to my git commit so once added with git add . it worked.

How to set up custom bash environment for different users with puppet?

I'm just getting started with puppet (and vagrant) to set up the development environment for our team, which consists of 8+ developers, each of which have their particular bash configuration, etc. I've got all the software installed on the system to quickly deploy new development virtual machines, but I'm not sure the best way to set up the development environment for each particular user in an automated way (we will end up having several development environments and it would be convenient to write this once and be done).
For example, I'd like to set up a user joe, clone Joe's configuration repo from github, and then run a script in that github repository to set up the environment for Joe. Any suggestions for how to do this for Joe as well as Jimmy, James, Julie, Jane, Jim, Jake, and Jimbo?
In case its any help, the development machines will almost certainly be ubuntu systems.
In addition to #Matt's suggestion, I created a custom puppet module that instantiates the configuration environment for each individual based on their github preferences. The resulting puppet module users looks something like this:
users/
├── manifests
│   ├── init.pp # base level configurations for all users
│   ├── jake.pp # custom setup for jake
│   ├── james.pp # custom setup for james
│   ├── jane.pp # custom setup for jane
│   ├── jim.pp # custom setup for jim
│   ├── jimbo.pp # custom setup for joe
│   ├── jimmy.pp # custom setup for jimmy
│   ├── joe.pp # custom setup for julie
│   └── julie.pp # custom setup for jimbo
└── templates
The relevant tidbit is in the custom setup files for each user. For example, here's what jim.pp might look like:
class users::jim {
# make sure that all base configuration in init.pp is set up first
require users
# add the user here
user { 'jim':
# comment => 'Dean Malmgren',
home => '/home/jim',
shell => '/bin/bash',
uid => 201,
managehome => 'true',
groups => ['sudo', 'vagrant'],
provider => 'useradd',
password => '$6$kxHLEuHW$78m3zHVLu0XUUDaU4bT.PEg./FfcloJiWml',
}
# clone the repository the first time
exec { 'jim-clone-dotfiles':
command => 'git clone git://github.com/jim/dotfiles.git && python dotfiles/create_softlinks.py',
cwd => '/home/jim',
creates => '/home/jim/dotfiles',
user => 'jim',
group => 'jim',
require => [ Package['git'] ],
}
# fetch and update if jim decides to update his dotfiles repo
exec { 'jim-update-dotfiles':
command => 'git merge --ff-only origin/master && python create_softlinks.py',
cwd => '/home/jim/dotfiles',
unless => 'git fetch && git diff --exit-code origin/master',
user => 'jim',
group => 'jim',
require => Exec['jim-clone-dotfiles'],
}
}
You could use a puppet fact in the vagrant file to set the username and pass this through to your puppet manifests. Something like the following:
Vagrant.configure("2") do |config|
config.vm.provision :puppet do |puppet|
puppet.facter = {
"user_name" => ENV['USER']
}
end
end
This would pass the current logged in username through to puppet and then within your manifest files you could use the variable "$user_name" within your git commands to checkout the correct users repo and do any other related tasks.

Set / change + add Sinatra views folder

I am learning sinatra, and I am trying to create simple website. This is my web directory tree:
├── app.rb
│
├── admin
│   └── views
│  └── admin.rb
├── models
├── static
│  
└── views
and now I want render views just for admin. In other words: I have 2 views folder in different locations, admin for admin controller and views, and another views is for homepage.
Add config.ru file in root application folder
require './app'
require './admin/admin'
# run MyApp
run Rack::URLMap.new("/" => MyApp.new, "/admin" => AdminApp.new)
In app.rb
require 'sinatra'
require 'haml'
class MyApp < Sinatra::Base
get "/app" do
haml :app
end
end
In admin.rb
# admin.rb
class AdminApp < Sinatra::Base
get "/" do
haml :index
end
end
Finally in console rackup -p PORTNUMBER example
rackup -p 4000
Update
Reference to Gist

Resources