Puppet - How to use variables defined in manifests with hiera - puppet

is there a way to use a variable defined in some manifest with hiera?
This is how I tried it:
manifest.pp
if $::ipaddress_bond0 {
$primary_interface = 'bond0'
notify{"$primary_interface":}
}
else {
$primary_interface = 'eth0'
notify{"$primary_interface":}
}
hiera.yaml
some_config:
server:
foo:
bar: "%{::primary_interface}"

Yes it is possible. Look at the example:
test.pp
class nodes::test
{
$value1 = 'abc'
$value2 = hiera('test::value2')
$value3 = hiera('test::value3')
notify{ " v1 ${value1}": }
notify{ " v2 ${value2}": }
notify{ " v3 ${value3}": }
}
include nodes::test
test.yaml
test::value2: "%{value1}"
test::value3: "%{value4}"
run test:
puppet apply test.pp
Notice: v1 abc
Notice: v2 abc
Notice: v3
Keep in mind that using puppet variables in hiera is a really bad practice.

Related

Load yaml config with array type

I am writing a code where I am trying to load config.yaml file
impl ::std::default::Default for MyConfig {
fn default() -> Self { Self { foo: "".into(), conf: vec![] } }
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MyConfig {
foo: String,
conf: Vec<String>
}
let cfg: MyConfig = confy::load("config")?;
println!("{:#?}", cfg);
Config.yaml file
foo: "test"
conf:
gg
gb
gg
bb
Output
MyConfig {
url: "",
jobs: [],
}
I have kept the config.yaml file in the same folder where it is getting called. It looks like it is not able to load the file itself. What is getting missed there?
EDIT: When I changed the extension from yaml to toml, and provided full path, it found the file but the structure is expecting is
config.toml
foo = "test"
conf = ["gg","gb","gv","gx"]
full path
confy::load("c:/test/config")?;
Tried multiple places to keep it but not getting it, looks like it requires full path.
But I got the output
MyConfig {
url: "test",
jobs: [
"gg",
"gb",
"gv",
"gx",
],
}
While David Chopin's answer is correct that the YAML is not right, there is a couple of deeper issues.
Firstly, while it is not really documented, looking at the confy source, it expects TOML formatted data, not YAML - for simple cases they can be similar I think. (Turns out this is not 100% correct - the github page says you can switch to YAML this using features = ["yaml_conf"] in the Cargo.toml file)
Secondly, I'm guessing the root problem is that confy is not finding your configuration file.
The docs for confy::load state:
Load an application configuration from disk
A new configuration file is created with default values if none exists.
So, I think it's looking somewhere else, not finding your file and instead of erroring creating a nice default file in that location then returning that default for you.
I believe it should be formatted as follows:
foo: "test"
conf:
- gg
- gb
- gg
- bb

Terraform: YAML file rendering issue in storage section of container linux config of flatcar OS

I am trying to generate a file by template rendering to pass to the user data of the ec2 instance. I am using the third party terraform provider to generate an ignition file from the YAML.
data "ct_config" "worker" {
content = data.template_file.file.rendered
strict = true
pretty_print = true
}
data "template_file" "file" {
...
...
template = file("${path.module}/example.yml")
vars = {
script = file("${path.module}/script.sh")
}
}
example.yml
storage:
files:
- path: "/opt/bin/script"
mode: 0755
contents:
inline: |
${script}
Error:
Error: Error unmarshaling yaml: yaml: line 187: could not find expected ':'
on ../../modules/launch_template/launch_template.tf line 22, in data "ct_config" "worker":
22: data "ct_config" "worker" {
If I change ${script} to sample data then it works. Also, No matter what I put in the script.sh I am getting the same error.
You want this outcome (pseudocode):
storage:
files:
- path: "/opt/bin/script"
mode: 0755
contents:
inline: |
{{content of script file}}
In your current implementation, all lines after the first loaded from script.sh will not be indented and will not be interpreted as desired (the entire script.sh content) by a YAML decoder.
Using indent you can correct the indentation and using the newer templatefile functuin you can use a slightly cleaner setup for the template:
data "ct_config" "worker" {
content = local.ct_config_content
strict = true
pretty_print = true
}
locals {
ct_config_content = templatefile("${path.module}/example.yml", {
script = indent(10, file("${path.module}/script.sh"))
})
}
For clarity, here is the example.yml template file (from the original question) to use with the code above:
storage:
files:
- path: "/opt/bin/script"
mode: 0755
contents:
inline: |
${script}
I had this exact issue with ct_config, and figured it out today. You need to base64encode your script to ensure it's written correctly without newlines - without that, newlines in your script will make it to CT, which attempts to build an Ignition file, which cannot have newlines, causing the error you ran into originally.
Once encoded, you then just need to tell CT to !!binary the file to ensure Ignition correctly base64 decodes it on deploy:
data "template_file" "file" {
...
...
template = file("${path.module}/example.yml")
vars = {
script = base64encode(file("${path.module}/script.sh"))
}
}
storage:
files:
- path: "/opt/bin/script"
mode: 0755
contents:
inline: !!binary |
${script}

Groovy : multiple loops in template engine using GStringTemplateEngine()

This question is in continuation with earlier question I have posted before.
To enhance my templates further I need to have multiple loops in the template engines and the values need to be replaced from the HashMap Variables that were derived from a configuration file.
My HashMap looks something like this, let say envVar:
envVar = [
PROJECT:name,
env:[
[name:param1, value:value1],
[name:param2, value:value2],
[name:param3, value:value3]],
ports:[
[protocol:port1, port:1000],
[protocol:port2, port:2000]]
]
My template engine looks like this:
- env:
<< NEED TO LOOP HERE WITH env variables
- name : param1
value : value1
....
>>
project: "${PROJECT}"
......
......
......
ports:
<< NEED TO LOOP HERE WITH ports variables
- port: 1000
protocol : port1
....
>>
Code snippet is described below.
def f = new File('output.template')
def engine = new groovy.text.GStringTemplateEngine()
//....
//envVar are derived from another file
//....
def Template = engine.createTemplate(f).make(envVar)
println "${Template}"
Can someone explain me how to modify the above code snippet and templates to make sure the envVar will be replaced properly in the template engine.
You need to do an each for every variable. Your template file needs to look like this:
#cat output.template
- env:<% env.each { v -> %>
- name : ${v.name}
value : ${v.value}<% } %>
project: "${PROJECT}"
......
......
......
ports:<% ports.each { v -> %>
- port: ${v.port}
protocol: ${v.protocol}<% } %>
Then, your main script should look like this:
def f = new File('output.template')
def engine = new groovy.text.GStringTemplateEngine()
def envVar = [
PROJECT: name,
env:[
[name:'param1', value:'value1'],
[name:'param2', value:'value2'],
[name:'param3', value:'value3']
],
ports:[
[protocol:'port1', port:1000],
[protocol:'port2', port:2000]
]
]
def Template = engine.createTemplate(f).make(envVar)
println "${Template}".trim()
output:
#cat output.template
- env:
- name : param1
value : value1
- name : param2
value : value2
- name : param3
value : value3
project: "projectName"
......
......
......
ports:
- port: 1000
protocol: port1
- port: 2000
protocol: port2
to navigate through your env variable you can use the following:
envVar.env.each{
println "name : ${it.name}"
println "value : ${it.value}"
}
envVar.ports.each{
println "port : ${it.port}"
println "protocol : ${it.protocol}"
}

Passing elements in an array to hiera lookup as keys

I am getting an error when trying to execute my puppet module:
Error 400 on SERVER: certificatefqdn is not a hash or array when accessing it with certificatefile
This is what my hiera file looks like:
testmodule::install::certificates:
test1.domain.com:
certificatefile: 'certificate1.crt'
certificatepass: 'testpass1'
test2.domain.com:
certificatefile: 'certificate2.crt'
certificatepass: 'testpass2'
test3.domain.com:
certificatefile: 'certificate3.crt'
certificatepass: 'testpass3'
The init.pp in my module looks like this:
class testmodule (
$certificates = hiera('testmodule::install::certificates'),
)
{
$domains = [
test1.domain.com',
test2.domain.com',
test3.domain.com',
[
testmodule::install { $domains:
certificates => $certificates,
}
}
The install.pp in my module looks like this:
define testmodule::install ($certificates)
{
$domain = $name
$certificatefqdn = $certificates["$domain"]
$certificatefile = $certificatefqdn['certificatefile']
$certificatepass = $certificatefqdn['certificatepass']
notify{"This is the certificate file: $certificatefile" :}
}
I'm expecting to see an output like this for each of the elements in the domain array:
Notice: This is the certificate file: certificate2.crt
Notice: /Stage[main]/Testmodule/Testmodule::Install[certificate2.crt]/Notify[This is the certificate file: certificate2.crt]/message: defined 'message' as 'This is the certificate file: certificate2.crt'
Instead I see this:
Notice: This is the certificate file: test2.domain.com['certificatefile']
Notice: /Stage[main]/Testmodule/Testmodule::Install[test2.domain.com]/Notify[This is the certificate file: test2.domain.com['certificatefile']]/message: defined 'message' as 'This is the certificate file: test2.domain.com['certificatefile']'
How can I correctly access the keys in the nested hash in hiera using the elements in domains as the initial key?
The code appears to be correct already.
I have set this up according to your question:
1/
# manifests/init.pp
class test (
$certificates = hiera('test::install::certificates'),
) {
$domains = [
'test1.domain.com',
'test2.domain.com',
'test3.domain.com',
]
test::install { $domains:
certificates => $certificates,
}
}
2/
# manifests/install.pp
define test::install ($certificates) {
$domain = $name
$certificatefqdn = $certificates["$domain"]
$certificatefile = $certificatefqdn['certificatefile']
$certificatepass = $certificatefqdn['certificatepass']
notify{"This is the certificate file: $certificatefile" :}
}
3/
# spec/fixtures/hiera/data/common.yaml
---
test::install::certificates:
test1.domain.com:
certificatefile: 'certificate1.crt'
certificatepass: 'testpass1'
test2.domain.com:
certificatefile: 'certificate2.crt'
certificatepass: 'testpass2'
test3.domain.com:
certificatefile: 'certificate3.crt'
certificatepass: 'testpass3'
4/
# spec/fixtures/hiera/hiera.yaml
---
version: 5
defaults:
datadir: data
data_hash: yaml_data
hierarchy:
- name: "All levels"
paths:
- common.yaml
Compile and apply:
$ bundle exec puppet apply --modulepath spec/fixtures/modules --hiera_config spec/fixtures/hiera/hiera.yaml -e 'include test'
Warning: The function 'hiera' is deprecated in favor of using 'lookup'. See https://docs.puppet.com/puppet/5.3/reference/deprecated_language.html
(file & line not available)
Notice: Compiled catalog for alexs-macbook-pro.local in environment production in 0.14 seconds
Notice: This is the certificate file: certificate1.crt
Notice: /Stage[main]/Test/Test::Install[test1.domain.com]/Notify[This is the certificate file: certificate1.crt]/message: defined 'message' as 'This is the certificate file: certificate1.crt'
Notice: This is the certificate file: certificate2.crt
Notice: /Stage[main]/Test/Test::Install[test2.domain.com]/Notify[This is the certificate file: certificate2.crt]/message: defined 'message' as 'This is the certificate file: certificate2.crt'
Notice: This is the certificate file: certificate3.crt
Notice: /Stage[main]/Test/Test::Install[test3.domain.com]/Notify[This is the certificate file: certificate3.crt]/message: defined 'message' as 'This is the certificate file: certificate3.crt'
Notice: Applied catalog in 0.04 seconds
So I can't reproduce this.

Is better to use Hiera or basic node definition?

Which is better for Puppet 3.7 or newer ?
1- Hiera with Yaml definition as
ubuntu.yaml:
---
classes:
- google-chrome
- xcode
2- Base node definition as
node.pp:
node "ubuntu" {
include google-chrome
include xcode
}
I think you misunderstand purpose of using hiera. According to documentation:
Hiera is a key/value lookup tool for configuration data, built to make Puppet better and let you set node-specific data without repeating yourself.
Hiera gives you a versatile way to deliver configuration parameters. So in my opinion "2- Base node definition", with hiera as configuration provider is proper way of using puppet with hiera.
E.g: instead of definig:
node node1 {
class { 'some_class_1' :
param_1 => value1,
param_2 => value2,
}
class { 'some_class_2' :
param_1 => value3,
param_2 => value4,
}
}
node node2 {
class { 'some_class_1' :
param_1 => value11,
param_2 => value22,
}
class { 'some_class_2' :
param_1 => value33,
param_2 => value44,
}
}
Use hiera as configuration provider. In puppet define only:
node some_regexp { #or just provide node names
include some_class_1
include some_class_2
}
than define proper hiera files:
node1.yaml
some_class_1::param_1: value1
some_class_1::param_2: value2
some_class_2::param_1: value3
some_class_2::param_2: value4
node2.yaml
some_class_1::param_1: value11
some_class_1::param_2: value22
some_class_2::param_1: value33
some_class_2::param_2: value44

Resources