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