diff --git a/bin/puppet b/bin/puppet index a326ba148..9b7c7d64d 100755 --- a/bin/puppet +++ b/bin/puppet @@ -1,89 +1,73 @@ #!/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' -subcommand_name = Puppet::Util::CommandLine.subcommand_name - -if subcommand_name.nil? - puts usage, available -elsif builtins.include?(subcommand_name) #subcommand - require File.join(appdir, subcommand_name) - Puppet::Application[subcommand_name].run -else - abort "Error: Unknown command #{subcommand_name}.\n#{usage}\n#{available}" -end +Puppet::Util::CommandLine.new.execute diff --git a/lib/puppet/util/command_line.rb b/lib/puppet/util/command_line.rb index 5fe07b28d..6e67fd706 100644 --- a/lib/puppet/util/command_line.rb +++ b/lib/puppet/util/command_line.rb @@ -1,47 +1,89 @@ module Puppet module Util - module CommandLine + class CommandLine 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 + + def self.appdir + File.join('puppet', 'application') + end + + def self.available_subcommands + appdir = File.join('puppet', 'application') + absolute_appdir = $:.collect { |x| File.join(x,'puppet','application') }.detect{ |x| File.directory?(x) } + Dir[File.join(absolute_appdir, '*.rb')].map{|fn| File.basename(fn, '.rb')} + end + + def self.usage_message + usage = "Usage: puppet command " + available = "Available commands are: #{available_subcommands.sort.join(', ')}" + [usage, available].join("\n") + end + + def initialize( zero = $0, argv = ARGV, stdin = STDIN ) + @zero = zero + @argv = argv.dup + @stdin = stdin + + @subcommand_name, @args = self.class.subcommand_and_args( @zero, @argv, @stdin ) + end + + attr :subcommand_name + attr :args + + def execute + if subcommand_name.nil? + puts self.class.usage_message + elsif self.class.available_subcommands.include?(subcommand_name) #subcommand + require File.join(self.class.appdir, subcommand_name) + Puppet::Application[subcommand_name].run + else + abort "Error: Unknown command #{subcommand_name}.\n#{usage_message}" + end + end + + def legacy_executable_name + LegacyName[ subcommand_name ] + end end end end diff --git a/spec/unit/util/command_line.rb b/spec/unit/util/command_line.rb index a07438f9e..e20f51a99 100644 --- a/spec/unit/util/command_line.rb +++ b/spec/unit/util/command_line.rb @@ -1,96 +1,113 @@ #!/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 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 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 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 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 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 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 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 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 + describe "when instantiated" do + it "should provide the results of subcommand and args" do + Puppet::Util::CommandLine.expects(:subcommand_and_args).with("puppet", [], @pipe).returns(["command", ['args']]) + commandline = Puppet::Util::CommandLine.new("puppet", [], @pipe) + + commandline.subcommand_name.should == 'command' + commandline.args.should == ['args'] + end + + it "should provide the legacy executable name" do + Puppet::Util::CommandLine.expects(:subcommand_and_args).with("puppet", ['master'], @pipe).returns(["master", []]) + commandline = Puppet::Util::CommandLine.new("puppet", ['master'], @pipe) + + commandline.legacy_executable_name.should == 'puppetmasterd' + end + end + end