7

I need to perform some action (configure something) after stopping the tomcat service. Once the configuration is complete, I need to ensure that the tomcat service is up and running again. I have written following puppet code for the same:

Service {'tomcat': ensure => stopped }
->
class {'config':}
->
Service {'tomcat': ensure => running }

On puppet apply, it is complaining that

'Error: Duplicate declaration: Service[tomcat] is already declared in file'

How to fix this problem. What is the recipe in puppet to stop a service, perform some action and then bring back the service again?

ham-sandwich
  • 3,975
  • 10
  • 34
  • 46
Anand Patel
  • 6,031
  • 11
  • 48
  • 67

2 Answers2

7

In puppet, you can't declare same service again. that's the error you have.

With puppet, you needn't care of tomcat stop/start processes. It takes care the final status (called "idemotency"). After you define the relationship between package, config files and services, it will do all jobs for you. For example, you need to understand below processes in puppet and the differences between -> and ~>.

Package['tomcat'] -> File['server.xml']  ~> Service['tomcat']

In your case, you apply the change in tomcat config file, and puppet will restart the tomcat services automatically.

For your reference, here is the copy-paste from Introduction to Puppet blog to explain what's the meaning of idempotency:

One big difference between Puppet and most other tools is that Puppet configurations are idempotent, meaning they can safely be run multiple times. Once you develop your configuration, your machines will apply the configuration often — by default, every 30 minutes — and Puppet will only make any changes to the system if the system state does not match the configured state.

Update 2016:

Here another official Puppet blog post on idempotency: https://puppet.com/blog/idempotence-not-just-a-big-and-scary-word

Gabriel Petrovay
  • 20,476
  • 22
  • 97
  • 168
BMW
  • 42,880
  • 12
  • 99
  • 116
  • 1
    I would add that sometimes, you do need to be able to start and stop a service. Ordering can't solve everything. For instance, changing the UID of a user that has a running process (ie: tomcat), requires the process to be stopped first. – chizou Jan 14 '16 at 23:53
  • for your example, you need define another ordering, if UID of a user changed and need a service restart. – BMW Jan 15 '16 at 22:49
  • This post explains the concept but does not actually provide a solution. That's why I propose to accept Felix Frank's answer as "correct". – Christian Mar 10 '17 at 10:02
2

This is not directly possible with Puppet, as @BMW concludes correctly. There are some more points to note, however.

There is some promising work in progress that will add limited support for transitional state declaration. However, this will not (in its current alpha state at least) allow you to enter such a state in preparation for and during application of a whole class.

A common workaround for this kind of issue is to manage the entity in question with two or more resources. The exec type is a good catch all solution because it can manage virtually anything. The obvious drawback is that the exec will have to be tailored to your agents (what do you know - there's a point to Puppet's type system after all ;-). Assuming that the manifest will be for one platform only, this is simple:

exec {
    'stop-tomcat':
        command => 'service tomcat stop',
        onlyif  => 'service tomcat status',
        before  => [
            Class['config'],
            Service['config'],
        ],
}

Ordering the exec before Service['config'] is redundant (because the service requires the class), but it is good practice to express that the service resource should have the final say.

Felix Frank
  • 8,125
  • 1
  • 23
  • 30