You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ap...@apache.org on 2015/05/02 01:52:26 UTC

hbase git commit: HBASE-11658 Piped commands to hbase shell should return non-zero if shell command failed (Sean Busbey)

Repository: hbase
Updated Branches:
  refs/heads/0.98 97efb9215 -> 2f09b39f2


HBASE-11658 Piped commands to hbase shell should return non-zero if shell command failed (Sean Busbey)


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/2f09b39f
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/2f09b39f
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/2f09b39f

Branch: refs/heads/0.98
Commit: 2f09b39f27707ed05bac35bc3806e9cdb1210cf3
Parents: 97efb92
Author: Jonathan M Hsieh <jm...@apache.org>
Authored: Fri Aug 8 13:54:53 2014 -0700
Committer: Andrew Purtell <ap...@apache.org>
Committed: Fri May 1 16:48:15 2015 -0700

----------------------------------------------------------------------
 bin/hirb.rb                                     | 99 +++++++++++++++-----
 hbase-shell/src/main/ruby/shell.rb              |  5 +-
 hbase-shell/src/main/ruby/shell/commands.rb     | 18 ++--
 .../src/test/ruby/shell/noninteractive_test.rb  | 42 +++++++++
 hbase-shell/src/test/ruby/shell/shell_test.rb   | 10 ++
 5 files changed, 142 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/2f09b39f/bin/hirb.rb
----------------------------------------------------------------------
diff --git a/bin/hirb.rb b/bin/hirb.rb
index 0503c29..94b5cdb 100644
--- a/bin/hirb.rb
+++ b/bin/hirb.rb
@@ -19,6 +19,12 @@
 # File passed to org.jruby.Main by bin/hbase.  Pollutes jirb with hbase imports
 # and hbase  commands and then loads jirb.  Outputs a banner that tells user
 # where to find help, shell version, and loads up a custom hirb.
+#
+# In noninteractive mode, runs commands from stdin until completion or an error.
+# On success will exit with status 0, on any problem will exit non-zero. Callers
+# should only rely on "not equal to 0", because the current error exit code of 1
+# will likely be updated to diffentiate e.g. invalid commands, incorrect args,
+# permissions, etc.
 
 # TODO: Interrupt a table creation or a connection to a bad master.  Currently
 # has to time out.  Below we've set down the retries for rpc and hbase but
@@ -54,12 +60,16 @@ Usage: shell [OPTIONS] [SCRIPTFILE [ARGUMENTS]]
 
  -d | --debug                   Set DEBUG log levels.
  -h | --help                    This help.
+ -n | --noninteractive          Do not run within an IRB session
+                                and exit with non-zero status on
+                                first error.
 HERE
 found = []
 format = 'console'
 script2run = nil
 log_level = org.apache.log4j.Level::ERROR
 @shell_debug = false
+interactive = true
 for arg in ARGV
   if arg =~ /^--format=(.+)/i
     format = $1
@@ -80,6 +90,9 @@ for arg in ARGV
     @shell_debug = true
     found.push(arg)
     puts "Setting DEBUG log level..."
+  elsif arg == '-n' || arg == '--noninteractive'
+    interactive = false
+    found.push(arg)
   else
     # Presume it a script. Save it off for running later below
     # after we've set up some environment.
@@ -118,10 +131,11 @@ require 'shell/formatter'
 @hbase = Hbase::Hbase.new
 
 # Setup console
-@shell = Shell::Shell.new(@hbase, @formatter)
+@shell = Shell::Shell.new(@hbase, @formatter, interactive)
 @shell.debug = @shell_debug
 
 # Add commands to this namespace
+# TODO avoid polluting main namespace by using a binding
 @shell.export_commands(self)
 
 # Add help command
@@ -158,38 +172,75 @@ end
 # Include hbase constants
 include HBaseConstants
 
-# If script2run, try running it.  Will go on to run the shell unless
+# If script2run, try running it.  If we're in interactive mode, will go on to run the shell unless
 # script calls 'exit' or 'exit 0' or 'exit errcode'.
 load(script2run) if script2run
 
-# Output a banner message that tells users where to go for help
-@shell.print_banner
+if interactive
+  # Output a banner message that tells users where to go for help
+  @shell.print_banner
 
-require "irb"
-require 'irb/hirb'
+  require "irb"
+  require 'irb/hirb'
 
-module IRB
-  def self.start(ap_path = nil)
-    $0 = File::basename(ap_path, ".rb") if ap_path
+  module IRB
+    def self.start(ap_path = nil)
+      $0 = File::basename(ap_path, ".rb") if ap_path
 
-    IRB.setup(ap_path)
-    @CONF[:IRB_NAME] = 'hbase'
-    @CONF[:AP_NAME] = 'hbase'
-    @CONF[:BACK_TRACE_LIMIT] = 0 unless $fullBackTrace
+      IRB.setup(ap_path)
+      @CONF[:IRB_NAME] = 'hbase'
+      @CONF[:AP_NAME] = 'hbase'
+      @CONF[:BACK_TRACE_LIMIT] = 0 unless $fullBackTrace
 
-    if @CONF[:SCRIPT]
-      hirb = HIRB.new(nil, @CONF[:SCRIPT])
-    else
-      hirb = HIRB.new
-    end
+      if @CONF[:SCRIPT]
+        hirb = HIRB.new(nil, @CONF[:SCRIPT])
+      else
+        hirb = HIRB.new
+      end
 
-    @CONF[:IRB_RC].call(hirb.context) if @CONF[:IRB_RC]
-    @CONF[:MAIN_CONTEXT] = hirb.context
+      @CONF[:IRB_RC].call(hirb.context) if @CONF[:IRB_RC]
+      @CONF[:MAIN_CONTEXT] = hirb.context
 
-    catch(:IRB_EXIT) do
-      hirb.eval_input
+      catch(:IRB_EXIT) do
+        hirb.eval_input
+      end
     end
   end
-end
 
-IRB.start
+  IRB.start
+else
+  begin
+    # Noninteractive mode: if there is input on stdin, do a simple REPL.
+    # XXX Note that this purposefully uses STDIN and not Kernel.gets
+    #     in order to maintain compatibility with previous behavior where
+    #     a user could pass in script2run and then still pipe commands on
+    #     stdin.
+    require "irb/ruby-lex"
+    require "irb/workspace"
+    workspace = IRB::WorkSpace.new(binding())
+    scanner = RubyLex.new
+    scanner.set_input(STDIN)
+    scanner.each_top_level_statement do |statement, linenum|
+       puts(workspace.evaluate(nil, statement, 'stdin', linenum))
+    end
+  # XXX We're catching Exception on purpose, because we want to include
+  #     unwrapped java exceptions, syntax errors, eval failures, etc.
+  rescue Exception => exception
+    message = exception.to_s
+    # exception unwrapping in shell means we'll have to handle Java exceptions
+    # as a special case in order to format them properly.
+    if exception.kind_of? java.lang.Exception
+      $stderr.puts "java exception"
+      message = exception.get_message
+    end
+    # Include the 'ERROR' string to try to make transition easier for scripts that
+    # may have already been relying on grepping output.
+    puts "ERROR #{exception.class}: #{message}"
+    if $fullBacktrace
+      # re-raising the will include a backtrace and exit.
+      raise exception
+    else
+      exit 1
+    end
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/2f09b39f/hbase-shell/src/main/ruby/shell.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell.rb b/hbase-shell/src/main/ruby/shell.rb
index 6af4473..da5f4a9 100644
--- a/hbase-shell/src/main/ruby/shell.rb
+++ b/hbase-shell/src/main/ruby/shell.rb
@@ -71,13 +71,16 @@ module Shell
   class Shell
     attr_accessor :hbase
     attr_accessor :formatter
+    attr_accessor :interactive
+    alias interactive? interactive
 
     @debug = false
     attr_accessor :debug
 
-    def initialize(hbase, formatter)
+    def initialize(hbase, formatter, interactive=true)
       self.hbase = hbase
       self.formatter = formatter
+      self.interactive = interactive
     end
 
     def hbase_admin

http://git-wip-us.apache.org/repos/asf/hbase/blob/2f09b39f/hbase-shell/src/main/ruby/shell/commands.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/main/ruby/shell/commands.rb b/hbase-shell/src/main/ruby/shell/commands.rb
index 54fa204..1b079fb 100644
--- a/hbase-shell/src/main/ruby/shell/commands.rb
+++ b/hbase-shell/src/main/ruby/shell/commands.rb
@@ -37,13 +37,17 @@ module Shell
         while rootCause != nil && rootCause.respond_to?(:cause) && rootCause.cause != nil
           rootCause = rootCause.cause
         end
-        puts
-        puts "ERROR: #{rootCause}"
-        puts "Backtrace: #{rootCause.backtrace.join("\n           ")}" if debug
-        puts
-        puts "Here is some help for this command:"
-        puts help
-        puts
+        if @shell.interactive?
+          puts
+          puts "ERROR: #{rootCause}"
+          puts "Backtrace: #{rootCause.backtrace.join("\n           ")}" if debug
+          puts
+          puts "Here is some help for this command:"
+          puts help
+          puts
+        else
+          raise rootCause
+        end
       end
 
       def admin

http://git-wip-us.apache.org/repos/asf/hbase/blob/2f09b39f/hbase-shell/src/test/ruby/shell/noninteractive_test.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/test/ruby/shell/noninteractive_test.rb b/hbase-shell/src/test/ruby/shell/noninteractive_test.rb
new file mode 100644
index 0000000..14bdbc7
--- /dev/null
+++ b/hbase-shell/src/test/ruby/shell/noninteractive_test.rb
@@ -0,0 +1,42 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+require 'hbase'
+require 'shell'
+require 'shell/formatter'
+
+class NonInteractiveTest < Test::Unit::TestCase
+  def setup
+    @formatter = ::Shell::Formatter::Console.new()
+    @hbase = ::Hbase::Hbase.new($TEST_CLUSTER.getConfiguration)
+    @shell = Shell::Shell.new(@hbase, @formatter, false)
+  end
+
+  define_test "Shell::Shell noninteractive mode should throw" do
+    # XXX Exception instead of StandardError because we throw things
+    #     that aren't StandardError
+    assert_raise(ArgumentError) do
+      # incorrect number of arguments
+      @shell.command('create', 'foo')
+    end
+    @shell.command('create', 'foo', 'family_1')
+    exception = assert_raise(RuntimeError) do
+      # create a table that exists
+      @shell.command('create', 'foo', 'family_1')
+    end
+    assert_equal("Table already exists: foo!", exception.message)
+  end
+end

http://git-wip-us.apache.org/repos/asf/hbase/blob/2f09b39f/hbase-shell/src/test/ruby/shell/shell_test.rb
----------------------------------------------------------------------
diff --git a/hbase-shell/src/test/ruby/shell/shell_test.rb b/hbase-shell/src/test/ruby/shell/shell_test.rb
index 988d09e..56b7dc8 100644
--- a/hbase-shell/src/test/ruby/shell/shell_test.rb
+++ b/hbase-shell/src/test/ruby/shell/shell_test.rb
@@ -66,4 +66,14 @@ class ShellTest < Test::Unit::TestCase
   define_test "Shell::Shell#command should execute a command" do
     @shell.command('version')
   end
+
+  #-------------------------------------------------------------------------------
+
+  define_test "Shell::Shell interactive mode should not throw" do
+    # incorrect number of arguments
+    @shell.command('create', 'foo')
+    @shell.command('create', 'foo', 'family_1')
+    # create a table that exists
+    @shell.command('create', 'foo', 'family_1')
+  end
 end