Convert Heira variable to comma separated token - puppet

I have a yaml file where I can specify n number of options:
---
solr:
- dev
- test
I then call them into a puppet variable using heira:
if $solr_values == undef {
$solr_values = hiera('solr', false)
}
if count($solr_values) > 0 {
class { solr:
cores => [ $solr_values ],
}
}
However $solr_values is coming through as 'devtest' and not 'dev'. 'test' as I'd expect given that it's a list in yaml. Can someone advise on the best approach here?

The expression [ $solr_values ] gives you an array of arrays, e.g. [ [ 'dev', 'test', ... ] ], which is likely not what you want.
I suggest plain
cores => $solr_values,
Note that when you use the array variable $core/$solr_values in a string, e.g.
$debug = "VALUES: '$solr_values'"
Puppet will coerce the array into a string by simply concatenating the values, so you will still end up with 'devtest...'
What you want to do is make use of the join function from the stdlib module, e.g.
cores => join($solr_values, ','),

Related

Convert from Tuple of strings to strings in terraform

I have an issue where I want to pass a list of vpc_ids to aws_route53_zone while getting the id from a couple of module calls and iterating it from the state file.
The out put format I am using is:
output "development_vpc_id" {
value = [for vpc in values(module.layout)[*] : vpc.id if vpc.environment == "development"]
description = "VPC id for development env"
}
where I get the output like:
"development_vpc_id": {
"value": [
"xxxx"
],
"type": [
"tuple",
[
"string"
]
]
},
instead I want to achieve below:
"developmemt_vpc_id": {
"value": "xxx",
"type": "string"
},
Can someone please help me with the same.
There isn't any automatic way to "convert" a sequence of strings into a single string, because you need to decide how you want to represent the multiple separate strings once you've reduced it into only a single string.
One solution would be to apply JSON encoding so that your output value is a string containing JSON array syntax:
output "development_vpc_id" {
value = jsonencode([
for vpc in values(module.layout)[*] : vpc.id
if vpc.environment == "development"
])
}
Another possibility is to concatenate all of the strings together with a particular character as a marker to separate each one, such as a comma:
output "development_vpc_id" {
value = join(",", [
for vpc in values(module.layout)[*] : vpc.id
if vpc.environment == "development"
])
}
If you expect that this list will always contain exactly one item -- that is, if each of your objects has a unique environment value -- then you could also tell Terraform about that assumption using the one function:
output "development_vpc_id" {
value = one([
for vpc in values(module.layout)[*] : vpc.id
if vpc.environment == "development"
])
}
In this case, Terraform will either return the one element of this sequence or will raise an error saying there are too many items in the sequence. The one function therefore acts as an assertion to help you detect if there's a bug which causes there to be more than one item in this list, rather than just silently discarding some of the items.

Combine two arrays to create every possible combination with Azure Bicep [duplicate]

I want to create a resource for many regions, environments, apps, etc at once.
I'd like to do something like this:
param apps array = [
'app1'
'app2'
'app3'
]
param environments array = [
'alpha'
'beta'
]
param regions array = [
'ne'
'we'
'uks'
]
resource origin_group 'Microsoft.Cdn/profiles/origingroups#2021-06-01' = [ for region in regions: {
[ for env in environments: {
[ for app in apps: {
parent: profiles_global_fd_name_resource
name: '${env}-${region}-${app}-origin-group'
properties: {
loadBalancingSettings: {
sampleSize: 4
successfulSamplesRequired: 3
additionalLatencyInMilliseconds: 50
}
healthProbeSettings: {
probePath: '/'
probeRequestType: 'HEAD'
probeProtocol: 'Http'
probeIntervalInSeconds: 100
}
sessionAffinityState: 'Disabled'
}
}]
}]
}]
All docs mentioning nested loops talk about looping inside a resource to create many sub-resources. Not what I'm after. Perhaps another way would be to somehow merge all these arrays into a single array of objects of every possible iteration. Not sure where to start with that either.
Any help much appreciated.
This is not supported for the moment but it will (see Is there plans to support nested loop on resources?).
Using a little bit of math, you could achieve what you'd like (Not sure if you should):
param environments array = [ 'alpha', 'beta' ]
param regions array = [ 'ne', 'we', 'uks' ]
param apps array = [ 'app1', 'app2', 'app3' ]
// Setting some variables for clarity
var envCount = length(environments)
var regionCount = length(regions)
var appCount = length(apps)
// Setting the total number of combination
var originGroupCount = envCount * regionCount * appCount
// Iterate all possible combinations
output originGroupNames array = [for i in range(0, originGroupCount): {
name: '${environments[i / (regionCount * appCount) % envCount]}-${regions[i / appCount % regionCount]}-${apps[i % appCount]}-origin-group'
}]
this will output all possible combinations (I think) for origin group name.

Create a set from a list of sets

I am trying to create a predefined set of IAM roles.
locals {
default_iam_roles = {
group1 = {
name = "group:group1-group#mydomain.com"
roles = toset([
"roles/viewer"
])
}
group2 = {
name = "group:group2-group#mydomain.com"
roles = toset([
"roles/owner"
])
}
}
formatted_iam_roles = [ for member in local.default_iam_roles : setproduct([member.name], member.roles) ]
}
If I print local.formatted_iam_roles I get the following:
[
toset([
[
"group:group1-group#mydomain.com",
"roles/viewer",
],
]),
toset([
[
"group:group2-group#mydomain.com",
"roles/owner",
],
]),
]
Now I want to create a single set which contains all of the combinations contained in the list, so that I can feed it to a resource on a for_each statement, but I am not able to find the logic for this.
The expected output would be:
toset([
[
"group:group1-group#mydomain.com",
"roles/viewer",
],
[
"group:group2-group#mydomain.com",
"roles/owner",
]
])
The operation of taking multiple sets and deriving a new set which contains all of the elements across all of the input sets is called union and so Terraform's function for it is called setunion to follow that.
locals {
all_iam_roles = setunion(local.formatted_iam_roles...)
}
The ... symbol after the argument tells Terraform that it should use each element of local.formatted_iam_roles as a separate argument to setunion, because that function is defined as taking an arbitrary number of arguments that are all sets, rather than as taking a list of sets.
It might be helpful to think of setunion as being a similar sort of function as concat. The concat function joins multiple lists together, preserving each list's element order and the order the lists are given. Set elements don't have any particular order and contain each distinct value only once, and so the behavior of setunion is different but its purpose is related.
Your solution in the question was close, you just have to apply a flatten function to the setproduct output.
locals {
# ...
formatted_iam_roles = [for member in local.default_iam_roles : flatten(setproduct([member.name], member.roles))]
}
The output will be the following:
formatted_iam_roles = [
[
"group:group1-group#mydomain.com",
"roles/viewer",
],
[
"group:group2-group#mydomain.com",
"roles/owner",
],
]
Now, if you really want it to make a set for the final result, you can use toset, most like it will be pointless:
formatted_iam_roles = toset([for member in local.default_iam_roles : flatten(setproduct([member.name], member.roles))])
I finally faced the problem using maps instead of lists, because I needed to use a for_each argument and it doesn't accept a set of tuples as value.
If anyone knows a better approach, post it and I will be pleased of marking it as correct.
locals {
formatted_iam_roles = merge([ for member in local.default_iam_roles :
{ for pair in setproduct([member.name], member.roles) :
"${pair[0]}${pair[1]}" => { "name": pair[0], "role": pair[1] }
}
]...)
}
resource "google_project_iam_member" "team_access" {
for_each = local.formatted_iam_roles
project = var.project_id
member = each.value["name"]
role = each.value["role"]
}

Select first element of a nested list of lists

I am trying to get the first element in each nested list in a list of lists in JMESpath. Specifically, I am trying variations on:
aws redshift-data get-statement-result \
--id 0de36215-f1db-421f-8704-3242e1a03f21 \
--query 'Records[][0]'
Unfortunately this results in the parent list being firstly flattened, and thus I get just the first element of the first list.
The input data (simplified) is:
{
"Records": [
[{"stringValue": "rdsdb"},{"etc": 1}],
[{"stringValue": "testuser"},{"etc": 100}]
],
"ColumnMetadata": [{"etc": true}],
"TotalNumRows": 2
}
Given that input data, the output I would expect from that:
[
{"stringValue": "rdsdb"},
{"stringValue": "testuser"}
]
How can I get the first element in each nested list in a list of lists?
From what I gather from your comment, you over-simplified your example and you actually after a whole object, and not a single property.
With that requirement, you can achieve it using [*] instead of [], as the first one does not flatten projections, compared to the second one.
So, given the JSON input:
{
"Records": [
[{"stringValue": "rdsdb", "some-property": "foo"},{"etc": 1}],
[{"stringValue": "testuser"},{"etc": 100}]
],
"ColumnMetadata": [{"etc": true}],
"TotalNumRows": 2
}
The query:
Records[*][0]
Yields the expected:
[
{
"stringValue": "rdsdb",
"some-property": "foo"
},
{
"stringValue": "testuser"
}
]
Otherwise, you can filter out all the object in the array of array Records using a filter projection that would look if the key stringValue is not null with [?stringValue]
But because [] is creating a projection, as you raised, you have to stop that projection first, with the pipe operator |, as explained in the linked documentation chapter.
So given:
Records[]|[?stringValue]
This yields the expected:
[
{
"stringValue": "rdsdb"
},
{
"stringValue": "testuser"
}
]

Create a custom setxkbmap option

Oddly, this seems like it should be something that's been done before: I want to swap the numbers and symbols on the 1–0 keys across the top of my keyboard so that:
When I hit the 6 key, an * is typed instead of a 6.
When I hit Shift+6 the number 6 will display instead of an *.
There were several other modifications that I wanted to make, but quickly found that others had already accomplished these layout modifications before using options for setxkbmap (like caps:swapescape, for example).
Given the above, this is a 3-part question:
Is there an option for swapping numbers and symbols on the top row of my keyboard?
Whether there is or not, is there any way to find out if such a thing exists without having to manually browse the *.lst and *.xml files in /usr/share/X11/xkb/rules/?
Finally, if I were to create an option for setxkbmap, what would be an ideal approach, and how would I see about contributing my option back to the community?
As for question 3:
I have attempted to create the option without success (setxkbmap silently fails and I'm not even confident in my approach).
I can't find where the project is hosted.
Aside from man setxkbmap and various blog posts that touch on the topic, I've been unable to find any documentation on any of this.
Question 2:
For a list of all options available, you can execute localectl list-x11-keymap-options. This seems to only provide you with the options themselves, not the descriptions, so a better approach may be to execute the following on the XKB *.lst files:
for f in /usr/share/X11/xkb/rules/*.lst; do sed -ne '/^\! option$/{s///; :a' -e 'n;p;ba' -e '}' $f; done | sort -u
(sed reference)*
If you're looking for something related to swapping numbers, you can append | grep -i num, revealing several options for working with the numpad/keypad. Unfortunately, I think that all of the layouts have the numbers laid out in the templates related to alphanumeric characters, meaning they're built in to the regional layouts themselves (or the variant, in the case of dvorak).
Question 1:
There are three approaches that you can take.
Override layouts using xmodmap
You can create a somewhat versatile approach by creating an .Xmodmap file in your home to override mappings, as described on the Arch Wiki here.
Here is an example configuration: https://github.com/karma0/layouts/blob/master/home/.Xmodmap
Steps:
Drop .Xmodmap in your home.
Add the line xmodmap $HOME/.Xmodmap to your .profile
A quick and dirty, but flexible approach:
Run xkbcomp -xkb $DISPLAY xkbmap to generate a file xkbmap with your current configuration in it.
Modify it to match the desired configuration. Here's an example:
Original:
key <AE01> { [ 1, exclam ] };
key <AE02> { [ 2, at ] };
key <AE03> { [ 3, numbersign ] };
key <AE04> { [ 4, dollar ] };
key <AE05> { [ 5, percent ] };
key <AE06> { [ 6, asciicircum ] };
key <AE07> { [ 7, ampersand ] };
key <AE08> { [ 8, asterisk ] };
key <AE09> { [ 9, parenleft ] };
key <AE10> { [ 0, parenright ] };
Modified:
key <AE01> { [ exclam, 1 ] };
key <AE02> { [ at, 2 ] };
key <AE03> { [ numbersign, 3 ] };
key <AE04> { [ dollar,i 4 ] };
key <AE05> { [ percent, 5 ] };
key <AE06> { [ asciicircum, 6 ] };
key <AE07> { [ ampersand, 7 ] };
key <AE08> { [ asterisk, 8 ] };
key <AE09> { [ parenleft, 9 ] };
key <AE10> { [ parenright, 0 ] };
Execute the command xkbcomp -w 0 xkbmap $DISPLAY to load the new configuration.
Get the command to run at startup using xinitrc or similar.
Modify your layout and add a new variant
Open up your favorite layout file (likely under /usr/share/X11/xkb/symbols). We'll use the us file for this example.
Find your favorite variant within the file; workman-intl if you're like me.
Assuming you want to replicate the workman-intl layout, you can duplicate that section, and modify it similar to how I did here (note that this is copy/pasted from the intl template and the first and second columns are simply swapped):
partial alphanumeric_keys
xkb_symbols "workman-programmer" {
include "us(workman-intl)"
name[Group1]= "English (Workman, intl., with dead keys and num/sym swapped)";
key <AE01> { [ exclam, 1, exclamdown, onesuperior ] };
key <AE02> { [ at, 2, twosuperior, dead_doubleacute ] };
key <AE03> { [ numbersign, 3, threesuperior, dead_macron ] };
key <AE04> { [ dollar, 4, currency, sterling ] };
key <AE05> { [ percent, 5, EuroSign, dead_cedilla ] };
key <AE06> { [ dead_circumflex,6, onequarter, asciicircum ] };
key <AE07> { [ ampersand, 7, onehalf, dead_horn ] };
key <AE08> { [ asterisk, 8, threequarters, dead_ogonek ] };
key <AE09> { [ parenleft, 9, leftsinglequotemark, dead_breve ] };
key <AE10> { [ parenright, 0, rightsinglequotemark, dead_abovering ] };
};
The xkb_symbols line defines the name of your variation; the include line borrows everything you need from the variation of your choice within the file (here, it's the workman-intl variation in the us layout). Then, the definitions you want are what follows.
4. Add your new definition to /usr/share/xkb/rules/base.xml to the end of the variantList tag. Here's the one I used:
<variant>
<configItem>
<name>workman-programmer</name>
<description>English (Workman, intl., with dead keys and num/sym swapped)</description>
</configItem>
</variant>
Add the new variant and description to the ! variant section of /usr/share/X11/xkb/rules/base.lst as:
workman-programmer us: English (Workman, intl., with dead keys and num/sys swapped)'
Restart your Xorg server.
Setup the setxkbmap command to run using the new variant. Here's the one for this demonstration: setxkbmap -layout us -variant workman-programmer -option
Question 3:
Try as you might, you're not going to find the documentation until you start looking for xkb documentation, which is situated within the xorg ecosystem.
The best write-up out there is probably this one:
https://www.charvolant.org/doug/xkb/html/index.html
QUOTE:
Before you read this, please understand that I never wanted to write this document, being grossly under-qualified, but I always wanted to read it, and this was the only way.
Additionally, here are a list of links as well to get started on learning all of the intricacies of the xkb system in xorg: https://www.x.org/wiki/XKB/
Note: most of the documentation references relative paths within xkb as it is installed on your system. This is typically under /usr/share/X11/xkb
If you wish to contribute, this project lives under the xorg, which provides developer documentation here: https://www.x.org/wiki/guide/, or better, here: https://www.x.org/wiki/Development/

Resources