Skip to content

Commit 72510bf

Browse files
committed
Fixing #800 by refactoring how configurations are retrieved
from the server. The real problem was getting all of the validation done before any caching, which required a good bit more refactoring than I expected. In actuality, this commit is relatively small even though it covers many files; most of the changes just make the code clearer or shorter.
1 parent dd7caa7 commit 72510bf

File tree

30 files changed

+788
-563
lines changed

30 files changed

+788
-563
lines changed

CHANGELOG

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
Fixed #800 -- invalid configurations are no longer
2+
cached. This was done partially by adding a relationship
3+
validation step once the entire configuration is created,
4+
but it also required the previously-mentioned changes
5+
to how the configuration retrieval process works.
6+
7+
Removed some functionality from the Master client,
8+
since the local functionality has been replaced
9+
with the Indirector already, and rearranging how configuration
10+
retrieval is done to fix ordering and caching bugs.
11+
112
The node scope is now above all other scopes besides
213
the 'main' scope, which should help make its variables
314
visible to other classes, assuming those classes were

lib/puppet/metatype/instances.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ def self.create(args)
9999
end
100100
end
101101

102+
# If they've specified a type and called on the base, then
103+
# delegate to the subclass.
102104
if type
103105
if typeklass = self.type(type)
104106
return typeklass.create(hash)
@@ -233,19 +235,22 @@ def self.hash2trans(hash)
233235
hash.delete :name
234236
end
235237

236-
unless title
237-
raise Puppet::Error,
238-
"You must specify a title for objects of type %s" % self.to_s
238+
if configuration = hash[:configuration]
239+
hash.delete(:configuration)
239240
end
240241

242+
raise(Puppet::Error, "You must specify a title for objects of type %s" % self.to_s) unless title
243+
241244
if hash.include? :type
242245
unless self.validattr? :type
243246
hash.delete :type
244247
end
245248
end
249+
246250
# okay, now make a transobject out of hash
247251
begin
248252
trans = Puppet::TransObject.new(title, self.name.to_s)
253+
trans.configuration = configuration if configuration
249254
hash.each { |param, value|
250255
trans[param] = value
251256
}

lib/puppet/metatype/metaparams.rb

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class Puppet::Type
9494

9595
# We've got four relationship metaparameters, so this method is used
9696
# to reduce code duplication between them.
97-
def store_relationship(param, values)
97+
def munge_relationship(param, values)
9898
# We need to support values passed in as an array or as a
9999
# resource reference.
100100
result = []
@@ -194,20 +194,24 @@ def store_relationship(param, values)
194194
unless aliases.is_a?(Array)
195195
aliases = [aliases]
196196
end
197-
@resource.info "Adding aliases %s" % aliases.collect { |a|
198-
a.inspect
199-
}.join(", ")
197+
198+
raise(ArgumentError, "Cannot add aliases without a configuration") unless @resource.configuration
199+
200+
@resource.info "Adding aliases %s" % aliases.collect { |a| a.inspect }.join(", ")
201+
200202
aliases.each do |other|
201-
if obj = @resource.class[other]
202-
unless obj == @resource
203-
self.fail(
204-
"%s can not create alias %s: object already exists" %
205-
[@resource.title, other]
206-
)
203+
if obj = @resource.configuration.resource(@resource.class.name, other)
204+
unless obj.object_id == @resource.object_id
205+
self.fail("%s can not create alias %s: object already exists" % [@resource.title, other])
207206
end
208207
next
209208
end
209+
210+
# LAK:FIXME Old-school, add the alias to the class.
210211
@resource.class.alias(other, @resource)
212+
213+
# Newschool, add it to the configuration.
214+
@resource.configuration.alias(@resource, other)
211215
end
212216
end
213217
end
@@ -247,7 +251,16 @@ def self.inherited(sub)
247251
end
248252

249253
def munge(rels)
250-
@resource.store_relationship(self.class.name, rels)
254+
@resource.munge_relationship(self.class.name, rels)
255+
end
256+
257+
def validate_relationship
258+
@value.each do |value|
259+
unless @resource.configuration.resource(*value)
260+
description = self.class.direction == :in ? "dependency" : "dependent"
261+
raise Puppet::Error, "Could not find #{description} %s[%s]" % [value[0].to_s.capitalize, value[1]]
262+
end
263+
end
251264
end
252265

253266
# Create edges from each of our relationships. :in

lib/puppet/network/client/master.rb

Lines changed: 73 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -139,63 +139,57 @@ def getconfig
139139
facts = self.class.facts
140140
end
141141

142-
if self.configuration or FileTest.exists?(self.cachefile)
143-
if self.fresh?(facts)
144-
Puppet.info "Config is up to date"
145-
if self.configuration
146-
return
147-
end
148-
if oldtext = self.retrievecache
149-
begin
150-
@configuration = YAML.load(oldtext).to_configuration
151-
rescue => detail
152-
Puppet.warning "Could not load cached configuration: %s" % detail
153-
end
154-
return
155-
end
156-
end
157-
end
158-
Puppet.debug("getting config")
142+
raise Puppet::Network::ClientError.new("Could not retrieve any facts") unless facts.length > 0
159143

160144
# Retrieve the plugins.
161-
if Puppet[:pluginsync]
162-
getplugins()
163-
end
145+
getplugins() if Puppet[:pluginsync]
164146

165-
unless facts.length > 0
166-
raise Puppet::Network::ClientError.new(
167-
"Could not retrieve any facts"
168-
)
147+
if (self.configuration or FileTest.exist?(self.cachefile)) and self.fresh?(facts)
148+
Puppet.info "Configuration is up to date"
149+
return if use_cached_config
169150
end
170151

171-
unless objects = get_actual_config(facts)
172-
@configuration = nil
152+
Puppet.debug("Retrieving configuration")
153+
154+
# If we can't retrieve the configuration, just return, which will either
155+
# fail, or use the in-memory configuration.
156+
unless yaml_objects = get_actual_config(facts)
157+
use_cached_config(true)
173158
return
174159
end
175160

176-
unless objects.is_a?(Puppet::TransBucket)
177-
raise NetworkClientError,
178-
"Invalid returned objects of type %s" % objects.class
161+
begin
162+
objects = YAML.load(yaml_objects)
163+
rescue => detail
164+
msg = "Configuration could not be translated from yaml"
165+
msg += "; using cached configuration" if use_cached_config(true)
166+
Puppet.warning msg
167+
return
179168
end
180169

181170
self.setclasses(objects.classes)
182171

183172
# Clear all existing objects, so we can recreate our stack.
184-
if self.configuration
185-
clear()
186-
end
173+
clear() if self.configuration
187174

188175
# Now convert the objects to a puppet configuration graph.
189-
@configuration = objects.to_configuration
176+
begin
177+
@configuration = objects.to_configuration
178+
rescue => detail
179+
clear()
180+
puts detail.backtrace if Puppet[:trace]
181+
msg = "Configuration could not be instantiated: %s" % detail
182+
msg += "; using cached configuration" if use_cached_config(true)
183+
Puppet.warning msg
184+
return
185+
end
190186

191-
if @configuration.nil?
192-
raise Puppet::Error, "Configuration could not be processed"
187+
if ! @configuration.from_cache
188+
self.cache(yaml_objects)
193189
end
194190

195191
# Keep the state database up to date.
196192
@configuration.host_config = true
197-
198-
return @configuration
199193
end
200194

201195
# A simple proxy method, so it's easy to test.
@@ -270,11 +264,9 @@ def run(options = {})
270264
Puppet.err "Could not retrieve configuration: %s" % detail
271265
end
272266

273-
if defined? @configuration and @configuration
267+
if self.configuration
274268
@configuration.retrieval_duration = duration
275-
unless @local
276-
Puppet.notice "Starting configuration run"
277-
end
269+
Puppet.notice "Starting configuration run" unless @local
278270
benchmark(:notice, "Finished configuration run") do
279271
@configuration.apply(options)
280272
end
@@ -500,34 +492,16 @@ def facts_changed?(facts)
500492
# Actually retrieve the configuration, either from the server or from a
501493
# local master.
502494
def get_actual_config(facts)
503-
if @local
504-
return get_local_config(facts)
505-
else
506-
begin
507-
Timeout::timeout(self.class.timeout) do
508-
return get_remote_config(facts)
509-
end
510-
rescue Timeout::Error
511-
Puppet.err "Configuration retrieval timed out"
512-
return nil
495+
begin
496+
Timeout::timeout(self.class.timeout) do
497+
return get_remote_config(facts)
513498
end
499+
rescue Timeout::Error
500+
Puppet.err "Configuration retrieval timed out"
501+
return nil
514502
end
515503
end
516504

517-
# Retrieve a configuration from a local master.
518-
def get_local_config(facts)
519-
# If we're local, we don't have to do any of the conversion
520-
# stuff.
521-
objects = @driver.getconfig(facts, "yaml")
522-
@compile_time = Time.now
523-
524-
if objects == ""
525-
raise Puppet::Error, "Could not retrieve configuration"
526-
end
527-
528-
return objects
529-
end
530-
531505
# Retrieve a config from a remote master.
532506
def get_remote_config(facts)
533507
textobjects = ""
@@ -545,45 +519,18 @@ def get_remote_config(facts)
545519
end
546520

547521
rescue => detail
548-
puts detail.backtrace
549522
Puppet.err "Could not retrieve configuration: %s" % detail
550-
551-
unless Puppet[:usecacheonfailure]
552-
@configuration = nil
553-
Puppet.warning "Not using cache on failed configuration"
554-
return
555-
end
523+
return nil
556524
end
557525
end
558526

559-
fromcache = false
560-
if textobjects == ""
561-
unless textobjects = self.retrievecache
562-
raise Puppet::Error.new(
563-
"Cannot connect to server and there is no cached configuration"
564-
)
565-
end
566-
Puppet.warning "Could not get config; using cached copy"
567-
fromcache = true
568-
else
569-
@compile_time = Time.now
570-
Puppet::Util::Storage.cache(:configuration)[:facts] = facts
571-
Puppet::Util::Storage.cache(:configuration)[:compile_time] = @compile_time
572-
end
527+
return nil if textobjects == ""
573528

574-
begin
575-
objects = YAML.load(textobjects)
576-
rescue => detail
577-
raise Puppet::Error,
578-
"Could not understand configuration: %s" %
579-
detail.to_s
580-
end
529+
@compile_time = Time.now
530+
Puppet::Util::Storage.cache(:configuration)[:facts] = facts
531+
Puppet::Util::Storage.cache(:configuration)[:compile_time] = @compile_time
581532

582-
if @cache and ! fromcache
583-
self.cache(textobjects)
584-
end
585-
586-
return objects
533+
return textobjects
587534
end
588535

589536
def lockfile
@@ -609,4 +556,32 @@ def splay
609556
Puppet.info "Sleeping for %s seconds (splay is enabled)" % time
610557
sleep(time)
611558
end
559+
560+
private
561+
562+
# Use our cached config, optionally specifying whether this is
563+
# necessary because of a failure.
564+
def use_cached_config(because_of_failure = false)
565+
return true if self.configuration
566+
567+
if because_of_failure and ! Puppet[:usecacheonfailure]
568+
@configuration = nil
569+
Puppet.warning "Not using cache on failed configuration"
570+
return false
571+
end
572+
573+
return false unless oldtext = self.retrievecache
574+
575+
begin
576+
@configuration = YAML.load(oldtext).to_configuration
577+
@configuration.from_cache = true
578+
@configuration.host_config = true
579+
rescue => detail
580+
puts detail.backtrace if Puppet[:trace]
581+
Puppet.warning "Could not load cached configuration: %s" % detail
582+
clear
583+
return false
584+
end
585+
return true
586+
end
612587
end

lib/puppet/network/handler/runner.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def run(tags = nil, ignoreschedules = false, fg = true, client = nil, clientip =
4343
end
4444

4545
if ignoreschedules
46-
msg += " without schedules"
46+
msg += " ignoring schedules"
4747
end
4848

4949
Puppet.notice msg

0 commit comments

Comments
 (0)