Passing multiple arrays to define

1,216 views
Skip to first unread message

Jist Anidiot

unread,
Jul 23, 2012, 1:16:21 PM7/23/12
to puppet...@googlegroups.com
I'm trying to make a defined type with two parameters and pass an array for each parameter.

My first try was something like this:

init.pp

define mything ($user, $version) {
     exec {$user_$version: 
         command => something needing both $user and $version
         }


}


in the node file:

$user = [ "A", "B",  "C", "D"]

$version = [ "1.5", "2.0", "4.2", "0.01" ]

mything { "thing":
    user => $user,
    version => $version,

}

Now of course this didn't do what I want it to do (give all the users in $users  all of the versions in $version)

Searching I came across a post that said do something using $title like 

init.pp

define mything ($version) {
    exec {$version: 
        command => something using $foo and $title

    }
}

and the node file

$user = [ "A", "B", "C", "D" ]

mything { $user:
    version =>  "1.5"

}

However this still means I'd have to do one for each version (and figure out how to avoid a Duplicate definition problem.

Is there an easy or a right way to do what I'm trying to do?

Thanks in advance.

Felix Frank

unread,
Jul 24, 2012, 5:02:19 AM7/24/12
to puppet...@googlegroups.com
On 07/23/2012 07:16 PM, Jist Anidiot wrote:
> and the node file
>
> $user = [ "A", "B", "C", "D" ]
>
> mything { $user:
> version => "1.5"
>
> }
>
> However this still means I'd have to do one for each version (and figure
> out how to avoid a Duplicate definition problem.
>
> Is there an easy or a right way to do what I'm trying to do?
>
> Thanks in advance.

If I'm reading this right, you're trying to join two arrays in a
distinct fashion. I'm afraid puppet cannot easily do that.

Your best bet is the create_resources function, if your puppet is recent
enough. Even then you cannot use arrays per se, but have to structure
your data accordingly:

$users = {
"A" => "1.5",
"B" => "2.0",
...
}

Then you can feed this hash to create_resources. With this structure,
your "mything" define would expect the user name to be the resource title.

mything($version) {
exec { "create_mything_for_${name}_with_version_${version}": ... }
}

HTH,
Felix

Jist Anidiot

unread,
Jul 24, 2012, 9:10:15 AM7/24/12
to puppet...@googlegroups.com
Thanks for the reply.  More info inline.



On Tuesday, July 24, 2012 5:02:19 AM UTC-4, Felix.Frank wrote:

If I'm reading this right, you're trying to join two arrays in a
distinct fashion. I'm afraid puppet cannot easily do that.

Your best bet is the create_resources function, if your puppet is recent
enough. Even then you cannot use arrays per se, but have to structure
your data accordingly:

$users = {
  "A" => "1.5",
  "B" => "2.0",
  ...
}


I wasn't exactly clear with what I'm trying to do.  If I understand based on your example, I'd want something like:

$users = {
  "A" => [ "1.5", "2.0", "4.2", "0.01" ],
  "B" => [ "1.5", "2.0", "4.2", "0.01" ],  
  ...
}


 
Then you can feed this hash to create_resources. With this structure,
your "mything" define would expect the user name to be the resource title.

mything($version) {
  exec { "create_mything_for_${name}_with_version_${version}": ... }
}

HTH,
Felix


I'm not at all understanding what to do with this create_resources function. 
 
I'm trying something like:
 create_resources(mythingmodule::mything, $users)

where $user and mything are as described above.

I get an error that looks like it is still taking the version array and squishing it together.  It complains it can't execute "create_mything_for_A_with_version_1.52.04.20.01"

What can I try now?

jcbollinger

unread,
Jul 24, 2012, 9:31:39 AM7/24/12
to puppet...@googlegroups.com


On Monday, July 23, 2012 12:16:21 PM UTC-5, Jist Anidiot wrote:
I'm trying to make a defined type with two parameters and pass an array for each parameter.

In that, you succeeded.
 

My first try was something like this:

init.pp

define mything ($user, $version) {
     exec {$user_$version: 
         command => something needing both $user and $version
         }


}


in the node file:

$user = [ "A", "B",  "C", "D"]

$version = [ "1.5", "2.0", "4.2", "0.01" ]

mything { "thing":
    user => $user,
    version => $version,

}

Now of course this didn't do what I want it to do (give all the users in $users  all of the versions in $version)


Right.  Now let's put that in puppet terms: you want to declare a resource for each combination of one element of $user with one element of $version.  As Felix observed, that can also be expressed as working with a (cross) join of the two arrays.

 

Searching I came across a post that said do something using $title like 

init.pp

define mything ($version) {
    exec {$version: 
        command => something using $foo and $title

    }
}

and the node file

$user = [ "A", "B", "C", "D" ]

mything { $user:
    version =>  "1.5"

}


I think it important to not only come up with a means to your end, but also to understand what you've done once you get there.  What you need to know in this case is that Puppet provides a shortcut for declaring multiple resources of the same type and with the same parameters.  If you give it an array -- whether a literal one or a variable or function result -- as the title of a resource, then Puppet creates one resource for each element of the array.  That can be used similarly to a 'for' loop in a procedural language, though it isn't a loop at all.
 
However this still means I'd have to do one for each version (and figure out how to avoid a Duplicate definition problem.

Is there an easy or a right way to do what I'm trying to do?

Depending on the details of what you're trying to do, Felix's suggestion of the create_resources() function might be your best bet.  On the other hand, you can also do it by interposing another defined type, and using Puppet's built-in regsubst() and split() functions.  That obtains simpler data at the expense of more complex resources:

define mythings_user($versions) {
  # $name == $title == the user

  mything { regsubst($versions, '^.*$', "$name:\0"): }
}

define mything () {
  # $name == $title == the version

  $user_and_version = split($name, ':')
  $user = $user_and_version[0]
  $version = $user_and_version[1]

  exec { "mything $name":
    command => "echo mything v. $version for $user"
  }
}

When the first argument to regsubst() is an array, it performs the specified substitution on each element, and returns an array of the results.  Thus, its usage in definition Mythings_user creates an array of strings of the form "user:version", which are suitable distinct titles for all the needed Mything resources.  The Mything definition then splits its name/title to get the user and version pieces.  Kinda ugly, really.


John



Jist Anidiot

unread,
Jul 24, 2012, 11:35:45 AM7/24/12
to puppet...@googlegroups.com


On Tuesday, July 24, 2012 9:31:39 AM UTC-4, jcbollinger wrote:
I think it important to not only come up with a means to your end, but also to understand what you've done once you get there.  What you need to know in this case is that Puppet provides a shortcut for declaring multiple resources of the same type and with the same parameters.  If you give it an array -- whether a literal one or a variable or function result -- as the title of a resource, then Puppet creates one resource for each element of the array.  That can be used similarly to a 'for' loop in a procedural language, though it isn't a loop at all.
 
However this still means I'd have to do one for each version (and figure out how to avoid a Duplicate definition problem.

Is there an easy or a right way to do what I'm trying to do?

Depending on the details of what you're trying to do, Felix's suggestion of the create_resources() function might be your best bet.  On the other hand, you can also do it by interposing another defined type, and using Puppet's built-in regsubst() and split() functions.  That obtains simpler data at the expense of more complex resources:

define mythings_user($versions) {
  # $name == $title == the user

  mything { regsubst($versions, '^.*$', "$name:\0"): }
}

define mything () {
  # $name == $title == the version

  $user_and_version = split($name, ':')
  $user = $user_and_version[0]
  $version = $user_and_version[1]

  exec { "mything $name":
    command => "echo mything v. $version for $user"
  }
}

When the first argument to regsubst() is an array, it performs the specified substitution on each element, and returns an array of the results.  Thus, its usage in definition Mythings_user creates an array of strings of the form "user:version", which are suitable distinct titles for all the needed Mything resources.  The Mything definition then splits its name/title to get the user and version pieces.  Kinda ugly, really.


John


This almost worked, except when it is called the 2nd or 3rd time, puppet complains that it cannot reassign variable user_and_version.
 

jcbollinger

unread,
Jul 25, 2012, 9:29:18 AM7/25/12
to puppet...@googlegroups.com


On Tuesday, July 24, 2012 10:35:45 AM UTC-5, Jist Anidiot wrote:


On Tuesday, July 24, 2012 9:31:39 AM UTC-4, jcbollinger wrote:
[...]
define mything () {
  # $name == $title == the version

  $user_and_version = split($name, ':')
  $user = $user_and_version[0]
  $version = $user_and_version[1]

  exec { "mything $name":
    command => "echo mything v. $version for $user"
  }
}

[...]


This almost worked, except when it is called the 2nd or 3rd time, puppet complains that it cannot reassign variable user_and_version.
 

Something is very strange with that.  It is routine practice for definition bodies to set local variables, and for such definitions to be instantiated multiple times.  Among other things, it is sometimes used to mangle definitions' properties.  For that matter, definitions' property variables are defined separately for each instance, too.

To troubleshoot your problem, I will need to see the exact code you tested, including the corresponding manifest file layout, and the exact error message Puppet emitted.


John

Message has been deleted

jcbollinger

unread,
Jul 25, 2012, 4:20:07 PM7/25/12
to puppet...@googlegroups.com


On Wednesday, July 25, 2012 9:55:36 AM UTC-5, Jist Anidiot wrote:
On Wednesday, July 25, 2012 9:29:18 AM UTC-4, jcbollinger wrote:


Something is very strange with that.  It is routine practice for definition bodies to set local variables, and for such definitions to be instantiated multiple times.  Among other things, it is sometimes used to mangle definitions' properties.  For that matter, definitions' property variables are defined separately for each instance, too.

To troubleshoot your problem, I will need to see the exact code you tested, including the corresponding manifest file layout, and the exact error message Puppet emitted.


Yes something was very strange.  I did something wrong.  I've got it working now.  The only major problem I had was that puppet didn't like 

mything { regsubst($versions, '^.*$', "$name:\0"): }

I had to take out the regsubst function and do $foo = regsubst(...) mything { $foo: } I don't understand why that was necessary.


Probably the parser just isn't smart enough to handle functions as resource titles.  It looks like I was over-ambitious about its capabilities, and misguided you on that point.  My apologies.

I'm glad you have it working now.


John

Reply all
Reply to author
Forward
0 new messages