hotdog 0.5.4 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/hotdog.gemspec +3 -1
- data/lib/hotdog/application.rb +13 -7
- data/lib/hotdog/commands/down.rb +6 -6
- data/lib/hotdog/commands/pssh.rb +79 -42
- data/lib/hotdog/commands/search.rb +9 -9
- data/lib/hotdog/commands/ssh.rb +58 -38
- data/lib/hotdog/commands/up.rb +2 -2
- data/lib/hotdog/commands.rb +85 -134
- data/lib/hotdog/version.rb +1 -1
- data/spec/core/application_spec.rb +3 -3
- data/spec/optparse/down_spec.rb +49 -0
- data/spec/optparse/hosts_spec.rb +33 -0
- data/spec/optparse/pssh_spec.rb +76 -0
- data/spec/optparse/search_spec.rb +33 -0
- data/spec/optparse/ssh_spec.rb +76 -0
- data/spec/optparse/tags_spec.rb +33 -0
- data/spec/optparse/up_spec.rb +33 -0
- metadata +52 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3bcc93be20391d227013b6c05bb9113d68bc404
|
4
|
+
data.tar.gz: 3a8bc479c3122dc4b18ecf37d575e7b49dd8c1f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c9646d739e72b331cdfdf145da4e93b0c44598b64618a20db48eff13185a9bc277550581f2288e20ee548c8001466c454d30d43df900d9cef8e773bcf5f4bef
|
7
|
+
data.tar.gz: d51c0697ca67251ded20b0c2807af26ead5d4f7ed87b3c189ee109ff464c2a5cfccc82efeff20ec8f6cdcd0a2a07ab4ee5c1f374d1df4c14cdf9d5b98994fe03
|
data/hotdog.gemspec
CHANGED
@@ -23,7 +23,9 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_development_dependency "rspec", "~> 3.3.0"
|
24
24
|
|
25
25
|
spec.add_dependency "dogapi", ">= 1.13.0"
|
26
|
+
spec.add_dependency "multi_json", "~> 1.11.2"
|
27
|
+
spec.add_dependency "oj", "~> 2.12.14"
|
28
|
+
spec.add_dependency "parallel", "~> 1.6.1"
|
26
29
|
spec.add_dependency "parslet", "~> 1.6.2"
|
27
30
|
spec.add_dependency "sqlite3", "~> 1.3.10"
|
28
|
-
spec.add_dependency "parallel", "~> 1.4.1"
|
29
31
|
end
|
data/lib/hotdog/application.rb
CHANGED
@@ -13,6 +13,7 @@ module Hotdog
|
|
13
13
|
@optparse = OptionParser.new
|
14
14
|
@optparse.version = Hotdog::VERSION
|
15
15
|
@options = {
|
16
|
+
endpoint: ENV.fetch("DATADOG_HOST", "https://app.datadoghq.com"),
|
16
17
|
api_key: ENV["DATADOG_API_KEY"],
|
17
18
|
application_key: ENV["DATADOG_APPLICATION_KEY"],
|
18
19
|
application: self,
|
@@ -53,7 +54,7 @@ module Hotdog
|
|
53
54
|
|
54
55
|
begin
|
55
56
|
command = ( args.shift || "help" )
|
56
|
-
get_command(command).
|
57
|
+
get_command(command).tap do |cmd|
|
57
58
|
@optparse.banner = "Usage: hotdog #{command} [options]"
|
58
59
|
cmd.define_options(@optparse, @options)
|
59
60
|
args = cmd.parse_options(@optparse, args)
|
@@ -69,7 +70,7 @@ module Hotdog
|
|
69
70
|
options[:headers] = true
|
70
71
|
end
|
71
72
|
|
72
|
-
options[:formatter] = get_formatter(options[:format])
|
73
|
+
options[:formatter] = get_formatter(options[:format])
|
73
74
|
|
74
75
|
if options[:debug] or options[:verbose]
|
75
76
|
options[:logger].level = Logger::DEBUG
|
@@ -86,6 +87,9 @@ module Hotdog
|
|
86
87
|
|
87
88
|
private
|
88
89
|
def define_options
|
90
|
+
@optparse.on("--endpoint ENDPOINT", "Datadog API endpoint") do |endpoint|
|
91
|
+
options[:endpoint] = endpoint
|
92
|
+
end
|
89
93
|
@optparse.on("--api-key API_KEY", "Datadog API key") do |api_key|
|
90
94
|
options[:api_key] = api_key
|
91
95
|
end
|
@@ -139,29 +143,31 @@ module Hotdog
|
|
139
143
|
|
140
144
|
def get_formatter(name)
|
141
145
|
begin
|
142
|
-
Hotdog::Formatters.const_get(const_name(name))
|
146
|
+
klass = Hotdog::Formatters.const_get(const_name(name))
|
143
147
|
rescue NameError
|
144
148
|
if library = find_library("hotdog/formatters", name)
|
145
149
|
load library
|
146
|
-
Hotdog::Formatters.const_get(const_name(File.basename(library, ".rb")))
|
150
|
+
klass = Hotdog::Formatters.const_get(const_name(File.basename(library, ".rb")))
|
147
151
|
else
|
148
152
|
raise(NameError.new("unknown format: #{name}"))
|
149
153
|
end
|
150
154
|
end
|
155
|
+
klass.new
|
151
156
|
end
|
152
157
|
|
153
158
|
def get_command(name)
|
154
159
|
begin
|
155
|
-
Hotdog::Commands.const_get(const_name(name))
|
160
|
+
klass = Hotdog::Commands.const_get(const_name(name))
|
156
161
|
rescue NameError
|
157
162
|
if library = find_library("hotdog/commands", name)
|
158
163
|
load library
|
159
|
-
Hotdog::Commands.const_get(const_name(File.basename(library, ".rb")))
|
164
|
+
klass = Hotdog::Commands.const_get(const_name(File.basename(library, ".rb")))
|
160
165
|
else
|
161
166
|
require "hotdog/commands/help"
|
162
|
-
Hotdog::Commands::Help
|
167
|
+
klass = Hotdog::Commands::Help
|
163
168
|
end
|
164
169
|
end
|
170
|
+
klass.new(self)
|
165
171
|
end
|
166
172
|
|
167
173
|
def find_library(dirname, name)
|
data/lib/hotdog/commands/down.rb
CHANGED
@@ -6,13 +6,13 @@ module Hotdog
|
|
6
6
|
module Commands
|
7
7
|
class Down < BaseCommand
|
8
8
|
def define_options(optparse, options={})
|
9
|
-
|
10
|
-
|
9
|
+
options[:downtime] = 86400
|
10
|
+
options[:start] = Time.new
|
11
11
|
optparse.on("--downtime DURATION") do |v|
|
12
|
-
|
12
|
+
options[:downtime] = v.to_i
|
13
13
|
end
|
14
14
|
optparse.on("--start TIME") do |v|
|
15
|
-
|
15
|
+
options[:start] = Time.parse(v)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -23,8 +23,8 @@ module Hotdog
|
|
23
23
|
else
|
24
24
|
scope = arg
|
25
25
|
end
|
26
|
-
code, schedule =
|
27
|
-
logger.debug("dog.schedule_donwtime(%s, :start => %s, :end => %s) #==> [%s, %s]" % [scope.inspect,
|
26
|
+
code, schedule = dog.schedule_downtime(scope, :start => options[:start].to_i, :end => (options[:start]+options[:downtime]).to_i)
|
27
|
+
logger.debug("dog.schedule_donwtime(%s, :start => %s, :end => %s) #==> [%s, %s]" % [scope.inspect, options[:start].to_i, (options[:start]+options[:downtime]).to_i, code.inspect, schedule.inspect])
|
28
28
|
if code.to_i / 100 != 2
|
29
29
|
raise("dog.schedule_downtime(%s, ...) returns [%s, %s]" % [scope.inspect, code.inspect, schedule.inspect])
|
30
30
|
end
|
data/lib/hotdog/commands/pssh.rb
CHANGED
@@ -14,7 +14,9 @@ module Hotdog
|
|
14
14
|
options[:user] = nil
|
15
15
|
options[:port] = nil
|
16
16
|
options[:identity_file] = nil
|
17
|
+
options[:forward_agent] = false
|
17
18
|
options[:max_parallelism] = nil
|
19
|
+
options[:color] = :auto
|
18
20
|
|
19
21
|
optparse.on("-o SSH_OPTION", "Passes this string to ssh command through shell. This option may be given multiple times") do |option|
|
20
22
|
options[:options] += [option]
|
@@ -22,6 +24,9 @@ module Hotdog
|
|
22
24
|
optparse.on("-i SSH_IDENTITY_FILE", "SSH identity file path") do |path|
|
23
25
|
options[:identity_file] = path
|
24
26
|
end
|
27
|
+
optparse.on("-A", "Enable agent forwarding", TrueClass) do |b|
|
28
|
+
options[:forward_agent] = b
|
29
|
+
end
|
25
30
|
optparse.on("-p PORT", "Port of the remote host", Integer) do |port|
|
26
31
|
options[:port] = port
|
27
32
|
end
|
@@ -31,13 +36,30 @@ module Hotdog
|
|
31
36
|
optparse.on("-P PARALLELISM", "Max parallelism", Integer) do |n|
|
32
37
|
options[:max_parallelism] = n
|
33
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
|
34
45
|
end
|
35
46
|
|
47
|
+
def parse_options(optparse, args=[])
|
48
|
+
if args.index("--")
|
49
|
+
@remote_command = args.slice(args.index("--") + 1, args.length).join(" ")
|
50
|
+
optparse.parse(args.slice(0, args.index("--")))
|
51
|
+
else
|
52
|
+
@remote_command = nil
|
53
|
+
optparse.parse(args)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
attr_reader :remote_command
|
57
|
+
|
36
58
|
def run(args=[], options={})
|
37
|
-
use_color = STDOUT.tty?
|
38
59
|
expression = args.join(" ").strip
|
39
|
-
if expression.empty?
|
40
|
-
|
60
|
+
if expression.empty?
|
61
|
+
# return everything if given expression is empty
|
62
|
+
expression = "*"
|
41
63
|
end
|
42
64
|
|
43
65
|
begin
|
@@ -47,61 +69,76 @@ module Hotdog
|
|
47
69
|
exit(1)
|
48
70
|
end
|
49
71
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
STDERR.puts("no match found: #{
|
72
|
+
result0 = evaluate(node, self)
|
73
|
+
if 0 < result0.length
|
74
|
+
exec_command(result0, options)
|
75
|
+
else
|
76
|
+
STDERR.puts("no match found: #{expression}")
|
55
77
|
exit(1)
|
56
78
|
end
|
79
|
+
end
|
57
80
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
if path = options[:identity_file]
|
66
|
-
cmdline << "-i" << Shellwords.escape(path)
|
67
|
-
end
|
68
|
-
if port = options[:port]
|
69
|
-
cmdline << "-p" << port.to_s
|
70
|
-
end
|
71
|
-
if options[:forward_agent]
|
72
|
-
cmdline << "-A"
|
73
|
-
end
|
74
|
-
|
75
|
-
cmdline << "-o" << "BatchMode=yes"
|
76
|
-
|
77
|
-
user = options[:user]
|
78
|
-
|
79
|
-
threads = options[:max_parallelism] || addresses.size
|
80
|
-
stats = Parallel.map(addresses, in_threads: threads) { |address,name|
|
81
|
-
if use_color
|
81
|
+
def exec_command(result0, options={})
|
82
|
+
result, fields = get_hosts(result0)
|
83
|
+
hosts = result.flatten
|
84
|
+
threads = options[:max_parallelism] || hosts.size
|
85
|
+
stats = Parallel.map(hosts.zip(hosts), in_threads: threads) { |host, name|
|
86
|
+
if use_color?
|
82
87
|
header = "\e[0;36m#{name}\e[00m"
|
83
88
|
else
|
84
89
|
header = name
|
85
90
|
end
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
else
|
90
|
-
c << address
|
91
|
-
end
|
92
|
-
logger.debug("execute: #{Shellwords.join(c)}")
|
93
|
-
IO.popen([*c, in: :close, err: [:child, :out]]) do |io|
|
91
|
+
cmdline = build_command_string(host, options)
|
92
|
+
logger.debug("execute: #{cmdline}")
|
93
|
+
IO.popen(cmdline, in: :close, err: [:child, :out]) do |io|
|
94
94
|
io.each_line do |line|
|
95
|
-
STDOUT.write
|
95
|
+
STDOUT.write("#{header}: #{line}")
|
96
96
|
end
|
97
97
|
end
|
98
98
|
$?.success? # $? is thread-local variable
|
99
99
|
}
|
100
|
-
|
101
100
|
unless stats.all?
|
102
101
|
exit(1)
|
103
102
|
end
|
104
103
|
end
|
104
|
+
|
105
|
+
def build_command_string(host, options={})
|
106
|
+
# build ssh command
|
107
|
+
base_cmdline = ["ssh"]
|
108
|
+
if options[:forward_agent]
|
109
|
+
base_cmdline << "-A"
|
110
|
+
end
|
111
|
+
if options[:identity_file]
|
112
|
+
base_cmdline << "-i" << options[:identity_file]
|
113
|
+
end
|
114
|
+
if options[:user]
|
115
|
+
base_cmdline << "-l" << options[:user]
|
116
|
+
end
|
117
|
+
base_cmdline << "-o" << "BatchMode=yes"
|
118
|
+
if options[:options]
|
119
|
+
base_cmdline += options[:options].flat_map { |option| ["-o", option] }
|
120
|
+
end
|
121
|
+
if options[:port]
|
122
|
+
base_cmdline << "-p" << options[:port].to_s
|
123
|
+
end
|
124
|
+
cmdline = base_cmdline + [host]
|
125
|
+
if @remote_command
|
126
|
+
cmdline << "--" << @remote_command
|
127
|
+
end
|
128
|
+
Shellwords.join(cmdline)
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
def use_color?
|
133
|
+
case options[:color]
|
134
|
+
when :always
|
135
|
+
true
|
136
|
+
when :never
|
137
|
+
false
|
138
|
+
else
|
139
|
+
STDOUT.tty?
|
140
|
+
end
|
141
|
+
end
|
105
142
|
end
|
106
143
|
end
|
107
144
|
end
|
@@ -26,18 +26,18 @@ module Hotdog
|
|
26
26
|
exit(1)
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
if 0 <
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
logger.info("found %d host(s)." % result.length)
|
29
|
+
result0 = evaluate(node, self)
|
30
|
+
if 0 < result0.length
|
31
|
+
result, fields = get_hosts_with_search_tags(result0, node)
|
32
|
+
if options[:limit]
|
33
|
+
STDOUT.print(format(result.take(options[:limit]), fields: fields))
|
34
|
+
logger.info("found %d host(s), limited to %d in result." % [result.length, options[:limit]])
|
36
35
|
else
|
37
|
-
|
36
|
+
STDOUT.print(format(result, fields: fields))
|
37
|
+
logger.info("found %d host(s)." % result.length)
|
38
38
|
end
|
39
39
|
else
|
40
|
-
STDERR.puts("no match found: #{
|
40
|
+
STDERR.puts("no match found: #{expression}")
|
41
41
|
exit(1)
|
42
42
|
end
|
43
43
|
end
|
data/lib/hotdog/commands/ssh.rb
CHANGED
@@ -15,7 +15,6 @@ module Hotdog
|
|
15
15
|
options[:port] = nil
|
16
16
|
options[:identity_file] = nil
|
17
17
|
options[:forward_agent] = false
|
18
|
-
options[:verbose] = false
|
19
18
|
|
20
19
|
optparse.on("-n", "--index INDEX", "Use this index of host if multiple servers are found", Integer) do |index|
|
21
20
|
options[:index] = index
|
@@ -40,10 +39,22 @@ module Hotdog
|
|
40
39
|
end
|
41
40
|
end
|
42
41
|
|
42
|
+
def parse_options(optparse, args=[])
|
43
|
+
if args.index("--")
|
44
|
+
@remote_command = args.slice(args.index("--") + 1, args.length).join(" ")
|
45
|
+
optparse.parse(args.slice(0, args.index("--")))
|
46
|
+
else
|
47
|
+
@remote_command = nil
|
48
|
+
optparse.parse(args)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
attr_reader :remote_command
|
52
|
+
|
43
53
|
def run(args=[], options={})
|
44
54
|
expression = args.join(" ").strip
|
45
55
|
if expression.empty?
|
46
|
-
|
56
|
+
# return everything if given expression is empty
|
57
|
+
expression = "*"
|
47
58
|
end
|
48
59
|
|
49
60
|
begin
|
@@ -53,57 +64,66 @@ module Hotdog
|
|
53
64
|
exit(1)
|
54
65
|
end
|
55
66
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
elsif result.empty?
|
61
|
-
STDERR.puts("no match found: #{search_args.join(" ")}")
|
62
|
-
exit(1)
|
63
|
-
else
|
64
|
-
if options[:index] && result.length > options[:index]
|
65
|
-
host = result[options[:index]]
|
67
|
+
result0 = evaluate(node, self)
|
68
|
+
if 0 < result0.length
|
69
|
+
if result0.length == 1
|
70
|
+
exec_command([result0.first], options)
|
66
71
|
else
|
67
|
-
|
72
|
+
if options[:index] and options[:index] < result0.length
|
73
|
+
exec_command([result0[options[:index]]], options)
|
74
|
+
else
|
75
|
+
result, fields = get_hosts_with_search_tags(result0, node)
|
68
76
|
|
69
|
-
|
70
|
-
|
71
|
-
|
77
|
+
# add "index" field
|
78
|
+
result = result.each_with_index.map { |host, i| [i] + host }
|
79
|
+
fields = ["index"] + fields
|
72
80
|
|
73
|
-
|
74
|
-
|
75
|
-
|
81
|
+
STDERR.print(format(result, fields: fields))
|
82
|
+
logger.info("found %d host(s)." % result.length)
|
83
|
+
exit(1)
|
84
|
+
end
|
76
85
|
end
|
86
|
+
else
|
87
|
+
STDERR.puts("no match found: #{expression}")
|
88
|
+
exit(1)
|
77
89
|
end
|
90
|
+
exit(127)
|
91
|
+
end
|
78
92
|
|
79
|
-
|
80
|
-
|
93
|
+
def exec_command(result0, options={})
|
94
|
+
result, fields = get_hosts(result0)
|
95
|
+
hosts = result.flatten
|
96
|
+
cmdline = build_command_string(hosts.first, options)
|
97
|
+
logger.debug("execute: #{cmdline}")
|
98
|
+
exec(cmdline)
|
99
|
+
end
|
81
100
|
|
101
|
+
def build_command_string(host, options={})
|
82
102
|
# build ssh command
|
83
|
-
|
84
|
-
options[:
|
85
|
-
|
103
|
+
base_cmdline = ["ssh"]
|
104
|
+
if options[:forward_agent]
|
105
|
+
base_cmdline << "-A"
|
86
106
|
end
|
87
|
-
if
|
88
|
-
|
107
|
+
if options[:identity_file]
|
108
|
+
base_cmdline << "-i" << options[:identity_file]
|
89
109
|
end
|
90
|
-
if
|
91
|
-
|
110
|
+
if options[:user]
|
111
|
+
base_cmdline << "-l" << options[:user]
|
92
112
|
end
|
93
|
-
if options[:
|
94
|
-
|
113
|
+
if options[:options]
|
114
|
+
base_cmdline += options[:options].flat_map { |option| ["-o", option] }
|
115
|
+
end
|
116
|
+
if options[:port]
|
117
|
+
base_cmdline << "-p" << options[:port].to_s
|
95
118
|
end
|
96
119
|
if options[:verbose]
|
97
|
-
|
120
|
+
base_cmdline << "-v"
|
98
121
|
end
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
cmdline << address
|
122
|
+
cmdline = base_cmdline + [host]
|
123
|
+
if @remote_command
|
124
|
+
cmdline << "--" << @remote_command
|
103
125
|
end
|
104
|
-
|
105
|
-
exec(*cmdline)
|
106
|
-
exit(127)
|
126
|
+
Shellwords.join(cmdline)
|
107
127
|
end
|
108
128
|
end
|
109
129
|
end
|
data/lib/hotdog/commands/up.rb
CHANGED
@@ -13,7 +13,7 @@ module Hotdog
|
|
13
13
|
arg
|
14
14
|
end
|
15
15
|
}
|
16
|
-
code, all_downtimes =
|
16
|
+
code, all_downtimes = dog.get_all_downtimes()
|
17
17
|
if code.to_i / 100 != 2
|
18
18
|
raise("dog.get_all_downtimes() returns [%s, %s]" % [code.inspect, all_downtimes.inspect])
|
19
19
|
end
|
@@ -23,7 +23,7 @@ module Hotdog
|
|
23
23
|
}
|
24
24
|
|
25
25
|
cancel_downtimes.each do |downtime|
|
26
|
-
code, cancel =
|
26
|
+
code, cancel = dog.cancel_downtime(downtime["id"])
|
27
27
|
if code.to_i / 100 != 2
|
28
28
|
raise("dog.cancel_downtime(%s) returns [%s, %s]" % [downtime["id"].inspect, code.inspect, cancel.inspect])
|
29
29
|
end
|
data/lib/hotdog/commands.rb
CHANGED
@@ -2,8 +2,12 @@
|
|
2
2
|
|
3
3
|
require "fileutils"
|
4
4
|
require "dogapi"
|
5
|
-
require "
|
5
|
+
require "multi_json"
|
6
|
+
require "oj"
|
7
|
+
require "open-uri"
|
8
|
+
require "parallel"
|
6
9
|
require "sqlite3"
|
10
|
+
require "uri"
|
7
11
|
|
8
12
|
module Hotdog
|
9
13
|
module Commands
|
@@ -18,7 +22,7 @@ module Hotdog
|
|
18
22
|
@application = application
|
19
23
|
@logger = application.options[:logger]
|
20
24
|
@options = application.options
|
21
|
-
@dog =
|
25
|
+
@dog = nil # lazy initialization
|
22
26
|
@prepared_statements = {}
|
23
27
|
end
|
24
28
|
attr_reader :application
|
@@ -31,13 +35,7 @@ module Hotdog
|
|
31
35
|
|
32
36
|
def execute(q, args=[])
|
33
37
|
update_db
|
34
|
-
|
35
|
-
logger.debug("execute: #{q} -- #{args.inspect}")
|
36
|
-
prepare(@db, q).execute(args)
|
37
|
-
rescue
|
38
|
-
logger.error("failed: #{q} -- #{args.inspect}")
|
39
|
-
raise
|
40
|
-
end
|
38
|
+
execute_db(@db, q, args)
|
41
39
|
end
|
42
40
|
|
43
41
|
def fixed_string?()
|
@@ -183,9 +181,12 @@ module Hotdog
|
|
183
181
|
if (not options[:force] and File.exist?(persistent) and Time.new < File.mtime(persistent) + options[:expiry]) or options[:offline]
|
184
182
|
begin
|
185
183
|
persistent_db = SQLite3::Database.new(persistent)
|
186
|
-
persistent_db.execute(
|
187
|
-
|
188
|
-
|
184
|
+
persistent_db.execute(<<-EOS)
|
185
|
+
SELECT hosts_tags.host_id FROM hosts_tags
|
186
|
+
INNER JOIN hosts ON hosts_tags.host_id = hosts.id
|
187
|
+
INNER JOIN tags ON hosts_tags.tag_id = tags.id
|
188
|
+
LIMIT 1;
|
189
|
+
EOS
|
189
190
|
@db = persistent_db
|
190
191
|
return
|
191
192
|
rescue SQLite3::SQLException
|
@@ -198,12 +199,28 @@ module Hotdog
|
|
198
199
|
end
|
199
200
|
|
200
201
|
memory_db = SQLite3::Database.new(":memory:")
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
202
|
+
execute_db(memory_db, <<-EOS)
|
203
|
+
CREATE TABLE IF NOT EXISTS hosts (
|
204
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
205
|
+
name VARCHAR(255) NOT NULL COLLATE NOCASE
|
206
|
+
);
|
207
|
+
EOS
|
208
|
+
execute_db(memory_db, "CREATE UNIQUE INDEX IF NOT EXISTS hosts_name ON hosts ( name );")
|
209
|
+
execute_db(memory_db, <<-EOS)
|
210
|
+
CREATE TABLE IF NOT EXISTS tags (
|
211
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
212
|
+
name VARCHAR(200) NOT NULL COLLATE NOCASE,
|
213
|
+
value VARCHAR(200) NOT NULL COLLATE NOCASE
|
214
|
+
);
|
215
|
+
EOS
|
216
|
+
execute_db(memory_db, "CREATE UNIQUE INDEX IF NOT EXISTS tags_name_value ON tags ( name, value );")
|
217
|
+
execute_db(memory_db, <<-EOS)
|
218
|
+
CREATE TABLE IF NOT EXISTS hosts_tags (
|
219
|
+
host_id INTEGER NOT NULL,
|
220
|
+
tag_id INTEGER NOT NULL
|
221
|
+
);
|
222
|
+
EOS
|
223
|
+
execute_db(memory_db, "CREATE UNIQUE INDEX IF NOT EXISTS hosts_tags_host_id_tag_id ON hosts_tags ( host_id, tag_id );")
|
207
224
|
|
208
225
|
all_tags = get_all_tags()
|
209
226
|
|
@@ -211,23 +228,13 @@ module Hotdog
|
|
211
228
|
known_tags = all_tags.keys.map { |tag| split_tag(tag) }.uniq
|
212
229
|
known_tags.each_slice(SQLITE_LIMIT_COMPOUND_SELECT / 2) do |known_tags|
|
213
230
|
q = "INSERT OR IGNORE INTO tags (name, value) VALUES %s" % known_tags.map { "(?, ?)" }.join(", ")
|
214
|
-
|
215
|
-
prepare(memory_db, q).execute(known_tags)
|
216
|
-
rescue
|
217
|
-
logger.error("failed: #{q} -- #{known_tags.inspect}")
|
218
|
-
raise
|
219
|
-
end
|
231
|
+
execute_db(memory_db, q, known_tags)
|
220
232
|
end
|
221
233
|
|
222
234
|
known_hosts = all_tags.values.reduce(:+).uniq
|
223
235
|
known_hosts.each_slice(SQLITE_LIMIT_COMPOUND_SELECT) do |known_hosts|
|
224
236
|
q = "INSERT OR IGNORE INTO hosts (name) VALUES %s" % known_hosts.map { "(?)" }.join(", ")
|
225
|
-
|
226
|
-
prepare(memory_db, q).execute(known_hosts)
|
227
|
-
rescue
|
228
|
-
logger.error("failed: #{q} -- #{known_hosts.inspect}")
|
229
|
-
raise
|
230
|
-
end
|
237
|
+
execute_db(memory_db, q, known_hosts)
|
231
238
|
end
|
232
239
|
|
233
240
|
all_tags.each do |tag, hosts|
|
@@ -237,10 +244,18 @@ module Hotdog
|
|
237
244
|
"( SELECT id FROM hosts WHERE name IN (%s) ) AS host, " \
|
238
245
|
"( SELECT id FROM tags WHERE name = ? AND value = ? LIMIT 1 ) AS tag;" % hosts.map { "?" }.join(", ")
|
239
246
|
begin
|
240
|
-
|
241
|
-
rescue
|
242
|
-
|
243
|
-
|
247
|
+
execute_db(memory_db, q, (hosts + split_tag(tag)))
|
248
|
+
rescue SQLite3::RangeException => error
|
249
|
+
# FIXME: bulk insert occationally fails even if there are no errors in bind parameters
|
250
|
+
# `bind_param': bind or column index out of range (SQLite3::RangeException)
|
251
|
+
logger.warn("bulk insert failed due to #{error.message}. fallback to normal insert.")
|
252
|
+
hosts.each do |host|
|
253
|
+
q = "INSERT OR REPLACE INTO hosts_tags (host_id, tag_id) " \
|
254
|
+
"SELECT host.id, tag.id FROM " \
|
255
|
+
"( SELECT id FROM hosts WHERE name = ? ) AS host, " \
|
256
|
+
"( SELECT id FROM tags WHERE name = ? AND value = ? LIMIT 1 ) AS tag;"
|
257
|
+
execute_db(memory_db, q, [host] + split_tag(tag))
|
258
|
+
end
|
244
259
|
end
|
245
260
|
end
|
246
261
|
end
|
@@ -257,19 +272,37 @@ module Hotdog
|
|
257
272
|
end
|
258
273
|
end
|
259
274
|
|
260
|
-
def
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
275
|
+
def execute_db(db, q, args=[])
|
276
|
+
begin
|
277
|
+
logger.debug("execute: #{q} -- #{args.inspect}")
|
278
|
+
prepare(db, q).execute(args)
|
279
|
+
rescue
|
280
|
+
logger.error("failed: #{q} -- #{args.inspect}")
|
281
|
+
raise
|
265
282
|
end
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
283
|
+
end
|
284
|
+
|
285
|
+
def get_all_tags() #==> Hash<Tag,Array<Host>>
|
286
|
+
endpoint = options[:endpoint]
|
287
|
+
requests = {all_downtime: "/api/v1/downtime", all_tags: "/api/v1/tags/hosts"}
|
288
|
+
query = URI.encode_www_form(api_key: options[:api_key], application_key: options[:application_key])
|
289
|
+
begin
|
290
|
+
responses = Hash[Parallel.map(requests) { |name, request_path|
|
291
|
+
uri = URI.join(endpoint, "#{request_path}?#{query}")
|
292
|
+
begin
|
293
|
+
response = uri.open("User-Agent" => "hotdog/#{Hotdog::VERSION}") { |fp| fp.read }
|
294
|
+
[name, MultiJson.load(response)]
|
295
|
+
rescue OpenURI::HTTPError => error
|
296
|
+
code, body = error.io.status
|
297
|
+
raise(RuntimeError.new("dog.get_#{name}() returns [#{code.inspect}, ...]"))
|
298
|
+
end
|
299
|
+
}]
|
300
|
+
rescue => error
|
301
|
+
STDERR.puts(error.message)
|
302
|
+
exit(1)
|
270
303
|
end
|
271
304
|
now = Time.new.to_i
|
272
|
-
downtimes =
|
305
|
+
downtimes = responses.fetch(:all_downtime, []).select { |downtime|
|
273
306
|
# active downtimes
|
274
307
|
downtime["active"] and ( downtime["start"].nil? or downtime["start"] < now ) and ( downtime["end"].nil? or now <= downtime["end"] )
|
275
308
|
}.flat_map { |downtime|
|
@@ -279,7 +312,11 @@ module Hotdog
|
|
279
312
|
if not downtimes.empty?
|
280
313
|
logger.info("ignore host(s) with scheduled downtimes: #{downtimes.inspect}")
|
281
314
|
end
|
282
|
-
Hash[all_tags
|
315
|
+
Hash[responses.fetch(:all_tags, {}).fetch("tags", []).map { |tag, hosts| [tag, hosts.reject { |host| downtimes.include?(host) }] }]
|
316
|
+
end
|
317
|
+
|
318
|
+
def dog()
|
319
|
+
@dog ||= Dogapi::Client.new(options[:api_key], options[:application_key])
|
283
320
|
end
|
284
321
|
|
285
322
|
def split_tag(tag)
|
@@ -296,95 +333,9 @@ module Hotdog
|
|
296
333
|
end
|
297
334
|
|
298
335
|
def copy_db(src, dst)
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
create_table_tags(dst)
|
303
|
-
create_table_hosts_tags(dst)
|
304
|
-
|
305
|
-
hosts = prepare(src, "SELECT id, name FROM hosts").execute().to_a
|
306
|
-
hosts.each_slice(SQLITE_LIMIT_COMPOUND_SELECT / 2) do |hosts|
|
307
|
-
q = "INSERT INTO hosts (id, name) VALUES %s" % hosts.map { "(?, ?)" }.join(", ")
|
308
|
-
begin
|
309
|
-
prepare(dst, q).execute(hosts)
|
310
|
-
rescue
|
311
|
-
logger.error("failed: #{q} -- #{hosts.inspect}")
|
312
|
-
raise
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
tags = prepare(src, "SELECT id, name, value FROM tags").execute().to_a
|
317
|
-
tags.each_slice(SQLITE_LIMIT_COMPOUND_SELECT / 3) do |tags|
|
318
|
-
q = "INSERT INTO tags (id, name, value) VALUES %s" % tags.map { "(?, ?, ?)" }.join(", ")
|
319
|
-
begin
|
320
|
-
prepare(dst, q).execute(tags)
|
321
|
-
rescue
|
322
|
-
logger.error("failed: #{q} -- #{tags.inspect}")
|
323
|
-
raise
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
hosts_tags = prepare(src, "SELECT host_id, tag_id FROM hosts_tags").to_a
|
328
|
-
hosts_tags.each_slice(SQLITE_LIMIT_COMPOUND_SELECT / 2) do |hosts_tags|
|
329
|
-
q = "INSERT INTO hosts_tags (host_id, tag_id) VALUES %s" % hosts_tags.map { "(?, ?)" }.join(", ")
|
330
|
-
begin
|
331
|
-
prepare(dst, q).execute(hosts_tags)
|
332
|
-
rescue
|
333
|
-
logger.error("failed: #{q} -- #{hosts_tags.inspect}")
|
334
|
-
raise
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
create_index_hosts(dst)
|
339
|
-
create_index_tags(dst)
|
340
|
-
create_index_hosts_tags(dst)
|
341
|
-
end
|
342
|
-
end
|
343
|
-
|
344
|
-
def create_table_hosts(db)
|
345
|
-
q = "CREATE TABLE IF NOT EXISTS hosts ( " \
|
346
|
-
"id INTEGER PRIMARY KEY AUTOINCREMENT, " \
|
347
|
-
"name VARCHAR(255) NOT NULL COLLATE NOCASE " \
|
348
|
-
");"
|
349
|
-
logger.debug(q)
|
350
|
-
db.execute(q)
|
351
|
-
end
|
352
|
-
|
353
|
-
def create_index_hosts(db)
|
354
|
-
q = "CREATE UNIQUE INDEX IF NOT EXISTS hosts_name ON hosts ( name );"
|
355
|
-
logger.debug(q)
|
356
|
-
db.execute(q)
|
357
|
-
end
|
358
|
-
|
359
|
-
def create_table_tags(db)
|
360
|
-
q = "CREATE TABLE IF NOT EXISTS tags ( " \
|
361
|
-
"id INTEGER PRIMARY KEY AUTOINCREMENT, " \
|
362
|
-
"name VARCHAR(200) NOT NULL COLLATE NOCASE, " \
|
363
|
-
"value VARCHAR(200) NOT NULL COLLATE NOCASE " \
|
364
|
-
");"
|
365
|
-
logger.debug(q)
|
366
|
-
db.execute(q)
|
367
|
-
end
|
368
|
-
|
369
|
-
def create_index_tags(db)
|
370
|
-
q = "CREATE UNIQUE INDEX IF NOT EXISTS tags_name_value ON tags ( name, value );"
|
371
|
-
logger.debug(q)
|
372
|
-
db.execute(q)
|
373
|
-
end
|
374
|
-
|
375
|
-
def create_table_hosts_tags(db)
|
376
|
-
q = "CREATE TABLE IF NOT EXISTS hosts_tags ( " \
|
377
|
-
"host_id INTEGER NOT NULL, " \
|
378
|
-
"tag_id INTEGER NOT NULL " \
|
379
|
-
");"
|
380
|
-
logger.debug(q)
|
381
|
-
db.execute(q)
|
382
|
-
end
|
383
|
-
|
384
|
-
def create_index_hosts_tags(db)
|
385
|
-
q = "CREATE UNIQUE INDEX IF NOT EXISTS hosts_tags_host_id_tag_id ON hosts_tags ( host_id, tag_id );"
|
386
|
-
logger.debug(q)
|
387
|
-
db.execute(q)
|
336
|
+
backup = SQLite3::Backup.new(dst, "main", src, "main")
|
337
|
+
backup.step(-1)
|
338
|
+
backup.finish
|
388
339
|
end
|
389
340
|
end
|
390
341
|
end
|
data/lib/hotdog/version.rb
CHANGED
@@ -23,9 +23,9 @@ describe "application" do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it "returns proper class by name" do
|
26
|
-
expect(app.__send__(:get_command, "hosts")).to
|
27
|
-
expect(app.__send__(:get_command, "search")).to
|
28
|
-
expect(app.__send__(:get_command, "tags")).to
|
26
|
+
expect(app.__send__(:get_command, "hosts")).to be_a(Hotdog::Commands::Hosts)
|
27
|
+
expect(app.__send__(:get_command, "search")).to be_a(Hotdog::Commands::Search)
|
28
|
+
expect(app.__send__(:get_command, "tags")).to be_a(Hotdog::Commands::Tags)
|
29
29
|
end
|
30
30
|
|
31
31
|
it "raises error if the action is base-command" do
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "hotdog/application"
|
3
|
+
require "hotdog/commands/down"
|
4
|
+
|
5
|
+
describe "option parser for down" do
|
6
|
+
let(:app) {
|
7
|
+
Hotdog::Application.new
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:cmd) {
|
11
|
+
Hotdog::Commands::Down.new(app)
|
12
|
+
}
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
allow(app).to receive(:get_command).with("down") {
|
16
|
+
cmd
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
it "cannot handle subcommand options before subcommand" do
|
21
|
+
expect {
|
22
|
+
app.main(["--downtime", "86400", "down", "foo", "bar", "baz"])
|
23
|
+
}.to raise_error(OptionParser::InvalidOption)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "can handle subcommand options after subcommand" do
|
27
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
28
|
+
downtime: 12345,
|
29
|
+
verbose: false,
|
30
|
+
))
|
31
|
+
app.main(["down", "--downtime", "12345", "foo", "bar", "baz"])
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can handle common options before subcommand" do
|
35
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
36
|
+
downtime: 12345,
|
37
|
+
verbose: true,
|
38
|
+
))
|
39
|
+
app.main(["--verbose", "down", "--downtime", "12345", "foo", "bar", "baz"])
|
40
|
+
end
|
41
|
+
|
42
|
+
it "can handle common options after subcommand" do
|
43
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
44
|
+
downtime: 12345,
|
45
|
+
verbose: true,
|
46
|
+
))
|
47
|
+
app.main(["down", "--downtime", "12345", "--verbose", "foo", "bar", "baz"])
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "hotdog/application"
|
3
|
+
require "hotdog/commands/hosts"
|
4
|
+
|
5
|
+
describe "option parser for hosts" do
|
6
|
+
let(:app) {
|
7
|
+
Hotdog::Application.new
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:cmd) {
|
11
|
+
Hotdog::Commands::Hosts.new(app)
|
12
|
+
}
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
allow(app).to receive(:get_command).with("hosts") {
|
16
|
+
cmd
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can handle common options before subcommand" do
|
21
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
22
|
+
verbose: true,
|
23
|
+
))
|
24
|
+
app.main(["--verbose", "hosts", "foo", "bar", "baz"])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can handle common options after subcommand" do
|
28
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
29
|
+
verbose: true,
|
30
|
+
))
|
31
|
+
app.main(["hosts", "--verbose", "foo", "bar", "baz"])
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "hotdog/application"
|
3
|
+
require "hotdog/commands/pssh"
|
4
|
+
|
5
|
+
describe "option parser for pssh" do
|
6
|
+
let(:app) {
|
7
|
+
Hotdog::Application.new
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:cmd) {
|
11
|
+
Hotdog::Commands::Pssh.new(app)
|
12
|
+
}
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
allow(app).to receive(:get_command).with("pssh") {
|
16
|
+
cmd
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
it "cannot handle subcommand options before subcommand" do
|
21
|
+
expect {
|
22
|
+
app.main(["-P", "42", "pssh", "foo", "bar", "baz"])
|
23
|
+
}.to raise_error(OptionParser::InvalidOption)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "can handle subcommand options after subcommand" do
|
27
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
28
|
+
max_parallelism: 42,
|
29
|
+
verbose: false,
|
30
|
+
))
|
31
|
+
app.main(["pssh", "-P", "42", "foo", "bar", "baz"])
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can handle common options before subcommand" do
|
35
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
36
|
+
max_parallelism: 42,
|
37
|
+
verbose: true,
|
38
|
+
))
|
39
|
+
app.main(["--verbose", "pssh", "-P", "42", "foo", "bar", "baz"])
|
40
|
+
end
|
41
|
+
|
42
|
+
it "can handle common options after subcommand" do
|
43
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
44
|
+
max_parallelism: 42,
|
45
|
+
verbose: true,
|
46
|
+
))
|
47
|
+
app.main(["pssh", "-P", "42", "--verbose", "foo", "bar", "baz"])
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can handle subcommand options with remote command, 1" do
|
51
|
+
allow(cmd).to receive(:run).with([], a_hash_including(
|
52
|
+
max_parallelism: 42,
|
53
|
+
verbose: true,
|
54
|
+
))
|
55
|
+
app.main(["pssh", "-P", "42", "--verbose", "--", "foo", "bar", "baz"])
|
56
|
+
expect(cmd.remote_command).to eq("foo bar baz")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "can handle subcommand options with remote command, 2" do
|
60
|
+
allow(cmd).to receive(:run).with(["foo"], a_hash_including(
|
61
|
+
max_parallelism: 42,
|
62
|
+
verbose: true,
|
63
|
+
))
|
64
|
+
app.main(["pssh", "-P", "42", "--verbose", "foo", "--", "bar", "baz"])
|
65
|
+
expect(cmd.remote_command).to eq("bar baz")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "can handle subcommand options with remote command, 3" do
|
69
|
+
allow(cmd).to receive(:run).with(["foo"], a_hash_including(
|
70
|
+
max_parallelism: 42,
|
71
|
+
verbose: true,
|
72
|
+
))
|
73
|
+
app.main(["pssh", "-P", "42", "--verbose", "foo", "--", "bar", "--", "baz"])
|
74
|
+
expect(cmd.remote_command).to eq("bar -- baz")
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "hotdog/application"
|
3
|
+
require "hotdog/commands/search"
|
4
|
+
|
5
|
+
describe "option parser for search" do
|
6
|
+
let(:app) {
|
7
|
+
Hotdog::Application.new
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:cmd) {
|
11
|
+
Hotdog::Commands::Search.new(app)
|
12
|
+
}
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
allow(app).to receive(:get_command).with("search") {
|
16
|
+
cmd
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can handle common options before subcommand" do
|
21
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
22
|
+
verbose: true,
|
23
|
+
))
|
24
|
+
app.main(["--verbose", "search", "foo", "bar", "baz"])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can handle common options after subcommand" do
|
28
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
29
|
+
verbose: true,
|
30
|
+
))
|
31
|
+
app.main(["search", "--verbose", "foo", "bar", "baz"])
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "hotdog/application"
|
3
|
+
require "hotdog/commands/ssh"
|
4
|
+
|
5
|
+
describe "option parser for ssh" do
|
6
|
+
let(:app) {
|
7
|
+
Hotdog::Application.new
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:cmd) {
|
11
|
+
Hotdog::Commands::Ssh.new(app)
|
12
|
+
}
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
allow(app).to receive(:get_command).with("ssh") {
|
16
|
+
cmd
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
it "cannot handle subcommand options before subcommand" do
|
21
|
+
expect {
|
22
|
+
app.main(["--index", "42", "ssh", "foo", "bar", "baz"])
|
23
|
+
}.to raise_error(OptionParser::InvalidOption)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "can handle subcommand options after subcommand" do
|
27
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
28
|
+
index: 42,
|
29
|
+
verbose: false,
|
30
|
+
))
|
31
|
+
app.main(["ssh", "--index", "42", "foo", "bar", "baz"])
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can handle common options before subcommand" do
|
35
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
36
|
+
index: 42,
|
37
|
+
verbose: true,
|
38
|
+
))
|
39
|
+
app.main(["--verbose", "ssh", "--index", "42", "foo", "bar", "baz"])
|
40
|
+
end
|
41
|
+
|
42
|
+
it "can handle common options after subcommand" do
|
43
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
44
|
+
index: 42,
|
45
|
+
verbose: true,
|
46
|
+
))
|
47
|
+
app.main(["ssh", "--index", "42", "--verbose", "foo", "bar", "baz"])
|
48
|
+
end
|
49
|
+
|
50
|
+
it "can handle subcommand options with remote command, 1" do
|
51
|
+
allow(cmd).to receive(:run).with([], a_hash_including(
|
52
|
+
index: 42,
|
53
|
+
verbose: true,
|
54
|
+
))
|
55
|
+
app.main(["ssh", "--index", "42", "--verbose", "--", "foo", "bar", "baz"])
|
56
|
+
expect(cmd.remote_command).to eq("foo bar baz")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "can handle subcommand options with remote command, 2" do
|
60
|
+
allow(cmd).to receive(:run).with(["foo"], a_hash_including(
|
61
|
+
index: 42,
|
62
|
+
verbose: true,
|
63
|
+
))
|
64
|
+
app.main(["ssh", "--index", "42", "--verbose", "foo", "--", "bar", "baz"])
|
65
|
+
expect(cmd.remote_command).to eq("bar baz")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "can handle subcommand options with remote command, 3" do
|
69
|
+
allow(cmd).to receive(:run).with(["foo"], a_hash_including(
|
70
|
+
index: 42,
|
71
|
+
verbose: true,
|
72
|
+
))
|
73
|
+
app.main(["ssh", "--index", "42", "--verbose", "foo", "--", "bar", "--", "baz"])
|
74
|
+
expect(cmd.remote_command).to eq("bar -- baz")
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "hotdog/application"
|
3
|
+
require "hotdog/commands/tags"
|
4
|
+
|
5
|
+
describe "option parser for tags" do
|
6
|
+
let(:app) {
|
7
|
+
Hotdog::Application.new
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:cmd) {
|
11
|
+
Hotdog::Commands::Tags.new(app)
|
12
|
+
}
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
allow(app).to receive(:get_command).with("tags") {
|
16
|
+
cmd
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can handle common options before subcommand" do
|
21
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
22
|
+
verbose: true,
|
23
|
+
))
|
24
|
+
app.main(["--verbose", "tags", "foo", "bar", "baz"])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can handle common options after subcommand" do
|
28
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
29
|
+
verbose: true,
|
30
|
+
))
|
31
|
+
app.main(["tags", "--verbose", "foo", "bar", "baz"])
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "hotdog/application"
|
3
|
+
require "hotdog/commands/up"
|
4
|
+
|
5
|
+
describe "option parser for up" do
|
6
|
+
let(:app) {
|
7
|
+
Hotdog::Application.new
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:cmd) {
|
11
|
+
Hotdog::Commands::Up.new(app)
|
12
|
+
}
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
allow(app).to receive(:get_command).with("up") {
|
16
|
+
cmd
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can handle common options before subcommand" do
|
21
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
22
|
+
verbose: true,
|
23
|
+
))
|
24
|
+
app.main(["--verbose", "up", "foo", "bar", "baz"])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "can handle common options after subcommand" do
|
28
|
+
allow(cmd).to receive(:run).with(["foo", "bar", "baz"], a_hash_including(
|
29
|
+
verbose: true,
|
30
|
+
))
|
31
|
+
app.main(["up", "--verbose", "foo", "bar", "baz"])
|
32
|
+
end
|
33
|
+
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.
|
4
|
+
version: 0.6.0
|
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-
|
11
|
+
date: 2015-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -67,47 +67,75 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 1.13.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: multi_json
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 1.
|
75
|
+
version: 1.11.2
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 1.
|
82
|
+
version: 1.11.2
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: oj
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 2.12.14
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 2.12.14
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: parallel
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 1.
|
103
|
+
version: 1.6.1
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.6.1
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: parslet
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.6.2
|
104
118
|
type: :runtime
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: 1.
|
124
|
+
version: 1.6.2
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: sqlite3
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.3.10
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.3.10
|
111
139
|
description: Yet another command-line tool for Datadog
|
112
140
|
email:
|
113
141
|
- peek824545201@gmail.com
|
@@ -153,6 +181,13 @@ files:
|
|
153
181
|
- spec/formatter/text_spec.rb
|
154
182
|
- spec/formatter/tsv_spec.rb
|
155
183
|
- spec/formatter/yaml_spec.rb
|
184
|
+
- spec/optparse/down_spec.rb
|
185
|
+
- spec/optparse/hosts_spec.rb
|
186
|
+
- spec/optparse/pssh_spec.rb
|
187
|
+
- spec/optparse/search_spec.rb
|
188
|
+
- spec/optparse/ssh_spec.rb
|
189
|
+
- spec/optparse/tags_spec.rb
|
190
|
+
- spec/optparse/up_spec.rb
|
156
191
|
- spec/parser/glob_expression_spec.rb
|
157
192
|
- spec/parser/parser_spec.rb
|
158
193
|
- spec/parser/regexp_expression_spec.rb
|
@@ -192,6 +227,13 @@ test_files:
|
|
192
227
|
- spec/formatter/text_spec.rb
|
193
228
|
- spec/formatter/tsv_spec.rb
|
194
229
|
- spec/formatter/yaml_spec.rb
|
230
|
+
- spec/optparse/down_spec.rb
|
231
|
+
- spec/optparse/hosts_spec.rb
|
232
|
+
- spec/optparse/pssh_spec.rb
|
233
|
+
- spec/optparse/search_spec.rb
|
234
|
+
- spec/optparse/ssh_spec.rb
|
235
|
+
- spec/optparse/tags_spec.rb
|
236
|
+
- spec/optparse/up_spec.rb
|
195
237
|
- spec/parser/glob_expression_spec.rb
|
196
238
|
- spec/parser/parser_spec.rb
|
197
239
|
- spec/parser/regexp_expression_spec.rb
|