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)}")
Related
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
How Nim's way assign variable in or beside other expression at once, the one that'd always led to:
Error: expression 's = "foo"' has no type (or is ambiguous)
when trying like c/c++ code if (s = "foo").len > 5 { cout<< "Yes" ;} or some else?
the point was that, how to have variable assignment in some expressions at once,
if (let s = "foo"; s).len > 5:
echo "Yes"
or
var s: string
if (s = "foo"; s).len > 5:
echo "Yes"
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.
After terraform version update from 0.11 to 0.12.26, I'm seeing error with lookup and list of values inside map.
variable "foo" {
type = map
}
foo = {
x.y = "bar"
}
I have a map "foo" as variable type (map) and then i have key-value pair in map with x.y = "bar". In lookup, I'm trying to read value of x.y as,
lookup(var.foo, x.y)
with this, I'm getting error,
Error: Ambiguous attribute key
on line 13:
13: x.y = "bar"
If this expression is intended to be a reference, wrap it in parentheses. If
it's instead intended as a literal name containing periods, wrap it in quotes
to create a string literal.
can someone help?
If you want to have a map key that contains a dot character . then you must write the key in quotes, so Terraform can see that you intend to produce a string containing a dot rather than to use the value of the y attribute of variable x:
foo = {
"x.y" = "bar"
}
Likewise, to access that element you'll need to quote the key in the index expression, like foo["x.y"]. You could also potentially use lookup(foo, "x.y") -- still with the quotes -- but that approach is deprecated in Terraform 0.12 because foo["x.y"] has replaced it as the main way to access an element from a map value.
I need to do something similar to bar = (foo == null) ? null : foo.bar
Is Null Propagation Operator (?.) available in Node.js? If no, Is this a proposal for the newer version?
[Edit] To add more clarity, I want to avoid NPE when accessing foo.bar (when foo is not defined) and the expression should return a null instead.
Update based on below comment:
The below code will evaluate to the value of foo if foo is falsey (null, undefined, false, empty string, etc), or to foo.bar if foo is not falsey. Like the || operator, the && operator also returns whatever it evaluates last. If foo is falsey, then it will abort evaluation and return the value of foo. If foo is not falsey, then it will continue and evaluate foo.bar and return whatever that is.
foo && foo.bar
Original answer:
You can accomplish this in Javascript using the or operator, ||
The or operator does not necessarily evaluate to a boolean. It returns the last argument that it evaluates the truthiness of.
So, building off of your example, the following code sets foo equal to foo.bar.
foo = null || foo.bar
And the following compares foo to foo.bar (I'm not sure if the double =s in your code was intentional or not. If it was intentional, then consider using triple =s instead)
foo === (null || foo.bar)
This concept is frequently used for setting default values. For example, a function that takes an options argument may do something like this.
options.start = options.start || 0;
options.color = options.color || "red";
options.length = options.length || 10;
Just be careful of one thing. In the last line of the above code, if options.length is 0, then it will be set to 10, because 0 evaluates to false. In that scenario, you probably want to do things the "old-fashioned" way:
options.length = options.length === undefined ? 10 : options.length;
const bar = foo && foo.bar;
This is called Short-circuit evaluation, where second part (part after &&) is only evaluated if the first part is a truthy value.
JavaScript has added support for this, It's called Optional Chaining.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining