Using variables in a Puppet resource chain - resources

I've got a puppet manifest using an array to define some required packages to be installed by APT. I've also got some resource chaining occurring to specify dependencies (I don't want to use 'requires' or 'before' because it makes the code difficult to read. The problem is in the chaining line, the array gets expanded and breaks the chaining. I've followed the custom function workaround here Puppet Syntax: how to include an array of objects into an ordering -> chain? which works but seems a bit overkill to define a function.
$my_deps = ["autoconf", "automake1.9", "autotools-dev", "binutils"]
package { $my_deps:
ensure => installed,
}
exec {'c_update_apt':
command => '/usr/bin/apt-get update',
path => '/usr/bin/',
}
Exec['c_update_apt'] -> Package[ $my_deps ]
This errors saying 'Package[autoconf]Package[automake1.9].... not matched'. Any suggestions most appreciated.

You could use the '<| |>' collection syntax to make sure the Exec block is executed before any packages are installed, instead of just the 4 in your list.
Exec['c_update_apt'] -> Package <| |>
See the bottom of the http://docs.puppetlabs.com/guides/language_guide.html#chaining-resources section.

Related

How can I override a Nix derivative without throwing `cannot coerce a set to a string`?

Or, the goal: How can I take a single package from Nix unstable in a declarative manner?
I'm new to NixOS and currently trying to install a newer version of Consul than the default 0.5.2 of my NixOS version (latest stable). I'm attempting this by overriding the derivative in my /etc/nix/configuration.nix.
I'd like to keep running stable, but I found unstable had the version of Consul that I wanted (0.7.0) already, and so I decided to use this package's attributes as a starting point to override https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/consul/default.nix
I copied it for most part into my configuration.nix, here are the relevant sections:
nixpkgs.config.packageOverrides = pkgs: rec {
consul = pkgs.lib.overrideDerivation pkgs.consul (attrs: rec {
version = "0.7.0";
name = "consul-${version}";
rev = "v${version}";
goPackagePath = "github.com/hashicorp/consul";
src = pkgs.fetchFromGitHub {
owner = "hashicorp";
repo = "consul";
inherit rev;
sha256 = "04h5y5vixjh9np9lsrk02ypbqwcq855h7l1jlnl1vmfq3sfqjds7";
};
# Keep consul.ui for backward compatability
passthru.ui = pkgs.consul-ui;
});
};
environment.systemPackages = with pkgs; [
vim
which
telnet
consul-ui
consul-alerts
consul-template
consul
];
I'm running nix-build (Nix) 1.11.2 which throws:
$ nixos-rebuild switch
building Nix...
building the system configuration...
error: cannot coerce a set to a string, at /etc/nixos/configuration.nix:19:7
(use ‘--show-trace’ to show detailed location information)
When I look at line 19 it's where name is set to "consul-${version}".
Why there is type-coercion going on here? Any tips will be greatly appreciated!
I'm also wondering if there is a better way to run just a single package in unstable, yet doing so declaratively from configuration.nix, rather than imperatively?
To add to what Rok said:
Which should point you that an error actually happens at passthru, line. If you comment it out it will probably build. I'm assuming some recursive calls are at play here and error occurs when it tries to evaluate consul/consul-ui packages.
If you're just starting out, you can safely ignore what follows and perhaps come back to it if/when you're curious about the nitty-gritty.
The problem here is that overrideDerivation is a kind of low-level approach to overriding things. Behind stdenv.mkDerivation, we have a much smaller primitive function called derivation. The derivation function takes some attributes and (more or less -- see the docs for the finer details) just passes those attributes as environment variables during the build. The stdenv.mkDerivation function, on the other hand, has a whole bunch of smarts layered on top that massages the attributes given to it before passing them onto derivation -- and in some cases, as is the case with passthru, it doesn't pass the attribute to derivation at all.
Back to overrideDerivation: it takes the final, tweaked attributes that stdenv.mkDerivation would pass to derivation, and just before that happens it allows you to override those attributes with the function you give it (e.g. that implies that, at that point, passthru has already been removed). When your function adds a passthru, that makes its way into derivation, which then wants to coerce the value of passthru into a string so it can make passthru an environment variable during the build; however, because passthru now points at a attribute-set, and such coercion isn't supported, Nix then complains.
So this sort of puts us in an odd situation. To illustrate, I'll copy the source for the consul package here:
{ stdenv, lib, buildGoPackage, consul-ui, fetchFromGitHub }:
buildGoPackage rec {
name = "consul-${version}";
version = "0.6.4";
rev = "v${version}";
goPackagePath = "github.com/hashicorp/consul";
src = fetchFromGitHub {
owner = "hashicorp";
repo = "consul";
inherit rev;
sha256 = "0p6m2rl0d30w418n4fzc4vymqs3vzfa468czmy4znkjmxdl5vp5a";
};
# Keep consul.ui for backward compatability
passthru.ui = consul-ui;
}
(Note that buildGoPackage is a wrapper around stdenv.mkDerivation.)
You might be familiar with e.g. consul.override, which allows you to supply different inputs (e.g. maybe a different version of consul-ui, or buildGoPackage), but it doesn't allow you to override things that aren't inputs (e.g. src, passthru, etc). Meanwhile, overrideDerivation allows you to modify the attrs given to derivation, but not the ones given to stdenv.mkDerivation. Ideally there would be something in-between, that would allow for manipulating the attrs given to stdenv.mkDerivation, and it so happens that there's a PR open to address this:
https://github.com/NixOS/nixpkgs/pull/18660
Welcome to Nix/NixOS :)
Whenever you need to know more about the error you can use --show-trace and that would give you more verbose error. In your case you would see something like
error: while evaluating the attribute ‘passthru’ of the derivation ‘consul-0.7.0’ at /home/rok/tmp/consul.nix:6:3:
cannot coerce a set to a string, at /home/rok/tmp/consul.nix:6:3
Which should point you that an error actually happens at passthru, line. If you comment it out it will probably build. I'm assuming some recursive calls are at play here and error occurs when it tries to evaluate consul/consul-ui packages.
As for overriding only one package from unstable channel something like this is needed
let
unstable_pkgs = import ./path/to/unstabe/nixpkgs {};
# or
# unstable_pkgs = import (pkgs.fetchFromGitHub {...}) {};
in {
...
nixpkgs.config.packageOverrides = pkgs: rec {
consul = unstable_pkgs.consul;
};
...
}
I haven't try the above, but I'm assuming it will work.

Defining puppet class ordering when if statement exists

I have a Puppet class which, among other things, sets up a whole bunch of Debian repositories, performs an apt-get update and then installs a few packages.
The ordering must be strictly as follows:
setup apt sources
apt-get update
install application packages
To define the ordering, I have used the following chaining after declaring all of my resources:
File['/etc/apt/apt.conf','/etc/apt/sources.list'] ->
Exec['apt-get update'] ->
Class['vim','sudo', ..., ..., ]
However, here is the issue:
If I set vim to install only if the host is a virtual machine, and the host I am performing the Puppet run on is physical, the run will fail. I assume this is because it is attempting to run Class['vim'] as per my chaining but cannot find the class declaration due to the contents of the if statement not being parsed.
Here is the code:
if $::is_virtual == 'true' {
class {'vim':
set_default => true,
}
}
I can work around this by adding a "requires => Exec['apt-get update']" to my vim resource declaration and removing from my chain, but this feels a bit hacky. Here is what my class declaration looks like once I do this:
if $::is_virtual == 'true' {
class {'vim':
requires => Exec['apt-get update'],
set_default => true,
}
}
Does anybody have any other methods of dealing with such ordering issues? Is there anyway I can deal with ordering classes held within conditional statements which may or may not be triggered via chaining? Any advice is appreciated.
Your approach using require is sound. If you really have to stick to the chaining operator, you can wrap the if block in yet another class.
class site::vim {
if $::is_virtual == 'true' { ... }
}
Then include and chain Class[site::vim], which will honor the is_virtual value on its own.

How do I interpret dependency cycles in puppet?

I have a puppet program that I'm trying to use stages in to better manage timing, but when I try that, nothing happens. I then tried to just use a dependency chain, but that throws back this error:
Error: Could not apply complete catalog: Found 1 dependency cycle:
(Anchor[apt::ppa::ppa:saltstack/salt] => Apt::Ppa[ppa:saltstack/salt] => class[Pp_package_manager] => Class[User_manager] => User_manager::User[coder] => User[coder] => File[/etc/default/perfectpitch] => Class[Pp_package_manager])
I'm trying to understand what this error is telling me, but the => signs are confusing the heck out of me. I also tried to open up the .dot file using the --graph flag, but that just confuses me as well. I'd love a guiedhttps://gist.github.com/supereman16/1ff46d6fbb1c7ac9b709.
I'd love a guide on how to interpret these and possibly some help with where the problem actually is. Any help will be greatly appreciated in helping me understand this error, and the steps I should take to find the problem and fix it. Thanks in advance!
Please read this article about relations and ordering in puppet.
In summary.
Puppet is a declarative language, where you describe a desired state of your system (not how to achieve it). So when it compiles manifests code to catalogue, it tries to establish order in which resources should be realized achieve desired state (it creates graph of dependencies).
E.g you cannot run mysql server unless you install mysql package.
Generally puppet properly solves relationships between resources. But sometimes it needs help. For each resource you can manually define relationship between other resources, using before, require, notify, subscribe metaparameters. Unfortunately, using these metaparameters you can easily create a cycle of dependencies.
E.g
file { a: require => File['b'] }
file { b: require => File['c'] }
file { c: require => File['a'] }
Such declaration of resources will create a cycle of dependencies causing compilation error similar to what you have.
It the message you provide, a => b means do resource a before resource b.
You got a cycle of dependencies: ...=> class[Pp_package_manager] => ... Class[Pp_package_manager].
I'm guessing you have defined relationships File[/etc/default/perfectpitch] => Class[Pp_package_manager] and class[Pp_package_manager] => Class[User_manager] , which causes an error.

How to understand plusignment operator in puppet

In URL https://docs.puppetlabs.com/references/glossary.html#plusignment-operator, here is its explanation:
The +> operator, which allows you to add values to resource attributes using the (‘plusignment’) syntax. Useful when you want to override resource attributes without having to respecify already declared values.
For example, I have code like this:
Package {
require => File['/etc/apt/apt.conf.d/no-cache'],
}
package { 'php5-cgi':
ensure => present,
}
Package[ 'php5-cli' ] {
require +> Package['php5-cgi'],
}
What did the operator +> mean here?
Other sample:
subscribe +> Sshkey['www.example.com']
The attribute in question takes a value that is composed of the right hand side and whatever it would have taken otherwise.
In your example, the package { 'php5-cgi' } would normally use a require value of File['/etc/apt/apt.conf.d/no-cache'], since it is the default for all package resources. Through the plusignment, you end up with a value of
require => [ File['/etc/apt/apt.conf.d/no-cache'], Package['php5-cgi'] ]
The php5-cli package builds a relation to both the referenced file and the php5-cgi package.
The same logic will apply to the subscribe metaparameter from your second example. I cannot comment on the whole semantics without more context.
The plusignment works for all resource attributes, although the resulting array values will not make sense for many of them.

Puppet - test if a package already defined?

I'm writing some puppet modules and have a package defined in two modules hence get the following error:
err: Could not retrieve catalog from remote server: Error 400 on SERVER: Duplicate definition: Package[gnome-session-fallback] is already defined in file /etc/puppet/modules/vnc4server/manifests/init.pp at line 3; cannot redefine at /etc/puppet/modules/vino/manifests/init.pp:7 on node l
Hence want to ensure that the package has not already been defined but the following does not work:
if ! defined ('gnome-session-fallback') {
package { 'gnome-session-fallback':
ensure => installed,
}
}
Can anyone suggest how to fix this, and on the broader scale, what is the "proper" approach to avoiding clashes such as this in modules?
You are missing Package[] inside defined(). The correct way to do it:
if ! defined(Package['gnome-session-fallback']) {
package { 'gnome-session-fallback':
ensure => installed,
}
}
The cleanest way to do this is to use the ensure_resource function from puppetlabs-stdlib:
ensure_resource('package', 'gnome-session-fallback', {'ensure' => 'present'})
To answer my own question about what the "proper" approach is : This issue is discussed at https://groups.google.com/forum/?fromgroups=#!topic/puppet-users/julAujaVsVk and jcbollenger offers what looks like a "best-practice" solution - resources which are defined multiple times should be moved into their own module and included into the classes on which they depend. I applied this and solved my problem.
This doesn't actually answer why "if !defined" fails however...
One cleaner way (among multiple ways) is to create a virtual package resource and then realize it. You can realize the same virtual package multiple times without error.
#package { 'gnome-session-fallback':
ensure => installed,
}
And then where you need it:
realize( Package[ 'gnome-session-fallback' ] )

Resources