hotdog 0.6.3 → 0.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e940923c28e0c281c17da003c2e2d3e0135efe78
4
- data.tar.gz: 54316787a42899bdfb71c7900eebe7b600dbabe4
3
+ metadata.gz: f1b8937e6ed17779e09622476dae786c9f7e04a4
4
+ data.tar.gz: 59d5cf9204f2b3cf7a272734ca62253f09b01347
5
5
  SHA512:
6
- metadata.gz: ed99d65cefb7ba8ee9a99f8d63855fc6fb234bda876a92bab531dd29574ffdea86c200a4a26e890f7fe18d17e7b8ec02164a69c6929cd12af6b37b5b810c0715
7
- data.tar.gz: 5bf40e301f0567576028475d95017088bff3547ab0a55da2430de8ea9fcdb849ae74202767751a8fb744b5a7505663afb2b842328ee23f5d0ad7daf30ea26993
6
+ metadata.gz: c763c88d2083cb10007195b7962edd980a47f0145a613081b1a5c9713da91ffed3350daa7bdd1317f7e9d00b928141f69a5005e6fa2026deb127a6e6a1b71173
7
+ data.tar.gz: 796c96d4b2c954690fc9bbf97339d8ebe4213fc7c3c92fafc5c5a67b90914c208ad4eea7c3a2b21f5c871dc4562c002ff561b443321d949c24038b3491743caf
@@ -53,44 +53,47 @@ module Hotdog
53
53
  args = @optparse.order(argv)
54
54
 
55
55
  begin
56
- command = ( args.shift || "help" )
57
- get_command(command).tap do |cmd|
58
- @optparse.banner = "Usage: hotdog #{command} [options]"
59
- cmd.define_options(@optparse, @options)
60
- args = cmd.parse_options(@optparse, args)
61
- unless options[:api_key]
62
- raise("DATADOG_API_KEY is not set")
63
- end
64
-
65
- unless options[:application_key]
66
- raise("DATADOG_APPLICATION_KEY is not set")
67
- end
68
-
69
- if options[:format] == "ltsv"
70
- options[:headers] = true
71
- end
72
-
73
- options[:formatter] = get_formatter(options[:format])
74
-
75
- if options[:debug] or options[:verbose]
76
- options[:logger].level = Logger::DEBUG
77
- else
78
- options[:logger].level = Logger::INFO
79
- end
80
-
81
- cmd.run(args, @options)
56
+ command_name = ( args.shift || "help" )
57
+ begin
58
+ command = get_command(command_name)
59
+ rescue NameError
60
+ STDERR.puts("hotdog: '#{command_name}' is not a hotdog command.")
61
+ get_command("help").parse_options(@optparse, ["commands"])
62
+ exit(1)
82
63
  end
83
- rescue OptionParser::ParseError => error
84
- STDERR.puts("hotdog: #{error.message}")
85
- require "hotdog/commands/help"
86
- get_command(command).tap do |cmd|
87
- if Hotdog::Commands::Help === cmd
88
- STDERR.puts("hotdog: '#{command}' is not a hotdog command.")
89
- else
90
- cmd.parse_options(@optparse, ["--help"])
91
- end
64
+
65
+ @optparse.banner = "Usage: hotdog #{command_name} [options]"
66
+ command.define_options(@optparse, @options)
67
+
68
+ begin
69
+ args = command.parse_options(@optparse, args)
70
+ rescue OptionParser::ParseError => error
71
+ STDERR.puts("hotdog: #{error.message}")
72
+ command.parse_options(@optparse, ["--help"])
73
+ exit(1)
74
+ end
75
+
76
+ unless options[:api_key]
77
+ raise("DATADOG_API_KEY is not set")
92
78
  end
93
- exit(1)
79
+
80
+ unless options[:application_key]
81
+ raise("DATADOG_APPLICATION_KEY is not set")
82
+ end
83
+
84
+ if options[:format] == "ltsv"
85
+ options[:headers] = true
86
+ end
87
+
88
+ options[:formatter] = get_formatter(options[:format])
89
+
90
+ if options[:debug] or options[:verbose]
91
+ options[:logger].level = Logger::DEBUG
92
+ else
93
+ options[:logger].level = Logger::INFO
94
+ end
95
+
96
+ command.run(args, @options)
94
97
  rescue Errno::EPIPE
95
98
  # nop
96
99
  end
@@ -174,8 +177,7 @@ module Hotdog
174
177
  load library
175
178
  klass = Hotdog::Commands.const_get(const_name(File.basename(library, ".rb")))
176
179
  else
177
- require "hotdog/commands/help"
178
- klass = Hotdog::Commands::Help
180
+ raise(NameError.new("unknown command: #{name}"))
179
181
  end
180
182
  end
181
183
  klass.new(self)
@@ -6,8 +6,29 @@ module Hotdog
6
6
  module Commands
7
7
  class Help < BaseCommand
8
8
  def run(args=[], options={})
9
- ruby = File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["ruby_install_name"])
10
- exit(system(ruby, $0, "--help") ? 0 : 1)
9
+ commands = command_files.map { |file| File.basename(file, ".rb") }.sort.uniq
10
+ if "commands" == args.first
11
+ STDOUT.puts("hotdog commands are:")
12
+ commands.each do |command|
13
+ STDOUT.puts("- #{command}")
14
+ end
15
+ else
16
+ ruby = File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["ruby_install_name"])
17
+ if commands.include?(args.first)
18
+ exit(system(ruby, $0, args.first, "--help") ? 0 : 1)
19
+ else
20
+ exit(system(ruby, $0, "--help") ? 0 : 1)
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+ def load_path()
27
+ $LOAD_PATH.map { |path| File.join(path, "hotdog/commands") }.select { |path| File.directory?(path) }
28
+ end
29
+
30
+ def command_files()
31
+ load_path.flat_map { |path| Dir.glob(File.join(path, "*.rb")) }.select { |file| File.file?(file) }
11
32
  end
12
33
  end
13
34
  end
@@ -1,130 +1,25 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "json"
4
+ require "parallel"
4
5
  require "parslet"
5
- require "hotdog/commands/search"
6
6
  require "shellwords"
7
- require "parallel"
7
+ require "hotdog/commands/ssh"
8
8
 
9
9
  module Hotdog
10
10
  module Commands
11
- class Pssh < Search
12
- def define_options(optparse, options={})
13
- options[:options] = []
14
- options[:user] = nil
15
- options[:port] = nil
16
- options[:identity_file] = nil
17
- options[:forward_agent] = false
18
- options[:max_parallelism] = nil
19
- options[:color] = :auto
20
-
21
- optparse.on("-o SSH_OPTION", "Passes this string to ssh command through shell. This option may be given multiple times") do |option|
22
- options[:options] += [option]
23
- end
24
- optparse.on("-i SSH_IDENTITY_FILE", "SSH identity file path") do |path|
25
- options[:identity_file] = path
26
- end
27
- optparse.on("-A", "Enable agent forwarding", TrueClass) do |b|
28
- options[:forward_agent] = b
29
- end
30
- optparse.on("-p PORT", "Port of the remote host", Integer) do |port|
31
- options[:port] = port
32
- end
33
- optparse.on("-u SSH_USER", "SSH login user name") do |user|
34
- options[:user] = user
35
- end
36
- optparse.on("-P PARALLELISM", "Max parallelism", Integer) do |n|
37
- options[:max_parallelism] = n
38
- end
39
- optparse.on("-v", "--verbose", "Enable verbose ode") do |v|
40
- options[:verbose] = v
41
- end
42
- optparse.on("--color=WHEN", "--colour=WHEN", "Enable colors") do |color|
43
- options[:color] = color
44
- end
45
- end
46
-
47
- def run(args=[], options={})
48
- expression = args.join(" ").strip
49
- if expression.empty?
50
- # return everything if given expression is empty
51
- expression = "*"
52
- end
53
-
54
- begin
55
- node = parse(expression)
56
- rescue Parslet::ParseFailed => error
57
- STDERR.puts("syntax error: " + error.cause.ascii_tree)
58
- exit(1)
59
- end
60
-
61
- result0 = evaluate(node, self)
62
- if 0 < result0.length
63
- exec_command(result0, options)
64
- else
65
- STDERR.puts("no match found: #{expression}")
66
- exit(1)
67
- end
68
- end
69
-
11
+ class Pssh < SshAlike
70
12
  private
71
- def exec_command(result0, options={})
72
- result, fields = get_hosts(result0)
73
- hosts = result.flatten
74
- threads = options[:max_parallelism] || hosts.size
13
+ def run_main(hosts, options={})
75
14
  use_color_p = use_color?
76
- stats = Parallel.map(hosts.zip(hosts), in_threads: threads) { |host, name|
77
- cmdline = build_command_string(host, options)
78
- logger.debug("execute: #{cmdline}")
79
- IO.popen(cmdline, in: :close, err: [:child, :out]) do |io|
80
- io.each_with_index do |s, i|
81
- STDOUT.write("\e[0;36m") if use_color_p
82
- STDOUT.write("#{name}:#{i}:")
83
- STDOUT.write("\e[0m") if use_color_p
84
- STDOUT.write(s)
85
- end
86
- end
87
- $?.success? # $? is thread-local variable
15
+ stats = Parallel.map(hosts, in_threads: parallelism(hosts)) { |host|
16
+ cmdline = build_command_string(host, @remote_command, options)
17
+ exec_command(host, cmdline, true, use_color_p)
88
18
  }
89
- unless stats.all?
90
- exit(1)
91
- end
92
- end
93
-
94
- def build_command_string(host, options={})
95
- # build ssh command
96
- base_cmdline = ["ssh"]
97
- if options[:forward_agent]
98
- base_cmdline << "-A"
99
- end
100
- if options[:identity_file]
101
- base_cmdline << "-i" << options[:identity_file]
102
- end
103
- if options[:user]
104
- base_cmdline << "-l" << options[:user]
105
- end
106
- base_cmdline << "-o" << "BatchMode=yes"
107
- if options[:options]
108
- base_cmdline += options[:options].flat_map { |option| ["-o", option] }
109
- end
110
- if options[:port]
111
- base_cmdline << "-p" << options[:port].to_s
112
- end
113
- cmdline = base_cmdline + [host]
114
- if @remote_command
115
- cmdline << "--" << @remote_command
116
- end
117
- Shellwords.join(cmdline)
118
- end
119
-
120
- def use_color?
121
- case options[:color]
122
- when :always
123
- true
124
- when :never
125
- false
19
+ if stats.all?
20
+ exit(0)
126
21
  else
127
- STDOUT.tty?
22
+ exit(1)
128
23
  end
129
24
  end
130
25
  end
@@ -742,11 +742,7 @@ module Hotdog
742
742
  def evaluate(environment, options={})
743
743
  values = environment.execute(@query, @values).map { |row| row.first }
744
744
  if values.empty? and @fallback
745
- @fallback.evaluate(environment, options).tap do |values|
746
- if values.empty?
747
- environment.logger.info("no result: #{self.dump.inspect}")
748
- end
749
- end
745
+ @fallback.evaluate(environment, options)
750
746
  else
751
747
  values
752
748
  end
@@ -2,29 +2,21 @@
2
2
 
3
3
  require "json"
4
4
  require "parslet"
5
- require "hotdog/commands/search"
6
5
  require "shellwords"
6
+ require "hotdog/commands/search"
7
7
 
8
8
  module Hotdog
9
9
  module Commands
10
- class Ssh < Search
10
+ class SshAlike < Search
11
11
  def define_options(optparse, options={})
12
- options[:index] = nil
13
12
  options[:options] = []
14
13
  options[:user] = nil
15
14
  options[:port] = nil
16
15
  options[:identity_file] = nil
17
16
  options[:forward_agent] = false
17
+ options[:color] = :auto
18
+ options[:max_parallelism] = nil
18
19
 
19
- optparse.on("-D BIND_ADDRESS", "Specifies a local \"dynamic\" application-level port forwarding") do |bind_address|
20
- options[:dynamic_port_forward] = bind_address
21
- end
22
- optparse.on("-L BIND_ADDRESS", "Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side") do |bind_address|
23
- options[:port_forward] = bind_address
24
- end
25
- optparse.on("-n", "--index INDEX", "Use this index of host if multiple servers are found", Integer) do |index|
26
- options[:index] = index
27
- end
28
20
  optparse.on("-o SSH_OPTION", "Passes this string to ssh command through shell. This option may be given multiple times") do |option|
29
21
  options[:options] += [option]
30
22
  end
@@ -43,6 +35,15 @@ module Hotdog
43
35
  optparse.on("-v", "--verbose", "Enable verbose ode") do |v|
44
36
  options[:verbose] = v
45
37
  end
38
+ optparse.on("--filter=COMMAND", "Command to filter search result.") do |command|
39
+ options[:filter_command] = command
40
+ end
41
+ optparse.on("-P PARALLELISM", "Max parallelism", Integer) do |n|
42
+ options[:max_parallelism] = n
43
+ end
44
+ optparse.on("--color=WHEN", "--colour=WHEN", "Enable colors") do |color|
45
+ options[:color] = color
46
+ end
46
47
  end
47
48
 
48
49
  def run(args=[], options={})
@@ -60,73 +61,161 @@ module Hotdog
60
61
  end
61
62
 
62
63
  result0 = evaluate(node, self)
63
- if 0 < result0.length
64
- if result0.length == 1
65
- exec_command([result0.first], options)
64
+ result, fields = get_hosts_with_search_tags(result0, node)
65
+ hosts = filter_hosts(result.flatten)
66
+ validate_hosts!(hosts)
67
+ run_main(hosts, options)
68
+ end
69
+
70
+ private
71
+ def parallelism(hosts)
72
+ options[:max_parallelism] || hosts.size
73
+ end
74
+
75
+ def filter_hosts(hosts)
76
+ if options[:filter_command]
77
+ use_color_p = use_color?
78
+ filtered_hosts = Parallel.map(hosts, in_threads: parallelism(hosts)) { |host|
79
+ cmdline = build_command_string(host, options[:filter_command], options)
80
+ [host, exec_command(host, cmdline, false, use_color_p)]
81
+ }.select { |host, stat|
82
+ stat
83
+ }.map { |host, stat|
84
+ host
85
+ }
86
+ if hosts == filtered_hosts
87
+ hosts
66
88
  else
67
- if options[:index] and options[:index] < result0.length
68
- exec_command([result0[options[:index]]], options)
69
- else
70
- result, fields = get_hosts_with_search_tags(result0, node)
71
-
72
- # add "index" field
73
- result = result.each_with_index.map { |host, i| [i] + host }
74
- fields = ["index"] + fields
75
-
76
- STDERR.print(format(result, fields: fields))
77
- logger.info("found %d host(s)." % result.length)
78
- exit(1)
79
- end
89
+ logger.info("filtered host(s): #{(hosts - filtered_hosts).inspect}")
90
+ filtered_hosts
80
91
  end
81
92
  else
82
- STDERR.puts("no match found: #{expression}")
93
+ hosts
94
+ end
95
+ end
96
+
97
+ def validate_hosts!(hosts)
98
+ if hosts.length < 1
99
+ STDERR.puts("no match found")
83
100
  exit(1)
84
101
  end
85
- exit(127)
86
102
  end
87
103
 
88
- private
89
- def exec_command(result0, options={})
90
- result, fields = get_hosts(result0)
91
- hosts = result.flatten
92
- cmdline = build_command_string(hosts.first, options)
93
- logger.debug("execute: #{cmdline}")
94
- exec(cmdline)
104
+ def run_main(hosts, options={})
105
+ raise(NotImplementedError)
95
106
  end
96
107
 
97
- def build_command_string(host, options={})
98
- # build ssh command
99
- base_cmdline = ["ssh"]
108
+ def build_command_options(options={})
109
+ cmdline = []
100
110
  if options[:forward_agent]
101
- base_cmdline << "-A"
102
- end
103
- if options[:dynamic_port_forward]
104
- base_cmdline << "-D" << options[:dynamic_port_forward]
105
- end
106
- if options[:port_forward]
107
- base_cmdline << "-L" << options[:port_forward]
111
+ cmdline << "-A"
108
112
  end
109
113
  if options[:identity_file]
110
- base_cmdline << "-i" << options[:identity_file]
114
+ cmdline << "-i" << options[:identity_file]
111
115
  end
112
116
  if options[:user]
113
- base_cmdline << "-l" << options[:user]
117
+ cmdline << "-l" << options[:user]
114
118
  end
115
119
  if options[:options]
116
- base_cmdline += options[:options].flat_map { |option| ["-o", option] }
120
+ cmdline += options[:options].flat_map { |option| ["-o", option] }
117
121
  end
118
122
  if options[:port]
119
- base_cmdline << "-p" << options[:port].to_s
123
+ cmdline << "-p" << options[:port].to_s
120
124
  end
121
125
  if options[:verbose]
122
- base_cmdline << "-v"
126
+ cmdline << "-v"
123
127
  end
124
- cmdline = base_cmdline + [host]
125
- if @remote_command
126
- cmdline << "--" << @remote_command
128
+ cmdline
129
+ end
130
+
131
+ def build_command_string(host, command=nil, options={})
132
+ # build ssh command
133
+ cmdline = ["ssh"] + build_command_options(options) + [host]
134
+ if command
135
+ cmdline << "--" << command
127
136
  end
128
137
  Shellwords.join(cmdline)
129
138
  end
139
+
140
+ def use_color?
141
+ case options[:color]
142
+ when :always
143
+ true
144
+ when :never
145
+ false
146
+ else
147
+ STDOUT.tty?
148
+ end
149
+ end
150
+
151
+ def exec_command(identifier, cmdline, output=true, colorize=false)
152
+ logger.debug("execute: #{cmdline}")
153
+ IO.popen(cmdline, in: :close, err: [:child, :out]) do |io|
154
+ io.each_with_index do |s, i|
155
+ if output
156
+ STDOUT.write("\e[0;36m") if colorize
157
+ STDOUT.write("#{identifier}:#{i}:")
158
+ STDOUT.write("\e[0m") if colorize
159
+ STDOUT.write(s)
160
+ end
161
+ end
162
+ end
163
+ $?.success? # $? is thread-local variable
164
+ end
165
+ end
166
+
167
+ class Ssh < SshAlike
168
+ def define_options(optparse, options={})
169
+ super
170
+ options[:index] = nil
171
+ optparse.on("-D BIND_ADDRESS", "Specifies a local \"dynamic\" application-level port forwarding") do |bind_address|
172
+ options[:dynamic_port_forward] = bind_address
173
+ end
174
+ optparse.on("-L BIND_ADDRESS", "Specifies that the given port on the local (client) host is to be forwarded to the given host and port on the remote side") do |bind_address|
175
+ options[:port_forward] = bind_address
176
+ end
177
+ optparse.on("-n", "--index INDEX", "Use this index of host if multiple servers are found", Integer) do |index|
178
+ options[:index] = index
179
+ end
180
+ end
181
+
182
+ private
183
+ def filter_hosts(hosts)
184
+ hosts = super
185
+ if options[:index] and options[:index] < hosts.length
186
+ [hosts[options[:index]]]
187
+ else
188
+ hosts
189
+ end
190
+ end
191
+
192
+ def validate_hosts!(hosts)
193
+ super
194
+ if hosts.length != 1
195
+ result = hosts.each_with_index.map { |host, i| [i, host] }
196
+ STDERR.print(format(result, fields: ["index", "host"]))
197
+ logger.error("found %d hosts." % result.length)
198
+ exit(1)
199
+ end
200
+ end
201
+
202
+ def run_main(hosts, options={})
203
+ cmdline = build_command_string(hosts.first, @remote_command, options)
204
+ logger.debug("execute: #{cmdline}")
205
+ exec(cmdline)
206
+ exit(127)
207
+ end
208
+
209
+ def build_command_options(options={})
210
+ arguments = super
211
+ if options[:dynamic_port_forward]
212
+ arguments << "-D" << options[:dynamic_port_forward]
213
+ end
214
+ if options[:port_forward]
215
+ arguments << "-L" << options[:port_forward]
216
+ end
217
+ arguments
218
+ end
130
219
  end
131
220
  end
132
221
  end
@@ -1,3 +1,3 @@
1
1
  module Hotdog
2
- VERSION = "0.6.3"
2
+ VERSION = "0.6.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hotdog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.3
4
+ version: 0.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yamashita Yuu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-14 00:00:00.000000000 Z
11
+ date: 2015-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler