elasticshell 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +119 -55
- data/VERSION +1 -1
- data/bin/es +0 -2
- data/lib/elasticshell.rb +30 -25
- data/lib/elasticshell/client.rb +34 -13
- data/lib/elasticshell/command.rb +14 -170
- data/lib/elasticshell/commands/blank.rb +14 -0
- data/lib/elasticshell/commands/cd.rb +27 -0
- data/lib/elasticshell/commands/connect.rb +31 -0
- data/lib/elasticshell/commands/df.rb +23 -0
- data/lib/elasticshell/commands/help.rb +77 -0
- data/lib/elasticshell/commands/ls.rb +66 -0
- data/lib/elasticshell/commands/pretty.rb +21 -0
- data/lib/elasticshell/commands/pwd.rb +17 -0
- data/lib/elasticshell/commands/request.rb +81 -0
- data/lib/elasticshell/commands/request_parser.rb +77 -0
- data/lib/elasticshell/commands/set_verb.rb +23 -0
- data/lib/elasticshell/commands/unknown.rb +17 -0
- data/lib/elasticshell/scopes.rb +81 -50
- data/lib/elasticshell/scopes/cluster.rb +8 -16
- data/lib/elasticshell/scopes/global.rb +22 -23
- data/lib/elasticshell/scopes/index.rb +35 -29
- data/lib/elasticshell/scopes/mapping.rb +8 -28
- data/lib/elasticshell/scopes/nodes.rb +6 -15
- data/lib/elasticshell/shell.rb +155 -93
- data/lib/elasticshell/utils.rb +10 -0
- data/lib/elasticshell/{error.rb → utils/error.rb} +1 -0
- data/lib/elasticshell/utils/has_name.rb +14 -0
- data/lib/elasticshell/utils/has_verb.rb +15 -0
- data/lib/elasticshell/{log.rb → utils/log.rb} +1 -1
- data/lib/elasticshell/utils/recognizes_verb.rb +25 -0
- data/spec/elasticshell/client_spec.rb +55 -0
- data/spec/elasticshell/commands/blank_spec.rb +14 -0
- data/spec/elasticshell/commands/cd_spec.rb +23 -0
- data/spec/elasticshell/commands/connect_spec.rb +21 -0
- data/spec/elasticshell/commands/df_spec.rb +18 -0
- data/spec/elasticshell/commands/help_spec.rb +21 -0
- data/spec/elasticshell/commands/ls_spec.rb +24 -0
- data/spec/elasticshell/commands/pretty_spec.rb +19 -0
- data/spec/elasticshell/commands/pwd_spec.rb +14 -0
- data/spec/elasticshell/commands/request_parser_spec.rb +4 -0
- data/spec/elasticshell/commands/request_spec.rb +60 -0
- data/spec/elasticshell/commands/set_verb_spec.rb +14 -0
- data/spec/elasticshell/scopes_spec.rb +79 -0
- data/spec/elasticshell/shell_spec.rb +19 -0
- data/spec/elasticshell/utils/has_name_spec.rb +15 -0
- data/spec/elasticshell/utils/has_verb_spec.rb +24 -0
- data/spec/elasticshell/utils/recognizes_verb_spec.rb +23 -0
- data/spec/spec_helper.rb +4 -5
- data/spec/support/data.yml +45 -0
- data/spec/support/fake_output.rb +27 -0
- metadata +73 -4
@@ -0,0 +1,27 @@
|
|
1
|
+
module Elasticshell
|
2
|
+
module Commands
|
3
|
+
class Cd < Command
|
4
|
+
|
5
|
+
def self.matches? input
|
6
|
+
input =~ /^cd/
|
7
|
+
end
|
8
|
+
|
9
|
+
def evaluate!
|
10
|
+
if input =~ /^cd$/
|
11
|
+
shell.scope = Scopes.global(:client => shell.client)
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
15
|
+
return unless input =~ /^cd\s+(.+)$/
|
16
|
+
scope = $1
|
17
|
+
if scope =~ %r!^/!
|
18
|
+
shell.scope = Scopes.from_path(scope, :client => shell.client)
|
19
|
+
else
|
20
|
+
shell.scope = Scopes.from_path(File.expand_path(File.join(shell.scope.path, scope)), :client => shell.client)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Elasticshell
|
4
|
+
module Commands
|
5
|
+
class Connect < Command
|
6
|
+
|
7
|
+
def self.matches? input
|
8
|
+
input =~ /^connect(?: |$)/i
|
9
|
+
end
|
10
|
+
|
11
|
+
def evaluate!
|
12
|
+
servers = (input.split(/\s+/, 2)[1] || '').split(/\s+/)
|
13
|
+
if servers.empty?
|
14
|
+
shell.client.connect()
|
15
|
+
else
|
16
|
+
servers.each do |server|
|
17
|
+
begin
|
18
|
+
uri = URI.parse(server + "/")
|
19
|
+
rescue => e
|
20
|
+
raise ArgumentError.new("#{server} is not a valid URI")
|
21
|
+
end
|
22
|
+
raise ArgumentError.new("#{server} is not a valid URI for an ElasticSearch server") unless uri.path == '/'
|
23
|
+
end
|
24
|
+
shell.client.connect(:servers => servers)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Elasticshell
|
2
|
+
module Commands
|
3
|
+
class Df < Command
|
4
|
+
|
5
|
+
def self.matches? input
|
6
|
+
input =~ /^df/i
|
7
|
+
end
|
8
|
+
|
9
|
+
def evaluate!
|
10
|
+
be_connected!
|
11
|
+
global = shell.scope_from_path("/")
|
12
|
+
global.status["indices"].each_pair do |index_name, data|
|
13
|
+
size = data["index"]["size_in_bytes"]
|
14
|
+
human_size = data["index"]["size"]
|
15
|
+
shell.print("%6s %6s \e[32m%s\e[0m" % [size, human_size, index_name])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Elasticshell
|
2
|
+
module Commands
|
3
|
+
class Help < Command
|
4
|
+
|
5
|
+
def self.matches? input
|
6
|
+
input =~ /^help/i
|
7
|
+
end
|
8
|
+
|
9
|
+
def long?
|
10
|
+
input =~ /help +help/i
|
11
|
+
end
|
12
|
+
|
13
|
+
def evaluate!
|
14
|
+
if long?
|
15
|
+
shell.print <<HEADER
|
16
|
+
|
17
|
+
INTRODUCTION
|
18
|
+
|
19
|
+
Elasticshell wraps Elasticsearch's HTTP REST API with a convenient
|
20
|
+
command-line shell.
|
21
|
+
|
22
|
+
Elasticshell will try by default to connect to a locally running copy
|
23
|
+
of Elasticsearch on its default port (9200) when it starts up. If
|
24
|
+
Elasticshell cannot connect on startup, it will print an error
|
25
|
+
message. You can make Elasticshell connect to a different set of
|
26
|
+
servers using the `connect' command below.
|
27
|
+
|
28
|
+
COMMANDS
|
29
|
+
HEADER
|
30
|
+
end
|
31
|
+
shell.print <<HELP
|
32
|
+
|
33
|
+
pwd
|
34
|
+
Print the current scope.
|
35
|
+
|
36
|
+
connect [SERVER[,SERVER]...]
|
37
|
+
Connect to the given list of comma-separated Elasticseach servers.
|
38
|
+
|
39
|
+
ls
|
40
|
+
Show what common requests or child scopes are within the
|
41
|
+
current scope. Try `ll' for a long listing.
|
42
|
+
|
43
|
+
cd [SCOPE]
|
44
|
+
Change to the given scope. Current scope is reflected in the
|
45
|
+
prompt (it's `#{shell.scope.path}' right now).
|
46
|
+
|
47
|
+
[get|post|put|delete]
|
48
|
+
Set the default HTTP verb (currently `#{shell.verb}').
|
49
|
+
|
50
|
+
df
|
51
|
+
Show a brief listing of disk usage by index.
|
52
|
+
|
53
|
+
help
|
54
|
+
Show contextual help. Try `help help' for even more detail.
|
55
|
+
|
56
|
+
[VERB] PATH[?QUERY] [BODY]
|
57
|
+
Send an HTTP request using the given VERB to the given PATH, including
|
58
|
+
QUERY string and BODY if given. BODY can be the name of a local file on
|
59
|
+
disk or `-' to read from STDIN. If no verb is given, use the default
|
60
|
+
verb (currently `#{shell.verb}').
|
61
|
+
|
62
|
+
HELP
|
63
|
+
shell.print("Try `help help' for more detailed help with examples.") unless long?
|
64
|
+
if long?
|
65
|
+
shell.print <<FOOTER
|
66
|
+
|
67
|
+
EXAMPLES
|
68
|
+
TBD.
|
69
|
+
FOOTER
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Elasticshell
|
2
|
+
module Commands
|
3
|
+
class Ls < Command
|
4
|
+
|
5
|
+
attr_accessor :scope
|
6
|
+
|
7
|
+
def self.matches? input
|
8
|
+
input =~ /^l(s|l|a)?(?: .+)?$/i
|
9
|
+
end
|
10
|
+
|
11
|
+
def evaluate!
|
12
|
+
be_connected!
|
13
|
+
if input =~ /^l(?:s|l|a)? +(.+)$/
|
14
|
+
self.scope = shell.scope_from_path($1)
|
15
|
+
else
|
16
|
+
self.scope = shell.scope
|
17
|
+
end
|
18
|
+
self.scope.refresh!
|
19
|
+
input =~ /^l(?:l|a)/ ? ll! : ls!
|
20
|
+
end
|
21
|
+
|
22
|
+
def sort array
|
23
|
+
with_underscores = array.find_all { |element| element =~ /^_/ }
|
24
|
+
without_underscores = array.find_all { |element| element =~ /^[^_]/ }
|
25
|
+
without_underscores.sort + with_underscores.sort
|
26
|
+
end
|
27
|
+
|
28
|
+
def ll!
|
29
|
+
sort(scope.scopes).each do |scope_name|
|
30
|
+
case
|
31
|
+
when scope.path == '/' && scope.indices.include?(scope_name)
|
32
|
+
index = shell.scope_from_path("/#{scope_name}")
|
33
|
+
total_shards = index.status["_shards"]["total"]
|
34
|
+
succ_shards = index.status["_shards"]["successful"]
|
35
|
+
fail_shards = index.status["_shards"]["failed"]
|
36
|
+
size = index.status["indices"][scope_name]["index"]["size_in_bytes"]
|
37
|
+
human_size = index.status["indices"][scope_name]["index"]["size"]
|
38
|
+
num_docs = index.status["indices"][scope_name]["docs"]["num_docs"]
|
39
|
+
shell.print("i %10s %6s %6s \e[32m%s\e[0m" % ["#{total_shards}/#{succ_shards}/#{fail_shards}", num_docs, human_size, scope_name])
|
40
|
+
when scope.class == Scopes::Index && scope.mappings.include?(scope_name)
|
41
|
+
shell.print("m \e[32m%s\e[0m" % [scope_name])
|
42
|
+
else
|
43
|
+
shell.print shell.format(:scope_long_format, "%s", scope_name)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
sort(scope.request_names).each do |request|
|
47
|
+
shell.print shell.format(:request_long_format, "%r", request)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def ls!
|
52
|
+
output = []
|
53
|
+
sort(scope.scopes).map do |scope|
|
54
|
+
output << shell.format(:scope_format, "%s", scope)
|
55
|
+
end
|
56
|
+
sort(scope.request_names).map do |request|
|
57
|
+
output << shell.format(:request_format, "%r", request)
|
58
|
+
end
|
59
|
+
shell.print output.join(' ')
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Elasticshell
|
2
|
+
module Commands
|
3
|
+
class Pretty < Command
|
4
|
+
|
5
|
+
def self.matches? input
|
6
|
+
input =~ /^pretty/i
|
7
|
+
end
|
8
|
+
|
9
|
+
def evaluate!
|
10
|
+
if shell.pretty?
|
11
|
+
shell.not_pretty!
|
12
|
+
else
|
13
|
+
shell.pretty!
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Elasticshell
|
2
|
+
module Commands
|
3
|
+
class Request < Command
|
4
|
+
|
5
|
+
extend RecognizesVerb
|
6
|
+
|
7
|
+
attr_accessor :response, :request
|
8
|
+
|
9
|
+
def self.matches? input
|
10
|
+
input =~ Regexp.new("^(#{verb_re}\s+)?.", true)
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse!
|
14
|
+
self.request = Parser.new(self).request
|
15
|
+
end
|
16
|
+
|
17
|
+
def perform_request!
|
18
|
+
self.response = shell.client.request(request[:verb], {:op => request[:path] }, request[:query_options].merge(:log => shell.log_requests?), request[:body])
|
19
|
+
end
|
20
|
+
|
21
|
+
def pipe?
|
22
|
+
pipe_to_ruby? || pipe_to_irb? || pipe_to_file?
|
23
|
+
end
|
24
|
+
|
25
|
+
def pipe_to_file?
|
26
|
+
input =~ /\s>\s*.+$/
|
27
|
+
end
|
28
|
+
|
29
|
+
def output_filename
|
30
|
+
input =~ /\s>\s*(.+)$/
|
31
|
+
$1
|
32
|
+
end
|
33
|
+
|
34
|
+
def pipe_to_file!
|
35
|
+
File.open(output_filename, 'w') { |f| f.puts response.to_s }
|
36
|
+
end
|
37
|
+
|
38
|
+
def pipe_to_irb?
|
39
|
+
input =~ /\s\|\s*$/
|
40
|
+
end
|
41
|
+
|
42
|
+
def irb!
|
43
|
+
require 'ripl'
|
44
|
+
Ripl.start(:binding => binding)
|
45
|
+
end
|
46
|
+
|
47
|
+
def pipe_to_ruby?
|
48
|
+
input =~ /\s\|\s*\S+/
|
49
|
+
end
|
50
|
+
|
51
|
+
def ruby_code
|
52
|
+
return unless pipe?
|
53
|
+
input =~ /\s\|(.*)$/
|
54
|
+
$1.to_s
|
55
|
+
end
|
56
|
+
|
57
|
+
def ruby!
|
58
|
+
eval(ruby_code, binding)
|
59
|
+
end
|
60
|
+
|
61
|
+
def evaluate!
|
62
|
+
be_connected!
|
63
|
+
parse!
|
64
|
+
perform_request!
|
65
|
+
case
|
66
|
+
when pipe_to_irb?
|
67
|
+
irb!
|
68
|
+
when pipe_to_ruby?
|
69
|
+
ruby!
|
70
|
+
when pipe_to_file?
|
71
|
+
pipe_to_file!
|
72
|
+
else
|
73
|
+
shell.print(response)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
require 'elasticshell/commands/request_parser'
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Elasticshell
|
2
|
+
module Commands
|
3
|
+
class Request
|
4
|
+
|
5
|
+
class Parser
|
6
|
+
|
7
|
+
include RecognizesVerb
|
8
|
+
|
9
|
+
attr_accessor :command, :raw, :verb, :request_string, :raw_path, :raw_body, :path, :query_options, :body
|
10
|
+
|
11
|
+
def initialize(command)
|
12
|
+
self.command = command
|
13
|
+
end
|
14
|
+
|
15
|
+
def request
|
16
|
+
parse!
|
17
|
+
{ :verb => verb, :path => path, :query_options => query_options, :body => body }
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse!
|
21
|
+
strip_redirect!
|
22
|
+
split_verb_and_request!
|
23
|
+
split_path_and_body!
|
24
|
+
interpret_path!
|
25
|
+
construct_body!
|
26
|
+
end
|
27
|
+
|
28
|
+
def strip_redirect!
|
29
|
+
self.raw = command.shell.input.gsub(/ (?:\||>).*$/,'')
|
30
|
+
end
|
31
|
+
|
32
|
+
def strip_file_redirect!
|
33
|
+
self.raw = command.shell.input.gsub(/ >.*$/,'')
|
34
|
+
end
|
35
|
+
|
36
|
+
def split_verb_and_request!
|
37
|
+
if raw =~ Regexp.new("^(#{verb_re})\s+(.+)$", true)
|
38
|
+
self.verb, self.request_string = canonicalize_verb($1), $2
|
39
|
+
else
|
40
|
+
self.verb, self.request_string = command.shell.verb, raw
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def split_path_and_body!
|
45
|
+
self.raw_path, self.raw_body = request_string.split(/ /, 2)
|
46
|
+
end
|
47
|
+
|
48
|
+
def interpret_path!
|
49
|
+
relative_path, query_string = raw_path.split('?')
|
50
|
+
self.path = File.expand_path(relative_path, command.shell.scope.path)
|
51
|
+
|
52
|
+
self.query_options = {}
|
53
|
+
URI.decode_www_form(query_string || '').each do |k, v|
|
54
|
+
self.query_options[k] = v
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def construct_body!
|
59
|
+
self.body = case
|
60
|
+
when raw_body.nil?
|
61
|
+
''
|
62
|
+
when raw_body == '-'
|
63
|
+
command.shell.error.puts("Reading request body from STDIN. Press `C-d' to terminate input...")
|
64
|
+
command.shell.input_stream.gets(nil)
|
65
|
+
when File.exist?(raw_body)
|
66
|
+
File.read(raw_body)
|
67
|
+
else
|
68
|
+
raw_body
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|