Puppet generates errors every time a module changes

puppetpuppetmaster

I have a trivial puppet 2.7.18 installation as follows:

=== manifests/site.pp ===
node build-1 {
  include mod1
  include mod2
  include mod3
}

=== modules/mod1/manifests/init.pp ===
import "*"

=== modules/mod1/manifests/mod1.pp ===
class mod1 {
  file { "/tmp/mod1.file": ensure => present }
}

=== modules/mod2/manifests/init.pp ===
import "*"

=== modules/mod2/manifests/mod2.pp ===
class mod2 {
  file { "/tmp/mod2.file": ensure => present }
}

=== modules/mod3/manifests/init.pp ===
import "*"

=== modules/mod3/manifests/mod3.pp ===
class mod3 {
  file { "/tmp/mod3.file": ensure => present }
}

When I try to run puppet agent on the build-1 host, I get the following message:

: 0 build-1; sudo puppet agent --noop --test
err: Could not retrieve catalog from remote server: Error 400 on SERVER: Could not find class mod1 for build-1 at /etc/puppet/manifests/site.pp:2 on node build-1
warning: Not using cache on failed catalog
err: Could not retrieve catalog; skipping run

If I run it again, I get the same message, but for class mod2. Running it a third time gives me the message for class mod3. Finally, running it a fourth time works. But, if I do nothing more than touch one of the module files (e.g., mod1.pp), then I have to go through the whole thing again; running puppet agent until every module recompiles properly. This is obviously not sustainable.

Some other things I noticed:

  1. This seems similar to https://stackoverflow.com/questions/15289988/puppet-could-not-find-class-hiccups-often-once-after-manifest-module-change, except that one appears to be for version 3.0 and the bug ticket it refers to specifically says that it doesn't affect 2.7. In any case, switching to a passenger setup didn't help.
  2. If I change the module layout to just put the whole module definition into init.pp instead of importing the real module definition, I don't get the problem. But that's not going to scale well for complicated modules.

Best Answer

Is there any reason you can't have the definition of the main class in the init.pp file?

My understanding of the class discovery is rudimentary, but the file modules/mod1/mod1.pp would be automatically checked for the mod1::mod1 class, not the mod1 class.

As far as I'm aware, the mod1 class should always be defined in init.pp, but that doesn't mean your entire module's config has to be in there - sub-classes are helpful!

The module design that I believe is recommended these days is along these lines:

mod1/init.pp:

class mod1 {
  include mod1::install
  include mod1::config
  include mod1::service
}

mod1/install.pp:

class mod1::install {
  package { "somepackage":
    ensure => installed,
  }
}

mod1/config.pp:

class mod1::config {
  file { "/etc/someapp.conf":
    content => "foo",
    require => Class["mod1::install"],
    notify  => Class["mod1::service"],
  }
}

mod1/service.pp:

class mod1::service {
  service { "someapp":
    ensure => running,
  }
}

Edit: further, you shouldn't be using import inside of modules:

http://docs.puppetlabs.com/puppet/2.7/reference/lang_import.html

The behavior of import within autoloaded manifests is undefined, and may vary randomly between minor versions of Puppet. You should never place import statements in modules; they should only exist in site.pp.