Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make puppet stop a service before replacing a file?

Tags:

puppet

I'm trying to avoid a race condition when replacing the software behind a puppet Service.

To do that, puppet needs to stop the service, replace the executable, then start the service. Is there a way to talk puppet into doing that? Its preferred way of doing things seems to be to replace the executable, then check the status and start the service again if necessary.

(This example is contrived. The real race condition is nowhere near this simple...)

Here's the puppet manifest I'm using to simulate this problem:

$O = '1'
$I = '2'

exec { hi :
        command => '/bin/echo "$(/bin/date +%s) puppet says hello" >> /tmp/freebird.log' ,
        }

file { exe :
        name => "/tmp/freebird" ,
        ensure => present ,
        owner => "root" ,
        group => "root" ,
        mode => "0555" ,
        source => "/root/test-v$I" ,
        }

file { init :
        name => '/etc/init.d/freebird' ,
        ensure => present,
        owner => "root",
        group => "root",
        mode => "0555",
        source => "/root/test.init" ,
        }

service { freebird :
        ensure => running,
        enable => true,
        hasrestart => true,
        hasstatus => true,
        require => [ File[init], File[exe] ],
        }

Here's the test-v1 file. The test-v2 file is the same but with v=2.

#!/bin/bash
v=1

while true
do
        echo "$(date +%s) $v" >> /tmp/freebird-v.log
        sleep 1
done

And the init.d script:

#!/bin/bash
#
# /etc/rc.d/init.d/freebird

# chkconfig: 2345 90 10
# description:       freebird
# Provides:          freebird
# Required-Start:    $syslog $remote_fs
# Should-Start:
# Required-Stop:     $syslog $remote_fs
# Should-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description:  freebird 


# Source function library.
. /etc/rc.d/init.d/functions

xme=freebird
export PATH=/sbin:/bin:/usr/sbin:/usr/bin

function L () {
        echo "$(date +%s) $*" 1>&2
        echo "$(date +%s) $*" >> /tmp/$xme.log
        }


case "$1" in
        (start) L $1 $xme
                ( /tmp/$xme &)
                ;;
        (stop) L $1 $xme
                fuser -k /tmp/$xme
                ;;
        (status) L $1 $xme
                /sbin/fuser /tmp/$xme >/dev/null 2>&1
                ;;
        (restart) L $1 $xme
                $0 stop
                $0 start
                ;;
        (*)
                echo "Usage: $xme {start|stop|status|restart]"
                exit 1
                ;;
esac
like image 628
bugi Avatar asked Feb 18 '12 00:02

bugi


People also ask

How do I stop puppet service?

To stop the daemon, use the process ID from the agent's pidfile : sudo kill $(puppet config print pidfile --section agent) (Optional) Configure the run interval. The Puppet agent service defaults to doing a configuration run every 30 minutes.

How do you restart a puppet service?

The actual command used to restart the service depends on the platform and can be configured: If you set hasrestart to true, Puppet will use the init script's restart command. You can provide an explicit command for restarting with the restart attribute.

Which attribute is used to start a service at boot time?

enable. (Property: This attribute represents concrete state on the target system.) Whether a service should be enabled to start at boot. This property behaves differently depending on the platform; wherever possible, it relies on local tools to enable or disable a given service.

What is a puppet agent?

Puppet agent is the application that manages the configurations on your nodes. It requires a Puppet primary server to fetch configuration catalogs from. Depending on your infrastructure and needs, you can manage systems with Puppet agent as a service, as a cron job, or on demand.


2 Answers

I'm trying to avoid a race condition when replacing the software behind a puppet Service.

To do that, puppet needs to stop the service, replace the executable, then start the service. Is there a way to talk puppet into doing that? Its preferred way of doing things seems to be to replace the executable, then check the status and start the service again if necessary.

So the problem with what Puppet is currently doing is that it should always be restarting the service after replacing certain files?

If so, you should use a notify/subscribe relationship to always trigger the service restart after files have been replaced. Taking your example service, we can add subscriptions onto the files that make it up (in the same way you might with a config) and this will trigger a restart if either of them changes.

service { freebird :
        ensure => running,
        enable => true,
        hasrestart => true,
        hasstatus => true,
        require => [ File[init], File[exe] ],
        subscribe => [ File[init], File[exe] ],
        }

The other way of doing it is to use your OS package management, which Puppet has good support for. You would then trigger the restart (or a stop/start in pre/post install) from the package scripts, leaving Puppet to ensure the service is configured and running. Have a look at Jordan Sissel's fpm project for a tool to easily build many package formats.

like image 60
Dominic Cleal Avatar answered Oct 21 '22 10:10

Dominic Cleal


I will rephrase the question for clarity and search, then provide a solution.

I would suggest however, that if you have the option to do so, use the pre-install feature of your native packaging system.

Q: How to emulate rpm's pre-install script via puppet. One use case is to stop the puppet service before installing the executable, then start it up again after replacing the file. This is in contrast to puppet's normal ordering of replace the file, then restart the service.

Fortunately, my use case already requires the symlink mess. If yours doesn't, please post your solution.

To run the test comprised of the files below, I edit $tversion in test.pp then paste this into my terminal:

fuser /tmp/freebird-v.log /tmp/freebird
: > /tmp/freebird.log
echo ==== >> /tmp/freebird.log ; puppet apply --verbose --onetime --no-daemonize test.pp 2>&1 | tee ~/D ; cat /tmp/freebird.log
ps auxww|grep freebird
fuser /tmp/freebird-v.log /tmp/freebird

File test.pp:

$tversion = '1'

exec { hi :
        command => '/bin/echo "$(/bin/date +%s) puppet says hello" >> /tmp/freebird.log' ,
        }

file { exe :
        name => "/tmp/freebird-v$tversion" ,
        ensure => present ,
        owner => "root" ,
        group => "root" ,
        mode => "0555" ,
        content => template("/root/test-template") ,
        }

file { exe_ln :
        name => "/tmp/freebird" ,
        ensure => link ,
        owner => "root" ,
        group => "root" ,
        mode => "0555" ,
        target => "/tmp/freebird-v$tversion" ,
        }

file { init :
        name => '/etc/init.d/freebird' ,
        ensure => present,
        owner => "root",
        group => "root",
        mode => "0555",
        source => "/root/test.init" ,
        }

exec { freebird_stop_if_incoherent :
        command => '/sbin/service freebird stop' ,
        refreshonly => false , # required for when entering at exe_ln
        onlyif => "/sbin/service freebird status && ! test /tmp/freebird -ef '/tmp/freebird-v$tversion'" , # short-circuits the refreshonly for most cases
        }

service { freebird :
        ensure => running,
        enable => true,
        hasrestart => true,
        hasstatus => true,
        }

File[exe_ln]            <~ Exec[freebird_stop_if_incoherent]
Service[freebird]       <- File[exe_ln]

File test-template:

#!/bin/bash
v=<%= tversion %>

while true
do
        echo "$(date +%s) $v" >> /tmp/freebird-v.log
        sleep 1
done

File test.init:

#!/bin/bash
#
# /etc/rc.d/init.d/freebird

# chkconfig: 2345 90 10
# description:       freebird
# Provides:          freebird
# Required-Start:    $syslog $remote_fs
# Should-Start:
# Required-Stop:     $syslog $remote_fs
# Should-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description:  freebird 


# Source function library.
. /etc/rc.d/init.d/functions

xme=freebird
export PATH=/sbin:/bin:/usr/sbin:/usr/bin

function L () {
        local pid=$$
        local ppid=$(ps l $pid |awk '{print $4}' |tail -1)
        local extra="-- $(ps $ppid|tail -1|sed 's,^[^/]*/,/, ; s,/[0-9][^/]*/,/,')"
        echo "$(date +%s) $pid $ppid $* $extra" 1>&2
        echo "$(date +%s) $pid $ppid $* $extra" >>/tmp/$xme.log 2>&1
        }


case "$1" in
        (start) L $1 $xme
                fuser /tmp/$xme >/dev/null 2>&1 || ( /tmp/$xme &)
                ;;
        (stop) L $1 $xme
                fuser /tmp/$xme 2>&1
                fuser -k /tmp/$xme 1>&2 ||true
                fuser /tmp/$xme 2>&1
                true
                ;;
        (status) L $1 $xme
                /sbin/fuser /tmp/$xme >/dev/null 2>&1
                ;;
        (restart) L $1 $xme
                fuser -k /tmp/$xme 1>&2 ||true
                ( /tmp/$xme &)
                ;;
        (*)
                echo "Usage: $xme {start|stop|status|restart]"
                exit 1
                ;;
esac
like image 28
bugi Avatar answered Oct 21 '22 09:10

bugi