Print %YAML <version> on output`s header - ruby

This is the code:
file '/etc/myproduct/myfile.yaml' do
content node['myproduct']['config'].to_hash.to_yaml( :UseVersion => true, :UseHeader => true )
mode 0644
owner 'root'
group 'root'
end
output:
---
vars:
address-groups:
Im trying to add %YAML 1.1 above --- so it will look like this:
%YAML 1.1
---
vars:
address-groups:
also tried to_yaml only - no good.

I would probably just do it the brute-force way:
content "%YAML 1.1\n" + node['myproduct']['config'].to_hash.to_yaml
I'm sure there is a better way, but that works so ... :)

Related

Update hash values with hash key

I'm facing a problem that I couldn't find a working solution yet.
I have my YAML config file for the environment, let's call it development.yml.
This file is used to create the hash that should be updated:
data = YAML.load_file(File.join(Rails.root, 'config', 'environments', 'development.yml'))
What I'm trying to accomplish is something along these lines. Let's suppose we have an element of the sort
data['server']['test']['user']
data['server']['test']['password']
What I want to have is:
data['server']['test']['user'] = #{Server.Test.User}
data['server']['test']['password'] = #{Server.Test.Password}
The idea is to create a placeholder for each value that is the key mapping for that value dynamically, going until the last level of the hash and replacing the value with the mapping to this value, concatenating the keys.
Sorry, it doesn't solve my problem. The location data['server']['test']['user'] will be built dynamically, via a loop that will go through a nested Hash. The only way I found to do it was to append to the string the key for the current iteration of the Hash. At the end, I have a string like "data['server']['test']['name']", which I was thinking on converting to a variable data['server']['test']['name'] and then assigning to this variable the value #{Server.Test.Name}. Reading my question I'm not sure if this is clear, I hope this helps to clarify it.
Input sample:
api: 'active'
server:
test:
user: 'test'
password: 'passwordfortest'
prod:
user: 'nottest'
password: 'morecomplicatedthantest'
In this case, the final result should be to update this yml file in this way:
api: #{Api}
server:
test:
user: #{Server.Test.User}
password: #{Server.Test.Password}
prod:
user: #{Server.Prod.User}
password: #{Server.Prod.Password}
It sounds silly, but I couldn't figure out a way to do it.
I am posting another answer now since I realize what the question is all about.
Use Iteraptor gem:
require 'iteraptor'
require 'yaml'
# or load from file
yaml = <<-YAML.squish
api: 'active'
server:
test:
user: 'test'
password: 'passwordfortest'
prod:
user: 'nottest'
password: 'morecomplicatedthantest'
YAML
mapped =
yaml.iteraptor.map(full_parent: true) do |parent, (k, _)|
v = parent.map(&:capitalize).join('.')
[k, "\#{#{v}}"]
end
puts YAML.dump(mapped)
#⇒ ---
# api: "#{Api}"
# server:
# test:
# user: "#{Server.Test.User}"
# password: "#{Server.Test.Password}"
# prod:
# user: "#{Server.Prod.User}"
# password: "#{Server.Prod.Password}"
puts YAML.dump(mapped).delete('"')
#⇒ ---
# api: #{Api}
# server:
# test:
# user: #{Server.Test.User}
# password: #{Server.Test.Password}
# prod:
# user: #{Server.Prod.User}
# password: #{Server.Prod.Password}
Use String#%:
input = %|
data['server']['host']['name'] = %{server_host}
data['server']['host']['user'] = %{server_host_user}
data['server']['host']['password'] = %{server_host_password}
|
puts (
input % {server_host: "Foo",
server_host_user: "Bar",
server_host_password: "Baz"})
#⇒ data['server']['host']['name'] = Foo
# data['server']['host']['user'] = Bar
# data['server']['host']['password'] = Baz
You can not add key-value pair to a string.
data['server']['host'] # => which results in a string
Option 1:
You can either save Server.Host as host name in the hash
data['server']['host']['name'] = "#{Server.Host}"
data['server']['host']['user'] = "#{Server.Host.User}"
data['server']['host']['password'] = "#{Server.Host.Password}"
Option 2:
You can construct the hash in a single step with Host as key.
data['server']['host'] = { "#{Server.Host}" => {
'user' => "#{Server.Host.User}",
'password' => "#{Server.Host.Password}"
}
}

How do i define variable within a template cookbook

I am trying to edit the existing user_management cookbook on the supermarket to include sudoers. I seem to be having problems properly defining the sudoers_groups variable within the template.
Link to default cookbook
https://github.com/FFIN/user_management/blob/master/recipes/default.rb
Here is what my vault looks like.
knife vault show testusers john
action: create
comment: John Smith
dbpass: secret
gid: john
id: john
password: $1$d$xKNtrFrifo6f7tLFW1xh750
shell: /bin/bash
sudo_pwdless: true
sudoer: false
sudoer_group:
command: ALL
name: admin
sudo_pwdless: false
command: ALL
name: wheel
sudo_pwdless: false
command: ALL
name: sysadmin
sudo_pwdless: true
uid: 1002
username: john`
Here is the template section of my recipe
sudoer_users = Array.new()
if user['sudoer']
command = user['command'] ? user['command'] : 'ALL'
hash = { :uname => user['username'], :command => command, :sudo_pwdless => user['sudo_pwdless'] }
sudoer_users.push(hash)
end
template "/etc/sudoers" do
source 'sudoers.erb'
mode '0440'
owner 'root'
group node['root_group']
variables(
:sudoers_users => sudoer_users,
:sudoers_groups => node[:testcookbook][:testusers][:sudoer_group]
)
only_if { sudoer_users }
end
When i run the recipe, i get the following error
Recipe Compile Error in /var/chef/cache/cookbooks/newuser/recipes/default.rb ============================================. ==================================== NoMethodError
-------------`
undefined method [] for nil:NilClass
template "/etc/sudoers" do
61: source 'sudoers.erb'
62: mode '0440'
63: owner 'root'
64: group node['root_group']
65: variables(
66: :sudoers_users => sudoer_users,
67>> :sudoers_groups => node[ :testcookbook][ :testusers][ :sudoer_group]
68: )
69: only_if { sudoer_users }
70: end
My question is how do i go about defining the sudoers_group variable so that it only iterates the sudoer_group section within the vault?
Unfortunately Ruby doesn't give us enough info to check which is undefined, but either node[:testcookbook] or node[:testcookbook][:testusers] is unset/undefined. Double check where you are setting the sudoer_group value because it is likely either misformatted or not uploaded to the Chef Server.
Here is what i did to finally resolve the issue.
I added the following as part of my variables in attributes/default.rb.
default['testcookbook']['testusers']['sudoer_group'] = [
{"name" => "admin", "sudo_pwdless" => false, "command" => "ALL"},
{"name" => "wheel", "sudo_pwdless" => false, "command" => "ALL"},
{"name" => "sysadmin", "sudo_pwdless" => true, "command" => "ALL"}
]`

Write users to .htpasswd in chef recipe

In a chef recipe invoked by chef-solo / vagrant I'm trying to write a .htpasswd file from an object of users.
I specify the users in vagrantfile like this...
chef.json = {
:apache => {
...
:my_users => {
:john => "test",
:matt => "test2"
}
...
My chef recipe looks like this at the moment:
file "/etc/apache2/.htpasswd" do
content "john:n5MfEoHOIQkKg"
owner "#{node['apache']['user']}"
group "#{node['apache']['group']}"
mode '0644'
action :create
end
As you can see I have hard coded John's credentials in there - however, I'm not a Ruby dev and I'm missing some very basic knowledge here...
How can I write all user credentials in the node['apache']['my_users'] attribute (defined in the chef.json) in a loop into the file while creating the password hash for each clear text password?
Note: I'm trying to avoid using a template for this simple file.
I got this working using the LWRP Charlie suggested.
First step is to modify the definition of users to be a proper array:
chef.json = {
:apache => {
...
:my_users => [
{ :username => "john", :password => "test1" },
{ :username => "matt", :password => "test2" }
]
...
I include the htpasswd dependency to metadata and bershelf.
Then in my recipe I create the users in a loop using the htpasswd call:
node[:apache][:my_users].each do |user|
htpasswd "/etc/apache2/.htpasswd" do
user user['username']
password user['password']
end
end
The htpasswd man page looks like it uses MD5 hashing on the passwords.
Perhaps you can generate md5 hashes in your recipe's Ruby code?
You can do it the native way, it requires htpasswd to be installed:
execute 'set password' do
sensitive true
command "htpasswd -cb /etc/htpasswd.users #{user} #{password}"
creates '/etc/htpasswd.users'
end
file '/etc/htpasswd.users' do
owner 'www-data'
group 'www-data'
mode 0o600
end

Chef template loop: can't convert Chef::Node::immutableMash into String

I've got a Vagrant setup in which I'm trying to use Chef-solo to generate an conf file which loops though defined variables to pass to the application. Everything is working except the loop and I'm not familiar enough with Ruby/Chef to spot the error.
I'm going to lay out the whole chain of events in case there is something along the way that is the problem, but the first portions of this process seem to work fine.
A config file is written in yaml and includes env variable definitions to be passed:
...
variables:
- DEBUG: 2
...
The config file is read in by the Vagrantfile into a ruby hash and used to create the Chef json nodes:
...
settings = YAML::load(File.read("config.yaml"))
# Provision The Virtual Machine Using Chef
config.vm.provision "chef_solo" do |chef|
chef.json = {
"mysql" => {"server_root_password" => "secret"},
"postgresql" => {"password" => {"postgres" => "secret"}},
"nginx" => {"pid" => "/run/nginx.pid"},
"php-fpm" => {"pid" => "/run/php5-fpm.pid"},
"databases" => settings["databases"] || [],
"sites" => settings["sites"] || [],
"variables" => settings["variables"] || []
}
...
A bunch of chef cookbooks are run (apt, php, nginx, mysql etc) and finally my custom cookbook which is whats giving me grief. The portion of the cookbook responsible for creating a the conf file is shown here:
# Configure All Of The Server Environment Variables
template "#{node['php-fpm']['pool_conf_dir']}/vars.conf" do
source "vars.erb"
owner "root"
group "root"
mode 0644
variables(
:vars => node['variables']
)
notifies :restart, "service[php-fpm]"
end
And the vars.erb is just a one-liner
<%= #vars.each {|key, value| puts "env[" + key + " = " + value } %>
So, when I run all this chef spits out an error about not being able to convert a hash to a string.
can't convert Chef::Node::immutableMash into String
So for some reason this is coming across as an immutableMash and the value of key ends up being the hash [{"DEBUG"=>2}] and value ends up a nil object, but I'm not sure why or how to correct it.
The hash is ending up as the value of key in your example because the YAML file declares DEBUG: 2 as a list member of variables. This translates to variables being an array with a single hash member.
Try changing the template code to this:
<%= #vars[0].each {|key, value| puts "env[" + key + " = " + value } %>
Or try changing the YAML to this and not changing the template code:
variables:
DEBUG: 2
Either change will get your template loop iterating over the hash that you are expecting.

Extract comments

I have this code in a controller:
# =begin action
# url: /albums
# method: GET
# autentication: true
# return: [json, xml]
# =end
def show
...
end
Is there some gem that reads comments and returns info in json format, or does something like this? I want to get it to manipulate and generate files with this information.
{
"url" => /albums
"method" => GET
"autentication" => true
"return" => [json, xml]
}
I don't think there is a gem doing exactly what you want, but the task seems to be pretty easy to devide:
first, you need to parse the file and pull that comments - it shouldn't be difficult to do it with a simple ruby script.
Then having info in format like:
url: /albums
method: GET
autentication: true
return: [json, xml]
which seems pretty like a YAML, you can do simply
YAML::load(string).to_json

Resources