summaryrefslogtreecommitdiffstats
path: root/lib/puppet/network
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet/network')
-rw-r--r--lib/puppet/network/client/master.rb171
-rwxr-xr-xlib/puppet/network/handler/runner.rb2
-rw-r--r--lib/puppet/network/http.rb13
-rw-r--r--lib/puppet/network/http/handler.rb109
-rw-r--r--lib/puppet/network/http/mongrel.rb54
-rw-r--r--lib/puppet/network/http/mongrel/rest.rb37
-rw-r--r--lib/puppet/network/http/mongrel/xmlrpc.rb4
-rw-r--r--lib/puppet/network/http/webrick.rb51
-rw-r--r--lib/puppet/network/http/webrick/rest.rb41
-rw-r--r--lib/puppet/network/http/webrick/xmlrpc.rb4
-rw-r--r--lib/puppet/network/server.rb105
11 files changed, 439 insertions, 152 deletions
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
-