Copy file from Puppet master to agent unless software is installed - ruby

execution of exec or package types by puppet master controllable
Installation of package X on linux, unless X has been installed already:
package { "X": }
&&
Installation of executable Y on Windows, unless Y has been installed already:
exec { "packageYInstalled":
command => "packageY /S",
require => "C:\\temp\\packageY",
unless => "packageYinstalled";
}
Execution of file type by puppet master uncontrollable as unless attribute is not allowed in puppet file type
puppet file attributes
file { "packageYCopiedToTempFolder": }
path => "C:\\temp\\packageY",
source => "puppet:///files/packageY";
}
Execute installers from shared (samba) folder instead of copy it first to agent system does not solve the issue
Puppet runs executed on multiple external systems
Executables, zips and or tar.gz packages are copied to the remote systems during every puppet run, while these files where removed after installation and software has been installed already

The way i tackle this, and there might be a better ways to do this :
Create a module for installing the product
In that module, write a custom fact for discovering the version installed
In the installer class, wrap everything in an 'if'
i.e.
class productx::install (
$version,
$installer_path,
) {
# productx_version is a fact
if ! $::productx_version {
do the install
}
}
You can do other neat stuff then, like audit the software in your environment

Related

Run cmd commands with open source puppet

Can someone please help me with the open source puppet?
I want to provide a jar file to a windows client and execute the .jar file with the command line.
The .jar file is actually an update for an application which is running as a service.
I am poorly familiar with the puppet language but would guess something like this to execute the jar file:
exec { 'jar_execution':
command => 'cmd.exe /c java -jar foo.jar',
}
Should this be part of the manifest which could look like this?
service { 'fooservice':
name => foo_service,
ensure => running,
enable => true,
}
file { 'foo.jar':
path => 'C:/foo/temp/foo.jar',
ensure => file,
source => "puppet:///modules/foo/foo.jar",
}
exec { 'jar_execution':
command => 'cmd.exe /c java -jar C:/foor/temp/foo.jar',
}
And how does the agent actually run this command?
There are few architectural considerations:
Use archive resource (from archive module) instead of: file with your .jar in your module's .git history.
Have all artifacts (e.g. binary files) served by another service like Apache Archiva, Nexus or Artifactory or even Puppet Server itself. If the .jar is not developed by your company, you may want to use the authoritative source with maybe some internal caching.
Name your resources in a way that are global to your whole infrastructure, otherwise you may have events that notify themselves and produce undesired outcome.
Order of resources in puppet doesn't matter. You want to order the resources the way it makes sense to you and use the before, after, require, notify to ensure dependency.
I would recommend having binaries files outside of a module, as binaries are not supposed to be versioned. You would probably have another service that can serve files, or even your puppet infrastructure can provide those packages, in the similar way it provides the puppet-agent itself, regardless if you use Puppet OSS or Puppet Enterprise.
archive { 'C:/foo/temp/foo.jar':
source => https://location/to/your/.jar,
}
exec { 'C:/foo/temp/foo.jar': # notice in resource name recommended to use / not \
command => 'cmd.exe /c java -jar C:/foor/temp/foo.jar',
refreshonly => true # this one executes exec only if the file was re-downloaded (maybe it's signature has changed), or the file was removed from disk and was re-downloaded.
onlyif => 'test -f C:\foo\temp\foo.jar' # or some command that guards you to not run this .jar every time you run puppet
subscribe => Archive['C:/foo/temp/foo.jar'],
notify => Service['foo_service'] # most probably you need to restart the service after you ran the .jar, otherwise you wouldn't have added in your question.
}
service { 'foo_service':
ensure => running,
enable => true,
}
I notice that in your example you don't need to remove your .jar after being executed. In case you need that, another exec command can remove the file downloaded in the same

Yocto parallel configuration packages files conflict

I have a base package that gives my functionality (wireguard-tools, taken from internet).
This package includes no configuration files for the network interfaces (as it should).
Then I created a few packages with this configuration files that are only to be deployed one for each respective image (e.g. image-1 includes wireguard-1-conf while image-2 includes wireguard-2-conf).
I would like to setup SystemD, but I can only do this when I have an interface configured, and it will only happen when the *-conf package is installed.
Unfortunately, the SystemD service file ("wg-quick#.service") is deployed by wireguard-tools package and my dependent package, the *-conf one, cannot see it:
ERROR: Function failed: SYSTEMD_SERVICE_wireguard-1-conf value wg-quick#wg0.service does not exist
I managed to do a dirty workaround but I feel dirtiest doing this in my *-conf recipe:
do_install_append () {
touch ${D}${systemd_system_unitdir}/wg-quick#wg0.service
pkg_postinst_${PN} () {
rm -f $D/${systemd_system_unitdir}/wg-quick#wg0.service
How should I proceed to make it work "the right way"?
Is there an elegant way of making "wg-quick#.service" from wireguard-tools accessible to *-conf?
Thanks in advance.
Additional Info
My *-conf recipes inherit systemd and include wireguard-tools dependency:
inherit systemd
...
DEPENDS_${PN} = "wireguard-tools"
RDEPENDS_${PN} = "wireguard-tools"
I so nothing else worth to mention in my recipes.

Puppet: generate statement fails when trying to retrieve default path of an executable

I have built a stanza to remove a ruby gem package from our servers. The problem is that the ruby gem executable is installed in different paths on the servers, so on one server it could be in /opt/ruby/bin/gem on other servers it's in /usr/local/rvm/rubies/ruby-2.0.0-p353/bin/gem
My stanza uses the generate function in puppet to pull out the default ruby gem installation as follows:
$ruby_gem_location = generate('which', 'gem')
exec { "remove-remote_syslog":
command => "gem uninstall remote_syslog",
path => "$ruby_gem_location:/opt/ruby/bin:/usr/bin:/usr/sbin",
onlyif => "$ruby_gem_location list|grep remote_syslog"
}
When I run puppet agent I get the following error:
Generators must be fully qualified at ****redacted*
I have also tried to provide a default path for the which command as follows:
$ruby_gem_location = generate('/usr/bin/which', 'gem')
and now the error says : Could not evaluate: Could not find command '/usr/bin/gem
I checked the target server and the gem command is in
/usr/local/rvm/rubies/ruby-2.0.0-p353/bin/gem
What am I doing wrong?
How can I pull out the default ruby gem location on our servers?
Thank you in advance
Your code
$ruby_gem_location = generate('/usr/bin/which', 'gem')
will generate a full path to your gem command (if it succeeds). From the result you describe, I think it is generating '/usr/bin/gem', which is perhaps a symlink to the real gem command. You are putting that into your command path instead of just the directory part, and that will not be helpful. It is not, however, the source of the error message you report.
The real problem here is that generate(), like all DSL fucntions, runs during catalog building. I infer from your results that you are using a master / agent setup, so generate() is giving you a full path to gem -- evidently /usr/bin/gem -- on the master. Since the whole point is that different servers have gem installed in different places, this is unhelpful. The actual error message arises from an attempt to execute your onlyif command with the wrong path to gem.
Your best way forward is probably to create a custom fact with which each node can report the appropriate location of the gem binary. You can then use that fact's value in your Exec, maybe:
exec { "remove-remote_syslog":
command => "$::ruby_gem_path uninstall remote_syslog",
onlyif => "$::ruby_gem_path list | grep remote_syslog"
}
Note that you don't need a path attribute if you give a complete path to the executable in the first place.
Details on creating the $::ruby_gem_path custom fact depend on a number of factors, and in their full generality they are rather too broad for SO, but PL provides good documentation.

Dependency loop during Pupppet provisioning due missing OS package

Im trying to provision my development server using Vagrant and Puppet. Below is some of my Puppet Manifest at this point. The issue im having is that im ending up in a dependency loop which is ofcourse correct. The only problem is that i dont see a way to do it without so therefor i need some help.
Im using the latest version of the box provided by Puppetlabs named puppetlabs/ubuntu-14.04-64-puppet. While adding a PPA to the package manager i receive an error that apt-add-repository is not available. Therefor you need to install the software-properties-common package.
The only problem is that before installing this package, you need to run apt-get update. The second problem is that the manifest wont accept it and it will try to add the PPA before so that, ofcourse which is a logic conclusion, it only has to update the package manager once. But by picking this last solution i will end up in a loop which triggers an error:
==> default: Error: Failed to apply catalog: Found 1 dependency cycle:
==> default: (Exec[add-apt-repository-ppa:ondrej/php-7.0] => Class[Apt::Update] => Exec[apt_update] => Class[Apt::Update] =>
Package[git] => Class[Systempackages] => Apt::Ppa[ppa:ondrej/php-7.0]
=> Exec[add-apt-repository-ppa:ondrej/php-7.0])
class systempackages {
package { [ 'git', 'curl', 'acl', 'unattended-upgrades', 'vim', 'software-properties-common']:
ensure => "installed",
require => [
Class['apt::update'],
],
}
}
/*===========================================*/
## System
Exec { path => [ "/bin/", "/sbin/" , "/usr/bin/", "/usr/sbin/" ] }
class{'systempackages':}
# APT
class { 'apt':
update => {
frequency => 'always',
},
}
apt::ppa { 'ppa:ondrej/php-7.0':
before => Package['php7.0-cli'],
require => Class['systempackages'],
}
# PHP
package {'php7.0-cli':
ensure => 'installed',
}
Given that this is on vagrant, I suggest installing package software-properties-common manually as part of your Vagrantfile.
Something like config.vm.provision "shell", inline: "apt-get update && apt-get install software-properties-common should work.
The circular dependency reflects the fact that Puppet is not a provisioning system. It can be used by a provisioning system or in conjunction with one, but it depends on a fairly substantial software stack being available before it can get off the ground. If Package 'software-properties-common' is necessary for full functioning of the Apt subsystem, then your best bet is to rely on your provisioning system to install it, so that it is available before Puppet ever runs, and to avoid declaring any relationship between that package and the classes and resources of the Apt module.
You are also impacted by the puppetlabs-apt module being quite good about declaring the relationships needed to ensure proper order of application. This is a double-edged sword, however: people cause themselves trouble with surprising frequency by declaring their own relationships with classes or defined types from that module that conflict with the ones it declares itself. In particular, it is asking for trouble to have your Apt::ppa resource require a class containing resources that themselves require any class or resource from the Apt module.
In any case, class apt::update is not a public class of the module. The main implication is that code outside the module should not reference it in any way. You should instead rely on the value you provided for class parameter $apt::update to instruct Puppet to perform an apt-get update at a suitable time.

Puppet - How to only run 'apt-get update' if a package needs to be installed or updated

I can't seem to figure out how to get Puppet to not run 'apt-get update' during every run.
The standard yet inefficient way:
The way I've been doing this is with the main Puppet manifest having:
exec { 'apt-get update':
path => '/usr/bin',
}
Then each subsequent module that needs a package installed has:
package { 'nginx':
ensure => 'present',
require => Exec['apt-get update'],
}
The problem with this is that, every time Puppet runs, Apt gets updated. This puts unnecessary load on our systems and network.
The solution I tried, but fails:
I looked in the Puppet docs and read about subscribe and refreshonly.
Refresh: exec resources can respond to refresh events (via notify, subscribe, or the ~> arrow). The refresh behavior of execs is non-standard, and can be affected by the refresh and refreshonly attributes:
If refreshonly is set to true, the exec will only run when it receives an event. This is the most reliable way to use refresh with execs.
subscribe
One or more resources that this resource depends on, expressed as resource references. Multiple resources can be specified as an array of references. When this attribute is present:
The subscribed resource(s) will be applied before this resource.
so I tried this in the main Puppet manifest:
# Setup this exec type to be used later.
# Only gets run when needed via "subscribe" calls when installing packages.
exec { 'apt-get update':
path => '/usr/bin',
refreshonly => true,
}
Then this in the module manifests:
# Ensure that Nginx is installed.
package { 'nginx':
ensure => 'present',
subscribe => Exec['apt-get update'],
}
But this fails because apt-get update doesn't get run before installing Nginx, so Apt can't find it.
Surely this is something others have encountered? What's the best way to solve this?
Puppet has a hard time coping with this scenario, because all resources are synchronized in a specific order. For each resource Puppet determines whether it needs a sync, and then acts accordingly, all in one step.
What you would need is a way to implement this process:
check if resource A (a package, say) needs a sync action (e.g., needs installing)
if so, trigger an action on resource B first (the exec for apt-get update)
once that is finished, perform the operation on resource A
And while it would be most helpful if there was such a feature, there currently is not.
It is usually the best approach to try and determine the necessity of apt-get update from changes to the configuration (new repositories added, new keys installed etc.). Changes to apt's configuration can then notify the apt-get upate resource. All packages can safely require this resource.
For the regular refreshing of the database, it is easier to rely on a daily cronjob or similar.
I run 'apt-get update' in a cron script on a daily basis, under the assumption that I don't care if it takes up to 24 hours to update OS packages via apt. Thus...
file { "/etc/cron.daily/updates":
source => "puppet:///modules/myprog/updates",
mode => 755
}
Where /etc/cron.daily/updates is, of course:
#!/bin/sh
apt-get -y update
Then for the applications, I just tell puppet something like:
# Ensure that Nginx is installed.
package { 'nginx':
ensure => latest
}
And done, once apt-get update runs, nginx will get updated to the latest version within the next twenty minutes or so (the next time puppet runs its recipe). Note that this requires you to have done 'apt-get update' in the initial image via whatever process you used to install puppet into the image (for example, if this is in CloudFormation, via the UserData section of the LaunchConfiguration). That is a reasonable requirement, IMHO.
If you want to do 'apt-get update' more often, you'll need to put a cron script into /etc/cron.d with the times you want to run it. I plopped it into cron.daily because that was often enough for me.
This is what you need to do - create an apt-get wrapper that would do apt-get update followed by calling a real apt-get (/usr/bin/apt-get) for install. Install the wrapper into a directory that will be in a PATH before apt-get.
Modify /usr/lib/ruby/vendor_ruby/puppet/provider/package/apt.rb and locate the line:
commands :aptget => "/usr/bin/apt-get"
( it will be right below has_features :versionenable, :install_options )
replace that line with:
commands :aptget => "apt-get"
You're done. For some boneheaded reason puppet insists on calling commands with absolute path rather than using a sane PATH variable.

Resources