summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2007-11-08 19:48:21 -0600
committerLuke Kanies <luke@madstop.com>2007-11-08 19:48:21 -0600
commit59cc25d798f20bc01943ca341c04716cb037064a (patch)
tree8697db62d970cdff7736a6acb17e97872d42d536 /lib/puppet
parent3f583dc133ce50ae34bfc151474c6d4196f803ca (diff)
parent956daa5b4b1c61db9a5e1d7638ca819005fd7ef0 (diff)
downloadpuppet-59cc25d798f20bc01943ca341c04716cb037064a.tar.gz
puppet-59cc25d798f20bc01943ca341c04716cb037064a.tar.xz
puppet-59cc25d798f20bc01943ca341c04716cb037064a.zip
Merge branch 'routing' into test_routing
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/indirector/indirection.rb8
-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
10 files changed, 373 insertions, 53 deletions
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/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
-