But really, I don't understand the complaint. Writing explicit dependencies literally what Puppet -- or any other config management system -- is about. Puppet cannot possibly know what your intent is. So you have to tell it. Just like you have to tell the OP's system. It's simple enough.
Having used Puppet for a few years, my biggest complaint is that it's not quite modular enough. For example, in one system I have a way of declaring an "app". An app may have some dependencies (Postgres, Memcached), settings, users, etc. I have two choices, I think; either have one large, monolithic define allowing me to instantiate the app from a bunch of config:
apps::app {
"someapp":
environment => 'production',
uses_unicorn => true,
db_name => 'someapp_production',
db_user => 'someapp',
memcached_servers => [...],
# etc.
...which means that every setting has to be a variable that's passed around and used by the define. Or I can split it up into pieces: apps::app {
"someapp":
environment => 'production';
}
apps::memcached_config {
"someapp":
servers => [...];
}
apps::db_config {
"someapp":
host => ...,
db_name => ...,
db_user => ...;
}
apps::unicorn_config {
"someapp":
;
}
The problem with the latter approach is that eventually, much the configuration needs to be collected into just a few actual config files. Let's imagine that the app uses a single "app.conf". Normally you would build it using a template. But with the second approach, you would have to build it out of fragments that come from multiple defines; probably you would collect them in a conf.d type folder, and then use an "exec" definition to concatenate them into "app.conf".Puppet is very modular, but it's a bit awkward when it comes to this level of modularity -- or I haven't found a better way.
Other tools just execute manifests top to bottom, which is a much less error-prone approach.
I agree that finding the right abstraction level for modules (and for reuse) is not well solved.
Then assign classes like: class{'ntp':}-> class{'ssh':}-> class{'other_stuff':}
problem solved?
But this is Prolog and ordering in Prolog is deterministic. Clause goals are proven in order. When there are multiple clauses with the same predicate, they are executed in order. If this weren't the case extra-logical facilities such as cuts wouldn't work in Prolog
In my internship last summer, I arbitrarily chose Puppet to automate the configuration of the company's developer environments. Puppet's unpredictable ordering should be a write-and-forget benefit, but in reality it's very difficult to reason about. In the end, my script imposed a top-to-bottom ordering with requires.
Also, developing configuration scripts against Vagrant is a huge pain. While `puppet parser validate` checks for syntax errors, most of the logical, dependency-based errors reveal themselves after Vagrant takes a good minute to fire up.
Anyway, hopefully my Puppet-Vagrant scripts aren't causing any maintenance nightmares...