diff --git a/bin/puppet b/bin/puppet index 5e619de6e..a326ba148 100755 --- a/bin/puppet +++ b/bin/puppet @@ -1,89 +1,89 @@ #!/usr/bin/env ruby # # = Synopsis # # Run a stand-alone +puppet+ manifest. # # = Usage # # puppet apply [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose] [-e|--execute] # [--detailed-exitcodes] [-l|--logdest ] # # = Description # # This is the standalone puppet execution tool; use it to execute # individual manifests that you write. If you need to execute site-wide # manifests, use 'puppet agent' and 'puppet master'. # # = 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://reductivelabs.com/trac/puppet/wiki/ConfigurationReference for # the full list of acceptable parameters. A commented list of all # configuration options can also be generated by running puppet with # '--genconfig'. # # debug:: # Enable full debugging. # # detailed-exitcodes:: # Provide transaction information via exit codes. If this is enabled, an exit # code of '2' means there were changes, and an exit code of '4' means that there # were failures during the transaction. # # help:: # Print this help message # # loadclasses:: # Load any stored classes. 'puppet agent' caches configured classes (usually at # /etc/puppet/classes.txt), and setting this option causes all of those classes # to be set in your puppet manifest. # # logdest:: # Where to send messages. Choose between syslog, the console, and a log file. # Defaults to sending messages to the console. # # execute:: # Execute a specific piece of Puppet code # # verbose:: # Print extra information. # # = Example # # puppet -l /tmp/manifest.log manifest.pp # # = Author # # Luke Kanies # # = Copyright # # Copyright (c) 2005 Reductive Labs, LLC # Licensed under the GNU Public License #Puppet::Application[:apply].run # this is so the RDoc::usage hack can find this file appdir = File.join('puppet', 'application') absolute_appdir = $:.collect { |x| File.join(x,'puppet','application') }.detect{ |x| File.directory?(x) } builtins = Dir[File.join(absolute_appdir, '*.rb')].map{|fn| File.basename(fn, '.rb')} usage = "Usage: puppet command " available = "Available commands are: #{builtins.sort.join(', ')}" require 'puppet/util/command_line' -$puppet_subcommand_name = Puppet::Util::CommandLine.shift_subcommand_from_argv +subcommand_name = Puppet::Util::CommandLine.subcommand_name -if $puppet_subcommand_name.nil? +if subcommand_name.nil? puts usage, available -elsif builtins.include?($puppet_subcommand_name) #subcommand - require File.join(appdir, $puppet_subcommand_name) - Puppet::Application[$puppet_subcommand_name].run +elsif builtins.include?(subcommand_name) #subcommand + require File.join(appdir, subcommand_name) + Puppet::Application[subcommand_name].run else - abort "Error: Unknown command #{$puppet_subcommand_name}.\n#{usage}\n#{available}" + abort "Error: Unknown command #{subcommand_name}.\n#{usage}\n#{available}" end diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb index 1245b1006..7b404a906 100644 --- a/lib/puppet/application.rb +++ b/lib/puppet/application.rb @@ -1,402 +1,402 @@ require 'puppet' require 'optparse' # This class handles all the aspects of a Puppet application/executable # * setting up options # * setting up logs # * choosing what to run # * representing execution status # # === Usage # The application is a Puppet::Application object that register itself in the list # of available application. Each application needs a +name+ and a getopt +options+ # description array. # # The executable uses the application object like this: # Puppet::Application[:example].run # # # Puppet::Application.new(:example) do # # preinit do # # perform some pre initialization # @all = false # end # # # dispatch is called to know to what command to call # dispatch do -# ARGV.shift +# Puppet::Util::CommandLine.args.shift # end # # option("--arg ARGUMENT") do |v| # @args << v # end # # option("--debug", "-d") do |v| # @debug = v # end # # option("--all", "-a:) do |v| # @all = v # end # # unknown do |opt,arg| # # last chance to manage an option # ... # # let's say to the framework we finally handle this option # true # end # # command(:read) do # # read action # end # # command(:write) do # # writeaction # end # # end # # === Preinit # The preinit block is the first code to be called in your application, before option parsing, # setup or command execution. # # === Options # Puppet::Application uses +OptionParser+ to manage the application options. # Options are defined with the +option+ method to which are passed various # arguments, including the long option, the short option, a description... # Refer to +OptionParser+ documentation for the exact format. # * If the option method is given a block, this one will be called whenever # the option is encountered in the command-line argument. # * If the option method has no block, a default functionnality will be used, that # stores the argument (or true/false if the option doesn't require an argument) in # the global (to the application) options array. # * If a given option was not defined by a the +option+ method, but it exists as a Puppet settings: # * if +unknown+ was used with a block, it will be called with the option name and argument # * if +unknown+ wasn't used, then the option/argument is handed to Puppet.settings.handlearg for # a default behavior # # --help is managed directly by the Puppet::Application class, but can be overriden. # # === Setup # Applications can use the setup block to perform any initialization. # The defaul +setup+ behaviour is to: read Puppet configuration and manage log level and destination # # === What and how to run # If the +dispatch+ block is defined it is called. This block should return the name of the registered command # to be run. # If it doesn't exist, it defaults to execute the +main+ command if defined. # # === Execution state # The class attributes/methods of Puppet::Application serve as a global place to set and query the execution # status of the application: stopping, restarting, etc. The setting of the application status does not directly # aftect its running status; it's assumed that the various components within the application will consult these # settings appropriately and affect their own processing accordingly. Control operations (signal handlers and # the like) should set the status appropriately to indicate to the overall system that it's the process of # stopping or restarting (or just running as usual). # # So, if something in your application needs to stop the process, for some reason, you might consider: # # def stop_me! # # indicate that we're stopping # Puppet::Application.stop! # # ...do stuff... # end # # And, if you have some component that involves a long-running process, you might want to consider: # # def my_long_process(giant_list_to_munge) # giant_list_to_munge.collect do |member| # # bail if we're stopping # return if Puppet::Application.stop_requested? # process_member(member) # end # end class Puppet::Application include Puppet::Util BINDIRS = %w{sbin bin}.map{|dir| File.expand_path(File.dirname(__FILE__)) + "/../../#{dir}/*"}.join(" ") @@applications = {} def self.applications; @@applications end class << self include Puppet::Util attr_accessor :run_status def clear! self.run_status = nil end def stop! self.run_status = :stop_requested end def restart! self.run_status = :restart_requested end # Indicates that Puppet::Application.restart! has been invoked and components should # do what is necessary to facilitate a restart. def restart_requested? :restart_requested == run_status end # Indicates that Puppet::Application.stop! has been invoked and components should do what is necessary # for a clean stop. def stop_requested? :stop_requested == run_status end # Indicates that one of stop! or start! was invoked on Puppet::Application, and some kind of process # shutdown/short-circuit may be necessary. def interrupted? [:restart_requested, :stop_requested].include? run_status end # Indicates that Puppet::Application believes that it's in usual running mode (no stop/restart request # currently active). def clear? run_status.nil? end # Only executes the given block if the run status of Puppet::Application is clear (no restarts, stops, # etc. requested). # Upon block execution, checks the run status again; if a restart has been requested during the block's # execution, then controlled_run will send a new HUP signal to the current process. # Thus, long-running background processes can potentially finish their work before a restart. def controlled_run(&block) return unless clear? result = block.call Process.kill(:HUP, $$) if restart_requested? result end end attr_reader :options, :opt_parser def self.[](name) name = symbolize(name) @@applications[name] end def should_parse_config @parse_config = true end def should_not_parse_config @parse_config = false end def should_parse_config? unless @parse_config.nil? return @parse_config end @parse_config = true end # used to declare a new command def command(name, &block) meta_def(symbolize(name), &block) end # used as a catch-all for unknown option def unknown(&block) meta_def(:handle_unknown, &block) end # used to declare code that handle an option def option(*options, &block) long = options.find { |opt| opt =~ /^--/ }.gsub(/^--(?:\[no-\])?([^ =]+).*$/, '\1' ).gsub('-','_') fname = "handle_#{long}" if (block_given?) meta_def(symbolize(fname), &block) else meta_def(symbolize(fname)) do |value| self.options["#{long}".to_sym] = value end end @opt_parser.on(*options) do |value| self.send(symbolize(fname), value) end end # used to declare accessor in a more natural way in the # various applications def attr_accessor(*args) args.each do |arg| meta_def(arg) do instance_variable_get("@#{arg}".to_sym) end meta_def("#{arg}=") do |value| instance_variable_set("@#{arg}".to_sym, value) end end end # used to declare code run instead the default setup def setup(&block) meta_def(:run_setup, &block) end # used to declare code to choose which command to run def dispatch(&block) meta_def(:get_command, &block) end # used to execute code before running anything else def preinit(&block) meta_def(:run_preinit, &block) end def initialize(name, banner = nil, &block) @opt_parser = OptionParser.new(banner) @name = symbolize(name) init_default @options = {} instance_eval(&block) if block_given? @@applications[@name] = self end # initialize default application behaviour def init_default setup do default_setup end dispatch do :main end # empty by default preinit do end option("--version", "-V") do |arg| puts "%s" % Puppet.version exit end option("--help", "-h") do |v| help end end # This is the main application entry point def run exit_on_fail("initialize") { run_preinit } exit_on_fail("parse options") { parse_options } exit_on_fail("parse configuration file") { Puppet.settings.parse } if should_parse_config? exit_on_fail("prepare for execution") { run_setup } exit_on_fail("run") { run_command } end def main raise NotImplementedError, "No valid command or main" end def run_command if command = get_command() and respond_to?(command) send(command) else main end end def default_setup # Handle the logging settings if options[:debug] or options[:verbose] Puppet::Util::Log.newdestination(:console) if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end end unless options[:setdest] Puppet::Util::Log.newdestination(:syslog) end end def parse_options # get all puppet options optparse_opt = [] optparse_opt = Puppet.settings.optparse_addargs(optparse_opt) # convert them to OptionParser format optparse_opt.each do |option| @opt_parser.on(*option) do |arg| handlearg(option[0], arg) end end # scan command line argument begin @opt_parser.parse! rescue OptionParser::ParseError => detail $stderr.puts detail $stderr.puts "Try '#{$0} --help'" exit(1) end end def handlearg(opt, arg) # rewrite --[no-]option to --no-option if that's what was given if opt =~ /\[no-\]/ and !arg opt = opt.gsub(/\[no-\]/,'no-') end # otherwise remove the [no-] prefix to not confuse everybody opt = opt.gsub(/\[no-\]/, '') unless respond_to?(:handle_unknown) and send(:handle_unknown, opt, arg) # Puppet.settings.handlearg doesn't handle direct true/false :-) if arg.is_a?(FalseClass) arg = "false" elsif arg.is_a?(TrueClass) arg = "true" end Puppet.settings.handlearg(opt, arg) end end # this is used for testing def self.exit(code) exit(code) end def help if Puppet.features.usage? # RH:FIXME: My goodness, this is ugly. ::RDoc.const_set("PuppetSourceFile", @name) def (::RDoc).caller docfile = `grep -l 'Puppet::Application\\[:#{::RDoc::PuppetSourceFile}\\]' #{BINDIRS}`.chomp super << "#{docfile}:0" end ::RDoc::usage && exit else puts "No help available unless you have RDoc::usage installed" exit end rescue Errno::ENOENT puts "No help available for puppet #@name" exit end private def exit_on_fail(message, code = 1) begin yield rescue RuntimeError, NotImplementedError => detail puts detail.backtrace if Puppet[:trace] $stderr.puts "Could not %s: %s" % [message, detail] exit(code) end end end diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb index 787ce375f..d977cf1d0 100644 --- a/lib/puppet/application/apply.rb +++ b/lib/puppet/application/apply.rb @@ -1,183 +1,183 @@ require 'puppet' require 'puppet/application' require 'puppet/configurer' require 'puppet/network/handler' require 'puppet/network/client' Puppet::Application.new(:apply) do should_parse_config option("--debug","-d") option("--execute EXECUTE","-e") do |arg| options[:code] = arg end option("--loadclasses","-L") option("--verbose","-v") option("--use-nodes") option("--detailed-exitcodes") option("--apply catalog", "-a catalog") do |arg| options[:catalog] = arg end option("--logdest LOGDEST", "-l") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:logset] = true rescue => detail $stderr.puts detail.to_s end end dispatch do if options[:catalog] :apply elsif Puppet[:parseonly] :parseonly else :main end end command(:apply) do require 'puppet/configurer' if options[:catalog] == "-" text = $stdin.read else text = File.read(options[:catalog]) end begin catalog = Puppet::Resource::Catalog.convert_from(Puppet::Resource::Catalog.default_format,text) unless catalog.is_a?(Puppet::Resource::Catalog) catalog = Puppet::Resource::Catalog.pson_create(catalog) end rescue => detail raise Puppet::Error, "Could not deserialize catalog from pson: %s" % detail end catalog = catalog.to_ral configurer = Puppet::Configurer.new configurer.run :catalog => catalog end command(:parseonly) do # Set our code or file to use. - if options[:code] or ARGV.length == 0 + if options[:code] or Puppet::Util::CommandLine.args.length == 0 Puppet[:code] = options[:code] || STDIN.read else - Puppet[:manifest] = ARGV.shift + Puppet[:manifest] = Puppet::Util::CommandLine.args.shift end begin Puppet::Resource::TypeCollection.new(Puppet[:environment]).perform_initial_import rescue => detail Puppet.err detail exit 1 end exit 0 end command(:main) do # Set our code or file to use. - if options[:code] or ARGV.length == 0 + if options[:code] or Puppet::Util::CommandLine.args.length == 0 Puppet[:code] = options[:code] || STDIN.read else - Puppet[:manifest] = ARGV.shift + Puppet[:manifest] = Puppet::Util::CommandLine.args.shift end # Collect our facts. unless facts = Puppet::Node::Facts.find(Puppet[:certname]) raise "Could not find facts for %s" % Puppet[:certname] end # Find our Node unless node = Puppet::Node.find(Puppet[:certname]) raise "Could not find node %s" % Puppet[:certname] end # Merge in the facts. node.merge(facts.values) # Allow users to load the classes that puppetd creates. if options[:loadclasses] file = Puppet[:classfile] if FileTest.exists?(file) unless FileTest.readable?(file) $stderr.puts "%s is not readable" % file exit(63) end node.classes = File.read(file).split(/[\s\n]+/) end end begin # Compile our catalog starttime = Time.now catalog = Puppet::Resource::Catalog.find(node.name, :use_node => node) # Translate it to a RAL catalog catalog = catalog.to_ral catalog.host_config = true if Puppet[:graph] or Puppet[:report] catalog.finalize catalog.retrieval_duration = Time.now - starttime configurer = Puppet::Configurer.new configurer.execute_prerun_command # And apply it transaction = catalog.apply configurer.execute_postrun_command status = 0 if not Puppet[:noop] and options[:detailed_exitcodes] then transaction.generate_report exit(transaction.report.exit_status) else exit(0) end rescue => detail puts detail.backtrace if Puppet[:trace] if detail.is_a?(XMLRPC::FaultException) $stderr.puts detail.message else $stderr.puts detail end exit(1) end end setup do if Puppet.settings.print_configs? exit(Puppet.settings.print_configs ? 0 : 1) end # If noop is set, then also enable diffs if Puppet[:noop] Puppet[:show_diff] = true end unless options[:logset] Puppet::Util::Log.newdestination(:console) end client = nil server = nil trap(:INT) do $stderr.puts "Exiting" exit(1) end if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] Puppet::Util::Log.level = :info end end end diff --git a/lib/puppet/application/cert.rb b/lib/puppet/application/cert.rb index f48e5301a..7a7784b09 100644 --- a/lib/puppet/application/cert.rb +++ b/lib/puppet/application/cert.rb @@ -1,76 +1,76 @@ require 'puppet' require 'puppet/application' require 'puppet/ssl/certificate_authority' Puppet::Application.new(:cert) do should_parse_config attr_accessor :mode, :all, :ca, :digest def find_mode(opt) modes = Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS tmp = opt.sub("--", '').to_sym @mode = modes.include?(tmp) ? tmp : nil end option("--clean", "-c") do @mode = :destroy end option("--all", "-a") do @all = true end option("--digest DIGEST") do |arg| @digest = arg end option("--debug", "-d") do |arg| Puppet::Util::Log.level = :debug end Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject {|m| m == :destroy }.each do |method| option("--#{method}", "-%s" % method.to_s[0,1] ) do find_mode("--#{method}") end end option("--verbose", "-v") do Puppet::Util::Log.level = :info end command(:main) do if @all hosts = :all else - hosts = ARGV.collect { |h| puts h; h.downcase } + hosts = Puppet::Util::CommandLine.args.collect { |h| puts h; h.downcase } end begin @ca.apply(:revoke, :to => hosts) if @mode == :destroy @ca.apply(@mode, :to => hosts, :digest => @digest) rescue => detail puts detail.backtrace if Puppet[:trace] puts detail.to_s exit(24) end end setup do if Puppet.settings.print_configs? exit(Puppet.settings.print_configs ? 0 : 1) end Puppet::Util::Log.newdestination :console Puppet::SSL::Host.ca_location = :only begin @ca = Puppet::SSL::CertificateAuthority.new rescue => detail puts detail.backtrace if Puppet[:trace] puts detail.to_s exit(23) end end end diff --git a/lib/puppet/application/describe.rb b/lib/puppet/application/describe.rb index d3d335496..ea4ac162c 100644 --- a/lib/puppet/application/describe.rb +++ b/lib/puppet/application/describe.rb @@ -1,214 +1,214 @@ require 'puppet' require 'puppet/application' class Formatter def initialize(width) @width = width end def wrap(txt, opts) return "" unless txt && !txt.empty? work = (opts[:scrub] ? scrub(txt) : txt) indent = (opts[:indent] ? opts[:indent] : 0) textLen = @width - indent patt = Regexp.new("^(.{0,#{textLen}})[ \n]") prefix = " " * indent res = [] while work.length > textLen if work =~ patt res << $1 work.slice!(0, $&.length) else res << work.slice!(0, textLen) end end res << work if work.length.nonzero? return prefix + res.join("\n" + prefix) end def header(txt, sep = "-") "\n#{txt}\n" + sep * txt.size end private def scrub(text) # For text with no carriage returns, there's nothing to do. if text !~ /\n/ return text end indent = nil # If we can match an indentation, then just remove that same level of # indent from every line. if text =~ /^(\s+)/ indent = $1 return text.gsub(/^#{indent}/,'') else return text end end end class TypeDoc def initialize @format = Formatter.new(76) @types = {} Puppet::Type.loadall Puppet::Type.eachtype { |type| next if type.name == :component @types[type.name] = type } end def list_types puts "These are the types known to puppet:\n" @types.keys.sort { |a, b| a.to_s <=> b.to_s }.each do |name| type = @types[name] s = type.doc.gsub(/\s+/, " ") n = s.index(".") if n.nil? s = ".. no documentation .." elsif n > 45 s = s[0, 45] + " ..." else s = s[0, n] end printf "%-15s - %s\n", name, s end end def format_type(name, opts) name = name.to_sym unless @types.has_key?(name) puts "Unknown type #{name}" return end type = @types[name] puts @format.header(name.to_s, "=") puts @format.wrap(type.doc, :indent => 0, :scrub => true) + "\n\n" puts @format.header("Parameters") if opts[:parameters] format_attrs(type, [:property, :param]) else list_attrs(type, [:property, :param]) end if opts[:meta] puts @format.header("Meta Parameters") if opts[:parameters] format_attrs(type, [:meta]) else list_attrs(type, [:meta]) end end if type.providers.size > 0 puts @format.header("Providers") if opts[:providers] format_providers(type) else list_providers(type) end end end # List details about attributes def format_attrs(type, attrs) docs = {} type.allattrs.each do |name| kind = type.attrtype(name) if attrs.include?(kind) && name != :provider docs[name] = type.attrclass(name).doc end end docs.sort { |a,b| a[0].to_s <=> b[0].to_s }.each { |name, doc| print "\n- **%s**" % name if type.namevar == name and name != :name puts " (*namevar*)" else puts "" end puts @format.wrap(doc, :indent => 4, :scrub => true) } end # List the names of attributes def list_attrs(type, attrs) params = [] type.allattrs.each do |name| kind = type.attrtype(name) if attrs.include?(kind) && name != :provider params << name.to_s end end puts @format.wrap(params.sort.join(", "), :indent => 4) end def format_providers(type) type.providers.sort { |a,b| a.to_s <=> b.to_s }.each { |prov| puts "\n- **%s**" % prov puts @format.wrap(type.provider(prov).doc, :indent => 4, :scrub => true) } end def list_providers(type) list = type.providers.sort { |a,b| a.to_s <=> b.to_s }.join(", ") puts @format.wrap(list, :indent => 4) end end Puppet::Application.new(:describe,"#{$0} [options] [type]") do should_not_parse_config option("--short", "-s", "Only list parameters without detail") do |arg| options[:parameters] = false end option("--providers","-p") option("--list", "-l") option("--meta","-m") preinit do options[:parameters] = true end command(:main) do doc = TypeDoc.new if options[:list] doc.list_types else options[:types].each { |name| doc.format_type(name, options) } end end setup do - options[:types] = ARGV.dup + options[:types] = Puppet::Util::CommandLine.args.dup unless options[:list] || options[:types].size > 0 handle_help(nil) end if options[:list] && options[:types].size > 0 $stderr.puts "Warning: ignoring types when listing all types" end end end diff --git a/lib/puppet/application/doc.rb b/lib/puppet/application/doc.rb index 32f9ba75c..0a11d6045 100644 --- a/lib/puppet/application/doc.rb +++ b/lib/puppet/application/doc.rb @@ -1,224 +1,224 @@ require 'puppet' require 'puppet/application' require 'puppet/util/reference' require 'puppet/network/handler' require 'puppet/util/rdoc' $tab = " " Reference = Puppet::Util::Reference Puppet::Application.new(:doc) do should_not_parse_config attr_accessor :unknown_args, :manifest preinit do {:references => [], :mode => :text, :format => :to_rest }.each do |name,value| options[name] = value end @unknown_args = [] @manifest = false end option("--all","-a") option("--outputdir OUTPUTDIR","-o") option("--verbose","-v") option("--debug","-d") option("--format FORMAT", "-f") do |arg| method = "to_%s" % arg if Reference.method_defined?(method) options[:format] = method else raise "Invalid output format %s" % arg end end option("--mode MODE", "-m") do |arg| if Reference.modes.include?(arg) or arg.intern==:rdoc options[:mode] = arg.intern else raise "Invalid output mode %s" % arg end end option("--list", "-l") do |arg| puts Reference.references.collect { |r| Reference.reference(r).doc }.join("\n") exit(0) end option("--reference REFERENCE", "-r") do |arg| options[:references] << arg.intern end unknown do |opt, arg| @unknown_args << {:opt => opt, :arg => arg } true end dispatch do return options[:mode] if [:rdoc, :trac, :markdown].include?(options[:mode]) return :other end command(:rdoc) do exit_code = 0 files = [] unless @manifest env = Puppet::Node::Environment.new files += env.modulepath files << File.dirname(env[:manifest]) end - files += ARGV + files += Puppet::Util::CommandLine.args Puppet.info "scanning: %s" % files.inspect Puppet.settings.setdefaults("puppetdoc", "document_all" => [false, "Document all resources"] ) Puppet.settings[:document_all] = options[:all] || false begin if @manifest Puppet::Util::RDoc.manifestdoc(files) else options[:outputdir] = "doc" unless options[:outputdir] Puppet::Util::RDoc.rdoc(options[:outputdir], files) end rescue => detail if Puppet[:trace] puts detail.backtrace end $stderr.puts "Could not generate documentation: %s" % detail exit_code = 1 end exit exit_code end command(:trac) do options[:references].each do |name| section = Puppet::Util::Reference.reference(name) or raise "Could not find section %s" % name unless options[:mode] == :pdf section.trac end end end command(:markdown) do text = "" with_contents = false exit_code = 0 options[:references].sort { |a,b| a.to_s <=> b.to_s }.each do |name| raise "Could not find reference %s" % name unless section = Puppet::Util::Reference.reference(name) begin # Add the per-section text, but with no ToC text += section.send(options[:format], with_contents) text += Puppet::Util::Reference.footer text.gsub!(/`\w+\s+([^`]+)`:trac:/) { |m| $1 } Puppet::Util::Reference.markdown(name, text) text = "" rescue => detail puts detail.backtrace $stderr.puts "Could not generate reference %s: %s" % [name, detail] exit_code = 1 next end end exit exit_code end command(:other) do text = "" if options[:references].length > 1 with_contents = false else with_contents = true end exit_code = 0 options[:references].sort { |a,b| a.to_s <=> b.to_s }.each do |name| raise "Could not find reference %s" % name unless section = Puppet::Util::Reference.reference(name) begin # Add the per-section text, but with no ToC text += section.send(options[:format], with_contents) rescue => detail puts detail.backtrace $stderr.puts "Could not generate reference %s: %s" % [name, detail] exit_code = 1 next end end unless with_contents # We've only got one reference text += Puppet::Util::Reference.footer end # Replace the trac links, since they're invalid everywhere else text.gsub!(/`\w+\s+([^`]+)`:trac:/) { |m| $1 } if options[:mode] == :pdf Puppet::Util::Reference.pdf(text) else puts text end exit exit_code end setup do # sole manifest documentation - if ARGV.size > 0 + if Puppet::Util::CommandLine.args.size > 0 options[:mode] = :rdoc @manifest = true end if options[:mode] == :rdoc setup_rdoc else setup_reference end end def setup_reference if options[:all] # Don't add dynamic references to the "all" list. options[:references] = Reference.references.reject do |ref| Reference.reference(ref).dynamic? end end if options[:references].empty? options[:references] << :type end end def setup_rdoc(dummy_argument=:work_arround_for_ruby_GC_bug) # consume the unknown options # and feed them as settings if @unknown_args.size > 0 @unknown_args.each do |option| # force absolute path for modulepath when passed on commandline if option[:opt]=="--modulepath" or option[:opt] == "--manifestdir" option[:arg] = option[:arg].split(':').collect { |p| File.expand_path(p) }.join(':') end Puppet.settings.handlearg(option[:opt], option[:arg]) end end # hack to get access to puppetmasterd modulepath and manifestdir Puppet[:name] = "puppetmasterd" # Now parse the config Puppet.parse_config # 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 Puppet::Util::Log.newdestination(:console) end end end diff --git a/lib/puppet/application/filebucket.rb b/lib/puppet/application/filebucket.rb index ed67009aa..cd7c854af 100644 --- a/lib/puppet/application/filebucket.rb +++ b/lib/puppet/application/filebucket.rb @@ -1,87 +1,92 @@ require 'puppet' require 'puppet/application' require 'puppet/file_bucket/dipper' Puppet::Application.new(:filebucket) do should_not_parse_config option("--bucket BUCKET","-b") option("--debug","-d") option("--local","-l") option("--remote","-r") option("--verbose","-v") + class << self + attr :args + end + dispatch do - ARGV.shift + @args = Puppet::Util::CommandLine.args + args.shift end command(:get) do - md5 = ARGV.shift + md5 = args.shift out = @client.getfile(md5) print out end command(:backup) do - ARGV.each do |file| + args.each do |file| unless FileTest.exists?(file) $stderr.puts "%s: no such file" % file next end unless FileTest.readable?(file) $stderr.puts "%s: cannot read file" % file next end md5 = @client.backup(file) puts "%s: %s" % [file, md5] end end command(:restore) do - file = ARGV.shift - md5 = ARGV.shift + file = args.shift + md5 = args.shift @client.restore(file, md5) end setup do Puppet::Log.newdestination(:console) @client = nil @server = nil trap(:INT) do $stderr.puts "Cancelling" exit(1) end if options[:debug] Puppet::Log.level = :debug elsif options[:verbose] Puppet::Log.level = :info end # Now parse the config Puppet.parse_config if Puppet.settings.print_configs? exit(Puppet.settings.print_configs ? 0 : 1) end begin if options[:local] or options[:bucket] path = options[:bucket] || Puppet[:bucketdir] @client = Puppet::FileBucket::Dipper.new(:Path => path) else @client = Puppet::FileBucket::Dipper.new(:Server => Puppet[:server]) end rescue => detail $stderr.puts detail if Puppet[:trace] puts detail.backtrace end exit(1) end end end diff --git a/lib/puppet/application/resource.rb b/lib/puppet/application/resource.rb index ae4349850..78aed95c5 100644 --- a/lib/puppet/application/resource.rb +++ b/lib/puppet/application/resource.rb @@ -1,123 +1,124 @@ require 'puppet' require 'puppet/application' require 'facter' Puppet::Application.new(:resource) do should_not_parse_config attr_accessor :host, :extra_params preinit do @extra_params = [] @host = nil Facter.loadfacts end option("--debug","-d") option("--verbose","-v") option("--edit","-e") option("--host HOST","-H") do |arg| @host = arg end option("--types", "-t") do |arg| types = [] Puppet::Type.loadall Puppet::Type.eachtype do |t| next if t.name == :component types << t.name.to_s end puts types.sort exit end option("--param PARAM", "-p") do |arg| @extra_params << arg.to_sym end command(:main) do - type = ARGV.shift or raise "You must specify the type to display" + args = Puppet::Util::CommandLine.args + type = args.shift or raise "You must specify the type to display" typeobj = Puppet::Type.type(type) or raise "Could not find type #{type}" - name = ARGV.shift + name = args.shift params = {} - ARGV.each do |setting| + args.each do |setting| if setting =~ /^(\w+)=(.+)$/ params[$1] = $2 else raise "Invalid parameter setting %s" % setting end end if options[:edit] and @host raise "You cannot edit a remote host" end properties = typeobj.properties.collect { |s| s.name } format = proc {|trans| trans.dup.collect do |param, value| if value.nil? or value.to_s.empty? trans.delete(param) elsif value.to_s == "absent" and param.to_s != "ensure" trans.delete(param) end unless properties.include?(param) or @extra_params.include?(param) trans.delete(param) end end trans.to_manifest } if @host Puppet::Resource.indirection.terminus_class = :rest port = Puppet[:puppetport] key = ["https://#{host}:#{port}", "production", "resources", type, name].join('/') else key = [type, name].join('/') end text = if name if params.empty? [ Puppet::Resource.find( key ) ] else [ Puppet::Resource.new( type, name, params ).save( key ) ] end else Puppet::Resource.search( key, {} ) end.map(&format).join("\n") if options[:edit] file = "/tmp/x2puppet-#{Process.pid}.pp" begin File.open(file, "w") do |f| f.puts text end ENV["EDITOR"] ||= "vi" system(ENV["EDITOR"], file) system("puppet -v " + file) ensure #if FileTest.exists? file # File.unlink(file) #end end else puts text end end setup do Puppet::Util::Log.newdestination(:console) # Now parse the config Puppet.parse_config if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] Puppet::Util::Log.level = :info end end end diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index a9cb48419..66f038307 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1,811 +1,800 @@ # The majority of the system configuration parameters are set in this file. module Puppet # If we're running the standalone puppet process as a non-root user, # use basedirs that are in the user's home directory. conf = nil var = nil - legacy_name = Hash.new{ |h,k| k }.update({ - 'agent' => 'puppetd', - 'cert' => 'puppetca', - 'doc' => 'puppetdoc', - 'filebucket' => 'filebucket', - 'apply' => 'puppet', - 'describe' => 'pi', - 'queue' => 'puppetqd', - 'resource' => 'ralsh', - 'kick' => 'puppetrun', - 'master' => 'puppetmasterd', - }) - name = legacy_name[ $puppet_subcommand_name ] || $0.gsub(/.+#{File::SEPARATOR}/,'').sub(/\.rb$/, '') + require 'puppet/util/command_line' + name = Puppet::Util::CommandLine.legacy_executable_name # Make File.expand_path happy require 'etc' ENV["HOME"] ||= Etc.getpwuid(Process.uid).dir if name != "puppetmasterd" and Puppet::Util::SUIDManager.uid != 0 conf = File.expand_path("~/.puppet") var = File.expand_path("~/.puppet/var") else # Else, use system-wide directories. conf = "/etc/puppet" var = "/var/lib/puppet" end self.setdefaults(:main, :confdir => [conf, "The main Puppet configuration directory. The default for this parameter is calculated based on the user. If the process is runnig as root or the user that ``puppetmasterd`` is supposed to run as, it defaults to a system directory, but if it's running as any other user, it defaults to being in ``~``."], :vardir => [var, "Where Puppet stores dynamic and growing data. The default for this parameter is calculated specially, like `confdir`_."], :name => [name, "The name of the service, if we are running as one. The default is essentially $0 without the path or ``.rb``."] ) if name == "puppetmasterd" logopts = {:default => "$vardir/log", :mode => 0750, :owner => "service", :group => "service", :desc => "The Puppet log directory." } else logopts = ["$vardir/log", "The Puppet log directory."] end setdefaults(:main, :logdir => logopts) # This name hackery is necessary so that the rundir is set reasonably during # unit tests. if Process.uid == 0 and %w{puppetd puppetmasterd}.include?(self.name) rundir = "/var/run/puppet" else rundir = "$vardir/run" end self.setdefaults(:main, :trace => [false, "Whether to print stack traces on some errors"], :autoflush => [false, "Whether log files should always flush to disk."], :syslogfacility => ["daemon", "What syslog facility to use when logging to syslog. Syslog has a fixed list of valid facilities, and you must choose one of those; you cannot just make one up."], :statedir => { :default => "$vardir/state", :mode => 01755, :desc => "The directory where Puppet state is stored. Generally, this directory can be removed without causing harm (although it might result in spurious service restarts)." }, :rundir => { :default => rundir, :mode => 01777, :desc => "Where Puppet PID files are kept." }, :genconfig => [false, "Whether to just print a configuration to stdout and exit. Only makes sense when used interactively. Takes into account arguments specified on the CLI."], :genmanifest => [false, "Whether to just print a manifest to stdout and exit. Only makes sense when used interactively. Takes into account arguments specified on the CLI."], :configprint => ["", "Print the value of a specific configuration parameter. If a parameter is provided for this, then the value is printed and puppet exits. Comma-separate multiple values. For a list of all values, specify 'all'. This feature is only available in Puppet versions higher than 0.18.4."], :color => ["ansi", "Whether to use colors when logging to the console. Valid values are ``ansi`` (equivalent to ``true``), ``html`` (mostly used during testing with TextMate), and ``false``, which produces no color."], :mkusers => [false, "Whether to create the necessary user and group that puppetd will run as."], :manage_internal_file_permissions => [true, "Whether Puppet should manage the owner, group, and mode of files it uses internally" ], :path => {:default => "none", :desc => "The shell search path. Defaults to whatever is inherited from the parent process.", :call_on_define => true, # Call our hook with the default value, so we always get the libdir set. :hook => proc do |value| ENV["PATH"] = "" if ENV["PATH"].nil? ENV["PATH"] = value unless value == "none" paths = ENV["PATH"].split(File::PATH_SEPARATOR) %w{/usr/sbin /sbin}.each do |path| unless paths.include?(path) ENV["PATH"] += File::PATH_SEPARATOR + path end end value end }, :libdir => {:default => "$vardir/lib", :desc => "An extra search path for Puppet. This is only useful for those files that Puppet will load on demand, and is only guaranteed to work for those cases. In fact, the autoload mechanism is responsible for making sure this directory is in Ruby's search path", :call_on_define => true, # Call our hook with the default value, so we always get the libdir set. :hook => proc do |value| if defined? @oldlibdir and $:.include?(@oldlibdir) $:.delete(@oldlibdir) end @oldlibdir = value $: << value end }, :ignoreimport => [false, "A parameter that can be used in commit hooks, since it enables you to parse-check a single file rather than requiring that all files exist."], :authconfig => [ "$confdir/namespaceauth.conf", "The configuration file that defines the rights to the different namespaces and methods. This can be used as a coarse-grained authorization system for both ``puppetd`` and ``puppetmasterd``." ], :environment => {:default => "production", :desc => "The environment Puppet is running in. For clients (e.g., ``puppetd``) this determines the environment itself, which is used to find modules and much more. For servers (i.e., ``puppetmasterd``) this provides the default environment for nodes we know nothing about." }, :diff_args => ["-u", "Which arguments to pass to the diff command when printing differences between files."], :diff => ["diff", "Which diff command to use when printing differences between files."], :show_diff => [false, "Whether to print a contextual diff when files are being replaced. The diff is printed on stdout, so this option is meaningless unless you are running Puppet interactively. This feature currently requires the ``diff/lcs`` Ruby library."], :daemonize => { :default => true, :desc => "Send the process into the background. This is the default.", :short => "D" }, :maximum_uid => [4294967290, "The maximum allowed UID. Some platforms use negative UIDs but then ship with tools that do not know how to handle signed ints, so the UIDs show up as huge numbers that can then not be fed back into the system. This is a hackish way to fail in a slightly more useful way when that happens."], :node_terminus => ["plain", "Where to find information about nodes."], :catalog_terminus => ["compiler", "Where to get node catalogs. This is useful to change if, for instance, you'd like to pre-compile catalogs and store them in memcached or some other easily-accessed store."], :httplog => { :default => "$logdir/http.log", :owner => "root", :mode => 0640, :desc => "Where the puppetd web server logs." }, :http_proxy_host => ["none", "The HTTP proxy host to use for outgoing connections. Note: You may need to use a FQDN for the server hostname when using a proxy."], :http_proxy_port => [3128, "The HTTP proxy port to use for outgoing connections"], :filetimeout => [ 15, "The minimum time to wait (in seconds) between checking for updates in configuration files. This timeout determines how quickly Puppet checks whether a file (such as manifests or templates) has changed on disk." ], :queue_type => ["stomp", "Which type of queue to use for asynchronous processing."], :queue_type => ["stomp", "Which type of queue to use for asynchronous processing."], :queue_source => ["stomp://localhost:61613/", "Which type of queue to use for asynchronous processing. If your stomp server requires authentication, you can include it in the URI as long as your stomp client library is at least 1.1.1"], :async_storeconfigs => {:default => false, :desc => "Whether to use a queueing system to provide asynchronous database integration. Requires that ``puppetqd`` be running and that 'PSON' support for ruby be installed.", :hook => proc do |value| if value # This reconfigures the terminii for Node, Facts, and Catalog Puppet.settings[:storeconfigs] = true # But then we modify the configuration Puppet::Resource::Catalog.cache_class = :queue else raise "Cannot disable asynchronous storeconfigs in a running process" end end }, :thin_storeconfigs => {:default => false, :desc => "Boolean; wether storeconfigs store in the database only the facts and exported resources. If true, then storeconfigs performance will be higher and still allow exported/collected resources, but other usage external to Puppet might not work", :hook => proc do |value| Puppet.settings[:storeconfigs] = true if value end }, :config_version => ["", "How to determine the configuration version. By default, it will be the time that the configuration is parsed, but you can provide a shell script to override how the version is determined. The output of this script will be added to every log message in the reports, allowing you to correlate changes on your hosts to the source version on the server."], :zlib => [true, "Boolean; whether to use the zlib library", ], :prerun_command => ["", "A command to run before every agent run. If this command returns a non-zero return code, the entire Puppet run will fail."], :postrun_command => ["", "A command to run after every agent run. If this command returns a non-zero return code, the entire Puppet run will be considered to have failed, even though it might have performed work during the normal run."] ) hostname = Facter["hostname"].value domain = Facter["domain"].value if domain and domain != "" fqdn = [hostname, domain].join(".") else fqdn = hostname end Puppet.setdefaults(:main, # We have to downcase the fqdn, because the current ssl stuff (as oppsed to in master) doesn't have good facilities for # manipulating naming. :certname => {:default => fqdn.downcase, :desc => "The name to use when handling certificates. Defaults to the fully qualified domain name.", :call_on_define => true, # Call our hook with the default value, so we're always downcased :hook => proc { |value| raise(ArgumentError, "Certificate names must be lower case; see #1168") unless value == value.downcase }}, :certdnsnames => ['', "The DNS names on the Server certificate as a colon-separated list. If it's anything other than an empty string, it will be used as an alias in the created certificate. By default, only the server gets an alias set up, and only for 'puppet'."], :certdir => { :default => "$ssldir/certs", :owner => "service", :desc => "The certificate directory." }, :ssldir => { :default => "$confdir/ssl", :mode => 0771, :owner => "service", :desc => "Where SSL certificates are kept." }, :publickeydir => { :default => "$ssldir/public_keys", :owner => "service", :desc => "The public key directory." }, :requestdir => { :default => "$ssldir/certificate_requests", :owner => "service", :desc => "Where host certificate requests are stored." }, :privatekeydir => { :default => "$ssldir/private_keys", :mode => 0750, :owner => "service", :desc => "The private key directory." }, :privatedir => { :default => "$ssldir/private", :mode => 0750, :owner => "service", :desc => "Where the client stores private certificate information." }, :passfile => { :default => "$privatedir/password", :mode => 0640, :owner => "service", :desc => "Where puppetd stores the password for its private key. Generally unused." }, :hostcsr => { :default => "$ssldir/csr_$certname.pem", :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their certificate requests." }, :hostcert => { :default => "$certdir/$certname.pem", :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their certificates." }, :hostprivkey => { :default => "$privatekeydir/$certname.pem", :mode => 0600, :owner => "service", :desc => "Where individual hosts store and look for their private key." }, :hostpubkey => { :default => "$publickeydir/$certname.pem", :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their public key." }, :localcacert => { :default => "$certdir/ca.pem", :mode => 0644, :owner => "service", :desc => "Where each client stores the CA certificate." }, :hostcrl => { :default => "$ssldir/crl.pem", :mode => 0644, :owner => "service", :desc => "Where the host's certificate revocation list can be found. This is distinct from the certificate authority's CRL." } ) setdefaults(:ca, :ca_name => ["$certname", "The name to use the Certificate Authority certificate."], :cadir => { :default => "$ssldir/ca", :owner => "service", :group => "service", :mode => 0770, :desc => "The root directory for the certificate authority." }, :cacert => { :default => "$cadir/ca_crt.pem", :owner => "service", :group => "service", :mode => 0660, :desc => "The CA certificate." }, :cakey => { :default => "$cadir/ca_key.pem", :owner => "service", :group => "service", :mode => 0660, :desc => "The CA private key." }, :capub => { :default => "$cadir/ca_pub.pem", :owner => "service", :group => "service", :desc => "The CA public key." }, :cacrl => { :default => "$cadir/ca_crl.pem", :owner => "service", :group => "service", :mode => 0664, :desc => "The certificate revocation list (CRL) for the CA. Will be used if present but otherwise ignored.", :hook => proc do |value| if value == 'false' Puppet.warning "Setting the :cacrl to 'false' is deprecated; Puppet will just ignore the crl if yours is missing" end end }, :caprivatedir => { :default => "$cadir/private", :owner => "service", :group => "service", :mode => 0770, :desc => "Where the CA stores private certificate information." }, :csrdir => { :default => "$cadir/requests", :owner => "service", :group => "service", :desc => "Where the CA stores certificate requests" }, :signeddir => { :default => "$cadir/signed", :owner => "service", :group => "service", :mode => 0770, :desc => "Where the CA stores signed certificates." }, :capass => { :default => "$caprivatedir/ca.pass", :owner => "service", :group => "service", :mode => 0660, :desc => "Where the CA stores the password for the private key" }, :serial => { :default => "$cadir/serial", :owner => "service", :group => "service", :mode => 0644, :desc => "Where the serial number for certificates is stored." }, :autosign => { :default => "$confdir/autosign.conf", :mode => 0644, :desc => "Whether to enable autosign. Valid values are true (which autosigns any key request, and is a very bad idea), false (which never autosigns any key request), and the path to a file, which uses that configuration file to determine which keys to sign."}, :ca_days => ["", "How long a certificate should be valid. This parameter is deprecated, use ca_ttl instead"], :ca_ttl => ["5y", "The default TTL for new certificates; valid values must be an integer, optionally followed by one of the units 'y' (years of 365 days), 'd' (days), 'h' (hours), or 's' (seconds). The unit defaults to seconds. If this parameter is set, ca_days is ignored. Examples are '3600' (one hour) and '1825d', which is the same as '5y' (5 years) "], :ca_md => ["md5", "The type of hash used in certificates."], :req_bits => [2048, "The bit length of the certificates."], :keylength => [1024, "The bit length of keys."], :cert_inventory => { :default => "$cadir/inventory.txt", :mode => 0644, :owner => "service", :group => "service", :desc => "A Complete listing of all certificates" } ) # Define the config default. self.setdefaults(self.settings[:name], :config => ["$confdir/puppet.conf", "The configuration file for #{Puppet[:name]}."], :pidfile => ["$rundir/$name.pid", "The pid file"], :bindaddress => ["", "The address a listening server should bind to. Mongrel servers default to 127.0.0.1 and WEBrick defaults to 0.0.0.0."], :servertype => {:default => "webrick", :desc => "The type of server to use. Currently supported options are webrick and mongrel. If you use mongrel, you will need a proxy in front of the process or processes, since Mongrel cannot speak SSL.", :call_on_define => true, # Call our hook with the default value, so we always get the correct bind address set. :hook => proc { |value| value == "webrick" ? Puppet.settings[:bindaddress] = "0.0.0.0" : Puppet.settings[:bindaddress] = "127.0.0.1" if Puppet.settings[:bindaddress] == "" } } ) self.setdefaults(:puppetmasterd, :user => ["puppet", "The user puppetmasterd should run as."], :group => ["puppet", "The group puppetmasterd should run as."], :manifestdir => ["$confdir/manifests", "Where puppetmasterd looks for its manifests."], :manifest => ["$manifestdir/site.pp", "The entry-point manifest for puppetmasterd."], :code => ["", "Code to parse directly. This is essentially only used by ``puppet``, and should only be set if you're writing your own Puppet executable"], :masterlog => { :default => "$logdir/puppetmaster.log", :owner => "service", :group => "service", :mode => 0660, :desc => "Where puppetmasterd logs. This is generally not used, since syslog is the default log destination." }, :masterhttplog => { :default => "$logdir/masterhttp.log", :owner => "service", :group => "service", :mode => 0660, :create => true, :desc => "Where the puppetmasterd web server logs." }, :masterport => [8140, "Which port puppetmasterd listens on."], :parseonly => [false, "Just check the syntax of the manifests."], :node_name => ["cert", "How the puppetmaster determines the client's identity and sets the 'hostname', 'fqdn' and 'domain' facts for use in the manifest, in particular for determining which 'node' statement applies to the client. Possible values are 'cert' (use the subject's CN in the client's certificate) and 'facter' (use the hostname that the client reported in its facts)"], :bucketdir => { :default => "$vardir/bucket", :mode => 0750, :owner => "service", :group => "service", :desc => "Where FileBucket files are stored." }, :rest_authconfig => [ "$confdir/auth.conf", "The configuration file that defines the rights to the different rest indirections. This can be used as a fine-grained authorization system for ``puppetmasterd``." ], :ca => [true, "Wether the master should function as a certificate authority."], :modulepath => {:default => "$confdir/modules:/usr/share/puppet/modules", :desc => "The search path for modules as a colon-separated list of directories.", :type => :setting }, # We don't want this to be considered a file, since it's multiple files. :ssl_client_header => ["HTTP_X_CLIENT_DN", "The header containing an authenticated client's SSL DN. Only used with Mongrel. This header must be set by the proxy to the authenticated client's SSL DN (e.g., ``/CN=puppet.reductivelabs.com``). See http://reductivelabs.com/puppet/trac/wiki/UsingMongrel for more information."], :ssl_client_verify_header => ["HTTP_X_CLIENT_VERIFY", "The header containing the status message of the client verification. Only used with Mongrel. This header must be set by the proxy to 'SUCCESS' if the client successfully authenticated, and anything else otherwise. See http://reductivelabs.com/puppet/trac/wiki/UsingMongrel for more information."], # To make sure this directory is created before we try to use it on the server, we need # it to be in the server section (#1138). :yamldir => {:default => "$vardir/yaml", :owner => "service", :group => "service", :mode => "750", :desc => "The directory in which YAML data is stored, usually in a subdirectory."}, :server_datadir => {:default => "$vardir/server_data", :owner => "service", :group => "service", :mode => "750", :desc => "The directory in which serialized data is stored, usually in a subdirectory."}, :reports => ["store", "The list of reports to generate. All reports are looked for in puppet/reports/.rb, and multiple report names should be comma-separated (whitespace is okay)." ], :reportdir => {:default => "$vardir/reports", :mode => 0750, :owner => "service", :group => "service", :desc => "The directory in which to store reports received from the client. Each client gets a separate subdirectory."}, :fileserverconfig => ["$confdir/fileserver.conf", "Where the fileserver configuration is stored."], :rrddir => {:default => "$vardir/rrd", :owner => "service", :group => "service", :desc => "The directory where RRD database files are stored. Directories for each reporting host will be created under this directory." }, :rrdinterval => ["$runinterval", "How often RRD should expect data. This should match how often the hosts report back to the server."], :strict_hostname_checking => [false, "Whether to only search for the complete hostname as it is in the certificate when searching for node information in the catalogs."] ) self.setdefaults(:puppetd, :localconfig => { :default => "$statedir/localconfig", :owner => "root", :mode => 0660, :desc => "Where puppetd caches the local configuration. An extension indicating the cache format is added automatically."}, :statefile => { :default => "$statedir/state.yaml", :mode => 0660, :desc => "Where puppetd and puppetmasterd store state associated with the running configuration. In the case of puppetmasterd, this file reflects the state discovered through interacting with clients." }, :clientyamldir => {:default => "$vardir/client_yaml", :mode => "750", :desc => "The directory in which client-side YAML data is stored."}, :client_datadir => {:default => "$vardir/client_data", :mode => "750", :desc => "The directory in which serialized data is stored on the client."}, :classfile => { :default => "$statedir/classes.txt", :owner => "root", :mode => 0644, :desc => "The file in which puppetd stores a list of the classes associated with the retrieved configuration. Can be loaded in the separate ``puppet`` executable using the ``--loadclasses`` option."}, :puppetdlog => { :default => "$logdir/puppetd.log", :owner => "root", :mode => 0640, :desc => "The log file for puppetd. This is generally not used." }, :server => ["puppet", "The server to which server puppetd should connect"], :ignoreschedules => [false, "Boolean; whether puppetd should ignore schedules. This is useful for initial puppetd runs."], :puppetport => [8139, "Which port puppetd listens on."], :noop => [false, "Whether puppetd should be run in noop mode."], :runinterval => [1800, # 30 minutes "How often puppetd applies the client configuration; in seconds."], :listen => [false, "Whether puppetd should listen for connections. If this is true, then by default only the ``runner`` server is started, which allows remote authorized and authenticated nodes to connect and trigger ``puppetd`` runs."], :ca_server => ["$server", "The server to use for certificate authority requests. It's a separate server because it cannot and does not need to horizontally scale."], :ca_port => ["$masterport", "The port to use for the certificate authority."], :catalog_format => { :default => "", :desc => "(Deprecated for 'preferred_serialization_format') What format to use to dump the catalog. Only supports 'marshal' and 'yaml'. Only matters on the client, since it asks the server for a specific format.", :hook => proc { |value| if value Puppet.warning "Setting 'catalog_format' is deprecated; use 'preferred_serialization_format' instead." Puppet.settings[:preferred_serialization_format] = value end } }, :preferred_serialization_format => ["pson", "The preferred means of serializing ruby instances for passing over the wire. This won't guarantee that all instances will be serialized using this method, since not all classes can be guaranteed to support this format, but it will be used for all classes that support it."], :puppetdlockfile => [ "$statedir/puppetdlock", "A lock file to temporarily stop puppetd from doing anything."], :usecacheonfailure => [true, "Whether to use the cached configuration when the remote configuration will not compile. This option is useful for testing new configurations, where you want to fix the broken configuration rather than reverting to a known-good one." ], :use_cached_catalog => [false, "Whether to only use the cached catalog rather than compiling a new catalog on every run. Puppet can be run with this enabled by default and then selectively disabled when a recompile is desired."], :ignorecache => [false, "Ignore cache and always recompile the configuration. This is useful for testing new configurations, where the local cache may in fact be stale even if the timestamps are up to date - if the facts change or if the server changes." ], :downcasefacts => [false, "Whether facts should be made all lowercase when sent to the server."], :dynamicfacts => ["memorysize,memoryfree,swapsize,swapfree", "Facts that are dynamic; these facts will be ignored when deciding whether changed facts should result in a recompile. Multiple facts should be comma-separated."], :splaylimit => ["$runinterval", "The maximum time to delay before runs. Defaults to being the same as the run interval."], :splay => [false, "Whether to sleep for a pseudo-random (but consistent) amount of time before a run."], :clientbucketdir => { :default => "$vardir/clientbucket", :mode => 0750, :desc => "Where FileBucket files are stored locally." }, :configtimeout => [120, "How long the client should wait for the configuration to be retrieved before considering it a failure. This can help reduce flapping if too many clients contact the server at one time." ], :reportserver => { :default => "$server", :call_on_define => false, :desc => "(Deprecated for 'report_server') The server to which to send transaction reports.", :hook => proc do |value| if value Puppet.settings[:report_server] = value end end }, :report_server => ["$server", "The server to which to send transaction reports." ], :report_port => ["$masterport", "The port to communicate with the report_server." ], :report => [false, "Whether to send reports after every transaction." ], :graph => [false, "Whether to create dot graph files for the different configuration graphs. These dot files can be interpreted by tools like OmniGraffle or dot (which is part of ImageMagick)."], :graphdir => ["$statedir/graphs", "Where to store dot-outputted graphs."] ) # Plugin information. self.setdefaults(:main, :plugindest => ["$libdir", "Where Puppet should store plugins that it pulls down from the central server."], :pluginsource => ["puppet://$server/plugins", "From where to retrieve plugins. The standard Puppet ``file`` type is used for retrieval, so anything that is a valid file source can be used here."], :pluginsync => [false, "Whether plugins should be synced with the central server."], :pluginsignore => [".svn CVS .git", "What files to ignore when pulling down plugins."] ) # Central fact information. self.setdefaults(:main, :factpath => {:default => "$vardir/lib/facter/:$vardir/facts", :desc => "Where Puppet should look for facts. Multiple directories should be colon-separated, like normal PATH variables.", :call_on_define => true, # Call our hook with the default value, so we always get the value added to facter. :type => :setting, # Don't consider it a file, because it could be multiple colon-separated files :hook => proc { |value| Facter.search(value) if Facter.respond_to?(:search) }}, :factdest => ["$vardir/facts/", "Where Puppet should store facts that it pulls down from the central server."], :factsource => ["puppet://$server/facts/", "From where to retrieve facts. The standard Puppet ``file`` type is used for retrieval, so anything that is a valid file source can be used here."], :factsync => [false, "Whether facts should be synced with the central server."], :factsignore => [".svn CVS", "What files to ignore when pulling down facts."] ) self.setdefaults(:tagmail, :tagmap => ["$confdir/tagmail.conf", "The mapping between reporting tags and email addresses."], :sendmail => [%x{which sendmail 2>/dev/null}.chomp, "Where to find the sendmail binary with which to send email."], :reportfrom => ["report@" + [Facter["hostname"].value, Facter["domain"].value].join("."), "The 'from' email address for the reports."], :smtpserver => ["none", "The server through which to send email reports."] ) self.setdefaults(:rails, :dblocation => { :default => "$statedir/clientconfigs.sqlite3", :mode => 0660, :owner => "service", :group => "service", :desc => "The database cache for client configurations. Used for querying within the language." }, :dbadapter => [ "sqlite3", "The type of database to use." ], :dbmigrate => [ false, "Whether to automatically migrate the database." ], :dbname => [ "puppet", "The name of the database to use." ], :dbserver => [ "localhost", "The database server for caching. Only used when networked databases are used."], :dbport => [ "", "The database password for caching. Only used when networked databases are used."], :dbuser => [ "puppet", "The database user for caching. Only used when networked databases are used."], :dbpassword => [ "puppet", "The database password for caching. Only used when networked databases are used."], :dbsocket => [ "", "The database socket location. Only used when networked databases are used. Will be ignored if the value is an empty string."], :dbconnections => [ 0, "The number of database connections. Only used when networked databases are used. Will be ignored if the value is an empty string or is less than 1."], :railslog => {:default => "$logdir/rails.log", :mode => 0600, :owner => "service", :group => "service", :desc => "Where Rails-specific logs are sent" }, :rails_loglevel => ["info", "The log level for Rails connections. The value must be a valid log level within Rails. Production environments normally use ``info`` and other environments normally use ``debug``."] ) setdefaults(:transaction, :tags => ["", "Tags to use to find resources. If this is set, then only resources tagged with the specified tags will be applied. Values must be comma-separated."], :evaltrace => [false, "Whether each resource should log when it is being evaluated. This allows you to interactively see exactly what is being done."], :summarize => [false, "Whether to print a transaction summary." ] ) setdefaults(:main, :external_nodes => ["none", "An external command that can produce node information. The output must be a YAML dump of a hash, and that hash must have one or both of ``classes`` and ``parameters``, where ``classes`` is an array and ``parameters`` is a hash. For unknown nodes, the commands should exit with a non-zero exit code. This command makes it straightforward to store your node mapping information in other data sources like databases."]) setdefaults(:ldap, :ldapnodes => [false, "Whether to search for node configurations in LDAP. See http://reductivelabs.com/trac/puppet/wiki/LDAPNodes for more information."], :ldapssl => [false, "Whether SSL should be used when searching for nodes. Defaults to false because SSL usually requires certificates to be set up on the client side."], :ldaptls => [false, "Whether TLS should be used when searching for nodes. Defaults to false because TLS usually requires certificates to be set up on the client side."], :ldapserver => ["ldap", "The LDAP server. Only used if ``ldapnodes`` is enabled."], :ldapport => [389, "The LDAP port. Only used if ``ldapnodes`` is enabled."], :ldapstring => ["(&(objectclass=puppetClient)(cn=%s))", "The search string used to find an LDAP node."], :ldapclassattrs => ["puppetclass", "The LDAP attributes to use to define Puppet classes. Values should be comma-separated."], :ldapstackedattrs => ["puppetvar", "The LDAP attributes that should be stacked to arrays by adding the values in all hierarchy elements of the tree. Values should be comma-separated."], :ldapattrs => ["all", "The LDAP attributes to include when querying LDAP for nodes. All returned attributes are set as variables in the top-level scope. Multiple values should be comma-separated. The value 'all' returns all attributes."], :ldapparentattr => ["parentnode", "The attribute to use to define the parent node."], :ldapuser => ["", "The user to use to connect to LDAP. Must be specified as a full DN."], :ldappassword => ["", "The password to use to connect to LDAP."], :ldapbase => ["", "The search base for LDAP searches. It's impossible to provide a meaningful default here, although the LDAP libraries might have one already set. Generally, it should be the 'ou=Hosts' branch under your main directory."] ) setdefaults(:puppetmasterd, :storeconfigs => {:default => false, :desc => "Whether to store each client's configuration. This requires ActiveRecord from Ruby on Rails.", :call_on_define => true, # Call our hook with the default value, so we always get the libdir set. :hook => proc do |value| require 'puppet/node' require 'puppet/node/facts' require 'puppet/resource/catalog' if value raise "StoreConfigs not supported without ActiveRecord 2.1 or higher" unless Puppet.features.rails? Puppet::Resource::Catalog.cache_class = :active_record unless Puppet.settings[:async_storeconfigs] Puppet::Node::Facts.cache_class = :active_record Puppet::Node.cache_class = :active_record end end } ) # This doesn't actually work right now. setdefaults(:parser, :lexical => [false, "Whether to use lexical scoping (vs. dynamic)."], :templatedir => ["$vardir/templates", "Where Puppet looks for template files. Can be a list of colon-seperated directories." ] ) end diff --git a/lib/puppet/reference/providers.rb b/lib/puppet/reference/providers.rb index d425d803e..df02178e3 100644 --- a/lib/puppet/reference/providers.rb +++ b/lib/puppet/reference/providers.rb @@ -1,124 +1,124 @@ # This doesn't get stored in trac, since it changes every time. providers = Puppet::Util::Reference.newreference :providers, :title => "Provider Suitability Report", :depth => 1, :dynamic => true, :doc => "Which providers are valid for this machine" do types = [] Puppet::Type.loadall Puppet::Type.eachtype do |klass| next unless klass.providers.length > 0 types << klass end types.sort! { |a,b| a.name.to_s <=> b.name.to_s } - unless ARGV.empty? - types.reject! { |type| ! ARGV.include?(type.name.to_s) } + unless Puppet::Util::CommandLine.args.empty? + types.reject! { |type| ! Puppet::Util::CommandLine.args.include?(type.name.to_s) } end ret = "Details about this host:\n\n" # Throw some facts in there, so we know where the report is from. ["Ruby Version", "Puppet Version", "Operating System", "Operating System Release"].each do |label| name = label.gsub(/\s+/, '') value = Facter.value(name) ret += option(label, value) end ret += "\n" count = 1 # Produce output for each type. types.each do |type| features = type.features ret += "\n" # add a trailing newline # Now build up a table of provider suitability. headers = %w{Provider Suitable?} + features.collect { |f| f.to_s }.sort table_data = {} functional = false notes = [] begin default = type.defaultprovider.name rescue Puppet::DevError default = "none" end type.providers.sort { |a,b| a.to_s <=> b.to_s }.each do |pname| data = [] table_data[pname] = data provider = type.provider(pname) # Add the suitability note if missing = provider.suitable?(false) and missing.empty? data << "**X**" suit = true functional = true else data << "[%s]_" % [count] # A pointer to the appropriate footnote suit = false end # Add a footnote with the details about why this provider is unsuitable, if that's the case unless suit details = ".. [%s]\n" % count missing.each do |test, values| case test when :exists details += " - Missing files %s\n" % values.join(", ") when :variable values.each do |name, facts| if Puppet.settings.valid?(name) details += " - Setting %s (currently %s) not in list %s\n" % [name, Puppet.settings.value(name).inspect, facts.join(", ")] else details += " - Fact %s (currently %s) not in list %s\n" % [name, Facter.value(name).inspect, facts.join(", ")] end end when :true details += " - Got %s true tests that should have been false\n" % values when :false details += " - Got %s false tests that should have been true\n" % values when :feature details += " - Missing features %s\n" % values.collect { |f| f.to_s }.join(",") end end notes << details count += 1 end # Add a note for every feature features.each do |feature| if provider.features.include?(feature) data << "**X**" else data << "" end end end ret += h(type.name.to_s + "_", 2) ret += ".. _%s: %s\n\n" % [type.name, "http://reductivelabs.com/trac/puppet/wiki/TypeReference#%s" % type.name] ret += option("Default provider", default) ret += doctable(headers, table_data) notes.each do |note| ret += note + "\n" end ret += "\n" end ret += "\n" ret end providers.header = " Puppet resource types are usually backed by multiple implementations called ``providers``, which handle variance between platforms and tools. Different providers are suitable or unsuitable on different platforms based on things like the presence of a given tool. Here are all of the provider-backed types and their different providers. Any unmentioned types do not use providers yet. " diff --git a/lib/puppet/util/command_line.rb b/lib/puppet/util/command_line.rb index f231ee7f8..5fe07b28d 100644 --- a/lib/puppet/util/command_line.rb +++ b/lib/puppet/util/command_line.rb @@ -1,14 +1,47 @@ module Puppet module Util module CommandLine - def self.shift_subcommand_from_argv( argv = ARGV, stdin = STDIN ) - case argv.first - when nil; "apply" unless stdin.tty? # ttys get usage info - when "--help"; nil # help should give you usage, not the help for `puppet apply` - when /^-|\.pp$|\.rb$/; "apply" - else argv.shift + def self.subcommand_name(*args) + subcommand_name, args = subcommand_and_args(*args) + return subcommand_name + end + + def self.args(*args) + subcommand_name, args = subcommand_and_args(*args) + return args + end + + LegacyName = Hash.new{|h,k| k}.update({ + 'agent' => 'puppetd', + 'cert' => 'puppetca', + 'doc' => 'puppetdoc', + 'filebucket' => 'filebucket', + 'apply' => 'puppet', + 'describe' => 'pi', + 'queue' => 'puppetqd', + 'resource' => 'ralsh', + 'kick' => 'puppetrun', + 'master' => 'puppetmasterd', + }) + + def self.legacy_executable_name(*args) + LegacyName[ subcommand_name(*args) ] + end + + def self.subcommand_and_args( zero = $0, argv = ARGV, stdin = STDIN ) + zero = zero.gsub(/.*#{File::SEPARATOR}/,'').sub(/\.rb$/, '') + + if zero == 'puppet' + case argv.first + when nil; [ stdin.tty? ? nil : "apply", argv] # ttys get usage info + when "--help"; [nil, argv] # help should give you usage, not the help for `puppet apply` + when /^-|\.pp$|\.rb$/; ["apply", argv] + else [ argv.first, argv[1..-1] ] + end + else + [ zero, argv ] end end end end end diff --git a/spec/unit/application/apply.rb b/spec/unit/application/apply.rb index e9a1c72a0..6fdeebfcb 100755 --- a/spec/unit/application/apply.rb +++ b/spec/unit/application/apply.rb @@ -1,385 +1,384 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/application/apply' describe "Puppet" do before :each do @apply = Puppet::Application[:apply] Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) end [:debug,:loadclasses,:verbose,:use_nodes,:detailed_exitcodes].each do |option| it "should declare handle_#{option} method" do @apply.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @apply.options.expects(:[]=).with(option, 'arg') @apply.send("handle_#{option}".to_sym, 'arg') end end it "should set the code to the provided code when :execute is used" do @apply.options.expects(:[]=).with(:code, 'arg') @apply.send("handle_execute".to_sym, 'arg') end it "should ask Puppet::Application to parse Puppet configuration file" do @apply.should_parse_config?.should be_true end describe "when applying options" do it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @apply.handle_logdest("console") end it "should put the logset options to true" do @apply.options.expects(:[]=).with(:logset,true) @apply.handle_logdest("console") end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:trap) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) require 'lib/puppet/file_bucket/dipper' Puppet::FileBucket::Dipper.stubs(:new) STDIN.stubs(:read) @apply.options.stubs(:[]).with(any_parameters) end it "should set show_diff on --noop" do Puppet.stubs(:[]=) Puppet.stubs(:[]).with(:config) Puppet.stubs(:[]).with(:noop).returns(true) Puppet.expects(:[]=).with(:show_diff, true) @apply.run_setup end it "should set console as the log destination if logdest option wasn't provided" do Puppet::Log.expects(:newdestination).with(:console) @apply.run_setup end it "should set INT trap" do @apply.expects(:trap).with(:INT) @apply.run_setup end it "should set log level to debug if --debug was passed" do @apply.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @apply.run_setup end it "should set log level to info if --verbose was passed" do @apply.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @apply.run_setup end it "should print puppet config if asked to in Puppet config" do @apply.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @apply.run_setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @apply.run_setup }.should raise_error(SystemExit) end end describe "when executing" do it "should dispatch to parseonly if parseonly is set" do @apply.stubs(:options).returns({}) Puppet.stubs(:[]).with(:parseonly).returns(true) @apply.get_command.should == :parseonly end it "should dispatch to 'apply' if it was called with 'apply'" do @apply.options[:catalog] = "foo" @apply.get_command.should == :apply end it "should dispatch to main if parseonly is not set" do @apply.stubs(:options).returns({}) Puppet.stubs(:[]).with(:parseonly).returns(false) @apply.get_command.should == :main end describe "the parseonly command" do before :each do Puppet.stubs(:[]).with(:environment) Puppet.stubs(:[]).with(:manifest).returns("site.pp") Puppet.stubs(:err) @apply.stubs(:exit) @apply.options.stubs(:[]).with(:code).returns "some code" @collection = stub_everything Puppet::Resource::TypeCollection.stubs(:new).returns(@collection) end it "should use a Puppet Resource Type Collection to parse the file" do @collection.expects(:perform_initial_import) @apply.parseonly end it "should exit with exit code 0 if no error" do @apply.expects(:exit).with(0) @apply.parseonly end it "should exit with exit code 1 if error" do @collection.stubs(:perform_initial_import).raises(Puppet::ParseError) @apply.expects(:exit).with(1) @apply.parseonly end end describe "the main command" do before :each do Puppet.stubs(:[]) Puppet.settings.stubs(:use) Puppet.stubs(:[]).with(:prerun_command).returns "" Puppet.stubs(:[]).with(:postrun_command).returns "" Puppet.stubs(:[]).with(:trace).returns(true) @apply.options.stubs(:[]) @facts = stub_everything 'facts' Puppet::Node::Facts.stubs(:find).returns(@facts) @node = stub_everything 'node' Puppet::Node.stubs(:find).returns(@node) @catalog = stub_everything 'catalog' @catalog.stubs(:to_ral).returns(@catalog) Puppet::Resource::Catalog.stubs(:find).returns(@catalog) STDIN.stubs(:read) @transaction = stub_everything 'transaction' @catalog.stubs(:apply).returns(@transaction) @apply.stubs(:exit) end it "should set the code to run from --code" do @apply.options.stubs(:[]).with(:code).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") @apply.main end it "should set the code to run from STDIN if no arguments" do - ARGV.stubs(:length).returns(0) + Puppet::Util::CommandLine.stubs(:args).returns([]) STDIN.stubs(:read).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") @apply.main end it "should set the manifest if some files are passed on command line" do - ARGV.stubs(:length).returns(1) - ARGV.stubs(:shift).returns("site.pp") + Puppet::Util::CommandLine.stubs(:args).returns(['site.pp']) Puppet.expects(:[]=).with(:manifest,"site.pp") @apply.main end it "should collect the node facts" do Puppet::Node::Facts.expects(:find).returns(@facts) @apply.main end it "should raise an error if we can't find the node" do Puppet::Node::Facts.expects(:find).returns(nil) lambda { @apply.main }.should raise_error end it "should find the node" do Puppet::Node.expects(:find).returns(@node) @apply.main end it "should raise an error if we can't find the node" do Puppet::Node.expects(:find).returns(nil) lambda { @apply.main }.should raise_error end it "should merge in our node the loaded facts" do @facts.stubs(:values).returns("values") @node.expects(:merge).with("values") @apply.main end it "should load custom classes if loadclasses" do @apply.options.stubs(:[]).with(:loadclasses).returns(true) Puppet.stubs(:[]).with(:classfile).returns("/etc/puppet/classes.txt") FileTest.stubs(:exists?).with("/etc/puppet/classes.txt").returns(true) FileTest.stubs(:readable?).with("/etc/puppet/classes.txt").returns(true) File.stubs(:read).with("/etc/puppet/classes.txt").returns("class") @node.expects(:classes=) @apply.main end it "should compile the catalog" do Puppet::Resource::Catalog.expects(:find).returns(@catalog) @apply.main end it "should transform the catalog to ral" do @catalog.expects(:to_ral).returns(@catalog) @apply.main end it "should finalize the catalog" do @catalog.expects(:finalize) @apply.main end it "should call the prerun and postrun commands on a Configurer instance" do configurer = stub 'configurer' Puppet::Configurer.expects(:new).returns configurer configurer.expects(:execute_prerun_command) configurer.expects(:execute_postrun_command) @apply.main end it "should apply the catalog" do @catalog.expects(:apply) @apply.main end describe "with detailed_exitcodes" do it "should exit with report's computed exit status" do Puppet.stubs(:[]).with(:noop).returns(false) @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @apply.expects(:exit).with(666) @apply.main end it "should always exit with 0 if option is disabled" do Puppet.stubs(:[]).with(:noop).returns(false) @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(false) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @apply.expects(:exit).with(0) @apply.main end it "should always exit with 0 if --noop" do Puppet.stubs(:[]).with(:noop).returns(true) @apply.options.stubs(:[]).with(:detailed_exitcodes).returns(true) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @apply.expects(:exit).with(0) @apply.main end end end describe "the 'apply' command" do it "should read the catalog in from disk if a file name is provided" do @apply.options[:catalog] = "/my/catalog.pson" File.expects(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new @apply.apply end it "should read the catalog in from stdin if '-' is provided" do @apply.options[:catalog] = "-" $stdin.expects(:read).returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new @apply.apply end it "should deserialize the catalog from the default format" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:default_format).returns :rot13_piglatin Puppet::Resource::Catalog.stubs(:convert_from).with(:rot13_piglatin,'something').returns Puppet::Resource::Catalog.new @apply.apply end it "should fail helpfully if deserializing fails" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something syntacically invalid" lambda { @apply.apply }.should raise_error(Puppet::Error) end it "should convert plain data structures into a catalog if deserialization does not do so" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,"something").returns({:foo => "bar"}) Puppet::Resource::Catalog.expects(:pson_create).with({:foo => "bar"}).returns(Puppet::Resource::Catalog.new) @apply.apply end it "should convert the catalog to a RAL catalog and use a Configurer instance to apply it" do @apply.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns catalog catalog.expects(:to_ral).returns "mycatalog" configurer = stub 'configurer' Puppet::Configurer.expects(:new).returns configurer configurer.expects(:run).with(:catalog => "mycatalog") @apply.apply end end end end diff --git a/spec/unit/application/cert.rb b/spec/unit/application/cert.rb index a777a8c54..8757cf36c 100644 --- a/spec/unit/application/cert.rb +++ b/spec/unit/application/cert.rb @@ -1,167 +1,167 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/application/cert' describe "PuppetCA" do before :each do @cert_app = Puppet::Application[:cert] Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) end it "should ask Puppet::Application to parse Puppet configuration file" do @cert_app.should_parse_config?.should be_true end it "should declare a main command" do @cert_app.should respond_to(:main) end Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject{ |m| m == :destroy }.each do |method| it "should declare option --#{method}" do @cert_app.should respond_to("handle_#{method}".to_sym) end end it "should set log level to info with the --verbose option" do Puppet::Log.expects(:level=).with(:info) @cert_app.handle_verbose(0) end it "should set log level to debug with the --debug option" do Puppet::Log.expects(:level=).with(:debug) @cert_app.handle_debug(0) end it "should set the fingerprint digest with the --digest option" do @cert_app.handle_digest(:digest) @cert_app.digest.should == :digest end it "should set mode to :destroy for --clean" do @cert_app.handle_clean(0) @cert_app.mode.should == :destroy end it "should set all to true for --all" do @cert_app.handle_all(0) @cert_app.all.should be_true end Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject { |m| m == :destroy }.each do |method| it "should set mode to #{method} with option --#{method}" do @cert_app.send("handle_#{method}".to_sym, nil) @cert_app.mode.should == method end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet::SSL::Host.stubs(:ca_location=) Puppet::SSL::CertificateAuthority.stubs(:new) end it "should set console as the log destination" do Puppet::Log.expects(:newdestination).with(:console) @cert_app.run_setup end it "should print puppet config if asked to in Puppet config" do @cert_app.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @cert_app.run_setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @cert_app.run_setup }.should raise_error(SystemExit) end it "should set the CA location to 'only'" do Puppet::SSL::Host.expects(:ca_location=).with(:only) @cert_app.run_setup end it "should create a new certificate authority" do Puppet::SSL::CertificateAuthority.expects(:new) @cert_app.run_setup end end describe "when running" do before :each do @cert_app.all = false @ca = stub_everything 'ca' @cert_app.ca = @ca - ARGV.stubs(:collect).returns([]) + Puppet::Util::CommandLine.stubs(:args).returns([]) end it "should delegate to the CertificateAuthority" do @ca.expects(:apply) @cert_app.main end it "should delegate with :all if option --all was given" do @cert_app.handle_all(0) @ca.expects(:apply).with { |mode,to| to[:to] == :all } @cert_app.main end it "should delegate to ca.apply with the hosts given on command line" do - ARGV.stubs(:collect).returns(["host"]) + Puppet::Util::CommandLine.stubs(:args).returns(["host"]) @ca.expects(:apply).with { |mode,to| to[:to] == ["host"]} @cert_app.main end it "should send the currently set digest" do - ARGV.stubs(:collect).returns(["host"]) + Puppet::Util::CommandLine.stubs(:args).returns(["host"]) @cert_app.handle_digest(:digest) @ca.expects(:apply).with { |mode,to| to[:digest] == :digest} @cert_app.main end it "should delegate to ca.apply with current set mode" do @cert_app.mode = "currentmode" - ARGV.stubs(:collect).returns(["host"]) + Puppet::Util::CommandLine.stubs(:args).returns(["host"]) @ca.expects(:apply).with { |mode,to| mode == "currentmode" } @cert_app.main end it "should revoke cert if mode is clean" do @cert_app.mode = :destroy - ARGV.stubs(:collect).returns(["host"]) + Puppet::Util::CommandLine.stubs(:args).returns(["host"]) @ca.expects(:apply).with { |mode,to| mode == :revoke } @ca.expects(:apply).with { |mode,to| mode == :destroy } @cert_app.main end end end diff --git a/spec/unit/application/describe.rb b/spec/unit/application/describe.rb index f9a601454..fd0c5e002 100755 --- a/spec/unit/application/describe.rb +++ b/spec/unit/application/describe.rb @@ -1,84 +1,84 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/application/describe' describe Puppet::Application[:describe] do before :each do @describe = Puppet::Application[:describe] end it "should ask Puppet::Application to not parse Puppet configuration file" do @describe.should_parse_config?.should be_false end it "should declare a main command" do @describe.should respond_to(:main) end it "should declare a preinit block" do @describe.should respond_to(:run_preinit) end [:providers,:list,:meta].each do |option| it "should declare handle_#{option} method" do @describe.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @describe.options.expects(:[]=).with("#{option}".to_sym, 'arg') @describe.send("handle_#{option}".to_sym, 'arg') end end describe "in preinit" do it "should set options[:parameteers] to true" do @describe.run_preinit @describe.options[:parameters].should be_true end end describe "when handling parameters" do it "should set options[:parameters] to false" do @describe.handle_short(nil) @describe.options[:parameters].should be_false end end describe "during setup" do - it "should collect ARGV in options[:types]" do - ARGV.stubs(:dup).returns(['1','2']) + it "should collect arguments in options[:types]" do + Puppet::Util::CommandLine.stubs(:args).returns(['1','2']) @describe.run_setup @describe.options[:types].should == ['1','2'] end end describe "when running" do before :each do @typedoc = stub 'type_doc' TypeDoc.stubs(:new).returns(@typedoc) end it "should call list_types if options list is set" do @describe.options[:list] = true @typedoc.expects(:list_types) @describe.run_command end it "should call format_type for each given types" do @describe.options[:list] = false @describe.options[:types] = ['type'] @typedoc.expects(:format_type).with('type', @describe.options) @describe.run_command end end end diff --git a/spec/unit/application/doc.rb b/spec/unit/application/doc.rb index 267a70d8b..185612873 100755 --- a/spec/unit/application/doc.rb +++ b/spec/unit/application/doc.rb @@ -1,364 +1,359 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/application/doc' describe "doc" do before :each do @doc = Puppet::Application[:doc] @doc.stubs(:puts) @doc.run_preinit Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) end it "should ask Puppet::Application to not parse Puppet configuration file" do @doc.should_parse_config?.should be_false end it "should declare a other command" do @doc.should respond_to(:other) end it "should declare a rdoc command" do @doc.should respond_to(:rdoc) end it "should declare a trac command" do @doc.should respond_to(:trac) end it "should declare a fallback for unknown options" do @doc.should respond_to(:handle_unknown) end it "should declare a preinit block" do @doc.should respond_to(:run_preinit) end describe "in preinit" do it "should set references to []" do @doc.run_preinit @doc.options[:references].should == [] end it "should init mode to text" do @doc.run_preinit @doc.options[:mode].should == :text end it "should init format to to_rest" do @doc.run_preinit @doc.options[:format].should == :to_rest end end describe "when handling options" do [:all, :outputdir, :verbose, :debug].each do |option| it "should declare handle_#{option} method" do @doc.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @doc.options.expects(:[]=).with(option, 'arg') @doc.send("handle_#{option}".to_sym, 'arg') end end it "should store the format if valid" do Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(true) @doc.options.expects(:[]=).with(:format, 'to_format') @doc.handle_format('format') end it "should raise an error if the format is not valid" do Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(false) lambda { @doc.handle_format('format') } end it "should store the mode if valid" do Puppet::Util::Reference.stubs(:modes).returns(stub('mode', :include? => true)) @doc.options.expects(:[]=).with(:mode, :mode) @doc.handle_mode('mode') end it "should store the mode if :rdoc" do Puppet::Util::Reference.modes.stubs(:include?).with('rdoc').returns(false) @doc.options.expects(:[]=).with(:mode, :rdoc) @doc.handle_mode('rdoc') end it "should raise an error if the mode is not valid" do Puppet::Util::Reference.modes.stubs(:include?).with('unknown').returns(false) lambda { @doc.handle_mode('unknown') } end it "should list all references on list and exit" do reference = stubs 'reference' ref = stubs 'ref' Puppet::Util::Reference.stubs(:references).returns([reference]) Puppet::Util::Reference.expects(:reference).with(reference).returns(ref) ref.expects(:doc) @doc.expects(:exit) @doc.handle_list(nil) end it "should add reference to references list with --reference" do @doc.options[:references] = [:ref1] @doc.handle_reference('ref2') @doc.options[:references].should == [:ref1,:ref2] end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) - ARGV.stubs(:size).returns(0) + Puppet::Util::CommandLine.stubs(:args).returns([]) end it "should default to rdoc mode if there are command line arguments" do - ARGV.stubs(:size).returns(1) + Puppet::Util::CommandLine.stubs(:args).returns(["1"]) @doc.stubs(:setup_rdoc) @doc.options.expects(:[]=).with(:mode,:rdoc) @doc.run_setup end it "should call setup_rdoc in rdoc mode" do @doc.options.stubs(:[]).with(:mode).returns(:rdoc) @doc.expects(:setup_rdoc) @doc.run_setup end it "should call setup_reference if not rdoc" do @doc.options.stubs(:[]).with(:mode).returns(:test) @doc.expects(:setup_reference) @doc.run_setup end describe "in non-rdoc mode" do it "should get all non-dynamic reference if --all" do @doc.options.stubs(:[]).with(:all).returns(true) @doc.options.stubs(:[]).with(:references).returns([]) static = stub 'static', :dynamic? => false dynamic = stub 'dynamic', :dynamic? => true Reference.stubs(:reference).with(:static).returns(static) Reference.stubs(:reference).with(:dynamic).returns(dynamic) Reference.stubs(:references).returns([:static,:dynamic]) @doc.options.stubs(:[]=).with(:references, [:static]) @doc.setup_reference end it "should default to :type if no references" do @doc.options.stubs(:[]).with(:all).returns(false) array = stub 'array', :empty? => true @doc.options.stubs(:[]).with(:references).returns(array) array.expects(:<<).with(:type) @doc.setup_reference end end describe "in rdoc mode" do before :each do @doc.options.stubs(:[]).returns(false) Puppet.stubs(:[]=).with(:name, "puppetmasterd") Puppet.stubs(:parse_config) Puppet::Util::Log.stubs(:level=) Puppet::Util::Log.stubs(:newdestination) end describe "when there are unknown args" do it "should expand --modulepath if any" do @doc.unknown_args = [ { :opt => "--modulepath", :arg => "path" } ] Puppet.settings.stubs(:handlearg) File.expects(:expand_path).with("path") @doc.setup_rdoc end it "should expand --manifestdir if any" do @doc.unknown_args = [ { :opt => "--manifestdir", :arg => "path" } ] Puppet.settings.stubs(:handlearg) File.expects(:expand_path).with("path") @doc.setup_rdoc end it "should give them to Puppet.settings" do @doc.unknown_args = [ { :opt => :option, :arg => :argument } ] Puppet.settings.expects(:handlearg).with(:option,:argument) @doc.setup_rdoc end end it "should pretend to be puppetmasterd" do Puppet.expects(:[]=).with(:name, "puppetmasterd") @doc.setup_rdoc end it "should parse puppet configuration" do Puppet.expects(:parse_config) @doc.setup_rdoc end it "should set log level to debug if --debug" do @doc.options.stubs(:[]).with(:debug).returns(true) Puppet::Util::Log.expects(:level=).with(:debug) @doc.setup_rdoc end it "should set log level to info if --verbose" do @doc.options.stubs(:[]).with(:verbose).returns(true) Puppet::Util::Log.expects(:level=).with(:info) @doc.setup_rdoc end it "should set log destination to console if --verbose" do @doc.options.stubs(:[]).with(:verbose).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @doc.setup_rdoc end it "should set log destination to console if --debug" do @doc.options.stubs(:[]).with(:debug).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @doc.setup_rdoc end end end describe "when running" do before :each do end describe "in trac mode" do it "should call trac for each reference" do ref = stub 'ref' Puppet::Util::Reference.stubs(:reference).with(:ref).returns(ref) @doc.options.stubs(:[]).with(:references).returns([:ref]) @doc.options.stubs(:[]).with(:mode).returns(:trac) ref.expects(:trac) @doc.trac end end describe "in rdoc mode" do before :each do @doc.manifest = false Puppet.stubs(:info) Puppet.stubs(:[]).with(:trace).returns(false) @env = stub 'env' Puppet::Node::Environment.stubs(:new).returns(@env) @env.stubs(:modulepath).returns(['modules']) @env.stubs(:[]).with(:manifest).returns('manifests/site.pp') Puppet.stubs(:[]).with(:modulepath).returns('modules') Puppet.stubs(:[]).with(:manifestdir).returns('manifests') @doc.options.stubs(:[]).with(:all).returns(false) @doc.options.stubs(:[]).with(:outputdir).returns('doc') Puppet.settings.stubs(:[]=).with(:document_all, false) Puppet.settings.stubs(:setdefaults) Puppet::Util::RDoc.stubs(:rdoc) @doc.stubs(:exit) File.stubs(:expand_path).with('modules').returns('modules') File.stubs(:expand_path).with('manifests').returns('manifests') - @old = ARGV.dup - ARGV.clear - end - - after :each do - ARGV << @old + Puppet::Util::CommandLine.stubs(:args).returns([]) end it "should set document_all on --all" do @doc.options.expects(:[]).with(:all).returns(true) Puppet.settings.expects(:[]=).with(:document_all, true) @doc.rdoc end it "should call Puppet::Util::RDoc.rdoc in full mode" do Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests']) @doc.rdoc end it "should call Puppet::Util::RDoc.rdoc in full mode with outputdir set to doc if no --outputdir" do @doc.options.expects(:[]).with(:outputdir).returns(false) Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests']) @doc.rdoc end it "should call Puppet::Util::RDoc.manifestdoc in manifest mode" do @doc.manifest = true Puppet::Util::RDoc.expects(:manifestdoc) @doc.rdoc end it "should get modulepath and manifestdir values from the environment" do @env.expects(:modulepath).returns(['envmodules1','envmodules2']) @env.expects(:[]).with(:manifest).returns('envmanifests/site.pp') Puppet::Util::RDoc.expects(:rdoc).with('doc', ['envmodules1','envmodules2','envmanifests']) @doc.rdoc end end describe "in the other modes" do it "should get reference in given format" do reference = stub 'reference' @doc.options.stubs(:[]).with(:mode).returns(:none) @doc.options.stubs(:[]).with(:references).returns([:ref]) Puppet::Util::Reference.expects(:reference).with(:ref).returns(reference) @doc.options.stubs(:[]).with(:format).returns(:format) @doc.stubs(:exit) reference.expects(:send).with { |format,contents| format == :format }.returns('doc') @doc.other end end end end diff --git a/spec/unit/application/filebucket.rb b/spec/unit/application/filebucket.rb index f78c0b7be..37cc93997 100644 --- a/spec/unit/application/filebucket.rb +++ b/spec/unit/application/filebucket.rb @@ -1,220 +1,220 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/application/filebucket' -describe "Filebucket" do +describe Puppet::Application[:filebucket] do before :each do @filebucket = Puppet::Application[:filebucket] end it "should ask Puppet::Application to not parse Puppet configuration file" do @filebucket.should_parse_config?.should be_false end it "should declare a get command" do @filebucket.should respond_to(:get) end it "should declare a backup command" do @filebucket.should respond_to(:backup) end it "should declare a restore command" do @filebucket.should respond_to(:restore) end [:bucket, :debug, :local, :remote, :verbose].each do |option| it "should declare handle_#{option} method" do @filebucket.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @filebucket.options.expects(:[]=).with("#{option}".to_sym, 'arg') @filebucket.send("handle_#{option}".to_sym, 'arg') end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:settraps) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) Puppet::FileBucket::Dipper.stubs(:new) @filebucket.options.stubs(:[]).with(any_parameters) end it "should set console as the log destination" do Puppet::Log.expects(:newdestination).with(:console) @filebucket.run_setup end it "should trap INT" do @filebucket.expects(:trap).with(:INT) @filebucket.run_setup end it "should set log level to debug if --debug was passed" do @filebucket.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @filebucket.run_setup end it "should set log level to info if --verbose was passed" do @filebucket.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @filebucket.run_setup end it "should Parse puppet config" do Puppet.expects(:parse_config) @filebucket.run_setup end it "should print puppet config if asked to in Puppet config" do @filebucket.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @filebucket.run_setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @filebucket.run_setup }.should raise_error(SystemExit) end describe "with local bucket" do before :each do @filebucket.options.stubs(:[]).with(:local).returns(true) end it "should create a client with the default bucket if none passed" do Puppet.stubs(:[]).with(:bucketdir).returns("path") Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Path] == "path" } @filebucket.run_setup end it "should create a local Dipper with the given bucket" do @filebucket.options.stubs(:[]).with(:bucket).returns("path") Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Path] == "path" } @filebucket.run_setup end end describe "with remote bucket" do it "should create a remote Client to the configured server" do Puppet.stubs(:[]).with(:server).returns("puppet.reductivelabs.com") Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Server] == "puppet.reductivelabs.com" } @filebucket.run_setup end end end describe "when running" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:settraps) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) Puppet::FileBucket::Dipper.stubs(:new) @filebucket.options.stubs(:[]).with(any_parameters) @client = stub 'client' Puppet::FileBucket::Dipper.stubs(:new).returns(@client) @filebucket.run_setup end it "should use the first non-option parameter as the dispatch" do - ARGV.stubs(:shift).returns(:get) + Puppet::Util::CommandLine.stubs(:args).returns([:get]) @filebucket.get_command.should == :get end describe "the command get" do before :each do @filebucket.stubs(:print) end it "should call the client getfile method" do @client.expects(:getfile) @filebucket.get end it "should call the client getfile method with the given md5" do md5="DEADBEEF" - ARGV.stubs(:shift).returns(md5) + @filebucket.stubs(:args).returns([md5]) @client.expects(:getfile).with(md5) @filebucket.get end it "should print the file content" do @client.stubs(:getfile).returns("content") @filebucket.expects(:print).returns("content") @filebucket.get end end describe "the command backup" do it "should call the client backup method for each given parameter" do @filebucket.stubs(:puts) FileTest.stubs(:exists?).returns(true) FileTest.stubs(:readable?).returns(true) - ARGV.stubs(:each).multiple_yields("file1","file2") + @filebucket.stubs(:args).returns(["file1", "file2"]) @client.expects(:backup).with("file1") @client.expects(:backup).with("file2") @filebucket.backup end end describe "the command restore" do it "should call the client getfile method with the given md5" do md5="DEADBEEF" file="testfile" - ARGV.stubs(:shift).returns(file,md5) + @filebucket.stubs(:args).returns([file, md5]) @client.expects(:restore).with(file,md5) @filebucket.restore end end end end diff --git a/spec/unit/application/resource.rb b/spec/unit/application/resource.rb index 6224ee083..90fd3cc66 100755 --- a/spec/unit/application/resource.rb +++ b/spec/unit/application/resource.rb @@ -1,255 +1,232 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/application/resource' describe "resource" do before :each do @resource = Puppet::Application[:resource] Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) Puppet::Resource.indirection.stubs(:terminus_class=) end it "should ask Puppet::Application to not parse Puppet configuration file" do @resource.should_parse_config?.should be_false end it "should declare a main command" do @resource.should respond_to(:main) end it "should declare a host option" do @resource.should respond_to(:handle_host) end it "should declare a types option" do @resource.should respond_to(:handle_types) end it "should declare a param option" do @resource.should respond_to(:handle_param) end it "should declare a preinit block" do @resource.should respond_to(:run_preinit) end describe "in preinit" do it "should set hosts to nil" do @resource.run_preinit @resource.host.should be_nil end it "should init extra_params to empty array" do @resource.run_preinit @resource.extra_params.should == [] end it "should load Facter facts" do Facter.expects(:loadfacts).once @resource.run_preinit end end describe "when handling options" do [:debug, :verbose, :edit].each do |option| it "should declare handle_#{option} method" do @resource.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @resource.options.expects(:[]=).with(option, 'arg') @resource.send("handle_#{option}".to_sym, 'arg') end end it "should set options[:host] to given host" do @resource.handle_host(:whatever) @resource.host.should == :whatever end it "should load an display all types with types option" do type1 = stub_everything 'type1', :name => :type1 type2 = stub_everything 'type2', :name => :type2 Puppet::Type.stubs(:loadall) Puppet::Type.stubs(:eachtype).multiple_yields(type1,type2) @resource.stubs(:exit) @resource.expects(:puts).with(['type1','type2']) @resource.handle_types(nil) end it "should add param to extra_params list" do @resource.extra_params = [ :param1 ] @resource.handle_param("whatever") @resource.extra_params.should == [ :param1, :whatever ] end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) end it "should set console as the log destination" do Puppet::Log.expects(:newdestination).with(:console) @resource.run_setup end it "should set log level to debug if --debug was passed" do @resource.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @resource.run_setup end it "should set log level to info if --verbose was passed" do @resource.options.stubs(:[]).with(:debug).returns(false) @resource.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @resource.run_setup end it "should Parse puppet config" do Puppet.expects(:parse_config) @resource.run_setup end end describe "when running" do - def set_args(args) - (ARGV.clear << args).flatten! - end - - def push_args(*args) - @args_stack ||= [] - @args_stack << ARGV.dup - set_args(args) - end - - def pop_args - set_args(@args_stack.pop) - end - before :each do @type = stub_everything 'type', :properties => [] - push_args('type') + Puppet::Util::CommandLine.stubs(:args).returns(['type']) Puppet::Type.stubs(:type).returns(@type) end - after :each do - pop_args - end - it "should raise an error if no type is given" do - push_args + Puppet::Util::CommandLine.stubs(:args).returns([]) lambda { @resource.main }.should raise_error - pop_args end it "should raise an error when editing a remote host" do @resource.options.stubs(:[]).with(:edit).returns(true) @resource.host = 'host' lambda { @resource.main }.should raise_error end it "should raise an error if the type is not found" do Puppet::Type.stubs(:type).returns(nil) lambda { @resource.main }.should raise_error end describe "with a host" do before :each do @resource.stubs(:puts) @resource.host = 'host' Puppet::Resource.stubs(:find ).never Puppet::Resource.stubs(:search).never Puppet::Resource.stubs(:save ).never end it "should search for resources" do Puppet::Resource.expects(:search).with('https://host:8139/production/resources/type/', {}).returns([]) @resource.main end it "should describe the given resource" do - push_args('type','name') + Puppet::Util::CommandLine.stubs(:args).returns(['type', 'name']) x = stub_everything 'resource' Puppet::Resource.expects(:find).with('https://host:8139/production/resources/type/name').returns(x) @resource.main - pop_args end it "should add given parameters to the object" do - push_args('type','name','param=temp') + Puppet::Util::CommandLine.stubs(:args).returns(['type','name','param=temp']) res = stub "resource" res.expects(:save).with('https://host:8139/production/resources/type/name').returns(res) res.expects(:collect) res.expects(:to_manifest) Puppet::Resource.expects(:new).with('type', 'name', {'param' => 'temp'}).returns(res) @resource.main - pop_args end end describe "without a host" do before :each do @resource.stubs(:puts) @resource.host = nil Puppet::Resource.stubs(:find ).never Puppet::Resource.stubs(:search).never Puppet::Resource.stubs(:save ).never end it "should search for resources" do Puppet::Resource.expects(:search).with('type/', {}).returns([]) @resource.main end it "should describe the given resource" do - push_args('type','name') + Puppet::Util::CommandLine.stubs(:args).returns(['type','name']) x = stub_everything 'resource' Puppet::Resource.expects(:find).with('type/name').returns(x) @resource.main - pop_args end it "should add given parameters to the object" do - push_args('type','name','param=temp') + Puppet::Util::CommandLine.stubs(:args).returns(['type','name','param=temp']) res = stub "resource" res.expects(:save).with('type/name').returns(res) res.expects(:collect) res.expects(:to_manifest) Puppet::Resource.expects(:new).with('type', 'name', {'param' => 'temp'}).returns(res) @resource.main - pop_args end end end end diff --git a/spec/unit/util/command_line.rb b/spec/unit/util/command_line.rb index 265535b78..a07438f9e 100644 --- a/spec/unit/util/command_line.rb +++ b/spec/unit/util/command_line.rb @@ -1,78 +1,96 @@ #!/usr/bin/env ruby Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } require 'puppet/util/command_line' describe Puppet::Util::CommandLine do before do @tty = stub("tty", :tty? => true ) @pipe = stub("pipe", :tty? => false) end it "should pull off the first argument if it looks like a subcommand" do - args = %w( client --help whatever.pp ) - command = Puppet::Util::CommandLine.shift_subcommand_from_argv( args, @tty ) + command, args = Puppet::Util::CommandLine.subcommand_and_args("puppet", %w( client --help whatever.pp ), @tty ) command.should == "client" args.should == %w( --help whatever.pp ) end it "should use 'apply' if the first argument looks like a .pp file" do - args = %w( whatever.pp ) - command = Puppet::Util::CommandLine.shift_subcommand_from_argv( args, @tty ) + command, args = Puppet::Util::CommandLine.subcommand_and_args("puppet", %w( whatever.pp ), @tty ) command.should == "apply" args.should == %w( whatever.pp ) end it "should use 'apply' if the first argument looks like a .rb file" do - args = %w( whatever.rb ) - command = Puppet::Util::CommandLine.shift_subcommand_from_argv( args, @tty ) + command, args = Puppet::Util::CommandLine.subcommand_and_args("puppet", %w( whatever.rb ), @tty ) command.should == "apply" args.should == %w( whatever.rb ) end it "should use 'apply' if the first argument looks like a flag" do - args = %w( --debug ) - command = Puppet::Util::CommandLine.shift_subcommand_from_argv( args, @tty ) + command, args = Puppet::Util::CommandLine.subcommand_and_args("puppet", %w( --debug ), @tty ) command.should == "apply" args.should == %w( --debug ) end it "should use 'apply' if the first argument is -" do - args = %w( - ) - command = Puppet::Util::CommandLine.shift_subcommand_from_argv( args, @tty ) + command, args = Puppet::Util::CommandLine.subcommand_and_args("puppet", %w( - ), @tty ) command.should == "apply" args.should == %w( - ) end it "should return nil if the first argument is --help" do - args = %w( --help ) - command = Puppet::Util::CommandLine.shift_subcommand_from_argv( args, @tty ) + command, args = Puppet::Util::CommandLine.subcommand_and_args("puppet", %w( --help ), @tty ) command.should == nil end it "should return nil if there are no arguments on a tty" do - args = [] - command = Puppet::Util::CommandLine.shift_subcommand_from_argv( args, @tty ) + command, args = Puppet::Util::CommandLine.subcommand_and_args("puppet", [], @tty ) command.should == nil args.should == [] end it "should use 'apply' if there are no arguments on a pipe" do - args = [] - command = Puppet::Util::CommandLine.shift_subcommand_from_argv( args, @pipe ) + command, args = Puppet::Util::CommandLine.subcommand_and_args("puppet", [], @pipe ) command.should == "apply" args.should == [] end + it "should provide a convenience method that only returns the subcommand" do + Puppet::Util::CommandLine.expects(:subcommand_and_args).with("puppet", [], @pipe ).returns(["command", ['args']]) + command = Puppet::Util::CommandLine.subcommand_name( "puppet", [], @pipe ) + command.should == "command" + end + + it "should provide a convenience method that only returns the args" do + Puppet::Util::CommandLine.expects(:subcommand_and_args).with("puppet", [], @pipe ).returns(["command", ['args']]) + args = Puppet::Util::CommandLine.args( "puppet", [], @pipe ) + args.should == ['args'] + end + + it "should return the executable name if it is not puppet" do + command, args = Puppet::Util::CommandLine.subcommand_and_args("puppetmasterd", [], @tty ) + + command.should == "puppetmasterd" + end + + it "should translate subcommand names into their legacy equivalent" do + Puppet::Util::CommandLine.legacy_executable_name("puppet", ["master"], @tty).should == "puppetmasterd" + end + + it "should leave legacy command names alone" do + Puppet::Util::CommandLine.legacy_executable_name("puppetmasterd", [], @tty).should == "puppetmasterd" + end + end diff --git a/test/other/puppet.rb b/test/other/puppet.rb index 1839333e5..2d55c2fba 100755 --- a/test/other/puppet.rb +++ b/test/other/puppet.rb @@ -1,101 +1,94 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../lib/puppettest' require 'puppet' require 'puppettest' # Test the different features of the main puppet module class TestPuppetModule < Test::Unit::TestCase include PuppetTest include SignalObserver def mkfakeclient Class.new(Puppet::Network::Client) do def initialize end def runnow Puppet.info "fake client has run" end end end def mktestclass Class.new do def initialize(file) @file = file end def started? FileTest.exists?(@file) end def start File.open(@file, "w") do |f| f.puts "" end end def shutdown File.unlink(@file) end end end def test_path oldpath = ENV["PATH"] cleanup do ENV["PATH"] = oldpath end newpath = oldpath + ":" + "/something/else" assert_nothing_raised do Puppet[:path] = newpath end assert_equal(newpath, ENV["PATH"]) end def test_libdir oldlibs = $:.dup cleanup do $:.each do |dir| unless oldlibs.include?(dir) $:.delete(dir) end end end one = tempfile() two = tempfile() Dir.mkdir(one) Dir.mkdir(two) # Make sure setting the libdir gets the dir added to $: assert_nothing_raised do Puppet[:libdir] = one end assert($:.include?(one), "libdir was not added") # Now change it, make sure it gets added and the old one gets # removed assert_nothing_raised do Puppet[:libdir] = two end assert($:.include?(two), "libdir was not added") assert(! $:.include?(one), "old libdir was not removed") end def test_name - # Make sure it defaults to $0 without the rb - should = $0.gsub(/.+#{File::SEPARATOR}/,'').sub(/\.rb$/, '') - - assert_equal(should, Puppet[:name], "default name was not right") - - assert_nothing_raised("Could not reset name") do - Puppet[:name] = "puppetca" - end + Puppet[:name] = "puppetca" assert_equal("puppetca", Puppet[:name], "name reset did not take") end end