Including a definition by variable - puppet

I'm working on putting together some puppet scripts - I've got a list of services (probably about 20 or so) that need to be deployed in very similar fashions.
Stop existing service
Get package out of nexus
Unzip it into directory
Set configuration variables
Start service
The problem is #4. Each service has a slightly different configuration. I'd like to use augeas to set the configurations for each service, and it seemed to make sense to make a definition for each service's config, and then that definition loaded in the main service definition.
So, I've got something like the following:
definition service ($serviceName) {
//stopping, wget, unzip
config[downcase($serviceName)] { "${servicename}_config":
serviceName => $serviceName
}
//start
}
Then, I've got (for example) in the config module's manifest folder "foo.pp"
definition config::foo {
//set some stuff
}
This isn't working due to various rules that I'm sure I've broken but are unaware of. Is there a 'standard' way of doing what I'm trying to do?

You could try the code below. You can put all these in a define type with variables for service name myserv. I would suggest of using templates to set the configuration rather than augeas... easier to control.
exec { "stop_myserv" :
command => "service stop myserv",
path => "/path/to/command/service",
refreshonly => true,
}
exec { "get_pkg_from_nexus" :
command => "/command/to/do/the/above && unzip to/dirctory",
path => "/path/to/command",
require => Exec["stop_myserv"],
}
file { "set_configuration" :
path => "/etc/somewhere/file",
content => template("modulename/file.erb"),
mode => "999",
group => "jiut",
owner => "muit",
require => Exec["get_pkg_from_nexus"],
}
service { "myserv" :
ensure => running,
subscribe => File["set_configuration"],
}

Related

Verifying if package is installed before downloading it in PUPPET

That is my manifest code which download, install and remove installer on a host.
class googlechrome_2 {
package { 'GoogleChrome':
ensure => installed,
source => 'C:\Soft\ChromeSetup.msi',
install_options => ['/qn'],
require => File['GoogleChromeMsi'],
}
file { 'GoogleChromeMsi':
ensure => file,
path => 'C:\Soft\ChromeSetup.msi',
source => 'puppet:///files/production/ChromeSetup.msi',
}
exec { 'msi_removing':
command => 'C:\Windows\System32\cmd.exe /c del C:\Soft\ChromeSetup.msi',
}
}
In this case my windows host always download chromesetup.msi regardless if google chrome already installed or not. How can I realize kind of "if condition" here to avoid downloading msi package each time in case if this package already installed?
In this case my windows host always download chromesetup.msi regardless if google chrome already installed or not.
Yes. Having the File resource in the node's catalog specifies that the file should be managed. Therefore, if it is not initially in the target state then Puppet will attempt to fix that.
By managing the file present but also including an Exec that removes the file, you ensure that the target node cannot achieve a stable state.
How can I realize kind of "if condition" here to avoid downloading msi package each time in case if this package already installed?
Simplest option: don't remove the installer.
Variation: Put the installer in an accessible network folder, so that you not only don't remove it, but you also don't install it.
If you really want a conditional: then it should be based on a custom fact that reports on the installation status of the package in question. You then use a Puppet if statement to control the contents of the node's catalog appropriately. Something along these lines, for example:
package { 'GoogleChrome':
ensure => 'installed',
source => 'C:\Soft\ChromeSetup.msi',
install_options => ['/qn'],
# relationhip with File['GoogleChromeMsi'] now declared on the other end
}
if $facts['chrome_is_installed'] {
file { 'GoogleChromeMsi':
ensure => 'absent',
path => 'C:\Soft\ChromeSetup.msi',
}
} else {
file { 'GoogleChromeMsi':
ensure => 'file',
path => 'C:\Soft\ChromeSetup.msi',
source => 'puppet:///files/production/ChromeSetup.msi',
before => Package['GoogleChrome'],
}
exec { 'msi_removing':
command => 'C:\Windows\System32\cmd.exe /c del C:\Soft\ChromeSetup.msi',
require => Package['GoogleChrome'],
}
}
I've solved this by using network shared folder in the package source:
class googlechrome_smb {
package { 'Google Chrome':
ensure => installed,
source => '\\\xxx.xxx.xxx.xxx\winfiles\ChromeSetup.msi',
install_options => ['/qn'],
}
}

Puppet validate_cmd alternatives in Puppet 2.7

My puppet version is 2.7.25. What I would like to do is create a users home directory if it does not already exist and only if the user resides in LDAP.
I was hoping to use the exit status from this command to test upon.
/usr/bin/getent passwd username
Here is what I thought the puppet code would look like if I was running the correct version of puppet.
define ldap-users::virtual ($gid,$gname) {
$home_root = "/export/home"
file {
"${home_root}/${title}":
ensure => directory,
owner => $title,
group => $gid,
mode => 0700,
validate_cmd => "/usr/bin/getent passwd ${title}",
}
}
Using Felix's answer from validate_cmd in Puppet: supporting older versions, if you have puppetlabs-stdlib installed, you should be able to write some code that can do that for you. Something like this (might need modifying):
file {"${home_root}/${title}":
ensure => directory,
owner => $title,
group => $gid,
mode => '0700',
}
if versioncmp($puppetversion, '3.5') >= 0 {
File["${home_root}/${title}"] { validate_cmd => "/usr/bin/getent passwd ${title} #%"
}
else {
validate_cmd('/dev/null', '/usr/bin/getent passwd ${title} #%', "getent could not find the user specified ${title}")
}
This allows you to write backwards compatible code, that will still work when you upgrade beyond 3.5 (Which I would recommend, Puppet 2.7 has been EOL since October 1st 2013!)
Caveats
The validate_cmd is primarily used to point to a created file to run the command (such as visudo to check a sudoers file). So we can just make the validate_cmd run the getent command, and add a hash to comment out the actual file path.
Bear in mind this is not the standard usecase for the command, and will throw an error if the command fails, you might get some unexpected behaviour.
If you want a better workflow, a custom fact to check if the user exists before trying to create the folder might be a better idea:
require 'pathname'
export_home_dirs = Pathname.glob('/export/home/**')
export_home_dirs.each do |export_home|
Facter.add("export_home_user_#{export_home}") { setcode { "/export/home/#{export_home}" } }
end
Then gate your logic behind that custom fact.

Custom fact should run after a package is installed

I have a small custom fact in a my php module
Facter.add('php_extension_version') do
setcode do
Facter::Core::Execution.exec("php -i | awk '/^PHP Extension =>/ { print $4}'") || nil
end
end
This obviously requires the php binary to be installed. However, I noticed that all facts are run once before applying the catalog, so this fact is invalid before php installed.
Is there any way of gathering the information after the module is installed? Is there perhaps another way of exposing this information except facter?
Update
I'm using the two facts to determine which of multiple .so files is the right one to install:
if $php_zts_enabled {
$so_name = "newrelic-$php_extension_version.so"
} else {
$so_name = "newrelic-$php_extension_version-zts.so"
}
file {"/usr/lib64/php5/extensions/newrelic.so":
source => "file:///opt/newrelic-php5-$version-linux/agent/x64/$so_name",
owner => root,
group => root,
mode => 0644,
notify => Service['apache'],
require => Exec["extract-php-agent-$version"]
}
The files that are located in the agent/x64 directory can be
newrelic-20060613.so newrelic-20090626-zts.so newrelic-20121212.so newrelic-20131226-zts.so
newrelic-20060613-zts.so newrelic-20100525.so newrelic-20121212-zts.so
newrelic-20090626.so newrelic-20100525-zts.so newrelic-20131226.so
You essentially have only two opportunities to execute code on the node:
As part of a Facter fact. As you are aware, this happens before puppet applies a catalog, so any facts dependent on the results of the puppet run will not be useful until the next run.
As part of a custom provider. You can create a custom type and provider for installing the extensions that checks the node state before deciding what to do. Providers execute on the node, and as long as you know the overall provider lifecycle you can make this happen after the PHP install. However, this is incredibly complex compared to normal puppet modules.
Outside of those options, the normal way of doing this would be to enforce the version and configuration of php within your own manifests, and then pass that information to here. You should already know the version of PHP and its extensions based on what packages you have installed.
I would modify the fact so that it's present only when the binary is present (hence it won't be present at the very first run).
Facter.add('php_extension_version') do
setcode do
if system("which php > /dev/null 2>&1")
Facter::Core::Execution.exec("php -i | awk '/^PHP Extension =>/ { print $4}'") || nil
end
end
end
and then in your manifest you'd wrap the original code in the if
if $php_extension_version {
if $php_zts_enabled {
$so_name = "newrelic-$php_extension_version.so"
} else {
$so_name = "newrelic-$php_extension_version-zts.so"
}
file {"/usr/lib64/php5/extensions/newrelic.so":
source => "file:///opt/newrelic-php5-$version-linux/agent/x64/$so_name",
owner => root,
group => root,
mode => 0644,
notify => Service['apache'],
require => Exec["extract-php-agent-$version"]
}
}

execute puppet class if file is modified by some package

I'm using puppet to deploy standardized ubuntu installs as well as configuration files.
I'm facing a problem where installing a certain package (through a dependency), will overwrite a critical config file. Is there a way to monitor whether this file changes (get's overwritten by some package) and restore it's original content?
what would be the best way of achieving this?
This is the class which takes care of configuring /etc/nsswitch.conf:
class nsswitchconfig {
# roll out nsswitch
class { 'nsswitch':
passwd => ['compat'],
group => ['compat'],
hosts => ['files'],
automount => ['files'],
}
notify { "hate #8040": message => "work around bug #8040" }
}
this is the class which overwrites /etc/nsswitch.conf
class desktop {
include nsswitchconfig
$package_name = ["ubuntu-desktop" ]
package { $package_name:
ensure => latest,
}
}
If the nsswitch class configures the file, all you need is to make sure it runs after the class that overrides it.
So in your case:
class { 'nsswitch':
passwd => ['compat'],
group => ['compat'],
hosts => ['files'],
automount => ['files'],
require => Class['desktop'],
}
should do the trick. (note the require part)

Is this the correct way to change a config file using puppet?

I have a rails app and I'd like to change the ./config/environment/production.rb file to have a different config based on what I want that server to do.
So, I'm going into the .rb file from the .pp file and changing some strings then restarting the service. This just seems really poor form to me. Is there a better way to do this? I've been asked to deliver 1 RPM and change the config via puppet, so...
class Cloud-widget($MServer, $GoogleEarthServer, $CSever) {
package { "Cloud-widget":
ensure => installed
}
service { "Cloud-widget":
ensure => running,
}
<%
file_names = ['./config/environment/production.rb']
file_names.each do |file_name|
puts text.gsub(/.*config.mserver(.*)/, "config.mserver_root = \"#{$Merver}\"")
puts text.gsub(/.*config.google_earth_url(.*)/, "config.google_earth_url( = \"#{$GoogleEarthServer}\"")
puts text.gsub(/.*config.cserver_base_url(.*)/, "config.cserver_base_url = \"#{$CServer}\"")
end
File.open(file_name, "w") {|file| file.puts output_of_gsub}
%>
service { Cloud-widget:
ensure => running,
subscribe => File["./config/environment/production.rb"],
}
}
No, that is not a good way to achieve what you need.
You could look at templates and generate the config files that way. That way, you can use variables in the config file.
If you need create conf from pattern you should use INI-file module from Puppetlabs
ini_setting { "sample setting":
path => '/tmp/foo.ini',
section => 'foo',
setting => 'foosetting',
value => 'FOO!',
ensure => present,
}
install this module from puppet:
puppet module install cprice404-inifile

Resources