Jira (PUP-10118) resources type fails to purge package when packages exist whose $name != $title

7 views
Skip to first unread message

Shaun (JIRA)

unread,
Nov 1, 2019, 12:16:56 PM11/1/19
to puppe...@googlegroups.com
Shaun created an issue
 
Puppet / Bug PUP-10118
resources type fails to purge package when packages exist whose $name != $title
Issue Type: Bug Bug
Affects Versions: PUP 5.5.17
Assignee: Unassigned
Components: Types and Providers
Created: 2019/11/01 9:11 AM
Priority: Normal Normal
Reporter: Shaun

Puppet Version: 5.5.17
Puppet Server Version: n/a (fails also on puppet apply)
OS Name/Version: RHEL 7.6

Experimenting to remove unmanaged packages, I have experimented with the following stanza:

resources {'package':
  purge => true,
} 

Because it is not uncommon for various modules to define package resources with differing titles and names, such as:

package {'http-server':
  name => 'apache24',
  ensure => 'installed',
} 

Desired Behavior:
I expect the code to display a list of packages for deletion when run with the --noop flag.

Actual Behavior:
The catalog fails to compile with following error when I use my example manifest:

Error: Failed to apply catalog: Cannot alias Package[sl] to [nil, "sl", :yum]; resource ["Package", nil, "sl", :yum] already declared (file: /root/example.pp, line: 2)

Examples:

# example.pp
# This causes errors because $title != $name
package {'waste-of-time':
  name => 'sl',
  ensure => 'installed',
}
 
resources {'package':
  purge => true,
}

Run puppet apply --noop example.pp to see this error yourself. PLEASE BE CAREFUL AND ENSURE NOOP.

 

Add Comment Add Comment
 
This message was sent by Atlassian JIRA (v7.7.1#77002-sha1:e75ca93)
Atlassian logo

Josh Cooper (JIRA)

unread,
Nov 1, 2019, 7:36:03 PM11/1/19
to puppe...@googlegroups.com
Josh Cooper commented on Bug PUP-10118
 
Re: resources type fails to purge package when packages exist whose $name != $title

Yes, there is confusion about title vs resource identity (as determined by its uniqueness_keys). As a result the resources type, doesn't realize the resource is already in the catalog, and tries to add an ensure => absent resource to purge the package. But that conflicts with already present package in the catalog.

The problem starts in the resources type

  reject { |r| catalog.resource_refs.include? r.ref }.

First, resource_refs is an Array, so we're doing a linear scan of all resources in the catalog. And we repeat that for each resource_type.instances. So we've got a quadratic there.

Second, resource_refs contains strings with the type and title. But the title is taken from the manifest, so it's something like "Package[waste-of-time]". As a result, the installed package sl never matches.

Third, it's possible to call catalog.resource(r.class, r.title) and try to lookup the resource. However, the method generates an intermediate Puppet::Resource object, and loses track of other namevars (like provider for the package type). As a result the generated uniqueness key contains:

[nil, "sl", nil]

instead of

[nil, "sl", :yum]

It would be better to add a method to the catalog that checks if a resource is already present, and call that from resources. It avoids creating intermediate Puppet::Resource objects and ensures all of the uniqueness keys are present. Something like:

diff --git a/lib/puppet/resource/catalog.rb b/lib/puppet/resource/catalog.rb
index d4bde7cc78..92b11f85b2 100644
--- a/lib/puppet/resource/catalog.rb
+++ b/lib/puppet/resource/catalog.rb
@@ -403,6 +403,12 @@ class Puppet::Resource::Catalog < Puppet::Graph::SimpleGraph
     result
   end
 
+  def contains?(resource)
+    raise ArgumentError unless resource.is_a?(Puppet::Type)
+    type_name = Puppet::Resource.new(resource).type
+    @resource_table.has_key?([type_name, resource.uniqueness_key].flatten)
+  end
+
   def resource_refs
     resource_keys.collect{ |type, name| name.is_a?( String ) ? "#{type}[#{name}]" : nil}.compact
   end
diff --git a/lib/puppet/type/resources.rb b/lib/puppet/type/resources.rb
index 51c102c5f4..83ae3af465 100644
--- a/lib/puppet/type/resources.rb
+++ b/lib/puppet/type/resources.rb
@@ -115,7 +115,7 @@ Puppet::Type.newtype(:resources) do
   def generate
     return [] unless self.purge?
     resource_type.instances.
-      reject { |r| catalog.resource_refs.include? r.ref }.
+      reject { |r| catalog.contains?(r) }.
       select { |r| check(r) }.
       select { |r| r.class.validproperty?(:ensure) }.
       select { |r| able_to_ensure_absent?(r) }.

Josh Cooper (JIRA)

unread,
Nov 3, 2019, 10:19:03 PM11/3/19
to puppe...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages