Hi John, Thanks for the reply.
On 05/06/2013, at 12:33 AM, jcbollinger <john.bollin...@stjude.org> wrote: > On Tuesday, June 4, 2013 1:22:08 AM UTC-5, Tom Lanyon wrote: > I'm testing a 'cleanup' stage which runs after Stage[main] and removes a > bunch of package resources. > > To do this, I tried a simple check of defined(Package[<foo>]) combined with a > custom facter fact (called 'app_packages'): > > > class app::package::cleaner { > > > > define check_and_remove { > > if !defined(Package[$title]) { > > package { $title: > > ensure => absent > > } > > } > > } > > > > $apps = split($::app_packages, ',') > > check_and_remove { $apps: } > > > > } > > > > node 'foo' { > > class { 'app::package::cleaner': stage => 'cleanup' } > > > } > > Unfortunately, this results in a dependency cycle. It appears that putting > the Package[$title] resource reference in defined() actually invokes an > implicit dependency between my cleanup helper resource in the cleanup stage > and the original Package resource in the main stage. > > > Augeas[redacted] => Service[iptables] => Class[Iptables] => Stage[main] => > > Stage[cleanup] => Class[App::Package::Cleaner] => > > App::Package::Cleaner::Check_and_remove[package-434] => > > Package[package-434] => Exec[app-graceful-restart] => Class[App] => > > Stage[main] > >> Does it do that when Package[package-434] is already declared elsewhere, or >> only when it is not? Sorry, I should have been clearer that this occurs when Package[package-434] IS declared elsewhere. "!defined(Package[package-434])" therefore is false, so just by referencing the existing declaration within the defined() call it seems to incite an implicit dependency. >> Is this implicit dependency expected behaviour or am I doing something >> Bad(tm)? > > Both. > > Supposing that the target package is not declared elsewhere (so that the > !defined() condition is true) the definition will declare the package itself > to ensure it absent, and in that case you would expect a relationship between > the defined-type instance and the resource declared by it. If elsewhere you > have specific references to that package, applicable resource parameter > defaults, or collectors that will match that package, then you can get > relationships with it that are not evident from the defined type body. > > On the other hand, defined() is evil. Do not use it. Ever. I had this discussion with someone on #puppet IRC earlier and they ended up with "Oh, in your case, defined() is probably actually what you want." > I usually attribute its malignancy to the parse-order dependency it > inherently creates -- which is indeed a serious problem -- but in this case I > think trying to use it to approach your problem it has also obfuscated your > manifests enough to confuse you about the scope and nature of some of your > other declarations. > > Instead of using defined(), you can apply logic farther upstream to make the > correct declaration in the first (one) place or to apply resource parameter > overrides to the correct resources. Alternatively, you can simply determine > by other means what packages need to be ensured absent, such as by filtering > a list of possible packages against a list of packages that are supposed to > be installed. Some of those options may still susceptible to the problem you > observed, however, if relevant relationships spring from declarations > elsewhere, as I described they may do. I've tried this other ways, but here's an example of why farther upstream logic doesn't work: define myapp ($requested_package){ package { $requested_package: ensure => present } define package_cleanup { $installed_package = $title if $installed_package != $requested_package { package { $installed_package: ensure => purged } } } # assuming a facter fact named 'installed_packages' package_cleanup { split($::installed_packages, ','): } } # now in the case of: # $::installed_packages = 'one,two,three' # with: myapp { 'oneA': requested_package => 'one' } myapp { 'twoA': requested_package => 'two' } myapp { 'oneB': requested_package => 'one' } # we'd end up with package conflicts because # Myapp[oneA] will define Package[one] (present) # then define Package[two], Package[three] (absent), # and Myapp[twoA] will try and define Package[two] # (present) and fail with a non-uniqueness error. I don't see how this is doable without defined() or some other check of the catalog to see what packages are "needed" elsewhere. Do you have any suggestions? > For the record, however, no order-of-application relationship should be > implied by the reference itself. Therefore, when the referenced Package is > declared elsewhere (so that the !defined() condition is false), there should > be a relationship between App::Package::Cleaner::Check_and_remove[foo] and > Package[foo] only if that relationship is declared somewhere else. I'd hoped that using a Stage to run after everything else would sort this all out, but I'm now not sure this is correct and also see your earlier post about never using stages [with this and "defined() is evil", which pieces of Puppet /can/ we use safely?]. Thanks, Tom -- 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+unsubscr...@googlegroups.com. To post to this group, send email to puppet-users@googlegroups.com. Visit this group at http://groups.google.com/group/puppet-users?hl=en. For more options, visit https://groups.google.com/groups/opt_out.