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
12 def initialize(catalog, relationship_graph, prioritizer)
13   @catalog = catalog
14   @relationship_graph = relationship_graph
15   @prioritizer = prioritizer
16   @resources_failed_to_generate = false
17 end

Public Instance Methods

eval_generate(resource) click to toggle source
   # File lib/puppet/transaction/additional_resource_generator.rb
52 def eval_generate(resource)
53   return false unless resource.respond_to?(:eval_generate)
54   raise Puppet::DevError, _("Depthfirst resources are not supported by eval_generate") if resource.depthfirst?
55   begin
56     generated = replace_duplicates_with_catalog_resources(resource.eval_generate)
57     return false if generated.empty?
58   rescue => detail
59     @resources_failed_to_generate = true
60     #TRANSLATORS eval_generate is a method name and should be left untranslated
61     resource.log_exception(detail, _("Failed to generate additional resources using 'eval_generate': %{detail}") % { detail: detail })
62     return false
63   end
64   add_resources(generated, resource)
65 
66   made = Hash[generated.map(&:name).zip(generated)]
67   contain_generated_resources_in(resource, made)
68   connect_resources_to_ancestors(resource, made)
69 
70   true
71 end
generate_additional_resources(resource) click to toggle source
   # File lib/puppet/transaction/additional_resource_generator.rb
19 def generate_additional_resources(resource)
20   return unless resource.respond_to?(:generate)
21   begin
22     generated = resource.generate
23   rescue => detail
24     @resources_failed_to_generate = true
25     resource.log_exception(detail, _("Failed to generate additional resources using 'generate': %{detail}") % { detail: detail })
26   end
27   return unless generated
28   generated = [generated] unless generated.is_a?(Array)
29   generated.collect! do |res|
30     @catalog.resource(res.ref) || res
31   end
32   unless resource.depthfirst?
33     # This is reversed because PUP-1963 changed how generated
34     # resources were added to the catalog. It exists for backwards
35     # compatibility only, and can probably be removed in Puppet 5
36     #
37     # Previously, resources were given sequential priorities in the
38     # relationship graph. Post-1963, resources are added to the
39     # catalog one by one adjacent to the parent resource. This
40     # causes an implicit reversal of their application order from
41     # the old code. The reverse makes it all work like it did.
42     generated.reverse!
43   end
44   generated.each do |res|
45     add_resource(res, resource)
46 
47     add_generated_directed_dependency(resource, res)
48     generate_additional_resources(res)
49   end
50 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
211 def add_conditional_directed_dependency(parent, child, label=nil)
212   @relationship_graph.add_vertex(child)
213   edge = parent.depthfirst? ? [child, parent] : [parent, child]
214   if @relationship_graph.edge?(*edge.reverse)
215     parent.debug "Skipping automatic relationship to #{child}"
216   else
217     @relationship_graph.add_relationship(edge[0],edge[1],label)
218   end
219 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
155 def add_generated_directed_dependency(parent, child, label=nil)
156   if parent.depthfirst?
157     source = child
158     target = parent
159   else
160     source = parent
161     target = child
162   end
163 
164   # For each potential relationship metaparam, check if parent or
165   # child references the other. If there are none, we should add our
166   # edge.
167   edge_exists = Puppet::Type.relationship_params.any? { |param|
168     param_sym = param.name.to_sym
169 
170     if parent[param_sym]
171       parent_contains = parent[param_sym].any? { |res|
172         res.ref == child.ref
173       }
174     else
175       parent_contains = false
176     end
177 
178     if child[param_sym]
179       child_contains = child[param_sym].any? { |res|
180         res.ref == parent.ref
181       }
182     else
183       child_contains = false
184     end
185 
186     parent_contains || child_contains
187   }
188 
189   if not edge_exists
190     # We *cannot* use target.to_resource here!
191     #
192     # For reasons that are beyond my (and, perhaps, human)
193     # comprehension, to_resource will call retrieve. This is
194     # problematic if a generated resource needs the system to be
195     # changed by a previous resource (think a file on a path
196     # controlled by a mount resource).
197     #
198     # Instead of using to_resource, we just construct a resource as
199     # if the arguments to the Type instance had been passed to a
200     # Resource instead.
201     resource = ::Puppet::Resource.new(target.class, target.title,
202                                       :parameters => target.original_parameters)
203 
204     source[:before] ||= []
205     source[:before] << resource
206   end
207 end
add_resource(res, parent_resource, priority=nil) click to toggle source
    # File lib/puppet/transaction/additional_resource_generator.rb
132 def add_resource(res, parent_resource, priority=nil)
133   if @catalog.resource(res.ref).nil?
134     res.merge_tags_from(parent_resource)
135     if parent_resource.depthfirst?
136       @catalog.add_resource_before(parent_resource, res)
137     else
138       @catalog.add_resource_after(parent_resource, res)
139     end
140     @catalog.add_edge(@catalog.container_of(parent_resource), res) if @catalog.container_of(parent_resource)
141     if @relationship_graph && priority
142       # If we have a relationship_graph we should add the resource
143       # to it (this is an eval_generate). If we don't, then the
144       # relationship_graph has not yet been created and we can skip
145       # adding it.
146       @relationship_graph.add_vertex(res, priority)
147     end
148     res.finish
149   end
150 end
add_resources(generated, resource) click to toggle source
    # File lib/puppet/transaction/additional_resource_generator.rb
125 def add_resources(generated, resource)
126   generated.each do |res|
127     priority = @prioritizer.generate_priority_contained_in(resource, res)
128     add_resource(res, resource, priority)
129   end
130 end
connect_resources_to_ancestors(resource, made) click to toggle source
    # File lib/puppet/transaction/additional_resource_generator.rb
114 def connect_resources_to_ancestors(resource, made)
115   made.values.each do |res|
116     # Depend on the nearest ancestor we generated, falling back to the
117     # resource if we have none
118     parent_name = res.ancestors.find { |a| made[a] and made[a] != res }
119     parent = made[parent_name] || resource
120 
121     add_conditional_directed_dependency(parent, res)
122   end
123 end
contain_generated_resources_in(resource, made) click to toggle source
    # File lib/puppet/transaction/additional_resource_generator.rb
 81 def contain_generated_resources_in(resource, made)
 82   sentinel = Puppet::Type.type(:whit).new(:name => "completed_#{resource.title}", :catalog => resource.catalog)
 83   # Tag the completed whit with all of the tags of the generating resource
 84   # except the type name to allow event propogation to succeed beyond the whit
 85   # "boundary" when filtering resources with tags. We include auto-generated
 86   # tags such as the type name to support implicit filtering as well as
 87   # explicit. Note that resource#tags returns a duplicate of the resource's
 88   # tags.
 89   sentinel.merge_tags_from(resource)
 90   priority = @prioritizer.generate_priority_contained_in(resource, sentinel)
 91   @relationship_graph.add_vertex(sentinel, priority)
 92 
 93   redirect_edges_to_sentinel(resource, sentinel, made)
 94 
 95   made.values.each do |res|
 96     # This resource isn't 'completed' until each child has run
 97     add_conditional_directed_dependency(res, sentinel, Puppet::Graph::RelationshipGraph::Default_label)
 98   end
 99 
100   # This edge allows the resource's events to propagate, though it isn't
101   # strictly necessary for ordering purposes
102   add_conditional_directed_dependency(resource, sentinel, Puppet::Graph::RelationshipGraph::Default_label)
103 end
redirect_edges_to_sentinel(resource, sentinel, made) click to toggle source
    # File lib/puppet/transaction/additional_resource_generator.rb
105 def redirect_edges_to_sentinel(resource, sentinel, made)
106   @relationship_graph.adjacent(resource, :direction => :out, :type => :edges).each do |e|
107     next if made[e.target.name]
108 
109     @relationship_graph.add_relationship(sentinel, e.target, e.label)
110     @relationship_graph.remove_edge! e
111   end
112 end
replace_duplicates_with_catalog_resources(generated) click to toggle source
   # File lib/puppet/transaction/additional_resource_generator.rb
75 def replace_duplicates_with_catalog_resources(generated)
76   generated.collect do |generated_resource|
77     @catalog.resource(generated_resource.ref) || generated_resource
78   end
79 end