I am iterating over a hash of arrays in one of my puppet manifests
1 # class to manage needed packages
2 class profile::packages (
3 ){
4 $packages = hiera_hash('profile::packages::managed', {})
5
6 if $packages != {} {
7 $packages.each | String $package_state, Array $packages_array | {
8 $packages_array.each | Integer $idx, String $package | {
9 notify { "${package} with ${idx} should be ${package_state}": }
10 }
11 }
12 }
13 }
but I get a puppet-lint warning.
WARNING: top-scope variable being used without an explicit namespace
on line 8
Blockquote
WARNING: top-scope variable being used without an explicit
namespace on line 9
The code is running well, so it's a question of puppet-lint. Maybe there is a way to configure puppet-lint to recognize variables in the lambda blocks?
This is a known issue in puppet-lint at the time of writing. It has limited support for Puppet 4 language features.
Related
In the context of Jenkins pipelines, I have some Groovy code that's enumerating a list, creating closures, and then using that value in the closure as a key to lookup another value in a map. This appears to be rife with some sort of anomaly or race condition almost every time.
This is a simplification of the code:
def tasks = [:]
for (platformName in platforms) {
// ...
tasks[platformName] = {
def componentUploadPath = componentUploadPaths[platformName]
echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
// ...
}
tasks.failFast = true
parallel(tasks)
platforms has two values. I will usually see two iterations and two tasks registered and the keys in tasks will be correct, but the echo statement inside the closure indicates that we're just running one of the platforms twice:
14:20:02 [platform2] Uploading for platform [platform1] to [some_path/platform1].
14:20:02 [platform1] Uploading for platform [platform1] to [some_path/platform1].
It's ridiculous.
What do I need to add or do differently?
It's the same issue as you'd see in Javascript.
When you generate the closures in a for loop, they are bound to a variable, not the value of the variable.
When the loop exits, and the closures are run, they will all be using the same value...that is -- the last value in the for loop before it exited
For example, you'd expect the following to print 1 2 3 4, but it doesn't
def closures = []
for (i in 1..4) {
closures << { -> println i }
}
closures.each { it() }
It prints 4 4 4 4
To fix this, you need to do one of two things... First, you could capture the value in a locally scoped variable, then close over this variable:
for (i in 1..4) {
def n = i
closures << { -> println n }
}
The second thing you could do is use groovy's each or collect as each time they are called, the variable is a different instance, so it works again:
(1..4).each { i ->
closures << { -> println i }
}
For your case, you can loop over platforms and collect into a map at the same time by using collectEntries:
def tasks = platforms.collectEntries { platformName ->
[
platformName,
{ ->
def componentUploadPath = componentUploadPaths[platformName]
echo "Uploading for platform [${platformName}] to [${componentUploadPath}]."
}
]
}
Hope this helps!
/* Hello World in Groovy */
println("Hello world")
int a = 5
if (1 == 1){
println a
fcn() //line 11
}
def fcn(){
println a //line 15
}
This is my Groovy script, which gets the error
Hello world
5
Caught: groovy.lang.MissingPropertyException: No such property: a for class: main
groovy.lang.MissingPropertyException: No such property: a for class: main
at main.fcn(main.groovy:15)
at main.run(main.groovy:11)
when executed. Why the variable a is not available within the function fcn?
You can define variable a differently:
Option 1
a = 5
Option 2
import groovy.transform.Field
...
#Field int a = 5
The rational is to define a field in a scope of the script as opposed to the variable defined in the "run method" of the Script and hence not accessible from within other functions.
Consider checking this thread for more information
A link to Field annotation document also provides relevant information
Option 3
You can define the fcn as a proper function, which is in Groovy represented by Closure.
Then you can access the variables of outer scope:
int a = 5
def fcn = {
println a
}
if(true){
fcn()
}
I'm playing a bit with NativeCall to get familiar with that side of Perl6. Of course, I'm trying to load libstatgrab first (what else?).
So I start with easiest part - the host information. Since no cluster support yet, it's just one result - no worries for complication.
The code:
#!/usr/bin/env perl6
use v6;
use NativeCall;
enum sg_error (
SG_ERROR_NONE => 0,
SG_ERROR_INVALID_ARGUMENT => 1,
...
);
class sg_error_details is repr('CStruct') {
has int32 $.error;
has int32 $.errno_value;
has Str $.error_arg;
};
sub sg_init(int32 $ignore_errors) returns int32 is native('statgrab') { * };
enum sg_host_state (
sg_unknown_configuration => 0,
sg_physical_host => 1,
sg_virtual_machine => 2,
sg_paravirtual_machine => 3,
sg_hardware_virtualized => 4
);
class sg_host_info is repr('CStruct') {
has Str $.os_name;
has Str $.os_release;
has Str $.os_version;
has Str $.platform;
has Str $.hostname;
has uint32 $.bitwidth;
has int32 $.host_state;
has uint32 $.ncpus;
has uint32 $.maxcpus;
has uint64 $.uptime;
has uint64 $.systime;
};
sub sg_get_host_info(size_t is rw) returns Pointer is native('statgrab') is symbol('sg_get_host_info_r') { * };
sub sg_free_host_info(Pointer) is native('statgrab') is symbol('sg_free_stats_buf') { * };
sub MAIN() {
my int32 $ignore_errors = 0;
my $error = sg_init($ignore_errors);
if $error != SG_ERROR_NONE {
say "Maeh: $error";
exit 1;
}
my size_t $num_host_infos = 0;
my $res = sg_get_host_info($num_host_infos);
if $num_host_infos > 0 {
my $host_info = nativecast(sg_host_info, $res);
with $host_info {
say "You're using ", $_.os_name, " on ", $_.hostname;
}
}
sg_free_host_info($res);
}
Starting it (dumb) results in loading library error:
$ perl6 statgrab.p6
Cannot locate native library 'libstatgrab.dylib': dlopen(libstatgrab.dylib, 1): image not found
in method setup at /Users/sno/rakudo/share/perl6/sources/24DD121B5B4774C04A7084827BFAD92199756E03 (NativeCall) line 283
in method CALL-ME at /Users/sno/rakudo/share/perl6/sources/24DD121B5B4774C04A7084827BFAD92199756E03 (NativeCall) line 570
in sub MAIN at statgrab.p6 line 95
in block <unit> at statgrab.p6 line 93
Okay - giving it some search path:
$ LD_LIBRARY_PATH=/opt/pkg/lib:$LD_LIBRARY_PATH perl6 statgrab.p6
Cannot locate native library 'libstatgrab.dylib': dlopen(libstatgrab.dylib, 1): image not found
Same picture when using DYLD_LIBRARY_PATH - which is supported by dlopen(3) on Darwin, too.
But changing in the directory works:
$ (cd /opt/pkg/lib && perl6 /data/Projects/OSS/p6-Unix-Statgrab/statgrab.p6 )
You're using Darwin on ernie.[...]
Is there a lack of search path passthrough in the way how moarvm is called?
doug$ perl6 -v
This is Rakudo Star version 2018.10 built on MoarVM version 2018.10
implementing Perl 6.c.
On a fairly recent Rakudo Star on MacOS High Sierra, the script worked "out of the box" for me:
edited script to remove '...'.
Script failed to load the library (really missing!)
brew install libstatgrab
Script ran successfully:
vader:learning doug$ perl6 nativecall_mac_Sno.pl6
You're using Darwin on Vader.local
Homebrew installed the library as follows:
$ v /usr/local/lib
total 11904
-rw-r--r-- 1 doug admin 6080828 Sep 23 12:40 libmoar.dylib
lrwxr-xr-x 1 doug admin 51 Mar 23 11:02 libstatgrab.10.dylib# -> ../Cellar/libstatgrab/0.91/lib/libstatgrab.10.dylib
lrwxr-xr-x 1 doug admin 44 Mar 23 11:02 libstatgrab.a# -> ../Cellar/libstatgrab/0.91/lib/libstatgrab.a
lrwxr-xr-x 1 doug admin 48 Mar 23 11:02 libstatgrab.dylib# -> ../Cellar/libstatgrab/0.91/lib/libstatgrab.dylib
drwxr-xr-x 3 doug admin 102 Mar 23 11:02 pkgconfig/
For me, the perl6 executable is indeed a shell script, but it worked (there was no need to pass any extra LD_LIBRARY_PATH=...).
doug$ file `which perl6`
/Applications/Rakudo/bin/perl6: POSIX shell script text executable, ASCII text, with very long lines
doug$ set | grep LIBRARY
doug$
I have also had issues with my nativecall scripts finding the library, but have always solved them by fixing the library install and/or supplying 'LD_LIBRARY_PATH'.
Sorry this experience was Less Than Awesome for you
I'm making a package, where I have to get a symbol's value by its name in a sub, while the symbol is defined outside the sub.
Here is the simplified code, it works as expected:
#! /usr/bin/env perl6
sub dump_value($symbol) {
say ::("$symbol")
}
# usage:
my $x = 10;
dump_value('$x');
# expected output: 10
# actual output: 10
Then I put the 'dump_value' in a standalone file as below:
# somelib.pm6
unit module somelib;
sub dump_value($symbol) is export {
say ::("$symbol")
}
# client.pl6
#! /usr/bin/env perl6
use lib ".";
use somelib;
my $x = 10;
dump_value('$x');
The compiler complained:
No such symbol '$x'
in sub dump_value at xxx/somelib.pm6 (somelib) line 3
in block <unit> at ./client.pl6 line 8
Following are some experiments. None of them succeeded.
say ::("MY::$symbol")
say ::("OUR::$symbol")
say ::("OUTER::$symbol")
say ::("CLIENT::$symbol")
...
So how to fix the code?
UPDATE:
Thank you! CALLERS::($symbol) solved my original problem. But in a bit more complex situation, the complier complained again:
# somelib.pm6
unit module somelib;
sub dump_value(#symbols) is export {
# output: 6
say CALLERS::('$x');
# error: No such symbol 'CALLERS::$x'
say #symbols.map({ CALLERS::($^id) } )
}
# client.pl6
#! /usr/bin/env perl6
use lib ".";
use somelib;
my $x = 6;
my $y = 8;
dump_value(<$x $y>);
UPDATE AGAIN:
use OUTER::CALLERS::($^id).
UPDATE AGAIN AND AGAIN:
After I put the 'dump_value' in another sub, it didn't work any more!
# somelib.pm6
unit module somelib;
sub dump_value(#symbols) is export {
say #symbols.map({ OUTER::CALLERS::($^id) } )
}
sub wrapped_dump_value(#symbols) is export {
dump_value(#symbols)
}
#! /usr/bin/env perl6
use lib ".";
use somelib;
my $x = 6;
my $y = 8;
# ouput: (6 8)
dump_value(<$x $y>);
# error: No such symbol 'OUTER::CALLERS::$x'
wrapped_dump_value(<$x $y>);
According to the documentation:
An initial :: doesn't imply global. Here as part of the interpolation
syntax it doesn't even imply package. After the interpolation of the
::() component, the indirect name is looked up exactly as if it had
been there in the original source code, with priority given first to
leading pseudo-package names, then to names in the lexical scope
(searching scopes outwards, ending at CORE).
So when you write say ::("$symbol") in dump_value() in the somelib package, it will first lookup $symbol in the current scope, which has value '$x' then try to look up $x (also in the current scope), but the variable $x is defined in the caller's lexical scope, so you get the No such symbol '$x' error.
You can refer to the caller's lexical symbol given by the value of $symbol using either:
CALLER::MY::($symbol); # lexical symbols from the immediate caller's lexical scope
or
CALLERS::($symbol); # Dynamic symbols in any caller's lexical scope
see the package documentation page.
Couple of things:
use lib ".";
use somelib;
our $x = 10; # You need to export the value into the global scope
dump_value('$x');
Then, use the global scope:
unit module somelib;
sub dump_value($symbol) is export {
say GLOBAL::("$symbol")
}
I am having errors in Alloy (4.2) specifications of the following kind:
You must specify a scope for sig "this/Univ"
The issue is easy to reproduce with a toy example:
open util/ordering[State]
open util/integer
sig State { value : Int }
fact {
first.value = 0
all s:State, s': s.next | s'.value = plus[s.value, 1]
}
run { } for 5 State, 3 Int
All of the above is fine. Now, when I define State in an external file and import it with an open statement, I get the "Univ scope" error:
open util/ordering[State]
open util/integer
open State
fact {
first.value = 0
all s:State, s': s.next | s'.value = plus[s.value, 1]
}
run { } for 5 State, 3 Int
I tried several variations of the above without success.
Why does this happen and how can it be solved?
In my project, it would be useful for me to define the target sig of the ordering module in a different file.
Thanks,
Eduardo
This is an Alloy "design bug".
It was decided that a Univ signature would appear when no signatures are defined in the module in order to check some property over built-in relations (e.g., unit, iden, none).
You have many ways of going around this problem, here is a selection :
You can add ",0 Univ" at the end of your run command
You can add a signature in your Alloy module
You can specify a global scope of zero (run { } for 0 but 5 State, 3 Int )
See this question for additional informations