Running a command once after a group of packages is installed - puppet

I have an existing puppet manifest which installs a bunch of php5 packages and only after being installed restarts apache. The simplified manifest is something like
package { 'apache-php':
name => $modules,
ensure => installed
}
exec {'enable-mod-php':
command => $enable_cmd,
refreshonly => true
}
Package['apache-php'] ~> Exec['enable-mod-php'] ~> Service['apache']
After a system upgrade catalog runs have started failing with the following error message:
Error: Failed to apply catalog: Parameter name failed on Package[apache-php]: Name must be a String not Array at /etc/puppet/modules/apache/manifests/php.pp:22
I found out that I was using an undocumented feature/bug: Puppet 3.4.0 name as an array in package.
However, I'm having a hard time finding out how to redo my setup after the upgrade. How can I rewrite this manifest so that it works with more recent puppet versions?

Instead of using an arbitrary title for the package define in your example. (eg. apache-php) and using a name parameter, you can do the following:
$modules = ['foo','bar','baz']
package { $modules:
ensure => present
notify => Exec['enable-mod-php']
}
exec {'enable-mod-php':
command => $enable_cmd,
refreshonly => true,
notify => Service['apache']
}
service { 'apache':
# your apache params
}
I haven't looked at the code for the package provider, but can verify that the above works. You should also note that chaining arrows are all well and good, but according to the Puppet style guide, metaparameters are preferred.
Hope this helps.

Related

Docker not installed before running containers with Puppet

I am trying to run a docker container using the puppetlabs/docker module. However when the Puppet agent attempts to run the container I receive the error
Error: Failed to apply catalog: No such file or directory - docker
It would seem that neither the docker daemon nor the docker client are being installed before Puppet attempts to create the container.
An excerpt of the configuration is as follows:
Puppetfile
# frozen_string_literal: true
forge 'https://forge.puppet.com'
# Modules from the Puppet Forge
# ...
mod 'puppetlabs-docker', '4.4.0'
# ...
agent.pp
class profile::runner::agent (
Enum[present, absent] $ensure = present,
String $version = undef,
String $image = undef,
String $container_name = "${facts['group']}-agent",
Array[String] $container_environment = [],
) {
class { 'docker':
version => $version,
}
# ...
docker::run { $container_name:
ensure => $ensure,
image => $image,
env => $container_environment,
net => 'host',
restart => 'unless-stopped',
}
# ...
}
My understanding is that this configuration from the puppetlabs/docker module is supposed to ensure docker is installed before any container is started. I considered making a bug report on the module itself but this issue would surely be so common I'd be surprised if it had not already been reported. So I concluded that I must be doing something incorrect.
I have tried the usual metaparameters but none seem to have any effect and result in the same error. For example I have tried
docker::run { $container_name:
ensure => $ensure,
image => $image,
env => $container_environment,
net => 'host',
restart => 'unless-stopped',
require => Class['docker'], # and also Package['docker']
}
It may be worth mentioning that the Puppet agent is running on RockyLinux so therefore a RedHat OS family. If I remove the docker::run from the configuration and then run the Puppet agent then the catalog is applied successfully but of course the container does not run. Then adding the docker::run back to the configuration and running the agent again will run the container successfully. This is what has indicated to me that there is a dependancy issue that I have not been able to resolve.
Since neither chaining arrows nor metaparameters seem to have an affect in this specific situation I have had to resolve this issue using a guard clause that checks if Docker is installed using the facts provided by the puppetlabs/docker module
if $facts['docker_client_version'] != undef {
docker::run { $container_name:
ensure => $ensure,
image => $image,
env => $container_environment,
net => 'host',
restart => 'unless-stopped',
}
}
This is not ideal as it has the effect of requiring the catalog to be applied twice before the container will be run but it does solve the problem.
Declared classes are not automatically ordered relative to each other or relative to resources declared in the same context. There are good reasons for that, but they are tangential to the question.
However, you can use the same techniques to impose relative ordering on classes that you can use for resources: the before, require, notify, and subscribe metaparameters, and the chaining arrows. For your particular case, you might also be able to use the require function (even if you also use a resource like class declaration of class docker, but in the case, the resource-like declaration must precede the require).
For example, this is a pretty good way to make sure that class docker is applied before every docker::run instance, wherever declared:
class { 'docker':
version => $version,
}
-> Docker::Run<||>
That does, however, have the side effect of realizing any virtual docker::run instances you might have declared. If that's a problem then go with one of the other alternatives.

How to provide a startup service file in Puppet

We have RedHat 7.2 Linux OS and use puppet to perform our tasks. I am using puppet to install some software, which has worked fine and now the final step is to create an OS level service. In earlier versions of RHEL, we used chkconfig but that has been replaced with systemctl. Of course, the recommended way of performing this task is using a service. Since this is a custom software, I have my own startup script that I usually copy over to /etc/init.d, run chkconfig and then startup the service. How do I perform these tasks via Puppet for RedHat 7.2 OS ? I only want to create the service (not start it up or anything). This way, when the server reboots, the service will startup the app.
EDIT :
#redstonemercury for RHEL 7 I would think the following would be required. But your suggestion definitely helps as I was thinking along the same lines.
https://serverfault.com/questions/814611/puppet-generated-systemd-unit-files
file { '/lib/systemd/system/myservice.service':
mode => '0644',
owner => 'root',
group => 'root',
content => template('modulename/myservice.systemd.erb'),
}~>
exec { 'myservice-systemd-reload':
command => 'systemctl daemon-reload',
path => [ '/usr/bin', '/bin', '/usr/sbin' ],
refreshonly => true,
}
In puppet, use a package resource to install the package (assuming it's in repos that you're declaring already), then use a file resource to declare the /etc/init.d file, and put require => Package[<package_resource_name>] as a parameter in the file declaration to ensure the custom file gets created after the package has installed (so doesn't potentially get overwritten by the package's /etc/init.d file). E.g.:
package { 'mypackage':
ensure => present,
}
file { '/etc/init.d/mypackage':
ensure => present,
content => template('mypackage/myinitd'),
require => Package['mypackage'],
}
This is if you want to use a template. For a file, instead of content use source: source => puppet://modules/mypackage/myinitd

Puppet noop When Executable does not exist yet

The following is a simplified manifest I am running:
package {'ruby2.4':
ensure => installed
}
exec { "gem2.4_install_bundler":
command => "/usr/bin/gem2.4 install bundler",
require => Package['ruby2.4']
}
Puppet apply runs this manifest correctly i.e
installs ruby2.4 package (which includes gem2.4)
Installs bundler using gem2.4
However, puppet apply --noop FAILS because puppet cannot find the executable '/usr/bin/gem2.4' because ruby2.4 is not installed with --noop.
My question is if there is a standard way to test a scenario like this with puppet apply --noop? To validate that my puppet manifest is executing correctly?
It occurs to me that I may have to parse the output and validate the order of the executions. If this is the case, is there a standard way/tool for this?
A last resort is a very basic check that the puppet at least runs, which can be determined with the --detailed-exitcodes option. (a code different to 1).
Thank you in advance
rspec-puppet is the standard tool for that level of verification. It can build a catalog from the manifest (e.g. for a class, defined type, or host) and then you can write tests to verify the contents.
In your case you could verify that the package resource exists, that the exec resource exists, and verify the ordering between them. This would be just as effective as running the agent with --noop mode and parsing the output - but easier and cheaper to run.
rspec-puppet works best with modules, so assuming you follow the setup for your module from the website (adding rspec-puppet to your Gemfile, running rspec-puppet-init), and let's say this is in a class called ruby24, a simple spec in spec/classes/ruby24_spec.rb would be:
require 'spec_helper'
describe 'ruby24' do
it { is_expected.to compile.with_all_deps }
it { is_expected.to contain_package('ruby2.4').with_ensure('installed') }
it { is_expected.to contain_exec('gem2.4_install_bundler').with_command('/usr/bin/gem2.4 install bundler') }
it { is_expected.to contain_exec('gem2.4_install_bundler').that_requires('Package[ruby2.4]') }
end

Installing pear package using puppet

I am trying to install the pear package "HTTP_Request2" using puppet. The target OS is RHEL 7.
I have come across 2 puppet modules that I believe should be able to do this
https://forge.puppetlabs.com/rafaelfc/pear
https://forge.puppetlabs.com/example42/php
Ideally I would like to use the refaelfc module as it seems more specialized for this task. The problem I have with this is that to gain internet access on the target server you need to go through a proxy, I can't seem to find anything in this module to set this. Without this set puppet just times out when trying to install the module.
Using option 2 I add the following to my manifest:
include php
php::pear::config { http_proxy: value => "http://xx.xx.xx.xx:xxxx" }
php::pear::module { 'HTTP_Request2':
repository => 'pear.phpunit.de',
alldeps => 'true',
require => Php::Pear::Config['http_proxy'],
}
When doing this I get the error:
Error: Execution of '/bin/yum -d 0 -e 0 -y list php-pear-HTTP_Request2' returned 1: Error: No matching Packages to list
It doesn't seem right to be that it should be calling on yum? How can I get puppet to install this pear package?
Managed to get it working using a combination of the example42/php module and a custom script:
include php
php::pear::config { http_proxy:
value => "http://xx.xx.xx.xx:xxxx",
}
exec { 'HTTP_Request2':
command => '/usr/bin/pear install HTTP_Request2',
unless => '/usr/bin/pear info HTTP_Request2',
require => Php::Pear::Config['http_proxy'],
}

Augeas support on my Vagrant machine?

I'm trying to getting support for augeas on my Vagrant machine.
I tried to install some package with these directives:
package { "augeas-tools": ensure => installed }
package { "libaugeas-dev": ensure => installed }
package { "libaugeas-ruby": ensure => installed }
When i try to use augeas on my manifests, after the vm boot i receive this error:
err: Could not find a suitable provider for augeas
I'm using the precise32 official box with Vagrant 1.0.3.
Vagrant 1.0.3 has ruby 1.8.7 and puppet 2.7.14
$ ruby -v
$ ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-linux]
$ puppet help
$ Puppet v2.7.14
This is my little manifest with php class, included after apache class, mysql and other classes tested separately.
All things works correctly excepting for the augeas command.
class php {
exec { "apt-update":
command => "/usr/bin/apt-get update",
refreshonly => true;
}
package { "augeas-tools": ensure => installed }
package { "libaugeas-dev": ensure => installed }
package { "libaugeas-ruby": ensure => installed }
package { "php5": ensure => installed }
package { "php5-cli": ensure => installed }
package { "php5-xdebug": ensure => installed }
package { "php5-curl": ensure => installed }
package { "php5-intl": ensure => installed }
package { "php5-imap": ensure => installed }
package { "php5-mcrypt": ensure => installed }
package { "php5-imagick": ensure => installed }
package { "php5-sqlite": ensure => installed }
package { "php5-gd": ensure => installed }
package { "php-apc": ensure => installed }
package {
"libapache2-mod-php5" :
ensure => installed,
require => Package["php5"]
}
augeas { "php-cli":
require => [
Package["php5"],
Package["augeas-tools"],
Package["libaugeas-dev"],
Package["libaugeas-ruby"],
],
context => "/etc/php5/cli/php.ini",
changes => [
"set date.timezone Europe/Rome",
"set short_open_tag Off",
];
}
augeas { "php-apache":
require => [
Package["php5"],
Package["augeas-tools"],
Package["libaugeas-dev"],
Package["libaugeas-ruby"],
],
context => "/etc/php5/apache2/php.ini",
changes => [
"set date.timezone Europe/Rome",
"set short_open_tag Off",
];
}
}
After installation of packages, logging in the vagrant machine with "vagrant ssh", i launch:
vagrant#precise32:~$ ruby -raugeas -e "puts Augeas.open"
#<Augeas:0xb77a3598>
Thanks in advance!
I added the following to my Vagrantfile and it augeas started working.
Before declaring puppet provisioner add the following line, if on ubuntu:
config.vm.provision :shell, :inline => "sudo apt-get update && sudo apt-get install puppet -y"
This will update your apt packages and then update puppet client whose latest version already has a fix.
It turns out that this wasn't fixed in bug #6907 that I referenced in my other answer. That fix only worked for Puppet providers that depended on commands that were then supplied during the run.
For the Augeas provider, it uses an internal Puppet called "features" to check if the ruby-augeas library is available or not. Features are only being checked once and the results cached, so even after installing the library, this meant the feature still evaluated to false.
I filed this upstream as bug #14822 and have sent a pull request with a fix. Testing with the patch, I now get this successful run:
notice: /Stage[main]//Package[ruby-augeas]/ensure: created
notice: /Stage[main]//Augeas[test]/returns: executed successfully
I'm not familiar with Vagrant, but I think you'll need to find a workaround to install the libaugeas-ruby package before the Puppet run in the meantime.
On Puppet 2.7.14, this should work as the dependencies for providers will only be evaluated when they're needed - i.e. when Puppet needs to run those Augeas resources.
Without the full Puppet log file to confirm, I suspect that it's because you're missing explicit dependencies between the Augeas package(s) and the Augeas resources that need them. Remember, listing the resources in the manifest in that order doesn't mean Puppet executes it that way.
You could either add requires parameters to every Augeas resource:
augeas { "php-cli":
require => [ Package["php5"], Package["libaugeas-ruby"] ],
# ...
}
Or use the chaining syntax to automatically make every Augeas resource depend on a package. Add this on a line inside the class, but not inside any resource:
Package["libaugeas-ruby"] -> Augeas <| |>
After reading answer from #m0dlx I inspect /home/vagrant/postinstall.sh file and found that Vagrant uses own copy of Ruby:
# The base path to the Ruby used for the Chef and Puppet gems ruby_home="/opt/vagrant_ruby"
After that I find file augeas.rb at /opt/vagrant_ruby/lib/ruby/gems/1.8/gems/puppet-2.7.19/lib/puppet/provider/augeas/augeas.rb and edit it, by changing line
confine :true => Puppet.features.augeas?
to
confine :true => :augeas
(Shortly speaking I partially apply patch from #m0dlx.)
After that this error is gone.

Resources