diff --git a/lib/puppet/util/command_line.rb b/lib/puppet/util/command_line.rb index 3562a3dc0..4aff0a8cb 100644 --- a/lib/puppet/util/command_line.rb +++ b/lib/puppet/util/command_line.rb @@ -1,95 +1,99 @@ module Puppet module Util class CommandLine 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 initialize( zero = $0, argv = ARGV, stdin = STDIN ) @zero = zero @argv = argv.dup @stdin = stdin @subcommand_name, @args = subcommand_and_args( @zero, @argv, @stdin ) end attr :subcommand_name attr :args def appdir File.join('puppet', 'application') end def available_subcommands - absolute_appdir = $LOAD_PATH.collect { |x| File.join(x,'puppet','application') }.detect{ |x| File.directory?(x) } - Dir[File.join(absolute_appdir, '*.rb')].map{|fn| File.basename(fn, '.rb')} + absolute_appdirs = $LOAD_PATH.collect do |x| + File.join(x,'puppet','application') + end.select{ |x| File.directory?(x) } + absolute_appdirs.inject([]) do |commands, dir| + commands + Dir[File.join(dir, '*.rb')].map{|fn| File.basename(fn, '.rb')} + end.uniq end def usage_message usage = "Usage: puppet command " available = "Available commands are: #{available_subcommands.sort.join(', ')}" [usage, available].join("\n") end def require_application(application) require File.join(appdir, application) end def execute if subcommand_name.nil? puts usage_message elsif available_subcommands.include?(subcommand_name) #subcommand require_application subcommand_name Puppet::Application.find(subcommand_name).new(self).run else abort "Error: Unknown command #{subcommand_name}.\n#{usage_message}" unless execute_external_subcommand end end def execute_external_subcommand external_command = "puppet-#{subcommand_name}" require 'puppet/util' path_to_subcommand = Puppet::Util.which( external_command ) return false unless path_to_subcommand system( path_to_subcommand, *args ) true end def legacy_executable_name LegacyName[ subcommand_name ] end private def subcommand_and_args( zero, argv, stdin ) zero = File.basename(zero, '.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/util/command_line_spec.rb b/spec/unit/util/command_line_spec.rb index 7ba965249..a648eb4a1 100644 --- a/spec/unit/util/command_line_spec.rb +++ b/spec/unit/util/command_line_spec.rb @@ -1,108 +1,132 @@ #!/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 + include PuppetSpec::Files 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_line = Puppet::Util::CommandLine.new("puppet", %w{ client --help whatever.pp }, @tty ) command_line.subcommand_name.should == "client" command_line.args.should == %w{ --help whatever.pp } end it "should use 'apply' if the first argument looks like a .pp file" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ whatever.pp }, @tty ) command_line.subcommand_name.should == "apply" command_line.args.should == %w{ whatever.pp } end it "should use 'apply' if the first argument looks like a .rb file" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ whatever.rb }, @tty ) command_line.subcommand_name.should == "apply" command_line.args.should == %w{ whatever.rb } end it "should use 'apply' if the first argument looks like a flag" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ --debug }, @tty ) command_line.subcommand_name.should == "apply" command_line.args.should == %w{ --debug } end it "should use 'apply' if the first argument is -" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ - }, @tty ) command_line.subcommand_name.should == "apply" command_line.args.should == %w{ - } end it "should return nil if the first argument is --help" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ --help }, @tty ) command_line.subcommand_name.should == nil end it "should return nil if there are no arguments on a tty" do command_line = Puppet::Util::CommandLine.new("puppet", [], @tty ) command_line.subcommand_name.should == nil command_line.args.should == [] end it "should use 'apply' if there are no arguments on a pipe" do command_line = Puppet::Util::CommandLine.new("puppet", [], @pipe ) command_line.subcommand_name.should == "apply" command_line.args.should == [] end it "should return the executable name if it is not puppet" do command_line = Puppet::Util::CommandLine.new("puppetmasterd", [], @tty ) command_line.subcommand_name.should == "puppetmasterd" end it "should translate subcommand names into their legacy equivalent" do command_line = Puppet::Util::CommandLine.new("puppet", ["master"], @tty) command_line.legacy_executable_name.should == "puppetmasterd" end it "should leave legacy command names alone" do command_line = Puppet::Util::CommandLine.new("puppetmasterd", [], @tty) command_line.legacy_executable_name.should == "puppetmasterd" end describe "when the subcommand is not implemented" do it "should find and invoke an executable with a hyphenated name" do commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'], @tty) Puppet::Util.expects(:which).with('puppet-whatever').returns('/dev/null/puppet-whatever') commandline.expects(:system).with('/dev/null/puppet-whatever', 'argument') commandline.execute end describe "and an external implementation cannot be found" do it "should abort and show the usage message" do commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'], @tty) Puppet::Util.expects(:which).with('puppet-whatever').returns(nil) commandline.expects(:system).never commandline.expects(:usage_message).returns("the usage message") commandline.expects(:abort).with{|x| x =~ /the usage message/}.raises("stubbed abort") lambda{ commandline.execute }.should raise_error('stubbed abort') end end end + describe 'when loading commands' do + before do + @core_apps = ["describe", "filebucket", "kick", "queue", "resource", "agent", "cert", "apply", "doc", "master"] + @command_line = Puppet::Util::CommandLine.new("foo", %w{ client --help w hatever.pp }, @tty ) + end + it 'should be able to find all existing commands' do + @core_apps.each do |command| + @command_line.available_subcommands.should include command + end + end + describe 'when multiple paths have applications' do + before do + @dir=tmpdir('command_line_plugin_test') + @appdir="#{@dir}/puppet/application" + FileUtils.mkdir_p(@appdir) + FileUtils.touch("#{@appdir}/foo.rb") + $LOAD_PATH.unshift(@dir) + end + it 'should be able to find commands from both paths' do + @command_line.available_subcommands.should == ['foo'] + @core_apps + end + end + end end