hotdog 0.33.0 → 0.35.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/hotdog/application.rb +36 -92
- data/lib/hotdog/commands.rb +9 -57
- data/lib/hotdog/commands/down.rb +1 -13
- data/lib/hotdog/commands/search.rb +0 -4
- data/lib/hotdog/commands/ssh.rb +1 -0
- data/lib/hotdog/commands/tag.rb +3 -22
- data/lib/hotdog/commands/untag.rb +4 -31
- data/lib/hotdog/commands/up.rb +2 -21
- data/lib/hotdog/sources.rb +112 -0
- data/lib/hotdog/sources/datadog.rb +179 -0
- data/lib/hotdog/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c34bd3f352760468e86aecb50152087c9534938
|
4
|
+
data.tar.gz: 105739578293caaaf5b2264fc8c5614b1cccddda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d095e791a616d81e24cc2510c6fcceb8ed1461075a622a08b90ab6c6b3e865ac343c28ce2dbc7fb22239ac8e143e744bb5f009278f34ed6d46b7660b84b8994
|
7
|
+
data.tar.gz: 6ffd19158c137ad8eea99942a38c3fda059f9fcdc668df8f4cb8442cac3f8f916addea202e635a01b14d02a26e7bab4fae53e77f33ac7a4b9c59956f306e83aa
|
data/lib/hotdog/application.rb
CHANGED
@@ -6,6 +6,7 @@ require "optparse"
|
|
6
6
|
require "yaml"
|
7
7
|
require "hotdog/commands"
|
8
8
|
require "hotdog/formatters"
|
9
|
+
require "hotdog/sources"
|
9
10
|
require "hotdog/version"
|
10
11
|
|
11
12
|
module Hotdog
|
@@ -40,9 +41,9 @@ module Hotdog
|
|
40
41
|
@optparse = OptionParser.new
|
41
42
|
@optparse.version = Hotdog::VERSION
|
42
43
|
@options = {
|
43
|
-
endpoint:
|
44
|
-
api_key:
|
45
|
-
application_key:
|
44
|
+
endpoint: nil,
|
45
|
+
api_key: nil,
|
46
|
+
application_key: nil,
|
46
47
|
application: self,
|
47
48
|
confdir: find_confdir(File.expand_path(".")),
|
48
49
|
debug: false,
|
@@ -51,7 +52,7 @@ module Hotdog
|
|
51
52
|
force: false,
|
52
53
|
format: "text",
|
53
54
|
headers: false,
|
54
|
-
source:
|
55
|
+
source: "datadog",
|
55
56
|
status: nil,
|
56
57
|
listing: false,
|
57
58
|
logger: @logger,
|
@@ -69,23 +70,36 @@ module Hotdog
|
|
69
70
|
# reject nil values to declare sensible default later in subcommand
|
70
71
|
val.nil?
|
71
72
|
}
|
72
|
-
|
73
|
+
@source_provider = nil # will be initialized later in `main()`
|
73
74
|
define_options
|
74
75
|
end
|
75
76
|
attr_reader :logger
|
76
77
|
attr_reader :options
|
77
78
|
attr_reader :optparse
|
79
|
+
attr_reader :source_provider
|
78
80
|
|
79
81
|
def main(argv=[])
|
80
82
|
config = File.join(options[:confdir], "config.yml")
|
81
83
|
if File.file?(config)
|
82
|
-
|
84
|
+
begin
|
85
|
+
loaded = YAML.load(ERB.new(File.read(config)).result)
|
86
|
+
rescue => error
|
87
|
+
STDERR.puts("hotdog: failed to load configuration file at #{config.inspect}: #{error}")
|
88
|
+
exit(1)
|
89
|
+
end
|
83
90
|
if Hash === loaded
|
84
91
|
@options = @options.merge(Hash[loaded.map { |key, value| [Symbol === key ? key : key.to_s.to_sym, value] }])
|
85
92
|
end
|
86
93
|
end
|
87
94
|
args = @optparse.order(argv)
|
88
95
|
|
96
|
+
begin
|
97
|
+
@source_provider = get_source(@options[:source])
|
98
|
+
rescue NameError
|
99
|
+
STDERR.puts("hotdog: '#{@options[:source]}' is not a valid hotdog source.")
|
100
|
+
exit(1)
|
101
|
+
end
|
102
|
+
|
89
103
|
begin
|
90
104
|
command_name = ( args.shift || "help" )
|
91
105
|
begin
|
@@ -137,42 +151,6 @@ module Hotdog
|
|
137
151
|
end
|
138
152
|
end
|
139
153
|
|
140
|
-
def api_key()
|
141
|
-
if options[:api_key]
|
142
|
-
options[:api_key]
|
143
|
-
else
|
144
|
-
update_api_key!
|
145
|
-
if options[:api_key]
|
146
|
-
options[:api_key]
|
147
|
-
else
|
148
|
-
raise("DATADOG_API_KEY is not set")
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def application_key()
|
154
|
-
if options[:application_key]
|
155
|
-
options[:application_key]
|
156
|
-
else
|
157
|
-
update_application_key!
|
158
|
-
if options[:application_key]
|
159
|
-
options[:application_key]
|
160
|
-
else
|
161
|
-
raise("DATADOG_APPLICATION_KEY is not set")
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
def source()
|
167
|
-
options.fetch(:source, SOURCE_DATADOG)
|
168
|
-
end
|
169
|
-
|
170
|
-
def source_name(source=self.source)
|
171
|
-
{
|
172
|
-
SOURCE_DATADOG => "datadog",
|
173
|
-
}.fetch(source, "unknown")
|
174
|
-
end
|
175
|
-
|
176
154
|
def status()
|
177
155
|
options.fetch(:status, STATUS_RUNNING)
|
178
156
|
end
|
@@ -230,16 +208,7 @@ module Hotdog
|
|
230
208
|
options[:headers] = v
|
231
209
|
end
|
232
210
|
@optparse.on("--source=SOURCE", "Specify custom host source") do |v|
|
233
|
-
|
234
|
-
when /\A\d\z/i
|
235
|
-
options[:source] = v.to_i
|
236
|
-
when /\A(?:all|any)\z/i
|
237
|
-
options[:source] = nil
|
238
|
-
when /\A(?:datadog)\z/i
|
239
|
-
options[:source] = SOURCE_DATADOG
|
240
|
-
else
|
241
|
-
raise(OptionParser::InvalidArgument.new("unknown source: #{v}"))
|
242
|
-
end
|
211
|
+
@options[:source] = v
|
243
212
|
end
|
244
213
|
@optparse.on("--status=STATUS", "Specify custom host status") do |v|
|
245
214
|
case v
|
@@ -320,6 +289,21 @@ module Hotdog
|
|
320
289
|
klass.new(self)
|
321
290
|
end
|
322
291
|
|
292
|
+
def get_source(name)
|
293
|
+
begin
|
294
|
+
klass = Hotdog::Sources.const_get(const_name(name))
|
295
|
+
rescue NameError
|
296
|
+
library = find_library("hotdog/sources", name)
|
297
|
+
if library
|
298
|
+
load library
|
299
|
+
klass = Hotdog::Sources.const_get(const_name(File.basename(library, ".rb")))
|
300
|
+
else
|
301
|
+
raise(NameError.new("unknown source: #{name}"))
|
302
|
+
end
|
303
|
+
end
|
304
|
+
klass.new(self)
|
305
|
+
end
|
306
|
+
|
323
307
|
def find_library(dirname, name)
|
324
308
|
load_path = $LOAD_PATH.map { |path| File.join(path, dirname) }.select { |path| File.directory?(path) }
|
325
309
|
libraries = load_path.flat_map { |path| Dir.glob(File.join(path, "*.rb")) }.select { |file| File.file?(file) }
|
@@ -354,46 +338,6 @@ module Hotdog
|
|
354
338
|
end
|
355
339
|
end
|
356
340
|
end
|
357
|
-
|
358
|
-
def update_api_key!()
|
359
|
-
if options[:api_key_command]
|
360
|
-
logger.info("api_key_command> #{options[:api_key_command]}")
|
361
|
-
options[:api_key] = IO.popen(options[:api_key_command]) do |io|
|
362
|
-
io.read.strip
|
363
|
-
end
|
364
|
-
unless $?.success?
|
365
|
-
raise("failed: #{options[:api_key_command]}")
|
366
|
-
end
|
367
|
-
else
|
368
|
-
update_keys!
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
def update_application_key!()
|
373
|
-
if options[:application_key_command]
|
374
|
-
logger.info("application_key_command> #{options[:application_key_command]}")
|
375
|
-
options[:application_key] = IO.popen(options[:application_key_command]) do |io|
|
376
|
-
io.read.strip
|
377
|
-
end
|
378
|
-
unless $?.success?
|
379
|
-
raise("failed: #{options[:application_key_command]}")
|
380
|
-
end
|
381
|
-
else
|
382
|
-
update_keys!
|
383
|
-
end
|
384
|
-
end
|
385
|
-
|
386
|
-
def update_keys!()
|
387
|
-
if options[:key_command]
|
388
|
-
logger.info("key_command> #{options[:key_command]}")
|
389
|
-
options[:api_key], options[:application_key] = IO.popen(options[:key_command]) do |io|
|
390
|
-
io.read.strip.split(":", 2)
|
391
|
-
end
|
392
|
-
unless $?.success?
|
393
|
-
raise("failed: #{options[:key_command]}")
|
394
|
-
end
|
395
|
-
end
|
396
|
-
end
|
397
341
|
end
|
398
342
|
end
|
399
343
|
|
data/lib/hotdog/commands.rb
CHANGED
@@ -1,22 +1,16 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "fileutils"
|
4
|
-
require "dogapi"
|
5
|
-
require "multi_json"
|
6
|
-
require "oj"
|
7
|
-
require "open-uri"
|
8
|
-
require "parallel"
|
9
4
|
require "sqlite3"
|
10
|
-
require "uri"
|
11
5
|
|
12
6
|
module Hotdog
|
13
7
|
module Commands
|
14
8
|
class BaseCommand
|
15
9
|
def initialize(application)
|
16
10
|
@application = application
|
11
|
+
@source_provider = application.source_provider
|
17
12
|
@logger = application.logger
|
18
13
|
@options = application.options
|
19
|
-
@dog = nil # lazy initialization
|
20
14
|
@prepared_statements = {}
|
21
15
|
@persistent_db_path = File.join(@options.fetch(:confdir, "."), "hotdog.sqlite3")
|
22
16
|
end
|
@@ -262,25 +256,21 @@ module Hotdog
|
|
262
256
|
|
263
257
|
def create_db(db, options={})
|
264
258
|
options = @options.merge(options)
|
265
|
-
requests = {all_downtimes: "/api/v1/downtime", all_tags: "/api/v1/tags/hosts"}
|
266
259
|
begin
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
}]
|
260
|
+
all_tags = @source_provider.get_all_tags()
|
261
|
+
all_downtimes = @source_provider.get_all_downtimes().flat_map { |downtime|
|
262
|
+
# find host scopes
|
263
|
+
Array(downtime["scope"]).select { |scope| scope.start_with?("host:") }.map { |scope| scope.sub(/\Ahost:/, "") }
|
264
|
+
}
|
273
265
|
rescue => error
|
274
266
|
STDERR.puts(error.message)
|
275
267
|
exit(1)
|
276
268
|
end
|
277
|
-
all_tags = prepare_tags(responses.fetch(:all_tags, {}))
|
278
|
-
all_downtimes = prepare_downtimes(responses.fetch(:all_downtimes, {}))
|
279
269
|
if not all_downtimes.empty?
|
280
270
|
logger.info("ignore host(s) with scheduled downtimes: #{all_downtimes.inspect}")
|
281
271
|
end
|
282
272
|
db.transaction do
|
283
|
-
execute_db(db, "CREATE TABLE IF NOT EXISTS hosts (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(255) NOT NULL COLLATE NOCASE, source INTEGER NOT NULL DEFAULT #{
|
273
|
+
execute_db(db, "CREATE TABLE IF NOT EXISTS hosts (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(255) NOT NULL COLLATE NOCASE, source INTEGER NOT NULL DEFAULT #{@source_provider.id}, status INTEGER NOT NULL DEFAULT #{STATUS_PENDING});")
|
284
274
|
execute_db(db, "CREATE UNIQUE INDEX IF NOT EXISTS hosts_name ON hosts (name);")
|
285
275
|
execute_db(db, "CREATE TABLE IF NOT EXISTS tags (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(200) NOT NULL COLLATE NOCASE, value VARCHAR(200) NOT NULL COLLATE NOCASE);")
|
286
276
|
execute_db(db, "CREATE UNIQUE INDEX IF NOT EXISTS tags_name_value ON tags (name, value);")
|
@@ -288,11 +278,7 @@ module Hotdog
|
|
288
278
|
execute_db(db, "CREATE UNIQUE INDEX IF NOT EXISTS hosts_tags_host_id_tag_id ON hosts_tags (host_id, tag_id);")
|
289
279
|
|
290
280
|
execute_db(db, "CREATE TABLE IF NOT EXISTS source_names (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(200) NOT NULL COLLATE NOCASE);")
|
291
|
-
|
292
|
-
SOURCE_DATADOG => application.source_name(SOURCE_DATADOG),
|
293
|
-
}.each do |source_id, source_name|
|
294
|
-
execute_db(db, "INSERT OR IGNORE INTO source_names (id, name) VALUES (?, ?);", [source_id, source_name])
|
295
|
-
end
|
281
|
+
execute_db(db, "INSERT OR IGNORE INTO source_names (id, name) VALUES (?, ?);", [@source_provider.id, @source_provider.name])
|
296
282
|
|
297
283
|
execute_db(db, "CREATE TABLE IF NOT EXISTS status_names (id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(200) NOT NULL COLLATE NOCASE);")
|
298
284
|
{
|
@@ -343,42 +329,12 @@ module Hotdog
|
|
343
329
|
end
|
344
330
|
end
|
345
331
|
|
346
|
-
def datadog_get(request_path, query=nil)
|
347
|
-
# TODO: make this pluggable
|
348
|
-
endpoint = options[:endpoint]
|
349
|
-
query ||= URI.encode_www_form(api_key: application.api_key, application_key: application.application_key)
|
350
|
-
uri = URI.join(endpoint, "#{request_path}?#{query}")
|
351
|
-
begin
|
352
|
-
response = uri.open("User-Agent" => "hotdog/#{Hotdog::VERSION}") { |fp| fp.read }
|
353
|
-
MultiJson.load(response)
|
354
|
-
rescue OpenURI::HTTPError => error
|
355
|
-
code, _body = error.io.status
|
356
|
-
raise(RuntimeError.new("datadog: GET #{request_path} returns [#{code.inspect}, ...]"))
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
|
-
def prepare_tags(tags)
|
361
|
-
Hash(tags).fetch("tags", {})
|
362
|
-
end
|
363
|
-
|
364
|
-
def prepare_downtimes(downtimes)
|
365
|
-
now = Time.new.to_i
|
366
|
-
Array(downtimes).select { |downtime|
|
367
|
-
# active downtimes
|
368
|
-
downtime["active"] and ( downtime["start"].nil? or downtime["start"] < now ) and ( downtime["end"].nil? or now <= downtime["end"] ) and downtime["monitor_id"].nil?
|
369
|
-
}.flat_map { |downtime|
|
370
|
-
# find host scopes
|
371
|
-
downtime["scope"].select { |scope| scope.start_with?("host:") }.map { |scope| scope.sub(/\Ahost:/, "") }
|
372
|
-
}
|
373
|
-
end
|
374
|
-
|
375
332
|
def create_hosts(db, hosts, downtimes)
|
376
333
|
hosts.each_slice(SQLITE_LIMIT_COMPOUND_SELECT / 3) do |hosts|
|
377
334
|
q = "INSERT OR IGNORE INTO hosts (name, source, status) VALUES %s;" % hosts.map { "(?, ?, ?)" }.join(", ")
|
378
335
|
execute_db(db, q, hosts.map { |host|
|
379
|
-
source = SOURCE_DATADOG
|
380
336
|
status = downtimes.include?(host) ? STATUS_STOPPED : STATUS_RUNNING
|
381
|
-
[host,
|
337
|
+
[host, @source_provider.id, status]
|
382
338
|
})
|
383
339
|
end
|
384
340
|
|
@@ -447,10 +403,6 @@ module Hotdog
|
|
447
403
|
end
|
448
404
|
end
|
449
405
|
|
450
|
-
def dog()
|
451
|
-
@dog ||= Dogapi::Client.new(application.api_key, application.application_key)
|
452
|
-
end
|
453
|
-
|
454
406
|
def split_tag(tag)
|
455
407
|
tagname, tagvalue = tag.split(":", 2)
|
456
408
|
[rewrite_legacy_tagname(tagname), tagvalue || ""]
|
data/lib/hotdog/commands/down.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require "fileutils"
|
4
|
-
|
5
3
|
module Hotdog
|
6
4
|
module Commands
|
7
5
|
class Down < BaseCommand
|
@@ -70,20 +68,10 @@ module Hotdog
|
|
70
68
|
end
|
71
69
|
scopes.each do |scope|
|
72
70
|
with_retry(options) do
|
73
|
-
schedule_downtime(scope, options)
|
71
|
+
@source_provider.schedule_downtime(scope, options)
|
74
72
|
end
|
75
73
|
end
|
76
74
|
end
|
77
|
-
|
78
|
-
private
|
79
|
-
def schedule_downtime(scope, options={})
|
80
|
-
code, schedule = dog.schedule_downtime(scope, :start => options[:start].to_i, :end => (options[:start]+options[:downtime]).to_i)
|
81
|
-
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])
|
82
|
-
if code.to_i / 100 != 2
|
83
|
-
raise("dog.schedule_downtime(%s, ...) returns [%s, %s]" % [scope.inspect, code.inspect, schedule.inspect])
|
84
|
-
end
|
85
|
-
schedule
|
86
|
-
end
|
87
75
|
end
|
88
76
|
end
|
89
77
|
end
|
@@ -120,10 +120,6 @@ module Hotdog
|
|
120
120
|
# return everything if given expression is empty
|
121
121
|
expression = "*"
|
122
122
|
end
|
123
|
-
if options[:source]
|
124
|
-
source_name = application.source_name(options[:source])
|
125
|
-
expression = "@source:#{source_name} AND (#{expression})"
|
126
|
-
end
|
127
123
|
if options[:status]
|
128
124
|
status_name = application.status_name(options[:status])
|
129
125
|
expression = "@status:#{status_name} AND (#{expression})"
|
data/lib/hotdog/commands/ssh.rb
CHANGED
data/lib/hotdog/commands/tag.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require "fileutils"
|
4
|
-
|
5
3
|
module Hotdog
|
6
4
|
module Commands
|
7
5
|
class Tag < BaseCommand
|
@@ -15,7 +13,7 @@ module Hotdog
|
|
15
13
|
optparse.on("--retry-delay SECONDS") do |v|
|
16
14
|
options[:retry_delay] = v.to_i
|
17
15
|
end
|
18
|
-
optparse.on("--source SOURCE") do |v|
|
16
|
+
optparse.on("--tag-source SOURCE") do |v|
|
19
17
|
options[:tag_source] = v
|
20
18
|
end
|
21
19
|
optparse.on("-a TAG", "-t TAG", "--tag TAG", "Use specified tag name/value") do |v|
|
@@ -41,33 +39,16 @@ module Hotdog
|
|
41
39
|
hosts.each do |host|
|
42
40
|
if options[:tags].empty?
|
43
41
|
# nop; just show current tags
|
44
|
-
host_tags = with_retry { host_tags(host, source=options[:tag_source]) }
|
42
|
+
host_tags = with_retry { @source_provider.host_tags(host, source=options[:tag_source]) }
|
45
43
|
STDOUT.puts host_tags['tags'].inspect
|
46
44
|
else
|
47
45
|
# add all as user tags
|
48
46
|
with_retry(options) do
|
49
|
-
add_tags(host, options[:tags], source=options[:tag_source])
|
47
|
+
@source_provider.add_tags(host, options[:tags], source=options[:tag_source])
|
50
48
|
end
|
51
49
|
end
|
52
50
|
end
|
53
51
|
end
|
54
|
-
|
55
|
-
private
|
56
|
-
def add_tags(host_name, tags, options={})
|
57
|
-
code, resp = dog.add_tags(host_name, tags, options)
|
58
|
-
if code.to_i / 100 != 2
|
59
|
-
raise("dog.add_tags(#{host_name.inspect}, #{tags.inspect}, #{options.inspect}) returns [#{code.inspect}, #{resp.inspect}]")
|
60
|
-
end
|
61
|
-
resp
|
62
|
-
end
|
63
|
-
|
64
|
-
def host_tags(host_name, options={})
|
65
|
-
code, host_tags = dog.host_tags(host_name, options)
|
66
|
-
if code.to_i / 100 != 2
|
67
|
-
raise("dog.host_tags(#{host_name.inspect}, #{options.inspect}) returns [#{code.inspect}, #{host_tags.inspect}]")
|
68
|
-
end
|
69
|
-
host_tags
|
70
|
-
end
|
71
52
|
end
|
72
53
|
end
|
73
54
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require "fileutils"
|
4
|
-
|
5
3
|
module Hotdog
|
6
4
|
module Commands
|
7
5
|
class Untag < BaseCommand
|
@@ -15,7 +13,7 @@ module Hotdog
|
|
15
13
|
optparse.on("--retry-delay SECONDS") do |v|
|
16
14
|
options[:retry_delay] = v.to_i
|
17
15
|
end
|
18
|
-
optparse.on("--source SOURCE") do |v|
|
16
|
+
optparse.on("--tag-source SOURCE") do |v|
|
19
17
|
options[:tag_source] = v
|
20
18
|
end
|
21
19
|
optparse.on("-a TAG", "-t TAG", "--tag TAG", "Use specified tag name/value") do |v|
|
@@ -48,47 +46,22 @@ module Hotdog
|
|
48
46
|
if options[:tags].empty?
|
49
47
|
# delete all user tags
|
50
48
|
with_retry do
|
51
|
-
detach_tags(host, source=options[:tag_source])
|
49
|
+
@source_provider.detach_tags(host, source=options[:tag_source])
|
52
50
|
end
|
53
51
|
else
|
54
|
-
host_tags = with_retry { host_tags(host, source=options[:tag_source]) }
|
52
|
+
host_tags = with_retry { @source_provider.host_tags(host, source=options[:tag_source]) }
|
55
53
|
old_tags = host_tags["tags"]
|
56
54
|
new_tags = old_tags - options[:tags]
|
57
55
|
if old_tags == new_tags
|
58
56
|
# nop
|
59
57
|
else
|
60
58
|
with_retry do
|
61
|
-
update_tags(host, new_tags, source=options[:tag_source])
|
59
|
+
@source_provider.update_tags(host, new_tags, source=options[:tag_source])
|
62
60
|
end
|
63
61
|
end
|
64
62
|
end
|
65
63
|
end
|
66
64
|
end
|
67
|
-
|
68
|
-
private
|
69
|
-
def detach_tags(host_name, options={})
|
70
|
-
code, detach_tags = dog.detach_tags(host_name, options)
|
71
|
-
if code.to_i / 100 != 2
|
72
|
-
raise("dog.detach_tags(#{host_name.inspect}, #{options.inspect}) returns [#{code.inspect}, #{detach_tags.inspect}]")
|
73
|
-
end
|
74
|
-
detach_tags
|
75
|
-
end
|
76
|
-
|
77
|
-
def host_tags(host_name, options={})
|
78
|
-
code, host_tags = dog.host_tags(host_name, options)
|
79
|
-
if code.to_i / 100 != 2
|
80
|
-
raise("dog.host_tags(#{host_name.inspect}, #{options.inspect}) returns [#{code.inspect}, #{host_tags.inspect}]")
|
81
|
-
end
|
82
|
-
host_tags
|
83
|
-
end
|
84
|
-
|
85
|
-
def update_tags(host_name, tags, options={})
|
86
|
-
code, update_tags = dog.update_tags(host_name, tags, options)
|
87
|
-
if code.to_i / 100 != 2
|
88
|
-
raise("dog.update_tags(#{host_name.inspect}, #{tags.inspect}, #{options.inspect}) returns [#{code.inspect}, #{update_tags.inspect}]")
|
89
|
-
end
|
90
|
-
update_tags
|
91
|
-
end
|
92
65
|
end
|
93
66
|
end
|
94
67
|
end
|
data/lib/hotdog/commands/up.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require "fileutils"
|
4
|
-
|
5
3
|
module Hotdog
|
6
4
|
module Commands
|
7
5
|
class Up < BaseCommand
|
@@ -25,7 +23,7 @@ module Hotdog
|
|
25
23
|
}
|
26
24
|
all_downtimes = nil
|
27
25
|
with_retry(options) do
|
28
|
-
all_downtimes = get_all_downtimes(options)
|
26
|
+
all_downtimes = @source_provider.get_all_downtimes(options)
|
29
27
|
end
|
30
28
|
|
31
29
|
cancel_downtimes = all_downtimes.select { |downtime|
|
@@ -34,7 +32,7 @@ module Hotdog
|
|
34
32
|
|
35
33
|
cancel_downtimes.each do |downtime|
|
36
34
|
with_retry(options) do
|
37
|
-
cancel_downtime(downtime["id"], options)
|
35
|
+
@source_provider.cancel_downtime(downtime["id"], options)
|
38
36
|
end
|
39
37
|
end
|
40
38
|
|
@@ -57,23 +55,6 @@ module Hotdog
|
|
57
55
|
end
|
58
56
|
end
|
59
57
|
end
|
60
|
-
|
61
|
-
private
|
62
|
-
def get_all_downtimes(options={})
|
63
|
-
code, all_downtimes = dog.get_all_downtimes()
|
64
|
-
if code.to_i / 100 != 2
|
65
|
-
raise("dog.get_all_downtimes() returns [%s, %s]" % [code.inspect, all_downtimes.inspect])
|
66
|
-
end
|
67
|
-
all_downtimes
|
68
|
-
end
|
69
|
-
|
70
|
-
def cancel_downtime(id, options={})
|
71
|
-
code, cancel = dog.cancel_downtime(id)
|
72
|
-
if code.to_i / 100 != 2
|
73
|
-
raise("dog.cancel_downtime(%s) returns [%s, %s]" % [id.inspect, code.inspect, cancel.inspect])
|
74
|
-
end
|
75
|
-
cancel
|
76
|
-
end
|
77
58
|
end
|
78
59
|
end
|
79
60
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module Hotdog
|
4
|
+
module Sources
|
5
|
+
class BaseSource
|
6
|
+
def initialize(application)
|
7
|
+
@application = application
|
8
|
+
@logger = application.logger
|
9
|
+
@options = application.options
|
10
|
+
end
|
11
|
+
attr_reader :application
|
12
|
+
attr_reader :logger
|
13
|
+
attr_reader :options
|
14
|
+
|
15
|
+
def id() #=> Integer
|
16
|
+
raise(NotImplementedError)
|
17
|
+
end
|
18
|
+
|
19
|
+
def name() #=> String
|
20
|
+
raise(NotImplementedError)
|
21
|
+
end
|
22
|
+
|
23
|
+
def endpoint() #=> String
|
24
|
+
options[:endpoint]
|
25
|
+
end
|
26
|
+
|
27
|
+
def api_key() #=> String
|
28
|
+
options[:api_key]
|
29
|
+
end
|
30
|
+
|
31
|
+
def application_key() #=> String
|
32
|
+
options[:application_key]
|
33
|
+
end
|
34
|
+
|
35
|
+
def schedule_downtime(scope, options={})
|
36
|
+
raise(NotImplementedError)
|
37
|
+
end
|
38
|
+
|
39
|
+
def cancel_downtime(id, options={})
|
40
|
+
raise(NotImplementedError)
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_all_downtimes(options={})
|
44
|
+
#
|
45
|
+
# This should return some `Array<Hash<String,String>>` like follows
|
46
|
+
#
|
47
|
+
# ```json
|
48
|
+
# [
|
49
|
+
# {
|
50
|
+
# "recurrence": null,
|
51
|
+
# "end": 1533593208,
|
52
|
+
# "monitor_tags": [
|
53
|
+
# "*"
|
54
|
+
# ],
|
55
|
+
# "canceled": null,
|
56
|
+
# "monitor_id": null,
|
57
|
+
# "org_id": 12345,
|
58
|
+
# "disabled": false,
|
59
|
+
# "start": 1533592608,
|
60
|
+
# "creator_id": 78913,
|
61
|
+
# "parent_id": null,
|
62
|
+
# "timezone": "UTC",
|
63
|
+
# "active": false,
|
64
|
+
# "scope": [
|
65
|
+
# "host:i-abcdef01234567890"
|
66
|
+
# ],
|
67
|
+
# "message": null,
|
68
|
+
# "downtime_type": null,
|
69
|
+
# "id": 278432422,
|
70
|
+
# "updater_id": null
|
71
|
+
# }
|
72
|
+
# ]
|
73
|
+
# ```
|
74
|
+
#
|
75
|
+
raise(NotImplementedError)
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_all_tags(options={})
|
79
|
+
#
|
80
|
+
# This should return some `Hash<String,Array<String>>` like follows
|
81
|
+
#
|
82
|
+
# ```json
|
83
|
+
# {
|
84
|
+
# "tagname:tagvalue": [
|
85
|
+
# "foo",
|
86
|
+
# "bar",
|
87
|
+
# "baz"
|
88
|
+
# ]
|
89
|
+
# }
|
90
|
+
# ```
|
91
|
+
#
|
92
|
+
raise(NotImplementedError)
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_host_tags(host_name, options={})
|
96
|
+
raise(NotImplementedError)
|
97
|
+
end
|
98
|
+
|
99
|
+
def add_tags(host_name, tags, options={})
|
100
|
+
raise(NotImplementedError)
|
101
|
+
end
|
102
|
+
|
103
|
+
def detach_tags(host_name, options={})
|
104
|
+
raise(NotImplementedError)
|
105
|
+
end
|
106
|
+
|
107
|
+
def update_tags(host_name, tags, options={})
|
108
|
+
raise(NotImplementedError)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "dogapi"
|
4
|
+
require "multi_json"
|
5
|
+
require "oj"
|
6
|
+
require "open-uri"
|
7
|
+
require "uri"
|
8
|
+
|
9
|
+
module Hotdog
|
10
|
+
module Sources
|
11
|
+
class Datadog < BaseSource
|
12
|
+
def initialize(application)
|
13
|
+
super(application)
|
14
|
+
options[:endpoint] = ENV.fetch("DATADOG_HOST", "https://app.datadoghq.com")
|
15
|
+
options[:api_key] = ENV["DATADOG_API_KEY"]
|
16
|
+
options[:application_key] = ENV["DATADOG_APPLICATION_KEY"]
|
17
|
+
@dog = nil # lazy initialization
|
18
|
+
end
|
19
|
+
|
20
|
+
def id()
|
21
|
+
Hotdog::SOURCE_DATADOG
|
22
|
+
end
|
23
|
+
|
24
|
+
def name()
|
25
|
+
"datadog"
|
26
|
+
end
|
27
|
+
|
28
|
+
def endpoint()
|
29
|
+
options[:endpoint]
|
30
|
+
end
|
31
|
+
|
32
|
+
def api_key()
|
33
|
+
if options[:api_key]
|
34
|
+
options[:api_key]
|
35
|
+
else
|
36
|
+
update_api_key!
|
37
|
+
if options[:api_key]
|
38
|
+
options[:api_key]
|
39
|
+
else
|
40
|
+
raise("DATADOG_API_KEY is not set")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def application_key()
|
46
|
+
if options[:application_key]
|
47
|
+
options[:application_key]
|
48
|
+
else
|
49
|
+
update_application_key!
|
50
|
+
if options[:application_key]
|
51
|
+
options[:application_key]
|
52
|
+
else
|
53
|
+
raise("DATADOG_APPLICATION_KEY is not set")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def schedule_downtime(scope, options={})
|
59
|
+
code, schedule = dog.schedule_downtime(scope, :start => options[:start].to_i, :end => (options[:start]+options[:downtime]).to_i)
|
60
|
+
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])
|
61
|
+
if code.to_i / 100 != 2
|
62
|
+
raise("dog.schedule_downtime(%s, ...) returns [%s, %s]" % [scope.inspect, code.inspect, schedule.inspect])
|
63
|
+
end
|
64
|
+
schedule
|
65
|
+
end
|
66
|
+
|
67
|
+
def cancel_downtime(id, options={})
|
68
|
+
code, cancel = dog.cancel_downtime(id)
|
69
|
+
if code.to_i / 100 != 2
|
70
|
+
raise("dog.cancel_downtime(%s) returns [%s, %s]" % [id.inspect, code.inspect, cancel.inspect])
|
71
|
+
end
|
72
|
+
cancel
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_all_downtimes(options={})
|
76
|
+
now = Time.new.to_i
|
77
|
+
Array(datadog_get("/api/v1/downtime")).select { |downtime|
|
78
|
+
# active downtimes
|
79
|
+
downtime["active"] and ( downtime["start"].nil? or downtime["start"] < now ) and ( downtime["end"].nil? or now <= downtime["end"] ) and downtime["monitor_id"].nil?
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_all_tags(options={})
|
84
|
+
Hash(datadog_get("/api/v1/tags/hosts")).fetch("tags", {})
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_host_tags(host_name, options={})
|
88
|
+
code, host_tags = dog.host_tags(host_name, options)
|
89
|
+
if code.to_i / 100 != 2
|
90
|
+
raise("dog.host_tags(#{host_name.inspect}, #{options.inspect}) returns [#{code.inspect}, #{host_tags.inspect}]")
|
91
|
+
end
|
92
|
+
host_tags
|
93
|
+
end
|
94
|
+
|
95
|
+
def add_tags(host_name, tags, options={})
|
96
|
+
code, resp = dog.add_tags(host_name, tags, options)
|
97
|
+
if code.to_i / 100 != 2
|
98
|
+
raise("dog.add_tags(#{host_name.inspect}, #{tags.inspect}, #{options.inspect}) returns [#{code.inspect}, #{resp.inspect}]")
|
99
|
+
end
|
100
|
+
resp
|
101
|
+
end
|
102
|
+
|
103
|
+
def detach_tags(host_name, options={})
|
104
|
+
code, detach_tags = dog.detach_tags(host_name, options)
|
105
|
+
if code.to_i / 100 != 2
|
106
|
+
raise("dog.detach_tags(#{host_name.inspect}, #{options.inspect}) returns [#{code.inspect}, #{detach_tags.inspect}]")
|
107
|
+
end
|
108
|
+
detach_tags
|
109
|
+
end
|
110
|
+
|
111
|
+
def update_tags(host_name, tags, options={})
|
112
|
+
code, update_tags = dog.update_tags(host_name, tags, options)
|
113
|
+
if code.to_i / 100 != 2
|
114
|
+
raise("dog.update_tags(#{host_name.inspect}, #{tags.inspect}, #{options.inspect}) returns [#{code.inspect}, #{update_tags.inspect}]")
|
115
|
+
end
|
116
|
+
update_tags
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
def dog()
|
121
|
+
@dog ||= Dogapi::Client.new(self.api_key, self.application_key)
|
122
|
+
end
|
123
|
+
|
124
|
+
def datadog_get(request_path, query=nil)
|
125
|
+
query ||= URI.encode_www_form(api_key: self.api_key, application_key: self.application_key)
|
126
|
+
uri = URI.join(self.endpoint, "#{request_path}?#{query}")
|
127
|
+
begin
|
128
|
+
response = uri.open("User-Agent" => "hotdog/#{Hotdog::VERSION}") { |fp| fp.read }
|
129
|
+
MultiJson.load(response)
|
130
|
+
rescue OpenURI::HTTPError => error
|
131
|
+
code, _body = error.io.status
|
132
|
+
raise(RuntimeError.new("datadog: GET #{request_path} returns [#{code.inspect}, ...]"))
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def update_api_key!()
|
137
|
+
if options[:api_key_command]
|
138
|
+
logger.info("api_key_command> #{options[:api_key_command]}")
|
139
|
+
options[:api_key] = IO.popen(options[:api_key_command]) do |io|
|
140
|
+
io.read.strip
|
141
|
+
end
|
142
|
+
unless $?.success?
|
143
|
+
raise("failed: #{options[:api_key_command]}")
|
144
|
+
end
|
145
|
+
else
|
146
|
+
update_keys!
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def update_application_key!()
|
151
|
+
if options[:application_key_command]
|
152
|
+
logger.info("application_key_command> #{options[:application_key_command]}")
|
153
|
+
options[:application_key] = IO.popen(options[:application_key_command]) do |io|
|
154
|
+
io.read.strip
|
155
|
+
end
|
156
|
+
unless $?.success?
|
157
|
+
raise("failed: #{options[:application_key_command]}")
|
158
|
+
end
|
159
|
+
else
|
160
|
+
update_keys!
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def update_keys!()
|
165
|
+
if options[:key_command]
|
166
|
+
logger.info("key_command> #{options[:key_command]}")
|
167
|
+
options[:api_key], options[:application_key] = IO.popen(options[:key_command]) do |io|
|
168
|
+
io.read.strip.split(":", 2)
|
169
|
+
end
|
170
|
+
unless $?.success?
|
171
|
+
raise("failed: #{options[:key_command]}")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# vim:set ft=ruby :
|
data/lib/hotdog/version.rb
CHANGED
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.35.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: 2018-
|
11
|
+
date: 2018-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -179,6 +179,8 @@ files:
|
|
179
179
|
- lib/hotdog/formatters/text.rb
|
180
180
|
- lib/hotdog/formatters/tsv.rb
|
181
181
|
- lib/hotdog/formatters/yaml.rb
|
182
|
+
- lib/hotdog/sources.rb
|
183
|
+
- lib/hotdog/sources/datadog.rb
|
182
184
|
- lib/hotdog/version.rb
|
183
185
|
- spec/core/application_spec.rb
|
184
186
|
- spec/core/commands_spec.rb
|