Let us say I have an array of values
["node1.example.org", "node2.example.org"]
And I want to pass this using facter, and use it in puppet at such:
host {
$nodes:
...
}
How can I do this?
Facter 1.x cannot pass structured data as fact values. All facts will be coerced into String format. This is especially unfortunate for Arrays, because those will have their elements concatenated without a join marker.
It is advisable to make your fact return a [comma] seperated list, e.g. instead of returning Array result, do
result * ","
In your manifest, turn this back into an array
$nodes_array = split($nodes, ',')
host { $nodes_array: }
See the Puppet function reference and Ruby's Array methods.
Facter 2 does support Boolean, Hash and Array facts, but this may not yet be readily available.
Related
If I use the following yamldecode, I get the following output:
output "product" {
value = distinct(yamldecode(file("resource/lab/abc.yaml"))["account_list"]["resource_tags"][*]["TAG:product"])
}
Output:
+ product = [
+ "fargate",
+ "CRM",
]
I want fargate to be removed from my output and the expected output is this:
+ product = [
+ "CRM"
]
Please let me know how I can do this.
output "product" {
value = compact([for x in distinct(yamldecode(file("resource/lab/abc.yaml"))["account_list"]["resource_tags"][*]["TAG:product"]) : x == "fargate" ? "" : x])
}
I get this output:
test = [
"enter",
]
compact function solved the problem.
The Terraform language is based on functional principles rather than imperative principles and so it does not support directly modifying an existing data structure. Instead, the best way to think about goals like this is how to define a new value which differs from your existing value in some particular way.
For collection and structural types, the most common way to derive a new value with different elements is to use a for expression, which describes a rule for creating a new collection based on the elements of an existing collection. For each element in the source collection we can decide whether to use it in the result at all, and then if we do decide to use it we can also describe how to calculate a new element value based on the input element value.
In your case you seem to want to create a new list with fewer elements than an existing list. (Your question title mentions maps, but the question text shows a tuple which is behaving as a list.)
To produce a new collection with fewer elements we use the optional if clause of a for expression. For example:
[for x in var.example : x if x != "fargate"]
In the above expression, x refers to each element of var.example in turn. Terraform first evaluates the if clause, and expects it to return true if the element should be used. If so, it will then evaluate the expression immediately after the colon, which in this case is just x and so the result element is identical to the input element.
Your example expression also includes some unrelated work to decode a YAML string and then traverse to the list inside it. That expression replaces var.example in the above expression, as follows:
output "products" {
value = toset([
for x in yamldecode(file("${path.module}/resource/lab/abc.yaml"))["account_list"]["resource_tags"][*]["TAG:product"]: x
if x != "fargate"
])
}
I made some other small changes to the above compared to your example:
I named the output value "products" instead of "product", because it's returning a collection and it's Terraform language idiom to use plural names for collection values.
I wrapped the expression in toset instead of distinct, because from your description it seems like this is an unordered collection of unique strings rather than an ordered sequence of strings.
I added path.module to the front of the file path so that this will look for the file in the current module directory. If your output value is currently in a root module then this doesn't really matter because the module directory will always be the current working directory, but it's good practice to include this so that it's clear that the file belongs to the module.
Therefore returning a set is more appropriate than returning a list because it communicates to the caller of the module that they may not rely on the order of these items, and therefore allows the order to potentially change in future without it being a breaking change to your module.
Twig 2.8's new column filter:
Internally ... uses the PHP array_column function
but it does not support array_column's $index_key argument,
which is extremely useful for quickly converting arrays of objects into an array of id => value pairs.
Will this be supported in a future release?
If I have data in Hiera like:
resource_adapter_instances:
'Adp1':
adapter_plan_dir: "/opt/weblogic/middleware"
adapter_plan: 'Plan_DB.xml'
'Adp2':
adapter_plan_dir: "/opt/weblogic/middleware"
adapter_plan: 'ODB_Plan_DB.xml'
'Adp3':
adapter_plan_dir: "/opt/weblogic/middleware"
adapter_plan: 'Plan_DB.xml'
And I need to transform this into an array like this, noting duplicates are removed:
[/opt/weblogic/middleware/Plan_DB.xml, /opt/weblogic/middleware/ODB_Plan_DB.xml]
I know I have to use Puppet's map but I am really struggling with it.
I tried this:
$resource_adapter_instances = hiera('resource_adapter_instances', {})
$resource_adapter_paths = $resource_adapter_instances.map |$h|{$h['adapter_plan_dir']},{$h['adapter_plan']}.join('/').uniq
notice($resource_adapter_instances)
But that doesn't work, and emits syntax errors. How do I do this?
You are on the right track. A possible solution is as follows:
$resource_adapter_instances = lookup('resource_adapter_instances', {})
$resource_adapter_paths =
$resource_adapter_instances.map |$x| {
[$x[1]['adapter_plan_dir'], $x[1]['adapter_plan']].join('/')
}
.unique
notice($resource_adapter_paths)
A few further notes:
The hiera function is deprecated so I rewrote using lookup and you should too.
Puppet's map function can be a little confusing - especially if you need to iterate with it through a nested Hash, as in your case. On each iteration, Puppet passes each key and value pair as an array in the form [key, value]. Thus, $x[0] gets your Hash key (Adp1 etc) and $x[1] gets the data on the right hand side.
Puppet's unique function is not uniq as in Bash, Ruby etc but actually is spelt out as unique.
Note I've rewritten it without the massively long lines. It's much easier to read.
If you puppet apply that you'll get:
Notice: Scope(Class[main]): [/opt/weblogic/middleware/Plan_DB.xml,
/opt/weblogic/middleware/ODB_Plan_DB.xml]
The findvalue function in HTML::TreeBuilder::XPath returns a concatenation of any values found by the xpath query.
Why does it do this, and how could a concatenation of the values be useful to anyone?
Why does it do this?
When you call findvalue, you're requesting a single scalar value. If there are multiple matches, they have to be combined into a single value somehow.
From the documentation for HTML::TreeBuilder::XPath:
findvalue ($path)
...If the path returns a NodeSet, $nodeset->xpath_to_literal is called automatically for you (and thus a Tree::XPathEngine::Literal is returned).
And from the documentation for Tree::XPathEngine::NodeSet:
xpath_to_literal()
Returns the concatenation of all the string-values of all the nodes in the list.
An alternative would be to return the Tree::XPathEngine::NodeSet object so the user could iterate through the results himself, but the findvalues method already returns a list.
How could a concatenation of the values be useful to anyone?
For example:
use strict;
use warnings 'all';
use 5.010;
use HTML::TreeBuilder::XPath;
my $content = do { local $/; <DATA> };
my $tree = HTML::TreeBuilder::XPath->new_from_content($content);
say $tree->findvalue('//p');
__DATA__
<p>HTML is just text.</p>
<p>It can still make sense without the markup.</p>
Output:
HTML is just text.It can still make sense without the markup.
Usually, though, it makes more sense to get a list of matches and iterate through them instead of doing dumb concatenation, so you should use findvalues (plural) if you could have multiple matches.
Use
( $tree->findvalues('//p') )[0] ;
instead.
I am doing 2player game and when I get informations from server, it's in format "topic;arg1;arg2" so if I am sending positions it's "PlayerPos;x;y".
I then use split method with character ";".
But then... I even tried to write it on screen "PlayerPos" was written right, but it cannot be gained through if.
This is how I send info on server:
server.write("PlayerPos;"+player1.x+";"+player1.y);
And how I accept it on client:
String Get=client.readString();
String [] Getted = split(Get, ';');
fill(0);
text(Get,20,20);
text(Getted[0],20,40);
if(Getted[0]=="PlayerPos"){
text("HERE",20,100);
player1.x=parseInt(Getted[1]);
player1.x=parseInt(Getted[2]);
}
It writes me "PlayerPos;200;200" on screen, even "PlayerPos" under it. But it never writes "HERE" and it never makes it into the if.
Where is my mistake?
Don't use == when comparing String values. Use the equals() function instead:
if(Getted[0].equals("PlayerPos")){
From the Processing reference:
To compare the contents of two Strings, use the equals() method, as in if (a.equals(b)), instead of if (a == b). A String is an Object, so comparing them with the == operator only compares whether both Strings are stored in the same memory location. Using the equals() method will ensure that the actual contents are compared. (The troubleshooting reference has a longer explanation.)