Using Puppet to install Azure client to all nodes - azure

​First, sorry for asking basic and quite silly questions.
I'm very newbie and do not have much experience in this kind of operation.
I have read many documents from official site, tutorialspoint(gave me basic concepts of how puppet work) site and else but still confused and don't know where to start.
Since I wanted to install Azure to all slave nodes, I think I have to create classes like
class packages {
# the first, most obvious solution is
package { 'screen': ensure => 'installed' }
package { 'strace': ensure => 'installed' }
package { 'sudo': ensure => 'installed' }
# you can use a global package parameter
Package { ensure => 'installed' }
package { 'screen': }
package { 'strace': }
package { 'sudo': }
# you can specify the packages in an array ...
$enhancers = [ 'screen', 'strace', 'sudo' ]
package { $enhancers: ensure => 'installed' }
# ... and even combine it a global package parameter
Package { ensure => 'installed' }
$enhancers = [ 'screen', 'strace', 'sudo' ]
package { $enhancers: }
}
cr: https://www.puppetcookbook.com/posts/install-multiple-packages.html
But hey! where should I put that code to ?, How can I execute that? they do not tell me T-T
I really appreciate for all your kindness and your answers
Thanks
Edited on 26th Mar 2019
Thanks for all the comments, I've read for the architecture and be able to create a class now.

Note that The Puppet Cookbook goes back to the days of Puppet 3. It can still be useful, but it predates modern language features like iteration and data types, and no longer aligns with modern best practices.
Nowadays, I rarely see packages grouped into a class like this by the way. Often, packages are externalised in Hiera as data and read into a class, perhaps a "configure" or "install" class, via a packages parameter. (Not that I'm suggesting there's anything wrong with having a packages class.)
To the main part of your question:
But hey! where should I put that code to ?, How can I execute that? they do not tell me T-T
To learn more about how to organise your classes, you need to learn about the Roles and Profiles pattern.
UPDATE: As pointed out in the comments, you may be confused about more basic things than how to organise your classes. At this point, I should say that Stack Overflow is a site for asking specific questions that have a specific answer.
Do have a look at this page here. My suggestion is to follow the advice in there and join the Puppet Community Slack. People in that forum will be glad to help you get started and answer your questions in real time.

Related

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.

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.

Why are all my hosts installing a package defined for one host on the puppetmaster

I'm new to puppet and I'm trying to figure out how to get different hosts installing different packages, but I've stumbled upon an issue I can't figure out. These are my manifests:
My site.pp:
node default {
}
node 'debh3' inherits default {
}
node 'debh4' inherits default {
import "db"
}
node 'master' inherits default {
}
My db.pp:
package { 'mysql-server':
ensure => installed
}
service { 'mysql':
ensure => true,
enable => true,
require => Package['mysql-server']
}
With this setup, mysql-server is being installed on debh3.
If I replace the "import db" with the actual code inside my db.pp, then mysql-server is only installed on debh4 (which is the behaviour i'm after).
Does anyone have a clue what I'm doing wrong here? I've put it all in site.pp to ensure there are no other dependencies affecting anything.
Also note that the import statement is deprecated and about to be removed from Puppet 4.0.
You should move your code to modules. In this case, you want to create a db module.
In /etc/puppet/modules/db/manifests/install.pp
class db::install {
package { 'mysql-server':
ensure => installed
}
}
an in /etc/puppet/modules/db/manifests/service.pp
class db::service {
include db::install
service { 'mysql':
ensure => true,
enable => true,
require => Class['db::install'],
}
}
From you node block, you can then just
include db::install
include db::service
or even just include db::service.
You could have both resources in one class, but it's good practice to structure your code through multiple classes.
Upon further digging, I found this in the "import" documentation at https://docs.puppetlabs.com/puppet/latest/reference/lang_import.html:
Import statements have the following characteristics:
They read the contents of the requested file(s) and add their code to top scope
They are processed before any other code in the manifest is parsed
They cannot be contained by conditional structures or node/class definitions
These quirks mean the location of an import statement in a manifest does not matter.
This points to why what I was doing was incorrect and why it caused the behaviour. As for a solution, I will look into best practices and determine the "correct" way to structure my manifests.

resource ordering synchronization issue "->" doesn't work?

I have encounter really weird behaviour which goes against what I have learned, tutorial says etc. So I would be glad if someone could explain why that is happening.
I have a role module which is made up of composition of profiles (role-profile pattern). My role consists:
class role::lab_prg_c2_dn inherits lab_prg_c2 {
class { 'profile::cluster_data_node':
namenode_fqdn => $role::lab_prg_c2::namenode_fqdn,
secondarynamenode_fqdn => $role::lab_prg_c2::secondarynamenode_fqdn,
}
->
class{'bigdatasolution':}
}
First class installs technology and second one installs our components and items which are build on top of technology. Hence the technology need to be installed first, thats the reason for "->" dependency. However this seems to me doesn't work correctly. As components from class 'bigdatasolution' are installed somewhere before the class profile::cluster_data_node finishes.
I tried to use require => Class['profile::cluster_data_node'] but that doesn't make any difference!
The content of class{'bigdatasolution':} :
class bigdatasolution {
$hdfs_default_conf = '/usr/local/hadoop.hdfs.conf'
$hbase_default_conf = '/usr/local/hadoop.hbase.conf'
include symlinks
include bdjar
}
Symlinks - create symlinks for the configuration installed in class profile::cluster_data_node and are not directly managed - it will be presented when actually specified package get installed.
bdjar - add our jar to a technology library so content is as follows:
class bigdatasolution::bdjar {
file { "/usr/lib/hadoop/lib/bigdata-properties.jar":
ensure => present,
mode => 0644,
group => 'root',
owner => 'root',
source => "puppet:///modules/bigdatasolution/bigdata-properties.jar"
}
}
I even tried to put require => "technologycalClass" here but that doesn't help either.
Can someone please help me understand what's wrong and how that should be solved properly?
I Using puppet 3 and ordering is specified explicetly - so no arbitrary ordering set by puppet should happen.
Thanks
If your 'profile::cluster_data_node' class 'includes' other classes/modules they will have no dependency ordering with the 'bigdatasolution' class.
I see you actually do include symlinks and bdjar. Basically every piece of ordering you want to have in puppet, you need to write explicitly.
Here you should replace the include statements with require, that way the class cluster_data_node will require the other two modules to complete before it says it has completed. Include is a pretty lose way of importing things in puppet and in my opinion is best to just avoid it and go with explicit require statements instead.
TL;DR: included modules have no transitive ordering; required modules do.

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