hotdog 0.6.3 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
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