Skip to content

Commit 9aaed38

Browse files
author
Branan Riley
committed
(PUP-1963) allow dependencies on/for generated resources
Previously, generated resources could not have edge metaprams or participate in autorequire, since they were created AFTER those transformations had occured on the catalog. This changes resource generation to occur before those transformations. Because of this, the containment edges for generated resources now end up in the final relationship graph. This caused issues with purged resources, since the purge check now sees that a whit depends on the purged resource. This was resolved by excluding whits from the purge safety check. Since whits can only be generated by containment edges, and generated resources previously could not have containment edges, this is not a breaking change. In order to maintain manifest ordering, it is necessary to add the generated resources into the catalog adjacent to the generating resource. This is achieved with new helpers on the catalog class to handle ordered insertions. Existing spec tests handle verifying manifest ordering works correctly for generated resources, and integration tests for things like resource purging handle reasonably complex generation cases. New tests have been added only for the new edges that are being generated after this commit.
1 parent f9b1d96 commit 9aaed38

File tree

6 files changed

+187
-24
lines changed

6 files changed

+187
-24
lines changed

Diff for: lib/puppet/resource/catalog.rb

+23-4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,25 @@ def title_key_for_ref( ref )
7373
return a
7474
end
7575

76+
def add_resource_before(other, *resources)
77+
resources.each do |resource|
78+
other_title_key = title_key_for_ref(other.ref)
79+
idx = @resources.index(other_title_key)
80+
raise ArgumentError, "Cannot add resource #{resource.ref} before #{other.ref} because #{other.ref} is not yet in the catalog" if idx.nil?
81+
add_one_resource(resource, idx)
82+
end
83+
end
84+
85+
def add_resource_after(other, *resources)
86+
resources.each do |resource|
87+
other_title_key = title_key_for_ref(other.ref)
88+
idx = @resources.index(other_title_key)
89+
raise ArgumentError, "Cannot add resource #{resource.ref} after #{other.ref} because #{other.ref} is not yet in the catalog" if idx.nil?
90+
add_one_resource(resource, idx+1)
91+
end
92+
end
93+
94+
7695
def add_resource(*resources)
7796
resources.each do |resource|
7897
add_one_resource(resource)
@@ -86,23 +105,23 @@ def container_of(resource)
86105
adjacent(resource, :direction => :in)[0]
87106
end
88107

89-
def add_one_resource(resource)
108+
def add_one_resource(resource, idx=-1)
90109
title_key = title_key_for_ref(resource.ref)
91110
if @resource_table[title_key]
92111
fail_on_duplicate_type_and_title(resource, title_key)
93112
end
94113

95-
add_resource_to_table(resource, title_key)
114+
add_resource_to_table(resource, title_key, idx)
96115
create_resource_aliases(resource)
97116

98117
resource.catalog = self if resource.respond_to?(:catalog=)
99118
add_resource_to_graph(resource)
100119
end
101120
private :add_one_resource
102121

103-
def add_resource_to_table(resource, title_key)
122+
def add_resource_to_table(resource, title_key, idx)
104123
@resource_table[title_key] = resource
105-
@resources << title_key
124+
@resources.insert(idx, title_key)
106125
end
107126
private :add_resource_to_table
108127

Diff for: lib/puppet/transaction.rb

+4-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def perform_pre_run_checks
7979
# necessary events.
8080
def evaluate(&block)
8181
block ||= method(:eval_resource)
82-
generator = AdditionalResourceGenerator.new(@catalog, relationship_graph, @prioritizer)
82+
generator = AdditionalResourceGenerator.new(@catalog, nil, @prioritizer)
8383
@catalog.vertices.each { |resource| generator.generate_additional_resources(resource) }
8484

8585
perform_pre_run_checks
@@ -135,6 +135,9 @@ def evaluate(&block)
135135
end
136136
end
137137

138+
# Generate the relationship graph, set up our generator to use it
139+
# for eval_generate, then kick off our traversal.
140+
generator.relationship_graph = relationship_graph
138141
relationship_graph.traverse(:while => continue_while,
139142
:pre_process => pre_process,
140143
:overly_deferred_resource_handler => overly_deferred_resource_handler,

Diff for: lib/puppet/transaction/additional_resource_generator.rb

+71-8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#
66
# @api private
77
class Puppet::Transaction::AdditionalResourceGenerator
8+
attr_writer :relationship_graph
9+
810
def initialize(catalog, relationship_graph, prioritizer)
911
@catalog = catalog
1012
@relationship_graph = relationship_graph
@@ -22,11 +24,19 @@ def generate_additional_resources(resource)
2224
generated = [generated] unless generated.is_a?(Array)
2325
generated.collect do |res|
2426
@catalog.resource(res.ref) || res
25-
end.each do |res|
26-
priority = @prioritizer.generate_priority_contained_in(resource, res)
27-
add_resource(res, resource, priority)
28-
29-
add_conditional_directed_dependency(resource, res)
27+
end.reverse_each do |res|
28+
# This is reversed becuase PUP-1963 changed how generated
29+
# resources were added to the catalog. It exists for backwards
30+
# compatibility only, and can probably be removed in Puppet 5
31+
#
32+
# Previously, resources were given sequential priorities in the
33+
# relationship graph. Post-1963, resources are added to the
34+
# catalog one by one adjacent to the parent resource. This
35+
# causes an implicit reversal of their application order from
36+
# the old code. The reverse makes it all work like it did.
37+
add_resource(res, resource)
38+
39+
add_generated_directed_dependency(resource, res)
3040
generate_additional_resources(res)
3141
end
3242
end
@@ -102,16 +112,69 @@ def add_resources(generated, resource)
102112
end
103113
end
104114

105-
def add_resource(res, parent_resource, priority)
115+
def add_resource(res, parent_resource, priority=nil)
106116
if @catalog.resource(res.ref).nil?
107117
res.tag(*parent_resource.tags)
108-
@catalog.add_resource(res)
109-
@relationship_graph.add_vertex(res, priority)
118+
if parent_resource.depthfirst?
119+
@catalog.add_resource_before(parent_resource, res)
120+
else
121+
@catalog.add_resource_after(parent_resource, res)
122+
end
110123
@catalog.add_edge(@catalog.container_of(parent_resource), res)
124+
if @relationship_graph && priority
125+
# If we have a relationship_graph we should add the resource
126+
# to it (this is an eval_generate). If we don't, then the
127+
# relationship_graph has not yet been created and we can skip
128+
# adding it.
129+
@relationship_graph.add_vertex(res, priority)
130+
end
111131
res.finish
112132
end
113133
end
114134

135+
# add correct edge for depth- or breadth- first traversal of
136+
# generated resource. Skip generating the edge if there is already
137+
# some sort of edge between the two resources.
138+
def add_generated_directed_dependency(parent, child, label=nil)
139+
if parent.depthfirst?
140+
source = child
141+
target = parent.to_resource
142+
else
143+
source = parent
144+
target = child.to_resource
145+
end
146+
147+
# For each potential relationship metaparam, check if parent or
148+
# child references the other. If there are none, we should add our
149+
# edge.
150+
edge_exists = Puppet::Type.relationship_params.any? { |param|
151+
param_sym = param.name.to_sym
152+
153+
if parent[param_sym]
154+
parent_contains = parent[param_sym].any? { |res|
155+
res.ref == child.ref
156+
}
157+
else
158+
parent_contains = false
159+
end
160+
161+
if child[param_sym]
162+
child_contains = child[param_sym].any? { |res|
163+
res.ref == parent.ref
164+
}
165+
else
166+
child_contains = false
167+
end
168+
169+
parent_contains || child_contains
170+
}
171+
172+
if not edge_exists
173+
source[:before] ||= []
174+
source[:before] << target
175+
end
176+
end
177+
115178
# Copy an important relationships from the parent to the newly-generated
116179
# child resource.
117180
def add_conditional_directed_dependency(parent, child, label=nil)

Diff for: lib/puppet/transaction/resource_harness.rb

+9-4
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,15 @@ def perform_changes(resource, context)
9999
def allow_changes?(resource)
100100
if resource.purging? and resource.deleting? and deps = relationship_graph.dependents(resource) \
101101
and ! deps.empty? and deps.detect { |d| ! d.deleting? }
102-
deplabel = deps.collect { |r| r.ref }.join(",")
103-
plurality = deps.length > 1 ? "":"s"
104-
resource.warning "#{deplabel} still depend#{plurality} on me -- not purging"
105-
false
102+
real_deps = deps.reject { |r| r.class.name == :whit }
103+
if real_deps.empty?
104+
true
105+
else
106+
deplabel = real_deps.collect { |r| r.ref }.join(",")
107+
plurality = real_deps.length > 1 ? "":"s"
108+
resource.warning "#{deplabel} still depend#{plurality} on me -- not purging"
109+
false
110+
end
106111
else
107112
true
108113
end

Diff for: spec/lib/matchers/include_in_order.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
end
1212

1313
def failure_message
14-
"expected #{@actual.inspect} to include#{expected_to_sentence} in order"
14+
"expected #{@actual.inspect} to include#{expected} in order"
1515
end
1616

1717
def failure_message_when_negated
18-
"expected #{@actual.inspect} not to include#{expected_to_sentence} in order"
18+
"expected #{@actual.inspect} not to include#{expected} in order"
1919
end
2020
end

Diff for: spec/unit/transaction/additional_resource_generator_spec.rb

+78-5
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,48 @@ def eval_code
5252
end
5353
end
5454

55+
Puppet::Type.newtype(:autorequire) do
56+
newparam(:name) do
57+
isnamevar
58+
end
59+
60+
autorequire(:notify) do
61+
self[:name]
62+
end
63+
end
64+
65+
Puppet::Type.newtype(:gen_auto) do
66+
newparam(:name) do
67+
isnamevar
68+
end
69+
70+
newparam(:eval_after) do
71+
end
72+
73+
def generate()
74+
[ Puppet::Type.type(:autorequire).new(:name => self[:eval_after]) ]
75+
end
76+
end
77+
78+
Puppet::Type.newtype(:empty) do
79+
newparam(:name) do
80+
isnamevar
81+
end
82+
end
83+
84+
Puppet::Type.newtype(:gen_empty) do
85+
newparam(:name) do
86+
isnamevar
87+
end
88+
89+
newparam(:eval_after) do
90+
end
91+
92+
def generate()
93+
[ Puppet::Type.type(:empty).new(:name => self[:eval_after], :require => "Notify[#{self[:eval_after]}]") ]
94+
end
95+
end
96+
5597
context "when applying eval_generate" do
5698
it "should add the generated resources to the catalog" do
5799
catalog = compile_to_ral(<<-MANIFEST)
@@ -368,13 +410,44 @@ class container {
368410
"Notify[after]"))
369411
end
370412

371-
def relationships_after_generating(manifest, resource_to_generate)
372-
catalog = compile_to_ral(manifest)
373-
relationship_graph = relationship_graph_for(catalog)
413+
it "runs autorequire on the generated resource" do
414+
graph = relationships_after_generating(<<-MANIFEST, 'Gen_auto[thing]')
415+
gen_auto { thing:
416+
eval_after => hello,
417+
}
374418
375-
generate_resources_in(catalog, relationship_graph, resource_to_generate)
419+
notify { hello: }
420+
notify { goodbye: }
421+
MANIFEST
376422

377-
relationship_graph
423+
expect(order_resources_traversed_in(graph)).to(
424+
include_in_order("Gen_auto[thing]",
425+
"Notify[hello]",
426+
"Autorequire[hello]",
427+
"Notify[goodbye]"))
428+
end
429+
430+
it "evaluates metaparameters on the generated resource" do
431+
graph = relationships_after_generating(<<-MANIFEST, 'Gen_empty[thing]')
432+
gen_empty { thing:
433+
eval_after => hello,
434+
}
435+
436+
notify { hello: }
437+
notify { goodbye: }
438+
MANIFEST
439+
440+
expect(order_resources_traversed_in(graph)).to(
441+
include_in_order("Gen_empty[thing]",
442+
"Notify[hello]",
443+
"Empty[hello]",
444+
"Notify[goodbye]"))
445+
end
446+
447+
def relationships_after_generating(manifest, resource_to_generate)
448+
catalog = compile_to_ral(manifest)
449+
generate_resources_in(catalog, nil, resource_to_generate)
450+
relationship_graph_for(catalog)
378451
end
379452

380453
def generate_resources_in(catalog, relationship_graph, resource_to_generate)

0 commit comments

Comments
 (0)