class Transaction::AdditionalResourceGenerator

Adds additional resources to the catalog and relationship graph that are generated by existing resources. There are two ways that a resource can generate additional resources, either through the generate method or the eval_generate method.

@api private

Attributes

relationship_graph[W]
resources_failed_to_generate[R]
boolean

true if any resource has attempted and failed to generate resources

Public Class Methods

new(catalog, relationship_graph, prioritizer) click to toggle source
   # File lib/puppet/transaction/additional_resource_generator.rb
13 def initialize(catalog, relationship_graph, prioritizer)
14   @catalog = catalog
15   @relationship_graph = relationship_graph
16   @prioritizer = prioritizer
17   @resources_failed_to_generate = false
18 end

Public Instance Methods

eval_generate(resource) click to toggle source
   # File lib/puppet/transaction/additional_resource_generator.rb
53 def eval_generate(resource)
54   return false unless resource.respond_to?(:eval_generate)
55   raise Puppet::DevError, _("Depthfirst resources are not supported by eval_generate") if resource.depthfirst?
56   begin
57     generated = replace_duplicates_with_catalog_resources(resource.eval_generate)
58     return false if generated.empty?
59   rescue => detail
60     @resources_failed_to_generate = true
61     #TRANSLATORS eval_generate is a method name and should be left untranslated
62     resource.log_exception(detail, _("Failed to generate additional resources using 'eval_generate': %{detail}") % { detail: detail })
63     return false
64   end
65   add_resources(generated, resource)
66 
67   made = Hash[generated.map(&:name).zip(generated)]
68   contain_generated_resources_in(resource, made)
69   connect_resources_to_ancestors(resource, made)
70 
71   true
72 end
generate_additional_resources(resource) click to toggle source
   # File lib/puppet/transaction/additional_resource_generator.rb
20 def generate_additional_resources(resource)
21   return unless resource.respond_to?(:generate)
22   begin
23     generated = resource.generate
24   rescue => detail
25     @resources_failed_to_generate = true
26     resource.log_exception(detail, _("Failed to generate additional resources using 'generate': %{detail}") % { detail: detail })
27   end
28   return unless generated
29   generated = [generated] unless generated.is_a?(Array)
30   generated.collect! do |res|
31     @catalog.resource(res.ref) || res
32   end
33   unless resource.depthfirst?
34     # This is reversed because PUP-1963 changed how generated
35     # resources were added to the catalog. It exists for backwards
36     # compatibility only, and can probably be removed in Puppet 5
37     #
38     # Previously, resources were given sequential priorities in the
39     # relationship graph. Post-1963, resources are added to the
40     # catalog one by one adjacent to the parent resource. This
41     # causes an implicit reversal of their application order from
42     # the old code. The reverse makes it all work like it did.
43     generated.reverse!
44   end
45   generated.each do |res|
46     add_resource(res, resource)
47 
48     add_generated_directed_dependency(resource, res)
49     generate_additional_resources(res)
50   end
51 end

Private Instance Methods

add_conditional_directed_dependency(parent, child, label=nil) click to toggle source

Copy an important relationships from the parent to the newly-generated child resource.

    # File lib/puppet/transaction/additional_resource_generator.rb
212 def add_conditional_directed_dependency(parent, child, label=nil)
213   @relationship_graph.add_vertex(child)
214   edge = parent.depthfirst? ? [child, parent] : [parent, child]
215   if @relationship_graph.edge?(*edge.reverse)
216     parent.debug "Skipping automatic relationship to #{child}"
217   else
218     @relationship_graph.add_relationship(edge[0],edge[1],label)
219   end
220 end
add_generated_directed_dependency(parent, child, label=nil) click to toggle source

add correct edge for depth- or breadth- first traversal of generated resource. Skip generating the edge if there is already some sort of edge between the two resources.

    # File lib/puppet/transaction/additional_resource_generator.rb
156 def add_generated_directed_dependency(parent, child, label=nil)
157   if parent.depthfirst?
158     source = child
159     target = parent
160   else
161     source = parent
162     target = child
163   end
164 
165   # For each potential relationship metaparam, check if parent or
166   # child references the other. If there are none, we should add our
167   # edge.
168   edge_exists = Puppet::Type.relationship_params.any? { |param|
169     param_sym = param.name.to_sym
170 
171     if parent[param_sym]
172       parent_contains = parent[param_sym].any? { |res|
173         res.ref == child.ref
174       }
175     else
176       parent_contains = false
177     end
178 
179     if child[param_sym]
180       child_contains = child[param_sym].any? { |res|
181         res.ref == parent.ref
182       }
183     else
184       child_contains = false
185     end
186 
187     parent_contains || child_contains
188   }
189 
190   if not edge_exists
191     # We *cannot* use target.to_resource here!
192     #
193     # For reasons that are beyond my (and, perhaps, human)
194     # comprehension, to_resource will call retrieve. This is
195     # problematic if a generated resource needs the system to be
196     # changed by a previous resource (think a file on a path
197     # controlled by a mount resource).
198     #
199     # Instead of using to_resource, we just construct a resource as
200     # if the arguments to the Type instance had been passed to a
201     # Resource instead.
202     resource = ::Puppet::Resource.new(target.class, target.title,
203                                       :parameters => target.original_parameters)
204 
205     source[:before] ||= []
206     source[:before] << resource
207   end
208 end
add_resource(res, parent_resource, priority=nil) click to toggle source
    # File lib/puppet/transaction/additional_resource_generator.rb
133 def add_resource(res, parent_resource, priority=nil)
134   if @catalog.resource(res.ref).nil?
135     res.merge_tags_from(parent_resource)
136     if parent_resource.depthfirst?
137       @catalog.add_resource_before(parent_resource, res)
138     else
139       @catalog.add_resource_after(parent_resource, res)
140     end
141     @catalog.add_edge(@catalog.container_of(parent_resource), res) if @catalog.container_of(parent_resource)
142     if @relationship_graph && priority
143       # If we have a relationship_graph we should add the resource
144       # to it (this is an eval_generate). If we don't, then the
145       # relationship_graph has not yet been created and we can skip
146       # adding it.
147       @relationship_graph.add_vertex(res, priority)
148     end
149     res.finish
150   end
151 end
add_resources(generated, resource) click to toggle source
    # File lib/puppet/transaction/additional_resource_generator.rb
126 def add_resources(generated, resource)
127   generated.each do |res|
128     priority = @prioritizer.generate_priority_contained_in(resource, res)
129     add_resource(res, resource, priority)
130   end
131 end
connect_resources_to_ancestors(resource, made) click to toggle source
    # File lib/puppet/transaction/additional_resource_generator.rb
115 def connect_resources_to_ancestors(resource, made)
116   made.values.each do |res|
117     # Depend on the nearest ancestor we generated, falling back to the
118     # resource if we have none
119     parent_name = res.ancestors.find { |a| made[a] and made[a] != res }
120     parent = made[parent_name] || resource
121 
122     add_conditional_directed_dependency(parent, res)
123   end
124 end
contain_generated_resources_in(resource, made) click to toggle source
    # File lib/puppet/transaction/additional_resource_generator.rb
 82 def contain_generated_resources_in(resource, made)
 83   sentinel = Puppet::Type.type(:whit).new(:name => "completed_#{resource.title}", :catalog => resource.catalog)
 84   # Tag the completed whit with all of the tags of the generating resource
 85   # except the type name to allow event propogation to succeed beyond the whit
 86   # "boundary" when filtering resources with tags. We include auto-generated
 87   # tags such as the type name to support implicit filtering as well as
 88   # explicit. Note that resource#tags returns a duplicate of the resource's
 89   # tags.
 90   sentinel.merge_tags_from(resource)
 91   priority = @prioritizer.generate_priority_contained_in(resource, sentinel)
 92   @relationship_graph.add_vertex(sentinel, priority)
 93 
 94   redirect_edges_to_sentinel(resource, sentinel, made)
 95 
 96   made.values.each do |res|
 97     # This resource isn't 'completed' until each child has run
 98     add_conditional_directed_dependency(res, sentinel, Puppet::Graph::RelationshipGraph::Default_label)
 99   end
100 
101   # This edge allows the resource's events to propagate, though it isn't
102   # strictly necessary for ordering purposes
103   add_conditional_directed_dependency(resource, sentinel, Puppet::Graph::RelationshipGraph::Default_label)
104 end
redirect_edges_to_sentinel(resource, sentinel, made) click to toggle source
    # File lib/puppet/transaction/additional_resource_generator.rb
106 def redirect_edges_to_sentinel(resource, sentinel, made)
107   @relationship_graph.adjacent(resource, :direction => :out, :type => :edges).each do |e|
108     next if made[e.target.name]
109 
110     @relationship_graph.add_relationship(sentinel, e.target, e.label)
111     @relationship_graph.remove_edge! e
112   end
113 end
replace_duplicates_with_catalog_resources(generated) click to toggle source
   # File lib/puppet/transaction/additional_resource_generator.rb
76 def replace_duplicates_with_catalog_resources(generated)
77   generated.collect do |generated_resource|
78     @catalog.resource(generated_resource.ref) || generated_resource
79   end
80 end