Puppet: Dependency cycle

158 views
Skip to first unread message

Sergey Arlashin

unread,
Jun 18, 2014, 9:52:03 AM6/18/14
to puppet...@googlegroups.com
Hi,
I have a weird dependency cycle issue. This is just a test module which I created while trying to solve this issue:

modules/testmod/manifests/init.pp:

class testmod {
package { 'nginx': ensure => installed }
service { 'nginx':
ensure => running,
enable => true,
require => Package['nginx']
}
}

class testmod::nginxtest {
file { '/tmp/nginx.test':
ensure => present,
notify => Service['nginx'];
}
}

manifests/test-node.pp:

node 'test-node.site' {
class { 'testmod': }
class { 'testmod::nginxtest':
require => Class['testmod']
}
}

When I apply this on node test-node.site I get:

Error: Could not apply complete catalog: Found 1 dependency cycle:
==> test-node: (File[/tmp/nginx.test] => Service[nginx] => Class[Testmod] => Class[Testmod::Nginxtest] => File[/tmp/nginx.test])
==> test-node: Try the '--graph' option and opening the resulting '.dot' file in OmniGraffle or GraphViz

Could you please help me figure out why this is happening ? It seems there is nothing in test mod whih requires testmod::nginxtest .

Thanks in advance.

--
Best regards,
Sergey Arlashin

Felix Frank

unread,
Jun 18, 2014, 9:55:31 AM6/18/14
to puppet...@googlegroups.com
Hi,

On 06/18/2014 03:51 PM, Sergey Arlashin wrote:
> Could you please help me figure out why this is happening ? It seems there is nothing in test mod whih requires testmod::nginxtest .

actually yes, there is.

file { '/tmp/nginx.test':
ensure => present,
notify => Service['nginx'];
}

The 'notify' implicitly puts File['/tmp/nginx.test'] *before*
Service['nginx'].

The

class { 'testmod::nginxtest':
require => Class['testmod']
}

ends up doing the opposite - wanting the Service['nginx'] before
File['/tmp/nginx.test'].

HTH,
Felix

Sergey Arlashin

unread,
Jun 18, 2014, 10:22:12 AM6/18/14
to puppet...@googlegroups.com
Hm, ok.
But when I use 'subscribe' instead of 'notify'

class testmod {
package { 'nginx': ensure => installed }
service { 'nginx':
ensure => running,
enable => true,
require => Package['nginx'],
subscribe => File['/tmp/nginx.test']
}
}
class testmod::nginxtest {
file { '/tmp/nginx.test':
ensure => present
}
}

I get

==> test-node: Error: Could not apply complete catalog: Found 1 dependency cycle:
==> test-node: (File[/tmp/nginx.test] => Service[nginx] => Class[Testmod] => Class[Testmod::Nginxtest] => File[/tmp/nginx.test])
==> test-node: Try the '--graph' option and opening the resulting '.dot' file in OmniGraffle or GraphViz

I do need to have 2 separate modules. I need the class 'testmode' to run before class 'test mod::nginxtest'. And I need to restart service 'nginx' when I change '/tmp/nginx.test'.

So then the question is - how to do this properly? Do I need to create something like exec { 'nginx restart': … } ?
> --
> You received this message because you are subscribed to the Google Groups "Puppet Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to puppet-users...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-users/53A19A45.30903%40alumni.tu-berlin.de.
> For more options, visit https://groups.google.com/d/optout.

Christopher Wood

unread,
Jun 18, 2014, 11:01:10 AM6/18/14
to puppet...@googlegroups.com
Also see these for interesting ideas:

http://www.craigdunn.org/2012/05/239/
http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-1/
http://garylarizza.com/blog/2014/02/17/puppet-workflow-part-2/


In your place, this is how I would arrange what you want, because I often cram configs together (huzzah legacy systems):

class testmod::packages {
package { 'nginx': }
}

class testmod::services {
service { 'nginx':
ensure => running,
enable => true,
}
}

class testmod::nginxtest {
file { '/tmp/nginx.test': }
}

# you might also look into "contain" over "include"?
class testmod {
include ::testmod::packages
include ::testmod::services
Class['::testmod::packages'] ~> Class['::testmod::services']
}

# this shows how a generic testmod is all normal-ish...
class profile::testmod {
include ::testmod
}

# ...but a testmod nginx tester is a bit special
# obviously you could move includes/chaining around
class profile::testmodnginx {
include ::testmod::packages
include ::testmod::services
include ::testmod::nginxtest
Class['::testmod::packages'] ~> Class['::testmod::services']
Class['::testmod::packages'] -> Class['::testmod::nginxtest']
Class['::testmod::nginxtest'] ~> Class['::testmod::services']
}


Or you could do it in a parameterized fashion:

class testmod ( $nginxtest = false ) {

package { 'nginx': }

# from stdlib, see puppet forge
if str2bool($nginxtest) {
file { '/tmp/nginx.test':
ensure => present,
require => Package['nginx'],
notify => Service['nginx'],
}
}

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

}

Then in hiera:

testmod::nginxtest: true

And somewhere else:

include testmod

Or the declarative way, having a higher chance of hurting you later:

class { 'testmod':
nginxtest => true,
}


Or you could do it any way you wanted, really.

(Cue zillions of different posts about the right way to do this.)


On Wed, Jun 18, 2014 at 06:22:00PM +0400, Sergey Arlashin wrote:
> Hm, ok.
> But when I use 'subscribe' instead of 'notify'
>
> class testmod {
> package { 'nginx': ensure => installed }
> service { 'nginx':
> ensure => running,
> enable => true,
> require => Package['nginx'],
> subscribe => File['/tmp/nginx.test']
> }
> }
> class testmod::nginxtest {
> file { '/tmp/nginx.test':
> ensure => present
> }
> }
>
> I get
>
> ==> test-node: Error: Could not apply complete catalog: Found 1 dependency cycle:
> ==> test-node: (File[/tmp/nginx.test] => Service[nginx] => Class[Testmod] => Class[Testmod::Nginxtest] => File[/tmp/nginx.test])
> ==> test-node: Try the '--graph' option and opening the resulting '.dot' file in OmniGraffle or GraphViz
>
> I do need to have 2 separate modules. I need the class 'testmode' to run before class 'test mod::nginxtest'. And I need to restart service 'nginx' when I change '/tmp/nginx.test'.
>
> So then the question is - how to do this properly? Do I need to create something like exec { 'nginx restart': ... } ?
> To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-users/9548D943-73FE-41B1-984C-1EDC8FAD40D3%40gmail.com.

Felix Frank

unread,
Jun 18, 2014, 11:05:00 AM6/18/14
to puppet...@googlegroups.com
Wow, Christopher gave an exhaustive answer :-)

I'll try and Keep It Simple.

On 06/18/2014 04:22 PM, Sergey Arlashin wrote:
> I do need to have 2 separate modules. I need the class 'testmode' to run before class 'test mod::nginxtest'. And I need to restart service 'nginx' when I change '/tmp/nginx.test'.

Yes, you face the same issue, seeing as you just brought it in a
different notation. The same relationships are established.

> So then the question is - how to do this properly? Do I need to create something like exec { 'nginx restart': ... } ?

This is actually not possible.

If Puppet should refresh the service upon change of the config file, it
*must* evaluate the file resource before the service resource.

You need to disentangle your relationships and come up with an order in
which you want your resources applied, e.g.

Package -> File -> Service

This may indeed mean that you cannot require a whole class. But as
Christopher has advised, you mileage may increase if you structure the
classes more distinctly.

HTH,
Felix

Sergey Arlashin

unread,
Jun 18, 2014, 11:13:19 AM6/18/14
to puppet...@googlegroups.com
Christopher, Felix, I got the idea. Thank you very much!

--
Best regards,
Sergey Arlashin



> --
> You received this message because you are subscribed to the Google Groups "Puppet Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to puppet-users...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/puppet-users/53A1AA93.9000708%40alumni.tu-berlin.de.
Reply all
Reply to author
Forward
0 new messages