puppet manifest check if variable is not empty - puppet

Following this existing link - Puppet how to tell if a variable is set. which is , below is the piece of the puppet manifest script :
if defined('$to_dir') {
notify { "Fourthvalue of $from_dir and $to_dir ... ":}
notify { "Fourth$to_dc... ":}
$worker_name = "acme${port}_${machine}${from_dir}_${to_dir}"
$system_id = "${machine}${from_dir}.${to_dir}
} else {
$worker_name = "acme${port}_${machine}_${pod}"
$system_id = $::fqdn
}
However, when we pass "to_dir" as null, it is still going into if block as actual(expect to be in else block).
Even tried using if $to_dir { or if $to_dir != undef { , this did not help.
The value of "to_dir" will be a word either "abc" or "def".
Please advise if something is wrong..

puppet manifest check if variable is not empty
You're throwing around a variety of terms -- "defined", "empty", "null" -- that mean different things or nothing in Puppet. But taken in toto, I think your purpose would be served by testing whether the variable in question is defined and contains a nonempty string. You can do that by matching the variable against an appropriate type expression. For example,
if $to_dir =~ String[1] {
# ...
}
That tests that variable $to_dir contains a string at least one character long. The condition will evaluate to false if $to_dir has not been assigned a value, or if it has been assigned a value of a type different from String, or if its value is an empty string. If the value is a string, it puts no other requirements on the contents. In particular, the value could consist only of one or more space characters.

i think the main issue here is that a variable which is instantiated with $var = undef is a defined variable with a value set to undef (its imo not well named). that said you checking the truthy state or undef (or using something specific to the datatype like size or empty) should all work. e.g.
# at this point defined('$var') == false
$var = undef
# now defined('$var') evalutes to false
# you should be able to check with the following that evaluate to true
($var == undef)
!$var
# you can also use the following which evaluates to false
$var =~ NotUndef

Related

`puppet` undef variable and re-assignment

I am trying to understand what re-assigning a variable means in puppet. I have the following code:
$my_var = "${facts['os']['famil']}"
notice("value of my_var ${my_var}")
if $my_var != undef {
notice("my_var evaluates to true")
} else {
notice("my_var evaluates to false")
}
notice("Type of my_var ${type(my_var)}")
$my_var = 'Linux'
notice("Finishing now")
Notice there is an intentional type on the first line famil instead of family. So puppet fact should not return a value for this fact. However, to my surprise I am seeing this output when I run the code:
Notice: Scope(Class[main]): value of my_var
Notice: Scope(Class[main]): my_var evaluates to true
Notice: Scope(Class[main]): Type of my_var String
Error: Evaluation Error: Cannot reassign variable '$my_var' (file: test.pp, line: 9, column: 9) on node govlupe1.std4.edge.vrsn.com
I have following questions:
How did puppet decide that type of my_var is String when it clearly has no value?
Why is this expression $my_var != undef true?
If the value of my_var is null (undef) then why is puppet complaining about re-assigning the variable?
About 3., whatever value is assigned to $my_var, even undef, it cannot be reassigned another value.
Here is what Puppet documentation says:
Unlike most other languages, Puppet only allows a given variable to be assigned once within a given scope. You cannot change the value of a variable, although you can assign a different value to the same variable name in a new scope
Example:
> puppet apply -e '$myvar = undef ; $myvar = undef'
Error: Evaluation Error: Cannot reassign variable '$myvar' (line: 1, column: 25) on node
About 1. and 2., the type is string because it is the result of an interpolation.
Try with this:
$my_var = $facts['os']['famil']
if $my_var != undef {
notice("my_var evaluates to true")
} else {
notice("my_var evaluates to false")
}
notice("Type of my_var ${type(my_var)}")

Terraform Dynamic Block with conditional based on variable string values

I'm after a dynamic block only when a string var is a certain 2 values (stg or prod). This code doesn't work:
dynamic "log" {
for_each = var.environment == "stg" || var.environment == "prod" ? [1] : [0]
content {
category = "AppServiceAntivirusScanAuditLogs"
enabled = true
}
}
So I want this block when environment is "stg" or "prod" but don't want it when it is anything else. This runs but the logic doesn't work.
I've done something similar in the past using a boolean variable and this has worked but am reluctant to add another variable when I can surely evaluate these strings somehow?
Also tried moving the logic to the "enabled =" field which works but due to the nature of the issue I'm having, I need to do it at the block level.
Your conditionals for the ternary are correct, but the return values are not. When coding a ternary for an optional nested block, the "falsey" return value must be empty. This can be an empty set, list, object, or map type. The type does need to be consistent with your "truthy" return value. In your situation, you are returning a list type for the "truthy" value, so we need to return an empty list type for the "falsey" value:
dynamic "log" {
for_each = var.environment == "stg" || var.environment == "prod" ? [1] : []
content {
category = "AppServiceAntivirusScanAuditLogs"
enabled = true
}
}
As expected, there will be zero iterations on an empty value, which is the desired behavior for the return on the "falsey" conditional. As a side note, my personal preference is to return ["this"] for the "truthy" conditional on optional nested block ternaries to be consistent with recommended practices in Terraform around non-specific block naming conventions.

Groovy Gotcha: String interpolation does not always execute, e.g. in `<list>.contains()`

Answer: it has to do with GString type and "lazy evaluation."
See http://docs.groovy-lang.org/latest/html/documentation/index.html#_string_interpolation for the formal documentation.
See https://blog.mrhaki.com/2009/08/groovy-goodness-string-strings-strings.html for someone's write-up on this.
Firm solution in the code below as commenter said is to explicitly cast it on creation using String targ = "${TARGET_DATA}"
I'm seeing what seems on the surface to be a delayed string interpolation or something in Groovy. I've figured out workarounds for my immediate needs, but the behaviour is a real gotcha, and a potential source for serious bugs...
I strongly suspect it arises from Groovy being a meta-language for Java, and some objects not using the usual string-matching routines.
This was discovered when we were trying to use a string interpolation on some parameter in Jenkins, and checking it against a list of pre-approved values - hence the resulting example below.
Consider this code:
TARGET_DATA= "hello"
data = ["hello"]
targ = "${TARGET_DATA}"
// Case 1: Check for basic interpolated string
if( data.contains(targ) ) {
println "Contained interpolated string"
} else {
println "Interpolation failed"
}
// Case 2: Check to see if using something that actively forces its interpolation changes the variable
println "interpolating targ = ${targ}"
if( data.contains(targ) ) {
println "Contained re-interpolated string"
} else {
println "re-Interpolation failed"
}
// Case 3: Use direct variable assignment
targ = TARGET_DATA
if( data.contains(targ) ) {
println "Contained assigned variable"
} else {
println "Assignment failed"
}
Its output is this:
Interpolation failed
interpolating targ = message: hello
re-Interpolation failed
Contained assigned variable
This indicates that:
In case 1 , the placeholder string is checked for in the list, and fails, as it hasn't been interpolated
In case 2, after forcing the interpreter to perform an interpolation against targ, the content of that variable isn't updated. At this stage, targ still contains a literal placeholder string
In case 3, after assigning the initial variable directly to the target variable, we get a successful match
My guess is that targ literally contains a string starting with a dollar sign, curly brace, and a variable name, etc. This only resolves under certain conditions, like the use of a println , but not in the case of a <list>.contains() which just gets the uninterpolated variable as-is, and does not know during check, to interpolate it.
Using targ = new String("${TARGET_DATA}") does actively interpolate the string however, as the call to function somehow registers as something active.
However this code does interpolate correctly:
TARGET_DATA= "hello"
targ = "${TARGET_DATA}"
def eq(var1) { return var1 == "hello" }
basic_check = eq(targ)
println "${basic_check}" // resolves as true
which means that at some point, the string is interpolated - possibly the == operation has been reimplemented by Groovy to call String's equality function always:
Such that, Groovy re-implemented the String object - and its equality check - but the <list>.contains() function doesn't use that comparator (or it is not caught during script interpretation, due to being compiled code in the Java standard library) and so fails to trigger the interpolation substitution...
Can someone shed some light here please?
targ is of type Gstring, rather than a java String. GString retains the information for how to build itself from the interpolated form.
Because targ isn't a String, it will never pass the equality check required by List.contains, where the List contrains a String.

Understanding Raku's `&?BLOCK` compile-time variable

I really appreciate the Raku's &?BLOCK variable – it lets you recurse within an unnamed block, which can be extremely powerful. For example, here's a simple, inline, and anonymous factorial function:
{ when $_ ≤ 1 { 1 };
$_ × &?BLOCK($_ - 1) }(5) # OUTPUT: «120»
However, I have some questions about it when used in more complex situations. Consider this code:
{ say "Part 1:";
my $a = 1;
print ' var one: '; dd $a;
print ' block one: '; dd &?BLOCK ;
{
my $a = 2;
print ' var two: '; dd $a;
print ' outer var: '; dd $OUTER::a;
print ' block two: '; dd &?BLOCK;
print "outer block: "; dd &?OUTER::BLOCK
}
say "\nPart 2:";
print ' block one: '; dd &?BLOCK;
print 'postfix for: '; dd &?BLOCK for (1);
print ' prefix for: '; for (1) { dd &?BLOCK }
};
which yields this output (I've shortened the block IDs):
Part 1:
var one: Int $a = 1
block one: -> ;; $_? is raw = OUTER::<$_> { #`(Block|…6696) ... }
var two: Int $a = 2
outer var: Int $a = 1
block two: -> ;; $_? is raw = OUTER::<$_> { #`(Block|…8496) ... }
outer block: -> ;; $_? is raw = OUTER::<$_> { #`(Block|…8496) ... }
Part 2:
block one: -> ;; $_? is raw = OUTER::<$_> { #`(Block|…6696) ... }
postfix for: -> ;; $_ is raw { #`(Block|…9000) ... }
prefix for: -> ;; $_ is raw { #`(Block|…9360) ... }
Here's what I don't understand about that: why does the &?OUTER::BLOCK refer (based on its ID) to block two rather than block one? Using OUTER with $a correctly causes it to refer to the outer scope, but the same thing doesn't work with &?BLOCK. Is it just not possible to use OUTER with &?BLOCK? If not, is there a way to access the outer block from the inner block? (I know that I can assign &?BLOCK to a named variable in the outer block and then access that variable in the inner block. I view that as a workaround but not a full solution because it sacrifices the ability to refer to unnamed blocks, which is where much of &?BLOCK's power comes from.)
Second, I am very confused by Part 2. I understand why the &?BLOCK that follows the prefix for refers to an inner block. But why does the &?BLOCK that precedes the postfix for also refer to its own block? Is a block implicitly created around the body of the for statement? My understanding is that the postfix forms were useful in large part because they do not require blocks. Is that incorrect?
Finally, why do some of the blocks have OUTER::<$_> in the but others do not? I'm especially confused by Block 2, which is not the outermost block.
Thanks in advance for any help you can offer! (And if any of the code behavior shown above indicates a Rakudo bug, I am happy to write it up as an issue.)
That's some pretty confusing stuff you've encountered. That said, it does all make some kind of sense...
Why does the &?OUTER::BLOCK refer (based on its ID) to block two rather than block one?
Per the doc, &?BLOCK is a "special compile variable", as is the case for all variables that have a ? as their twigil.
As such it's not a symbol that can be looked up at run-time, which is what syntax like $FOO::bar is supposed to be about afaik.
So I think the compiler ought by rights reject use of a "compile variable" with the package lookup syntax. (Though I'm not sure. Does it make sense to do "run-time" lookups in the COMPILING package?)
There may already be a bug filed (in either of the GH repos rakudo/rakudo/issues or raku/old-issues-tracker/issues) about it being erroneous to try to do a run-time lookup of a special compile variable (the ones with a ? twigil). If not, it makes sense to me to file one.
Using OUTER with $a correctly causes it to refer to the outer scope
The symbol associated with the $a variable in the outer block is stored in the stash associated with the outer block. This is what's referenced by OUTER.
Is it just not possible to use OUTER with &?BLOCK?
I reckon not for the reasons given above. Let's see if anyone corrects me.
If not, is there a way to access the outer block from the inner block?
You could pass it as an argument. In other words, close the inner block with }(&?BLOCK); instead of just }. Then you'd have it available as $_ in the inner block.
Why does the &?BLOCK that precedes the postfix for also refer to its own block?
It is surprising until you know why, but...
Is a block implicitly created around the body of the for statement?
Seems so, so the body can take an argument passed by each iteration of the for.
My understanding is that the postfix forms were useful in large part because they do not require blocks.
I've always thought of their benefit as being that they A) avoid a separate lexical scope and B) avoid having to type in the braces.
Is that incorrect?
It seems so. for has to be able to supply a distinct $_ to its statement(s) (you can put a series of statements in parens), so if you don't explicitly write braces, it still has to create a distinct lexical frame, and presumably it was considered better that the &?BLOCK variable tracked that distinct frame with its own $_, and "pretended" that was a "block", and displayed its gist with a {...}, despite there being no explicit {...}.
Why do some of the blocks have OUTER::<$_> in them but others do not?
While for (and given etc) always passes an "it" aka $_ argument to its blocks/statements, other blocks do not have an argument automatically passed to them, but they will accept one if it's manually passed by the writer of code manually passing one.
To support this wonderful idiom in which one can either pass or not pass an argument, blocks other than ones that are automatically fed an $_ are given this default of binding $_ to the outer block's $_.
I'm especially confused by Block 2, which is not the outermost block.
I'm confused by you being especially confused by that. :) If the foregoing hasn't sufficiently cleared this last aspect up for you, please comment on what it is about this last bit that's especially confusing.
During compilation the compiler has to keep track of various things. One of which is the current block that it is compiling.
The block object gets stored in the compiled code wherever it sees the special variable $?BLOCK.
Basically the compile-time variables aren't really variables, but more of a macro.
So whenever it sees $?BLOCK the compiler replaces it with whatever the current block the compiler is currently compiling.
It just happens that $?OUTER::BLOCK is somehow close enough to $?BLOCK that it replaces that too.
I can show you that there really isn't a variable by that name by trying to look it up by name.
{ say ::('&?BLOCK') } # ERROR: No such symbol '&?BLOCK'
Also every pair of {} (that isn't a hash ref or hash index) denotes a new block.
So each of these lines will say something different:
{
say $?BLOCK.WHICH;
say "{ $?BLOCK.WHICH }";
if True { say $?BLOCK.WHICH }
}
That means if you declare a variable inside one of those constructs it is contained to that construct.
"{ my $a = "abc"; say $a }"; # abc
say $a; # COMPILE ERROR: Variable '$a' is not declared
if True { my $b = "def"; say $b } # def
say $b; # COMPILE ERROR: Variable '$b' is not declared
In the case of postfix for, the left side needs to be a lambda/closure so that for can set $_ to the current value.
It was probably just easier to fake it up to be a Block than to create a new Code type just for that use.
Especially since an entire Raku source file is also considered a Block.
A bare Block can have an optional argument.
my &foo;
given 5 {
&foo = { say $_ }
}
foo( ); # 5
foo(42); # 42
If you give it an argument it sets $_ to that value.
If you don't, $_ will point to whatever $_ was outside of that declaration. (Closure)
For many of the uses of that construct, doing that can be very handy.
sub call-it-a (&c){
c()
}
sub call-it-b (&c, $arg){
c( $arg * 10 )
}
for ^5 {
call-it-a( { say $_ } ); # 0␤ 1␤ 2␤ 3␤ 4␤
call-it-b( { say $_ }, $_ ); # 0␤10␤20␤30␤40␤
}
For call-it-a we needed it to be a closure over $_ to work.
For call-it-b we needed it to be an argument instead.
By having :( ;; $_? is raw = OUTER::<$_> ) as the signature it caters to both use-cases.
This makes it easy to create simple lambdas that just do what you want them to do.

Match two strings with && but getting match on either/or

I'm trying to take a number of input strings and have a
do {
} while
loop matching the strings with
while (!sqlw.equals(w) && !sqlc.equals(c));
However this returns positive match if either one or the other is matched, rather than both. Any ideas? I guess it is something simple.
So, the thing is, in this line:
while (!sqlw.equals(w) && !sqlc.equals(c));
the expression becomes false if either sqlw.equals(w) is true (making !sqlw.equals(w) false) or !sqlc.equals(c) is true (making !sqlc.equals(c) false).
Try replacing && with || like so
while (!sqlw.equals(w) || !sqlc.equals(c));
That way, the expression is false only if !sqlw.equals(w) and !sqlc.equals(c) are both false (i.e. when both match).
EDIT: You could also change it to
while (!(sqlw.equals(w) && sqlc.equals(c)));
which does the same thing but is perhaps a little more faithful to what you wanted (loop while we haven't matched both).
The equals method returns a boolean value. Try sqlw.equals('w') == false.
Also make sure your data types are the same in both cases. One might be a string and the other an array object of some sort.

Resources