hotdog 0.33.0 → 0.35.0
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 +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
|