HBASE-11686 Shell code should create a binding / irb workspace instead of polluting the root namespace (#2141)

* 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.

* 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

* Typo s/is/if/
This commit is contained in:
Elliot 2020-07-27 23:56:09 -04:00 committed by GitHub
parent 7e6e7a7051
commit 7eff07d6bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 271 additions and 138 deletions

View File

@ -58,6 +58,8 @@ Usage: shell [OPTIONS] [SCRIPTFILE [ARGUMENTS]]
-h | --help This help. -h | --help This help.
-n | --noninteractive Do not run within an IRB session and exit with non-zero -n | --noninteractive Do not run within an IRB session and exit with non-zero
status on first error. status on first error.
--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 -Dkey=value Pass hbase-*.xml Configuration overrides. For example, to
use an alternate zookeeper ensemble, pass: use an alternate zookeeper ensemble, pass:
-Dhbase.zookeeper.quorum=zookeeper.example.org -Dhbase.zookeeper.quorum=zookeeper.example.org
@ -81,6 +83,7 @@ script2run = nil
log_level = org.apache.log4j.Level::ERROR log_level = org.apache.log4j.Level::ERROR
@shell_debug = false @shell_debug = false
interactive = true interactive = true
top_level_definitions = false
_configuration = nil _configuration = nil
D_ARG = '-D'.freeze D_ARG = '-D'.freeze
while (arg = ARGV.shift) while (arg = ARGV.shift)
@ -108,6 +111,8 @@ while (arg = ARGV.shift)
warn '[INFO] the -r | --return-values option is ignored. we always behave '\ warn '[INFO] the -r | --return-values option is ignored. we always behave '\
'as though it was given.' 'as though it was given.'
found.push(arg) found.push(arg)
elsif arg == '--top-level-defs'
top_level_definitions = true
else else
# Presume it a script. Save it off for running later below # Presume it a script. Save it off for running later below
# after we've set up some environment. # after we've set up some environment.
@ -143,21 +148,10 @@ require 'shell/formatter'
@shell = Shell::Shell.new(@hbase, interactive) @shell = Shell::Shell.new(@hbase, interactive)
@shell.debug = @shell_debug @shell.debug = @shell_debug
# Add commands to this namespace ##
# TODO avoid polluting main namespace by using a binding # Toggle shell debugging
@shell.export_commands(self) #
# @return [Boolean] true if debug is turned on after updating the flag
# Add help command
def help(command = nil)
@shell.help(command)
end
# Backwards compatibility method
def tools
@shell.help_group('tools')
end
# Debugging method
def debug def debug
if @shell_debug if @shell_debug
@shell_debug = false @shell_debug = false
@ -173,26 +167,34 @@ def debug
debug? debug?
end end
##
# Print whether debug is on or off
def debug? def debug?
puts "Debug mode is #{@shell_debug ? 'ON' : 'OFF'}\n\n" puts "Debug mode is #{@shell_debug ? 'ON' : 'OFF'}\n\n"
nil nil
end end
# Include hbase constants # For backwards compatibility, this will export all the HBase shell commands, constants, and
include HBaseConstants # 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 # 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'. # 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 if interactive
# Output a banner message that tells users where to go for help # Output a banner message that tells users where to go for help
@shell.print_banner @shell.print_banner
require 'irb' require 'irb'
require 'irb/ext/change-ws'
require 'irb/hirb' require 'irb/hirb'
module IRB module IRB
# Override of the default IRB.start
def self.start(ap_path = nil) def self.start(ap_path = nil)
$0 = File.basename(ap_path, '.rb') if ap_path $0 = File.basename(ap_path, '.rb') if ap_path
@ -207,7 +209,12 @@ if interactive
HIRB.new HIRB.new
end 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] @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 @CONF[:MAIN_CONTEXT] = hirb.context
catch(:IRB_EXIT) do catch(:IRB_EXIT) do
@ -217,48 +224,4 @@ if interactive
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
# 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 end

View File

@ -27,6 +27,7 @@ java_import org.apache.hadoop.hbase.ServerName
java_import org.apache.hadoop.hbase.TableName java_import org.apache.hadoop.hbase.TableName
java_import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder java_import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder
java_import org.apache.hadoop.hbase.client.TableDescriptorBuilder java_import org.apache.hadoop.hbase.client.TableDescriptorBuilder
java_import org.apache.hadoop.hbase.HConstants
# Wrapper for org.apache.hadoop.hbase.client.HBaseAdmin # Wrapper for org.apache.hadoop.hbase.client.HBaseAdmin
@ -1011,7 +1012,7 @@ module Hbase
cfdb.setTimeToLive(arg.delete(ColumnFamilyDescriptorBuilder::TTL)) if arg.include?(ColumnFamilyDescriptorBuilder::TTL) 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.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.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.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.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) cfdb.setCompressTags(JBoolean.valueOf(arg.delete(ColumnFamilyDescriptorBuilder::COMPRESS_TAGS))) if arg.include?(ColumnFamilyDescriptorBuilder::COMPRESS_TAGS)

View File

@ -61,6 +61,9 @@ end
module Hbase module Hbase
# rubocop:disable Metrics/ClassLength # rubocop:disable Metrics/ClassLength
class QuotasAdmin class QuotasAdmin
include HBaseConstants
include HBaseQuotasConstants
def initialize(admin) def initialize(admin)
@admin = admin @admin = admin
end end

View File

@ -23,8 +23,6 @@ java_import org.apache.hadoop.hbase.util.Pair
module Hbase module Hbase
class RSGroupAdmin class RSGroupAdmin
include HBaseConstants
def initialize(connection) def initialize(connection)
@connection = connection @connection = connection
@admin = @connection.getAdmin @admin = @connection.getAdmin
@ -213,11 +211,11 @@ module Hbase
unless arg.is_a?(Hash) unless arg.is_a?(Hash)
raise(ArgumentError, "#{arg.class} of #{arg.inspect} is not of Hash type") raise(ArgumentError, "#{arg.class} of #{arg.inspect} is not of Hash type")
end end
method = arg[METHOD] method = arg[::HBaseConstants::METHOD]
if method == 'unset' if method == 'unset'
configuration.remove(arg[NAME]) configuration.remove(arg[::HBaseConstants::NAME])
elsif method == 'set' elsif method == 'set'
arg.delete(METHOD) arg.delete(::HBaseConstants::METHOD)
for k, v in arg for k, v in arg
v = v.to_s unless v.nil? v = v.to_s unless v.nil?
configuration.put(k, v) configuration.put(k, v)

View File

@ -22,8 +22,6 @@ include Java
module Hbase module Hbase
class SecurityAdmin class SecurityAdmin
include HBaseConstants
def initialize(admin) def initialize(admin)
@admin = admin @admin = admin
@connection = @admin.getConnection @connection = @admin.getConnection

View File

@ -27,8 +27,6 @@ end
module Hbase module Hbase
class TaskMonitor class TaskMonitor
include HBaseConstants
#--------------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------------
# Represents information reported by a server on a single MonitoredTask # Represents information reported by a server on a single MonitoredTask
class Task class Task

View File

@ -116,7 +116,7 @@ module HBaseConstants
promote_constants(org.apache.hadoop.hbase.client.TableDescriptorBuilder.constants) promote_constants(org.apache.hadoop.hbase.client.TableDescriptorBuilder.constants)
end end
# Include classes definition # Ensure that hbase class definitions are imported
require 'hbase/hbase' require 'hbase/hbase'
require 'hbase/admin' require 'hbase/admin'
require 'hbase/taskmonitor' require 'hbase/taskmonitor'
@ -126,5 +126,3 @@ require 'hbase/replication_admin'
require 'hbase/security' require 'hbase/security'
require 'hbase/visibility_labels' require 'hbase/visibility_labels'
require 'hbase/rsgroup_admin' require 'hbase/rsgroup_admin'
include HBaseQuotasConstants

View File

@ -16,6 +16,29 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
require 'irb'
require 'irb/workspace'
#
# 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
# 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 # Shell commands module
module Shell module Shell
@ -115,20 +138,48 @@ module Shell
@rsgroup_admin ||= hbase.rsgroup_admin @rsgroup_admin ||= hbase.rsgroup_admin
end end
def export_commands(where) ##
# 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
# 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| ::Shell.commands.keys.each do |cmd|
# here where is the IRB namespace target.send :define_singleton_method, cmd.to_sym, lambda { |*args|
# this method just adds the call to the specified command ret = shell_inst.command(cmd.to_s, *args)
# 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 puts
return ret ret
}
end end
EOF # Export help method
target.send :define_singleton_method, :help, lambda { |command = nil|
shell_inst.help(command)
nil
}
# Export tools method for backwards compatibility
target.send :define_singleton_method, :tools, lambda {
shell_inst.help_group('tools')
nil
}
end 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 end
def command_instance(command) def command_instance(command)
@ -238,6 +289,61 @@ The HBase shell is the (J)Ruby IRB with the above HBase-specific commands added.
For more on the HBase Shell, see http://hbase.apache.org/book.html For more on the HBase Shell, see http://hbase.apache.org/book.html
HERE HERE
end 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, 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)
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 => 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 e.is_a? java.lang.Exception
warn 'java exception'
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 #{e.class}: #{message}"
if $fullBacktrace
# re-raising the will include a backtrace and exit.
raise e
else
exit 1
end
end
nil
end
end end
# rubocop:enable Metrics/ClassLength # rubocop:enable Metrics/ClassLength
end end

View File

@ -38,7 +38,7 @@ EOF
def command(snapshot_name, table, args = {}) def command(snapshot_name, table, args = {})
raise(ArgumentError, 'Arguments should be a Hash') unless args.is_a?(Hash) 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) admin.clone_snapshot(snapshot_name, table, restore_acl)
end end

View File

@ -48,7 +48,7 @@ EOF
# No QUOTAS if hbase:meta table # No QUOTAS if hbase:meta table
puts puts
formatter.header(%w[QUOTAS]) 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]) formatter.row([quota])
end end
formatter.footer(count) formatter.footer(count)

View File

@ -36,7 +36,7 @@ EOF
ns = namespace.to_s ns = namespace.to_s
if admin.exists?(::HBaseQuotasConstants::QUOTA_TABLE_NAME.to_s) if admin.exists?(::HBaseQuotasConstants::QUOTA_TABLE_NAME.to_s)
puts formatter.header(%w[QUOTAS]) 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]) formatter.row([quota])
end end
formatter.footer(count) formatter.footer(count)

View File

@ -47,7 +47,7 @@ EOF
elsif !options.is_a? Hash elsif !options.is_a? Hash
# When options isn't a hash, assume it's the server name # When options isn't a hash, assume it's the server name
# and create the hash internally # and create the hash internally
options = { SERVER_NAME => options } options = { ::HBaseConstants::SERVER_NAME => options }
end end
raise "Table #{table_name} must be enabled." unless admin.enabled?(table_name) raise "Table #{table_name} must be enabled." unless admin.enabled?(table_name)
@ -85,7 +85,7 @@ EOF
hregion_locator_instance = conn_instance.getRegionLocator(TableName.valueOf(table_name)) hregion_locator_instance = conn_instance.getRegionLocator(TableName.valueOf(table_name))
hregion_locator_list = hregion_locator_instance.getAllRegionLocations.to_a hregion_locator_list = hregion_locator_instance.getAllRegionLocations.to_a
results = [] results = []
desired_server_name = options[SERVER_NAME] desired_server_name = options[::HBaseConstants::SERVER_NAME]
begin begin
# Filter out region servers which we don't want, default to all RS # Filter out region servers which we don't want, default to all RS
@ -93,11 +93,16 @@ EOF
# A locality threshold of "1.0" would be all regions (cannot have greater than 1 locality) # 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 # Regions which have a `dataLocality` less-than-or-equal to this value are accepted
locality_threshold = 1.0 locality_threshold = 1.0
if options.key? LOCALITY_THRESHOLD if options.key? ::HBaseConstants::LOCALITY_THRESHOLD
value = options[LOCALITY_THRESHOLD] value = options[::HBaseConstants::LOCALITY_THRESHOLD]
# Value validation. Must be a Float, and must be between [0, 1.0] # 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 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"
end
unless valid_locality_threshold? value
raise "#{::HBaseConstants::LOCALITY_THRESHOLD} must be between 0 and 1.0, inclusive"
end
locality_threshold = value locality_threshold = value
end end

View File

@ -37,7 +37,7 @@ EOF
def command(snapshot_name, args = {}) def command(snapshot_name, args = {})
raise(ArgumentError, 'Arguments should be a Hash') unless args.is_a?(Hash) 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) admin.restore_snapshot(snapshot_name, restore_acl)
end end
end end

View File

@ -123,31 +123,36 @@ EOF
end end
def command(args = {}) def command(args = {})
if args.key?(TYPE) if args.key?(::HBaseConstants::TYPE)
qtype = args.delete(TYPE) qtype = args.delete(::HBaseConstants::TYPE)
case qtype case qtype
when THROTTLE when ::HBaseQuotasConstants::THROTTLE
if args[LIMIT].eql? NONE if args[::HBaseConstants::LIMIT].eql? ::HBaseConstants::NONE
args.delete(LIMIT) args.delete(::HBaseConstants::LIMIT)
quotas_admin.unthrottle(args) quotas_admin.unthrottle(args)
else else
quotas_admin.throttle(args) quotas_admin.throttle(args)
end end
when SPACE when ::HBaseQuotasConstants::SPACE
if args[LIMIT].eql? NONE if args[::HBaseConstants::LIMIT].eql? ::HBaseConstants::NONE
args.delete(LIMIT) args.delete(::HBaseConstants::LIMIT)
# Table/Namespace argument is verified in remove_space_limit # Table/Namespace argument is verified in remove_space_limit
quotas_admin.remove_space_limit(args) quotas_admin.remove_space_limit(args)
else else
raise(ArgumentError, 'Expected a LIMIT to be provided') unless args.key?(LIMIT) unless args.key?(::HBaseConstants::LIMIT)
raise(ArgumentError, 'Expected a POLICY to be provided') unless args.key?(POLICY) 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) quotas_admin.limit_space(args)
end end
else else
raise 'Invalid TYPE argument. got ' + qtype raise 'Invalid TYPE argument. got ' + qtype
end end
elsif args.key?(GLOBAL_BYPASS) elsif args.key?(::HBaseQuotasConstants::GLOBAL_BYPASS)
quotas_admin.set_global_bypass(args.delete(GLOBAL_BYPASS), args) quotas_admin.set_global_bypass(args.delete(::HBaseQuotasConstants::GLOBAL_BYPASS), args)
else else
raise 'Expected TYPE argument' raise 'Expected TYPE argument'
end end

View File

@ -23,12 +23,11 @@ require 'hbase_constants'
require 'hbase/hbase' require 'hbase/hbase'
require 'hbase/table' require 'hbase/table'
include HBaseConstants
module Hbase module Hbase
# Tests for the `status` shell command # Tests for the `status` shell command
class StatusTest < Test::Unit::TestCase class StatusTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
setup_hbase setup_hbase
@ -126,7 +125,7 @@ module Hbase
define_test "Snapshot should work when SKIP_FLUSH args" do define_test "Snapshot should work when SKIP_FLUSH args" do
drop_test_snapshot() 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) list = command(:list_snapshots, @create_test_snapshot)
assert_equal(1, list.size) assert_equal(1, list.size)
end end
@ -156,7 +155,7 @@ module Hbase
assert_match(/f1/, admin.describe(restore_table)) assert_match(/f1/, admin.describe(restore_table))
assert_match(/f2/, admin.describe(restore_table)) assert_match(/f2/, admin.describe(restore_table))
command(:snapshot, restore_table, @create_test_snapshot) 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_no_match(/f1/, admin.describe(restore_table))
assert_match(/f2/, admin.describe(restore_table)) assert_match(/f2/, admin.describe(restore_table))
drop_test_table(restore_table) drop_test_table(restore_table)

View File

@ -23,7 +23,6 @@ require 'hbase_constants'
require 'hbase/hbase' require 'hbase/hbase'
require 'hbase/table' require 'hbase/table'
include HBaseConstants
module Hbase module Hbase
class AdminHelpersTest < Test::Unit::TestCase class AdminHelpersTest < Test::Unit::TestCase
@ -63,6 +62,8 @@ module Hbase
# rubocop:disable Metrics/ClassLength # rubocop:disable Metrics/ClassLength
class AdminMethodsTest < Test::Unit::TestCase class AdminMethodsTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
include HBaseQuotasConstants
def setup def setup
setup_hbase setup_hbase
@ -550,9 +551,9 @@ module Hbase
ns = @create_test_name ns = @create_test_name
command(:create_namespace, ns) command(:create_namespace, ns)
command(:set_quota, command(:set_quota,
TYPE => SPACE, TYPE => ::HBaseQuotasConstants::SPACE,
LIMIT => '1G', LIMIT => '1G',
POLICY => NO_INSERTS, POLICY => ::HBaseQuotasConstants::NO_INSERTS,
NAMESPACE => ns) NAMESPACE => ns)
output = capture_stdout { command(:describe_namespace, ns) } output = capture_stdout { command(:describe_namespace, ns) }
puts output puts output
@ -567,8 +568,8 @@ module Hbase
assert(output.include?('1 row(s)')) assert(output.include?('1 row(s)'))
command(:set_quota, command(:set_quota,
TYPE => SPACE, TYPE => ::HBaseQuotasConstants::SPACE,
LIMIT => NONE, LIMIT => ::HBaseConstants::NONE,
NAMESPACE => ns) NAMESPACE => ns)
output = capture_stdout { command(:describe_namespace, ns) } output = capture_stdout { command(:describe_namespace, ns) }
assert(output.include?('0 row(s)')) assert(output.include?('0 row(s)'))
@ -675,6 +676,8 @@ module Hbase
# Simple administration methods tests # Simple administration methods tests
class AdminCloneTableSchemaTest < Test::Unit::TestCase class AdminCloneTableSchemaTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
setup_hbase setup_hbase
# Create table test table name # Create table test table name
@ -758,6 +761,8 @@ module Hbase
# Simple administration methods tests # Simple administration methods tests
class AdminRegionTest < Test::Unit::TestCase class AdminRegionTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
setup_hbase setup_hbase
# Create test table if it does not exist # Create test table if it does not exist
@ -846,6 +851,7 @@ module Hbase
# rubocop:disable Metrics/ClassLength # rubocop:disable Metrics/ClassLength
class AdminAlterTableTest < Test::Unit::TestCase class AdminAlterTableTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
setup_hbase setup_hbase

View File

@ -18,8 +18,6 @@
require 'shell' require 'shell'
require 'hbase_constants' require 'hbase_constants'
include HBaseConstants
java_import 'org.apache.hadoop.hbase.HRegionLocation' java_import 'org.apache.hadoop.hbase.HRegionLocation'
java_import 'org.apache.hadoop.hbase.client.RegionInfoBuilder' java_import 'org.apache.hadoop.hbase.client.RegionInfoBuilder'
java_import 'org.apache.hadoop.hbase.ServerName' java_import 'org.apache.hadoop.hbase.ServerName'
@ -27,6 +25,7 @@ java_import 'org.apache.hadoop.hbase.ServerName'
module Hbase module Hbase
class NoClusterListRegionsTest < Test::Unit::TestCase class NoClusterListRegionsTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
define_test 'valid_locality_values' do define_test 'valid_locality_values' do
command = ::Shell::Commands::ListRegions.new(nil) command = ::Shell::Commands::ListRegions.new(nil)

View File

@ -23,12 +23,12 @@ require 'hbase_constants'
require 'hbase/hbase' require 'hbase/hbase'
require 'hbase/table' require 'hbase/table'
include HBaseConstants
module Hbase module Hbase
# rubocop:disable Metrics/ClassLength # rubocop:disable Metrics/ClassLength
class SpaceQuotasTest < Test::Unit::TestCase class SpaceQuotasTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
include HBaseQuotasConstants
def setup def setup
setup_hbase setup_hbase

View File

@ -23,8 +23,6 @@ require 'hbase_constants'
require 'hbase/hbase' require 'hbase/hbase'
require 'hbase/table' require 'hbase/table'
include HBaseConstants
java_import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshot java_import org.apache.hadoop.hbase.quotas.SpaceQuotaSnapshot
java_import org.apache.hadoop.hbase.quotas.SpaceViolationPolicy java_import org.apache.hadoop.hbase.quotas.SpaceViolationPolicy
java_import org.apache.hadoop.hbase.TableName java_import org.apache.hadoop.hbase.TableName
@ -32,6 +30,7 @@ java_import org.apache.hadoop.hbase.TableName
module Hbase module Hbase
class NoClusterSpaceQuotasTest < Test::Unit::TestCase class NoClusterSpaceQuotasTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
define_test '_parse_size accepts various forms of byte shorthand' do define_test '_parse_size accepts various forms of byte shorthand' do
qa = ::Hbase::QuotasAdmin.new(nil) qa = ::Hbase::QuotasAdmin.new(nil)

View File

@ -22,7 +22,6 @@ require 'hbase_constants'
require 'hbase/hbase' require 'hbase/hbase'
require 'hbase/table' require 'hbase/table'
include HBaseConstants
include Java include Java
java_import org.apache.hadoop.hbase.replication.SyncReplicationState java_import org.apache.hadoop.hbase.replication.SyncReplicationState
@ -30,6 +29,7 @@ java_import org.apache.hadoop.hbase.replication.SyncReplicationState
module Hbase module Hbase
class ReplicationAdminTest < Test::Unit::TestCase class ReplicationAdminTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
@peer_id = '1' @peer_id = '1'

View File

@ -22,12 +22,11 @@ require 'hbase_constants'
require 'hbase/hbase' require 'hbase/hbase'
require 'hbase/table' require 'hbase/table'
include HBaseConstants
module Hbase module Hbase
# Simple secure administration methods tests # Simple secure administration methods tests
class SecureAdminMethodsTest < Test::Unit::TestCase class SecureAdminMethodsTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
setup_hbase setup_hbase

View File

@ -19,12 +19,12 @@
require 'hbase_constants' require 'hbase_constants'
include HBaseConstants
module Hbase module Hbase
# Constructor tests # Constructor tests
class TableConstructorTest < Test::Unit::TestCase class TableConstructorTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
setup_hbase setup_hbase
end end
@ -97,6 +97,7 @@ module Hbase
# Simple data management methods tests # Simple data management methods tests
class TableSimpleMethodsTest < Test::Unit::TestCase class TableSimpleMethodsTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
setup_hbase setup_hbase
@ -225,6 +226,7 @@ module Hbase
# rubocop:disable Metrics/ClassLength # rubocop:disable Metrics/ClassLength
class TableComplexMethodsTest < Test::Unit::TestCase class TableComplexMethodsTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
setup_hbase setup_hbase

View File

@ -23,11 +23,10 @@ require 'hbase_constants'
require 'hbase/hbase' require 'hbase/hbase'
require 'hbase/table' require 'hbase/table'
include HBaseConstants
module Hbase module Hbase
class NoClusterConnectionTest < Test::Unit::TestCase class NoClusterConnectionTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
puts "starting shell" puts "starting shell"

View File

@ -22,12 +22,11 @@ require 'hbase_constants'
require 'hbase/hbase' require 'hbase/hbase'
require 'hbase/table' require 'hbase/table'
include HBaseConstants
module Hbase module Hbase
# Simple secure administration methods tests # Simple secure administration methods tests
class VisibilityLabelsAdminMethodsTest < Test::Unit::TestCase class VisibilityLabelsAdminMethodsTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
setup_hbase setup_hbase

View File

@ -17,11 +17,10 @@
require 'hbase_constants' require 'hbase_constants'
require 'shell' require 'shell'
include HBaseConstants
module Hbase module Hbase
class ConverterTest < Test::Unit::TestCase class ConverterTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
non_ascii_text = '⻆⻇' non_ascii_text = '⻆⻇'
non_ascii_row = '⻄' non_ascii_row = '⻄'

View File

@ -20,11 +20,10 @@
require 'hbase_constants' require 'hbase_constants'
require 'shell' require 'shell'
include HBaseConstants
module Hbase module Hbase
class ListProceduresTest < Test::Unit::TestCase class ListProceduresTest < Test::Unit::TestCase
include TestHelpers include TestHelpers
include HBaseConstants
def setup def setup
setup_hbase setup_hbase

View File

@ -61,6 +61,46 @@ class ShellTest < Test::Unit::TestCase
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
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 define_test "Shell::Shell#command_instance should return a command class" do
assert_kind_of(Shell::Commands::Command, @shell.command_instance('version')) assert_kind_of(Shell::Commands::Command, @shell.command_instance('version'))
end end
@ -73,6 +113,24 @@ class ShellTest < Test::Unit::TestCase
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
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 define_test 'Shell::Shell#print_banner should display Reference Guide link' do
@shell.interactive = true @shell.interactive = true
output = capture_stdout { @shell.print_banner } output = capture_stdout { @shell.print_banner }

View File

@ -119,7 +119,7 @@ module Hbase
def create_test_table_with_region_replicas(name, num_of_replicas, splits) def create_test_table_with_region_replicas(name, num_of_replicas, splits)
# Create the table if needed # Create the table if needed
unless admin.exists?(name) unless admin.exists?(name)
command(:create, name, 'f1', { REGION_REPLICATION => num_of_replicas }, command(:create, name, 'f1', { ::HBaseConstants::REGION_REPLICATION => num_of_replicas },
splits) splits)
end end