From a7d220b828ec5f277a3a3bfb33f517fe864579d0 Mon Sep 17 00:00:00 2001 From: Rick Bradley Date: Fri, 5 Oct 2007 19:51:47 -0500 Subject: Moving the webrick/mongrel "servers" over to HTTPServer module instead of Server. Using Server as the master class for client connections. Server (former RESTServer) will instantiate the appropriate subclass based upon Puppet configurator setting. There are now tests broken in the network section which I can't seem to figure out yet. Not a happy place to be. --- bin/puppetd | 4 +- bin/puppetmasterd | 8 +- lib/puppet/feature/base.rb | 2 +- lib/puppet/network/http_server.rb | 2 + lib/puppet/network/http_server/mongrel.rb | 139 +++++++++++++++++++++++ lib/puppet/network/http_server/webrick.rb | 163 +++++++++++++++++++++++++++ lib/puppet/network/rest_server.rb | 37 ------ lib/puppet/network/server.rb | 66 ++++++++++- lib/puppet/network/server/mongrel.rb | 139 ----------------------- lib/puppet/network/server/webrick.rb | 163 --------------------------- spec/unit/network/rest_server.rb | 136 ---------------------- spec/unit/network/server.rb | 181 ++++++++++++++++++++++++++++++ test/lib/puppettest/servertest.rb | 4 +- test/network/handler/ca.rb | 2 +- test/network/server/mongrel_test.rb | 2 +- test/network/server/webrick.rb | 8 +- test/ral/types/filesources.rb | 4 +- 17 files changed, 566 insertions(+), 494 deletions(-) create mode 100644 lib/puppet/network/http_server.rb create mode 100644 lib/puppet/network/http_server/mongrel.rb create mode 100644 lib/puppet/network/http_server/webrick.rb delete mode 100644 lib/puppet/network/rest_server.rb delete mode 100644 lib/puppet/network/server/mongrel.rb delete mode 100644 lib/puppet/network/server/webrick.rb delete mode 100644 spec/unit/network/rest_server.rb create mode 100644 spec/unit/network/server.rb diff --git a/bin/puppetd b/bin/puppetd index a09d3c73b..1235a36fe 100755 --- a/bin/puppetd +++ b/bin/puppetd @@ -390,10 +390,10 @@ if Puppet[:listen] and ! options[:onetime] args[:Handlers] = handlers args[:Port] = Puppet[:puppetport] - require 'puppet/network/server/webrick' + require 'puppet/network/http_server/webrick' begin - server = Puppet::Network::Server::WEBrick.new(args) + server = Puppet::Network::HTTPServer::WEBrick.new(args) rescue => detail $stderr.puts detail puts detail.backtrace diff --git a/bin/puppetmasterd b/bin/puppetmasterd index f764b1901..39802f229 100755 --- a/bin/puppetmasterd +++ b/bin/puppetmasterd @@ -238,11 +238,11 @@ begin case Puppet[:servertype] when "webrick" # use the default, um, everything - require 'puppet/network/server/webrick' - webserver = server = Puppet::Network::Server::WEBrick.new(:Handlers => handlers) + require 'puppet/network/http_server/webrick' + webserver = server = Puppet::Network::HTTPServer::WEBrick.new(:Handlers => handlers) when "mongrel": - require 'puppet/network/server/mongrel' - server = Puppet::Network::Server::Mongrel.new(handlers) + require 'puppet/network/http_server/mongrel' + server = Puppet::Network::HTTPServer::Mongrel.new(handlers) addr = Puppet[:bindaddress] if addr == "" addr = "127.0.0.1" diff --git a/lib/puppet/feature/base.rb b/lib/puppet/feature/base.rb index 6368d0931..518e9b419 100644 --- a/lib/puppet/feature/base.rb +++ b/lib/puppet/feature/base.rb @@ -18,7 +18,7 @@ Puppet.features.add(:libshadow, :libs => ["shadow"]) Puppet.features.add(:root) { require 'puppet/util/suidmanager'; Puppet::Util::SUIDManager.uid == 0 } # We've got mongrel available -Puppet.features.add(:mongrel, :libs => %w{rubygems mongrel puppet/network/server/mongrel}) +Puppet.features.add(:mongrel, :libs => %w{rubygems mongrel puppet/network/http_server/mongrel}) # We have lcs diff Puppet.features.add :diff, :libs => %w{diff/lcs diff/lcs/hunk} diff --git a/lib/puppet/network/http_server.rb b/lib/puppet/network/http_server.rb new file mode 100644 index 000000000..1ca01b0a6 --- /dev/null +++ b/lib/puppet/network/http_server.rb @@ -0,0 +1,2 @@ +module Puppet::Network::HTTPServer +end diff --git a/lib/puppet/network/http_server/mongrel.rb b/lib/puppet/network/http_server/mongrel.rb new file mode 100644 index 000000000..ba4b048b3 --- /dev/null +++ b/lib/puppet/network/http_server/mongrel.rb @@ -0,0 +1,139 @@ +#!/usr/bin/env ruby +# File: 06-11-14-mongrel_xmlrpc.rb +# Author: Manuel Holtgrewe +# +# Copyright (c) 2006 Manuel Holtgrewe, 2007 Luke Kanies +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# This file is based heavily on a file retrieved from +# http://ttt.ggnore.net/2006/11/15/xmlrpc-with-mongrel-and-ruby-off-rails/ + +require 'rubygems' +require 'mongrel' +require 'xmlrpc/server' +require 'puppet/network/xmlrpc/server' +require 'puppet/network/http_server' +require 'puppet/network/client_request' +require 'puppet/daemon' + +require 'resolv' + +# This handler can be hooked into Mongrel to accept HTTP requests. After +# checking whether the request itself is sane, the handler forwards it +# to an internal instance of XMLRPC::BasicServer to process it. +# +# You can access the server by calling the Handler's "xmlrpc_server" +# attribute accessor method and add XMLRPC handlers there. For example: +# +#
+# handler = XmlRpcHandler.new
+# handler.xmlrpc_server.add_handler("my.add") { |a, b| a.to_i + b.to_i }
+# 
+module Puppet::Network + class HTTPServer::Mongrel < ::Mongrel::HttpHandler + include Puppet::Daemon + attr_reader :xmlrpc_server + + def initialize(handlers) + # Create a new instance of BasicServer. We are supposed to subclass it + # but that does not make sense since we would not introduce any new + # behaviour and we have to subclass Mongrel::HttpHandler so our handler + # works for Mongrel. + @xmlrpc_server = Puppet::Network::XMLRPCServer.new + handlers.each do |name, args| + unless handler = Puppet::Network::Handler.handler(name) + raise ArgumentError, "Invalid handler %s" % name + end + @xmlrpc_server.add_handler(handler.interface, handler.new(args)) + end + end + + # This method produces the same results as XMLRPC::CGIServer.serve + # from Ruby's stdlib XMLRPC implementation. + def process(request, response) + # Make sure this has been a POST as required for XMLRPC. + request_method = request.params[Mongrel::Const::REQUEST_METHOD] || Mongrel::Const::GET + if request_method != "POST" then + response.start(405) { |head, out| out.write("Method Not Allowed") } + return + end + + # Make sure the user has sent text/xml data. + request_mime = request.params["CONTENT_TYPE"] || "text/plain" + if parse_content_type(request_mime).first != "text/xml" then + response.start(400) { |head, out| out.write("Bad Request") } + return + end + + # Make sure there is data in the body at all. + length = request.params[Mongrel::Const::CONTENT_LENGTH].to_i + if length <= 0 then + response.start(411) { |head, out| out.write("Length Required") } + return + end + + # Check the body to be valid. + if request.body.nil? or request.body.size != length then + response.start(400) { |head, out| out.write("Bad Request") } + return + end + + info = client_info(request) + + # All checks above passed through + response.start(200) do |head, out| + head["Content-Type"] = "text/xml; charset=utf-8" + begin + out.write(@xmlrpc_server.process(request.body, info)) + rescue => detail + puts detail.backtrace + raise + end + end + end + + private + + def client_info(request) + params = request.params + ip = params["REMOTE_ADDR"] + if dn = params[Puppet[:ssl_client_header]] and dn.include?("/CN=") + client = dn.sub("/CN=", '') + valid = (params[Puppet[:ssl_client_verify_header]] == 'SUCCESS') + else + client = Resolv.getname(ip) + valid = false + end + + info = Puppet::Network::ClientRequest.new(client, ip, valid) + + return info + end + + # Taken from XMLRPC::ParseContentType + def parse_content_type(str) + a, *b = str.split(";") + return a.strip, *b + end + end +end + diff --git a/lib/puppet/network/http_server/webrick.rb b/lib/puppet/network/http_server/webrick.rb new file mode 100644 index 000000000..2cf85b7b6 --- /dev/null +++ b/lib/puppet/network/http_server/webrick.rb @@ -0,0 +1,163 @@ +require 'puppet' +require 'puppet/daemon' +require 'webrick' +require 'webrick/https' +require 'fcntl' + +require 'puppet/sslcertificates/support' +require 'puppet/network/xmlrpc/webrick_servlet' +require 'puppet/network/http_server' +require 'puppet/network/client' + +module Puppet + class ServerError < RuntimeError; end + module Network + # The old-school, pure ruby webrick server, which is the default serving + # mechanism. + class HTTPServer::WEBrick < WEBrick::HTTPServer + include Puppet::Daemon + include Puppet::SSLCertificates::Support + + # Read the CA cert and CRL and populate an OpenSSL::X509::Store + # with them, with flags appropriate for checking client + # certificates for revocation + def x509store + if Puppet[:cacrl] == 'none' + # No CRL, no store needed + return nil + end + unless File.exist?(Puppet[:cacrl]) + raise Puppet::Error, "Could not find CRL" + end + crl = OpenSSL::X509::CRL.new(File.read(Puppet[:cacrl])) + store = OpenSSL::X509::Store.new + store.purpose = OpenSSL::X509::PURPOSE_ANY + store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK + unless self.ca_cert + raise Puppet::Error, "No CA certificate" + end + + store.add_file(Puppet[:localcacert]) + store.add_crl(crl) + return store + end + + # Set up the http log. + def httplog + args = [] + + # yuck; separate http logs + file = nil + Puppet.settings.use(:main, :ssl, Puppet[:name]) + if Puppet[:name] == "puppetmasterd" + file = Puppet[:masterhttplog] + else + file = Puppet[:httplog] + end + + # open the log manually to prevent file descriptor leak + file_io = open(file, "a+") + file_io.sync + file_io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) + + args << file_io + if Puppet[:debug] + args << WEBrick::Log::DEBUG + end + + log = WEBrick::Log.new(*args) + + + return log + end + + # Create our server, yo. + def initialize(hash = {}) + Puppet.info "Starting server for Puppet version %s" % Puppet.version + + if handlers = hash[:Handlers] + handler_instances = setup_handlers(handlers) + else + raise ServerError, "A server must have handlers" + end + + unless self.read_cert + if ca = handler_instances.find { |handler| handler.is_a?(Puppet::Network::Handler.ca) } + request_cert(ca) + else + raise Puppet::Error, "No certificate and no CA; cannot get cert" + end + end + + setup_webrick(hash) + + super(hash) + + # make sure children don't inherit the sockets + listeners.each { |sock| + sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) + } + + Puppet.info "Listening on port %s" % hash[:Port] + + # this creates a new servlet for every connection, + # but all servlets have the same list of handlers + # thus, the servlets can have their own state -- passing + # around the requests and such -- but the handlers + # have a global state + + # mount has to be called after the server is initialized + servlet = Puppet::Network::XMLRPC::WEBrickServlet.new( + handler_instances) + self.mount("/RPC2", servlet) + end + + # Create a ca client to set up our cert for us. + def request_cert(ca) + client = Puppet::Network::Client.ca.new(:CA => ca) + unless client.request_cert + raise Puppet::Error, "Could get certificate" + end + end + + # Create all of our handler instances. + def setup_handlers(handlers) + unless handlers.is_a?(Hash) + raise ServerError, "Handlers must have arguments" + end + + handlers.collect { |handler, args| + hclass = nil + unless hclass = Handler.handler(handler) + raise ServerError, "Invalid handler %s" % handler + end + hclass.new(args) + } + end + + # Handle all of the many webrick arguments. + def setup_webrick(hash) + hash[:Port] ||= Puppet[:masterport] + hash[:Logger] ||= self.httplog + hash[:AccessLog] ||= [ + [ self.httplog, WEBrick::AccessLog::COMMON_LOG_FORMAT ], + [ self.httplog, WEBrick::AccessLog::REFERER_LOG_FORMAT ] + ] + + hash[:SSLCertificateStore] = x509store + hash[:SSLCertificate] = self.cert + hash[:SSLPrivateKey] = self.key + hash[:SSLStartImmediately] = true + hash[:SSLEnable] = true + hash[:SSLCACertificateFile] = Puppet[:localcacert] + hash[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_PEER + hash[:SSLCertName] = nil + + if addr = Puppet[:bindaddress] and addr != "" + hash[:BindAddress] = addr + end + end + end + end +end + diff --git a/lib/puppet/network/rest_server.rb b/lib/puppet/network/rest_server.rb deleted file mode 100644 index d1206928c..000000000 --- a/lib/puppet/network/rest_server.rb +++ /dev/null @@ -1,37 +0,0 @@ -class Puppet::Network::RESTServer - attr_reader :server - - def initialize(args = {}) - raise(ArgumentError, "requires :server to be specified") unless args[:server] - @routes = {} - @listening = false - @server = args[:server] - 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) - end - end - - def listening? - @listening - end - - def listen - raise "Cannot listen -- already listening" if listening? - @listening = true - end - - def unlisten - raise "Cannot unlisten -- not currently listening" unless listening? - @listening = false - end -end diff --git a/lib/puppet/network/server.rb b/lib/puppet/network/server.rb index 0dee30b10..84a71a6b4 100644 --- a/lib/puppet/network/server.rb +++ b/lib/puppet/network/server.rb @@ -1,4 +1,66 @@ -# Just a stub, so we can correctly scope other classes. -module Puppet::Network::Server # :nodoc: +class Puppet::Network::Server + attr_reader :server_type + + # 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 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) + end + end + + def listening? + @listening + end + + def listen + raise "Cannot listen -- already listening" if listening? + start_web_server + @listening = true + end + + def unlisten + raise "Cannot unlisten -- not currently listening" unless listening? + stop_web_server + @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 end + diff --git a/lib/puppet/network/server/mongrel.rb b/lib/puppet/network/server/mongrel.rb deleted file mode 100644 index 8cd7e807b..000000000 --- a/lib/puppet/network/server/mongrel.rb +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env ruby -# File: 06-11-14-mongrel_xmlrpc.rb -# Author: Manuel Holtgrewe -# -# Copyright (c) 2006 Manuel Holtgrewe, 2007 Luke Kanies -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# This file is based heavily on a file retrieved from -# http://ttt.ggnore.net/2006/11/15/xmlrpc-with-mongrel-and-ruby-off-rails/ - -require 'rubygems' -require 'mongrel' -require 'xmlrpc/server' -require 'puppet/network/xmlrpc/server' -require 'puppet/network/server' -require 'puppet/network/client_request' -require 'puppet/daemon' - -require 'resolv' - -# This handler can be hooked into Mongrel to accept HTTP requests. After -# checking whether the request itself is sane, the handler forwards it -# to an internal instance of XMLRPC::BasicServer to process it. -# -# You can access the server by calling the Handler's "xmlrpc_server" -# attribute accessor method and add XMLRPC handlers there. For example: -# -#
-# handler = XmlRpcHandler.new
-# handler.xmlrpc_server.add_handler("my.add") { |a, b| a.to_i + b.to_i }
-# 
-module Puppet::Network - class Server::Mongrel < ::Mongrel::HttpHandler - include Puppet::Daemon - attr_reader :xmlrpc_server - - def initialize(handlers) - # Create a new instance of BasicServer. We are supposed to subclass it - # but that does not make sense since we would not introduce any new - # behaviour and we have to subclass Mongrel::HttpHandler so our handler - # works for Mongrel. - @xmlrpc_server = Puppet::Network::XMLRPCServer.new - handlers.each do |name, args| - unless handler = Puppet::Network::Handler.handler(name) - raise ArgumentError, "Invalid handler %s" % name - end - @xmlrpc_server.add_handler(handler.interface, handler.new(args)) - end - end - - # This method produces the same results as XMLRPC::CGIServer.serve - # from Ruby's stdlib XMLRPC implementation. - def process(request, response) - # Make sure this has been a POST as required for XMLRPC. - request_method = request.params[Mongrel::Const::REQUEST_METHOD] || Mongrel::Const::GET - if request_method != "POST" then - response.start(405) { |head, out| out.write("Method Not Allowed") } - return - end - - # Make sure the user has sent text/xml data. - request_mime = request.params["CONTENT_TYPE"] || "text/plain" - if parse_content_type(request_mime).first != "text/xml" then - response.start(400) { |head, out| out.write("Bad Request") } - return - end - - # Make sure there is data in the body at all. - length = request.params[Mongrel::Const::CONTENT_LENGTH].to_i - if length <= 0 then - response.start(411) { |head, out| out.write("Length Required") } - return - end - - # Check the body to be valid. - if request.body.nil? or request.body.size != length then - response.start(400) { |head, out| out.write("Bad Request") } - return - end - - info = client_info(request) - - # All checks above passed through - response.start(200) do |head, out| - head["Content-Type"] = "text/xml; charset=utf-8" - begin - out.write(@xmlrpc_server.process(request.body, info)) - rescue => detail - puts detail.backtrace - raise - end - end - end - - private - - def client_info(request) - params = request.params - ip = params["REMOTE_ADDR"] - if dn = params[Puppet[:ssl_client_header]] and dn.include?("/CN=") - client = dn.sub("/CN=", '') - valid = (params[Puppet[:ssl_client_verify_header]] == 'SUCCESS') - else - client = Resolv.getname(ip) - valid = false - end - - info = Puppet::Network::ClientRequest.new(client, ip, valid) - - return info - end - - # Taken from XMLRPC::ParseContentType - def parse_content_type(str) - a, *b = str.split(";") - return a.strip, *b - end - end -end - diff --git a/lib/puppet/network/server/webrick.rb b/lib/puppet/network/server/webrick.rb deleted file mode 100644 index dd9a91c7c..000000000 --- a/lib/puppet/network/server/webrick.rb +++ /dev/null @@ -1,163 +0,0 @@ -require 'puppet' -require 'puppet/daemon' -require 'webrick' -require 'webrick/https' -require 'fcntl' - -require 'puppet/sslcertificates/support' -require 'puppet/network/xmlrpc/webrick_servlet' -require 'puppet/network/server' -require 'puppet/network/client' - -module Puppet - class ServerError < RuntimeError; end - module Network - # The old-school, pure ruby webrick server, which is the default serving - # mechanism. - class Server::WEBrick < WEBrick::HTTPServer - include Puppet::Daemon - include Puppet::SSLCertificates::Support - - # Read the CA cert and CRL and populate an OpenSSL::X509::Store - # with them, with flags appropriate for checking client - # certificates for revocation - def x509store - if Puppet[:cacrl] == 'none' - # No CRL, no store needed - return nil - end - unless File.exist?(Puppet[:cacrl]) - raise Puppet::Error, "Could not find CRL" - end - crl = OpenSSL::X509::CRL.new(File.read(Puppet[:cacrl])) - store = OpenSSL::X509::Store.new - store.purpose = OpenSSL::X509::PURPOSE_ANY - store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK - unless self.ca_cert - raise Puppet::Error, "No CA certificate" - end - - store.add_file(Puppet[:localcacert]) - store.add_crl(crl) - return store - end - - # Set up the http log. - def httplog - args = [] - - # yuck; separate http logs - file = nil - Puppet.settings.use(:main, :ssl, Puppet[:name]) - if Puppet[:name] == "puppetmasterd" - file = Puppet[:masterhttplog] - else - file = Puppet[:httplog] - end - - # open the log manually to prevent file descriptor leak - file_io = open(file, "a+") - file_io.sync - file_io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) - - args << file_io - if Puppet[:debug] - args << WEBrick::Log::DEBUG - end - - log = WEBrick::Log.new(*args) - - - return log - end - - # Create our server, yo. - def initialize(hash = {}) - Puppet.info "Starting server for Puppet version %s" % Puppet.version - - if handlers = hash[:Handlers] - handler_instances = setup_handlers(handlers) - else - raise ServerError, "A server must have handlers" - end - - unless self.read_cert - if ca = handler_instances.find { |handler| handler.is_a?(Puppet::Network::Handler.ca) } - request_cert(ca) - else - raise Puppet::Error, "No certificate and no CA; cannot get cert" - end - end - - setup_webrick(hash) - - super(hash) - - # make sure children don't inherit the sockets - listeners.each { |sock| - sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) - } - - Puppet.info "Listening on port %s" % hash[:Port] - - # this creates a new servlet for every connection, - # but all servlets have the same list of handlers - # thus, the servlets can have their own state -- passing - # around the requests and such -- but the handlers - # have a global state - - # mount has to be called after the server is initialized - servlet = Puppet::Network::XMLRPC::WEBrickServlet.new( - handler_instances) - self.mount("/RPC2", servlet) - end - - # Create a ca client to set up our cert for us. - def request_cert(ca) - client = Puppet::Network::Client.ca.new(:CA => ca) - unless client.request_cert - raise Puppet::Error, "Could get certificate" - end - end - - # Create all of our handler instances. - def setup_handlers(handlers) - unless handlers.is_a?(Hash) - raise ServerError, "Handlers must have arguments" - end - - handlers.collect { |handler, args| - hclass = nil - unless hclass = Handler.handler(handler) - raise ServerError, "Invalid handler %s" % handler - end - hclass.new(args) - } - end - - # Handle all of the many webrick arguments. - def setup_webrick(hash) - hash[:Port] ||= Puppet[:masterport] - hash[:Logger] ||= self.httplog - hash[:AccessLog] ||= [ - [ self.httplog, WEBrick::AccessLog::COMMON_LOG_FORMAT ], - [ self.httplog, WEBrick::AccessLog::REFERER_LOG_FORMAT ] - ] - - hash[:SSLCertificateStore] = x509store - hash[:SSLCertificate] = self.cert - hash[:SSLPrivateKey] = self.key - hash[:SSLStartImmediately] = true - hash[:SSLEnable] = true - hash[:SSLCACertificateFile] = Puppet[:localcacert] - hash[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_PEER - hash[:SSLCertName] = nil - - if addr = Puppet[:bindaddress] and addr != "" - hash[:BindAddress] = addr - end - end - end - end -end - diff --git a/spec/unit/network/rest_server.rb b/spec/unit/network/rest_server.rb deleted file mode 100644 index 408bb36b3..000000000 --- a/spec/unit/network/rest_server.rb +++ /dev/null @@ -1,136 +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_server' - -describe Puppet::Network::RESTServer, "when initializing" do - it "should require specifying which HTTP server will be used to provide access to clients" do - Proc.new { Puppet::Network::RESTServer.new }.should raise_error(ArgumentError) - end -end - -describe Puppet::Network::RESTServer, "in general" do - before do - @server = Puppet::Network::RESTServer.new(:server => :mongrel) - 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.should == :mongrel - end - - it "should allow for multiple configurations, each allowing a different HTTP server handling different indirections" do - @server2 = Puppet::Network::RESTServer.new(:server => :webrick) - @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 - -describe Puppet::Network::RESTServer, "when listening is not turned on" do - before do - @server = Puppet::Network::RESTServer.new(:server => :mongrel) - end - - it "should allow listening to be turned on" do - Proc.new { @server.listen }.should_not raise_error - 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 not route HTTP GET requests on indirector's name to indirector find for the specified HTTP server" - it "should not route HTTP GET requests on indirector's plural name to indirector search for the specified HTTP server" - it "should not route HTTP DELETE requests on indirector's name to indirector destroy for the specified HTTP server" - it "should not route HTTP POST requests on indirector's name to indirector save for the specified HTTP server" - - # TODO: FIXME write integrations which fire up actual webrick / mongrel servers and are thus webrick / mongrel specific?] -end - -describe Puppet::Network::RESTServer, "when listening is turned on" do - before do - @server = Puppet::Network::RESTServer.new(:server => :mongrel) - @server.listen - end - - it "should allow listening to be turned off" do - Proc.new { @server.unlisten }.should_not raise_error - end - - it "should not allow listening to be turned on" do - Proc.new { @server.listen }.should raise_error(RuntimeError) - end - - it "should indicate that it is listening" do - @server.should be_listening - end - - it "should route HTTP GET requests on indirector's name to indirector find for the specified HTTP server" - it "should route HTTP GET requests on indirector's plural name to indirector search for the specified HTTP server" - it "should route HTTP DELETE requests on indirector's name to indirector destroy for the specified HTTP server" - it "should route HTTP POST requests on indirector's name to indirector save for the specified HTTP server" - - # TODO: FIXME [ write integrations which fire up actual webrick / mongrel servers and are thus webrick / mongrel specific?] -end diff --git a/spec/unit/network/server.rb b/spec/unit/network/server.rb new file mode 100644 index 000000000..5a1980fec --- /dev/null +++ b/spec/unit/network/server.rb @@ -0,0 +1,181 @@ +#!/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/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 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 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 + +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 allow listening to be turned on" do + Proc.new { @server.listen }.should_not raise_error + 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 cause the HTTP server to listen when listening is turned on" do + @server.expects(:start_web_server) + @server.listen + end + + it "should not route HTTP GET requests on indirector's name to indirector find for the specified HTTP server" + it "should not route HTTP GET requests on indirector's plural name to indirector search for the specified HTTP server" + it "should not route HTTP DELETE requests on indirector's name to indirector destroy for the specified HTTP server" + it "should not route HTTP POST requests on indirector's name to indirector save for the specified HTTP server" + + # TODO: FIXME write integrations which fire up actual webrick / mongrel servers and are thus webrick / mongrel specific?] +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 + + it "should allow listening to be turned off" do + Proc.new { @server.unlisten }.should_not raise_error + end + + it "should not allow listening to be turned on" do + Proc.new { @server.listen }.should raise_error(RuntimeError) + 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 + @server.expects(:stop_web_server) + @server.unlisten + end + + it "should route HTTP GET requests on indirector's name to indirector find for the specified HTTP server" + it "should route HTTP GET requests on indirector's plural name to indirector search for the specified HTTP server" + it "should route HTTP DELETE requests on indirector's name to indirector destroy for the specified HTTP server" + it "should route HTTP POST requests on indirector's name to indirector save for the specified HTTP server" + + # TODO: FIXME [ write integrations which fire up actual webrick / mongrel servers and are thus webrick / mongrel specific?] +end diff --git a/test/lib/puppettest/servertest.rb b/test/lib/puppettest/servertest.rb index 3d1381111..11c24f5b4 100644 --- a/test/lib/puppettest/servertest.rb +++ b/test/lib/puppettest/servertest.rb @@ -1,5 +1,5 @@ require 'puppettest' -require 'puppet/network/server/webrick' +require 'puppet/network/http_server/webrick' module PuppetTest::ServerTest include PuppetTest @@ -47,7 +47,7 @@ module PuppetTest::ServerTest # then create the actual server server = nil assert_nothing_raised { - server = Puppet::Network::Server::WEBrick.new( + server = Puppet::Network::HTTPServer::WEBrick.new( :Port => @@port, :Handlers => handlers ) diff --git a/test/network/handler/ca.rb b/test/network/handler/ca.rb index 3e74f8e05..e349f9c4c 100755 --- a/test/network/handler/ca.rb +++ b/test/network/handler/ca.rb @@ -191,7 +191,7 @@ class TestCA < Test::Unit::TestCase server = nil Puppet[:name] = "puppetmasterd" assert_nothing_raised { - server = Puppet::Network::Server::WEBrick.new( + server = Puppet::Network::HTTPServer::WEBrick.new( :Port => @@port, :Handlers => { :CA => {}, # so that certs autogenerate diff --git a/test/network/server/mongrel_test.rb b/test/network/server/mongrel_test.rb index 05d18257b..52d8790eb 100755 --- a/test/network/server/mongrel_test.rb +++ b/test/network/server/mongrel_test.rb @@ -12,7 +12,7 @@ class TestMongrelServer < PuppetTest::TestCase def mkserver(handlers = nil) handlers ||= { :Status => nil } - mongrel = Puppet::Network::Server::Mongrel.new(handlers) + mongrel = Puppet::Network::HTTPServer::Mongrel.new(handlers) end # Make sure client info is correctly extracted. diff --git a/test/network/server/webrick.rb b/test/network/server/webrick.rb index 0f76659b1..5919461ed 100755 --- a/test/network/server/webrick.rb +++ b/test/network/server/webrick.rb @@ -3,7 +3,7 @@ $:.unshift("../../lib") if __FILE__ =~ /\.rb$/ require 'puppettest' -require 'puppet/network/server/webrick' +require 'puppet/network/http_server/webrick' require 'mocha' class TestWebrickServer < Test::Unit::TestCase @@ -19,7 +19,7 @@ class TestWebrickServer < Test::Unit::TestCase def test_basics server = nil assert_raise(Puppet::Error, "server succeeded with no cert") do - server = Puppet::Network::Server::WEBrick.new( + server = Puppet::Network::HTTPServer::WEBrick.new( :Port => @@port, :Handlers => { :Status => nil @@ -28,7 +28,7 @@ class TestWebrickServer < Test::Unit::TestCase end assert_nothing_raised("Could not create simple server") do - server = Puppet::Network::Server::WEBrick.new( + server = Puppet::Network::HTTPServer::WEBrick.new( :Port => @@port, :Handlers => { :CA => {}, # so that certs autogenerate @@ -112,7 +112,7 @@ class TestWebrickServer < Test::Unit::TestCase def mk_status_server server = nil assert_nothing_raised() { - server = Puppet::Network::Server::WEBrick.new( + server = Puppet::Network::HTTPServer::WEBrick.new( :Port => @@port, :Handlers => { :CA => {}, # so that certs autogenerate diff --git a/test/ral/types/filesources.rb b/test/ral/types/filesources.rb index 9517db5a5..489bc4e78 100755 --- a/test/ral/types/filesources.rb +++ b/test/ral/types/filesources.rb @@ -548,7 +548,7 @@ class TestFileSources < Test::Unit::TestCase serverpid = nil assert_nothing_raised() { - server = Puppet::Network::Server::WEBrick.new( + server = Puppet::Network::HTTPServer::WEBrick.new( :Handlers => { :CA => {}, # so that certs autogenerate :FileServer => { @@ -593,7 +593,7 @@ class TestFileSources < Test::Unit::TestCase serverpid = nil assert_nothing_raised("Could not start on port %s" % @port) { - server = Puppet::Network::Server::WEBrick.new( + server = Puppet::Network::HTTPServer::WEBrick.new( :Port => @port, :Handlers => { :CA => {}, # so that certs autogenerate -- cgit