diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/puppet/indirector/facts/memory.rb | 9 | ||||
-rw-r--r-- | lib/puppet/indirector/indirection.rb | 8 | ||||
-rw-r--r-- | lib/puppet/metatype/instances.rb | 11 | ||||
-rw-r--r-- | lib/puppet/metatype/metaparams.rb | 35 | ||||
-rw-r--r-- | lib/puppet/network/client/master.rb | 171 | ||||
-rwxr-xr-x | lib/puppet/network/handler/runner.rb | 2 | ||||
-rw-r--r-- | lib/puppet/network/http.rb | 13 | ||||
-rw-r--r-- | lib/puppet/network/http/handler.rb | 109 | ||||
-rw-r--r-- | lib/puppet/network/http/mongrel.rb | 54 | ||||
-rw-r--r-- | lib/puppet/network/http/mongrel/rest.rb | 37 | ||||
-rw-r--r-- | lib/puppet/network/http/mongrel/xmlrpc.rb | 4 | ||||
-rw-r--r-- | lib/puppet/network/http/webrick.rb | 51 | ||||
-rw-r--r-- | lib/puppet/network/http/webrick/rest.rb | 41 | ||||
-rw-r--r-- | lib/puppet/network/http/webrick/xmlrpc.rb | 4 | ||||
-rw-r--r-- | lib/puppet/network/server.rb | 105 | ||||
-rw-r--r-- | lib/puppet/node/configuration.rb | 20 | ||||
-rw-r--r-- | lib/puppet/parser/compile.rb | 30 | ||||
-rw-r--r-- | lib/puppet/transaction.rb | 2 | ||||
-rw-r--r-- | lib/puppet/transportable.rb | 93 | ||||
-rw-r--r-- | lib/puppet/type.rb | 21 | ||||
-rw-r--r-- | lib/puppet/type/component.rb | 4 | ||||
-rwxr-xr-x | lib/puppet/type/pfile/group.rb | 4 |
22 files changed, 569 insertions, 259 deletions
diff --git a/lib/puppet/indirector/facts/memory.rb b/lib/puppet/indirector/facts/memory.rb new file mode 100644 index 000000000..3c10d5964 --- /dev/null +++ b/lib/puppet/indirector/facts/memory.rb @@ -0,0 +1,9 @@ +require 'puppet/node/facts' +require 'puppet/indirector/memory' + +class Puppet::Node::Facts::Memory < Puppet::Indirector::Memory + desc "Keep track of facts in memory but nowhere else. This is used for + one-time compiles, such as what the stand-alone ``puppet`` does. + To use this terminus, you must load it with the data you want it + to contain." +end diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 9a9b1c0bf..816b4ffc5 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -13,6 +13,14 @@ class Puppet::Indirector::Indirection @@indirections.find { |i| i.name == name } end + # Find an indirected model by name. This is provided so that Terminus classes + # can specifically hook up with the indirections they are associated with. + def self.model(name) + match = @@indirections.find { |i| i.name == name } + return nil unless match + match.model + end + attr_accessor :name, :model # Create and return our cache terminus. diff --git a/lib/puppet/metatype/instances.rb b/lib/puppet/metatype/instances.rb index 2f9918067..8cc648e8f 100644 --- a/lib/puppet/metatype/instances.rb +++ b/lib/puppet/metatype/instances.rb @@ -99,6 +99,8 @@ class Puppet::Type end end + # If they've specified a type and called on the base, then + # delegate to the subclass. if type if typeklass = self.type(type) return typeklass.create(hash) @@ -233,19 +235,22 @@ class Puppet::Type hash.delete :name end - unless title - raise Puppet::Error, - "You must specify a title for objects of type %s" % self.to_s + if configuration = hash[:configuration] + hash.delete(:configuration) end + raise(Puppet::Error, "You must specify a title for objects of type %s" % self.to_s) unless title + if hash.include? :type unless self.validattr? :type hash.delete :type end end + # okay, now make a transobject out of hash begin trans = Puppet::TransObject.new(title, self.name.to_s) + trans.configuration = configuration if configuration hash.each { |param, value| trans[param] = value } diff --git a/lib/puppet/metatype/metaparams.rb b/lib/puppet/metatype/metaparams.rb index 1ab3f26c1..eb158a47d 100644 --- a/lib/puppet/metatype/metaparams.rb +++ b/lib/puppet/metatype/metaparams.rb @@ -94,7 +94,7 @@ class Puppet::Type # We've got four relationship metaparameters, so this method is used # to reduce code duplication between them. - def store_relationship(param, values) + def munge_relationship(param, values) # We need to support values passed in as an array or as a # resource reference. result = [] @@ -194,20 +194,24 @@ class Puppet::Type unless aliases.is_a?(Array) aliases = [aliases] end - @resource.info "Adding aliases %s" % aliases.collect { |a| - a.inspect - }.join(", ") + + raise(ArgumentError, "Cannot add aliases without a configuration") unless @resource.configuration + + @resource.info "Adding aliases %s" % aliases.collect { |a| a.inspect }.join(", ") + aliases.each do |other| - if obj = @resource.class[other] - unless obj == @resource - self.fail( - "%s can not create alias %s: object already exists" % - [@resource.title, other] - ) + if obj = @resource.configuration.resource(@resource.class.name, other) + unless obj.object_id == @resource.object_id + self.fail("%s can not create alias %s: object already exists" % [@resource.title, other]) end next end + + # LAK:FIXME Old-school, add the alias to the class. @resource.class.alias(other, @resource) + + # Newschool, add it to the configuration. + @resource.configuration.alias(@resource, other) end end end @@ -247,7 +251,16 @@ class Puppet::Type end def munge(rels) - @resource.store_relationship(self.class.name, rels) + @resource.munge_relationship(self.class.name, rels) + end + + def validate_relationship + @value.each do |value| + unless @resource.configuration.resource(*value) + description = self.class.direction == :in ? "dependency" : "dependent" + raise Puppet::Error, "Could not find #{description} %s[%s]" % [value[0].to_s.capitalize, value[1]] + end + end end # Create edges from each of our relationships. :in diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb index 5408cabe4..ea351ddc3 100644 --- a/lib/puppet/network/client/master.rb +++ b/lib/puppet/network/client/master.rb @@ -139,63 +139,57 @@ class Puppet::Network::Client::Master < Puppet::Network::Client facts = self.class.facts end - if self.configuration or FileTest.exists?(self.cachefile) - if self.fresh?(facts) - Puppet.info "Config is up to date" - if self.configuration - return - end - if oldtext = self.retrievecache - begin - @configuration = YAML.load(oldtext).to_configuration - rescue => detail - Puppet.warning "Could not load cached configuration: %s" % detail - end - return - end - end - end - Puppet.debug("getting config") + raise Puppet::Network::ClientError.new("Could not retrieve any facts") unless facts.length > 0 # Retrieve the plugins. - if Puppet[:pluginsync] - getplugins() - end + getplugins() if Puppet[:pluginsync] - unless facts.length > 0 - raise Puppet::Network::ClientError.new( - "Could not retrieve any facts" - ) + if (self.configuration or FileTest.exist?(self.cachefile)) and self.fresh?(facts) + Puppet.info "Configuration is up to date" + return if use_cached_config end - unless objects = get_actual_config(facts) - @configuration = nil + Puppet.debug("Retrieving configuration") + + # If we can't retrieve the configuration, just return, which will either + # fail, or use the in-memory configuration. + unless yaml_objects = get_actual_config(facts) + use_cached_config(true) return end - unless objects.is_a?(Puppet::TransBucket) - raise NetworkClientError, - "Invalid returned objects of type %s" % objects.class + begin + objects = YAML.load(yaml_objects) + rescue => detail + msg = "Configuration could not be translated from yaml" + msg += "; using cached configuration" if use_cached_config(true) + Puppet.warning msg + return end self.setclasses(objects.classes) # Clear all existing objects, so we can recreate our stack. - if self.configuration - clear() - end + clear() if self.configuration # Now convert the objects to a puppet configuration graph. - @configuration = objects.to_configuration + begin + @configuration = objects.to_configuration + rescue => detail + clear() + puts detail.backtrace if Puppet[:trace] + msg = "Configuration could not be instantiated: %s" % detail + msg += "; using cached configuration" if use_cached_config(true) + Puppet.warning msg + return + end - if @configuration.nil? - raise Puppet::Error, "Configuration could not be processed" + if ! @configuration.from_cache + self.cache(yaml_objects) end # Keep the state database up to date. @configuration.host_config = true - - return @configuration end # A simple proxy method, so it's easy to test. @@ -270,11 +264,9 @@ class Puppet::Network::Client::Master < Puppet::Network::Client Puppet.err "Could not retrieve configuration: %s" % detail end - if defined? @configuration and @configuration + if self.configuration @configuration.retrieval_duration = duration - unless @local - Puppet.notice "Starting configuration run" - end + Puppet.notice "Starting configuration run" unless @local benchmark(:notice, "Finished configuration run") do @configuration.apply(options) end @@ -500,34 +492,16 @@ class Puppet::Network::Client::Master < Puppet::Network::Client # Actually retrieve the configuration, either from the server or from a # local master. def get_actual_config(facts) - if @local - return get_local_config(facts) - else - begin - Timeout::timeout(self.class.timeout) do - return get_remote_config(facts) - end - rescue Timeout::Error - Puppet.err "Configuration retrieval timed out" - return nil + begin + Timeout::timeout(self.class.timeout) do + return get_remote_config(facts) end + rescue Timeout::Error + Puppet.err "Configuration retrieval timed out" + return nil end end - # Retrieve a configuration from a local master. - def get_local_config(facts) - # If we're local, we don't have to do any of the conversion - # stuff. - objects = @driver.getconfig(facts, "yaml") - @compile_time = Time.now - - if objects == "" - raise Puppet::Error, "Could not retrieve configuration" - end - - return objects - end - # Retrieve a config from a remote master. def get_remote_config(facts) textobjects = "" @@ -545,45 +519,18 @@ class Puppet::Network::Client::Master < Puppet::Network::Client end rescue => detail - puts detail.backtrace Puppet.err "Could not retrieve configuration: %s" % detail - - unless Puppet[:usecacheonfailure] - @configuration = nil - Puppet.warning "Not using cache on failed configuration" - return - end + return nil end end - fromcache = false - if textobjects == "" - unless textobjects = self.retrievecache - raise Puppet::Error.new( - "Cannot connect to server and there is no cached configuration" - ) - end - Puppet.warning "Could not get config; using cached copy" - fromcache = true - else - @compile_time = Time.now - Puppet::Util::Storage.cache(:configuration)[:facts] = facts - Puppet::Util::Storage.cache(:configuration)[:compile_time] = @compile_time - end + return nil if textobjects == "" - begin - objects = YAML.load(textobjects) - rescue => detail - raise Puppet::Error, - "Could not understand configuration: %s" % - detail.to_s - end + @compile_time = Time.now + Puppet::Util::Storage.cache(:configuration)[:facts] = facts + Puppet::Util::Storage.cache(:configuration)[:compile_time] = @compile_time - if @cache and ! fromcache - self.cache(textobjects) - end - - return objects + return textobjects end def lockfile @@ -609,4 +556,32 @@ class Puppet::Network::Client::Master < Puppet::Network::Client Puppet.info "Sleeping for %s seconds (splay is enabled)" % time sleep(time) end + + private + + # Use our cached config, optionally specifying whether this is + # necessary because of a failure. + def use_cached_config(because_of_failure = false) + return true if self.configuration + + if because_of_failure and ! Puppet[:usecacheonfailure] + @configuration = nil + Puppet.warning "Not using cache on failed configuration" + return false + end + + return false unless oldtext = self.retrievecache + + begin + @configuration = YAML.load(oldtext).to_configuration + @configuration.from_cache = true + @configuration.host_config = true + rescue => detail + puts detail.backtrace if Puppet[:trace] + Puppet.warning "Could not load cached configuration: %s" % detail + clear + return false + end + return true + end end diff --git a/lib/puppet/network/handler/runner.rb b/lib/puppet/network/handler/runner.rb index a8d0da9ce..c97e4791a 100755 --- a/lib/puppet/network/handler/runner.rb +++ b/lib/puppet/network/handler/runner.rb @@ -43,7 +43,7 @@ class Puppet::Network::Handler end if ignoreschedules - msg += " without schedules" + msg += " ignoring schedules" end Puppet.notice msg diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb new file mode 100644 index 000000000..062c67c71 --- /dev/null +++ b/lib/puppet/network/http.rb @@ -0,0 +1,13 @@ +class Puppet::Network::HTTP + def self.server_class_by_type(kind) + return Puppet::Network::HTTP::WEBrick if kind.to_sym == :webrick + if kind.to_sym == :mongrel + raise ArgumentError, "Mongrel is not installed on this platform" unless Puppet.features.mongrel? + return Puppet::Network::HTTP::Mongrel + end + raise ArgumentError, "Unknown HTTP server name [#{kind}]" + end +end + +require 'puppet/network/http/webrick' +require 'puppet/network/http/mongrel' diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb new file mode 100644 index 000000000..773381c8d --- /dev/null +++ b/lib/puppet/network/http/handler.rb @@ -0,0 +1,109 @@ +class Puppet::Network::HTTP::Handler + def initialize(args = {}) + raise ArgumentError unless @server = args[:server] + raise ArgumentError unless @handler = args[:handler] + @model = find_model_for_handler(@handler) + register_handler + end + + # handle an HTTP request + def process(request, response) + return do_find(request, response) if get?(request) and singular?(request) + return do_search(request, response) if get?(request) and plural?(request) + return do_destroy(request, response) if delete?(request) and singular?(request) + return do_save(request, response) if put?(request) and singular?(request) + raise ArgumentError, "Did not understand HTTP #{http_method(request)} request for '#{path(request)}'" + rescue Exception => e + return do_exception(request, response, e) + end + + private + + def do_find(request, response) + key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") + args = params(request) + result = @model.find(key, args).to_yaml + encode_result(request, response, result) + end + + def do_search(request, response) + args = params(request) + result = @model.search(args).collect {|obj| obj.to_yaml } + encode_result(request, response, result) + end + + def do_destroy(request, response) + key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") + args = params(request) + result = @model.destroy(key, args) + encode_result(request, response, YAML.dump(result)) + end + + def do_save(request, response) + data = body(request) + raise ArgumentError, "No data to save" if !data or data.empty? + args = params(request) + obj = @model.new + result = obj.save(args.merge(:data => data)).to_yaml + encode_result(request, response, result) + end + + def do_exception(request, response, exception, status=404) + encode_result(request, response, exception.to_s, status) + end + + def find_model_for_handler(handler) + Puppet::Indirector::Indirection.model(handler) || + raise(ArgumentError, "Cannot locate indirection [#{handler}].") + end + + def get?(request) + http_method(request) == 'GET' + end + + def put?(request) + http_method(request) == 'PUT' + end + + def delete?(request) + http_method(request) == 'DELETE' + end + + def singular?(request) + %r{/#{@handler.to_s}$}.match(path(request)) + end + + def plural?(request) + %r{/#{@handler.to_s}s$}.match(path(request)) + end + + # methods specific to a given web server + + def register_handler + raise NotImplementedError + end + + def http_method(request) + raise NotImplementedError + end + + def path(request) + raise NotImplementedError + end + + def request_key(request) + raise NotImplementedError + end + + def body(request) + raise NotImplementedError + end + + def params(request) + raise NotImplementedError + end + + def encode_result(request, response, result, status = 200) + raise NotImplementedError + end +end diff --git a/lib/puppet/network/http/mongrel.rb b/lib/puppet/network/http/mongrel.rb new file mode 100644 index 000000000..8ea669531 --- /dev/null +++ b/lib/puppet/network/http/mongrel.rb @@ -0,0 +1,54 @@ +require 'mongrel' if Puppet.features.mongrel? + +require 'puppet/network/http/mongrel/rest' +require 'puppet/network/http/mongrel/xmlrpc' + +class Puppet::Network::HTTP::Mongrel + def initialize(args = {}) + @listening = false + end + + def listen(args = {}) + raise ArgumentError, ":handlers must be specified." if !args[:handlers] or args[:handlers].empty? + raise ArgumentError, ":protocols must be specified." if !args[:protocols] or args[:protocols].empty? + raise ArgumentError, ":address must be specified." unless args[:address] + raise ArgumentError, ":port must be specified." unless args[:port] + raise "Mongrel server is already listening" if listening? + + @protocols = args[:protocols] + @handlers = args[:handlers] + @server = Mongrel::HttpServer.new(args[:address], args[:port]) + + setup_handlers + + @server.run + @listening = true + end + + def unlisten + raise "Mongrel server is not listening" unless listening? + @server.graceful_shutdown + @listening = false + end + + def listening? + @listening + end + + private + + def setup_handlers + @protocols.each do |protocol| + @handlers.each do |handler| + class_for_protocol(protocol).new(:server => @server, :handler => handler) + end + end + end + + # TODO/FIXME: need a spec which forces delegation to the real class + def class_for_protocol(protocol) + return Puppet::Network::HTTP::MongrelREST if protocol.to_sym == :rest + return Puppet::Network::HTTP::MongrelXMLRPC if protocol.to_sym == :xmlrpc + raise ArgumentError, "Unknown protocol [#{protocol}]." + end +end diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb new file mode 100644 index 000000000..db63613ab --- /dev/null +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -0,0 +1,37 @@ +require 'puppet/network/http/handler' + +class Puppet::Network::HTTP::MongrelREST < Puppet::Network::HTTP::Handler + + private + + def register_handler + @server.register('/' + @handler.to_s, self) + @server.register('/' + @handler.to_s + 's', self) + end + + def http_method(request) + request.params[Mongrel::Const::REQUEST_METHOD] + end + + def path(request) + '/' + request.params[Mongrel::Const::REQUEST_PATH].split('/')[1] + end + + def request_key(request) + request.params[Mongrel::Const::REQUEST_PATH].split('/')[2] + end + + def body(request) + request.body + end + + def params(request) + Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]) + end + + def encode_result(request, response, result, status = 200) + response.start(status) do |head, body| + body.write(result) + end + end +end diff --git a/lib/puppet/network/http/mongrel/xmlrpc.rb b/lib/puppet/network/http/mongrel/xmlrpc.rb new file mode 100644 index 000000000..92acd4f0e --- /dev/null +++ b/lib/puppet/network/http/mongrel/xmlrpc.rb @@ -0,0 +1,4 @@ +class Puppet::Network::HTTP::MongrelXMLRPC + def initialize(args = {}) + end +end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb new file mode 100644 index 000000000..c4b2ed3c6 --- /dev/null +++ b/lib/puppet/network/http/webrick.rb @@ -0,0 +1,51 @@ +require 'webrick' +require 'webrick/https' +require 'puppet/network/http/webrick/rest' +require 'puppet/network/http/webrick/xmlrpc' + +class Puppet::Network::HTTP::WEBrick + def initialize(args = {}) + @listening = false + end + + def listen(args = {}) + raise ArgumentError, ":handlers must be specified." if !args[:handlers] or args[:handlers].empty? + raise ArgumentError, ":protocols must be specified." if !args[:protocols] or args[:protocols].empty? + raise ArgumentError, ":address must be specified." unless args[:address] + raise ArgumentError, ":port must be specified." unless args[:port] + raise "WEBrick server is already listening" if listening? + + @protocols = args[:protocols] + @handlers = args[:handlers] + @server = WEBrick::HTTPServer.new(:BindAddress => args[:address], :Port => args[:port]) + setup_handlers + @server.start + @listening = true + end + + def unlisten + raise "WEBrick server is not listening" unless listening? + @server.shutdown + @listening = false + end + + def listening? + @listening + end + + private + + def setup_handlers + @protocols.each do |protocol| + @handlers.each do |handler| + class_for_protocol(protocol).new(:server => @server, :handler => handler) + end + end + end + + def class_for_protocol(protocol) + return Puppet::Network::HTTP::WEBrickREST if protocol.to_sym == :rest + return Puppet::Network::HTTP::WEBrickXMLRPC if protocol.to_sym == :xmlrpc + raise ArgumentError, "Unknown protocol [#{protocol}]." + end +end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb new file mode 100644 index 000000000..dd0c84d61 --- /dev/null +++ b/lib/puppet/network/http/webrick/rest.rb @@ -0,0 +1,41 @@ +require 'puppet/network/http/handler' + +class Puppet::Network::HTTP::WEBrickREST < Puppet::Network::HTTP::Handler + + # WEBrick uses a service() method to respond to requests. Simply delegate to the handler response() method. + def service(request, response) + process(request, response) + end + + private + + def register_handler + @server.mount('/' + @handler.to_s, self) + @server.mount('/' + @handler.to_s + 's', self) + end + + def http_method(request) + request.request_method + end + + def path(request) + '/' + request.path.split('/')[1] + end + + def request_key(request) + request.path.split('/')[2] + end + + def body(request) + request.body + end + + def params(request) + request.query + end + + def encode_result(request, response, result, status = 200) + response.status = status + response.body = result + end +end
\ No newline at end of file diff --git a/lib/puppet/network/http/webrick/xmlrpc.rb b/lib/puppet/network/http/webrick/xmlrpc.rb new file mode 100644 index 000000000..793708f8a --- /dev/null +++ b/lib/puppet/network/http/webrick/xmlrpc.rb @@ -0,0 +1,4 @@ +class Puppet::Network::HTTP::WEBrickXMLRPC + def initialize(args = {}) + end +end diff --git a/lib/puppet/network/server.rb b/lib/puppet/network/server.rb index 84a71a6b4..50e3bd686 100644 --- a/lib/puppet/network/server.rb +++ b/lib/puppet/network/server.rb @@ -1,66 +1,65 @@ class Puppet::Network::Server - attr_reader :server_type + attr_reader :server_type, :protocols, :address, :port - # which HTTP server subclass actually handles web requests of a certain type? (e.g., :rest => RESTServer) - def self.server_class_by_name(name) - klass = (name.to_s + 'Server').to_sym - const_get klass - end - - # we will actually return an instance of the Server subclass which handles the HTTP web server, instead of - # an instance of this generic Server class. A tiny bit of sleight-of-hand is necessary to make this happen. - def self.new(args = {}) - server_type = Puppet[:servertype] or raise "No servertype configuration found." - obj = self.server_class_by_name(server_type).allocate - obj.send :initialize, args.merge(:server_type => server_type) - obj - end - - def initialize(args = {}) - @routes = {} - @listening = false - @server_type = args[:server_type] - self.register(args[:handlers]) if args[:handlers] - end + def initialize(args = {}) + @server_type = Puppet[:servertype] or raise "No servertype configuration found." # e.g., WEBrick, Mongrel, etc. + http_server_class || raise(ArgumentError, "Could not determine HTTP Server class for server type [#{@server_type}]") + @address = args[:address] || Puppet[:bindaddress] || + raise(ArgumentError, "Must specify :address or configure Puppet :bindaddress.") + @port = args[:port] || Puppet[:masterport] || + raise(ArgumentError, "Must specify :port or configure Puppet :masterport") + @protocols = [] + @listening = false + @routes = {} + self.register(args[:handlers]) if args[:handlers] + end - def register(*indirections) - raise ArgumentError, "indirection names are required" if indirections.empty? - indirections.flatten.each { |i| @routes[i.to_sym] = true } - end + def register(*indirections) + raise ArgumentError, "Indirection names are required." if indirections.empty? + indirections.flatten.each { |i| @routes[i.to_sym] = true } + end - def unregister(*indirections) - indirections = @routes.keys if indirections.empty? - indirections.flatten.each do |i| - raise(ArgumentError, "indirection [%s] is not known" % i) unless @routes[i.to_sym] - @routes.delete(i.to_sym) + def unregister(*indirections) + raise "Cannot unregister indirections while server is listening." if listening? + indirections = @routes.keys if indirections.empty? + + indirections.flatten.each do |i| + raise(ArgumentError, "Indirection [%s] is unknown." % i) unless @routes[i.to_sym] + end + + indirections.flatten.each do |i| + @routes.delete(i.to_sym) + end end - end - def listening? - @listening - end + def listening? + @listening + end - def listen - raise "Cannot listen -- already listening" if listening? - start_web_server - @listening = true - end + def listen + raise "Cannot listen -- already listening." if listening? + http_server.listen(@routes.dup) + @listening = true + end - def unlisten - raise "Cannot unlisten -- not currently listening" unless listening? - stop_web_server - @listening = false - end + def unlisten + raise "Cannot unlisten -- not currently listening." unless listening? + http_server.unlisten + @listening = false + end + + def http_server_class + http_server_class_by_type(@server_type) + end private - def start_web_server - raise NotImplementedError, "this method needs to be implemented by the actual web server (sub)class" - end - - def stop_web_server - raise NotImplementedError, "this method needs to be implemented by the actual web server (sub)class" - end + def http_server + @http_server ||= http_server_class.new + end + + def http_server_class_by_type(kind) + Puppet::Network::HTTP.server_class_by_type(kind) + end end - diff --git a/lib/puppet/node/configuration.rb b/lib/puppet/node/configuration.rb index e49090d70..804f357d1 100644 --- a/lib/puppet/node/configuration.rb +++ b/lib/puppet/node/configuration.rb @@ -38,6 +38,10 @@ class Puppet::Node::Configuration < Puppet::PGraph # relationship graph. attr_accessor :is_relationship_graph + # Whether this configuration was retrieved from the cache, which affects + # whether it is written back out again. + attr_accessor :from_cache + # Add classes to our class list. def add_class(*classes) classes.each do |klass| @@ -66,6 +70,16 @@ class Puppet::Node::Configuration < Puppet::PGraph end end + # Create an alias for a resource. + def alias(resource, name) + resource.ref =~ /^(.+)\[/ + + newref = "%s[%s]" % [$1 || resource.class.name, name] + raise(ArgumentError, "Cannot alias %s to %s; resource %s already exists" % [resource.ref, name, newref]) if @resource_table[newref] + @resource_table[newref] = resource + @aliases[resource.ref] << newref + end + # Apply our configuration to the local host. Valid options # are: # :tags - set the tags that restrict what resources run @@ -274,6 +288,8 @@ class Puppet::Node::Configuration < Puppet::PGraph @applying = false @relationship_graph = nil + @aliases = Hash.new { |hash, key| hash[key] = [] } + if block_given? yield(self) finalize() @@ -331,7 +347,9 @@ class Puppet::Node::Configuration < Puppet::PGraph # references to the resource instances. def remove_resource(*resources) resources.each do |resource| - @resource_table.delete(resource.ref) if @resource_table.include?(resource.ref) + @resource_table.delete(resource.ref) + @aliases[resource.ref].each { |res_alias| @resource_table.delete(res_alias) } + @aliases[resource.ref].clear remove_vertex!(resource) if vertex?(resource) @relationship_graph.remove_vertex!(resource) if @relationship_graph and @relationship_graph.vertex?(resource) resource.remove diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb index f23b42a35..7f568f1b3 100644 --- a/lib/puppet/parser/compile.rb +++ b/lib/puppet/parser/compile.rb @@ -14,7 +14,7 @@ require 'puppet/util/errors' class Puppet::Parser::Compile include Puppet::Util include Puppet::Util::Errors - attr_reader :topscope, :parser, :node, :facts, :collections, :configuration + attr_reader :parser, :node, :facts, :collections, :configuration, :node_scope # Add a collection to the global list. def add_collection(coll) @@ -107,7 +107,7 @@ class Puppet::Parser::Compile # Evaluate all of the classes specified by the node. def evaluate_node_classes - evaluate_classes(@node.classes, @topscope) + evaluate_classes(@node.classes, topscope) end # Evaluate each specified class in turn. If there are any classes we can't @@ -142,9 +142,7 @@ class Puppet::Parser::Compile # Return a resource by either its ref or its type and title. def findresource(string, name = nil) - if name - string = "%s[%s]" % [string.capitalize, name] - end + string = "%s[%s]" % [string.capitalize, name] if name @resource_table[string] end @@ -173,7 +171,7 @@ class Puppet::Parser::Compile # using the top scope. Adds an edge between the scope and # its parent to the graph. def newscope(parent, options = {}) - parent ||= @topscope + parent ||= topscope options[:compile] = self options[:parser] ||= self.parser scope = Puppet::Parser::Scope.new(options) @@ -229,6 +227,12 @@ class Puppet::Parser::Compile @configuration.add_edge!(scope.resource, resource) end + # The top scope is usually the top-level scope, but if we're using AST nodes, + # then it is instead the node's scope. + def topscope + node_scope || @topscope + end + private # If ast nodes are enabled, then see if we can find and evaluate one. @@ -241,10 +245,7 @@ class Puppet::Parser::Compile break if astnode = @parser.nodes[name.to_s.downcase] end - unless astnode - astnode = @parser.nodes["default"] - end - unless astnode + unless (astnode ||= @parser.nodes["default"]) raise Puppet::ParseError, "Could not find default node or by name with '%s'" % node.names.join(", ") end @@ -253,6 +254,12 @@ class Puppet::Parser::Compile resource = Puppet::Parser::Resource.new(:type => "node", :title => astnode.classname, :scope => topscope, :source => topscope.source) store_resource(topscope, resource) @configuration.tag(astnode.classname) + + resource.evaluate + + # Now set the node scope appropriately, so that :topscope can + # behave differently. + @node_scope = class_scope(astnode) end # Evaluate our collections and return true if anything returned an object. @@ -318,6 +325,8 @@ class Puppet::Parser::Compile @configuration.add_vertex!(@main_resource) @resource_table["Class[main]"] = @main_resource + + @main_resource.evaluate end # Make sure the entire configuration is evaluated. @@ -408,7 +417,6 @@ class Puppet::Parser::Compile # A graph for maintaining scope relationships. @scope_graph = GRATR::Digraph.new - @scope_graph.add_vertex!(@topscope) # For maintaining the relationship between scopes and their resources. @configuration = Puppet::Node::Configuration.new(@node.name) diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb index c8fc2f199..ef53889cf 100644 --- a/lib/puppet/transaction.rb +++ b/lib/puppet/transaction.rb @@ -311,7 +311,7 @@ class Transaction ret = eval_resource(resource) end - if Puppet[:evaltrace] + if Puppet[:evaltrace] and @configuration.host_config? resource.info "Evaluated in %0.2f seconds" % seconds end ret diff --git a/lib/puppet/transportable.rb b/lib/puppet/transportable.rb index 281ad00d3..6a573489c 100644 --- a/lib/puppet/transportable.rb +++ b/lib/puppet/transportable.rb @@ -59,15 +59,11 @@ module Puppet tmpname = @type end trans = TransObject.new(tmpname, :component) - if defined? @parameters - @parameters.each { |param,value| - Puppet.debug "Defining %s on %s of type %s" % - [param,@name,@type] - trans[param] = value - } - else - #Puppet.debug "%s[%s] has no parameters" % [@type, @name] - end + @params.each { |param,value| + next unless Puppet::Type::Component.validattr?(param) + Puppet.debug "Defining %s on %s of type %s" % [param,@name,@type] + trans[param] = value + } Puppet::Type::Component.create(trans) end @@ -107,16 +103,7 @@ module Puppet def to_type retobj = nil if typeklass = Puppet::Type.type(self.type) - # FIXME This should really be done differently, but... - if retobj = typeklass[self.name] - self.each do |param, val| - retobj[param] = val - end - else - unless retobj = typeklass.create(self) - return nil - end - end + return typeklass.create(self) else return to_component end @@ -135,7 +122,7 @@ module Puppet class TransBucket include Enumerable - attr_accessor :name, :type, :file, :line, :classes, :keyword, :top + attr_accessor :name, :type, :file, :line, :classes, :keyword, :top, :configuration %w{delete shift include? length empty? << []}.each { |method| define_method(method) do |*args| @@ -218,11 +205,13 @@ module Puppet def to_configuration configuration = Puppet::Node::Configuration.new(Facter.value("hostname")) do |config| delver = proc do |obj| + obj.configuration = config unless container = config.resource(obj.to_ref) container = obj.to_type config.add_resource container end obj.each do |child| + child.configuration = config unless resource = config.resource(child.to_ref) next unless resource = child.to_type config.add_resource resource @@ -252,65 +241,25 @@ module Puppet end def to_type - # this container will contain the equivalent of all objects at - # this level - #container = Puppet::Component.new(:name => @name, :type => @type) - #unless defined? @name - # raise Puppet::DevError, "TransBuckets must have names" - #end unless defined? @type Puppet.debug "TransBucket '%s' has no type" % @name end - usetrans = true - if usetrans - tmpname = nil - - # Nodes have the same name and type - if self.name - tmpname = "%s[%s]" % [@type, self.name] - else - tmpname = @type - end - trans = TransObject.new(tmpname, :component) - if defined? @parameters - @parameters.each { |param,value| - Puppet.debug "Defining %s on %s of type %s" % - [param,@name,@type] - trans[param] = value - } - else - #Puppet.debug "%s[%s] has no parameters" % [@type, @name] - end - container = Puppet::Type::Component.create(trans) + # Nodes have the same name and type + if self.name + tmpname = "%s[%s]" % [@type, self.name] else - hash = { - :name => self.name, - :type => @type - } - if defined? @parameters - @parameters.each { |param,value| - Puppet.debug "Defining %s on %s of type %s" % - [param,@name,@type] - hash[param] = value - } - else - #Puppet.debug "%s[%s] has no parameters" % [@type, @name] - end - - container = Puppet::Type::Component.create(hash) + tmpname = @type end - #Puppet.info container.inspect - - # unless we successfully created the container, return an error - unless container - Puppet.warning "Got no container back" - return nil + trans = TransObject.new(tmpname, :component) + if defined? @parameters + @parameters.each { |param,value| + Puppet.debug "Defining %s on %s of type %s" % + [param,@name,@type] + trans[param] = value + } end - - # at this point, no objects at are level are still Transportable - # objects - return container + return Puppet::Type::Component.create(trans) end def param(param,value) diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index b7ff1f664..f5dd0f8dd 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -182,7 +182,7 @@ class Type # directly from it. This is the main object instantiation mechanism. if hash.is_a?(Puppet::TransObject) #self[:name] = hash[:name] - [:file, :line, :tags].each { |getter| + [:file, :line, :tags, :configuration].each { |getter| if hash.respond_to?(getter) setter = getter.to_s + "=" if val = hash.send(getter) @@ -194,10 +194,11 @@ class Type # XXX This will need to change when transobjects change to titles. @title = hash.name hash = hash.to_hash - elsif hash[:title] - # XXX This should never happen - @title = hash[:title] - hash.delete(:title) + else + if hash[:title] + @title = hash[:title] + hash.delete(:title) + end end # Before anything else, set our parent if it was included @@ -221,7 +222,7 @@ class Type if attrs.include?(namevar) attrs.delete(namevar) else - self.devfail "My namevar isn\'t a valid attribute...?" + self.devfail "My namevar isn't a valid attribute...?" end else self.devfail "I was not passed a namevar" @@ -284,6 +285,14 @@ class Type # Scheduling has to be done when the whole config is instantiated, so # that file order doesn't matter in finding them. self.schedule + + # Make sure all of our relationships are valid. Again, must be done + # when the entire configuration is instantiated. + self.class.relationship_params.collect do |klass| + if param = @parameters[klass.name] + param.validate_relationship + end + end.flatten.reject { |r| r.nil? } end # Return a cached value diff --git a/lib/puppet/type/component.rb b/lib/puppet/type/component.rb index 4dc542a65..7aa24a302 100644 --- a/lib/puppet/type/component.rb +++ b/lib/puppet/type/component.rb @@ -93,9 +93,9 @@ Puppet::Type.newtype(:component) do end # Initialize a new component - def initialize(args) + def initialize(*args) @children = [] - super(args) + super # If the title isn't a full resource reference, assume # we're a class and make an alias for that. diff --git a/lib/puppet/type/pfile/group.rb b/lib/puppet/type/pfile/group.rb index 9625b6354..5f7caf342 100755 --- a/lib/puppet/type/pfile/group.rb +++ b/lib/puppet/type/pfile/group.rb @@ -6,6 +6,10 @@ module Puppet name or group ID." @event = :file_changed + validate do |group| + raise(Puppet::Error, "Invalid group name '%s'" % group.inspect) unless group and group != "" + end + def id2name(id) if id > 70000 return nil |