Default parameters that rely on other parameters - puppet

I wrote a module for chruby, the Ruby version manager. This works fine with Puppet v3, but I just started using Puppet v4 and the $version param does not get interpolated in the $source_url string.
class chruby(
$version,
$source_url = "https://github.com/postmodern/chruby/archive/v${version}.tar.gz",
) {
I'll always want a version passed, and I may want a source url passed though usually not - have the rules changed that this is no longer allowed, and how can I get this to work with v4? I tried this:
unless $source_url {
$source_url = "https://github.com/postmodern/chruby/archive/v${version}.tar.gz"
}
In the class body but it also doesn't interpolate. I've checked there is a $version using notice. I can't find how to do this from the docs:
https://docs.puppetlabs.com/puppet/latest/reference/lang_conditional.html#unless-statements
https://docs.puppetlabs.com/puppet/latest/reference/lang_classes.html#class-parameters-and-variables
I have started using Hiera for some things and understand this may remove the need for defaults, but I've just started using it this last week so I'm not clear on that yet, but still would like to understand why this has changed from v3 to v4.
Any help or insight would be much appreciated.

Use another variable:
class chruby(
$version,
$source_url = undef,
) {
$actual_source_url = $source_url ? {
undef => "https://github.com/postmodern/chruby/archive/v${version}.tar.gz",
default => $source_url,
}
notice($actual_source_url)
}

Related

Self Reference loop EntityFramework problem, another way to solve it?

My project is Web Api Core 5.0 and i have got problems with self reference loop EntityFrameworkCore
In my last project i have used this:
services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
but on my current version .Net core it's not working,
i found something like this:
services.AddMvc().AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
});
but I dont like it, it shows $Id and $value on response, which i dont want to see
Is there another way of the way get rid of that self reference loop?
I read before:
JSON.NET Error Self referencing loop detected for type
and these don't help me
It was only a suggestion ... because your questions is about EF -- but the contents seem to stem from NewtonSoft and possibly Swagger. So I thought it might have more to do with Swagger for your reference loopoing issue. In any case, I've found this solves that type of issue for me. (you would need Swashbuckle.AspNetCore.Newtonsoft)
First add NewtonSoft options.
services.AddControllers()
.AddNewtonsoftJson(opts =>
{
//opts.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
//opts.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
opts.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
Then add swaggergen... and then newtonsoft support.
services.AddSwaggerGen(c =>
{
...
});
// NOW add
services.AddSwaggerGenNewtonsoftSupport();

Puppet manifest: Overriding default variables in Hash

I've been handed the code for a Puppet module which was written by someone else. I've been tasked with getting it working in an actual Puppet environment.
I'm struggling to override defaults in the module in the manifest file. Hopefully this is a syntax issue, and not a issue with the init class.
In init.pp:
class our_module(
# Defaults to be overridden in the manifest file
Hash $config = {
'id' => '38e18a',
'secret' => 'donttellanyone',
'path' => '/test/path'
}
){
# Logic here...
}
How can I override these attributes? I've tried the following which gives my an InvalidCredentialsException:
node 'my_node' {
class { 'our_module':
config => {
id => 'newid',
secret => 'newsecret',
path => '/newpath
}
}
I'm new to Puppet and still getting my head around the docs and the syntax.
Given class our_module as presented in the question, this variation on the node block is valid for declaring that class and customizing its config parameter:
node 'my_node' {
class { 'our_module':
config => {
id => 'newid',
secret => 'newsecret',
path => '/newpath'
}
}
}
Hopefully this is a syntax issue, and not a issue with the init class.
If what you're really using takes the same form as above, then I'm sorry to have to tell you that the problem is not with your class declaration. If your Puppet runs are successful for nodes that do not declare class our_module, then my conclusion would be that the issue is indeed with the class implementation.
I've tried the following which gives my an InvalidCredentialsException
I am disinclined to think that that arises during catalog building. I cannot completely rule out the possibility, but that sure looks like a Java exception, whereas the catalog builder is implemented mainly in Ruby. It could be coming from puppetserver, Puppet's Java-based server front end, but that would not depend on whether your manifests declare a particular class. My crystal ball suggests that the our_module implementation is Execing a Java program when it is applied to the client, and that it is that program that is throwing the exception.
Some of those possibilities could be related to bad class parameter data, but I don't see any reason to think that the issue arises from a syntax error.

NixOS - Module imports with arguments

Let's say I have my NixOS configuration.nix set up as follows:
{config, pkgs, ...}:
{
services.openssh.enable = true;
}
I now want to have a second file called networking.nix which sets my hostname based on an argument.
{config, pkgs, hostname, ...}:
{
networking.hostName = hostname
}
Is this possible? How can I include the file. I already tried doing it by using imports = [ ./networking.nix { hostname = "helloworld"; } ]; but that didn't work.
Thanks.
A 'NixOS configuration file' is simply a module that doesn't define options, so there is really no distinction. A configuration.nix file is just a module, and typically it does not define any options, so it can be written in the abbreviated form.
Defining options is the normal way for NixOS modules to pass information around, so that's the most idiomatic way to go about.
However, if you really must, for some very special reason, because you're doing very unusual things with NixOS, you can put arbitrary functions in imports. But you shouldn't, because it doesn't work well with the module system's custom error messages and potentially other aspects that rely on knowing where a module is defined. If you do so, do make sure it is an actual function. In your case, that would imply modifying the first line of networking.nix to make it a curried function:
hostname: {config, pkgs, ...}:
Not very pretty in my opinion. Although it is very explicit about what is going on, it deviates from what is to be expected of a NixOS module.
I encountered this problem today and came up with a fairly simple solution recommended in the manual.
foobar.nix
{ lib, withFoo ? "bar", ... }:
# simple error checking to ensure garbage isn't passed in
assert lib.asserts.assertOneOf "withFoo" withFoo [
"bar"
"baz"
# other valid choices ...
];
{
# ...
}
configuration.nix
args#{ ... }:
{
imports = [
# ...
(
import ./foobar.nix (
args
// { withFoo = "baz"; }
)
)
# ...
];
}
This is ideal for 'one off' options in your configurations.
You should be able to use the _module.argsoption [1] to do that. So your configuration.nix would be like:
{config, pkgs, ...}:
{
_module.args.hostname = "ahostname";
services.openssh.enable = true;
}
However where the values are very simple it will probably be much easier to just set them directly, e.g. just define networking.hostname in configuration.nix. This section of the manual re. merging and priorities may be helpful also [2].
Further discussion:
The value of _module.args is indeed applied to all imported configurations (though the value will only be used in modules that directly refer to it, such as the pkgs value, the ... represents all the values that aren't referenced).
For passing arguments to modules it seems a good approach to me, but from your comments perhaps a different approach might be more suitable.
Another solution could be to flip the relationship in the imports: rather than a single common config that passes multiple different arguments instead multiple different configs import the common configuration. E.g.
$cat ./common.nix
{ services.openssh.enable = true; }
$cat ./ahostname.nix
{ imports = [ ./common.nix ]; networking.hostname = "ahostname"; }
The NixOS config in this Reddit comment looks like it uses this approach. There are quite a few other NixOS configurations that people have shared publicly online so you might find some useful ideas there. The points in the answer from Robert Hensing are very useful to bear in mind as well.
However it's hard to say what might be a better solution in your case without knowing a bit more about the context in which you want to use it. You could create a new SO question with some more information on that which might make it easier to see a more appropriate solution.

Puppet 2.7: Updating hash in the parent scope fails

I need to recursively insert entries in a hash based on some logic. The state of the hash gets updated inside the defined type loop, but not in the outer scope. The following should clarify:
class Test {
$config = {}
define my_loop()
{
$config['a'] = 'b'
notify { "1) config = $config": } # shows that $config has a=>b
}
my_loop { 'loop' : }
notify { "2) config = $config":
require => My_loop['loop'] # shows that $config is empty
}
}
So, the problem is that $config inside the loop() contains a=>b, but outside the loop() it doesn't. I must be bumping against some scoping rules here.
Thoughts?
The values of Puppet variables are set once, and they do not change thereafter. In those few places that present the appearance of behaving differently, what actually happens is either that a modified local copy or an entirely independent variable is created.
Additionally, do not nest classes or defined types inside classes. Puppet allows it for historical reasons, but it doesn't have the semantics you probably expect, and it makes the nested class / type hard to find.
Consider writing a custom function to perform your computation and return the needed hash.
Also consider whether upgrading to a supported version of Puppet would be feasible. Version 2.7 is very old.

How to iterate over an array in Puppet

I would like to iterate over an array that is stored as a Facter fact, and for each element of the array create a new system user and a directory, and finally make API calls to AWS.
Example of the fact: my_env => [shared1,shared2,shared3]
How can I iterate over an array in Puppet?
This might work, depending on what you are doing
# Assuming fact my_env => [ shared1, shared2, shared3 ]
define my_resource {
file { "/var/tmp/$name":
ensure => directory,
mode => '0600',
}
user { $name:
ensure => present,
}
}
my_resource { $my_env: }
It will work if your requirements are simple, if not, Puppet makes this very hard to do. The Puppet developers have irrational prejudices against iteration based on a misunderstanding about how declarative languages work.
If this kind of resource doesn't work for you, perhaps you could give a better idea of which resource properties you are trying to set from your array?
EDIT:
With Puppet 4, this lamentable flaw was finally fixed. Current state of affairs documented here. As the documentation says, you'll find examples of the above solution in a lot of old code.
As of puppet 3.2 this is possible using the "future" parser like so:
$my_env = [ 'shared1', 'shared2', 'shared3', ]
each($my_env) |$value| {
file { "/var/tmp/$value":
ensure => directory,
mode => 0600,
}
user { $value:
ensure -> present,
}
}
See also: http://docs.puppetlabs.com/puppet/3/reference/lang_experimental_3_2.html#background-the-puppet-future-parser
Puppet 3.7 released earlier this month have the new DSL, which one feature is the iteration, check the following URL https://docs.puppetlabs.com/puppet/latest/reference/experiments_lambdas.html#enabling-lambdas-and-iteration
these new features can be enabled with the :
Setting parser = future in your puppet.conf file
or adding the command line switch --parser=future
hope that helps
As of latest Puppet (6.4.2), and since Puppet 4, iteration over arrays is supported in a few ways:
$my_arr = ['foo', 'bar', 'baz']
Each function:
$my_arr.each |$v| {
notice($v)
}
Each function alternative syntax:
each($my_arr) |$v| {
notice($v)
}
To get the index:
Pass a second argument to each:
$my_arr.each |$i, $v| {
notice("Index: $i, value: $v")
}
Comparison with Ruby:
Note that this grammar is inspired by Ruby but slightly different, and it's useful to show the two side by side to avoid confusion. Ruby would allow:
my_arr.each do |v|
notice(v)
end
Or:
my_arr.each { |v|
notice(v)
}
Other iteration functions:
Note that Puppet provides a number of other iteration functions:
each - Repeats a block of code a number of times, using a collection of values to provide different parameters each time.
slice - Repeats a block of code a number of times, using groups of values from a collection as parameters.
filter - Uses a block of code to transform a data structure by removing non-matching elements.
map - Uses a block of code to transform every value in a data structure.
reduce - Uses a block of code to create a new value, or data structure, by combining values from a provided data structure.
with - Evaluates a block of code once, isolating it in its own local scope. It doesn’t iterate, but has a family resemblance to the iteration functions.
Puppet 3 and earlier:
If you have inherited old code still using Puppet 3, the accepted answer is still correct:
define my_type {
notice($name)
}
my_type { $my_arr: }
Note however that this is usually considered bad style in modern Puppet.
itsbruce's answer is probably the best for now, but there is an iteration proposal going through puppetlabs' armatures process for possible implementation in future.
There is a "create_resources()" function in puppet. that will be very helpful while iterating over the list of itmes

Resources