How to execute a target action before this one when this one triggers

98 views
Skip to first unread message

Bret Wortman

unread,
Jan 27, 2014, 2:50:24 PM1/27/14
to puppet...@googlegroups.com
I'm looking at the case of distributing /etc/cups/printers.conf.

When this file changes, I'd like to distribute it. But before placing the new file, cupsd needs to be shut down, and restarted again afterwards. This can be done easily enough using an Exec to shut it down and the existing Service entry to trigger a restart, but how can I tell my manifest that when the File triggers, first do the Exec, then the File, then the Service?

Is there a way to chain these together to produce the effect I'm looking for? To stop a service before deploying a new config file, then start it up again?

file { '/etc/cups/printers.conf':
    require => Package['cups'],
    source => 'puppet:///modules/cups/printers.conf',
    notify => Service['cupsd'],
    :
}

service { 'cupsd':
    ensure => running,
    enable => true,
}

exec { 'stop-cupsd':
    command => 'service cupsd stop',
    :
}

jcbollinger

unread,
Jan 28, 2014, 9:10:06 AM1/28/14
to puppet...@googlegroups.com


On Monday, January 27, 2014 1:50:24 PM UTC-6, Bret Wortman wrote:
I'm looking at the case of distributing /etc/cups/printers.conf.

When this file changes, I'd like to distribute it. But before placing the new file, cupsd needs to be shut down, and restarted again afterwards. This can be done easily enough using an Exec to shut it down and the existing Service entry to trigger a restart, but how can I tell my manifest that when the File triggers, first do the Exec, then the File, then the Service?

Is there a way to chain these together to produce the effect I'm looking for? To stop a service before deploying a new config file, then start it up again?


No easy way to do that, no.  On the other hand, it's probably not necessary.  You should be able to put the new config in place first, then trigger a service restart; it is a rare service that cannot be updated in that way.  Moreover, it makes the length of the service interruption slightly shorter, and it does not leave the service down if the file update fails for some reason.  Your manifest will also be simpler.  Just remove the Exec, and you should be good to go.


John

Bret Wortman

unread,
Jan 28, 2014, 9:34:12 AM1/28/14
to puppet...@googlegroups.com
Yeah, that's what we do in 99% of our manifests, but for cups, the config file contains this disclaimer right at the top:

# Printer configuration file for CUPS v1.5.4
# Written by cupsd on 2014-01-24 09:47
# DO NOT EDIT THIS FILE WHEN CUPSD IS RUNNING

And the manpages say the same thing: "...If you do choose to edit this file manually, you will need to stop the scheduler first, make your changes, and then start the scheduler to make them active."

It just seems like a useful thing, if not exactly common, to say "hey, do this thing before me, but only when I trigger." Declaring a predicate step, as it were.


--
You received this message because you are subscribed to a topic in the Google Groups "Puppet Users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/puppet-users/MGIkGFm1LYY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to puppet-users...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-users/fddddd78-f5e9-4fa2-82c4-37a5eaef0d8a%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Jose Luis Ledesma

unread,
Jan 28, 2014, 12:42:18 PM1/28/14
to puppet...@googlegroups.com
I dont know if this may work, just an idea ( but I think it is really an ugly idea)

Setup a dummy file for printers.conf anywhere I the filesystem

Make it to notify the exec stop cups( setting refreshonly=true) chain it with the right file printers.conf and notify from here the cups service.

Regards,

jcbollinger

unread,
Jan 28, 2014, 3:48:01 PM1/28/14
to puppet...@googlegroups.com

In the same vein, but perhaps slightly nicer, might be to sync the conf file just once, with a secondary file on the target node as Jose suggests, and to use that to signal an Exec that does a stop; file copy; start sequence on the service.  Something like this:

file { '/var/lib/site/cupsd.conf':
  ensure => 'file',
  content => template('cupsd.conf.erb')
}

exec { 'install-cupsd.conf-update':
  command => '/sbin/service cups stop && cp /var/lib/site/cupsd.conf /etc/cups.d/ && /sbin/service/cups start',
  provider => 'shell',
  refreshonly => true,
  subscribe => File['/var/lib/site/cupsd.conf'']
}

The main problem with an approach along these lines is that it fails to notice local changes to the real target config file.  It will modify the true target file only when the computed content differs from the content of the proxy file (i.e. /var/lib/site/cupsd.conf), but if local changes are applied to /etc/cups.d/cupsd.conf then Puppet will not recognize the need to sync.

I suppose one could use an 'onlyif' parameter to the Exec instead of 'refreshonly' / 'subscribe', and roll in a comparison of the proxy config file with the real target.  That all will get very complicated very quickly, though, if you want to manage anything about the real config file other than its contents.

The idea of declaring a predicate as a separate resource that will be synced only if some other resource needs to be synced is not implemented in Puppet, and does not fit well into the Puppet model.  Each declared resource determines for itself whether it is in sync, and what to do if it isn't.

Therefore, the most correct way to do this in Puppet is to create a custom type / provider to handle it.  The first hint was that we were whipping up solutions around Execs.  The fact that the service and the config file need to be managed in concert, the latter around the former, is the real indication that the whole thing ought to be modeled as a single resource, however.


John

Bret Wortman

unread,
Jan 31, 2014, 12:49:28 PM1/31/14
to puppet...@googlegroups.com
Here's what I did, and it seems to be working for now:

        service { 'cups':
                ensure => running,
                enable => true,
        }

        file { '/etc/cups/printers.conf':
                require => Package['cups'],
                ensure => present,
                source => 'puppet:///modules/printers/printers.conf',
                mode => '0600',
                owner => 'root',
                group => 'lp',
                notify => Exec['replace-restart-cups'],
        }

        exec { 'replace-restart-cups':
                path => '/usr/bin:/usr/sbin',
                command => 'cp /etc/cups/printers.conf /tmp && systemctl stop cups.service && cp /tmp/printers.conf /etc/cups/',
                logoutput => true,
                refreshonly => true,
                notify => Service['cups'],
        }

Basically, the file replacement (in its original location) triggers a copy-shutdown-recopy sequence which is an unabashed hack. But since cups doesn't clobber the replaced file until the service shuts down, and we make a copy before we shut it down, this works. So far. But it's definitely kludgey.


Bret
Reply all
Reply to author
Forward
0 new messages