summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/network/server.rb98
-rw-r--r--spec/unit/network/rest_controller.rb65
-rw-r--r--spec/unit/network/server.rb341
3 files changed, 235 insertions, 269 deletions
diff --git a/lib/puppet/network/server.rb b/lib/puppet/network/server.rb
index 84a71a6b4..177cce4df 100644
--- a/lib/puppet/network/server.rb
+++ b/lib/puppet/network/server.rb
@@ -1,66 +1,58 @@
class Puppet::Network::Server
- attr_reader :server_type
+ attr_reader :server_type, :http_server
- # 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 = http_server_class_by_type(@server_type)
+ @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?
+ initialize_http_server
+ self.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?
+ self.http_server.unlisten
+ @listening = false
+ 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 initialize_http_server
+ @server = @http_server_class.new
+ end
+
+ def http_server_class_by_type(kind)
+ # TODO: this will become Puppet::Network::HTTP::WEBrick or Puppet::Network::HTTP::Mongrel
+ Class.new
+ end
end
-
diff --git a/spec/unit/network/rest_controller.rb b/spec/unit/network/rest_controller.rb
deleted file mode 100644
index 0bcc0abf2..000000000
--- a/spec/unit/network/rest_controller.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/env ruby
-#
-# Created by Rick Bradley on 2007-10-03.
-# Copyright (c) 2007. All rights reserved.
-
-require File.dirname(__FILE__) + '/../../spec_helper'
-
-require 'puppet/network/rest_controller'
-
-describe Puppet::Network::RESTController, "in general" do
- it "should route GET requests on indirector's name to indirector find for the model class"
- it "should route GET requests on indirector's plural name to indirector search for the model class"
- it "should route DELETE requests on indirector's name to indirector destroy for the model class"
- it "should route POST requests on indirector's name to indirector save for the model class"
- it "should serialize result data when methods are handled"
- it "should serialize an error condition when indirection method call generates an exception"
-end
-
-__END__
-
-# possible implementation of the satisfying class
-
-class RESTController
- def initialize(klass)
- @klass = klass
- end
-
- # TODO: is it possible to distinguish from the request object the path which we were called by?
-
- def do_GET(request, response)
- return do_GETS(request, response) if asked_for_plural?(request)
- args = request.something
- result = @klass.find args
- return serialize(result)
- end
-
- def do_GETS(request, response)
- args = request.something
- result = @klass.search args
- return serialize(result)
- end
-
- def do_DELETE(request, response)
- args = request.something
- result = @klass.destroy args
- return serialize(result)
- end
-
- def do_PUT(request, response)
- args = request.something
- obj = @klass.new(args)
- result = obj.save
- return serialize(result)
- end
-
- def do_POST(request, response)
- do_PUT(request, response)
- end
-
- private
-
- def asked_for_plural?(request)
- # TODO: pick apart the request and see if this was trying to do a plural or singular GET
- end
-end
diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb
index 17ed336de..a23f22058 100644
--- a/spec/unit/network/server.rb
+++ b/spec/unit/network/server.rb
@@ -9,176 +9,215 @@ require 'puppet/network/server'
# a fake server class, so we don't have to implement full autoloading etc. (or at least just yet) just to do testing
class TestServer < Puppet::Network::Server
- def start_web_server
- end
-
- def stop_web_server
- end
end
describe Puppet::Network::Server, "when initializing" do
- before do
- Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
- end
-
- it "should use the Puppet configurator to determine which HTTP server will be used to provide access to clients" do
- Puppet.expects(:[]).with(:servertype).returns(:suparserver)
- @server = Puppet::Network::Server.new
- @server.server_type.should == :suparserver
- end
-
- it "should fail to initialize if there is no HTTP server known to the Puppet configurator" do
- Puppet.expects(:[]).with(:servertype).returns(nil)
- Proc.new { Puppet::Network::Server.new }.should raise_error
- end
-
- it "should use the Puppet Configurator to determine what style of service we will offer to clients (e.g., REST, XMLRPC, ...)"
- it "should fail to initialize if there is no style of service known to the Puppet configurator"
-
- it "should allow registering indirections" do
- @server = Puppet::Network::Server.new(:handlers => [ :foo, :bar, :baz])
- Proc.new { @server.unregister(:foo, :bar, :baz) }.should_not raise_error
- end
-
- it "should not be listening after initialization" do
- Puppet::Network::Server.new.should_not be_listening
- end
+ before do
+ Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
+ end
+
+ it "should use the Puppet configurator to determine which HTTP server will be used to provide access to clients" do
+ Puppet.expects(:[]).with(:servertype).returns(:suparserver)
+ @server = Puppet::Network::Server.new
+ @server.server_type.should == :suparserver
+ end
+
+ it "should fail to initialize if there is no HTTP server known to the Puppet configurator" do
+ Puppet.expects(:[]).with(:servertype).returns(nil)
+ Proc.new { Puppet::Network::Server.new }.should raise_error
+ end
+
+ it "should allow registering indirections" do
+ @server = Puppet::Network::Server.new(:handlers => [ :foo, :bar, :baz])
+ Proc.new { @server.unregister(:foo, :bar, :baz) }.should_not raise_error
+ end
+
+ it "should not be listening after initialization" do
+ Puppet::Network::Server.new.should_not be_listening
+ end
end
describe Puppet::Network::Server, "in general" do
- before do
- Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
- Puppet.stubs(:[]).with(:servertype).returns(:suparserver)
- @server = Puppet::Network::Server.new
- end
-
- it "should allow registering an indirection for client access by specifying its indirection name" do
- Proc.new { @server.register(:foo) }.should_not raise_error
- end
-
- it "should require at least one indirection name when registering indirections for client access" do
- Proc.new { @server.register }.should raise_error(ArgumentError)
- end
-
- it "should allow for numerous indirections to be registered at once for client access" do
- Proc.new { @server.register(:foo, :bar, :baz) }.should_not raise_error
- end
-
- it "should allow the use of indirection names to specify which indirections are to be no longer accessible to clients" do
- @server.register(:foo)
- Proc.new { @server.unregister(:foo) }.should_not raise_error
- end
-
- it "should leave other indirections accessible to clients when turning off indirections" do
- @server.register(:foo, :bar)
- @server.unregister(:foo)
- Proc.new { @server.unregister(:bar)}.should_not raise_error
- end
-
- it "should allow specifying numerous indirections which are to be no longer accessible to clients" do
- @server.register(:foo, :bar)
- Proc.new { @server.unregister(:foo, :bar) }.should_not raise_error
- end
-
- it "should not allow turning off unknown indirection names" do
- @server.register(:foo, :bar)
- Proc.new { @server.unregister(:baz) }.should raise_error(ArgumentError)
- end
-
- it "should disable client access immediately when turning off indirections" do
- @server.register(:foo, :bar)
- @server.unregister(:foo)
- Proc.new { @server.unregister(:foo) }.should raise_error(ArgumentError)
- end
-
- it "should allow turning off all indirections at once" do
- @server.register(:foo, :bar)
- @server.unregister
- [ :foo, :bar, :baz].each do |indirection|
- Proc.new { @server.unregister(indirection) }.should raise_error(ArgumentError)
- end
- end
-
- it "should provide a means of determining whether it is listening" do
- @server.should respond_to(:listening?)
- end
-
- it "should provide a means of determining which HTTP server will be used to provide access to clients" do
- @server.server_type.should == :suparserver
- end
-
- it "should provide a means of determining which style of service is being offered to clients"
-
- it "should allow for multiple configurations, each handling different indirections" do
- @server2 = Puppet::Network::Server.new
- @server.register(:foo, :bar)
- @server2.register(:foo, :xyzzy)
- @server.unregister(:foo, :bar)
- @server2.unregister(:foo, :xyzzy)
- Proc.new { @server.unregister(:xyzzy) }.should raise_error(ArgumentError)
- Proc.new { @server2.unregister(:bar) }.should raise_error(ArgumentError)
- end
-end
+ before do
+ Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
+ Puppet.stubs(:[]).with(:servertype).returns(:suparserver)
+ @server = Puppet::Network::Server.new
+ end
+
+ it "should allow registering an indirection for client access by specifying its indirection name" do
+ Proc.new { @server.register(:foo) }.should_not raise_error
+ end
+
+ it "should require at least one indirection name when registering indirections for client access" do
+ Proc.new { @server.register }.should raise_error(ArgumentError)
+ end
+
+ it "should allow for numerous indirections to be registered at once for client access" do
+ Proc.new { @server.register(:foo, :bar, :baz) }.should_not raise_error
+ end
+
+ it "should allow the use of indirection names to specify which indirections are to be no longer accessible to clients" do
+ @server.register(:foo)
+ Proc.new { @server.unregister(:foo) }.should_not raise_error
+ end
-describe Puppet::Network::Server, "when listening is turned off" do
- before do
- Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
- Puppet.stubs(:[]).with(:servertype).returns(:suparserver)
- @server = Puppet::Network::Server.new
- end
+ it "should leave other indirections accessible to clients when turning off indirections" do
+ @server.register(:foo, :bar)
+ @server.unregister(:foo)
+ Proc.new { @server.unregister(:bar)}.should_not raise_error
+ end
+
+ it "should allow specifying numerous indirections which are to be no longer accessible to clients" do
+ @server.register(:foo, :bar)
+ Proc.new { @server.unregister(:foo, :bar) }.should_not raise_error
+ end
+
+ it "should not turn off any indirections if given unknown indirection names to turn off" do
+ @server.register(:foo, :bar)
+ Proc.new { @server.unregister(:foo, :bar, :baz) }.should raise_error(ArgumentError)
+ Proc.new { @server.unregister(:foo, :bar) }.should_not raise_error
+ end
- it "should allow listening to be turned on" do
- Proc.new { @server.listen }.should_not raise_error
- end
+ it "should not allow turning off unknown indirection names" do
+ @server.register(:foo, :bar)
+ Proc.new { @server.unregister(:baz) }.should raise_error(ArgumentError)
+ end
- it "should not allow listening to be turned off" do
- Proc.new { @server.unlisten }.should raise_error(RuntimeError)
- end
+ it "should disable client access immediately when turning off indirections" do
+ @server.register(:foo, :bar)
+ @server.unregister(:foo)
+ Proc.new { @server.unregister(:foo) }.should raise_error(ArgumentError)
+ end
- it "should indicate that it is not listening" do
- @server.should_not be_listening
- end
+ it "should allow turning off all indirections at once" do
+ @server.register(:foo, :bar)
+ @server.unregister
+ [ :foo, :bar, :baz].each do |indirection|
+ Proc.new { @server.unregister(indirection) }.should raise_error(ArgumentError)
+ end
+ end
- it "should cause the HTTP server to listen when listening is turned on" do
- @server.expects(:start_web_server)
- @server.listen
- end
+ it "should provide a means of determining whether it is listening" do
+ @server.should respond_to(:listening?)
+ end
- it "should not route HTTP GET requests to a controller for the registered indirection"
- it "should not route HTTP DELETE requests to a controller for the registered indirection"
- it "should not route HTTP POST requests to a controller for the registered indirection"
+ it "should provide a means of determining which HTTP server will be used to provide access to clients" do
+ @server.server_type.should == :suparserver
+ end
+
+ it "should allocate an instance of the appropriate HTTP server"
+
+ it "should allow for multiple configurations, each handling different indirections" do
+ @server2 = Puppet::Network::Server.new
+ @server.register(:foo, :bar)
+ @server2.register(:foo, :xyzzy)
+ @server.unregister(:foo, :bar)
+ @server2.unregister(:foo, :xyzzy)
+ Proc.new { @server.unregister(:xyzzy) }.should raise_error(ArgumentError)
+ Proc.new { @server2.unregister(:bar) }.should raise_error(ArgumentError)
+ end
- # TODO: FIXME write integrations which fire up actual webrick / mongrel servers and are thus webrick / mongrel specific?]
+ it "should provide a means of determining which style of service is being offered to clients"
end
-describe Puppet::Network::Server, "when listening is turned on" do
- before do
- Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
- Puppet.stubs(:[]).with(:servertype).returns(:suparserver)
- @server = Puppet::Network::Server.new
- @server.listen
- end
+describe Puppet::Network::Server, "when listening is off" do
+ before do
+ Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
+ Puppet.stubs(:[]).with(:servertype).returns(:suparserver)
+ @server = Puppet::Network::Server.new
+ @mock_http_server = mock('http server')
+ @mock_http_server.stubs(:listen)
+ @server.stubs(:http_server).returns(@mock_http_server)
+ end
- it "should allow listening to be turned off" do
- Proc.new { @server.unlisten }.should_not raise_error
- end
+ it "should allow listening to be turned on" do
+ Proc.new { @server.listen }.should_not raise_error
+ end
+
+ it "should cause the HTTP server to listen when listening is turned on" do
+ mock_http_server = mock('http server')
+ mock_http_server.expects(:listen)
+ @server.expects(:http_server).returns(mock_http_server)
+ @server.listen
+ end
- it "should not allow listening to be turned on" do
- Proc.new { @server.listen }.should raise_error(RuntimeError)
- end
+ it "should not allow listening to be turned off" do
+ Proc.new { @server.unlisten }.should raise_error(RuntimeError)
+ end
+
+ it "should indicate that it is not listening" do
+ @server.should_not be_listening
+ end
+end
+
+describe Puppet::Network::Server, "when listening is on" do
+ before do
+ Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer)
+ Puppet.stubs(:[]).with(:servertype).returns(:suparserver)
+ @server = Puppet::Network::Server.new
+ @mock_http_server = mock('http server')
+ @mock_http_server.stubs(:listen)
+ @mock_http_server.stubs(:unlisten)
+ @server.stubs(:http_server).returns(@mock_http_server)
+ @server.listen
+ @server.register(:foo)
+ end
+
+ it "should allow listening to be turned off" do
+ Proc.new { @server.unlisten }.should_not raise_error
+ end
- it "should indicate that listening is turned off" do
- @server.should be_listening
- end
+ it "should cause the HTTP server to stop listening when listening is turned off" do
+ mock_http_server = mock('http server')
+ mock_http_server.expects(:unlisten)
+ @server.expects(:http_server).returns(mock_http_server)
+ @server.unlisten
+ end
- it "should cause the HTTP server to stop listening when listening is turned off" do
- @server.expects(:stop_web_server)
- @server.unlisten
- end
+ it "should not allow listening to be turned on" do
+ Proc.new { @server.listen }.should raise_error(RuntimeError)
+ end
- it "should route HTTP GET requests to a controller for the registered indirection"
- it "should route HTTP DELETE requests to a controller for the registered indirection"
- it "should route HTTP POST requests to a controller for the registered indirection"
+ it "should indicate that listening is turned off" do
+ @server.should be_listening
+ end
+
+ it "should not allow for indirections to be turned off" do
+ Proc.new { @server.unregister(:foo) }.should raise_error(RuntimeError)
+ end
+end
+
+describe Class.new, "Puppet::Network::HTTP::Webrick (webrick http server class)" do
+ it "should allow listening"
+ it "should get a set of handlers when listening"
+ it "should allow unlistening"
+ it "should instantiate a specific handler (webrick+rest, e.g.) for each handler when listening, for each protocol being served (xmlrpc, rest, etc.)"
+ it "should mount each handler with the appropriate webrick path when listening"
+ it "should start webrick when listening"
+ it "should stop webrick when unlistening"
+end
- # TODO: FIXME [ write integrations which fire up actual webrick / mongrel servers and are thus webrick / mongrel specific?]
+describe Class.new, "Puppet::Network::HTTP::Mongrel (mongrel http server class)" do
+ it "should allow listening"
+ it "should get a set of handlers when listening"
+ it "should allow unlistening"
+ it "should instantiate a specific handler (mongrel+rest, e.g.) for each handler when listening, for each protocol being served (xmlrpc, rest, etc.)"
+ it "should mount each handler with the appropriate mongrel path when listening"
+ it "should start mongrel when listening"
+ it "should stop mongrel when unlistening"
end
+
+describe Class.new, "Puppet::Network::Handler::*::* (handler class (e.g., webrick+rest or mongrel+xmlrpc))" do
+ it "should be able to unserialize a request from the given httpserver answering for the given protocol handler, to be used by a controller"
+ it "should be able to serialize a result from a controller for return by the given httpserver responding with the given protocol"
+ it "should properly encode an exception from a controller for use by the httpserver for the given protocol handler"
+ it "should call the appropriate controller method"
+ it "should properly encode parameters on the request for use by the controller methods"
+end
+
+describe Class.new, "put these somewhere" do
+ it "should allow indirections to deny access to services based upon which client is connecting, or whether the client is authorized"
+ it "should deny access to clients based upon rules"
+end
+
+