diff --git a/lib/puppet/application/master.rb b/lib/puppet/application/master.rb index cd98b9aae..f4f4cbdea 100644 --- a/lib/puppet/application/master.rb +++ b/lib/puppet/application/master.rb @@ -1,248 +1,261 @@ require 'puppet/application' class Puppet::Application::Master < Puppet::Application should_parse_config run_mode :master option("--debug", "-d") option("--verbose", "-v") # internal option, only to be used by ext/rack/config.ru option("--rack") option("--compile host", "-c host") do |arg| options[:node] = arg end option("--logdest DEST", "-l DEST") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:setdest] = true rescue => detail puts detail.backtrace if Puppet[:debug] $stderr.puts detail.to_s end end option("--parseonly") do puts "--parseonly has been removed. Please use 'puppet parser validate '" exit 1 end def help <<-HELP puppet-master(8) -- The puppet master daemon ======== SYNOPSIS -------- The central puppet server. Functions as a certificate authority by default. USAGE ----- puppet master [-D|--daemonize|--no-daemonize] [-d|--debug] [-h|--help] [-l|--logdest |console|syslog] [-v|--verbose] [-V|--version] [--compile ] DESCRIPTION ----------- This command starts an instance of puppet master, running as a daemon and using Ruby's built-in Webrick webserver. Puppet master can also be managed by other application servers; when this is the case, this executable is not used. OPTIONS ------- Note that any configuration parameter that's valid in the configuration file is also a valid long argument. For example, 'ssldir' is a valid configuration parameter, so you can specify '--ssldir ' as an argument. See the configuration file documentation at http://docs.puppetlabs.com/references/stable/configuration.html for the full list of acceptable parameters. A commented list of all configuration options can also be generated by running puppet master with '--genconfig'. * --daemonize: Send the process into the background. This is the default. * --no-daemonize: Do not send the process into the background. * --debug: Enable full debugging. * --help: Print this help message. * --logdest: Where to send messages. Choose between syslog, the console, and a log file. Defaults to sending messages to syslog, or the console if debugging or verbosity is enabled. * --verbose: Enable verbosity. * --version: Print the puppet version number and exit. * --compile: Compile a catalogue and output it in JSON from the puppet master. Uses facts contained in the $vardir/yaml/ directory to compile the catalog. EXAMPLE ------- puppet master DIAGNOSTICS ----------- When running as a standalone daemon, puppet master accepts the following signals: * SIGHUP: Restart the puppet master server. * SIGINT and SIGTERM: Shut down the puppet master server. AUTHOR ------ Luke Kanies COPYRIGHT --------- Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License HELP end def preinit Signal.trap(:INT) do $stderr.puts "Cancelling startup" exit(0) end # Create this first-off, so we have ARGV require 'puppet/daemon' @daemon = Puppet::Daemon.new @daemon.argv = ARGV.dup end def run_command if options[:node] compile else main end end def compile Puppet::Util::Log.newdestination :console raise ArgumentError, "Cannot render compiled catalogs without pson support" unless Puppet.features.pson? begin unless catalog = Puppet::Resource::Catalog.indirection.find(options[:node]) raise "Could not compile catalog for #{options[:node]}" end jj catalog.to_resource rescue => detail $stderr.puts detail exit(30) end exit(0) end def main require 'etc' - require 'puppet/file_serving/content' - require 'puppet/file_serving/metadata' xmlrpc_handlers = [:Status, :FileServer, :Master, :Report, :Filebucket] xmlrpc_handlers << :CA if Puppet[:ca] # Make sure we've got a localhost ssl cert Puppet::SSL::Host.localhost # And now configure our server to *only* hit the CA for data, because that's # all it will have write access to. Puppet::SSL::Host.ca_location = :only if Puppet::SSL::CertificateAuthority.ca? if Puppet.features.root? begin Puppet::Util.chuser rescue => detail puts detail.backtrace if Puppet[:trace] $stderr.puts "Could not change user to #{Puppet[:user]}: #{detail}" exit(39) end end unless options[:rack] require 'puppet/network/server' @daemon.server = Puppet::Network::Server.new(:xmlrpc_handlers => xmlrpc_handlers) @daemon.daemonize if Puppet[:daemonize] else require 'puppet/network/http/rack' @app = Puppet::Network::HTTP::Rack.new(:xmlrpc_handlers => xmlrpc_handlers, :protocols => [:rest, :xmlrpc]) end Puppet.notice "Starting Puppet master version #{Puppet.version}" unless options[:rack] @daemon.start else return @app end end - def setup - raise Puppet::Error.new("Puppet master is not supported on Microsoft Windows") if Puppet.features.microsoft_windows? - + def setup_logs # Handle the logging settings. if options[:debug] or options[:verbose] if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end unless Puppet[:daemonize] or options[:rack] Puppet::Util::Log.newdestination(:console) options[:setdest] = true end end Puppet::Util::Log.newdestination(:syslog) unless options[:setdest] + end - exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? - - Puppet.settings.use :main, :master, :ssl, :metrics + def setup_terminuses + require 'puppet/file_serving/content' + require 'puppet/file_serving/metadata' # Cache our nodes in yaml. Currently not configurable. Puppet::Node.indirection.cache_class = :yaml Puppet::FileServing::Content.indirection.terminus_class = :file_server Puppet::FileServing::Metadata.indirection.terminus_class = :file_server Puppet::FileBucket::File.indirection.terminus_class = :file + end + def setup_ssl # Configure all of the SSL stuff. if Puppet::SSL::CertificateAuthority.ca? Puppet::SSL::Host.ca_location = :local Puppet.settings.use :ca Puppet::SSL::CertificateAuthority.instance else Puppet::SSL::Host.ca_location = :none end end + + def setup + raise Puppet::Error.new("Puppet master is not supported on Microsoft Windows") if Puppet.features.microsoft_windows? + + setup_logs + + exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs? + + Puppet.settings.use :main, :master, :ssl, :metrics + + setup_terminuses + + setup_ssl + end end diff --git a/lib/puppet/indirector/file_content/selector.rb b/lib/puppet/indirector/file_content/selector.rb index 7aac4ee68..32374ed74 100644 --- a/lib/puppet/indirector/file_content/selector.rb +++ b/lib/puppet/indirector/file_content/selector.rb @@ -1,29 +1,30 @@ +require 'puppet/file_serving/content' require 'puppet/indirector/file_content' require 'puppet/indirector/code' require 'puppet/file_serving/terminus_selector' class Puppet::Indirector::FileContent::Selector < Puppet::Indirector::Code desc "Select the terminus based on the request" include Puppet::FileServing::TerminusSelector def get_terminus(request) indirection.terminus(select(request)) end def find(request) get_terminus(request).find(request) end def search(request) get_terminus(request).search(request) end def authorized?(request) terminus = get_terminus(request) if terminus.respond_to?(:authorized?) terminus.authorized?(request) else true end end end diff --git a/lib/puppet/indirector/file_metadata/selector.rb b/lib/puppet/indirector/file_metadata/selector.rb index 26793b5c4..08e4fbb88 100644 --- a/lib/puppet/indirector/file_metadata/selector.rb +++ b/lib/puppet/indirector/file_metadata/selector.rb @@ -1,29 +1,30 @@ +require 'puppet/file_serving/metadata' require 'puppet/indirector/file_metadata' require 'puppet/indirector/code' require 'puppet/file_serving/terminus_selector' class Puppet::Indirector::FileMetadata::Selector < Puppet::Indirector::Code desc "Select the terminus based on the request" include Puppet::FileServing::TerminusSelector def get_terminus(request) indirection.terminus(select(request)) end def find(request) get_terminus(request).find(request) end def search(request) get_terminus(request).search(request) end def authorized?(request) terminus = get_terminus(request) if terminus.respond_to?(:authorized?) terminus.authorized?(request) else true end end end diff --git a/spec/integration/file_serving/content_spec.rb b/spec/integration/file_serving/content_spec.rb old mode 100755 new mode 100644 index e2efecfa4..08ff13d9b --- a/spec/integration/file_serving/content_spec.rb +++ b/spec/integration/file_serving/content_spec.rb @@ -1,14 +1,9 @@ #!/usr/bin/env rspec + require 'spec_helper' require 'puppet/file_serving/content' -require 'shared_behaviours/file_serving' - -describe Puppet::FileServing::Content, " when finding files" do - it_should_behave_like "Puppet::FileServing::Files" - before do - @test_class = Puppet::FileServing::Content - @indirection = Puppet::FileServing::Content.indirection - end +describe Puppet::FileServing::Content do + it_should_behave_like "a file_serving model" end diff --git a/spec/integration/file_serving/metadata_spec.rb b/spec/integration/file_serving/metadata_spec.rb old mode 100755 new mode 100644 index d9aaa37f5..d34ff3b3b --- a/spec/integration/file_serving/metadata_spec.rb +++ b/spec/integration/file_serving/metadata_spec.rb @@ -1,15 +1,10 @@ #!/usr/bin/env rspec + require 'spec_helper' require 'puppet/file_serving/metadata' -require 'shared_behaviours/file_serving' -require 'puppet/indirector/file_metadata/file_server' - -describe Puppet::FileServing::Metadata, " when finding files" do - it_should_behave_like "Puppet::FileServing::Files" - before do - @test_class = Puppet::FileServing::Metadata - @indirection = Puppet::FileServing::Metadata.indirection - end +describe Puppet::FileServing::Metadata do + it_should_behave_like "a file_serving model" end + diff --git a/spec/shared_behaviours/file_serving.rb b/spec/shared_behaviours/file_serving.rb index afcfac173..e0db20032 100755 --- a/spec/shared_behaviours/file_serving.rb +++ b/spec/shared_behaviours/file_serving.rb @@ -1,66 +1,80 @@ #!/usr/bin/env rspec -shared_examples_for "Puppet::FileServing::Files" do - it "should use the rest terminus when the 'puppet' URI scheme is used and a host name is present" do - uri = "puppet://myhost/fakemod/my/file" - - # It appears that the mocking somehow interferes with the caching subsystem. - # This mock somehow causes another terminus to get generated. - @indirection.terminus(:rest).expects(:find) - @indirection.find(uri) - end - it "should use the rest terminus when the 'puppet' URI scheme is used, no host name is present, and the process name is not 'puppet' or 'apply'" do - uri = "puppet:///fakemod/my/file" - Puppet.settings.stubs(:value).returns "foo" - Puppet.settings.stubs(:value).with(:name).returns("puppetd") - Puppet.settings.stubs(:value).with(:modulepath).returns("") - @indirection.terminus(:rest).expects(:find) - @indirection.find(uri) - end +shared_examples_for "Puppet::FileServing::Files" do |indirection| + %w[find search].each do |method| + let(:request) { Puppet::Indirector::Request.new(indirection, method, 'foo') } - it "should use the file_server terminus when the 'puppet' URI scheme is used, no host name is present, and the process name is 'puppet'" do - uri = "puppet:///fakemod/my/file" - Puppet::Node::Environment.stubs(:new).returns(stub("env", :name => "testing", :module => nil, :modulepath => [])) - Puppet.settings.stubs(:value).returns "" - Puppet.settings.stubs(:value).with(:name).returns("puppet") - Puppet.settings.stubs(:value).with(:fileserverconfig).returns("/whatever") - @indirection.terminus(:file_server).expects(:find) - @indirection.terminus(:file_server).stubs(:authorized?).returns(true) - @indirection.find(uri) - end + before :each do + # Stub this so we can set the :name setting + Puppet::Util::Settings::ReadOnly.stubs(:include?) + end - it "should use the file_server terminus when the 'puppet' URI scheme is used, no host name is present, and the process name is 'apply'" do - uri = "puppet:///fakemod/my/file" - Puppet::Node::Environment.stubs(:new).returns(stub("env", :name => "testing", :module => nil, :modulepath => [])) - Puppet.settings.stubs(:value).returns "" - Puppet.settings.stubs(:value).with(:name).returns("apply") - Puppet.settings.stubs(:value).with(:fileserverconfig).returns("/whatever") - @indirection.terminus(:file_server).expects(:find) - @indirection.terminus(:file_server).stubs(:authorized?).returns(true) - @indirection.find(uri) - end + describe "##{method}" do + it "should proxy to file terminus if the path is absolute" do + request.key = make_absolute('/tmp/foo') - it "should use the file terminus when the 'file' URI scheme is used" do - uri = Puppet::Util.path_to_uri(File.expand_path('/fakemod/my/other file')) - uri.scheme.should == 'file' - @indirection.terminus(:file).expects(:find) - @indirection.find(uri.to_s) - end + described_class.indirection.terminus(:file).class.any_instance.expects(method).with(request) - it "should use the file terminus when a fully qualified path is provided" do - uri = File.expand_path("/fakemod/my/file") - @indirection.terminus(:file).expects(:find) - @indirection.find(uri) - end + subject.send(method, request) + end + + it "should proxy to file terminus if the protocol is file" do + request.protocol = 'file' + + described_class.indirection.terminus(:file).class.any_instance.expects(method).with(request) + + subject.send(method, request) + end + + describe "when the protocol is puppet" do + before :each do + request.protocol = 'puppet' + end + + describe "and a server is specified" do + before :each do + request.server = 'puppet_server' + end + + it "should proxy to rest terminus if we're 'apply'" do + Puppet[:name] = 'apply' + + described_class.indirection.terminus(:rest).class.any_instance.expects(method).with(request) + + subject.send(method, request) + end + + it "should proxy to rest terminus if we aren't 'apply'" do + Puppet[:name] = 'not_apply' + + described_class.indirection.terminus(:rest).class.any_instance.expects(method).with(request) + + subject.send(method, request) + end + end + + describe "and no server is specified" do + before :each do + request.server = nil + end + + it "should proxy to file_server if we're 'apply'" do + Puppet[:name] = 'apply' + + described_class.indirection.terminus(:file_server).class.any_instance.expects(method).with(request) + + subject.send(method, request) + end + + it "should proxy to rest if we're not 'apply'" do + Puppet[:name] = 'not_apply' - it "should use the configuration to test whether the request is allowed" do - uri = "fakemod/my/file" - mount = mock 'mount' - config = stub 'configuration', :split_path => [mount, "eh"] - @indirection.terminus(:file_server).stubs(:configuration).returns config + described_class.indirection.terminus(:rest).class.any_instance.expects(method).with(request) - @indirection.terminus(:file_server).expects(:find) - mount.expects(:allowed?).returns(true) - @indirection.find(uri, :node => "foo", :ip => "bar") + subject.send(method, request) + end + end + end + end end end diff --git a/spec/shared_behaviours/file_serving_model.rb b/spec/shared_behaviours/file_serving_model.rb new file mode 100644 index 000000000..4ed6c1977 --- /dev/null +++ b/spec/shared_behaviours/file_serving_model.rb @@ -0,0 +1,73 @@ +#!/usr/bin/env rspec + +shared_examples_for "a file_serving model" do + include PuppetSpec::Files + + describe "#indirection" do + before :each do + # Never connect to the network, no matter what + described_class.indirection.terminus(:rest).class.any_instance.stubs(:find) + end + + describe "when running the master application" do + before :each do + Puppet::Application[:master].setup_terminuses + end + + { + "/etc/sudoers" => :file_server, + "file:///etc/sudoers" => :file_server, + "puppet:///modules/foo/bar" => :file_server, + "puppet://server/modules/foo/bar" => :file_server, + }.each do |key, terminus| + it "should use the #{terminus} terminus when requesting #{key.inspect}" do + described_class.indirection.terminus(terminus).class.any_instance.expects(:find) + + described_class.indirection.find(key) + end + end + end + + describe "when running the apply application" do + before :each do + # Stub this so we can set the 'name' setting + Puppet::Util::Settings::ReadOnly.stubs(:include?) + Puppet[:name] = 'apply' + end + + { + "/etc/sudoers" => :file, + "file:///etc/sudoers" => :file, + "puppet:///modules/foo/bar" => :file_server, + "puppet://server/modules/foo/bar" => :rest, + }.each do |key, terminus| + it "should use the #{terminus} terminus when requesting #{key.inspect}" do + described_class.indirection.terminus(terminus).class.any_instance.expects(:find) + + described_class.indirection.find(key) + end + end + end + + describe "when running another application" do + before :each do + # Stub this so we can set the 'name' setting + Puppet::Util::Settings::ReadOnly.stubs(:include?) + Puppet[:name] = 'agent' + end + + { + "/etc/sudoers" => :file, + "file:///etc/sudoers" => :file, + "puppet:///modules/foo/bar" => :rest, + "puppet://server/modules/foo/bar" => :rest, + }.each do |key, terminus| + it "should use the #{terminus} terminus when requesting #{key.inspect}" do + described_class.indirection.terminus(terminus).class.any_instance.expects(:find) + + described_class.indirection.find(key) + end + end + end + end +end diff --git a/spec/unit/indirector/file_content/selector_spec.rb b/spec/unit/indirector/file_content/selector_spec.rb new file mode 100644 index 000000000..8181a577b --- /dev/null +++ b/spec/unit/indirector/file_content/selector_spec.rb @@ -0,0 +1,10 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/indirector/file_content/selector' + +describe Puppet::Indirector::FileContent::Selector do + include PuppetSpec::Files + + it_should_behave_like "Puppet::FileServing::Files", :file_content +end diff --git a/spec/unit/indirector/file_metadata/selector_spec.rb b/spec/unit/indirector/file_metadata/selector_spec.rb new file mode 100644 index 000000000..e0a97c6d6 --- /dev/null +++ b/spec/unit/indirector/file_metadata/selector_spec.rb @@ -0,0 +1,11 @@ +#!/usr/bin/env rspec +require 'spec_helper' + +require 'puppet/indirector/file_metadata/selector' + +describe Puppet::Indirector::FileMetadata::Selector do + include PuppetSpec::Files + + it_should_behave_like "Puppet::FileServing::Files", :file_metadata +end +