From b05c630c29ac50e7c1356f0b09a99513ec756d8c Mon Sep 17 00:00:00 2001 From: Elliot Miller Date: Fri, 24 Jul 2020 14:51:23 -0400 Subject: [PATCH 1/3] HBASE-11686 Shell code should create a binding / irb workspace instead of polluting the root namespace - Refactor Shell.export_commands to define commands using ruby lambdas. Additionally, this change stores a reference to shell_inst in scope so that we no longer need to assume the existance of the variable @shell. - Add logic to Shell class for constructing an IRB workspace with its own binding and non-global receiver. This workspace is loaded with all HBase and IRB commands. - Create new method on Shell for evaluating input from an arbitrary IO instance within the created IRB workspace. This is based on work by Hsieh that was previously in bin/hirb.rb. This method is more generic and more testable. This single pattern can be used for both executing files and reading from stdin, therefore reducing complexity. - Move special 'help' and 'tools' command definitions to shell.rb. These commands are tightly linked with an instance of the shell, so it is easiest to have them defined together. - Remove all global includes of HBaseConstants from ruby test files. Before this change, tests were loading these constants into the top level, which could cause tests to pass that should really fail. - Try to reduce the number of places that constants are included. I think it's best to reference each ruby constant's full name, but where that would cause a big diff I instead moved the include to the innermost Module or Class. - Update docs and comments - Remove unneccessary includes - Add shell --top-level-cmds compatibility flag. Since this PR removes all the HBase symbols from the top-level receiver (ie. main Object), it is possible (albeit unlikely) that this will break operator scripts. This flag will export all the commands at the top-level like the shell previously did. --- bin/hirb.rb | 90 +++++---------- hbase-shell/src/main/ruby/hbase/admin.rb | 3 +- hbase-shell/src/main/ruby/hbase/quotas.rb | 3 + .../src/main/ruby/hbase/rsgroup_admin.rb | 8 +- hbase-shell/src/main/ruby/hbase/security.rb | 2 - .../src/main/ruby/hbase/taskmonitor.rb | 2 - hbase-shell/src/main/ruby/hbase_constants.rb | 4 +- hbase-shell/src/main/ruby/shell.rb | 105 ++++++++++++++++-- .../ruby/shell/commands/clone_snapshot.rb | 2 +- .../src/main/ruby/shell/commands/describe.rb | 2 +- .../ruby/shell/commands/describe_namespace.rb | 2 +- .../main/ruby/shell/commands/list_regions.rb | 12 +- .../src/main/ruby/shell/commands/set_quota.rb | 24 ++-- .../src/main/ruby/shell/hbase_receiver.rb | 33 ++++++ .../src/test/ruby/hbase/admin2_test.rb | 3 +- hbase-shell/src/test/ruby/hbase/admin_test.rb | 16 ++- .../hbase/list_regions_test_no_cluster.rb | 3 +- .../src/test/ruby/hbase/quotas_test.rb | 4 +- .../test/ruby/hbase/quotas_test_no_cluster.rb | 3 +- .../test/ruby/hbase/replication_admin_test.rb | 2 +- .../test/ruby/hbase/security_admin_test.rb | 3 +- hbase-shell/src/test/ruby/hbase/table_test.rb | 6 +- .../ruby/hbase/test_connection_no_cluster.rb | 3 +- .../hbase/visibility_labels_admin_test.rb | 3 +- .../src/test/ruby/shell/converter_test.rb | 3 +- .../test/ruby/shell/list_procedures_test.rb | 3 +- hbase-shell/src/test/ruby/test_helper.rb | 2 +- 27 files changed, 211 insertions(+), 135 deletions(-) create mode 100644 hbase-shell/src/main/ruby/shell/hbase_receiver.rb diff --git a/bin/hirb.rb b/bin/hirb.rb index fcbec7809c9d..4d1f0b683b64 100644 --- a/bin/hirb.rb +++ b/bin/hirb.rb @@ -58,6 +58,8 @@ -h | --help This help. -n | --noninteractive Do not run within an IRB session and exit with non-zero status on first error. + --top-level-cmds Compatibility flag to export HBase shell commands onto + Ruby's main object -Dkey=value Pass hbase-*.xml Configuration overrides. For example, to use an alternate zookeeper ensemble, pass: -Dhbase.zookeeper.quorum=zookeeper.example.org @@ -81,6 +83,7 @@ def add_to_configuration(c, arg) log_level = org.apache.log4j.Level::ERROR @shell_debug = false interactive = true +top_level_definitions = false _configuration = nil D_ARG = '-D'.freeze while (arg = ARGV.shift) @@ -108,6 +111,8 @@ def add_to_configuration(c, arg) warn '[INFO] the -r | --return-values option is ignored. we always behave '\ 'as though it was given.' found.push(arg) + elsif arg == '--top-level-cmds' + top_level_definitions = true else # Presume it a script. Save it off for running later below # after we've set up some environment. @@ -143,21 +148,10 @@ def add_to_configuration(c, arg) @shell = Shell::Shell.new(@hbase, 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 -def help(command = nil) - @shell.help(command) -end - -# Backwards compatibility method -def tools - @shell.help_group('tools') -end - -# Debugging method +## +# Toggle shell debugging +# +# @return [Boolean] true is debug is turned on after updating the flag def debug if @shell_debug @shell_debug = false @@ -173,26 +167,37 @@ def debug debug? end +## +# Print whether debug is on or off def debug? puts "Debug mode is #{@shell_debug ? 'ON' : 'OFF'}\n\n" nil end -# Include hbase constants -include HBaseConstants +# For backwards compatibility, this will export all the HBase shell commands onto Ruby's top-level +# receiver object known as "main". +if top_level_definitions + include HBaseConstants + @shell.export_commands(self) +end # 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 +@shell.eval_io(File.new(script2run)) if script2run + +# If we are not running interactively, evaluate standard input +@shell.eval_io(STDIN) unless interactive if interactive # Output a banner message that tells users where to go for help @shell.print_banner require 'irb' + require 'irb/ext/change-ws' require 'irb/hirb' module IRB + # Override of the default IRB.start def self.start(ap_path = nil) $0 = File.basename(ap_path, '.rb') if ap_path @@ -207,7 +212,12 @@ def self.start(ap_path = nil) HIRB.new end + shl = TOPLEVEL_BINDING.receiver.instance_variable_get :'@shell' + hirb.context.change_workspace shl.get_workspace + @CONF[:IRB_RC].call(hirb.context) if @CONF[:IRB_RC] + # Storing our current HBase IRB Context as the main context is imperative for several reasons, + # including auto-completion. @CONF[:MAIN_CONTEXT] = hirb.context catch(:IRB_EXIT) do @@ -217,48 +227,4 @@ def self.start(ap_path = nil) end 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 - - # RubyLex claims to take an IO but really wants an InputMethod - module IOExtensions - def encoding - external_encoding - end - end - IO.include IOExtensions - - 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.is_a? java.lang.Exception - warn '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 diff --git a/hbase-shell/src/main/ruby/hbase/admin.rb b/hbase-shell/src/main/ruby/hbase/admin.rb index 6bc09f18a765..807b5a10e80b 100644 --- a/hbase-shell/src/main/ruby/hbase/admin.rb +++ b/hbase-shell/src/main/ruby/hbase/admin.rb @@ -27,6 +27,7 @@ java_import org.apache.hadoop.hbase.TableName java_import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder java_import org.apache.hadoop.hbase.client.TableDescriptorBuilder +java_import org.apache.hadoop.hbase.HConstants # Wrapper for org.apache.hadoop.hbase.client.HBaseAdmin @@ -1011,7 +1012,7 @@ def cfd(arg, tdb) cfdb.setTimeToLive(arg.delete(ColumnFamilyDescriptorBuilder::TTL)) if arg.include?(ColumnFamilyDescriptorBuilder::TTL) cfdb.setDataBlockEncoding(org.apache.hadoop.hbase.io.encoding.DataBlockEncoding.valueOf(arg.delete(ColumnFamilyDescriptorBuilder::DATA_BLOCK_ENCODING))) if arg.include?(ColumnFamilyDescriptorBuilder::DATA_BLOCK_ENCODING) cfdb.setBlocksize(JInteger.valueOf(arg.delete(ColumnFamilyDescriptorBuilder::BLOCKSIZE))) if arg.include?(ColumnFamilyDescriptorBuilder::BLOCKSIZE) - cfdb.setMaxVersions(JInteger.valueOf(arg.delete(ColumnFamilyDescriptorBuilder::VERSIONS))) if arg.include?(ColumnFamilyDescriptorBuilder::VERSIONS) + cfdb.setMaxVersions(JInteger.valueOf(arg.delete(HConstants::VERSIONS))) if arg.include?(HConstants::VERSIONS) cfdb.setMinVersions(JInteger.valueOf(arg.delete(ColumnFamilyDescriptorBuilder::MIN_VERSIONS))) if arg.include?(ColumnFamilyDescriptorBuilder::MIN_VERSIONS) cfdb.setKeepDeletedCells(org.apache.hadoop.hbase.KeepDeletedCells.valueOf(arg.delete(ColumnFamilyDescriptorBuilder::KEEP_DELETED_CELLS).to_s.upcase)) if arg.include?(ColumnFamilyDescriptorBuilder::KEEP_DELETED_CELLS) cfdb.setCompressTags(JBoolean.valueOf(arg.delete(ColumnFamilyDescriptorBuilder::COMPRESS_TAGS))) if arg.include?(ColumnFamilyDescriptorBuilder::COMPRESS_TAGS) diff --git a/hbase-shell/src/main/ruby/hbase/quotas.rb b/hbase-shell/src/main/ruby/hbase/quotas.rb index 62c54c98ba84..75757c18fc46 100644 --- a/hbase-shell/src/main/ruby/hbase/quotas.rb +++ b/hbase-shell/src/main/ruby/hbase/quotas.rb @@ -61,6 +61,9 @@ module HBaseQuotasConstants module Hbase # rubocop:disable Metrics/ClassLength class QuotasAdmin + include HBaseConstants + include HBaseQuotasConstants + def initialize(admin) @admin = admin end diff --git a/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb b/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb index 2027c778dbec..dfffb5672a8a 100644 --- a/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb +++ b/hbase-shell/src/main/ruby/hbase/rsgroup_admin.rb @@ -23,8 +23,6 @@ module Hbase class RSGroupAdmin - include HBaseConstants - def initialize(connection) @connection = connection @admin = @connection.getAdmin @@ -213,11 +211,11 @@ def alter_rsgroup_config(rsgroup_name, *args) unless arg.is_a?(Hash) raise(ArgumentError, "#{arg.class} of #{arg.inspect} is not of Hash type") end - method = arg[METHOD] + method = arg[::HBaseConstants::METHOD] if method == 'unset' - configuration.remove(arg[NAME]) + configuration.remove(arg[::HBaseConstants::NAME]) elsif method == 'set' - arg.delete(METHOD) + arg.delete(::HBaseConstants::METHOD) for k, v in arg v = v.to_s unless v.nil? configuration.put(k, v) diff --git a/hbase-shell/src/main/ruby/hbase/security.rb b/hbase-shell/src/main/ruby/hbase/security.rb index e95f0946c413..652459a9668c 100644 --- a/hbase-shell/src/main/ruby/hbase/security.rb +++ b/hbase-shell/src/main/ruby/hbase/security.rb @@ -22,8 +22,6 @@ module Hbase class SecurityAdmin - include HBaseConstants - def initialize(admin) @admin = admin @connection = @admin.getConnection diff --git a/hbase-shell/src/main/ruby/hbase/taskmonitor.rb b/hbase-shell/src/main/ruby/hbase/taskmonitor.rb index 8509b5dc6529..02c84320c13c 100644 --- a/hbase-shell/src/main/ruby/hbase/taskmonitor.rb +++ b/hbase-shell/src/main/ruby/hbase/taskmonitor.rb @@ -27,8 +27,6 @@ module Hbase class TaskMonitor - include HBaseConstants - #--------------------------------------------------------------------------------------------- # Represents information reported by a server on a single MonitoredTask class Task diff --git a/hbase-shell/src/main/ruby/hbase_constants.rb b/hbase-shell/src/main/ruby/hbase_constants.rb index 9d16aa67fa83..cd21e78a8648 100644 --- a/hbase-shell/src/main/ruby/hbase_constants.rb +++ b/hbase-shell/src/main/ruby/hbase_constants.rb @@ -116,7 +116,7 @@ def self.promote_constants(constants) promote_constants(org.apache.hadoop.hbase.client.TableDescriptorBuilder.constants) end -# Include classes definition +# Ensure that hbase class definitions are imported require 'hbase/hbase' require 'hbase/admin' require 'hbase/taskmonitor' @@ -126,5 +126,3 @@ def self.promote_constants(constants) require 'hbase/security' require 'hbase/visibility_labels' require 'hbase/rsgroup_admin' - -include HBaseQuotasConstants diff --git a/hbase-shell/src/main/ruby/shell.rb b/hbase-shell/src/main/ruby/shell.rb index 92e52fae4a64..45f715a35193 100644 --- a/hbase-shell/src/main/ruby/shell.rb +++ b/hbase-shell/src/main/ruby/shell.rb @@ -16,6 +16,20 @@ # See the License for the specific language governing permissions and # limitations under the License. # +require 'irb' +require 'irb/workspace' +require 'shell/hbase_receiver' + +## +# HBaseIOExtensions is a module to be "mixed-in" (ie. included) to Ruby's IO class. It is required +# if you want to use RubyLex with an IO object. RubyLex claims to take an IO but really wants an +# InputMethod. +module HBaseIOExtensions + def encoding + external_encoding + end +end + # Shell commands module module Shell @@ -115,20 +129,33 @@ def hbase_rsgroup_admin @rsgroup_admin ||= hbase.rsgroup_admin end - def export_commands(where) + ## + # Create a class method on the given object for each of the loaded command classes + def export_commands(target) + # We need to store a reference to this Shell instance in the scope of this method so that it + # can be accessed later in the scope of the target object. + shell_inst = self + # If target is an instance, get the parent class + target = target.class unless target.is_a? Class + # Define each method as a lambda. We need to use a lambda (rather than a Proc or block) for + # its properties: preservation of local variables and return ::Shell.commands.keys.each do |cmd| - # here where is the IRB namespace - # this method just adds the call to the specified command - # which just references back to 'this' shell object - # a decently extensible way to add commands - where.send :instance_eval, <<-EOF - def #{cmd}(*args) - ret = @shell.command('#{cmd}', *args) - puts - return ret - end - EOF + target.send :define_method, cmd.to_sym, lambda { |*args| + ret = shell_inst.command("#{cmd}", *args) + puts + ret + } end + # Export help method + target.send :define_method, :help, lambda { |command=nil| + shell_inst.help(command) + nil + } + # Export tools method for backwards compatibility + target.send :define_method, :tools, lambda { + shell_inst.help_group('tools') + nil + } end def command_instance(command) @@ -238,6 +265,60 @@ def help_footer For more on the HBase Shell, see http://hbase.apache.org/book.html HERE end + + @irb_workspace = nil + ## + # Returns an IRB Workspace for this shell instance with all the IRB and HBase commands installed + def get_workspace + return @irb_workspace unless @irb_workspace == nil + hbase_receiver = HBaseReceiver.new + # install all the hbase commands BEFORE the irb commands so that our help command is installed + # rather than IRB's help command + export_commands(hbase_receiver) + # install all the IRB commands onto our receiver + IRB::ExtendCommandBundle.extend_object(hbase_receiver) + ::IRB::WorkSpace.new(hbase_receiver.get_binding) + end + + ## + # Read from an instance of Ruby's IO class and evaluate each line within the shell's workspace + # + # Unlike Ruby's require or load, this method allows us to execute code with a custom binding. In + # this case, we are using the binding constructed with all the HBase shell constants and + # methods. + def eval_io(io) + require 'irb/ruby-lex' + # Mixing HBaseIOExtensions into IO allows us to pass IO objects to RubyLex. + IO.include HBaseIOExtensions + + workspace = get_workspace + scanner = RubyLex.new + scanner.set_input(io) + + begin + scanner.each_top_level_statement do |statement, linenum| + puts(workspace.evaluate(nil, statement, 'stdin', linenum)) + end + 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.is_a? java.lang.Exception + warn '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 + nil + end end # rubocop:enable Metrics/ClassLength end diff --git a/hbase-shell/src/main/ruby/shell/commands/clone_snapshot.rb b/hbase-shell/src/main/ruby/shell/commands/clone_snapshot.rb index 8f0b35b39e7a..abc975919d92 100644 --- a/hbase-shell/src/main/ruby/shell/commands/clone_snapshot.rb +++ b/hbase-shell/src/main/ruby/shell/commands/clone_snapshot.rb @@ -38,7 +38,7 @@ def help def command(snapshot_name, table, args = {}) raise(ArgumentError, 'Arguments should be a Hash') unless args.is_a?(Hash) - restore_acl = args.delete(RESTORE_ACL) || false + restore_acl = args.delete(::HBaseConstants::RESTORE_ACL) || false admin.clone_snapshot(snapshot_name, table, restore_acl) end diff --git a/hbase-shell/src/main/ruby/shell/commands/describe.rb b/hbase-shell/src/main/ruby/shell/commands/describe.rb index 0553755daba3..c32b77d9eaba 100644 --- a/hbase-shell/src/main/ruby/shell/commands/describe.rb +++ b/hbase-shell/src/main/ruby/shell/commands/describe.rb @@ -48,7 +48,7 @@ def command(table) # No QUOTAS if hbase:meta table puts formatter.header(%w[QUOTAS]) - count = quotas_admin.list_quotas(TABLE => table.to_s) do |_, quota| + count = quotas_admin.list_quotas(::HBaseConstants::TABLE => table.to_s) do |_, quota| formatter.row([quota]) end formatter.footer(count) diff --git a/hbase-shell/src/main/ruby/shell/commands/describe_namespace.rb b/hbase-shell/src/main/ruby/shell/commands/describe_namespace.rb index fd14c4582f53..a929887dda15 100644 --- a/hbase-shell/src/main/ruby/shell/commands/describe_namespace.rb +++ b/hbase-shell/src/main/ruby/shell/commands/describe_namespace.rb @@ -36,7 +36,7 @@ def command(namespace) ns = namespace.to_s if admin.exists?(::HBaseQuotasConstants::QUOTA_TABLE_NAME.to_s) puts formatter.header(%w[QUOTAS]) - count = quotas_admin.list_quotas(NAMESPACE => ns) do |_, quota| + count = quotas_admin.list_quotas(::HBaseConstants::NAMESPACE => ns) do |_, quota| formatter.row([quota]) end formatter.footer(count) diff --git a/hbase-shell/src/main/ruby/shell/commands/list_regions.rb b/hbase-shell/src/main/ruby/shell/commands/list_regions.rb index 262ab1e8799e..49787800aff1 100644 --- a/hbase-shell/src/main/ruby/shell/commands/list_regions.rb +++ b/hbase-shell/src/main/ruby/shell/commands/list_regions.rb @@ -47,7 +47,7 @@ def command(table_name, options = nil, cols = nil) elsif !options.is_a? Hash # When options isn't a hash, assume it's the server name # and create the hash internally - options = { SERVER_NAME => options } + options = { ::HBaseConstants::SERVER_NAME => options } end raise "Table #{table_name} must be enabled." unless admin.enabled?(table_name) @@ -85,7 +85,7 @@ def command(table_name, options = nil, cols = nil) hregion_locator_instance = conn_instance.getRegionLocator(TableName.valueOf(table_name)) hregion_locator_list = hregion_locator_instance.getAllRegionLocations.to_a results = [] - desired_server_name = options[SERVER_NAME] + desired_server_name = options[::HBaseConstants::SERVER_NAME] begin # Filter out region servers which we don't want, default to all RS @@ -93,11 +93,11 @@ def command(table_name, options = nil, cols = nil) # A locality threshold of "1.0" would be all regions (cannot have greater than 1 locality) # Regions which have a `dataLocality` less-than-or-equal to this value are accepted locality_threshold = 1.0 - if options.key? LOCALITY_THRESHOLD - value = options[LOCALITY_THRESHOLD] + if options.key? ::HBaseConstants::LOCALITY_THRESHOLD + value = options[::HBaseConstants::LOCALITY_THRESHOLD] # Value validation. Must be a Float, and must be between [0, 1.0] - raise "#{LOCALITY_THRESHOLD} must be a float value" unless value.is_a? Float - raise "#{LOCALITY_THRESHOLD} must be between 0 and 1.0, inclusive" unless valid_locality_threshold? value + raise "#{::HBaseConstants::LOCALITY_THRESHOLD} must be a float value" unless value.is_a? Float + raise "#{::HBaseConstants::LOCALITY_THRESHOLD} must be between 0 and 1.0, inclusive" unless valid_locality_threshold? value locality_threshold = value end diff --git a/hbase-shell/src/main/ruby/shell/commands/set_quota.rb b/hbase-shell/src/main/ruby/shell/commands/set_quota.rb index 7e8e563ce031..1b315c5df916 100644 --- a/hbase-shell/src/main/ruby/shell/commands/set_quota.rb +++ b/hbase-shell/src/main/ruby/shell/commands/set_quota.rb @@ -123,31 +123,31 @@ def help end def command(args = {}) - if args.key?(TYPE) - qtype = args.delete(TYPE) + if args.key?(::HBaseConstants::TYPE) + qtype = args.delete(::HBaseConstants::TYPE) case qtype - when THROTTLE - if args[LIMIT].eql? NONE - args.delete(LIMIT) + when ::HBaseQuotasConstants::THROTTLE + if args[::HBaseConstants::LIMIT].eql? ::HBaseConstants::NONE + args.delete(::HBaseConstants::LIMIT) quotas_admin.unthrottle(args) else quotas_admin.throttle(args) end - when SPACE - if args[LIMIT].eql? NONE - args.delete(LIMIT) + when ::HBaseQuotasConstants::SPACE + if args[::HBaseConstants::LIMIT].eql? ::HBaseConstants::NONE + args.delete(::HBaseConstants::LIMIT) # Table/Namespace argument is verified in remove_space_limit quotas_admin.remove_space_limit(args) else - raise(ArgumentError, 'Expected a LIMIT to be provided') unless args.key?(LIMIT) - raise(ArgumentError, 'Expected a POLICY to be provided') unless args.key?(POLICY) + raise(ArgumentError, 'Expected a LIMIT to be provided') unless args.key?(::HBaseConstants::LIMIT) + raise(ArgumentError, 'Expected a POLICY to be provided') unless args.key?(::HBaseConstants::POLICY) quotas_admin.limit_space(args) end else raise 'Invalid TYPE argument. got ' + qtype end - elsif args.key?(GLOBAL_BYPASS) - quotas_admin.set_global_bypass(args.delete(GLOBAL_BYPASS), args) + elsif args.key?(::HBaseQuotasConstants::GLOBAL_BYPASS) + quotas_admin.set_global_bypass(args.delete(::HBaseQuotasConstants::GLOBAL_BYPASS), args) else raise 'Expected TYPE argument' end diff --git a/hbase-shell/src/main/ruby/shell/hbase_receiver.rb b/hbase-shell/src/main/ruby/shell/hbase_receiver.rb new file mode 100644 index 000000000000..86a018e557d0 --- /dev/null +++ b/hbase-shell/src/main/ruby/shell/hbase_receiver.rb @@ -0,0 +1,33 @@ +# +# +# 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_constants' +require 'hbase/quotas' + +# +# Simple class to act as the main receiver for an IRB Workspace (and its respective ruby Binding) +# in our HBase shell. This will hold all the commands we want in our shell. +# +class HBaseReceiver < Object + include HBaseConstants + include HBaseQuotasConstants + + def get_binding + binding + end +end diff --git a/hbase-shell/src/test/ruby/hbase/admin2_test.rb b/hbase-shell/src/test/ruby/hbase/admin2_test.rb index 16233c009f67..23fe4c29ba5c 100644 --- a/hbase-shell/src/test/ruby/hbase/admin2_test.rb +++ b/hbase-shell/src/test/ruby/hbase/admin2_test.rb @@ -23,12 +23,11 @@ require 'hbase/hbase' require 'hbase/table' -include HBaseConstants - module Hbase # Tests for the `status` shell command class StatusTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants def setup setup_hbase diff --git a/hbase-shell/src/test/ruby/hbase/admin_test.rb b/hbase-shell/src/test/ruby/hbase/admin_test.rb index 11798c338591..374d9c165399 100644 --- a/hbase-shell/src/test/ruby/hbase/admin_test.rb +++ b/hbase-shell/src/test/ruby/hbase/admin_test.rb @@ -23,7 +23,6 @@ require 'hbase/hbase' require 'hbase/table' -include HBaseConstants module Hbase class AdminHelpersTest < Test::Unit::TestCase @@ -63,6 +62,8 @@ def teardown # rubocop:disable Metrics/ClassLength class AdminMethodsTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants + include HBaseQuotasConstants def setup setup_hbase @@ -550,9 +551,9 @@ def teardown ns = @create_test_name command(:create_namespace, ns) command(:set_quota, - TYPE => SPACE, + TYPE => ::HBaseQuotasConstants::SPACE, LIMIT => '1G', - POLICY => NO_INSERTS, + POLICY => ::HBaseQuotasConstants::NO_INSERTS, NAMESPACE => ns) output = capture_stdout { command(:describe_namespace, ns) } puts output @@ -567,8 +568,8 @@ def teardown assert(output.include?('1 row(s)')) command(:set_quota, - TYPE => SPACE, - LIMIT => NONE, + TYPE => ::HBaseQuotasConstants::SPACE, + LIMIT => ::HBaseConstants::NONE, NAMESPACE => ns) output = capture_stdout { command(:describe_namespace, ns) } assert(output.include?('0 row(s)')) @@ -675,6 +676,8 @@ def teardown # Simple administration methods tests class AdminCloneTableSchemaTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants + def setup setup_hbase # Create table test table name @@ -758,6 +761,8 @@ def teardown # Simple administration methods tests class AdminRegionTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants + def setup setup_hbase # Create test table if it does not exist @@ -846,6 +851,7 @@ def teardown # rubocop:disable Metrics/ClassLength class AdminAlterTableTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants def setup setup_hbase diff --git a/hbase-shell/src/test/ruby/hbase/list_regions_test_no_cluster.rb b/hbase-shell/src/test/ruby/hbase/list_regions_test_no_cluster.rb index 73d744c345f6..6be259779d95 100644 --- a/hbase-shell/src/test/ruby/hbase/list_regions_test_no_cluster.rb +++ b/hbase-shell/src/test/ruby/hbase/list_regions_test_no_cluster.rb @@ -18,8 +18,6 @@ require 'shell' require 'hbase_constants' -include HBaseConstants - java_import 'org.apache.hadoop.hbase.HRegionLocation' java_import 'org.apache.hadoop.hbase.client.RegionInfoBuilder' java_import 'org.apache.hadoop.hbase.ServerName' @@ -27,6 +25,7 @@ module Hbase class NoClusterListRegionsTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants define_test 'valid_locality_values' do command = ::Shell::Commands::ListRegions.new(nil) diff --git a/hbase-shell/src/test/ruby/hbase/quotas_test.rb b/hbase-shell/src/test/ruby/hbase/quotas_test.rb index 9fddb83a4138..c4fca28bdfdc 100644 --- a/hbase-shell/src/test/ruby/hbase/quotas_test.rb +++ b/hbase-shell/src/test/ruby/hbase/quotas_test.rb @@ -23,12 +23,12 @@ require 'hbase/hbase' require 'hbase/table' -include HBaseConstants - module Hbase # rubocop:disable Metrics/ClassLength class SpaceQuotasTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants + include HBaseQuotasConstants def setup setup_hbase diff --git a/hbase-shell/src/test/ruby/hbase/quotas_test_no_cluster.rb b/hbase-shell/src/test/ruby/hbase/quotas_test_no_cluster.rb index 7de122553ffd..79f735021a8a 100644 --- a/hbase-shell/src/test/ruby/hbase/quotas_test_no_cluster.rb +++ b/hbase-shell/src/test/ruby/hbase/quotas_test_no_cluster.rb @@ -23,8 +23,6 @@ require 'hbase/hbase' require 'hbase/table' -include HBaseConstants - java_import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshot java_import org.apache.hadoop.hbase.quotas.SpaceViolationPolicy java_import org.apache.hadoop.hbase.TableName @@ -32,6 +30,7 @@ module Hbase class NoClusterSpaceQuotasTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants define_test '_parse_size accepts various forms of byte shorthand' do qa = ::Hbase::QuotasAdmin.new(nil) diff --git a/hbase-shell/src/test/ruby/hbase/replication_admin_test.rb b/hbase-shell/src/test/ruby/hbase/replication_admin_test.rb index f6f41d2f7eb9..72fbe943040f 100644 --- a/hbase-shell/src/test/ruby/hbase/replication_admin_test.rb +++ b/hbase-shell/src/test/ruby/hbase/replication_admin_test.rb @@ -22,7 +22,6 @@ require 'hbase/hbase' require 'hbase/table' -include HBaseConstants include Java java_import org.apache.hadoop.hbase.replication.SyncReplicationState @@ -30,6 +29,7 @@ module Hbase class ReplicationAdminTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants def setup @peer_id = '1' diff --git a/hbase-shell/src/test/ruby/hbase/security_admin_test.rb b/hbase-shell/src/test/ruby/hbase/security_admin_test.rb index e1360c27301b..6e9a50cafcd1 100644 --- a/hbase-shell/src/test/ruby/hbase/security_admin_test.rb +++ b/hbase-shell/src/test/ruby/hbase/security_admin_test.rb @@ -22,12 +22,11 @@ require 'hbase/hbase' require 'hbase/table' -include HBaseConstants - module Hbase # Simple secure administration methods tests class SecureAdminMethodsTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants def setup setup_hbase diff --git a/hbase-shell/src/test/ruby/hbase/table_test.rb b/hbase-shell/src/test/ruby/hbase/table_test.rb index 0eef0179b3f5..20bcb500a803 100644 --- a/hbase-shell/src/test/ruby/hbase/table_test.rb +++ b/hbase-shell/src/test/ruby/hbase/table_test.rb @@ -19,12 +19,12 @@ require 'hbase_constants' -include HBaseConstants - module Hbase # Constructor tests class TableConstructorTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants + def setup setup_hbase end @@ -97,6 +97,7 @@ def tearDown # Simple data management methods tests class TableSimpleMethodsTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants def setup setup_hbase @@ -225,6 +226,7 @@ def teardown # rubocop:disable Metrics/ClassLength class TableComplexMethodsTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants def setup setup_hbase diff --git a/hbase-shell/src/test/ruby/hbase/test_connection_no_cluster.rb b/hbase-shell/src/test/ruby/hbase/test_connection_no_cluster.rb index d240ff860466..361937634c3c 100644 --- a/hbase-shell/src/test/ruby/hbase/test_connection_no_cluster.rb +++ b/hbase-shell/src/test/ruby/hbase/test_connection_no_cluster.rb @@ -23,11 +23,10 @@ require 'hbase/hbase' require 'hbase/table' -include HBaseConstants - module Hbase class NoClusterConnectionTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants def setup puts "starting shell" diff --git a/hbase-shell/src/test/ruby/hbase/visibility_labels_admin_test.rb b/hbase-shell/src/test/ruby/hbase/visibility_labels_admin_test.rb index b42290fa8dda..e69710d69a53 100644 --- a/hbase-shell/src/test/ruby/hbase/visibility_labels_admin_test.rb +++ b/hbase-shell/src/test/ruby/hbase/visibility_labels_admin_test.rb @@ -22,12 +22,11 @@ require 'hbase/hbase' require 'hbase/table' -include HBaseConstants - module Hbase # Simple secure administration methods tests class VisibilityLabelsAdminMethodsTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants def setup setup_hbase diff --git a/hbase-shell/src/test/ruby/shell/converter_test.rb b/hbase-shell/src/test/ruby/shell/converter_test.rb index 8b6079bcb36b..51e674093f25 100644 --- a/hbase-shell/src/test/ruby/shell/converter_test.rb +++ b/hbase-shell/src/test/ruby/shell/converter_test.rb @@ -17,11 +17,10 @@ require 'hbase_constants' require 'shell' -include HBaseConstants - module Hbase class ConverterTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants non_ascii_text = '⻆⻇' non_ascii_row = '⻄' diff --git a/hbase-shell/src/test/ruby/shell/list_procedures_test.rb b/hbase-shell/src/test/ruby/shell/list_procedures_test.rb index 1cad23b0057f..2bf5824c0ee3 100644 --- a/hbase-shell/src/test/ruby/shell/list_procedures_test.rb +++ b/hbase-shell/src/test/ruby/shell/list_procedures_test.rb @@ -20,11 +20,10 @@ require 'hbase_constants' require 'shell' -include HBaseConstants - module Hbase class ListProceduresTest < Test::Unit::TestCase include TestHelpers + include HBaseConstants def setup setup_hbase diff --git a/hbase-shell/src/test/ruby/test_helper.rb b/hbase-shell/src/test/ruby/test_helper.rb index 78fba7a20635..26b142638f04 100644 --- a/hbase-shell/src/test/ruby/test_helper.rb +++ b/hbase-shell/src/test/ruby/test_helper.rb @@ -119,7 +119,7 @@ def create_test_table_with_splits_file(name, splits_file) def create_test_table_with_region_replicas(name, num_of_replicas, splits) # Create the table if needed unless admin.exists?(name) - command(:create, name, 'f1', { REGION_REPLICATION => num_of_replicas }, + command(:create, name, 'f1', { ::HBaseConstants::REGION_REPLICATION => num_of_replicas }, splits) end From 3d73834114be6a7268c159ca17c79c72156724bc Mon Sep 17 00:00:00 2001 From: Elliot Miller Date: Mon, 27 Jul 2020 12:26:47 -0400 Subject: [PATCH 2/3] HBASE-11686 Light refactoring with added unit tests - Fixes some constants references by admin test 2 - Install HBase commands as singleton methods on recevier instances so that multiple receivers may exist. - Rename new flag from --top-level-cmds to --top-level-defs to be more semantically accurate. - Create new helper method Shell::Shell#export_all to install @hbase, @shell, constants, and all hbase commands to a target receiver. As a result, the HBaseReceiver became very simple and could be moved to shell.rb. - Add unit tests for Shell::Shell#eval_io and Shell::Shell#export_all - Add @hbase and @shell to hbase-shell IRB workspace - Fix robocop issues within patch --- bin/hirb.rb | 13 ++-- hbase-shell/src/main/ruby/shell.rb | 61 +++++++++++++------ .../main/ruby/shell/commands/list_regions.rb | 9 ++- .../ruby/shell/commands/restore_snapshot.rb | 2 +- .../src/main/ruby/shell/commands/set_quota.rb | 9 ++- .../src/main/ruby/shell/hbase_receiver.rb | 33 ---------- .../src/test/ruby/hbase/admin2_test.rb | 4 +- hbase-shell/src/test/ruby/shell/shell_test.rb | 58 ++++++++++++++++++ 8 files changed, 123 insertions(+), 66 deletions(-) delete mode 100644 hbase-shell/src/main/ruby/shell/hbase_receiver.rb diff --git a/bin/hirb.rb b/bin/hirb.rb index 4d1f0b683b64..2422785c676c 100644 --- a/bin/hirb.rb +++ b/bin/hirb.rb @@ -58,7 +58,7 @@ -h | --help This help. -n | --noninteractive Do not run within an IRB session and exit with non-zero status on first error. - --top-level-cmds Compatibility flag to export HBase shell commands onto + --top-level-defs Compatibility flag to export HBase shell commands onto Ruby's main object -Dkey=value Pass hbase-*.xml Configuration overrides. For example, to use an alternate zookeeper ensemble, pass: @@ -111,7 +111,7 @@ def add_to_configuration(c, arg) warn '[INFO] the -r | --return-values option is ignored. we always behave '\ 'as though it was given.' found.push(arg) - elsif arg == '--top-level-cmds' + elsif arg == '--top-level-defs' top_level_definitions = true else # Presume it a script. Save it off for running later below @@ -174,12 +174,9 @@ def debug? nil end -# For backwards compatibility, this will export all the HBase shell commands onto Ruby's top-level -# receiver object known as "main". -if top_level_definitions - include HBaseConstants - @shell.export_commands(self) -end +# For backwards compatibility, this will export all the HBase shell commands, constants, and +# instance variables (@hbase and @shell) onto Ruby's top-level receiver object known as "main". +@shell.export_all(self) if top_level_definitions # 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'. diff --git a/hbase-shell/src/main/ruby/shell.rb b/hbase-shell/src/main/ruby/shell.rb index 45f715a35193..1217ca68a59a 100644 --- a/hbase-shell/src/main/ruby/shell.rb +++ b/hbase-shell/src/main/ruby/shell.rb @@ -18,7 +18,16 @@ # require 'irb' require 'irb/workspace' -require 'shell/hbase_receiver' + +# +# Simple class to act as the main receiver for an IRB Workspace (and its respective ruby Binding) +# in our HBase shell. This will hold all the commands we want in our shell. +# +class HBaseReceiver < Object + def get_binding + binding + end +end ## # HBaseIOExtensions is a module to be "mixed-in" (ie. included) to Ruby's IO class. It is required @@ -130,34 +139,49 @@ def hbase_rsgroup_admin end ## - # Create a class method on the given object for each of the loaded command classes + # Create singleton methods on the target receiver object for all the loaded commands + # + # Therefore, export_commands will create "class methods" if passed a Module/Class and if passed + # an instance the methods will not exist on any other instances of the instantiated class. def export_commands(target) # We need to store a reference to this Shell instance in the scope of this method so that it # can be accessed later in the scope of the target object. shell_inst = self - # If target is an instance, get the parent class - target = target.class unless target.is_a? Class # Define each method as a lambda. We need to use a lambda (rather than a Proc or block) for # its properties: preservation of local variables and return ::Shell.commands.keys.each do |cmd| - target.send :define_method, cmd.to_sym, lambda { |*args| - ret = shell_inst.command("#{cmd}", *args) + target.send :define_singleton_method, cmd.to_sym, lambda { |*args| + ret = shell_inst.command(cmd.to_s, *args) puts ret } end # Export help method - target.send :define_method, :help, lambda { |command=nil| + target.send :define_singleton_method, :help, lambda { |command = nil| shell_inst.help(command) nil } # Export tools method for backwards compatibility - target.send :define_method, :tools, lambda { + target.send :define_singleton_method, :tools, lambda { shell_inst.help_group('tools') nil } end + # Export HBase commands, constants, and variables to target receiver + def export_all(target) + raise ArgumentError, 'target should not be a module' if target.is_a? Module + + # add constants to class of target + target.class.include ::HBaseConstants + target.class.include ::HBaseQuotasConstants + # add instance variables @hbase and @shell for backwards compatibility + target.instance_variable_set :'@hbase', @hbase + target.instance_variable_set :'@shell', self + # add commands to target + export_commands(target) + end + def command_instance(command) ::Shell.commands[command.to_s].new(self) end @@ -270,11 +294,12 @@ def help_footer ## # Returns an IRB Workspace for this shell instance with all the IRB and HBase commands installed def get_workspace - return @irb_workspace unless @irb_workspace == nil + return @irb_workspace unless @irb_workspace.nil? + hbase_receiver = HBaseReceiver.new - # install all the hbase commands BEFORE the irb commands so that our help command is installed - # rather than IRB's help command - export_commands(hbase_receiver) + # Install all the hbase commands, constants, and instance variables @shell and @hbase. This + # must be BEFORE the irb commands are installed so that our help command is not overwritten. + export_all(hbase_receiver) # install all the IRB commands onto our receiver IRB::ExtendCommandBundle.extend_object(hbase_receiver) ::IRB::WorkSpace.new(hbase_receiver.get_binding) @@ -299,20 +324,20 @@ def eval_io(io) scanner.each_top_level_statement do |statement, linenum| puts(workspace.evaluate(nil, statement, 'stdin', linenum)) end - rescue Exception => exception - message = exception.to_s + rescue Exception => e + message = e.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.is_a? java.lang.Exception + if e.is_a? java.lang.Exception warn 'java exception' - message = exception.get_message + message = e.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}" + puts "ERROR #{e.class}: #{message}" if $fullBacktrace # re-raising the will include a backtrace and exit. - raise exception + raise e else exit 1 end diff --git a/hbase-shell/src/main/ruby/shell/commands/list_regions.rb b/hbase-shell/src/main/ruby/shell/commands/list_regions.rb index 49787800aff1..e0030393af6a 100644 --- a/hbase-shell/src/main/ruby/shell/commands/list_regions.rb +++ b/hbase-shell/src/main/ruby/shell/commands/list_regions.rb @@ -96,8 +96,13 @@ def command(table_name, options = nil, cols = nil) if options.key? ::HBaseConstants::LOCALITY_THRESHOLD value = options[::HBaseConstants::LOCALITY_THRESHOLD] # Value validation. Must be a Float, and must be between [0, 1.0] - raise "#{::HBaseConstants::LOCALITY_THRESHOLD} must be a float value" unless value.is_a? Float - raise "#{::HBaseConstants::LOCALITY_THRESHOLD} must be between 0 and 1.0, inclusive" unless valid_locality_threshold? value + unless value.is_a? Float + raise "#{::HBaseConstants::LOCALITY_THRESHOLD} must be a float value" + end + unless valid_locality_threshold? value + raise "#{::HBaseConstants::LOCALITY_THRESHOLD} must be between 0 and 1.0, inclusive" + end + locality_threshold = value end diff --git a/hbase-shell/src/main/ruby/shell/commands/restore_snapshot.rb b/hbase-shell/src/main/ruby/shell/commands/restore_snapshot.rb index be6ee3ca827a..56168fa4a609 100644 --- a/hbase-shell/src/main/ruby/shell/commands/restore_snapshot.rb +++ b/hbase-shell/src/main/ruby/shell/commands/restore_snapshot.rb @@ -37,7 +37,7 @@ def help def command(snapshot_name, args = {}) raise(ArgumentError, 'Arguments should be a Hash') unless args.is_a?(Hash) - restore_acl = args.delete(RESTORE_ACL) || false + restore_acl = args.delete(::HBaseConstants::RESTORE_ACL) || false admin.restore_snapshot(snapshot_name, restore_acl) end end diff --git a/hbase-shell/src/main/ruby/shell/commands/set_quota.rb b/hbase-shell/src/main/ruby/shell/commands/set_quota.rb index 1b315c5df916..5a5669a9cc88 100644 --- a/hbase-shell/src/main/ruby/shell/commands/set_quota.rb +++ b/hbase-shell/src/main/ruby/shell/commands/set_quota.rb @@ -139,8 +139,13 @@ def command(args = {}) # Table/Namespace argument is verified in remove_space_limit quotas_admin.remove_space_limit(args) else - raise(ArgumentError, 'Expected a LIMIT to be provided') unless args.key?(::HBaseConstants::LIMIT) - raise(ArgumentError, 'Expected a POLICY to be provided') unless args.key?(::HBaseConstants::POLICY) + unless args.key?(::HBaseConstants::LIMIT) + raise(ArgumentError, 'Expected a LIMIT to be provided') + end + unless args.key?(::HBaseConstants::POLICY) + raise(ArgumentError, 'Expected a POLICY to be provided') + end + quotas_admin.limit_space(args) end else diff --git a/hbase-shell/src/main/ruby/shell/hbase_receiver.rb b/hbase-shell/src/main/ruby/shell/hbase_receiver.rb deleted file mode 100644 index 86a018e557d0..000000000000 --- a/hbase-shell/src/main/ruby/shell/hbase_receiver.rb +++ /dev/null @@ -1,33 +0,0 @@ -# -# -# 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_constants' -require 'hbase/quotas' - -# -# Simple class to act as the main receiver for an IRB Workspace (and its respective ruby Binding) -# in our HBase shell. This will hold all the commands we want in our shell. -# -class HBaseReceiver < Object - include HBaseConstants - include HBaseQuotasConstants - - def get_binding - binding - end -end diff --git a/hbase-shell/src/test/ruby/hbase/admin2_test.rb b/hbase-shell/src/test/ruby/hbase/admin2_test.rb index 23fe4c29ba5c..e420c74779fb 100644 --- a/hbase-shell/src/test/ruby/hbase/admin2_test.rb +++ b/hbase-shell/src/test/ruby/hbase/admin2_test.rb @@ -125,7 +125,7 @@ def teardown define_test "Snapshot should work when SKIP_FLUSH args" do drop_test_snapshot() - command(:snapshot, @test_name, @create_test_snapshot, {SKIP_FLUSH => true}) + command(:snapshot, @test_name, @create_test_snapshot, {::HBaseConstants::SKIP_FLUSH => true}) list = command(:list_snapshots, @create_test_snapshot) assert_equal(1, list.size) end @@ -155,7 +155,7 @@ def teardown assert_match(/f1/, admin.describe(restore_table)) assert_match(/f2/, admin.describe(restore_table)) command(:snapshot, restore_table, @create_test_snapshot) - command(:alter, restore_table, METHOD => 'delete', NAME => 'f1') + command(:alter, restore_table, ::HBaseConstants::METHOD => 'delete', ::HBaseConstants::NAME => 'f1') assert_no_match(/f1/, admin.describe(restore_table)) assert_match(/f2/, admin.describe(restore_table)) drop_test_table(restore_table) diff --git a/hbase-shell/src/test/ruby/shell/shell_test.rb b/hbase-shell/src/test/ruby/shell/shell_test.rb index e7f5b2639ceb..9fd28c0f7c37 100644 --- a/hbase-shell/src/test/ruby/shell/shell_test.rb +++ b/hbase-shell/src/test/ruby/shell/shell_test.rb @@ -61,6 +61,46 @@ module Foo; end #------------------------------------------------------------------------------- + define_test 'Shell::Shell#export_all export commands, constants, and variables' do + module FooM; end + class FooC; end + foo = FooC.new + + # export_all should reject classes and modules as targets + assert_raise(ArgumentError) do + @shell.export_all(FooM) + end + assert_raise(ArgumentError) do + @shell.export_all(FooC) + end + + # For potency, verify that none of the commands, variables or constants exist before export + assert(!foo.respond_to?(:version)) + assert(foo.instance_variable_get(:'@shell').nil?) + assert(foo.instance_variable_get(:'@hbase').nil?) + assert(!foo.class.const_defined?(:IN_MEMORY_COMPACTION)) # From HBaseConstants + assert(!foo.class.const_defined?(:QUOTA_TABLE_NAME)) # From HBaseQuotasConstants + + @shell.export_all(foo) + + # Now verify that all the commands, variables, and constants are installed + assert(foo.respond_to?(:version)) + assert(foo.instance_variable_get(:'@shell') == @shell) + assert(foo.instance_variable_get(:'@hbase') == @hbase) + assert(foo.class.const_defined?(:IN_MEMORY_COMPACTION)) # From HBaseConstants + assert(foo.class.const_defined?(:QUOTA_TABLE_NAME)) # From HBaseQuotasConstants + + # commands should not exist on the class of target + assert_raise(NameError) do + FooC.method :version + end + assert_raise(NameError) do + FooC.instance_method :version + end + end + + #------------------------------------------------------------------------------- + define_test "Shell::Shell#command_instance should return a command class" do assert_kind_of(Shell::Commands::Command, @shell.command_instance('version')) end @@ -73,6 +113,24 @@ module Foo; end #----------------------------------------------------------------------------- + define_test 'Shell::Shell#eval_io should evaluate IO' do + # check that at least one of the commands is present while evaluating + io = StringIO.new <<~EOF + puts (respond_to? :list) + EOF + output = capture_stdout { @shell.eval_io(io) } + assert_match(/true/, output) + + # check that at least one of the HBase constants is present while evaluating + io = StringIO.new <<~EOF + ROWPREFIXFILTER + EOF + output = capture_stdout { @shell.eval_io(io) } + assert_match(/"ROWPREFIXFILTER"/, output) + end + + #----------------------------------------------------------------------------- + define_test 'Shell::Shell#print_banner should display Reference Guide link' do @shell.interactive = true output = capture_stdout { @shell.print_banner } From 9b6dd5ed90b2e5e6f01ded7b029c665c4bd0e580 Mon Sep 17 00:00:00 2001 From: Elliot Miller Date: Mon, 27 Jul 2020 14:48:51 -0400 Subject: [PATCH 3/3] Typo s/is/if/ --- bin/hirb.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/hirb.rb b/bin/hirb.rb index 2422785c676c..5b502f4ca3d5 100644 --- a/bin/hirb.rb +++ b/bin/hirb.rb @@ -151,7 +151,7 @@ def add_to_configuration(c, arg) ## # Toggle shell debugging # -# @return [Boolean] true is debug is turned on after updating the flag +# @return [Boolean] true if debug is turned on after updating the flag def debug if @shell_debug @shell_debug = false