class Puppet::Graph::RelationshipGraph
The relationship graph is the final form of a puppet catalog in which all dependency edges are explicitly in the graph. This form of the catalog is used to traverse the graph in the order in which resources are managed.
@api private
Constants
- Default_label
Impose our container information on another graph by using it to replace any container vertices X with a pair of vertices { admissible_X and completed_X } such that
0) completed_X depends on admissible_X 1) contents of X each depend on admissible_X 2) completed_X depends on each on the contents of X 3) everything which depended on X depends on completed_X 4) admissible_X depends on everything X depended on 5) the containers and their edges must be removed
Note that this requires attention to the possible case of containers which contain or depend on other containers, but has the advantage that the number of new edges created scales linearly with the number of contained vertices regardless of how containers are related; alternatives such as replacing container-edges with content-edges scale as the product of the number of external dependencies, which is to say geometrically in the case of nested / chained containers.
Attributes
Public Class Methods
Puppet::Graph::SimpleGraph::new
# File lib/puppet/graph/relationship_graph.rb 11 def initialize(prioritizer) 12 super() 13 14 @prioritizer = prioritizer 15 16 @ready = Puppet::Graph::RbTreeMap.new 17 @generated = {} 18 @done = {} 19 @blockers = {} 20 @providerless_types = [] 21 end
Public Instance Methods
Puppet::Graph::SimpleGraph#add_relationship
# File lib/puppet/graph/relationship_graph.rb 45 def add_relationship(f, t, label=nil) 46 super(f, t, label) 47 @ready.delete(@prioritizer.priority_of(t)) 48 end
Puppet::Graph::SimpleGraph#add_vertex
# File lib/puppet/graph/relationship_graph.rb 35 def add_vertex(vertex, priority = nil) 36 super(vertex) 37 38 if priority 39 @prioritizer.record_priority_for(vertex, priority) 40 else 41 @prioritizer.generate_priority_for(vertex) 42 end 43 end
# File lib/puppet/graph/relationship_graph.rb 79 def clear_blockers 80 @blockers.clear 81 end
# File lib/puppet/graph/relationship_graph.rb 83 def enqueue(*resources) 84 resources.each do |resource| 85 @ready[@prioritizer.priority_of(resource)] = resource 86 end 87 end
Enqueue the initial set of resources, those with no dependencies.
# File lib/puppet/graph/relationship_graph.rb 60 def enqueue_roots 61 vertices.each do |v| 62 @blockers[v] = direct_dependencies_of(v).length 63 enqueue(v) if @blockers[v] == 0 64 end 65 end
# File lib/puppet/graph/relationship_graph.rb 89 def finish(resource) 90 direct_dependents_of(resource).each do |v| 91 enqueue(v) if unblock(v) 92 end 93 @done[resource] = true 94 end
# File lib/puppet/graph/relationship_graph.rb 96 def next_resource 97 @ready.delete_min 98 end
# File lib/puppet/graph/relationship_graph.rb 23 def populate_from(catalog) 24 add_all_resources_as_vertices(catalog) 25 build_manual_dependencies 26 build_autorelation_dependencies(catalog) 27 28 write_graph(:relationships) if catalog.host_config? 29 30 replace_containers_with_anchors(catalog) 31 32 write_graph(:expanded_relationships) if catalog.host_config? 33 end
Puppet::Graph::SimpleGraph#remove_vertex!
# File lib/puppet/graph/relationship_graph.rb 50 def remove_vertex!(vertex) 51 super 52 @prioritizer.forget(vertex) 53 end
# File lib/puppet/graph/relationship_graph.rb 55 def resource_priority(resource) 56 @prioritizer.priority_of(resource) 57 end
# File lib/puppet/graph/relationship_graph.rb 100 def traverse(options = {}, &block) 101 continue_while = options[:while] || lambda { true } 102 pre_process = options[:pre_process] || lambda { |resource| } 103 overly_deferred_resource_handler = options[:overly_deferred_resource_handler] || lambda { |resource| } 104 canceled_resource_handler = options[:canceled_resource_handler] || lambda { |resource| } 105 teardown = options[:teardown] || lambda {} 106 graph_cycle_handler = options[:graph_cycle_handler] || lambda { [] } 107 108 cycles = report_cycles_in_graph 109 if cycles 110 graph_cycle_handler.call(cycles) 111 end 112 113 enqueue_roots 114 115 deferred_resources = [] 116 117 while continue_while.call() && (resource = next_resource) 118 if resource.suitable? 119 made_progress = true 120 121 pre_process.call(resource) 122 123 yield resource 124 125 finish(resource) 126 else 127 deferred_resources << resource 128 end 129 130 if @ready.empty? and deferred_resources.any? 131 if made_progress 132 enqueue(*deferred_resources) 133 else 134 deferred_resources.each do |res| 135 overly_deferred_resource_handler.call(res) 136 finish(res) 137 end 138 end 139 140 made_progress = false 141 deferred_resources = [] 142 end 143 end 144 145 if !continue_while.call() 146 while (resource = next_resource) 147 canceled_resource_handler.call(resource) 148 finish(resource) 149 end 150 end 151 152 teardown.call() 153 end
Decrement the blocker count for the resource by 1. If the number of blockers is unknown, count them and THEN decrement by 1.
# File lib/puppet/graph/relationship_graph.rb 69 def unblock(resource) 70 @blockers[resource] ||= direct_dependencies_of(resource).select { |r2| !@done[r2] }.length 71 if @blockers[resource] > 0 72 @blockers[resource] -= 1 73 else 74 resource.warning _("appears to have a negative number of dependencies") 75 end 76 @blockers[resource] <= 0 77 end
Private Instance Methods
# File lib/puppet/graph/relationship_graph.rb 157 def add_all_resources_as_vertices(catalog) 158 catalog.resources.each do |vertex| 159 add_vertex(vertex) 160 end 161 end
# File lib/puppet/graph/relationship_graph.rb 171 def build_autorelation_dependencies(catalog) 172 vertices.each do |vertex| 173 [:require,:subscribe].each do |rel_type| 174 vertex.send("auto#{rel_type}".to_sym, catalog).each do |edge| 175 # don't let automatic relationships conflict with manual ones. 176 next if edge?(edge.source, edge.target) 177 178 if edge?(edge.target, edge.source) 179 vertex.debug "Skipping automatic relationship with #{edge.source}" 180 else 181 vertex.debug "Adding auto#{rel_type} relationship with #{edge.source}" 182 if rel_type == :require 183 edge.event = :NONE 184 else 185 edge.callback = :refresh 186 edge.event = :ALL_EVENTS 187 end 188 add_edge(edge) 189 end 190 end 191 end 192 193 [:before,:notify].each do |rel_type| 194 vertex.send("auto#{rel_type}".to_sym, catalog).each do |edge| 195 # don't let automatic relationships conflict with manual ones. 196 next if edge?(edge.target, edge.source) 197 198 if edge?(edge.source, edge.target) 199 vertex.debug "Skipping automatic relationship with #{edge.target}" 200 else 201 vertex.debug "Adding auto#{rel_type} relationship with #{edge.target}" 202 if rel_type == :before 203 edge.event = :NONE 204 else 205 edge.callback = :refresh 206 edge.event = :ALL_EVENTS 207 end 208 add_edge(edge) 209 end 210 end 211 end 212 end 213 end
# File lib/puppet/graph/relationship_graph.rb 163 def build_manual_dependencies 164 vertices.each do |vertex| 165 vertex.builddepends.each do |edge| 166 add_edge(edge) 167 end 168 end 169 end
# File lib/puppet/graph/relationship_graph.rb 235 def replace_containers_with_anchors(catalog) 236 stage_class = Puppet::Type.type(:stage) 237 whit_class = Puppet::Type.type(:whit) 238 component_class = Puppet::Type.type(:component) 239 containers = catalog.resources.find_all { |v| (v.is_a?(component_class) or v.is_a?(stage_class)) and vertex?(v) } 240 # 241 # These two hashes comprise the aforementioned attention to the possible 242 # case of containers that contain / depend on other containers; they map 243 # containers to their sentinels but pass other vertices through. Thus we 244 # can "do the right thing" for references to other vertices that may or 245 # may not be containers. 246 # 247 admissible = Hash.new { |h,k| k } 248 completed = Hash.new { |h,k| k } 249 containers.each { |x| 250 admissible[x] = whit_class.new(:name => "admissible_#{x.ref}", :catalog => catalog) 251 completed[x] = whit_class.new(:name => "completed_#{x.ref}", :catalog => catalog) 252 253 # This copies the original container's tags over to the two anchor whits. 254 # Without this, tags are not propagated to the container's resources. 255 admissible[x].set_tags(x) 256 completed[x].set_tags(x) 257 258 priority = @prioritizer.priority_of(x) 259 add_vertex(admissible[x], priority) 260 add_vertex(completed[x], priority) 261 } 262 # 263 # Implement the six requirements listed above 264 # 265 containers.each { |x| 266 contents = catalog.adjacent(x, :direction => :out) 267 add_edge(admissible[x],completed[x]) if contents.empty? # (0) 268 contents.each { |v| 269 add_edge(admissible[x],admissible[v],Default_label) # (1) 270 add_edge(completed[v], completed[x], Default_label) # (2) 271 } 272 # (3) & (5) 273 adjacent(x,:direction => :in,:type => :edges).each { |e| 274 add_edge(completed[e.source],admissible[x],e.label) 275 remove_edge! e 276 } 277 # (4) & (5) 278 adjacent(x,:direction => :out,:type => :edges).each { |e| 279 add_edge(completed[x],admissible[e.target],e.label) 280 remove_edge! e 281 } 282 } 283 containers.each { |x| remove_vertex! x } # (5) 284 end