From 10039b94c77d4543d3b256b0bbda855d57a17be1 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Fri, 12 Oct 2007 16:17:00 -0500 Subject: interim checkin of network stuffs --- lib/puppet/network/server.rb | 98 +++++----- spec/unit/network/rest_controller.rb | 65 ------- spec/unit/network/server.rb | 341 +++++++++++++++++++---------------- 3 files changed, 235 insertions(+), 269 deletions(-) delete mode 100644 spec/unit/network/rest_controller.rb 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 + + -- cgit From e90191af9300fda00cd29d609ac80daff00332cc Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Fri, 12 Oct 2007 16:17:31 -0500 Subject: more stuff for the interim commit --- lib/puppet/network/controller.rb | 30 +++++++++++++++++++++++++++ lib/puppet/network/http.rb | 18 ++++++++++++++++ spec/unit/network/controller.rb | 45 ++++++++++++++++++++++++++++++++++++++++ spec/unit/network/http.rb | 13 ++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 lib/puppet/network/controller.rb create mode 100644 lib/puppet/network/http.rb create mode 100644 spec/unit/network/controller.rb create mode 100644 spec/unit/network/http.rb diff --git a/lib/puppet/network/controller.rb b/lib/puppet/network/controller.rb new file mode 100644 index 000000000..7e4cca643 --- /dev/null +++ b/lib/puppet/network/controller.rb @@ -0,0 +1,30 @@ +class Puppet::Network::Controller + def initialize(args = {}) + raise ArgumentError, ":indirection is required" unless args[:indirection] + @indirection = args[:indirection] + @klass = model_class_from_indirection_name(@indirection) + end + + def find(args = {}) + @klass.find(args) + end + + def destroy(args = {}) + @klass.destroy(args) + end + + def search(args = {}) + @klass.search(args) + end + + def save(args = {}) + instance = @klass.new(args) + instance.save + end + + private + + def model_class_from_indirection_name + Class.new # TODO : FIXME make this the indirection class + end +end diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb new file mode 100644 index 000000000..c46a73159 --- /dev/null +++ b/lib/puppet/network/http.rb @@ -0,0 +1,18 @@ +class Puppet::Network::HTTP + def self.new(args = {}) + raise ArgumentError, ":server_type is required" unless args[:server_type] + obj = class_for_server_type(args[:server_type]).allocate + obj.send :initialize, args.delete_if {|k,v| k == :server_type } + obj + end + + class << self + def class_for_server_type(server_type) + Class.new + # TODO: this will end up probably: { :webrick => ... } + + end + private :class_for_server_type + end +end + diff --git a/spec/unit/network/controller.rb b/spec/unit/network/controller.rb new file mode 100644 index 000000000..9098b6e25 --- /dev/null +++ b/spec/unit/network/controller.rb @@ -0,0 +1,45 @@ +#!/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/controller' + +describe Puppet::Network::Controller, "when initializing" do + it "should require an indirection name" do + Proc.new { Puppet::Network::Controller.new }.should raise_error(ArgumentError) + end +end + +describe Puppet::Network::Controller, "after initialization" do + before do + @mock_model_class = mock('model class') + Puppet::Network::Controller.any_instance.stubs(:model_class_from_indirection_name).returns(@mock_model_class) + @controller = Puppet::Network::Controller.new(:indirection => :foo) + end + + it "should delegate find to the indirection's model class's find" do + @mock_model_class.expects(:find).returns({:foo => :bar}) + @controller.find.should == { :foo => :bar } + end + + it "should delegate search to the indirection's model class's search" do + @mock_model_class.expects(:search).returns({:foo => :bar}) + @controller.search.should == { :foo => :bar } + end + + it "should delegate destroy to the indirection's model class's destroy" do + @mock_model_class.expects(:destroy).returns({:foo => :bar}) + @controller.destroy.should == { :foo => :bar } + end + + it "should delegate save to the indirection's model class's save" do + data = { :bar => :xyzzy } + mock_model_instance = mock('model instance') + @mock_model_class.expects(:new).with(data).returns(mock_model_instance) + mock_model_instance.expects(:save).returns({:foo => :bar}) + @controller.save(data).should == { :foo => :bar } + end +end \ No newline at end of file diff --git a/spec/unit/network/http.rb b/spec/unit/network/http.rb new file mode 100644 index 000000000..50ef92866 --- /dev/null +++ b/spec/unit/network/http.rb @@ -0,0 +1,13 @@ +#!/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/http' + +describe Puppet::Network::HTTP do + it "should require a server type when initializing" + it "should return an instance of the http server class corresponding to the server type" +end \ No newline at end of file -- cgit From 31384fea2263d9ee0e65312b4a0b956436022e63 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Mon, 15 Oct 2007 11:07:48 -0500 Subject: Pushing functionality down to webrick/mongrel classes now; cleanup in the base server / http server classes + specs. --- lib/puppet/network/http.rb | 20 ++----- lib/puppet/network/http/mongrel.rb | 2 + lib/puppet/network/http/webrick.rb | 2 + lib/puppet/network/server.rb | 15 +++-- spec/unit/network/http.rb | 13 ++++- spec/unit/network/server.rb | 111 ++++++++++++++++++++++++------------- 6 files changed, 102 insertions(+), 61 deletions(-) create mode 100644 lib/puppet/network/http/mongrel.rb create mode 100644 lib/puppet/network/http/webrick.rb diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb index c46a73159..86784e50e 100644 --- a/lib/puppet/network/http.rb +++ b/lib/puppet/network/http.rb @@ -1,18 +1,10 @@ class Puppet::Network::HTTP - def self.new(args = {}) - raise ArgumentError, ":server_type is required" unless args[:server_type] - obj = class_for_server_type(args[:server_type]).allocate - obj.send :initialize, args.delete_if {|k,v| k == :server_type } - obj - end - - class << self - def class_for_server_type(server_type) - Class.new - # TODO: this will end up probably: { :webrick => ... } - - end - private :class_for_server_type + def self.server_class_by_type(kind) + return Puppet::Network::HTTP::WEBRick if kind == :webrick + return Puppet::Network::HTTP::Mongrel if kind == :mongrel + 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/mongrel.rb b/lib/puppet/network/http/mongrel.rb new file mode 100644 index 000000000..dda3c1751 --- /dev/null +++ b/lib/puppet/network/http/mongrel.rb @@ -0,0 +1,2 @@ +class Puppet::Network::HTTP::Mongrel +end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb new file mode 100644 index 000000000..ad15261f6 --- /dev/null +++ b/lib/puppet/network/http/webrick.rb @@ -0,0 +1,2 @@ +class Puppet::Network::HTTP::WEBRick +end diff --git a/lib/puppet/network/server.rb b/lib/puppet/network/server.rb index 177cce4df..941cb9df1 100644 --- a/lib/puppet/network/server.rb +++ b/lib/puppet/network/server.rb @@ -1,9 +1,10 @@ class Puppet::Network::Server - attr_reader :server_type, :http_server + attr_reader :server_type, :http_server_class, :protocols 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) + @protocols = [] @listening = false @routes = {} self.register(args[:handlers]) if args[:handlers] @@ -33,26 +34,24 @@ class Puppet::Network::Server def listen raise "Cannot listen -- already listening." if listening? - initialize_http_server - self.http_server.listen(@routes.dup) + http_server.listen(@routes.dup) @listening = true end def unlisten raise "Cannot unlisten -- not currently listening." unless listening? - self.http_server.unlisten + http_server.unlisten @listening = false end private - def initialize_http_server - @server = @http_server_class.new + def http_server + @http_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 + Puppet::Network::HTTP.server_class_by_type(kind) end end diff --git a/spec/unit/network/http.rb b/spec/unit/network/http.rb index 50ef92866..c488411ff 100644 --- a/spec/unit/network/http.rb +++ b/spec/unit/network/http.rb @@ -8,6 +8,15 @@ require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/network/http' describe Puppet::Network::HTTP do - it "should require a server type when initializing" - it "should return an instance of the http server class corresponding to the server type" + it "should return the webrick HTTP server class when asked for a webrick server" do + Puppet::Network::HTTP.server_class_by_type(:webrick).should be(Puppet::Network::HTTP::WEBRick) + end + + it "should return the mongrel HTTP server class when asked for a mongrel server" do + Puppet::Network::HTTP.server_class_by_type(:mongrel).should be(Puppet::Network::HTTP::Mongrel) + end + + it "should return an error when asked for an unknown server" do + Proc.new { Puppet::Network::HTTP.server_class_by_type :foo }.should raise_error(ArgumentError) + end end \ No newline at end of file diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb index a23f22058..b2994c6f9 100644 --- a/spec/unit/network/server.rb +++ b/spec/unit/network/server.rb @@ -7,15 +7,13 @@ require File.dirname(__FILE__) + '/../../spec_helper' 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 -end describe Puppet::Network::Server, "when initializing" do before do - Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer) + @mock_http_server_class = mock('http server class') + Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) 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 @@ -26,6 +24,13 @@ describe Puppet::Network::Server, "when initializing" do Puppet.expects(:[]).with(:servertype).returns(nil) Proc.new { Puppet::Network::Server.new }.should raise_error end + + it "should ask the Puppet::Network::HTTP class to fetch the proper HTTP server class" do + Puppet.stubs(:[]).with(:servertype).returns(:suparserver) + mock_http_server_class = mock('http server class') + Puppet::Network::HTTP.expects(:server_class_by_type).with(:suparserver).returns(mock_http_server_class) + @server = Puppet::Network::Server.new + end it "should allow registering indirections" do @server = Puppet::Network::Server.new(:handlers => [ :foo, :bar, :baz]) @@ -39,7 +44,8 @@ end describe Puppet::Network::Server, "in general" do before do - Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer) + @mock_http_server_class = mock('http server class') + Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) Puppet.stubs(:[]).with(:servertype).returns(:suparserver) @server = Puppet::Network::Server.new end @@ -105,8 +111,6 @@ describe Puppet::Network::Server, "in general" 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) @@ -117,42 +121,40 @@ describe Puppet::Network::Server, "in general" do Proc.new { @server2.unregister(:bar) }.should raise_error(ArgumentError) end - it "should provide a means of determining which style of service is being offered to clients" + it "should provide a means of determining which style of service is being offered to clients" do + @server.protocols.should == [] + end end describe Puppet::Network::Server, "when listening is off" do before do - Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer) + @mock_http_server_class = mock('http server class') + Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) 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 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 indicate that it is not listening" do + @server.should_not be_listening + 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 + it "should allow listening to be turned on" do + Proc.new { @server.listen }.should_not raise_error + end + end describe Puppet::Network::Server, "when listening is on" do before do - Puppet::Network::Server.stubs(:server_class_by_name).returns(TestServer) + @mock_http_server_class = mock('http server class') + Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) Puppet.stubs(:[]).with(:servertype).returns(:suparserver) @server = Puppet::Network::Server.new @mock_http_server = mock('http server') @@ -160,29 +162,64 @@ describe Puppet::Network::Server, "when listening is on" do @mock_http_server.stubs(:unlisten) @server.stubs(:http_server).returns(@mock_http_server) @server.listen - @server.register(:foo) + end + + it "should indicate that listening is turned off" do + @server.should be_listening + end + + it "should not allow listening to be turned on" do + Proc.new { @server.listen }.should raise_error(RuntimeError) end it "should allow listening to be turned off" do Proc.new { @server.unlisten }.should_not raise_error 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 + +describe Puppet::Network::Server, "when listening is being turned on" do + before do + @mock_http_server_class = mock('http server class') + Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) + Puppet.stubs(:[]).with(:servertype).returns(:suparserver) + @server = Puppet::Network::Server.new + @mock_http_server = mock('http server') + @mock_http_server.stubs(:listen) end - it "should not allow listening to be turned on" do - Proc.new { @server.listen }.should raise_error(RuntimeError) + it "should fetch an instance of an HTTP server when listening is turned on" do + mock_http_server_class = mock('http server class') + mock_http_server_class.expects(:new).returns(@mock_http_server) + @server.expects(:http_server_class).returns(mock_http_server_class) + @server.listen + end + + it "should cause the HTTP server to listen when listening is turned on" do + @mock_http_server.expects(:listen) + @server.expects(:http_server).returns(@mock_http_server) + @server.listen + end +end + +describe Puppet::Network::Server, "when listening is being turned off" do + before do + @mock_http_server_class = mock('http server class') + Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) + 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) + @server.listen end - it "should indicate that listening is turned off" do - @server.should be_listening + it "should cause the HTTP server to stop listening when listening is turned off" do + @mock_http_server.expects(:unlisten) + @server.unlisten end - + it "should not allow for indirections to be turned off" do + @server.register(:foo) Proc.new { @server.unregister(:foo) }.should raise_error(RuntimeError) end end -- cgit From ec71e05a162ec299982b90707cc16231c608997b Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Mon, 15 Oct 2007 12:04:30 -0500 Subject: More unit specs for mongrel and webrick; more code to make them work, yo. --- lib/puppet/network/http.rb | 4 +-- lib/puppet/network/http/mongrel.rb | 19 ++++++++++++++ lib/puppet/network/http/webrick.rb | 21 ++++++++++++++- spec/unit/network/http/mongrel.rb | 53 ++++++++++++++++++++++++++++++++++++++ spec/unit/network/http/webrick.rb | 52 +++++++++++++++++++++++++++++++++++++ spec/unit/network/server.rb | 24 +---------------- 6 files changed, 147 insertions(+), 26 deletions(-) create mode 100644 spec/unit/network/http/mongrel.rb create mode 100644 spec/unit/network/http/webrick.rb diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb index 86784e50e..5dddad848 100644 --- a/lib/puppet/network/http.rb +++ b/lib/puppet/network/http.rb @@ -1,7 +1,7 @@ class Puppet::Network::HTTP def self.server_class_by_type(kind) - return Puppet::Network::HTTP::WEBRick if kind == :webrick - return Puppet::Network::HTTP::Mongrel if kind == :mongrel + return Puppet::Network::HTTP::WEBRick if kind.to_sym == :webrick + return Puppet::Network::HTTP::Mongrel if kind.to_sym == :mongrel raise ArgumentError, "Unknown HTTP server name [#{kind}]" end end diff --git a/lib/puppet/network/http/mongrel.rb b/lib/puppet/network/http/mongrel.rb index dda3c1751..dbdd72d42 100644 --- a/lib/puppet/network/http/mongrel.rb +++ b/lib/puppet/network/http/mongrel.rb @@ -1,2 +1,21 @@ +require 'mongrel' + class Puppet::Network::HTTP::Mongrel + def initialize(args = {}) + @listening = false + end + + def listen(args = {}) + raise ArgumentError if args.keys.empty? + raise "Mongrel server is already listening" if @listening + @server = Mongrel::HttpServer.new("0.0.0.0", "3000") + @server.run + @listening = true + end + + def unlisten + raise "Mongrel server is not listening" unless @listening + @server.graceful_shutdown + @listening = false + end end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index ad15261f6..77e55a224 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -1,2 +1,21 @@ -class Puppet::Network::HTTP::WEBRick +require 'webrick' +require 'webrick/https' + +class Puppet::Network::HTTP::WEBRick < WEBrick::HTTPServer + def initialize(args = {}) + @listening = false + end + + def listen(args = {}) + raise ArgumentError if args.keys.empty? + raise "WEBRick server is already listening" if @listening + # TODO / FIXME: this should be moved out of the wacky Puppet global namespace! + Puppet.start + @listening = true + end + + def unlisten + raise "WEBRick server is not listening" unless @listening + shutdown + end end diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb new file mode 100644 index 000000000..3e40efe79 --- /dev/null +++ b/spec/unit/network/http/mongrel.rb @@ -0,0 +1,53 @@ +#!/usr/bin/env ruby +# +# Created by Rick Bradley on 2007-10-15. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/network/http' + +describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do + before do + @server = Puppet::Network::HTTP::Mongrel.new + end + + it "should fail if already listening" do + @server.listen(:foo => :bar) + Proc.new { @server.listen(:foo => :bar) }.should raise_error(RuntimeError) + end + + it "should require at least one handler" do + Proc.new { @server.listen }.should raise_error(ArgumentError) + end + + it "should order a mongrel server to start" do + mock_mongrel = mock('mongrel httpserver') + mock_mongrel.expects(:run) + Mongrel::HttpServer.expects(:new).returns(mock_mongrel) + @server.listen(:foo => :bar) + end + + it "should instantiate a specific handler (mongrel+rest, e.g.) for each handler, for each protocol being served (xmlrpc, rest, etc.)" + it "should mount handlers on a mongrel path" + it "should be able to specify the address on which mongrel will listen" + it "should be able to specify the port on which mongrel will listen" +end + +describe Puppet::Network::HTTP::WEBRick, "when turning off listening" do + before do + @mock_mongrel = mock('mongrel httpserver') + @mock_mongrel.stubs(:run) + Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) + @server = Puppet::Network::HTTP::Mongrel.new + end + + it "should fail unless listening" do + Proc.new { @server.unlisten }.should raise_error(RuntimeError) + end + + it "should order mongrel server to stop" do + @server.listen(:foo => :bar) + @mock_mongrel.expects(:graceful_shutdown) + @server.unlisten + end +end diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb new file mode 100644 index 000000000..d162288fc --- /dev/null +++ b/spec/unit/network/http/webrick.rb @@ -0,0 +1,52 @@ +#!/usr/bin/env ruby +# +# Created by Rick Bradley on 2007-10-15. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet/network/http' + +describe Puppet::Network::HTTP::WEBRick, "when turning on listening" do + before do + @server = Puppet::Network::HTTP::WEBRick.new + end + + it "should fail if already listening" do + Puppet.stubs(:start) + @server.listen(:foo => :bar) + Proc.new { @server.listen(:foo => :bar) }.should raise_error(RuntimeError) + end + + it "should require at least one handler" do + Proc.new { @server.listen }.should raise_error(ArgumentError) + end + + it "should order a webrick server to start" do + Puppet.expects(:start) + @server.listen(:foo => :bar) + end + + it "should instantiate a specific handler (webrick+rest, e.g.) for each handler, for each protocol being served (xmlrpc, rest, etc.)" + it "should mount handlers on a webrick path" + + it "should be able to specify the address on which webrick will listen" + it "should be able to specify the port on which webrick will listen" +end + +describe Puppet::Network::HTTP::WEBRick, "when turning off listening" do + before do + @server = Puppet::Network::HTTP::WEBRick.new + end + + it "should fail unless listening" do + Proc.new { @server.unlisten }.should raise_error(RuntimeError) + end + + it "should order webrick server to stop" do + Puppet.stubs(:start).returns(true) + @server.should respond_to(:shutdown) + @server.expects(:shutdown) + @server.listen(:foo => :bar) + @server.unlisten + end +end diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb index b2994c6f9..f5d5e25d5 100644 --- a/spec/unit/network/server.rb +++ b/spec/unit/network/server.rb @@ -4,10 +4,8 @@ # Copyright (c) 2007. All rights reserved. require File.dirname(__FILE__) + '/../../spec_helper' - require 'puppet/network/server' - describe Puppet::Network::Server, "when initializing" do before do @mock_http_server_class = mock('http server class') @@ -212,7 +210,7 @@ describe Puppet::Network::Server, "when listening is being turned off" do @server.stubs(:http_server).returns(@mock_http_server) @server.listen end - + it "should cause the HTTP server to stop listening when listening is turned off" do @mock_http_server.expects(:unlisten) @server.unlisten @@ -224,26 +222,6 @@ describe Puppet::Network::Server, "when listening is being turned off" do 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 - -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" -- cgit From e56406f15086eb483c00a2904d8a75518412a905 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Mon, 15 Oct 2007 12:16:48 -0500 Subject: Implementing listening state tracking for webrick and mongrel. --- lib/puppet/network/http/mongrel.rb | 8 ++++++-- lib/puppet/network/http/webrick.rb | 9 +++++++-- spec/unit/network/http/mongrel.rb | 21 +++++++++++++++++++++ spec/unit/network/http/webrick.rb | 22 ++++++++++++++++++++-- 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/lib/puppet/network/http/mongrel.rb b/lib/puppet/network/http/mongrel.rb index dbdd72d42..49449cf59 100644 --- a/lib/puppet/network/http/mongrel.rb +++ b/lib/puppet/network/http/mongrel.rb @@ -7,15 +7,19 @@ class Puppet::Network::HTTP::Mongrel def listen(args = {}) raise ArgumentError if args.keys.empty? - raise "Mongrel server is already listening" if @listening + raise "Mongrel server is already listening" if listening? @server = Mongrel::HttpServer.new("0.0.0.0", "3000") @server.run @listening = true end def unlisten - raise "Mongrel server is not listening" unless @listening + raise "Mongrel server is not listening" unless listening? @server.graceful_shutdown @listening = false end + + def listening? + @listening + end end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index 77e55a224..ffea60eba 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -8,14 +8,19 @@ class Puppet::Network::HTTP::WEBRick < WEBrick::HTTPServer def listen(args = {}) raise ArgumentError if args.keys.empty? - raise "WEBRick server is already listening" if @listening + raise "WEBRick server is already listening" if listening? # TODO / FIXME: this should be moved out of the wacky Puppet global namespace! Puppet.start @listening = true end def unlisten - raise "WEBRick server is not listening" unless @listening + raise "WEBRick server is not listening" unless listening? shutdown + @listening = false + end + + def listening? + @listening end end diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb index 3e40efe79..f591a7fe2 100644 --- a/spec/unit/network/http/mongrel.rb +++ b/spec/unit/network/http/mongrel.rb @@ -6,6 +6,12 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/network/http' +describe Puppet::Network::HTTP::Mongrel, "after initializing" do + it "should not be listening" do + Puppet::Network::HTTP::Mongrel.new.should_not be_listening + end +end + describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do before do @server = Puppet::Network::HTTP::Mongrel.new @@ -26,6 +32,14 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do Mongrel::HttpServer.expects(:new).returns(mock_mongrel) @server.listen(:foo => :bar) end + + it "should be listening" do + mock_mongrel = mock('mongrel httpserver') + mock_mongrel.expects(:run) + Mongrel::HttpServer.expects(:new).returns(mock_mongrel) + @server.listen(:foo => :bar) + @server.should be_listening + end it "should instantiate a specific handler (mongrel+rest, e.g.) for each handler, for each protocol being served (xmlrpc, rest, etc.)" it "should mount handlers on a mongrel path" @@ -37,6 +51,7 @@ describe Puppet::Network::HTTP::WEBRick, "when turning off listening" do before do @mock_mongrel = mock('mongrel httpserver') @mock_mongrel.stubs(:run) + @mock_mongrel.stubs(:graceful_shutdown) Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) @server = Puppet::Network::HTTP::Mongrel.new end @@ -50,4 +65,10 @@ describe Puppet::Network::HTTP::WEBRick, "when turning off listening" do @mock_mongrel.expects(:graceful_shutdown) @server.unlisten end + + it "should not be listening" do + @server.listen(:foo => :bar) + @server.unlisten + @server.should_not be_listening + end end diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb index d162288fc..f414ca11d 100644 --- a/spec/unit/network/http/webrick.rb +++ b/spec/unit/network/http/webrick.rb @@ -6,13 +6,19 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/network/http' +describe Puppet::Network::HTTP::WEBRick, "after initializing" do + it "should not be listening" do + Puppet::Network::HTTP::WEBRick.new.should_not be_listening + end +end + describe Puppet::Network::HTTP::WEBRick, "when turning on listening" do before do @server = Puppet::Network::HTTP::WEBRick.new + Puppet.stubs(:start) end it "should fail if already listening" do - Puppet.stubs(:start) @server.listen(:foo => :bar) Proc.new { @server.listen(:foo => :bar) }.should raise_error(RuntimeError) end @@ -25,6 +31,11 @@ describe Puppet::Network::HTTP::WEBRick, "when turning on listening" do Puppet.expects(:start) @server.listen(:foo => :bar) end + + it "should be listening" do + @server.listen(:foo => :bar) + @server.should be_listening + end it "should instantiate a specific handler (webrick+rest, e.g.) for each handler, for each protocol being served (xmlrpc, rest, etc.)" it "should mount handlers on a webrick path" @@ -36,6 +47,8 @@ end describe Puppet::Network::HTTP::WEBRick, "when turning off listening" do before do @server = Puppet::Network::HTTP::WEBRick.new + @server.stubs(:shutdown) + Puppet.stubs(:start).returns(true) end it "should fail unless listening" do @@ -43,10 +56,15 @@ describe Puppet::Network::HTTP::WEBRick, "when turning off listening" do end it "should order webrick server to stop" do - Puppet.stubs(:start).returns(true) @server.should respond_to(:shutdown) @server.expects(:shutdown) @server.listen(:foo => :bar) @server.unlisten end + + it "should no longer be listening" do + @server.listen(:foo => :bar) + @server.unlisten + @server.should_not be_listening + end end -- cgit From 9a179ec3a9df62c6179e7151831c4f07197cfbce Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Mon, 15 Oct 2007 12:19:08 -0500 Subject: trivial: WEBRick -> WEBrick, to be more consistent with how the WEBrick ruby classes are named. --- lib/puppet/network/http.rb | 2 +- lib/puppet/network/http/webrick.rb | 6 +++--- spec/unit/network/http.rb | 2 +- spec/unit/network/http/mongrel.rb | 2 +- spec/unit/network/http/webrick.rb | 12 ++++++------ 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb index 5dddad848..044310d8e 100644 --- a/lib/puppet/network/http.rb +++ b/lib/puppet/network/http.rb @@ -1,6 +1,6 @@ class Puppet::Network::HTTP def self.server_class_by_type(kind) - return Puppet::Network::HTTP::WEBRick if kind.to_sym == :webrick + return Puppet::Network::HTTP::WEBrick if kind.to_sym == :webrick return Puppet::Network::HTTP::Mongrel if kind.to_sym == :mongrel raise ArgumentError, "Unknown HTTP server name [#{kind}]" end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index ffea60eba..85a329454 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -1,21 +1,21 @@ require 'webrick' require 'webrick/https' -class Puppet::Network::HTTP::WEBRick < WEBrick::HTTPServer +class Puppet::Network::HTTP::WEBrick < WEBrick::HTTPServer def initialize(args = {}) @listening = false end def listen(args = {}) raise ArgumentError if args.keys.empty? - raise "WEBRick server is already listening" if listening? + raise "WEBrick server is already listening" if listening? # TODO / FIXME: this should be moved out of the wacky Puppet global namespace! Puppet.start @listening = true end def unlisten - raise "WEBRick server is not listening" unless listening? + raise "WEBrick server is not listening" unless listening? shutdown @listening = false end diff --git a/spec/unit/network/http.rb b/spec/unit/network/http.rb index c488411ff..450d15487 100644 --- a/spec/unit/network/http.rb +++ b/spec/unit/network/http.rb @@ -9,7 +9,7 @@ require 'puppet/network/http' describe Puppet::Network::HTTP do it "should return the webrick HTTP server class when asked for a webrick server" do - Puppet::Network::HTTP.server_class_by_type(:webrick).should be(Puppet::Network::HTTP::WEBRick) + Puppet::Network::HTTP.server_class_by_type(:webrick).should be(Puppet::Network::HTTP::WEBrick) end it "should return the mongrel HTTP server class when asked for a mongrel server" do diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb index f591a7fe2..3456a16d6 100644 --- a/spec/unit/network/http/mongrel.rb +++ b/spec/unit/network/http/mongrel.rb @@ -47,7 +47,7 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do it "should be able to specify the port on which mongrel will listen" end -describe Puppet::Network::HTTP::WEBRick, "when turning off listening" do +describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do before do @mock_mongrel = mock('mongrel httpserver') @mock_mongrel.stubs(:run) diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb index f414ca11d..b070ec0d6 100644 --- a/spec/unit/network/http/webrick.rb +++ b/spec/unit/network/http/webrick.rb @@ -6,15 +6,15 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/network/http' -describe Puppet::Network::HTTP::WEBRick, "after initializing" do +describe Puppet::Network::HTTP::WEBrick, "after initializing" do it "should not be listening" do - Puppet::Network::HTTP::WEBRick.new.should_not be_listening + Puppet::Network::HTTP::WEBrick.new.should_not be_listening end end -describe Puppet::Network::HTTP::WEBRick, "when turning on listening" do +describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do before do - @server = Puppet::Network::HTTP::WEBRick.new + @server = Puppet::Network::HTTP::WEBrick.new Puppet.stubs(:start) end @@ -44,9 +44,9 @@ describe Puppet::Network::HTTP::WEBRick, "when turning on listening" do it "should be able to specify the port on which webrick will listen" end -describe Puppet::Network::HTTP::WEBRick, "when turning off listening" do +describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do before do - @server = Puppet::Network::HTTP::WEBRick.new + @server = Puppet::Network::HTTP::WEBrick.new @server.stubs(:shutdown) Puppet.stubs(:start).returns(true) end -- cgit From c34efbccf1eec9957253d4fcdcb4ea9c79837ad8 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Mon, 15 Oct 2007 14:47:25 -0500 Subject: Hooking up address/port support for the various servers w/ specs. Still need to start up a webrick server w/ address + port (this is far too incestuous with Puppet lib & Puppet.start at the moment). --- lib/puppet/network/http/mongrel.rb | 6 ++-- lib/puppet/network/http/webrick.rb | 5 ++- lib/puppet/network/server.rb | 6 +++- spec/unit/network/http/mongrel.rb | 43 +++++++++++++++-------- spec/unit/network/http/webrick.rb | 29 ++++++++++------ spec/unit/network/server.rb | 70 +++++++++++++++++++++++++++++++------- 6 files changed, 119 insertions(+), 40 deletions(-) diff --git a/lib/puppet/network/http/mongrel.rb b/lib/puppet/network/http/mongrel.rb index 49449cf59..bbba69fe3 100644 --- a/lib/puppet/network/http/mongrel.rb +++ b/lib/puppet/network/http/mongrel.rb @@ -6,9 +6,11 @@ class Puppet::Network::HTTP::Mongrel end def listen(args = {}) - raise ArgumentError if args.keys.empty? + raise ArgumentError, ":handlers must be specified." if !args[:handlers] or args[:handlers].keys.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? - @server = Mongrel::HttpServer.new("0.0.0.0", "3000") + @server = Mongrel::HttpServer.new(args[:address], args[:port]) @server.run @listening = true end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index 85a329454..c22bce938 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -7,8 +7,11 @@ class Puppet::Network::HTTP::WEBrick < WEBrick::HTTPServer end def listen(args = {}) - raise ArgumentError if args.keys.empty? + raise ArgumentError, ":handlers must be specified." if !args[:handlers] or args[:handlers].keys.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? + # TODO / FIXME: this should be moved out of the wacky Puppet global namespace! Puppet.start @listening = true diff --git a/lib/puppet/network/server.rb b/lib/puppet/network/server.rb index 941cb9df1..0541c1c3b 100644 --- a/lib/puppet/network/server.rb +++ b/lib/puppet/network/server.rb @@ -1,9 +1,13 @@ class Puppet::Network::Server - attr_reader :server_type, :http_server_class, :protocols + attr_reader :server_type, :http_server_class, :protocols, :address, :port 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) + @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 = {} diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb index 3456a16d6..f964e6844 100644 --- a/spec/unit/network/http/mongrel.rb +++ b/spec/unit/network/http/mongrel.rb @@ -15,45 +15,59 @@ end describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do before do @server = Puppet::Network::HTTP::Mongrel.new + @mock_mongrel = mock('mongrel') + @mock_mongrel.stubs(:run) + Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) + @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => { :foo => :bar }} end it "should fail if already listening" do - @server.listen(:foo => :bar) - Proc.new { @server.listen(:foo => :bar) }.should raise_error(RuntimeError) + @server.listen(@listen_params) + Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError) end it "should require at least one handler" do - Proc.new { @server.listen }.should raise_error(ArgumentError) + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :handlers == k}) }.should raise_error(ArgumentError) + end + + it "should require a listening address to be specified" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) + end + + it "should require a listening port to be specified" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError) end it "should order a mongrel server to start" do - mock_mongrel = mock('mongrel httpserver') - mock_mongrel.expects(:run) - Mongrel::HttpServer.expects(:new).returns(mock_mongrel) - @server.listen(:foo => :bar) + @mock_mongrel.expects(:run) + @server.listen(@listen_params) + end + + it "should tell mongrel to listen on the specified address and port" do + Mongrel::HttpServer.expects(:new).with("127.0.0.1", 31337).returns(@mock_mongrel) + @server.listen(@listen_params) end it "should be listening" do mock_mongrel = mock('mongrel httpserver') mock_mongrel.expects(:run) Mongrel::HttpServer.expects(:new).returns(mock_mongrel) - @server.listen(:foo => :bar) + @server.listen(@listen_params) @server.should be_listening end it "should instantiate a specific handler (mongrel+rest, e.g.) for each handler, for each protocol being served (xmlrpc, rest, etc.)" it "should mount handlers on a mongrel path" - it "should be able to specify the address on which mongrel will listen" - it "should be able to specify the port on which mongrel will listen" end -describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do +describe Puppet::Network::HTTP::Mongrel, "when turning off listening" do before do @mock_mongrel = mock('mongrel httpserver') @mock_mongrel.stubs(:run) - @mock_mongrel.stubs(:graceful_shutdown) Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) + @server = Puppet::Network::HTTP::Mongrel.new + @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => { :foo => :bar }} end it "should fail unless listening" do @@ -61,13 +75,14 @@ describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do end it "should order mongrel server to stop" do - @server.listen(:foo => :bar) + @server.listen(@listen_params) @mock_mongrel.expects(:graceful_shutdown) @server.unlisten end it "should not be listening" do - @server.listen(:foo => :bar) + @server.listen(@listen_params) + @mock_mongrel.stubs(:graceful_shutdown) @server.unlisten @server.should_not be_listening end diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb index b070ec0d6..9ab97e831 100644 --- a/spec/unit/network/http/webrick.rb +++ b/spec/unit/network/http/webrick.rb @@ -15,39 +15,48 @@ end describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do before do @server = Puppet::Network::HTTP::WEBrick.new + @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => { :foo => :bar }} Puppet.stubs(:start) end it "should fail if already listening" do - @server.listen(:foo => :bar) - Proc.new { @server.listen(:foo => :bar) }.should raise_error(RuntimeError) + @server.listen(@listen_params) + Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError) end it "should require at least one handler" do - Proc.new { @server.listen }.should raise_error(ArgumentError) + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :handlers == k}) }.should raise_error(ArgumentError) end + it "should require a listening address to be specified" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) + end + + it "should require a listening port to be specified" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError) + end + it "should order a webrick server to start" do Puppet.expects(:start) - @server.listen(:foo => :bar) + @server.listen(@listen_params) end + it "should tell webrick to listen on the specified address and port" + it "should be listening" do - @server.listen(:foo => :bar) + @server.listen(@listen_params) @server.should be_listening end it "should instantiate a specific handler (webrick+rest, e.g.) for each handler, for each protocol being served (xmlrpc, rest, etc.)" it "should mount handlers on a webrick path" - - it "should be able to specify the address on which webrick will listen" - it "should be able to specify the port on which webrick will listen" end describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do before do @server = Puppet::Network::HTTP::WEBrick.new @server.stubs(:shutdown) + @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => { :foo => :bar }} Puppet.stubs(:start).returns(true) end @@ -58,12 +67,12 @@ describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do it "should order webrick server to stop" do @server.should respond_to(:shutdown) @server.expects(:shutdown) - @server.listen(:foo => :bar) + @server.listen(@listen_params) @server.unlisten end it "should no longer be listening" do - @server.listen(:foo => :bar) + @server.listen(@listen_params) @server.unlisten @server.should_not be_listening end diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb index f5d5e25d5..6802c6aaa 100644 --- a/spec/unit/network/server.rb +++ b/spec/unit/network/server.rb @@ -10,33 +10,71 @@ describe Puppet::Network::Server, "when initializing" do before do @mock_http_server_class = mock('http server class') Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) + Puppet.stubs(:[]).with(:servertype).returns(:suparserver) + end + + it "should allow specifying a listening address" do + Puppet.stubs(:[]).with(:masterport).returns('') + @server = Puppet::Network::Server.new(:address => "127.0.0.1") + @server.address.should == "127.0.0.1" + end + + it "should allow specifying a listening port" do + Puppet.stubs(:[]).with(:bindaddress).returns('') + @server = Puppet::Network::Server.new(:port => 31337) + @server.port.should == 31337 + end + + it "should use the Puppet configurator to find a default listening address" do + Puppet.stubs(:[]).with(:masterport).returns('') + Puppet.expects(:[]).with(:bindaddress).returns("10.0.0.1") + @server = Puppet::Network::Server.new + @server.address.should == "10.0.0.1" + end + + it "should use the Puppet configurator to find a default listening port" do + Puppet.stubs(:[]).with(:bindaddress).returns('') + Puppet.expects(:[]).with(:masterport).returns(6667) + @server = Puppet::Network::Server.new + @server.port.should == 6667 + end + + it "should fail to initialize if no listening address can be found" do + Puppet.stubs(:[]).with(:masterport).returns(6667) + Puppet.stubs(:[]).with(:bindaddress).returns(nil) + Proc.new { Puppet::Network::Server.new }.should raise_error(ArgumentError) end + it "should fail to initialize if no listening port can be found" do + Puppet.stubs(:[]).with(:bindaddress).returns("127.0.0.1") + Puppet.stubs(:[]).with(:masterport).returns(nil) + Proc.new { Puppet::Network::Server.new }.should raise_error(ArgumentError) + 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 = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) @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 + Proc.new { Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) }.should raise_error end it "should ask the Puppet::Network::HTTP class to fetch the proper HTTP server class" do - Puppet.stubs(:[]).with(:servertype).returns(:suparserver) mock_http_server_class = mock('http server class') Puppet::Network::HTTP.expects(:server_class_by_type).with(:suparserver).returns(mock_http_server_class) - @server = Puppet::Network::Server.new + @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) end it "should allow registering indirections" do - @server = Puppet::Network::Server.new(:handlers => [ :foo, :bar, :baz]) + @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337, :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 + Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337).should_not be_listening end end @@ -45,7 +83,7 @@ describe Puppet::Network::Server, "in general" do @mock_http_server_class = mock('http server class') Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) Puppet.stubs(:[]).with(:servertype).returns(:suparserver) - @server = Puppet::Network::Server.new + @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) end it "should allow registering an indirection for client access by specifying its indirection name" do @@ -110,7 +148,7 @@ describe Puppet::Network::Server, "in general" do end it "should allow for multiple configurations, each handling different indirections" do - @server2 = Puppet::Network::Server.new + @server2 = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) @server.register(:foo, :bar) @server2.register(:foo, :xyzzy) @server.unregister(:foo, :bar) @@ -122,6 +160,14 @@ describe Puppet::Network::Server, "in general" do it "should provide a means of determining which style of service is being offered to clients" do @server.protocols.should == [] end + + it "should provide a means of determining the listening address" do + @server.address.should == "127.0.0.1" + end + + it "should provide a means of determining the listening port" do + @server.port.should == 31337 + end end describe Puppet::Network::Server, "when listening is off" do @@ -129,7 +175,7 @@ describe Puppet::Network::Server, "when listening is off" do @mock_http_server_class = mock('http server class') Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) Puppet.stubs(:[]).with(:servertype).returns(:suparserver) - @server = Puppet::Network::Server.new + @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) @server.stubs(:http_server).returns(@mock_http_server) @@ -154,7 +200,7 @@ describe Puppet::Network::Server, "when listening is on" do @mock_http_server_class = mock('http server class') Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) Puppet.stubs(:[]).with(:servertype).returns(:suparserver) - @server = Puppet::Network::Server.new + @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) @mock_http_server.stubs(:unlisten) @@ -180,7 +226,7 @@ describe Puppet::Network::Server, "when listening is being turned on" do @mock_http_server_class = mock('http server class') Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) Puppet.stubs(:[]).with(:servertype).returns(:suparserver) - @server = Puppet::Network::Server.new + @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) end @@ -204,7 +250,7 @@ describe Puppet::Network::Server, "when listening is being turned off" do @mock_http_server_class = mock('http server class') Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) Puppet.stubs(:[]).with(:servertype).returns(:suparserver) - @server = Puppet::Network::Server.new + @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) @server.stubs(:http_server).returns(@mock_http_server) -- cgit From ef8ebe0df4da0a0cd2f599308f40bd707ab18d92 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Mon, 15 Oct 2007 15:04:10 -0500 Subject: Implementing address & port support for new webrick server. --- lib/puppet/network/http/webrick.rb | 8 ++++++-- spec/unit/network/http/webrick.rb | 17 ++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index c22bce938..b74bf441e 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -1,7 +1,7 @@ require 'webrick' require 'webrick/https' -class Puppet::Network::HTTP::WEBrick < WEBrick::HTTPServer +class Puppet::Network::HTTP::WEBrick def initialize(args = {}) @listening = false end @@ -12,8 +12,12 @@ class Puppet::Network::HTTP::WEBrick < WEBrick::HTTPServer raise ArgumentError, ":port must be specified." unless args[:port] raise "WEBrick server is already listening" if listening? - # TODO / FIXME: this should be moved out of the wacky Puppet global namespace! + @server = WEBrick::HTTPServer.new(:BindAddress => args[:address], :Port => args[:port]) + + # TODO / FIXME is this really necessary? -- or can we do it in both mongrel and webrick? + Puppet.newservice(@server) Puppet.start + @listening = true end diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb index 9ab97e831..804be4c33 100644 --- a/spec/unit/network/http/webrick.rb +++ b/spec/unit/network/http/webrick.rb @@ -14,9 +14,12 @@ end describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do before do + Puppet.stubs(:start) + Puppet.stubs(:newservice) + @mock_webrick = mock('webrick') + WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => { :foo => :bar }} - Puppet.stubs(:start) end it "should fail if already listening" do @@ -41,7 +44,12 @@ describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do @server.listen(@listen_params) end - it "should tell webrick to listen on the specified address and port" + it "should tell webrick to listen on the specified address and port" do + WEBrick::HTTPServer.expects(:new).with {|args| + args[:Port] == 31337 and args[:BindAddress] == "127.0.0.1" + }.returns(@mock_webrick) + @server.listen(@listen_params) + end it "should be listening" do @server.listen(@listen_params) @@ -54,10 +62,13 @@ end describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do before do + Puppet.stubs(:start) + Puppet.stubs(:newservice) + @mock_webrick = mock('webrick') + WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new @server.stubs(:shutdown) @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => { :foo => :bar }} - Puppet.stubs(:start).returns(true) end it "should fail unless listening" do -- cgit From ba952029b057cb64cf28d9e4dfb5c78868a4b53f Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Mon, 15 Oct 2007 15:29:00 -0500 Subject: Partial support for building Handlers for all handler-protocol pairs. --- lib/puppet/network/http/mongrel.rb | 23 ++++++++++++++++++++++- lib/puppet/network/http/webrick.rb | 22 +++++++++++++++++++++- spec/unit/network/http/mongrel.rb | 36 +++++++++++++++++++++++++++++++----- spec/unit/network/http/webrick.rb | 23 +++++++++++++++++++---- 4 files changed, 93 insertions(+), 11 deletions(-) diff --git a/lib/puppet/network/http/mongrel.rb b/lib/puppet/network/http/mongrel.rb index bbba69fe3..ab1e616b1 100644 --- a/lib/puppet/network/http/mongrel.rb +++ b/lib/puppet/network/http/mongrel.rb @@ -6,10 +6,17 @@ class Puppet::Network::HTTP::Mongrel end def listen(args = {}) - raise ArgumentError, ":handlers must be specified." if !args[:handlers] or args[:handlers].keys.empty? + 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] + + setup_handlers + @server = Mongrel::HttpServer.new(args[:address], args[:port]) @server.run @listening = true @@ -24,4 +31,18 @@ class Puppet::Network::HTTP::Mongrel def listening? @listening end + + private + + def setup_handlers + @protocols.each do |protocol| + @handlers.each do |handler| + class_for_protocol_handler(protocol, handler).new + end + end + end + + def class_for_protocol_handler(protocol, handler) + Class.new + end end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index b74bf441e..00d13437c 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -7,11 +7,17 @@ class Puppet::Network::HTTP::WEBrick end def listen(args = {}) - raise ArgumentError, ":handlers must be specified." if !args[:handlers] or args[:handlers].keys.empty? + 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] + + setup_handlers + @server = WEBrick::HTTPServer.new(:BindAddress => args[:address], :Port => args[:port]) # TODO / FIXME is this really necessary? -- or can we do it in both mongrel and webrick? @@ -30,4 +36,18 @@ class Puppet::Network::HTTP::WEBrick def listening? @listening end + + private + + def setup_handlers + @handlers.each do |handler| + @protocols.each do |protocol| + class_for_protocol_handler(protocol, handler).new + end + end + end + + def class_for_protocol_handler(protocol, handler) + Class.new + end end diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb index f964e6844..6c8b65582 100644 --- a/spec/unit/network/http/mongrel.rb +++ b/spec/unit/network/http/mongrel.rb @@ -18,7 +18,7 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do @mock_mongrel = mock('mongrel') @mock_mongrel.stubs(:run) Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) - @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => { :foo => :bar }} + @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :configuration ], :protocols => [ :rest, :xmlrpc ] } end it "should fail if already listening" do @@ -30,6 +30,10 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :handlers == k}) }.should raise_error(ArgumentError) end + it "should require at least one protocol" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :protocols == k}) }.should raise_error(ArgumentError) + end + it "should require a listening address to be specified" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) end @@ -56,8 +60,31 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do @server.should be_listening end - it "should instantiate a specific handler (mongrel+rest, e.g.) for each handler, for each protocol being served (xmlrpc, rest, etc.)" - it "should mount handlers on a mongrel path" + it "should instantiate a specific handler (mongrel+rest, e.g.) for each named handler, for each named protocol)" do + @listen_params[:handlers].each do |handler| + @listen_params[:protocols].each do |protocol| + mock_handler = mock("handler instance for [#{protocol}]+[#{handler}]") + mock_handler_class = mock("handler class for [#{protocol}]+[#{handler}]") + mock_handler_class.expects(:new).returns(mock_handler) + @server.expects(:class_for_protocol_handler).with(protocol, handler).returns(mock_handler_class) + end + end + @server.listen(@listen_params) + end + + it "should mount each handler on a mongrel path" do + pending "a moment of clarity" + @listen_params[:handlers].each do |handler| + @listen_params[:protocols].each do |protocol| + mock_handler = mock("handler instance for [#{protocol}]+[#{handler}]") + mock_handler_class = mock("handler class for [#{protocol}]+[#{handler}]") + mock_handler_class.stubs(:new).returns(mock_handler) + @server.stubs(:class_for_protocol_handler).with(protocol, handler).returns(mock_handler_class) + # TODO / FIXME : HERE -- need to begin resolving the model behind the indirection + end + end + @server.listen(@listen_params) + end end describe Puppet::Network::HTTP::Mongrel, "when turning off listening" do @@ -65,9 +92,8 @@ describe Puppet::Network::HTTP::Mongrel, "when turning off listening" do @mock_mongrel = mock('mongrel httpserver') @mock_mongrel.stubs(:run) Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) - @server = Puppet::Network::HTTP::Mongrel.new - @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => { :foo => :bar }} + @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :configuration ], :protocols => [ :rest, :xmlrpc ] } end it "should fail unless listening" do diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb index 804be4c33..b94a6a78d 100644 --- a/spec/unit/network/http/webrick.rb +++ b/spec/unit/network/http/webrick.rb @@ -19,7 +19,7 @@ describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do @mock_webrick = mock('webrick') WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new - @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => { :foo => :bar }} + @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :configuration ], :protocols => [ :rest, :xmlrpc ] } end it "should fail if already listening" do @@ -31,6 +31,10 @@ describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :handlers == k}) }.should raise_error(ArgumentError) end + it "should require at least one protocol" do + Proc.new { @server.listen(@listen_params.delete_if {|k,v| :protocols == k}) }.should raise_error(ArgumentError) + end + it "should require a listening address to be specified" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) end @@ -55,8 +59,19 @@ describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do @server.listen(@listen_params) @server.should be_listening end - - it "should instantiate a specific handler (webrick+rest, e.g.) for each handler, for each protocol being served (xmlrpc, rest, etc.)" + + it "should instantiate a specific handler (mongrel+rest, e.g.) for each named handler, for each named protocol)" do + @listen_params[:handlers].each do |handler| + @listen_params[:protocols].each do |protocol| + mock_handler = mock("handler instance for [#{protocol}]+[#{handler}]") + mock_handler_class = mock("handler class for [#{protocol}]+[#{handler}]") + mock_handler_class.expects(:new).returns(mock_handler) + @server.expects(:class_for_protocol_handler).with(protocol, handler).returns(mock_handler_class) + end + end + @server.listen(@listen_params) + end + it "should mount handlers on a webrick path" end @@ -68,7 +83,7 @@ describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new @server.stubs(:shutdown) - @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => { :foo => :bar }} + @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :configuration ], :protocols => [ :rest, :xmlrpc ] } end it "should fail unless listening" do -- cgit From b1d62231c587e13ad78fe1bbd292a6c9f1cb99a1 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Mon, 15 Oct 2007 23:04:24 -0500 Subject: Bringing in initial handlers for server+protocol pairs. --- lib/puppet/network/http/mongrel.rb | 16 ++++++++++------ lib/puppet/network/http/webrick.rb | 15 +++++++++------ spec/unit/network/http/mongrel.rb | 30 +++++++++--------------------- spec/unit/network/http/webrick.rb | 20 ++++++++++---------- spec/unit/network/server.rb | 9 +++++++++ 5 files changed, 47 insertions(+), 43 deletions(-) diff --git a/lib/puppet/network/http/mongrel.rb b/lib/puppet/network/http/mongrel.rb index ab1e616b1..5b14d93c9 100644 --- a/lib/puppet/network/http/mongrel.rb +++ b/lib/puppet/network/http/mongrel.rb @@ -14,10 +14,10 @@ class Puppet::Network::HTTP::Mongrel @protocols = args[:protocols] @handlers = args[:handlers] - - setup_handlers - @server = Mongrel::HttpServer.new(args[:address], args[:port]) + + setup_handlers + @server.run @listening = true end @@ -37,12 +37,16 @@ class Puppet::Network::HTTP::Mongrel def setup_handlers @protocols.each do |protocol| @handlers.each do |handler| - class_for_protocol_handler(protocol, handler).new + class_for_protocol(protocol).new(:server => @server, :handler => handler) end end end - def class_for_protocol_handler(protocol, handler) - Class.new + # TODO/FIXME: need a spec which forces delegation to the real class + def class_for_protocol(protocol) + Class.new do + def initialize(args = {}) + end + end end end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index 00d13437c..474f66e4f 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -14,12 +14,11 @@ class Puppet::Network::HTTP::WEBrick raise "WEBrick server is already listening" if listening? @protocols = args[:protocols] - @handlers = args[:handlers] + @handlers = args[:handlers] + @server = WEBrick::HTTPServer.new(:BindAddress => args[:address], :Port => args[:port]) setup_handlers - @server = WEBrick::HTTPServer.new(:BindAddress => args[:address], :Port => args[:port]) - # TODO / FIXME is this really necessary? -- or can we do it in both mongrel and webrick? Puppet.newservice(@server) Puppet.start @@ -42,12 +41,16 @@ class Puppet::Network::HTTP::WEBrick def setup_handlers @handlers.each do |handler| @protocols.each do |protocol| - class_for_protocol_handler(protocol, handler).new + class_for_protocol(protocol).new(:server => @server, :handler => handler) end end end - def class_for_protocol_handler(protocol, handler) - Class.new + # TODO/FIXME: need a spec which forces delegation to the real class + def class_for_protocol(protocol) + Class.new do + def initialize(args = {}) + end + end end end diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb index 6c8b65582..0990a42d0 100644 --- a/spec/unit/network/http/mongrel.rb +++ b/spec/unit/network/http/mongrel.rb @@ -60,28 +60,16 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do @server.should be_listening end - it "should instantiate a specific handler (mongrel+rest, e.g.) for each named handler, for each named protocol)" do - @listen_params[:handlers].each do |handler| - @listen_params[:protocols].each do |protocol| - mock_handler = mock("handler instance for [#{protocol}]+[#{handler}]") - mock_handler_class = mock("handler class for [#{protocol}]+[#{handler}]") - mock_handler_class.expects(:new).returns(mock_handler) - @server.expects(:class_for_protocol_handler).with(protocol, handler).returns(mock_handler_class) - end - end - @server.listen(@listen_params) - end - - it "should mount each handler on a mongrel path" do - pending "a moment of clarity" - @listen_params[:handlers].each do |handler| - @listen_params[:protocols].each do |protocol| - mock_handler = mock("handler instance for [#{protocol}]+[#{handler}]") - mock_handler_class = mock("handler class for [#{protocol}]+[#{handler}]") - mock_handler_class.stubs(:new).returns(mock_handler) - @server.stubs(:class_for_protocol_handler).with(protocol, handler).returns(mock_handler_class) - # TODO / FIXME : HERE -- need to begin resolving the model behind the indirection + it "should instantiate a handler for each protocol+handler pair to configure web server routing" do + @listen_params[:protocols].each do |protocol| + mock_handler = mock("handler instance for [#{protocol}]") + mock_handler_class = mock("handler class for [#{protocol}]") + @listen_params[:handlers].each do |handler| + mock_handler_class.expects(:new).with {|args| + args[:server] == @mock_mongrel and args[:handler] == handler + }.returns(mock_handler) end + @server.expects(:class_for_protocol).with(protocol).at_least_once.returns(mock_handler_class) end @server.listen(@listen_params) end diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb index b94a6a78d..4d914dc76 100644 --- a/spec/unit/network/http/webrick.rb +++ b/spec/unit/network/http/webrick.rb @@ -60,19 +60,19 @@ describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do @server.should be_listening end - it "should instantiate a specific handler (mongrel+rest, e.g.) for each named handler, for each named protocol)" do - @listen_params[:handlers].each do |handler| - @listen_params[:protocols].each do |protocol| - mock_handler = mock("handler instance for [#{protocol}]+[#{handler}]") - mock_handler_class = mock("handler class for [#{protocol}]+[#{handler}]") - mock_handler_class.expects(:new).returns(mock_handler) - @server.expects(:class_for_protocol_handler).with(protocol, handler).returns(mock_handler_class) + it "should instantiate a handler for each protocol+handler pair to configure web server routing" do + @listen_params[:protocols].each do |protocol| + mock_handler = mock("handler instance for [#{protocol}]") + mock_handler_class = mock("handler class for [#{protocol}]") + @listen_params[:handlers].each do |handler| + mock_handler_class.expects(:new).with {|args| + args[:server] == @mock_webrick and args[:handler] == handler + }.returns(mock_handler) end + @server.expects(:class_for_protocol).with(protocol).at_least_once.returns(mock_handler_class) end - @server.listen(@listen_params) + @server.listen(@listen_params) end - - it "should mount handlers on a webrick path" end describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb index 6802c6aaa..528d47d0e 100644 --- a/spec/unit/network/server.rb +++ b/spec/unit/network/server.rb @@ -268,6 +268,15 @@ describe Puppet::Network::Server, "when listening is being turned off" do end end + + # TODO / FIXME : HERE -- need to begin resolving the model behind the indirection + # looks like: get the handler class, providing @server to it + # have the handler class register the handler @ the right URL + # handler class knows the correct path to use, correct registration method to call + # handler also know how to get the model class from the indirection name + # do we change indirection name to indirection (instead of saying "handlers")? + + 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" -- cgit From 099c5469bf8fd6bf1e65be1a8192c14e584e49c3 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Mon, 15 Oct 2007 23:33:12 -0500 Subject: Finish front end of delegation to server+protocol helper classes ("handlers"). --- lib/puppet/network/http/mongrel.rb | 9 +++++---- lib/puppet/network/http/mongrel/rest.rb | 4 ++++ lib/puppet/network/http/mongrel/xmlrpc.rb | 4 ++++ lib/puppet/network/http/webrick.rb | 10 +++++----- lib/puppet/network/http/webrick/rest.rb | 4 ++++ lib/puppet/network/http/webrick/xmlrpc.rb | 4 ++++ spec/unit/network/http/mongrel.rb | 15 +++++++++++++++ spec/unit/network/http/webrick.rb | 14 ++++++++++++++ 8 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 lib/puppet/network/http/mongrel/rest.rb create mode 100644 lib/puppet/network/http/mongrel/xmlrpc.rb create mode 100644 lib/puppet/network/http/webrick/rest.rb create mode 100644 lib/puppet/network/http/webrick/xmlrpc.rb diff --git a/lib/puppet/network/http/mongrel.rb b/lib/puppet/network/http/mongrel.rb index 5b14d93c9..3efc465ad 100644 --- a/lib/puppet/network/http/mongrel.rb +++ b/lib/puppet/network/http/mongrel.rb @@ -1,4 +1,6 @@ require 'mongrel' +require 'puppet/network/http/mongrel/rest' +require 'puppet/network/http/mongrel/xmlrpc' class Puppet::Network::HTTP::Mongrel def initialize(args = {}) @@ -44,9 +46,8 @@ class Puppet::Network::HTTP::Mongrel # TODO/FIXME: need a spec which forces delegation to the real class def class_for_protocol(protocol) - Class.new do - def initialize(args = {}) - end - end + 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..6e454c7d9 --- /dev/null +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -0,0 +1,4 @@ +class Puppet::Network::HTTP::MongrelREST + def initialize(args = {}) + 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 index 474f66e4f..6df7804c6 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -1,5 +1,7 @@ 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 = {}) @@ -46,11 +48,9 @@ class Puppet::Network::HTTP::WEBrick end end - # TODO/FIXME: need a spec which forces delegation to the real class def class_for_protocol(protocol) - Class.new do - def initialize(args = {}) - end - end + 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..5e9ccfc45 --- /dev/null +++ b/lib/puppet/network/http/webrick/rest.rb @@ -0,0 +1,4 @@ +class Puppet::Network::HTTP::WEBrickREST + def initialize(args = {}) + 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/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb index 0990a42d0..161080109 100644 --- a/spec/unit/network/http/mongrel.rb +++ b/spec/unit/network/http/mongrel.rb @@ -73,6 +73,21 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do end @server.listen(@listen_params) end + + it "should use a Mongrel + REST class to configure Mongrel when REST services are requested" do + Puppet::Network::HTTP::MongrelREST.expects(:new).at_least_once + @server.listen(@listen_params.merge(:protocols => [:rest])) + end + + it "should use a Mongrel + XMLRPC class to configure Mongrel when XMLRPC services are requested" do + Puppet::Network::HTTP::MongrelXMLRPC.expects(:new).at_least_once + @server.listen(@listen_params.merge(:protocols => [:xmlrpc])) + end + + it "should fail if services from an unknown protocol are requested" do + Proc.new { @server.listen(@listen_params.merge(:protocols => [ :foo ]))}.should raise_error(ArgumentError) + end + end describe Puppet::Network::HTTP::Mongrel, "when turning off listening" do diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb index 4d914dc76..81b2a0fa9 100644 --- a/spec/unit/network/http/webrick.rb +++ b/spec/unit/network/http/webrick.rb @@ -73,6 +73,20 @@ describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do end @server.listen(@listen_params) end + + it "should use a WEBrick + REST class to configure WEBrick when REST services are requested" do + Puppet::Network::HTTP::WEBrickREST.expects(:new).at_least_once + @server.listen(@listen_params.merge(:protocols => [:rest])) + end + + it "should use a WEBrick + XMLRPC class to configure WEBrick when XMLRPC services are requested" do + Puppet::Network::HTTP::WEBrickXMLRPC.expects(:new).at_least_once + @server.listen(@listen_params.merge(:protocols => [:xmlrpc])) + end + + it "should fail if services from an unknown protocol are requested" do + Proc.new { @server.listen(@listen_params.merge(:protocols => [ :foo ]))}.should raise_error(ArgumentError) + end end describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do -- cgit From ab4c7fa825e0d1f702adc215c7ff6d445d3b6559 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 00:14:02 -0500 Subject: Minor tweaks to make the ::Server initialization a bit more robust. Fail on unknown HTTP Server types; fail fast. --- lib/puppet/network/server.rb | 8 ++++++-- spec/unit/network/server.rb | 10 +++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/puppet/network/server.rb b/lib/puppet/network/server.rb index 0541c1c3b..50e3bd686 100644 --- a/lib/puppet/network/server.rb +++ b/lib/puppet/network/server.rb @@ -1,9 +1,9 @@ class Puppet::Network::Server - attr_reader :server_type, :http_server_class, :protocols, :address, :port + attr_reader :server_type, :protocols, :address, :port 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) + 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] || @@ -47,6 +47,10 @@ class Puppet::Network::Server http_server.unlisten @listening = false end + + def http_server_class + http_server_class_by_type(@server_type) + end private diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb index 528d47d0e..659eb1930 100644 --- a/spec/unit/network/server.rb +++ b/spec/unit/network/server.rb @@ -9,8 +9,8 @@ require 'puppet/network/server' describe Puppet::Network::Server, "when initializing" do before do @mock_http_server_class = mock('http server class') - Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) Puppet.stubs(:[]).with(:servertype).returns(:suparserver) + Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) end it "should allow specifying a listening address" do @@ -63,10 +63,14 @@ describe Puppet::Network::Server, "when initializing" do end it "should ask the Puppet::Network::HTTP class to fetch the proper HTTP server class" do - mock_http_server_class = mock('http server class') - Puppet::Network::HTTP.expects(:server_class_by_type).with(:suparserver).returns(mock_http_server_class) + Puppet::Network::HTTP.expects(:server_class_by_type).with(:suparserver).returns(@mock_http_server_class) @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) end + + it "should fail if the HTTP server class is unknown" do + Puppet::Network::HTTP.stubs(:server_class_by_type).returns(nil) + Proc.new { Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337) }.should raise_error(ArgumentError) + end it "should allow registering indirections" do @server = Puppet::Network::Server.new(:address => "127.0.0.1", :port => 31337, :handlers => [ :foo, :bar, :baz]) -- cgit From c06edda4c94ef9aa685ed44d7031bb39c4a2b0cc Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 10:15:14 -0500 Subject: First pass through initializers of {mongrel, webrick} REST handlers; hooks into Indirection to look up models from indirected names. --- lib/puppet/indirector/indirection.rb | 8 ++++++ lib/puppet/network/http/mongrel.rb | 4 +-- lib/puppet/network/http/mongrel/rest.rb | 14 ++++++++++ lib/puppet/network/http/webrick.rb | 6 ++--- lib/puppet/network/http/webrick/rest.rb | 14 ++++++++++ spec/unit/indirector/indirection.rb | 14 ++++++++-- spec/unit/network/http/mongrel.rb | 8 +++--- spec/unit/network/http/mongrel/rest.rb | 46 ++++++++++++++++++++++++++++++++ spec/unit/network/http/mongrel/xmlrpc.rb | 0 spec/unit/network/http/webrick.rb | 8 +++--- spec/unit/network/http/webrick/rest.rb | 46 ++++++++++++++++++++++++++++++++ spec/unit/network/http/webrick/xmlrpc.rb | 0 spec/unit/network/server.rb | 17 +----------- 13 files changed, 150 insertions(+), 35 deletions(-) create mode 100644 spec/unit/network/http/mongrel/rest.rb create mode 100644 spec/unit/network/http/mongrel/xmlrpc.rb create mode 100644 spec/unit/network/http/webrick/rest.rb create mode 100644 spec/unit/network/http/webrick/xmlrpc.rb diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 0d814c5ef..6930af494 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/mongrel.rb b/lib/puppet/network/http/mongrel.rb index 3efc465ad..bec94ac13 100644 --- a/lib/puppet/network/http/mongrel.rb +++ b/lib/puppet/network/http/mongrel.rb @@ -38,9 +38,7 @@ class Puppet::Network::HTTP::Mongrel def setup_handlers @protocols.each do |protocol| - @handlers.each do |handler| - class_for_protocol(protocol).new(:server => @server, :handler => handler) - end + class_for_protocol(protocol).new(:server => @server, :handlers => @handlers) end end diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index 6e454c7d9..34f1d8f90 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -1,4 +1,18 @@ class Puppet::Network::HTTP::MongrelREST def initialize(args = {}) + raise ArgumentError unless args[:server] + raise ArgumentError if !args[:handlers] or args[:handlers].empty? + + @models = {} + args[:handlers].each do |handler| + @models[handler] = find_model_for_handler(handler) + end + end + + private + + def find_model_for_handler(handler) + Puppet::Indirector::Indirection.model(handler) || + raise(ArgumentError, "Cannot locate indirection [#{handler}].") end end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index 6df7804c6..21d191d06 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -41,10 +41,8 @@ class Puppet::Network::HTTP::WEBrick private def setup_handlers - @handlers.each do |handler| - @protocols.each do |protocol| - class_for_protocol(protocol).new(:server => @server, :handler => handler) - end + @protocols.each do |protocol| + class_for_protocol(protocol).new(:server => @server, :handlers => @handlers) end end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index 5e9ccfc45..f70f2030f 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -1,4 +1,18 @@ class Puppet::Network::HTTP::WEBrickREST def initialize(args = {}) + raise ArgumentError unless args[:server] + raise ArgumentError if !args[:handlers] or args[:handlers].empty? + + @models = {} + args[:handlers].each do |handler| + @models[handler] = find_model_for_handler(handler) + end + end + + private + + def find_model_for_handler(handler) + Puppet::Indirector::Indirection.model(handler) || + raise(ArgumentError, "Cannot locate indirection [#{handler}].") end end \ No newline at end of file diff --git a/spec/unit/indirector/indirection.rb b/spec/unit/indirector/indirection.rb index 455b5dc67..cb1b4b8e8 100755 --- a/spec/unit/indirector/indirection.rb +++ b/spec/unit/indirector/indirection.rb @@ -23,7 +23,7 @@ describe Puppet::Indirector::Indirection do @indirection.find(@name).should == @instance end - it "should handle removing model instances from a terminus letting the appropriate terminus remove the instance" do + it "should handle removing model instances from a terminus by letting the appropriate terminus remove the instance" do @terminus.expects(:destroy).with(@name).returns(@instance) @indirection.destroy(@name).should == @instance end @@ -103,11 +103,21 @@ describe Puppet::Indirector::Indirection, " when managing indirection instances" @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) Puppet::Indirector::Indirection.instance(:test).should equal(@indirection) end - + it "should return nil when the named indirection has not been created" do Puppet::Indirector::Indirection.instance(:test).should be_nil end + it "should allow an indirection's model to be retrieved by name" do + mock_model = mock('model') + @indirection = Puppet::Indirector::Indirection.new(mock_model, :test) + Puppet::Indirector::Indirection.model(:test).should equal(mock_model) + end + + it "should return nil when no model matches the requested name" do + Puppet::Indirector::Indirection.model(:test).should be_nil + end + after do @indirection.delete if defined? @indirection end diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb index 161080109..3364efb92 100644 --- a/spec/unit/network/http/mongrel.rb +++ b/spec/unit/network/http/mongrel.rb @@ -64,11 +64,9 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do @listen_params[:protocols].each do |protocol| mock_handler = mock("handler instance for [#{protocol}]") mock_handler_class = mock("handler class for [#{protocol}]") - @listen_params[:handlers].each do |handler| - mock_handler_class.expects(:new).with {|args| - args[:server] == @mock_mongrel and args[:handler] == handler - }.returns(mock_handler) - end + mock_handler_class.expects(:new).with {|args| + args[:server] == @mock_mongrel and args[:handlers] == @listen_params[:handlers] + }.returns(mock_handler) @server.expects(:class_for_protocol).with(protocol).at_least_once.returns(mock_handler_class) end @server.listen(@listen_params) diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb new file mode 100644 index 000000000..4a6524ef6 --- /dev/null +++ b/spec/unit/network/http/mongrel/rest.rb @@ -0,0 +1,46 @@ +#!/usr/bin/env ruby +# +# Created by Rick Bradley on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../../spec_helper' +require 'puppet/network/http' + +describe Puppet::Network::HTTP::MongrelREST, "when initializing" do + before do + @mock_mongrel = mock('Mongrel server') + @params = { :server => @mock_mongrel, :handlers => [ :foo ] } + end + + it "should require access to a Mongrel server" do + Proc.new { Puppet::Network::HTTP::MongrelREST.new(@params.delete_if {|k,v| :server == k })}.should raise_error(ArgumentError) + end + + it "should require at least one indirection name" do + Proc.new { Puppet::Network::HTTP::MongrelREST.new(@params.delete_if {|k,v| :handlers == k })}.should raise_error(ArgumentError) + end + + it "should look up the indirection model from the indirection name" do + mock_model = mock('indirected model') + Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(mock_model) + Puppet::Network::HTTP::MongrelREST.new(@params) + end + + it "should fail if a handler is not indirected" do + Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(nil) + Proc.new { Puppet::Network::HTTP::MongrelREST.new(@params) }.should raise_error(ArgumentError) + end + + it "should register a listener for each indirection with the provided Mongrel server" +end + +describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do + it "should unpack request information from Mongrel" + it "should unpack parameters from the request for passing to controller methods" + it "should call the controller find method if the request represents a singular HTTP GET" + it "should call the controller search method if the request represents a plural HTTP GET" + it "should call the controller destroy method if the request represents an HTTP DELETE" + it "should call the controller save method if the request represents an HTTP PUT" + it "should serialize the result from the controller method for return back to Mongrel" + it "should serialize a controller expection result for return back to Mongrel" +end diff --git a/spec/unit/network/http/mongrel/xmlrpc.rb b/spec/unit/network/http/mongrel/xmlrpc.rb new file mode 100644 index 000000000..e69de29bb diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb index 81b2a0fa9..4bf742b6e 100644 --- a/spec/unit/network/http/webrick.rb +++ b/spec/unit/network/http/webrick.rb @@ -64,11 +64,9 @@ describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do @listen_params[:protocols].each do |protocol| mock_handler = mock("handler instance for [#{protocol}]") mock_handler_class = mock("handler class for [#{protocol}]") - @listen_params[:handlers].each do |handler| - mock_handler_class.expects(:new).with {|args| - args[:server] == @mock_webrick and args[:handler] == handler - }.returns(mock_handler) - end + mock_handler_class.expects(:new).with {|args| + args[:server] == @mock_webrick and args[:handlers] == @listen_params[:handlers] + }.returns(mock_handler) @server.expects(:class_for_protocol).with(protocol).at_least_once.returns(mock_handler_class) end @server.listen(@listen_params) diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb new file mode 100644 index 000000000..f17f89a15 --- /dev/null +++ b/spec/unit/network/http/webrick/rest.rb @@ -0,0 +1,46 @@ +#!/usr/bin/env ruby +# +# Created by Rick Bradley on 2007-10-16. +# Copyright (c) 2007. All rights reserved. + +require File.dirname(__FILE__) + '/../../../../spec_helper' +require 'puppet/network/http' + +describe Puppet::Network::HTTP::WEBrickREST, "when initializing" do + before do + @mock_webrick = mock('WEBrick server') + @params = { :server => @mock_webrick, :handlers => [ :foo ] } + end + + it "should require access to a WEBrick server" do + Proc.new { Puppet::Network::HTTP::WEBrickREST.new(@params.delete_if {|k,v| :server == k })}.should raise_error(ArgumentError) + end + + it "should require at least one indirection name" do + Proc.new { Puppet::Network::HTTP::WEBrickREST.new(@params.delete_if {|k,v| :handlers == k })}.should raise_error(ArgumentError) + end + + it "should look up the indirection model from the indirection name" do + mock_model = mock('indirected model') + Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(mock_model) + Puppet::Network::HTTP::WEBrickREST.new(@params) + end + + it "should fail if a handler is not indirected" do + Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(nil) + Proc.new { Puppet::Network::HTTP::WEBrickREST.new(@params) }.should raise_error(ArgumentError) + end + + it "should register a listener for each indirection with the provided WEBrick server" +end + +describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do + it "should unpack request information from WEBrick" + it "should unpack parameters from the request for passing to controller methods" + it "should call the controller find method if the request represents a singular HTTP GET" + it "should call the controller search method if the request represents a plural HTTP GET" + it "should call the controller destroy method if the request represents an HTTP DELETE" + it "should call the controller save method if the request represents an HTTP PUT" + it "should serialize the result from the controller method for return back to Mongrel" + it "should serialize a controller expection result for return back to Mongrel" +end diff --git a/spec/unit/network/http/webrick/xmlrpc.rb b/spec/unit/network/http/webrick/xmlrpc.rb new file mode 100644 index 000000000..e69de29bb diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb index 659eb1930..97ab4443a 100644 --- a/spec/unit/network/server.rb +++ b/spec/unit/network/server.rb @@ -273,25 +273,10 @@ describe Puppet::Network::Server, "when listening is being turned off" do end - # TODO / FIXME : HERE -- need to begin resolving the model behind the indirection - # looks like: get the handler class, providing @server to it - # have the handler class register the handler @ the right URL - # handler class knows the correct path to use, correct registration method to call - # handler also know how to get the model class from the indirection name - # do we change indirection name to indirection (instead of saying "handlers")? - - -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 + -- cgit From 3c370b3570d39c18799085793e083898cda72e68 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 10:44:02 -0500 Subject: Going back to each server+protocol object being responsible for only one indirection, as the REST vs. XMLRPC models are different enough that the object must register itself on initialization and handle the request when it comes in. --- lib/puppet/network/http/mongrel.rb | 4 +++- lib/puppet/network/http/mongrel/rest.rb | 8 ++------ lib/puppet/network/http/webrick.rb | 4 +++- lib/puppet/network/http/webrick/rest.rb | 8 ++------ spec/unit/network/http/mongrel.rb | 8 +++++--- spec/unit/network/http/mongrel/rest.rb | 6 +++--- spec/unit/network/http/webrick.rb | 8 +++++--- spec/unit/network/http/webrick/rest.rb | 6 +++--- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/lib/puppet/network/http/mongrel.rb b/lib/puppet/network/http/mongrel.rb index bec94ac13..3efc465ad 100644 --- a/lib/puppet/network/http/mongrel.rb +++ b/lib/puppet/network/http/mongrel.rb @@ -38,7 +38,9 @@ class Puppet::Network::HTTP::Mongrel def setup_handlers @protocols.each do |protocol| - class_for_protocol(protocol).new(:server => @server, :handlers => @handlers) + @handlers.each do |handler| + class_for_protocol(protocol).new(:server => @server, :handler => handler) + end end end diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index 34f1d8f90..0b2c43dfe 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -1,12 +1,8 @@ class Puppet::Network::HTTP::MongrelREST def initialize(args = {}) raise ArgumentError unless args[:server] - raise ArgumentError if !args[:handlers] or args[:handlers].empty? - - @models = {} - args[:handlers].each do |handler| - @models[handler] = find_model_for_handler(handler) - end + raise ArgumentError unless @handler = args[:handler] + @model = find_model_for_handler(@handler) end private diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index 21d191d06..53aa2e99b 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -42,7 +42,9 @@ class Puppet::Network::HTTP::WEBrick def setup_handlers @protocols.each do |protocol| - class_for_protocol(protocol).new(:server => @server, :handlers => @handlers) + @handlers.each do |handler| + class_for_protocol(protocol).new(:server => @server, :handler => handler) + end end end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index f70f2030f..497fa26cc 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -1,12 +1,8 @@ class Puppet::Network::HTTP::WEBrickREST def initialize(args = {}) raise ArgumentError unless args[:server] - raise ArgumentError if !args[:handlers] or args[:handlers].empty? - - @models = {} - args[:handlers].each do |handler| - @models[handler] = find_model_for_handler(handler) - end + raise ArgumentError unless @handler = args[:handler] + @model = find_model_for_handler(@handler) end private diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb index 3364efb92..161080109 100644 --- a/spec/unit/network/http/mongrel.rb +++ b/spec/unit/network/http/mongrel.rb @@ -64,9 +64,11 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do @listen_params[:protocols].each do |protocol| mock_handler = mock("handler instance for [#{protocol}]") mock_handler_class = mock("handler class for [#{protocol}]") - mock_handler_class.expects(:new).with {|args| - args[:server] == @mock_mongrel and args[:handlers] == @listen_params[:handlers] - }.returns(mock_handler) + @listen_params[:handlers].each do |handler| + mock_handler_class.expects(:new).with {|args| + args[:server] == @mock_mongrel and args[:handler] == handler + }.returns(mock_handler) + end @server.expects(:class_for_protocol).with(protocol).at_least_once.returns(mock_handler_class) end @server.listen(@listen_params) diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index 4a6524ef6..46a82183b 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -9,15 +9,15 @@ require 'puppet/network/http' describe Puppet::Network::HTTP::MongrelREST, "when initializing" do before do @mock_mongrel = mock('Mongrel server') - @params = { :server => @mock_mongrel, :handlers => [ :foo ] } + @params = { :server => @mock_mongrel, :handler => :foo } end it "should require access to a Mongrel server" do Proc.new { Puppet::Network::HTTP::MongrelREST.new(@params.delete_if {|k,v| :server == k })}.should raise_error(ArgumentError) end - it "should require at least one indirection name" do - Proc.new { Puppet::Network::HTTP::MongrelREST.new(@params.delete_if {|k,v| :handlers == k })}.should raise_error(ArgumentError) + it "should require an indirection name" do + Proc.new { Puppet::Network::HTTP::MongrelREST.new(@params.delete_if {|k,v| :handler == k })}.should raise_error(ArgumentError) end it "should look up the indirection model from the indirection name" do diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb index 4bf742b6e..81b2a0fa9 100644 --- a/spec/unit/network/http/webrick.rb +++ b/spec/unit/network/http/webrick.rb @@ -64,9 +64,11 @@ describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do @listen_params[:protocols].each do |protocol| mock_handler = mock("handler instance for [#{protocol}]") mock_handler_class = mock("handler class for [#{protocol}]") - mock_handler_class.expects(:new).with {|args| - args[:server] == @mock_webrick and args[:handlers] == @listen_params[:handlers] - }.returns(mock_handler) + @listen_params[:handlers].each do |handler| + mock_handler_class.expects(:new).with {|args| + args[:server] == @mock_webrick and args[:handler] == handler + }.returns(mock_handler) + end @server.expects(:class_for_protocol).with(protocol).at_least_once.returns(mock_handler_class) end @server.listen(@listen_params) diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb index f17f89a15..5a1b53da0 100644 --- a/spec/unit/network/http/webrick/rest.rb +++ b/spec/unit/network/http/webrick/rest.rb @@ -9,15 +9,15 @@ require 'puppet/network/http' describe Puppet::Network::HTTP::WEBrickREST, "when initializing" do before do @mock_webrick = mock('WEBrick server') - @params = { :server => @mock_webrick, :handlers => [ :foo ] } + @params = { :server => @mock_webrick, :handler => :foo } end it "should require access to a WEBrick server" do Proc.new { Puppet::Network::HTTP::WEBrickREST.new(@params.delete_if {|k,v| :server == k })}.should raise_error(ArgumentError) end - it "should require at least one indirection name" do - Proc.new { Puppet::Network::HTTP::WEBrickREST.new(@params.delete_if {|k,v| :handlers == k })}.should raise_error(ArgumentError) + it "should require an indirection name" do + Proc.new { Puppet::Network::HTTP::WEBrickREST.new(@params.delete_if {|k,v| :handler == k })}.should raise_error(ArgumentError) end it "should look up the indirection model from the indirection name" do -- cgit From b8c877c121f6b376cd44b13cb90d69c41d0fb004 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 11:13:17 -0500 Subject: Registration now built for {webrick,mongrel} REST handlers. --- lib/puppet/network/http/mongrel/rest.rb | 10 ++++++++-- lib/puppet/network/http/webrick/rest.rb | 10 ++++++++-- spec/unit/network/http/mongrel.rb | 6 +++--- spec/unit/network/http/mongrel/rest.rb | 24 +++++++++++++++++++----- spec/unit/network/http/webrick.rb | 2 ++ spec/unit/network/http/webrick/rest.rb | 24 +++++++++++++++++++----- 6 files changed, 59 insertions(+), 17 deletions(-) diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index 0b2c43dfe..452dafa85 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -1,12 +1,18 @@ class Puppet::Network::HTTP::MongrelREST def initialize(args = {}) - raise ArgumentError unless args[:server] + raise ArgumentError unless @server = args[:server] raise ArgumentError unless @handler = args[:handler] - @model = find_model_for_handler(@handler) + register_handler end private + def register_handler + @model = find_model_for_handler(@handler) + @server.register('/' + @handler.to_s, self) + @server.register('/' + @handler.to_s + 's', self) + end + def find_model_for_handler(handler) Puppet::Indirector::Indirection.model(handler) || raise(ArgumentError, "Cannot locate indirection [#{handler}].") diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index 497fa26cc..7b9a3912b 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -1,11 +1,17 @@ class Puppet::Network::HTTP::WEBrickREST def initialize(args = {}) - raise ArgumentError unless args[:server] + raise ArgumentError unless @server = args[:server] raise ArgumentError unless @handler = args[:handler] - @model = find_model_for_handler(@handler) + register_handler end private + + def register_handler + @model = find_model_for_handler(@handler) + @server.mount('/' + @handler.to_s, self) + @server.mount('/' + @handler.to_s + 's', self) + end def find_model_for_handler(handler) Puppet::Indirector::Indirection.model(handler) || diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb index 161080109..bde16fee3 100644 --- a/spec/unit/network/http/mongrel.rb +++ b/spec/unit/network/http/mongrel.rb @@ -17,6 +17,7 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do @server = Puppet::Network::HTTP::Mongrel.new @mock_mongrel = mock('mongrel') @mock_mongrel.stubs(:run) + @mock_mongrel.stubs(:register) Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :configuration ], :protocols => [ :rest, :xmlrpc ] } end @@ -53,9 +54,7 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do end it "should be listening" do - mock_mongrel = mock('mongrel httpserver') - mock_mongrel.expects(:run) - Mongrel::HttpServer.expects(:new).returns(mock_mongrel) + Mongrel::HttpServer.expects(:new).returns(@mock_mongrel) @server.listen(@listen_params) @server.should be_listening end @@ -94,6 +93,7 @@ describe Puppet::Network::HTTP::Mongrel, "when turning off listening" do before do @mock_mongrel = mock('mongrel httpserver') @mock_mongrel.stubs(:run) + @mock_mongrel.stubs(:register) Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) @server = Puppet::Network::HTTP::Mongrel.new @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :configuration ], :protocols => [ :rest, :xmlrpc ] } diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index 46a82183b..49562e866 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -9,6 +9,9 @@ require 'puppet/network/http' describe Puppet::Network::HTTP::MongrelREST, "when initializing" do before do @mock_mongrel = mock('Mongrel server') + @mock_mongrel.stubs(:register) + @mock_model = mock('indirected model') + Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model) @params = { :server => @mock_mongrel, :handler => :foo } end @@ -21,17 +24,28 @@ describe Puppet::Network::HTTP::MongrelREST, "when initializing" do end it "should look up the indirection model from the indirection name" do - mock_model = mock('indirected model') - Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(mock_model) + Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(@mock_model) Puppet::Network::HTTP::MongrelREST.new(@params) end - it "should fail if a handler is not indirected" do + it "should fail if the indirection is not known" do Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(nil) Proc.new { Puppet::Network::HTTP::MongrelREST.new(@params) }.should raise_error(ArgumentError) end - - it "should register a listener for each indirection with the provided Mongrel server" + + it "should register itself with the mongrel server for the singular HTTP methods" do + @mock_mongrel.expects(:register).with do |*args| + args.first == '/foo' and args.last.is_a? Puppet::Network::HTTP::MongrelREST + end + Puppet::Network::HTTP::MongrelREST.new(@params) + end + + it "should register itself with the mongrel server for the plural GET method" do + @mock_mongrel.expects(:register).with do |*args| + args.first == '/foos' and args.last.is_a? Puppet::Network::HTTP::MongrelREST + end + Puppet::Network::HTTP::MongrelREST.new(@params) + end end describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb index 81b2a0fa9..9ba04f164 100644 --- a/spec/unit/network/http/webrick.rb +++ b/spec/unit/network/http/webrick.rb @@ -17,6 +17,7 @@ describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do Puppet.stubs(:start) Puppet.stubs(:newservice) @mock_webrick = mock('webrick') + @mock_webrick.stubs(:mount) WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :configuration ], :protocols => [ :rest, :xmlrpc ] } @@ -94,6 +95,7 @@ describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do Puppet.stubs(:start) Puppet.stubs(:newservice) @mock_webrick = mock('webrick') + @mock_webrick.stubs(:mount) WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new @server.stubs(:shutdown) diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb index 5a1b53da0..70c50bf39 100644 --- a/spec/unit/network/http/webrick/rest.rb +++ b/spec/unit/network/http/webrick/rest.rb @@ -9,6 +9,9 @@ require 'puppet/network/http' describe Puppet::Network::HTTP::WEBrickREST, "when initializing" do before do @mock_webrick = mock('WEBrick server') + @mock_webrick.stubs(:mount) + @mock_model = mock('indirected model') + Puppet::Indirector::Indirection.stubs(:model).returns(@mock_model) @params = { :server => @mock_webrick, :handler => :foo } end @@ -21,17 +24,28 @@ describe Puppet::Network::HTTP::WEBrickREST, "when initializing" do end it "should look up the indirection model from the indirection name" do - mock_model = mock('indirected model') - Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(mock_model) + Puppet::Indirector::Indirection.expects(:model).returns(@mock_model) Puppet::Network::HTTP::WEBrickREST.new(@params) end - it "should fail if a handler is not indirected" do - Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(nil) + it "should fail if the indirection is not known" do + Puppet::Indirector::Indirection.expects(:model).returns(nil) Proc.new { Puppet::Network::HTTP::WEBrickREST.new(@params) }.should raise_error(ArgumentError) end - it "should register a listener for each indirection with the provided WEBrick server" + it "should register itself with the WEBrick server for the singular HTTP methods" do + @mock_webrick.expects(:mount).with do |*args| + args.first == '/foo' and args.last.is_a?(Puppet::Network::HTTP::WEBrickREST) + end + Puppet::Network::HTTP::WEBrickREST.new(@params) + end + + it "should register itself with the WEBrick server for the plural GET method" do + @mock_webrick.expects(:mount).with do |*args| + args.first == '/foos' and args.last.is_a?(Puppet::Network::HTTP::WEBrickREST) + end + Puppet::Network::HTTP::WEBrickREST.new(@params) + end end describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do -- cgit From 6ab78f62ee589e542fd653a54109c0f5141ea026 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 13:24:58 -0500 Subject: Inlined the controller, eliminating a class. Mongrel+REST has the right bits for request handling prior to the encode/decode/exception-handling bits. Refactored to make the common logic extractable to a base class. --- lib/puppet/network/controller.rb | 30 -------------------- lib/puppet/network/http/mongrel/rest.rb | 45 +++++++++++++++++++++++++++-- spec/unit/network/controller.rb | 45 ----------------------------- spec/unit/network/http/mongrel/rest.rb | 50 +++++++++++++++++++++++++++++---- spec/unit/network/http/webrick/rest.rb | 11 ++++++-- 5 files changed, 96 insertions(+), 85 deletions(-) delete mode 100644 lib/puppet/network/controller.rb delete mode 100644 spec/unit/network/controller.rb diff --git a/lib/puppet/network/controller.rb b/lib/puppet/network/controller.rb deleted file mode 100644 index 7e4cca643..000000000 --- a/lib/puppet/network/controller.rb +++ /dev/null @@ -1,30 +0,0 @@ -class Puppet::Network::Controller - def initialize(args = {}) - raise ArgumentError, ":indirection is required" unless args[:indirection] - @indirection = args[:indirection] - @klass = model_class_from_indirection_name(@indirection) - end - - def find(args = {}) - @klass.find(args) - end - - def destroy(args = {}) - @klass.destroy(args) - end - - def search(args = {}) - @klass.search(args) - end - - def save(args = {}) - instance = @klass.new(args) - instance.save - end - - private - - def model_class_from_indirection_name - Class.new # TODO : FIXME make this the indirection class - end -end diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index 452dafa85..f9a6e3796 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -5,16 +5,55 @@ class Puppet::Network::HTTP::MongrelREST register_handler end + # handle an HTTP request coming from Mongrel + def process(request, response) + return @model.find if get?(request) and singular?(request) + return @model.search if get?(request) and plural?(request) + return @model.destroy if delete?(request) and singular?(request) + return @model.new.save if put?(request) and singular?(request) + raise ArgumentError, "Did not understand HTTP #{http_method(request)} request for '#{path(request)}'" + # TODO: here, raise an exception, or do some defaulting or something + end + private + 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 + def register_handler @model = find_model_for_handler(@handler) @server.register('/' + @handler.to_s, self) @server.register('/' + @handler.to_s + 's', self) end - def find_model_for_handler(handler) - Puppet::Indirector::Indirection.model(handler) || - raise(ArgumentError, "Cannot locate indirection [#{handler}].") + def http_method(request) + request.params[Mongrel::Const::REQUEST_METHOD] end + + def path(request) + request.params[Mongrel::Const::REQUEST_PATH] + end + end diff --git a/spec/unit/network/controller.rb b/spec/unit/network/controller.rb deleted file mode 100644 index 9098b6e25..000000000 --- a/spec/unit/network/controller.rb +++ /dev/null @@ -1,45 +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/controller' - -describe Puppet::Network::Controller, "when initializing" do - it "should require an indirection name" do - Proc.new { Puppet::Network::Controller.new }.should raise_error(ArgumentError) - end -end - -describe Puppet::Network::Controller, "after initialization" do - before do - @mock_model_class = mock('model class') - Puppet::Network::Controller.any_instance.stubs(:model_class_from_indirection_name).returns(@mock_model_class) - @controller = Puppet::Network::Controller.new(:indirection => :foo) - end - - it "should delegate find to the indirection's model class's find" do - @mock_model_class.expects(:find).returns({:foo => :bar}) - @controller.find.should == { :foo => :bar } - end - - it "should delegate search to the indirection's model class's search" do - @mock_model_class.expects(:search).returns({:foo => :bar}) - @controller.search.should == { :foo => :bar } - end - - it "should delegate destroy to the indirection's model class's destroy" do - @mock_model_class.expects(:destroy).returns({:foo => :bar}) - @controller.destroy.should == { :foo => :bar } - end - - it "should delegate save to the indirection's model class's save" do - data = { :bar => :xyzzy } - mock_model_instance = mock('model instance') - @mock_model_class.expects(:new).with(data).returns(mock_model_instance) - mock_model_instance.expects(:save).returns({:foo => :bar}) - @controller.save(data).should == { :foo => :bar } - end -end \ No newline at end of file diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index 49562e866..c45ca8d66 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -49,12 +49,52 @@ describe Puppet::Network::HTTP::MongrelREST, "when initializing" do end describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do + before do + @mock_request = mock('mongrel http request') + @mock_response = mock('mongrel http response') + @mock_model_class = mock('indirected model class') + Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model_class) + @mock_mongrel = mock('mongrel http server') + @mock_mongrel.stubs(:register) + @handler = Puppet::Network::HTTP::MongrelREST.new(:server => @mock_mongrel, :handler => :foo) + end + + it "should call the model find method if the request represents a singular HTTP GET" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/foo'}) + @mock_model_class.expects(:find) + @handler.process(@mock_request, @mock_response) + end + + it "should call the model search method if the request represents a plural HTTP GET" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/foos'}) + @mock_model_class.expects(:search) + @handler.process(@mock_request, @mock_response) + end + + it "should call the model destroy method if the request represents an HTTP DELETE" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foo'}) + @mock_model_class.expects(:destroy) + @handler.process(@mock_request, @mock_response) + end + + it "should call the model save method if the request represents an HTTP PUT" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foo'}) + mock_model_instance = mock('indirected model instance') + mock_model_instance.expects(:save) + @mock_model_class.expects(:new).returns(mock_model_instance) + @handler.process(@mock_request, @mock_response) + end + + it "should fail if the HTTP method isn't supported" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'POST', Mongrel::Const::REQUEST_PATH => '/foo'}) + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end + it "should unpack request information from Mongrel" + it "should unpack parameters from the request for passing to controller methods" - it "should call the controller find method if the request represents a singular HTTP GET" - it "should call the controller search method if the request represents a plural HTTP GET" - it "should call the controller destroy method if the request represents an HTTP DELETE" - it "should call the controller save method if the request represents an HTTP PUT" + it "should serialize the result from the controller method for return back to Mongrel" - it "should serialize a controller expection result for return back to Mongrel" + + it "should serialize a controller exception result for return back to Mongrel" end diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb index 70c50bf39..418c97b6f 100644 --- a/spec/unit/network/http/webrick/rest.rb +++ b/spec/unit/network/http/webrick/rest.rb @@ -50,11 +50,18 @@ end describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do it "should unpack request information from WEBrick" + it "should unpack parameters from the request for passing to controller methods" + it "should call the controller find method if the request represents a singular HTTP GET" + it "should call the controller search method if the request represents a plural HTTP GET" + it "should call the controller destroy method if the request represents an HTTP DELETE" + it "should call the controller save method if the request represents an HTTP PUT" - it "should serialize the result from the controller method for return back to Mongrel" - it "should serialize a controller expection result for return back to Mongrel" + + it "should serialize the result from the controller method for return back to WEBrick" + + it "should serialize a controller exception result for return back to WEBrick" end -- cgit From 2a497fff66a7827059b712e84dcaff171ccab6be Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 13:57:56 -0500 Subject: Refactored to use a Handler base class for server+protocol handlers. Finally eliminated dependency on Puppet.start, etc., from WEBrick HTTP server class. {webrick,mongrel}+REST now support request handling uniformly; need encode/decode next. --- lib/puppet/network/http/handler.rb | 57 +++++++++++++++++++++++++++++++++ lib/puppet/network/http/mongrel/rest.rb | 47 +++------------------------ lib/puppet/network/http/webrick.rb | 9 ++---- lib/puppet/network/http/webrick/rest.rb | 21 +++++++----- spec/unit/network/http/webrick.rb | 14 +++----- spec/unit/network/http/webrick/rest.rb | 56 ++++++++++++++++++++++++++------ 6 files changed, 127 insertions(+), 77 deletions(-) create mode 100644 lib/puppet/network/http/handler.rb diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb new file mode 100644 index 000000000..54cec417b --- /dev/null +++ b/lib/puppet/network/http/handler.rb @@ -0,0 +1,57 @@ +class Puppet::Network::HTTP::Handler + def initialize(args = {}) + raise ArgumentError unless @server = args[:server] + raise ArgumentError unless @handler = args[:handler] + register_handler + end + + # handle an HTTP request coming from Mongrel + def process(request, response) + return @model.find if get?(request) and singular?(request) + return @model.search if get?(request) and plural?(request) + return @model.destroy if delete?(request) and singular?(request) + return @model.new.save if put?(request) and singular?(request) + raise ArgumentError, "Did not understand HTTP #{http_method(request)} request for '#{path(request)}'" + end + + private + + 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 UnimplementedError + end + + def http_method(request) + raise UnimplementedError + end + + def path(request) + raise UnimplementedError + end +end diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index f9a6e3796..8f3de957e 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -1,47 +1,9 @@ -class Puppet::Network::HTTP::MongrelREST - def initialize(args = {}) - raise ArgumentError unless @server = args[:server] - raise ArgumentError unless @handler = args[:handler] - register_handler - end - - # handle an HTTP request coming from Mongrel - def process(request, response) - return @model.find if get?(request) and singular?(request) - return @model.search if get?(request) and plural?(request) - return @model.destroy if delete?(request) and singular?(request) - return @model.new.save if put?(request) and singular?(request) - raise ArgumentError, "Did not understand HTTP #{http_method(request)} request for '#{path(request)}'" - # TODO: here, raise an exception, or do some defaulting or something - end - +require 'puppet/network/http/handler' + +class Puppet::Network::HTTP::MongrelREST < Puppet::Network::HTTP::Handler + private - 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 - def register_handler @model = find_model_for_handler(@handler) @server.register('/' + @handler.to_s, self) @@ -55,5 +17,4 @@ class Puppet::Network::HTTP::MongrelREST def path(request) request.params[Mongrel::Const::REQUEST_PATH] end - end diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb index 53aa2e99b..c4b2ed3c6 100644 --- a/lib/puppet/network/http/webrick.rb +++ b/lib/puppet/network/http/webrick.rb @@ -18,19 +18,14 @@ class Puppet::Network::HTTP::WEBrick @protocols = args[:protocols] @handlers = args[:handlers] @server = WEBrick::HTTPServer.new(:BindAddress => args[:address], :Port => args[:port]) - setup_handlers - - # TODO / FIXME is this really necessary? -- or can we do it in both mongrel and webrick? - Puppet.newservice(@server) - Puppet.start - + @server.start @listening = true end def unlisten raise "WEBrick server is not listening" unless listening? - shutdown + @server.shutdown @listening = false end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index 7b9a3912b..cefffd76d 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -1,8 +1,10 @@ -class Puppet::Network::HTTP::WEBrickREST - def initialize(args = {}) - raise ArgumentError unless @server = args[:server] - raise ArgumentError unless @handler = args[:handler] - register_handler +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 @@ -13,8 +15,11 @@ class Puppet::Network::HTTP::WEBrickREST @server.mount('/' + @handler.to_s + 's', self) end - def find_model_for_handler(handler) - Puppet::Indirector::Indirection.model(handler) || - raise(ArgumentError, "Cannot locate indirection [#{handler}].") + def http_method(request) + request.request_method + end + + def path(request) + request.path end end \ No newline at end of file diff --git a/spec/unit/network/http/webrick.rb b/spec/unit/network/http/webrick.rb index 9ba04f164..3ed223e7e 100644 --- a/spec/unit/network/http/webrick.rb +++ b/spec/unit/network/http/webrick.rb @@ -14,10 +14,8 @@ end describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do before do - Puppet.stubs(:start) - Puppet.stubs(:newservice) @mock_webrick = mock('webrick') - @mock_webrick.stubs(:mount) + [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :configuration ], :protocols => [ :rest, :xmlrpc ] } @@ -45,7 +43,7 @@ describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do end it "should order a webrick server to start" do - Puppet.expects(:start) + @mock_webrick.expects(:start) @server.listen(@listen_params) end @@ -92,13 +90,10 @@ end describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do before do - Puppet.stubs(:start) - Puppet.stubs(:newservice) @mock_webrick = mock('webrick') - @mock_webrick.stubs(:mount) + [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new - @server.stubs(:shutdown) @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :configuration ], :protocols => [ :rest, :xmlrpc ] } end @@ -107,8 +102,7 @@ describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do end it "should order webrick server to stop" do - @server.should respond_to(:shutdown) - @server.expects(:shutdown) + @mock_webrick.expects(:shutdown) @server.listen(@listen_params) @server.unlisten end diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb index 418c97b6f..472a09aca 100644 --- a/spec/unit/network/http/webrick/rest.rb +++ b/spec/unit/network/http/webrick/rest.rb @@ -49,19 +49,57 @@ describe Puppet::Network::HTTP::WEBrickREST, "when initializing" do end describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do - it "should unpack request information from WEBrick" - - it "should unpack parameters from the request for passing to controller methods" + before do + @mock_request = mock('webrick http request') + @mock_response = mock('webrick http response') + @mock_model_class = mock('indirected model class') + Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model_class) + @mock_webrick = mock('mongrel http server') + @mock_webrick.stubs(:mount) + @handler = Puppet::Network::HTTP::WEBrickREST.new(:server => @mock_webrick, :handler => :foo) + end - it "should call the controller find method if the request represents a singular HTTP GET" + it "should call the model find method if the request represents a singular HTTP GET" do + @mock_request.stubs(:request_method).returns('GET') + @mock_request.stubs(:path).returns('/foo') + @mock_model_class.expects(:find) + @handler.service(@mock_request, @mock_response) + end + + it "should call the model search method if the request represents a plural HTTP GET" do + @mock_request.stubs(:request_method).returns('GET') + @mock_request.stubs(:path).returns('/foos') + @mock_model_class.expects(:search) + @handler.service(@mock_request, @mock_response) + end - it "should call the controller search method if the request represents a plural HTTP GET" + it "should call the model destroy method if the request represents an HTTP DELETE" do + @mock_request.stubs(:request_method).returns('DELETE') + @mock_request.stubs(:path).returns('/foo') + @mock_model_class.expects(:destroy) + @handler.service(@mock_request, @mock_response) + end + + it "should call the model save method if the request represents an HTTP PUT" do + @mock_request.stubs(:request_method).returns('PUT') + @mock_request.stubs(:path).returns('/foo') + mock_model_instance = mock('indirected model instance') + mock_model_instance.expects(:save) + @mock_model_class.expects(:new).returns(mock_model_instance) + @handler.service(@mock_request, @mock_response) + end - it "should call the controller destroy method if the request represents an HTTP DELETE" + it "should fail if the HTTP method isn't supported" do + @mock_request.stubs(:request_method).returns('POST') + @mock_request.stubs(:path).returns('/foo') + Proc.new { @handler.service(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end + + it "should unpack request information from WEBrick" - it "should call the controller save method if the request represents an HTTP PUT" + it "should unpack parameters from the request for passing to controller methods" - it "should serialize the result from the controller method for return back to WEBrick" + it "should serialize the result from the controller method for return back to Mongrel" - it "should serialize a controller exception result for return back to WEBrick" + it "should serialize a controller exception result for return back to Mongrel" end -- cgit From abbc824ff4a565f0a0f1362b779252e876b86168 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 14:04:38 -0500 Subject: Tweak to move model lookup functionality into the Handler base class where it belongs. Robustifying the request sanitization a bit more. --- lib/puppet/network/http/handler.rb | 1 + lib/puppet/network/http/mongrel/rest.rb | 1 - lib/puppet/network/http/webrick/rest.rb | 1 - spec/unit/network/http/mongrel/rest.rb | 12 ++++++++++++ spec/unit/network/http/webrick/rest.rb | 15 +++++++++++++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 54cec417b..77df113e6 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -2,6 +2,7 @@ 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 diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index 8f3de957e..4c795481b 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -5,7 +5,6 @@ class Puppet::Network::HTTP::MongrelREST < Puppet::Network::HTTP::Handler private def register_handler - @model = find_model_for_handler(@handler) @server.register('/' + @handler.to_s, self) @server.register('/' + @handler.to_s + 's', self) end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index cefffd76d..ed29cfb75 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -10,7 +10,6 @@ class Puppet::Network::HTTP::WEBrickREST < Puppet::Network::HTTP::Handler private def register_handler - @model = find_model_for_handler(@handler) @server.mount('/' + @handler.to_s, self) @server.mount('/' + @handler.to_s + 's', self) end diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index c45ca8d66..5bb40d777 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -89,6 +89,18 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'POST', Mongrel::Const::REQUEST_PATH => '/foo'}) Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) end + + it "should fail if the request's pluralization is wrong" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foos'}) + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foos'}) + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end + + it "should fail if the request is for an unknown path" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/bar'}) + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end it "should unpack request information from Mongrel" diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb index 472a09aca..6eb44f4be 100644 --- a/spec/unit/network/http/webrick/rest.rb +++ b/spec/unit/network/http/webrick/rest.rb @@ -94,6 +94,21 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do @mock_request.stubs(:path).returns('/foo') Proc.new { @handler.service(@mock_request, @mock_response) }.should raise_error(ArgumentError) end + + it "should fail if the request's pluralization is wrong" do + @mock_request.stubs(:request_method).returns('DELETE') + @mock_request.stubs(:path).returns('/foos') + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_request.stubs(:request_method).returns('PUT') + @mock_request.stubs(:path).returns('/foos') + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end + + it "should fail if the request is for an unknown path" do + @mock_request.stubs(:request_method).returns('GET') + @mock_request.stubs(:path).returns('/bar') + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end it "should unpack request information from WEBrick" -- cgit From 216dd8c47ea42338c2dee0bf6528cdd7e37e0028 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 15:38:41 -0500 Subject: Refactoring, argument processing for model methods. --- lib/puppet/network/http/handler.rb | 42 +++++++++++++++++++++++++++------ lib/puppet/network/http/mongrel/rest.rb | 10 +++++++- lib/puppet/network/http/webrick/rest.rb | 10 +++++++- spec/unit/network/http/mongrel/rest.rb | 34 +++++++++++++++++++------- spec/unit/network/http/webrick/rest.rb | 36 +++++++++++++++++++++------- 5 files changed, 107 insertions(+), 25 deletions(-) diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 77df113e6..fb7b5c323 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -8,15 +8,35 @@ class Puppet::Network::HTTP::Handler # handle an HTTP request coming from Mongrel def process(request, response) - return @model.find if get?(request) and singular?(request) - return @model.search if get?(request) and plural?(request) - return @model.destroy if delete?(request) and singular?(request) - return @model.new.save if put?(request) and singular?(request) + 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)}'" end private + def do_find(request, response) + key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") + @model.find(key) + end + + def do_search(request, response) + @model.search + end + + def do_destroy(request, response) + key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") + @model.destroy(key) + end + + def do_save(request, response) + data = body(request) + raise ArgumentError, "No data to save" if !data or data.empty? + @model.new.save(:data => data) + end + def find_model_for_handler(handler) Puppet::Indirector::Indirection.model(handler) || raise(ArgumentError, "Cannot locate indirection [#{handler}].") @@ -45,14 +65,22 @@ class Puppet::Network::HTTP::Handler # methods specific to a given web server def register_handler - raise UnimplementedError + raise NotImplementedError end def http_method(request) - raise UnimplementedError + raise NotImplementedError end def path(request) - raise UnimplementedError + raise NotImplementedError end + + def request_key(request) + raise NotImplementedError + end + + def body(request) + raise NotImplementedError + end end diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index 4c795481b..f7807b19f 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -14,6 +14,14 @@ class Puppet::Network::HTTP::MongrelREST < Puppet::Network::HTTP::Handler end def path(request) - request.params[Mongrel::Const::REQUEST_PATH] + '/' + 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 end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index ed29cfb75..bfcd0784d 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -19,6 +19,14 @@ class Puppet::Network::HTTP::WEBrickREST < Puppet::Network::HTTP::Handler end def path(request) - request.path + '/' + request.path.split('/')[1] + end + + def request_key(request) + request.path.split('/')[2] + end + + def body(request) + request.body end end \ No newline at end of file diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index 5bb40d777..15ee06c7e 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -60,8 +60,8 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do end it "should call the model find method if the request represents a singular HTTP GET" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/foo'}) - @mock_model_class.expects(:find) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/foo/key'}) + @mock_model_class.expects(:find).with('key') @handler.process(@mock_request, @mock_response) end @@ -72,15 +72,16 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do end it "should call the model destroy method if the request represents an HTTP DELETE" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foo'}) - @mock_model_class.expects(:destroy) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foo/key'}) + @mock_model_class.expects(:destroy).with('key') @handler.process(@mock_request, @mock_response) end it "should call the model save method if the request represents an HTTP PUT" do @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foo'}) + @mock_request.stubs(:body).returns('this is a fake request body') mock_model_instance = mock('indirected model instance') - mock_model_instance.expects(:save) + mock_model_instance.expects(:save).with(:data => 'this is a fake request body') @mock_model_class.expects(:new).returns(mock_model_instance) @handler.process(@mock_request, @mock_response) end @@ -91,17 +92,34 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do end it "should fail if the request's pluralization is wrong" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foos'}) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foos/key'}) Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foos'}) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foos/key'}) Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) end it "should fail if the request is for an unknown path" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/bar'}) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/bar/key'}) Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) end + + it "should fail to find model if key is not specified" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/foo'}) + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end + + it "should fail to destroy model if key is not specified" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foo'}) + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end + + it "should fail to save model if data is not specified" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foo'}) + @mock_request.stubs(:body).returns('') + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end + it "should unpack request information from Mongrel" it "should unpack parameters from the request for passing to controller methods" diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb index 6eb44f4be..1a2faa95f 100644 --- a/spec/unit/network/http/webrick/rest.rb +++ b/spec/unit/network/http/webrick/rest.rb @@ -61,8 +61,8 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do it "should call the model find method if the request represents a singular HTTP GET" do @mock_request.stubs(:request_method).returns('GET') - @mock_request.stubs(:path).returns('/foo') - @mock_model_class.expects(:find) + @mock_request.stubs(:path).returns('/foo/key') + @mock_model_class.expects(:find).with('key') @handler.service(@mock_request, @mock_response) end @@ -75,16 +75,17 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do it "should call the model destroy method if the request represents an HTTP DELETE" do @mock_request.stubs(:request_method).returns('DELETE') - @mock_request.stubs(:path).returns('/foo') - @mock_model_class.expects(:destroy) + @mock_request.stubs(:path).returns('/foo/key') + @mock_model_class.expects(:destroy).with('key') @handler.service(@mock_request, @mock_response) end it "should call the model save method if the request represents an HTTP PUT" do @mock_request.stubs(:request_method).returns('PUT') @mock_request.stubs(:path).returns('/foo') + @mock_request.stubs(:body).returns('This is a fake request body') mock_model_instance = mock('indirected model instance') - mock_model_instance.expects(:save) + mock_model_instance.expects(:save).with(:data => 'This is a fake request body') @mock_model_class.expects(:new).returns(mock_model_instance) @handler.service(@mock_request, @mock_response) end @@ -97,18 +98,37 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do it "should fail if the request's pluralization is wrong" do @mock_request.stubs(:request_method).returns('DELETE') - @mock_request.stubs(:path).returns('/foos') + @mock_request.stubs(:path).returns('/foos/key') Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) @mock_request.stubs(:request_method).returns('PUT') - @mock_request.stubs(:path).returns('/foos') + @mock_request.stubs(:path).returns('/foos/key') Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) end it "should fail if the request is for an unknown path" do @mock_request.stubs(:request_method).returns('GET') - @mock_request.stubs(:path).returns('/bar') + @mock_request.stubs(:path).returns('/bar/key') + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end + + it "should fail to find model if key is not specified" do + @mock_request.stubs(:request_method).returns('GET') + @mock_request.stubs(:path).returns('/foo') Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) end + + it "should fail to destroy model if key is not specified" do + @mock_request.stubs(:request_method).returns('DELETE') + @mock_request.stubs(:path).returns('/foo') + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end + + it "should fail to save model if data is not specified" do + @mock_request.stubs(:request_method).returns('PUT') + @mock_request.stubs(:path).returns('/foo') + @mock_request.stubs(:body).returns('') + Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + end it "should unpack request information from WEBrick" -- cgit From 6cd0f371065da901d8cc3143d8859a389ca87582 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 15:52:41 -0500 Subject: Make it possible to run all tests even if mongrel isn't installed. Shouldn't "confine" produce some output when running spec? Who knows. --- lib/puppet/network/http/mongrel.rb | 3 ++- spec/unit/network/http/mongrel.rb | 6 ++++++ spec/unit/network/http/mongrel/rest.rb | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/puppet/network/http/mongrel.rb b/lib/puppet/network/http/mongrel.rb index 3efc465ad..8ea669531 100644 --- a/lib/puppet/network/http/mongrel.rb +++ b/lib/puppet/network/http/mongrel.rb @@ -1,4 +1,5 @@ -require 'mongrel' +require 'mongrel' if Puppet.features.mongrel? + require 'puppet/network/http/mongrel/rest' require 'puppet/network/http/mongrel/xmlrpc' diff --git a/spec/unit/network/http/mongrel.rb b/spec/unit/network/http/mongrel.rb index bde16fee3..b6ad07567 100644 --- a/spec/unit/network/http/mongrel.rb +++ b/spec/unit/network/http/mongrel.rb @@ -7,12 +7,16 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/network/http' describe Puppet::Network::HTTP::Mongrel, "after initializing" do + confine "Mongrel is not available" => Puppet.features.mongrel? + it "should not be listening" do Puppet::Network::HTTP::Mongrel.new.should_not be_listening end end describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do + confine "Mongrel is not available" => Puppet.features.mongrel? + before do @server = Puppet::Network::HTTP::Mongrel.new @mock_mongrel = mock('mongrel') @@ -90,6 +94,8 @@ describe Puppet::Network::HTTP::Mongrel, "when turning on listening" do end describe Puppet::Network::HTTP::Mongrel, "when turning off listening" do + confine "Mongrel is not available" => Puppet.features.mongrel? + before do @mock_mongrel = mock('mongrel httpserver') @mock_mongrel.stubs(:run) diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index 15ee06c7e..16fe94edb 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -7,6 +7,8 @@ require File.dirname(__FILE__) + '/../../../../spec_helper' require 'puppet/network/http' describe Puppet::Network::HTTP::MongrelREST, "when initializing" do + confine "Mongrel is not available" => Puppet.features.mongrel? + before do @mock_mongrel = mock('Mongrel server') @mock_mongrel.stubs(:register) @@ -49,6 +51,8 @@ describe Puppet::Network::HTTP::MongrelREST, "when initializing" do end describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do + confine "Mongrel is not available" => Puppet.features.mongrel? + before do @mock_request = mock('mongrel http request') @mock_response = mock('mongrel http response') -- cgit From ce349683b76ab9d21f4d89e2ec818c0755848a1d Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 15:56:03 -0500 Subject: Make the actual runtime be more robust when mongrel is not installed. --- lib/puppet/network/http.rb | 5 ++++- spec/unit/network/http.rb | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/puppet/network/http.rb b/lib/puppet/network/http.rb index 044310d8e..062c67c71 100644 --- a/lib/puppet/network/http.rb +++ b/lib/puppet/network/http.rb @@ -1,7 +1,10 @@ class Puppet::Network::HTTP def self.server_class_by_type(kind) return Puppet::Network::HTTP::WEBrick if kind.to_sym == :webrick - return Puppet::Network::HTTP::Mongrel if kind.to_sym == :mongrel + 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 diff --git a/spec/unit/network/http.rb b/spec/unit/network/http.rb index 450d15487..cb99d4d4c 100644 --- a/spec/unit/network/http.rb +++ b/spec/unit/network/http.rb @@ -15,7 +15,12 @@ describe Puppet::Network::HTTP do it "should return the mongrel HTTP server class when asked for a mongrel server" do Puppet::Network::HTTP.server_class_by_type(:mongrel).should be(Puppet::Network::HTTP::Mongrel) end - + + it "should fail to return the mongrel HTTP server class if mongrel is not available " do + Puppet.features.expects(:mongrel?).returns(false) + Proc.new { Puppet::Network::HTTP.server_class_by_type(:mongrel) }.should raise_error(ArgumentError) + end + it "should return an error when asked for an unknown server" do Proc.new { Puppet::Network::HTTP.server_class_by_type :foo }.should raise_error(ArgumentError) end -- cgit From 705f76fa1d9a95c54560a82e68c1c47b27755361 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 18:08:18 -0500 Subject: Argument passing now supported on {webrick,mongrel}+REST. --- lib/puppet/network/http/handler.rb | 16 ++++++-- lib/puppet/network/http/mongrel/rest.rb | 4 ++ lib/puppet/network/http/webrick/rest.rb | 4 ++ spec/unit/network/http/mongrel/rest.rb | 70 +++++++++++++++++++++++++++------ spec/unit/network/http/webrick/rest.rb | 48 ++++++++++++++++++++-- 5 files changed, 123 insertions(+), 19 deletions(-) diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index fb7b5c323..1f63024d6 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -19,22 +19,26 @@ class Puppet::Network::HTTP::Handler def do_find(request, response) key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") - @model.find(key) + args = params(request) + @model.find(key, args) end def do_search(request, response) - @model.search + args = params(request) + @model.search(args) end def do_destroy(request, response) key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") - @model.destroy(key) + args = params(request) + @model.destroy(key, args) end def do_save(request, response) data = body(request) raise ArgumentError, "No data to save" if !data or data.empty? - @model.new.save(:data => data) + args = params(request) + @model.new.save(args.merge(:data => data)) end def find_model_for_handler(handler) @@ -83,4 +87,8 @@ class Puppet::Network::HTTP::Handler def body(request) raise NotImplementedError end + + def params(request) + raise NotImplementedError + end end diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index f7807b19f..d87f9c733 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -24,4 +24,8 @@ class Puppet::Network::HTTP::MongrelREST < Puppet::Network::HTTP::Handler def body(request) request.body end + + def params(request) + Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]) + end end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index bfcd0784d..33b1ad8dc 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -29,4 +29,8 @@ class Puppet::Network::HTTP::WEBrickREST < Puppet::Network::HTTP::Handler def body(request) request.body end + + def params(request) + request.query + end end \ No newline at end of file diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index 16fe94edb..0ad9be2bc 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -64,25 +64,33 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do end it "should call the model find method if the request represents a singular HTTP GET" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/foo/key'}) - @mock_model_class.expects(:find).with('key') + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', + Mongrel::Const::REQUEST_PATH => '/foo/key', + 'QUERY_STRING' => ''}) + @mock_model_class.expects(:find).with('key', {}) @handler.process(@mock_request, @mock_response) end it "should call the model search method if the request represents a plural HTTP GET" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/foos'}) - @mock_model_class.expects(:search) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', + Mongrel::Const::REQUEST_PATH => '/foos', + 'QUERY_STRING' => '' }) + @mock_model_class.expects(:search).with({}) @handler.process(@mock_request, @mock_response) end it "should call the model destroy method if the request represents an HTTP DELETE" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foo/key'}) - @mock_model_class.expects(:destroy).with('key') + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', + Mongrel::Const::REQUEST_PATH => '/foo/key', + 'QUERY_STRING' => '' }) + @mock_model_class.expects(:destroy).with('key', {}) @handler.process(@mock_request, @mock_response) end it "should call the model save method if the request represents an HTTP PUT" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foo'}) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', + Mongrel::Const::REQUEST_PATH => '/foo', + 'QUERY_STRING' => '' }) @mock_request.stubs(:body).returns('this is a fake request body') mock_model_instance = mock('indirected model instance') mock_model_instance.expects(:save).with(:data => 'this is a fake request body') @@ -103,7 +111,9 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do end it "should fail if the request is for an unknown path" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/bar/key'}) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', + Mongrel::Const::REQUEST_PATH => '/bar/key', + 'QUERY_STRING' => '' }) Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) end @@ -123,11 +133,49 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) end - - it "should unpack request information from Mongrel" + it "should pass HTTP request parameters to model find" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', + Mongrel::Const::REQUEST_PATH => '/foo/key', + 'QUERY_STRING' => 'foo=baz&bar=xyzzy'}) + @mock_model_class.expects(:find).with do |key, args| + key == 'key' and args['foo'] == 'baz' and args['bar'] == 'xyzzy' + end + @handler.process(@mock_request, @mock_response) + end - it "should unpack parameters from the request for passing to controller methods" + it "should pass HTTP request parameters to model search" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', + Mongrel::Const::REQUEST_PATH => '/foos', + 'QUERY_STRING' => 'foo=baz&bar=xyzzy'}) + @mock_model_class.expects(:search).with do |args| + args['foo'] == 'baz' and args['bar'] == 'xyzzy' + end + @handler.process(@mock_request, @mock_response) + end + + it "should pass HTTP request parameters to model delete" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', + Mongrel::Const::REQUEST_PATH => '/foo/key', + 'QUERY_STRING' => 'foo=baz&bar=xyzzy'}) + @mock_model_class.expects(:destroy).with do |key, args| + key == 'key' and args['foo'] == 'baz' and args['bar'] == 'xyzzy' + end + @handler.process(@mock_request, @mock_response) + end + it "should pass HTTP request parameters to model save" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', + Mongrel::Const::REQUEST_PATH => '/foo', + 'QUERY_STRING' => 'foo=baz&bar=xyzzy'}) + @mock_request.stubs(:body).returns('this is a fake request body') + mock_model_instance = mock('indirected model instance') + mock_model_instance.expects(:save).with do |args| + args[:data] == 'this is a fake request body' and args['foo'] == 'baz' and args['bar'] == 'xyzzy' + end + @mock_model_class.expects(:new).returns(mock_model_instance) + @handler.process(@mock_request, @mock_response) + end + it "should serialize the result from the controller method for return back to Mongrel" it "should serialize a controller exception result for return back to Mongrel" diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb index 1a2faa95f..e5132595b 100644 --- a/spec/unit/network/http/webrick/rest.rb +++ b/spec/unit/network/http/webrick/rest.rb @@ -51,6 +51,7 @@ end describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do before do @mock_request = mock('webrick http request') + @mock_request.stubs(:query).returns({}) @mock_response = mock('webrick http response') @mock_model_class = mock('indirected model class') Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model_class) @@ -62,7 +63,7 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do it "should call the model find method if the request represents a singular HTTP GET" do @mock_request.stubs(:request_method).returns('GET') @mock_request.stubs(:path).returns('/foo/key') - @mock_model_class.expects(:find).with('key') + @mock_model_class.expects(:find).with('key', {}) @handler.service(@mock_request, @mock_response) end @@ -76,7 +77,7 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do it "should call the model destroy method if the request represents an HTTP DELETE" do @mock_request.stubs(:request_method).returns('DELETE') @mock_request.stubs(:path).returns('/foo/key') - @mock_model_class.expects(:destroy).with('key') + @mock_model_class.expects(:destroy).with('key', {}) @handler.service(@mock_request, @mock_response) end @@ -130,10 +131,49 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) end - it "should unpack request information from WEBrick" + it "should pass HTTP request parameters to model find" do + @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) + @mock_request.stubs(:request_method).returns('GET') + @mock_request.stubs(:path).returns('/foo/key') + @mock_model_class.expects(:find).with do |key, args| + key == 'key' and args[:foo] == :baz and args[:bar] == :xyzzy + end + @handler.service(@mock_request, @mock_response) + end - it "should unpack parameters from the request for passing to controller methods" + it "should pass HTTP request parameters to model search" do + @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) + @mock_request.stubs(:request_method).returns('GET') + @mock_request.stubs(:path).returns('/foos/key') + @mock_model_class.expects(:search).with do |args| + args[:foo] == :baz and args[:bar] == :xyzzy + end + @handler.service(@mock_request, @mock_response) + end + it "should pass HTTP request parameters to model destroy" do + @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) + @mock_request.stubs(:request_method).returns('DELETE') + @mock_request.stubs(:path).returns('/foo/key') + @mock_model_class.expects(:destroy).with do |key, args| + key == 'key' and args[:foo] == :baz and args[:bar] == :xyzzy + end + @handler.service(@mock_request, @mock_response) + end + + it "should pass HTTP request parameters to model save" do + @mock_request.stubs(:request_method).returns('PUT') + @mock_request.stubs(:path).returns('/foo') + @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) + @mock_request.stubs(:body).returns('This is a fake request body') + mock_model_instance = mock('indirected model instance') + mock_model_instance.expects(:save).with do |args| + args[:data] == 'This is a fake request body' and args[:foo] == :baz and args[:bar] == :xyzzy + end + @mock_model_class.expects(:new).returns(mock_model_instance) + @handler.service(@mock_request, @mock_response) + end + it "should serialize the result from the controller method for return back to Mongrel" it "should serialize a controller exception result for return back to Mongrel" -- cgit From e5921c5a8025d4e908a5a9b010a126f0b1d5ed15 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 16 Oct 2007 18:14:41 -0500 Subject: getting more fine-grained with the response specs -- the target is always moving. --- spec/unit/network/http/mongrel/rest.rb | 12 +++++++++--- spec/unit/network/http/webrick/rest.rb | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index 0ad9be2bc..7df63db14 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -176,7 +176,13 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do @handler.process(@mock_request, @mock_response) end - it "should serialize the result from the controller method for return back to Mongrel" - - it "should serialize a controller exception result for return back to Mongrel" + it "should generate a 200 response when a model find call succeeds" + it "should generate a 200 response when a model search call succeeds" + it "should generate a 200 response when a model destroy call succeeds" + it "should generate a 200 response when a model save call succeeds" + it "should return a serialized object when a model find call succeeds" + it "should return a list of serialized object matches when a model search call succeeds" + it "should return a serialized success result when a model destroy call succeeds" + it "should return a serialized success result when a model save call succeeds" + it "should serialize a controller exception when an exception is thrown by the handler" end diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb index e5132595b..bd4bd304c 100644 --- a/spec/unit/network/http/webrick/rest.rb +++ b/spec/unit/network/http/webrick/rest.rb @@ -174,7 +174,13 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do @handler.service(@mock_request, @mock_response) end - it "should serialize the result from the controller method for return back to Mongrel" - - it "should serialize a controller exception result for return back to Mongrel" + it "should generate a 200 response when a model find call succeeds" + it "should generate a 200 response when a model search call succeeds" + it "should generate a 200 response when a model destroy call succeeds" + it "should generate a 200 response when a model save call succeeds" + it "should return a serialized object when a model find call succeeds" + it "should return a list of serialized object matches when a model search call succeeds" + it "should return a serialized success result when a model destroy call succeeds" + it "should return a serialized success result when a model save call succeeds" + it "should serialize a controller exception when an exception is thrown by the handler" end -- cgit From d2b891f6e3b1460602a0056b1b9dc85028c989e1 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Wed, 17 Oct 2007 11:37:21 -0500 Subject: More specs, fleshing out the returns from REST --- lib/puppet/network/http/handler.rb | 8 ++++++-- lib/puppet/network/http/mongrel/rest.rb | 5 +++++ lib/puppet/network/http/webrick/rest.rb | 4 ++++ spec/unit/network/http/mongrel/rest.rb | 27 +++++++++++++++++++++++++-- spec/unit/network/server.rb | 6 ++++-- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 1f63024d6..0d4be3a8d 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -20,12 +20,12 @@ class Puppet::Network::HTTP::Handler def do_find(request, response) key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") args = params(request) - @model.find(key, args) + encode_result(request, response, @model.find(key, args)) end def do_search(request, response) args = params(request) - @model.search(args) + encode_result(request, response, @model.search(args)) end def do_destroy(request, response) @@ -91,4 +91,8 @@ class Puppet::Network::HTTP::Handler def params(request) raise NotImplementedError end + + def encode_result(request, response, result) + raise NotImplementedError + end end diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index d87f9c733..0d29a822e 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -28,4 +28,9 @@ class Puppet::Network::HTTP::MongrelREST < Puppet::Network::HTTP::Handler def params(request) Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]) end + + def encode_result(request, response, result) + response.start(200) do |head, body| + end + end end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index 33b1ad8dc..1475ed781 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -33,4 +33,8 @@ class Puppet::Network::HTTP::WEBrickREST < Puppet::Network::HTTP::Handler def params(request) request.query end + + def encode_result(request, response, result) + result + end end \ No newline at end of file diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index 7df63db14..f5bb867f7 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -56,6 +56,7 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do before do @mock_request = mock('mongrel http request') @mock_response = mock('mongrel http response') + @mock_response.stubs(:start) @mock_model_class = mock('indirected model class') Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model_class) @mock_mongrel = mock('mongrel http server') @@ -176,13 +177,35 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do @handler.process(@mock_request, @mock_response) end - it "should generate a 200 response when a model find call succeeds" - it "should generate a 200 response when a model search call succeeds" + it "should generate a 200 response when a model find call succeeds" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', + Mongrel::Const::REQUEST_PATH => '/foo/key', + 'QUERY_STRING' => ''}) + @mock_model_class.stubs(:find) + @mock_response.expects(:start).with(200) + @handler.process(@mock_request, @mock_response) + end + + it "should generate a 200 response when a model search call succeeds" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', + Mongrel::Const::REQUEST_PATH => '/foos', + 'QUERY_STRING' => ''}) + @mock_model_class.stubs(:search) + @mock_response.expects(:start).with(200) + @handler.process(@mock_request, @mock_response) + end + it "should generate a 200 response when a model destroy call succeeds" + it "should generate a 200 response when a model save call succeeds" + it "should return a serialized object when a model find call succeeds" + it "should return a list of serialized object matches when a model search call succeeds" + it "should return a serialized success result when a model destroy call succeeds" + it "should return a serialized success result when a model save call succeeds" + it "should serialize a controller exception when an exception is thrown by the handler" end diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb index 97ab4443a..48626fe43 100644 --- a/spec/unit/network/server.rb +++ b/spec/unit/network/server.rb @@ -278,5 +278,7 @@ describe Class.new, "put these somewhere" do it "should deny access to clients based upon rules" end - - +describe Puppet::Indirector, "stuff required by HTTP servers" do + it "should provide the model with the ability to serialize to XML" + it "should provide the model with the ability to deserialize from XML" +end -- cgit From e69a50afbe0c031833c4247a962cfda0b5996d78 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Wed, 17 Oct 2007 11:50:34 -0500 Subject: Fix test which is conditional on mongrel installation. --- spec/unit/network/http.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/unit/network/http.rb b/spec/unit/network/http.rb index cb99d4d4c..a282b650a 100644 --- a/spec/unit/network/http.rb +++ b/spec/unit/network/http.rb @@ -12,8 +12,10 @@ describe Puppet::Network::HTTP do Puppet::Network::HTTP.server_class_by_type(:webrick).should be(Puppet::Network::HTTP::WEBrick) end - it "should return the mongrel HTTP server class when asked for a mongrel server" do - Puppet::Network::HTTP.server_class_by_type(:mongrel).should be(Puppet::Network::HTTP::Mongrel) + if Puppet.features.mongrel? + it "should return the mongrel HTTP server class when asked for a mongrel server" do + Puppet::Network::HTTP.server_class_by_type(:mongrel).should be(Puppet::Network::HTTP::Mongrel) + end end it "should fail to return the mongrel HTTP server class if mongrel is not available " do -- cgit From d28a9041039860beb9e19da267bbad40ecebf8f1 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 23 Oct 2007 11:12:22 -0500 Subject: REST handlers now properly returning 200 status on success. --- lib/puppet/network/http/handler.rb | 4 +-- lib/puppet/network/http/webrick/rest.rb | 2 +- spec/unit/network/http/mongrel/rest.rb | 21 ++++++++++-- spec/unit/network/http/webrick/rest.rb | 57 +++++++++++++++++++++++++++------ 4 files changed, 69 insertions(+), 15 deletions(-) diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 4365fffca..9b21946c7 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -31,14 +31,14 @@ class Puppet::Network::HTTP::Handler def do_destroy(request, response) key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") args = params(request) - @model.destroy(key, args) + encode_result(request, response, @model.destroy(key, args)) end def do_save(request, response) data = body(request) raise ArgumentError, "No data to save" if !data or data.empty? args = params(request) - @model.new.save(args.merge(:data => data)) + encode_result(request, response, @model.new.save(args.merge(:data => data))) end def find_model_for_handler(handler) diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index 1475ed781..d30f9318b 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -35,6 +35,6 @@ class Puppet::Network::HTTP::WEBrickREST < Puppet::Network::HTTP::Handler end def encode_result(request, response, result) - result + response.status = 200 end end \ No newline at end of file diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index f5bb867f7..944b0896e 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -195,9 +195,26 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do @handler.process(@mock_request, @mock_response) end - it "should generate a 200 response when a model destroy call succeeds" + it "should generate a 200 response when a model destroy call succeeds" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', + Mongrel::Const::REQUEST_PATH => '/foo/key', + 'QUERY_STRING' => ''}) + @mock_model_class.stubs(:destroy) + @mock_response.expects(:start).with(200) + @handler.process(@mock_request, @mock_response) + end - it "should generate a 200 response when a model save call succeeds" + it "should generate a 200 response when a model save call succeeds" do + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', + Mongrel::Const::REQUEST_PATH => '/foo', + 'QUERY_STRING' => ''}) + @mock_request.stubs(:body).returns('this is a fake request body') + @mock_model_instance = mock('model instance') + @mock_model_instance.stubs(:save) + @mock_model_class.stubs(:new).returns(@mock_model_instance) + @mock_response.expects(:start).with(200) + @handler.process(@mock_request, @mock_response) + end it "should return a serialized object when a model find call succeeds" diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb index bd4bd304c..9d1f5fc6b 100644 --- a/spec/unit/network/http/webrick/rest.rb +++ b/spec/unit/network/http/webrick/rest.rb @@ -53,6 +53,7 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do @mock_request = mock('webrick http request') @mock_request.stubs(:query).returns({}) @mock_response = mock('webrick http response') + @mock_response.stubs(:status=) @mock_model_class = mock('indirected model class') Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model_class) @mock_webrick = mock('mongrel http server') @@ -85,9 +86,9 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do @mock_request.stubs(:request_method).returns('PUT') @mock_request.stubs(:path).returns('/foo') @mock_request.stubs(:body).returns('This is a fake request body') - mock_model_instance = mock('indirected model instance') - mock_model_instance.expects(:save).with(:data => 'This is a fake request body') - @mock_model_class.expects(:new).returns(mock_model_instance) + @mock_model_instance = mock('indirected model instance') + @mock_model_instance.expects(:save).with(:data => 'This is a fake request body') + @mock_model_class.expects(:new).returns(@mock_model_instance) @handler.service(@mock_request, @mock_response) end @@ -166,18 +167,54 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do @mock_request.stubs(:path).returns('/foo') @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) @mock_request.stubs(:body).returns('This is a fake request body') - mock_model_instance = mock('indirected model instance') - mock_model_instance.expects(:save).with do |args| + @mock_model_instance = mock('indirected model instance') + @mock_model_instance.expects(:save).with do |args| args[:data] == 'This is a fake request body' and args[:foo] == :baz and args[:bar] == :xyzzy end - @mock_model_class.expects(:new).returns(mock_model_instance) + @mock_model_class.expects(:new).returns(@mock_model_instance) @handler.service(@mock_request, @mock_response) end - it "should generate a 200 response when a model find call succeeds" - it "should generate a 200 response when a model search call succeeds" - it "should generate a 200 response when a model destroy call succeeds" - it "should generate a 200 response when a model save call succeeds" + it "should generate a 200 response when a model find call succeeds" do + @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) + @mock_request.stubs(:request_method).returns('GET') + @mock_request.stubs(:path).returns('/foo/key') + @mock_model_class.stubs(:find) + @mock_response.expects(:status=).with(200) + @handler.process(@mock_request, @mock_response) + end + + it "should generate a 200 response when a model search call succeeds" do + @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) + @mock_request.stubs(:request_method).returns('GET') + @mock_request.stubs(:path).returns('/foos/key') + @mock_model_class.stubs(:search) + @mock_response.expects(:status=).with(200) + @handler.process(@mock_request, @mock_response) + end + + it "should generate a 200 response when a model destroy call succeeds" do + @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) + @mock_request.stubs(:request_method).returns('DELETE') + @mock_request.stubs(:path).returns('/foo/key') + @mock_model_class.stubs(:destroy) + @mock_response.expects(:status=).with(200) + @handler.process(@mock_request, @mock_response) + end + + it "should generate a 200 response when a model save call succeeds" do + @mock_request.stubs(:request_method).returns('PUT') + @mock_request.stubs(:path).returns('/foo') + @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) + @mock_request.stubs(:body).returns('This is a fake request body') + @mock_model_instance = mock('indirected model instance') + @mock_model_class.stubs(:new).returns(@mock_model_instance) + @mock_model_instance.stubs(:save) + @mock_response.expects(:status=).with(200) + @handler.process(@mock_request, @mock_response) + end + + it "should return a serialized object when a model find call succeeds" it "should return a list of serialized object matches when a model search call succeeds" it "should return a serialized success result when a model destroy call succeeds" -- cgit From e7bfe0bf9b525d6216cbb24be99357d33c0b87ec Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 23 Oct 2007 12:09:05 -0500 Subject: Finish serializing successful results (via calls to to_yaml, etc.) for REST handlers. Refactor request building in REST handler specs. --- lib/puppet/network/http/handler.rb | 14 ++- lib/puppet/network/http/mongrel/rest.rb | 1 + lib/puppet/network/http/webrick/rest.rb | 1 + spec/unit/network/http/mongrel/rest.rb | 147 +++++++++++++++++-------------- spec/unit/network/http/webrick/rest.rb | 149 ++++++++++++++++++-------------- 5 files changed, 177 insertions(+), 135 deletions(-) diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 9b21946c7..2a537c767 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -20,25 +20,31 @@ class Puppet::Network::HTTP::Handler def do_find(request, response) key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") args = params(request) - encode_result(request, response, @model.find(key, args)) + result = @model.find(key, args).to_yaml + encode_result(request, response, result) end def do_search(request, response) args = params(request) - encode_result(request, response, @model.search(args)) + 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) - encode_result(request, response, @model.destroy(key, args)) + 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) - encode_result(request, response, @model.new.save(args.merge(:data => data))) + obj = @model.new + result = obj.save(args.merge(:data => data)).to_yaml + encode_result(request, response, result) end def find_model_for_handler(handler) diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index 0d29a822e..f22b4c4c9 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -31,6 +31,7 @@ class Puppet::Network::HTTP::MongrelREST < Puppet::Network::HTTP::Handler def encode_result(request, response, result) response.start(200) do |head, body| + body.write(result) end end end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index d30f9318b..8782df14f 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -36,5 +36,6 @@ class Puppet::Network::HTTP::WEBrickREST < Puppet::Network::HTTP::Handler def encode_result(request, response, result) response.status = 200 + response.body = result end end \ No newline at end of file diff --git a/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index 944b0896e..1cdc147dc 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -54,48 +54,68 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do confine "Mongrel is not available" => Puppet.features.mongrel? before do - @mock_request = mock('mongrel http request') - @mock_response = mock('mongrel http response') - @mock_response.stubs(:start) - @mock_model_class = mock('indirected model class') + @mock_request = stub('mongrel http request') + @mock_head = stub('response head') + @mock_body = stub('response body', :write => true) + @mock_response = stub('mongrel http response') + @mock_response.stubs(:start).yields(@mock_head, @mock_body) + @mock_model_class = stub('indirected model class') + @mock_mongrel = stub('mongrel http server', :register => true) Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model_class) - @mock_mongrel = mock('mongrel http server') - @mock_mongrel.stubs(:register) @handler = Puppet::Network::HTTP::MongrelREST.new(:server => @mock_mongrel, :handler => :foo) end - it "should call the model find method if the request represents a singular HTTP GET" do + def setup_find_request(params = {}) @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/foo/key', - 'QUERY_STRING' => ''}) + 'QUERY_STRING' => ''}.merge(params)) + @mock_model_class.stubs(:find) + end + + def setup_search_request(params = {}) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', + Mongrel::Const::REQUEST_PATH => '/foos', + 'QUERY_STRING' => '' }.merge(params)) + @mock_model_class.stubs(:search).returns([]) + end + + def setup_destroy_request(params = {}) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', + Mongrel::Const::REQUEST_PATH => '/foo/key', + 'QUERY_STRING' => '' }.merge(params)) + @mock_model_class.stubs(:destroy) + end + + def setup_save_request(params = {}) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', + Mongrel::Const::REQUEST_PATH => '/foo', + 'QUERY_STRING' => '' }.merge(params)) + @mock_request.stubs(:body).returns('this is a fake request body') + @mock_model_instance = stub('indirected model instance', :save => true) + @mock_model_class.stubs(:new).returns(@mock_model_instance) + end + + it "should call the model find method if the request represents a singular HTTP GET" do + setup_find_request @mock_model_class.expects(:find).with('key', {}) @handler.process(@mock_request, @mock_response) end it "should call the model search method if the request represents a plural HTTP GET" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', - Mongrel::Const::REQUEST_PATH => '/foos', - 'QUERY_STRING' => '' }) - @mock_model_class.expects(:search).with({}) + setup_search_request + @mock_model_class.expects(:search).with({}).returns([]) @handler.process(@mock_request, @mock_response) end it "should call the model destroy method if the request represents an HTTP DELETE" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', - Mongrel::Const::REQUEST_PATH => '/foo/key', - 'QUERY_STRING' => '' }) + setup_destroy_request @mock_model_class.expects(:destroy).with('key', {}) @handler.process(@mock_request, @mock_response) end it "should call the model save method if the request represents an HTTP PUT" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', - Mongrel::Const::REQUEST_PATH => '/foo', - 'QUERY_STRING' => '' }) - @mock_request.stubs(:body).returns('this is a fake request body') - mock_model_instance = mock('indirected model instance') - mock_model_instance.expects(:save).with(:data => 'this is a fake request body') - @mock_model_class.expects(:new).returns(mock_model_instance) + setup_save_request + @mock_model_instance.expects(:save).with(:data => 'this is a fake request body') @handler.process(@mock_request, @mock_response) end @@ -135,9 +155,7 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do end it "should pass HTTP request parameters to model find" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', - Mongrel::Const::REQUEST_PATH => '/foo/key', - 'QUERY_STRING' => 'foo=baz&bar=xyzzy'}) + setup_find_request('QUERY_STRING' => 'foo=baz&bar=xyzzy') @mock_model_class.expects(:find).with do |key, args| key == 'key' and args['foo'] == 'baz' and args['bar'] == 'xyzzy' end @@ -145,19 +163,15 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do end it "should pass HTTP request parameters to model search" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', - Mongrel::Const::REQUEST_PATH => '/foos', - 'QUERY_STRING' => 'foo=baz&bar=xyzzy'}) + setup_search_request('QUERY_STRING' => 'foo=baz&bar=xyzzy') @mock_model_class.expects(:search).with do |args| args['foo'] == 'baz' and args['bar'] == 'xyzzy' - end + end.returns([]) @handler.process(@mock_request, @mock_response) end it "should pass HTTP request parameters to model delete" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', - Mongrel::Const::REQUEST_PATH => '/foo/key', - 'QUERY_STRING' => 'foo=baz&bar=xyzzy'}) + setup_destroy_request('QUERY_STRING' => 'foo=baz&bar=xyzzy') @mock_model_class.expects(:destroy).with do |key, args| key == 'key' and args['foo'] == 'baz' and args['bar'] == 'xyzzy' end @@ -165,64 +179,65 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do end it "should pass HTTP request parameters to model save" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', - Mongrel::Const::REQUEST_PATH => '/foo', - 'QUERY_STRING' => 'foo=baz&bar=xyzzy'}) - @mock_request.stubs(:body).returns('this is a fake request body') - mock_model_instance = mock('indirected model instance') - mock_model_instance.expects(:save).with do |args| + setup_save_request('QUERY_STRING' => 'foo=baz&bar=xyzzy') + @mock_model_instance.expects(:save).with do |args| args[:data] == 'this is a fake request body' and args['foo'] == 'baz' and args['bar'] == 'xyzzy' end - @mock_model_class.expects(:new).returns(mock_model_instance) @handler.process(@mock_request, @mock_response) end it "should generate a 200 response when a model find call succeeds" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', - Mongrel::Const::REQUEST_PATH => '/foo/key', - 'QUERY_STRING' => ''}) - @mock_model_class.stubs(:find) + setup_find_request @mock_response.expects(:start).with(200) @handler.process(@mock_request, @mock_response) end it "should generate a 200 response when a model search call succeeds" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', - Mongrel::Const::REQUEST_PATH => '/foos', - 'QUERY_STRING' => ''}) - @mock_model_class.stubs(:search) + setup_search_request @mock_response.expects(:start).with(200) @handler.process(@mock_request, @mock_response) end it "should generate a 200 response when a model destroy call succeeds" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', - Mongrel::Const::REQUEST_PATH => '/foo/key', - 'QUERY_STRING' => ''}) - @mock_model_class.stubs(:destroy) - @mock_response.expects(:start).with(200) - @handler.process(@mock_request, @mock_response) + setup_destroy_request + @mock_response.expects(:start).with(200) + @handler.process(@mock_request, @mock_response) end it "should generate a 200 response when a model save call succeeds" do - @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', - Mongrel::Const::REQUEST_PATH => '/foo', - 'QUERY_STRING' => ''}) - @mock_request.stubs(:body).returns('this is a fake request body') - @mock_model_instance = mock('model instance') - @mock_model_instance.stubs(:save) - @mock_model_class.stubs(:new).returns(@mock_model_instance) - @mock_response.expects(:start).with(200) - @handler.process(@mock_request, @mock_response) + setup_save_request + @mock_response.expects(:start).with(200) + @handler.process(@mock_request, @mock_response) end - it "should return a serialized object when a model find call succeeds" + it "should return a serialized object when a model find call succeeds" do + setup_find_request + @mock_model_instance = stub('model instance') + @mock_model_instance.expects(:to_yaml) + @mock_model_class.stubs(:find).returns(@mock_model_instance) + @handler.process(@mock_request, @mock_response) + end - it "should return a list of serialized object matches when a model search call succeeds" + it "should return a list of serialized objects when a model search call succeeds" do + setup_search_request + mock_matches = [1..5].collect {|i| mock("model instance #{i}", :to_yaml => "model instance #{i}") } + @mock_model_class.stubs(:search).returns(mock_matches) + @handler.process(@mock_request, @mock_response) + end - it "should return a serialized success result when a model destroy call succeeds" + it "should return a serialized success result when a model destroy call succeeds" do + setup_destroy_request + @mock_model_class.stubs(:destroy).returns(true) + @mock_body.expects(:write).with("--- true\n") + @handler.process(@mock_request, @mock_response) + end - it "should return a serialized success result when a model save call succeeds" + it "should return a serialized object when a model save call succeeds" do + setup_save_request + @mock_model_instance.stubs(:save).returns(@mock_model_instance) + @mock_model_instance.expects(:to_yaml).returns('foo') + @handler.process(@mock_request, @mock_response) + end it "should serialize a controller exception when an exception is thrown by the handler" end diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb index 9d1f5fc6b..14505f919 100644 --- a/spec/unit/network/http/webrick/rest.rb +++ b/spec/unit/network/http/webrick/rest.rb @@ -8,8 +8,7 @@ require 'puppet/network/http' describe Puppet::Network::HTTP::WEBrickREST, "when initializing" do before do - @mock_webrick = mock('WEBrick server') - @mock_webrick.stubs(:mount) + @mock_webrick = stub('WEBrick server', :mount => true) @mock_model = mock('indirected model') Puppet::Indirector::Indirection.stubs(:model).returns(@mock_model) @params = { :server => @mock_webrick, :handler => :foo } @@ -50,43 +49,60 @@ end describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do before do - @mock_request = mock('webrick http request') - @mock_request.stubs(:query).returns({}) - @mock_response = mock('webrick http response') - @mock_response.stubs(:status=) - @mock_model_class = mock('indirected model class') + @mock_request = stub('webrick http request', :query => {}) + @mock_response = stub('webrick http response', :status= => true, :body= => true) + @mock_model_class = stub('indirected model class') + @mock_webrick = stub('mongrel http server', :mount => true) Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@mock_model_class) - @mock_webrick = mock('mongrel http server') - @mock_webrick.stubs(:mount) @handler = Puppet::Network::HTTP::WEBrickREST.new(:server => @mock_webrick, :handler => :foo) end + + def setup_find_request + @mock_request.stubs(:request_method).returns('GET') + @mock_request.stubs(:path).returns('/foo/key') + @mock_model_class.stubs(:find) + end - it "should call the model find method if the request represents a singular HTTP GET" do + def setup_search_request @mock_request.stubs(:request_method).returns('GET') + @mock_request.stubs(:path).returns('/foos') + @mock_model_class.stubs(:search).returns([]) + end + + def setup_destroy_request + @mock_request.stubs(:request_method).returns('DELETE') @mock_request.stubs(:path).returns('/foo/key') + @mock_model_class.stubs(:destroy) + end + + def setup_save_request + @mock_request.stubs(:request_method).returns('PUT') + @mock_request.stubs(:path).returns('/foo') + @mock_request.stubs(:body).returns('This is a fake request body') + @mock_model_instance = stub('indirected model instance', :save => true) + @mock_model_class.stubs(:new).returns(@mock_model_instance) + end + + it "should call the model find method if the request represents a singular HTTP GET" do + setup_find_request @mock_model_class.expects(:find).with('key', {}) @handler.service(@mock_request, @mock_response) end it "should call the model search method if the request represents a plural HTTP GET" do - @mock_request.stubs(:request_method).returns('GET') - @mock_request.stubs(:path).returns('/foos') - @mock_model_class.expects(:search) + setup_search_request + @mock_model_class.expects(:search).returns([]) @handler.service(@mock_request, @mock_response) end it "should call the model destroy method if the request represents an HTTP DELETE" do - @mock_request.stubs(:request_method).returns('DELETE') - @mock_request.stubs(:path).returns('/foo/key') + setup_destroy_request @mock_model_class.expects(:destroy).with('key', {}) @handler.service(@mock_request, @mock_response) end it "should call the model save method if the request represents an HTTP PUT" do - @mock_request.stubs(:request_method).returns('PUT') - @mock_request.stubs(:path).returns('/foo') - @mock_request.stubs(:body).returns('This is a fake request body') - @mock_model_instance = mock('indirected model instance') + setup_save_request @mock_model_instance.expects(:save).with(:data => 'This is a fake request body') @mock_model_class.expects(:new).returns(@mock_model_instance) @handler.service(@mock_request, @mock_response) @@ -102,6 +118,7 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do @mock_request.stubs(:request_method).returns('DELETE') @mock_request.stubs(:path).returns('/foos/key') Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_request.stubs(:request_method).returns('PUT') @mock_request.stubs(:path).returns('/foos/key') Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) @@ -133,9 +150,8 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do end it "should pass HTTP request parameters to model find" do + setup_find_request @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) - @mock_request.stubs(:request_method).returns('GET') - @mock_request.stubs(:path).returns('/foo/key') @mock_model_class.expects(:find).with do |key, args| key == 'key' and args[:foo] == :baz and args[:bar] == :xyzzy end @@ -143,19 +159,17 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do end it "should pass HTTP request parameters to model search" do + setup_search_request @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) - @mock_request.stubs(:request_method).returns('GET') - @mock_request.stubs(:path).returns('/foos/key') @mock_model_class.expects(:search).with do |args| args[:foo] == :baz and args[:bar] == :xyzzy - end + end.returns([]) @handler.service(@mock_request, @mock_response) end it "should pass HTTP request parameters to model destroy" do + setup_destroy_request @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) - @mock_request.stubs(:request_method).returns('DELETE') - @mock_request.stubs(:path).returns('/foo/key') @mock_model_class.expects(:destroy).with do |key, args| key == 'key' and args[:foo] == :baz and args[:bar] == :xyzzy end @@ -163,61 +177,66 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do end it "should pass HTTP request parameters to model save" do - @mock_request.stubs(:request_method).returns('PUT') - @mock_request.stubs(:path).returns('/foo') + setup_save_request @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) - @mock_request.stubs(:body).returns('This is a fake request body') - @mock_model_instance = mock('indirected model instance') @mock_model_instance.expects(:save).with do |args| args[:data] == 'This is a fake request body' and args[:foo] == :baz and args[:bar] == :xyzzy end - @mock_model_class.expects(:new).returns(@mock_model_instance) @handler.service(@mock_request, @mock_response) end it "should generate a 200 response when a model find call succeeds" do - @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) - @mock_request.stubs(:request_method).returns('GET') - @mock_request.stubs(:path).returns('/foo/key') - @mock_model_class.stubs(:find) - @mock_response.expects(:status=).with(200) - @handler.process(@mock_request, @mock_response) + setup_find_request + @mock_response.expects(:status=).with(200) + @handler.process(@mock_request, @mock_response) end it "should generate a 200 response when a model search call succeeds" do - @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) - @mock_request.stubs(:request_method).returns('GET') - @mock_request.stubs(:path).returns('/foos/key') - @mock_model_class.stubs(:search) - @mock_response.expects(:status=).with(200) - @handler.process(@mock_request, @mock_response) + setup_search_request + @mock_response.expects(:status=).with(200) + @handler.process(@mock_request, @mock_response) end it "should generate a 200 response when a model destroy call succeeds" do - @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) - @mock_request.stubs(:request_method).returns('DELETE') - @mock_request.stubs(:path).returns('/foo/key') - @mock_model_class.stubs(:destroy) - @mock_response.expects(:status=).with(200) - @handler.process(@mock_request, @mock_response) + setup_destroy_request + @mock_response.expects(:status=).with(200) + @handler.process(@mock_request, @mock_response) end it "should generate a 200 response when a model save call succeeds" do - @mock_request.stubs(:request_method).returns('PUT') - @mock_request.stubs(:path).returns('/foo') - @mock_request.stubs(:query).returns(:foo => :baz, :bar => :xyzzy) - @mock_request.stubs(:body).returns('This is a fake request body') - @mock_model_instance = mock('indirected model instance') - @mock_model_class.stubs(:new).returns(@mock_model_instance) - @mock_model_instance.stubs(:save) - @mock_response.expects(:status=).with(200) - @handler.process(@mock_request, @mock_response) - end - - - it "should return a serialized object when a model find call succeeds" - it "should return a list of serialized object matches when a model search call succeeds" - it "should return a serialized success result when a model destroy call succeeds" - it "should return a serialized success result when a model save call succeeds" + setup_save_request + @mock_response.expects(:status=).with(200) + @handler.process(@mock_request, @mock_response) + end + + it "should return a serialized object when a model find call succeeds" do + setup_find_request + @mock_model_instance = stub('model instance') + @mock_model_instance.expects(:to_yaml) + @mock_model_class.stubs(:find).returns(@mock_model_instance) + @handler.process(@mock_request, @mock_response) + end + + it "should return a list of serialized objects when a model search call succeeds" do + setup_search_request + mock_matches = [1..5].collect {|i| mock("model instance #{i}", :to_yaml => "model instance #{i}") } + @mock_model_class.stubs(:search).returns(mock_matches) + @handler.process(@mock_request, @mock_response) + end + + it "should return a serialized success result when a model destroy call succeeds" do + setup_destroy_request + @mock_model_class.stubs(:destroy).returns(true) + @mock_response.expects(:body=).with("--- true\n") + @handler.process(@mock_request, @mock_response) + end + + it "should return a serialized object when a model save call succeeds" do + setup_save_request + @mock_model_instance.stubs(:save).returns(@mock_model_instance) + @mock_model_instance.expects(:to_yaml).returns('foo') + @handler.process(@mock_request, @mock_response) + end + it "should serialize a controller exception when an exception is thrown by the handler" end -- cgit From 54fc80d5de7b881adca06c85206fb700f4278a73 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Tue, 23 Oct 2007 12:32:45 -0500 Subject: Exceptions on requests are now captured, exceptions are serialized, and exception text is passed back via REST. --- lib/puppet/network/http/handler.rb | 8 ++++- lib/puppet/network/http/mongrel/rest.rb | 4 +-- lib/puppet/network/http/webrick/rest.rb | 4 +-- spec/unit/network/http/mongrel/rest.rb | 62 ++++++++++++++++++++++++++++----- spec/unit/network/http/webrick/rest.rb | 60 ++++++++++++++++++++++++++----- 5 files changed, 116 insertions(+), 22 deletions(-) diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 2a537c767..172939538 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -13,6 +13,8 @@ class Puppet::Network::HTTP::Handler 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 @@ -47,6 +49,10 @@ class Puppet::Network::HTTP::Handler 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}].") @@ -98,7 +104,7 @@ class Puppet::Network::HTTP::Handler raise NotImplementedError end - def encode_result(request, response, result) + def encode_result(request, response, result, status = 200) raise NotImplementedError end end diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index f22b4c4c9..db63613ab 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -29,8 +29,8 @@ class Puppet::Network::HTTP::MongrelREST < Puppet::Network::HTTP::Handler Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]) end - def encode_result(request, response, result) - response.start(200) do |head, body| + def encode_result(request, response, result, status = 200) + response.start(status) do |head, body| body.write(result) end end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index 8782df14f..dd0c84d61 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -34,8 +34,8 @@ class Puppet::Network::HTTP::WEBrickREST < Puppet::Network::HTTP::Handler request.query end - def encode_result(request, response, result) - response.status = 200 + 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/spec/unit/network/http/mongrel/rest.rb b/spec/unit/network/http/mongrel/rest.rb index 1cdc147dc..9b2feb6ee 100644 --- a/spec/unit/network/http/mongrel/rest.rb +++ b/spec/unit/network/http/mongrel/rest.rb @@ -34,7 +34,7 @@ describe Puppet::Network::HTTP::MongrelREST, "when initializing" do Puppet::Indirector::Indirection.expects(:model).with(:foo).returns(nil) Proc.new { Puppet::Network::HTTP::MongrelREST.new(@params) }.should raise_error(ArgumentError) end - + it "should register itself with the mongrel server for the singular HTTP methods" do @mock_mongrel.expects(:register).with do |*args| args.first == '/foo' and args.last.is_a? Puppet::Network::HTTP::MongrelREST @@ -95,6 +95,10 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do @mock_model_class.stubs(:new).returns(@mock_model_instance) end + def setup_bad_request + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'POST', Mongrel::Const::REQUEST_PATH => '/foos'}) + end + it "should call the model find method if the request represents a singular HTTP GET" do setup_find_request @mock_model_class.expects(:find).with('key', {}) @@ -121,37 +125,45 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do it "should fail if the HTTP method isn't supported" do @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'POST', Mongrel::Const::REQUEST_PATH => '/foo'}) - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) end it "should fail if the request's pluralization is wrong" do @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foos/key'}) - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) + @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foos/key'}) - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) end it "should fail if the request is for an unknown path" do @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/bar/key', 'QUERY_STRING' => '' }) - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) end it "should fail to find model if key is not specified" do @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'GET', Mongrel::Const::REQUEST_PATH => '/foo'}) - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) end it "should fail to destroy model if key is not specified" do @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'DELETE', Mongrel::Const::REQUEST_PATH => '/foo'}) - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) end it "should fail to save model if data is not specified" do @mock_request.stubs(:params).returns({ Mongrel::Const::REQUEST_METHOD => 'PUT', Mongrel::Const::REQUEST_PATH => '/foo'}) @mock_request.stubs(:body).returns('') - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) end it "should pass HTTP request parameters to model find" do @@ -239,5 +251,37 @@ describe Puppet::Network::HTTP::MongrelREST, "when receiving a request" do @handler.process(@mock_request, @mock_response) end - it "should serialize a controller exception when an exception is thrown by the handler" + it "should serialize a controller exception when an exception is thrown by find" do + setup_find_request + @mock_model_class.expects(:find).raises(ArgumentError) + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) + end + + it "should serialize a controller exception when an exception is thrown by search" do + setup_search_request + @mock_model_class.expects(:search).raises(ArgumentError) + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) + end + + it "should serialize a controller exception when an exception is thrown by destroy" do + setup_destroy_request + @mock_model_class.expects(:destroy).raises(ArgumentError) + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) + end + + it "should serialize a controller exception when an exception is thrown by save" do + setup_save_request + @mock_model_instance.expects(:save).raises(ArgumentError) + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) + end + + it "should serialize a controller exception if the request fails" do + setup_bad_request + @mock_response.expects(:start).with(404) + @handler.process(@mock_request, @mock_response) + end end diff --git a/spec/unit/network/http/webrick/rest.rb b/spec/unit/network/http/webrick/rest.rb index 14505f919..aa7a3d53a 100644 --- a/spec/unit/network/http/webrick/rest.rb +++ b/spec/unit/network/http/webrick/rest.rb @@ -83,6 +83,11 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do @mock_model_class.stubs(:new).returns(@mock_model_instance) end + def setup_bad_request + @mock_request.stubs(:request_method).returns('POST') + @mock_request.stubs(:path).returns('/foos') + end + it "should call the model find method if the request represents a singular HTTP GET" do setup_find_request @mock_model_class.expects(:find).with('key', {}) @@ -111,42 +116,49 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do it "should fail if the HTTP method isn't supported" do @mock_request.stubs(:request_method).returns('POST') @mock_request.stubs(:path).returns('/foo') - Proc.new { @handler.service(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) end it "should fail if the request's pluralization is wrong" do @mock_request.stubs(:request_method).returns('DELETE') @mock_request.stubs(:path).returns('/foos/key') - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) @mock_request.stubs(:request_method).returns('PUT') @mock_request.stubs(:path).returns('/foos/key') - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) end it "should fail if the request is for an unknown path" do @mock_request.stubs(:request_method).returns('GET') @mock_request.stubs(:path).returns('/bar/key') - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) end it "should fail to find model if key is not specified" do @mock_request.stubs(:request_method).returns('GET') @mock_request.stubs(:path).returns('/foo') - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) end it "should fail to destroy model if key is not specified" do @mock_request.stubs(:request_method).returns('DELETE') @mock_request.stubs(:path).returns('/foo') - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) end it "should fail to save model if data is not specified" do @mock_request.stubs(:request_method).returns('PUT') @mock_request.stubs(:path).returns('/foo') @mock_request.stubs(:body).returns('') - Proc.new { @handler.process(@mock_request, @mock_response) }.should raise_error(ArgumentError) + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) end it "should pass HTTP request parameters to model find" do @@ -238,5 +250,37 @@ describe Puppet::Network::HTTP::WEBrickREST, "when receiving a request" do @handler.process(@mock_request, @mock_response) end - it "should serialize a controller exception when an exception is thrown by the handler" + it "should serialize a controller exception when an exception is thrown by find" do + setup_find_request + @mock_model_class.expects(:find).raises(ArgumentError) + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) + end + + it "should serialize a controller exception when an exception is thrown by search" do + setup_search_request + @mock_model_class.expects(:search).raises(ArgumentError) + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) + end + + it "should serialize a controller exception when an exception is thrown by destroy" do + setup_destroy_request + @mock_model_class.expects(:destroy).raises(ArgumentError) + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) + end + + it "should serialize a controller exception when an exception is thrown by save" do + setup_save_request + @mock_model_instance.expects(:save).raises(ArgumentError) + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) + end + + it "should serialize a controller exception if the request fails" do + setup_bad_request + @mock_response.expects(:status=).with(404) + @handler.process(@mock_request, @mock_response) + end end -- cgit From c7b36b76f1319ee18efee8ec1bdf08825cb66f81 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Fri, 26 Oct 2007 15:23:51 -0500 Subject: One significant step closer to getting autotest running properly on the Puppet specs. Created a spec/lib/monkey_patches/ directory for holding patches to RSpec functionality. Extraced 'confine' and 'runnable?' support from the local copy of RSpec (spec/lib/spec/) and now load them from the monkey_patches/ directory. Fixed a bad include in one of the specs. Made it possible for the gem-installed spec binary (which autotest calls) to be used with Puppet. Imported the Autotest::Rspec class, created a PuppetRspec autotest class, added a discovery.rb file for autotest to pick these up. Autotest still has the following problems: * it needs to be run with the proper include path: % ruby -I spec/lib/ `which autotest` * the patterns in our custom autotest handler (puppet_rspec) aren't yet fully specified (they only recognize changes in our spec files, not changes in the puppet libs which they are testing) --- lib/puppet/network/http/handler.rb | 3 +- spec/lib/autotest/discover.rb | 9 ++ spec/lib/autotest/puppet_rspec.rb | 13 +++ spec/lib/autotest/rspec.rb | 95 ++++++++++++++++++++++ .../add_confine_and_runnable_to_rspec_dsl.rb | 30 +++++++ spec/lib/spec/dsl/behaviour.rb | 3 - spec/lib/spec/runner/behaviour_runner.rb | 2 - spec/spec_helper.rb | 13 +-- spec/unit/indirector/node/memory.rb | 5 +- spec/unit/network/http.rb | 2 +- 10 files changed, 158 insertions(+), 17 deletions(-) create mode 100644 spec/lib/autotest/discover.rb create mode 100644 spec/lib/autotest/puppet_rspec.rb create mode 100644 spec/lib/autotest/rspec.rb create mode 100644 spec/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 172939538..773381c8d 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -29,8 +29,7 @@ class Puppet::Network::HTTP::Handler def do_search(request, response) args = params(request) result = @model.search(args).collect {|obj| obj.to_yaml } - encode_result(request, response, result) - + encode_result(request, response, result) end def do_destroy(request, response) diff --git a/spec/lib/autotest/discover.rb b/spec/lib/autotest/discover.rb new file mode 100644 index 000000000..0ac563724 --- /dev/null +++ b/spec/lib/autotest/discover.rb @@ -0,0 +1,9 @@ +require 'autotest' + +Autotest.add_discovery do + "rspec" +end + +Autotest.add_discovery do + "puppet" +end diff --git a/spec/lib/autotest/puppet_rspec.rb b/spec/lib/autotest/puppet_rspec.rb new file mode 100644 index 000000000..e7de8b615 --- /dev/null +++ b/spec/lib/autotest/puppet_rspec.rb @@ -0,0 +1,13 @@ +require 'autotest' +require 'autotest/rspec' + +class Autotest::PuppetRspec < Autotest::Rspec + def initialize # :nodoc: + super + @test_mappings = { + %r%^spec/(unit|integration)/.*\.rb$% => proc { |filename, _| + filename + }, + } + end +end diff --git a/spec/lib/autotest/rspec.rb b/spec/lib/autotest/rspec.rb new file mode 100644 index 000000000..d4b77ea6b --- /dev/null +++ b/spec/lib/autotest/rspec.rb @@ -0,0 +1,95 @@ +require 'autotest' + +class RspecCommandError < StandardError; end + +class Autotest::Rspec < Autotest + + def initialize(kernel=Kernel, separator=File::SEPARATOR, alt_separator=File::ALT_SEPARATOR) # :nodoc: + super() + @kernel, @separator, @alt_separator = kernel, separator, alt_separator + @spec_command = spec_command + + # watch out: Ruby bug (1.8.6): + # %r(/) != /\// + # since Ruby compares the REGEXP source, not the resulting pattern + @test_mappings = { + %r%^spec/.*\.rb$% => kernel.proc { |filename, _| + filename + }, + %r%^lib/(.*)\.rb$% => kernel.proc { |_, m| + ["spec/#{m[1]}_spec.rb"] + }, + %r%^spec/(spec_helper|shared/.*)\.rb$% => kernel.proc { + files_matching %r%^spec/.*_spec\.rb$% + } + } + end + + def tests_for_file(filename) + super.select { |f| @files.has_key? f } + end + + alias :specs_for_file :tests_for_file + + def failed_results(results) + results.scan(/^\d+\)\n(?:\e\[\d*m)?(?:.*?Error in )?'([^\n]*)'(?: FAILED)?(?:\e\[\d*m)?\n(.*?)\n\n/m) + end + + def handle_results(results) + @files_to_test = consolidate_failures failed_results(results) + unless @files_to_test.empty? then + hook :red + else + hook :green + end unless $TESTING + @tainted = true unless @files_to_test.empty? + end + + def consolidate_failures(failed) + filters = Hash.new { |h,k| h[k] = [] } + failed.each do |spec, failed_trace| + @files.keys.select{|f| f =~ /spec\//}.each do |f| + if failed_trace =~ Regexp.new(f) + filters[f] << spec + break + end + end + end + return filters + end + + def make_test_cmd(files_to_test) + return "#{ruby} -S #{@spec_command} #{add_options_if_present} #{files_to_test.keys.flatten.join(' ')}" + end + + def add_options_if_present + File.exist?("spec/spec.opts") ? "-O spec/spec.opts " : "" + end + + # Finds the proper spec command to use. Precendence + # is set in the lazily-evaluated method spec_commands. Alias + Override + # that in ~/.autotest to provide a different spec command + # then the default paths provided. + def spec_command + spec_commands.each do |command| + if File.exists?(command) + return @alt_separator ? (command.gsub @separator, @alt_separator) : command + end + end + + raise RspecCommandError, "No spec command could be found!" + end + + # Autotest will look for spec commands in the following + # locations, in this order: + # + # * bin/spec + # * default spec bin/loader installed in Rubygems + def spec_commands + [ + File.join('bin', 'spec'), + File.join(Config::CONFIG['bindir'], 'spec') + ] + end + +end diff --git a/spec/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb b/spec/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb new file mode 100644 index 000000000..a2467eac8 --- /dev/null +++ b/spec/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl.rb @@ -0,0 +1,30 @@ +dir = File.expand_path(File.dirname(__FILE__)) +$LOAD_PATH.unshift("#{dir}/../../lib") +$LOAD_PATH.unshift("#{dir}/../../../lib") +$LOAD_PATH.unshift("#{dir}/../../../test/lib") # Add the old test dir, so that we can still find our local mocha and spec + +require 'spec' +require 'puppettest' +require 'puppettest/runnable_test' + +module Spec + module Runner + class BehaviourRunner + def run_behaviours + @behaviours.each do |behaviour| + # LAK:NOTE: this 'runnable' test is Puppet-specific. + next unless behaviour.runnable? + behaviour.run(@options.reporter, @options.dry_run, @options.reverse, @options.timeout) + end + end + end + end +end + +module Spec + module DSL + class EvalModule < Module + include PuppetTest::RunnableTest + end + end +end diff --git a/spec/lib/spec/dsl/behaviour.rb b/spec/lib/spec/dsl/behaviour.rb index 159a0ba7e..cc71ccffe 100644 --- a/spec/lib/spec/dsl/behaviour.rb +++ b/spec/lib/spec/dsl/behaviour.rb @@ -2,9 +2,6 @@ require(File.expand_path(File.dirname(__FILE__) + '../../../../../test/lib/puppe module Spec module DSL - class EvalModule < Module; - include PuppetTest::RunnableTest - end class Behaviour extend BehaviourCallbacks diff --git a/spec/lib/spec/runner/behaviour_runner.rb b/spec/lib/spec/runner/behaviour_runner.rb index 078490e92..1ac891f3c 100644 --- a/spec/lib/spec/runner/behaviour_runner.rb +++ b/spec/lib/spec/runner/behaviour_runner.rb @@ -55,8 +55,6 @@ module Spec def run_behaviours @behaviours.each do |behaviour| - # LAK:NOTE: this 'runnable' test is Puppet-specific. - next unless behaviour.runnable? behaviour.run(@options.reporter, @options.dry_run, @options.reverse, @options.timeout) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3017f272a..bfac9095f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,12 +1,11 @@ dir = File.expand_path(File.dirname(__FILE__)) -$:.unshift("#{dir}/lib") -$:.unshift("#{dir}/../lib") +$LOAD_PATH.unshift("#{dir}/lib") +$LOAD_PATH.unshift("#{dir}/../lib") +$LOAD_PATH.unshift("#{dir}/../test/lib") # Add the old test dir, so that we can still find our local mocha and spec -# Add the old test dir, so that we can still find mocha and spec -$:.unshift("#{dir}/../test/lib") - -require 'mocha' require 'puppettest' +require 'puppettest/runnable_test' +require 'mocha' require 'spec' Spec::Runner.configure do |config| @@ -19,3 +18,5 @@ Spec::Runner.configure do |config| teardown() if respond_to? :teardown end end + +require "#{dir}/lib/monkey_patches/add_confine_and_runnable_to_rspec_dsl" diff --git a/spec/unit/indirector/node/memory.rb b/spec/unit/indirector/node/memory.rb index f57cae818..a924c6209 100755 --- a/spec/unit/indirector/node/memory.rb +++ b/spec/unit/indirector/node/memory.rb @@ -4,9 +4,8 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/indirector/node/memory' -# All of our behaviour is described here, so we always have to -# include it. -require 'unit/indirector/memory' +# All of our behaviour is described here, so we always have to include it. +require File.dirname(__FILE__) + '/../memory' describe Puppet::Node::Memory do before do diff --git a/spec/unit/network/http.rb b/spec/unit/network/http.rb index a282b650a..79a0a88d4 100644 --- a/spec/unit/network/http.rb +++ b/spec/unit/network/http.rb @@ -26,4 +26,4 @@ describe Puppet::Network::HTTP do it "should return an error when asked for an unknown server" do Proc.new { Puppet::Network::HTTP.server_class_by_type :foo }.should raise_error(ArgumentError) end -end \ No newline at end of file +end -- cgit From 956daa5b4b1c61db9a5e1d7638ca819005fd7ef0 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Fri, 26 Oct 2007 22:58:47 -0500 Subject: This won't be perfect by any stretch, but put in a moderately reasonable autotest config file. --- spec/lib/autotest/puppet_rspec.rb | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/spec/lib/autotest/puppet_rspec.rb b/spec/lib/autotest/puppet_rspec.rb index e7de8b615..8536f3912 100644 --- a/spec/lib/autotest/puppet_rspec.rb +++ b/spec/lib/autotest/puppet_rspec.rb @@ -5,9 +5,42 @@ class Autotest::PuppetRspec < Autotest::Rspec def initialize # :nodoc: super @test_mappings = { + # the libraries under lib/puppet + %r%^lib/puppet/(.*)\.rb$% => proc { |filename, m| + files_matching %r!spec/(unit|integration)/#{m[1]}.rb! + }, + + # the actual spec files themselves %r%^spec/(unit|integration)/.*\.rb$% => proc { |filename, _| filename }, + + # force a complete re-run for all of these: + + # main puppet lib + %r!^lib/puppet\.rb$! => proc { |filename, _| + files_matching %r!spec/(unit|integration)/.*\.rb! + }, + + # the spec_helper + %r!^spec/spec_helper\.rb$! => proc { |filename, _| + files_matching %r!spec/(unit|integration)/.*\.rb! + }, + + # the puppet test libraries + %r!^test/lib/puppettest/.*! => proc { |filename, _| + files_matching %r!spec/(unit|integration)/.*\.rb! + }, + + # the puppet spec libraries + %r!^spec/lib/spec.*! => proc { |filename, _| + files_matching %r!spec/(unit|integration)/.*\.rb! + }, + + # the monkey patches for rspec + %r!^spec/lib/monkey_patches/.*! => proc { |filename, _| + files_matching %r!spec/(unit|integration)/.*\.rb! + }, } end end -- cgit