hotdog 0.0.7 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d1a630c98690e6d1df7a32b7baf48e24776da49a
4
- data.tar.gz: ab4daa9a831412d598eb235e3e4ed8a4516b2dcf
3
+ metadata.gz: c02920ad489cbbbf48668151aa47add9a237cc07
4
+ data.tar.gz: 135a3312a19650537706eac8d00e7ec27fe503bc
5
5
  SHA512:
6
- metadata.gz: cb2c5b244339014184b56d4c1ab68bce58bc8fa2f71e84eef15319a8157be1532304647760fcd0b41d200cd8e1fdb895e6ca6a8309c7cd79d3ce9f6e67379c6f
7
- data.tar.gz: 00ea3e8ddc439588571c788a0ec4f2546fb084d81ef18749b92cb83ab239ce2ea6df3f7c1ad21f17ca9f487794c805a1c5eb1fd007857d81dc58b3f9be9e3746
6
+ metadata.gz: ad15864f8d0cb38904e9e377c8c416aa28432ab032d8c109161bffa578e165bb572165658f260a10f6eb50dee59ed950f5b647aec165767eace829c03f80d0f9
7
+ data.tar.gz: 0b452f257046f22a261deada1cba9e53e6e7aec8ef60dcbdc43991c4dabcf567a38888a72215d7d50a5cd1d7dd838f45723f017a978991b75f8e7add94f99668
data/README.md CHANGED
@@ -42,12 +42,6 @@ EOF
42
42
 
43
43
  ## Usage
44
44
 
45
- Initialize host information. This may take several minutes.
46
-
47
- ```sh
48
- $ hotdog update
49
- ```
50
-
51
45
  List all registered hosts.
52
46
 
53
47
  ```sh
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "logger"
4
4
  require "optparse"
5
- require "shellwords"
6
- require "sqlite3"
7
5
  require "yaml"
8
6
  require "hotdog/commands"
9
7
  require "hotdog/formatters"
@@ -11,34 +9,34 @@ require "hotdog/formatters"
11
9
  module Hotdog
12
10
  class Application
13
11
  def initialize()
14
- @confdir = find_confdir(File.expand_path("."))
15
12
  @optparse = OptionParser.new
16
13
  @options = {
14
+ api_key: ENV["DATADOG_API_KEY"],
15
+ application_key: ENV["DATADOG_APPLICATION_KEY"],
16
+ application: self,
17
+ confdir: find_confdir(File.expand_path(".")),
17
18
  debug: false,
18
- environment: "default",
19
- minimum_expiry: 3600, # 1 hour
20
- random_expiry: 5940, # 99 hours
19
+ expiry: 180,
21
20
  fixed_string: false,
22
21
  force: false,
23
22
  format: "plain",
24
23
  headers: false,
25
24
  listing: false,
26
- logger: Logger.new(STDERR),
25
+ logger: Logger.new(STDERR).tap { |logger|
26
+ logger.level = Logger::INFO
27
+ },
27
28
  max_time: 5,
28
- api_key: ENV["DATADOG_API_KEY"],
29
- application_key: ENV["DATADOG_APPLICATION_KEY"],
30
29
  print0: false,
31
30
  print1: true,
32
31
  tags: [],
33
32
  verbose: false,
34
33
  }
35
- @options[:logger].level = Logger::INFO
36
34
  define_options
37
35
  end
38
36
  attr_reader :options
39
37
 
40
38
  def main(argv=[])
41
- config = File.join(@confdir, "config.yml")
39
+ config = File.join(options[:confdir], "config.yml")
42
40
  if File.file?(config)
43
41
  loaded = YAML.load(File.read(config))
44
42
  if Hash === loaded
@@ -63,28 +61,16 @@ module Hotdog
63
61
  options[:logger].level = Logger::INFO
64
62
  end
65
63
 
66
- sqlite = File.expand_path(File.join(@confdir, "#{options[:environment]}.db"))
67
- FileUtils.mkdir_p(File.dirname(sqlite))
68
- @db = SQLite3::Database.new(sqlite)
69
- @db.synchronous = "off"
70
-
71
64
  begin
72
65
  command = ( args.shift || "help" )
73
- c = run_command(command, args)
74
- if c.suspended?
75
- exit(2)
66
+ get_command(command).new(@options.dup).tap do |cmd|
67
+ cmd.run(args)
76
68
  end
77
69
  rescue Errno::EPIPE
78
70
  # nop
79
71
  end
80
72
  end
81
73
 
82
- def run_command(command, args=[])
83
- get_command(command).new(@db, options.merge(application: self)).tap do |c|
84
- c.run(args)
85
- end
86
- end
87
-
88
74
  private
89
75
  def define_options
90
76
  @optparse.on("--api-key API_KEY", "Datadog API key") do |api_key|
@@ -99,15 +85,9 @@ module Hotdog
99
85
  @optparse.on("-1", "Use newline as separator") do |v|
100
86
  options[:print1] = v
101
87
  end
102
- @optparse.on("-B", "--blocking", "Enable blocking mode") do
103
- options[:max_time] = -1
104
- end
105
88
  @optparse.on("-d", "--[no-]debug", "Enable debug mode") do |v|
106
89
  options[:debug] = v
107
90
  end
108
- @optparse.on("-E ENVIRONMENT", "--environment ENVIRONMENT", "Specify environment") do |environment|
109
- options[:environment] = environment
110
- end
111
91
  @optparse.on("--fixed-string", "Interpret pattern as fixed string") do |v|
112
92
  options[:fixed_string] = v
113
93
  end
@@ -126,9 +106,6 @@ module Hotdog
126
106
  @optparse.on("-a TAG", "-t TAG", "--tag TAG", "Use specified tag name/value") do |tag|
127
107
  options[:tags] += [tag]
128
108
  end
129
- @optparse.on("-m SECONDS", "--max-time SECONDS", Integer, "Maximum time in seconds") do |seconds|
130
- options[:max_time] = seconds
131
- end
132
109
  @optparse.on("-V", "--[no-]verbose", "Enable verbose mode") do |v|
133
110
  options[:verbose] = v
134
111
  end
@@ -1,25 +1,32 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require "fileutils"
3
4
  require "dogapi"
4
- require "logger"
5
+ require "json"
6
+ require "sqlite3"
5
7
 
6
8
  module Hotdog
7
9
  module Commands
8
10
  class BaseCommand
9
- def initialize(db, options={})
10
- @db = db
11
+ def initialize(options={})
12
+ @application = options[:application]
13
+ @confdir = options[:confdir]
14
+ @expiry = options[:expiry]
11
15
  @fixed_string = options[:fixed_string]
16
+ @force = options[:force]
12
17
  @formatter = options[:formatter]
18
+ @listing = options[:listing]
13
19
  @logger = options[:logger]
14
20
  @tags = options[:tags]
15
- @application = options[:application]
16
21
  @options = options
17
22
  @dog = Dogapi::Client.new(options[:api_key], options[:application_key])
18
- @started_at = Time.new
19
- @suspended = false
20
23
  end
21
24
  attr_reader :application
25
+ attr_reader :confdir
26
+ attr_reader :expiry
27
+ attr_reader :force
22
28
  attr_reader :formatter
29
+ attr_reader :listing
23
30
  attr_reader :logger
24
31
  attr_reader :tags
25
32
  attr_reader :options
@@ -29,6 +36,7 @@ module Hotdog
29
36
  end
30
37
 
31
38
  def execute(query, *args)
39
+ update_db
32
40
  q = query.strip
33
41
  if 0 < args.length
34
42
  q += " -- VALUES (#{args.map { |arg| Array === arg ? "(#{arg.join(", ")})" : arg.inspect }.join(", ")})"
@@ -41,10 +49,6 @@ module Hotdog
41
49
  @fixed_string
42
50
  end
43
51
 
44
- def suspended?()
45
- @suspended
46
- end
47
-
48
52
  private
49
53
  def format(result, options={})
50
54
  @formatter.format(result, @options.merge(options))
@@ -55,285 +59,248 @@ module Hotdog
55
59
  end
56
60
 
57
61
  def get_hosts(hosts=[])
62
+ update_db
58
63
  if 0 < tags.length
59
64
  result = hosts.map { |host_id|
60
- update_host_tags(host_id, @options.merge(tags: tags))
61
65
  tags.map { |tag|
62
66
  tag_name, tag_value = tag.split(":", 2)
63
67
  case tag_name
64
- when "expires_at"
65
- @get_hosts_q6 ||= @db.prepare(<<-EOS)
66
- SELECT expires_at FROM hosts_tags WHERE host_id = ? LIMIT 1;
67
- EOS
68
- logger.debug("get_hosts_q6(%s)" % [host_id.inspect])
69
- @get_hosts_q6.execute(host_id).map { |row| Time.at(row.first).strftime("%Y-%m-%dT%H:%M:%S") }.first
70
68
  when "host"
71
- @get_hosts_q1 ||= @db.prepare(<<-EOS)
72
- SELECT name FROM hosts WHERE id = ? LIMIT 1;
73
- EOS
74
- logger.debug("get_hosts_q1(%s)" % [host_id.inspect])
75
- @get_hosts_q1.execute(host_id).map { |row| row.first }.first
69
+ select_name_from_hosts_by_id(@db, host_id)
76
70
  else
77
- if not glob?(tag_name)
78
- @get_hosts_q2 ||= @db.prepare(<<-EOS)
79
- SELECT tags.value FROM hosts_tags
80
- INNER JOIN tags ON hosts_tags.tag_id = tags.id
81
- WHERE hosts_tags.host_id = ? AND tags.name = ?;
82
- EOS
83
- logger.debug("get_hosts_q2(%s, %s)" % [host_id.inspect, tag_name.inspect])
84
- @get_hosts_q2.execute(host_id, tag_name).map { |row| row.first }.join(",")
71
+ if glob?(tag_name)
72
+ select_tag_values_from_hosts_tags_by_host_id_and_tag_name_glob(@db, host_id, tag_name)
85
73
  else
86
- @get_hosts_q5 ||= @db.prepare(<<-EOS)
87
- SELECT tags.value FROM hosts_tags
88
- INNER JOIN tags ON hosts_tags.tag_id = tags.id
89
- WHERE hosts_tags.host_id = ? AND tags.name GLOB ?;
90
- EOS
91
- logger.debug("get_hosts_q5(%s, %s)", host_id.inspect, tag_name.inspect)
92
- @get_hosts_q5.execute(host_id, tag_name).map { |row| row.first }.join(",")
74
+ select_tag_values_from_hosts_tags_by_host_id_and_tag_name(@db, host_id, tag_name)
93
75
  end
94
76
  end
95
77
  }
96
78
  }
97
79
  fields = tags
98
80
  else
99
- if options[:listing]
100
-
81
+ if @listing
101
82
  fields = []
102
- hosts = execute(<<-EOS % hosts.map { "?" }.join(", "), hosts)
103
- SELECT id, name FROM hosts WHERE id IN (%s) ORDER BY name;
104
- EOS
83
+ hosts = execute("SELECT id, name FROM hosts WHERE id IN (%s)" % hosts.map { "?" }.join(", "), hosts)
105
84
  result = hosts.map { |host_id, host_name|
106
- update_host_tags(host_name, @options.dup)
107
- @get_hosts_q3 ||= @db.prepare(<<-EOS)
108
- SELECT DISTINCT tags.name FROM hosts_tags
109
- INNER JOIN tags ON hosts_tags.tag_id = tags.id
110
- WHERE hosts_tags.host_id = ?;
111
- EOS
112
- logger.debug("get_hosts_q3(%s)" % [host_id.inspect])
113
- tag_names = @get_hosts_q3.execute(host_id).map { |row| row.first }
85
+ tag_names = select_tag_names_from_hosts_tags_by_host_id(@db, host_id)
114
86
  tag_names.each do |tag_name|
115
87
  fields << tag_name unless fields.index(tag_name)
116
88
  end
117
89
  [host_name] + fields.map { |tag_name|
118
- @get_hosts_q4 ||= @db.prepare(<<-EOS)
119
- SELECT tags.value FROM hosts_tags
120
- INNER JOIN tags ON hosts_tags.tag_id = tags.id
121
- WHERE hosts_tags.host_id = ? AND tags.name = ?;
122
- EOS
123
- logger.debug("get_hosts_q4(%s, %s)" % [host_id.inspect, tag_name.inspect])
124
- @get_hosts_q4.execute(host_id, tag_name).map { |row| row.first }.join(",")
90
+ select_tag_values_from_hosts_tags_by_host_id_and_tag_name(@db, host_id, tag_name)
125
91
  }
126
92
  }
127
93
  fields = ["host"] + fields
128
94
  else
129
95
  fields = ["host"]
130
- result = execute(<<-EOS % hosts.map { "?" }.join(", "), hosts)
131
- SELECT name FROM hosts WHERE id IN (%s) ORDER BY name;
132
- EOS
96
+ result = execute("SELECT name FROM hosts WHERE id IN (%s)" % hosts.map { "?" }.join(", "), hosts)
133
97
  end
134
98
  end
135
99
  [result, fields]
136
100
  end
137
101
 
138
- def update_hosts(options={})
139
- if suspended?
140
- return
141
- else
142
- if not options[:force]
143
- # Update host list on every expirations to update frequently.
144
- @update_hosts_q1 ||= @db.prepare("SELECT MIN(expires_at) FROM hosts_tags;")
145
- logger.debug("update_hosts_q1()")
146
- if expires_at = @update_hosts_q1.execute().map { |row| row.first }.first
147
- if Time.new.to_i < expires_at
148
- logger.debug("next update will run after %s." % [Time.at(expires_at)])
149
- return
150
- else
151
- logger.debug("minimum expires_at was %s. start updateing." % [Time.at(expires_at)])
152
- end
153
- else
154
- logger.debug("expires_at not found. start updateing.")
102
+ def update_db(options={})
103
+ if @db.nil?
104
+ FileUtils.mkdir_p(confdir)
105
+ persistent = File.join(confdir, "persistent.db")
106
+
107
+ if not @force and File.exist?(persistent) and Time.new < File.mtime(persistent) + expiry
108
+ begin
109
+ persistent_db = SQLite3::Database.new(persistent)
110
+ persistent_db.execute("SELECT id, name FROM hosts LIMIT 1")
111
+ persistent_db.execute("SELECT id, name, value FROM tags LIMIT 1")
112
+ persistent_db.execute("SELECT host_id, tag_id FROM hosts_tags LIMIT 1")
113
+ @db = persistent_db
114
+ return
115
+ rescue SQLite3::SQLException
116
+ persistent_db.close()
155
117
  end
156
118
  end
157
119
 
158
- code, result = @dog.search("hosts:")
159
- logger.debug("dog.serarch(%s) #==> [%s, %s]" % ["hosts:".inspect, code.inspect, result.inspect])
120
+ memory_db = SQLite3::Database.new(":memory:")
121
+ create_table_hosts(memory_db)
122
+ create_index_hosts(memory_db)
123
+ create_table_tags(memory_db)
124
+ create_index_tags(memory_db)
125
+ create_table_hosts_tags(memory_db)
126
+ create_index_hosts_tags(memory_db)
127
+
128
+ code, result = @dog.all_tags()
129
+ logger.debug("dog.all_tags() #==> [%s, ...]" % [code.inspect])
160
130
  if code.to_i / 100 != 2
161
- raise("dog.search(%s) returns (%s: %s)" % ["hosts:".inspect, code.inspect, result.inspect])
131
+ raise("dog.all_tags() returns (%s: ...)" % [code.inspect])
162
132
  end
163
133
 
164
- resume_host_tags
165
- execute(<<-EOS % result["results"]["hosts"].map { "LOWER(?)" }.join(", "), result["results"]["hosts"])
166
- DELETE FROM hosts_tags WHERE host_id NOT IN
167
- ( SELECT id FROM hosts WHERE LOWER(name) IN ( %s ) );
168
- EOS
169
-
170
- result["results"]["hosts"].each_with_index do |host_name, i|
171
- @update_hosts_q2 ||= @db.prepare("INSERT OR IGNORE INTO hosts (name) VALUES (?);")
172
- logger.debug("update_hosts_q2(%s)" % [host_name.inspect])
173
- @update_hosts_q2.execute(host_name)
174
- update_host_tags(host_name, options)
175
-
176
- elapsed_time = Time.new - @started_at
177
- if 0 < options[:max_time] and options[:max_time] < elapsed_time
178
- length = result["results"]["hosts"].length
179
- logger.info("update_host_tags: exceeded maximum time (#{options[:max_time]} < #{elapsed_time}) after #{i+1}/#{length}. will resume on next run.")
180
- suspend_host_tags
181
- break
134
+ result["tags"].each do |tag, hosts|
135
+ tag_name, tag_value = tag.split(":", 2)
136
+ tag_value ||= ""
137
+ insert_or_ignore_into_tags(memory_db, tag_name, tag_value)
138
+ hosts.each do |host_name|
139
+ insert_or_ignore_into_hosts(memory_db, host_name)
140
+ insert_or_replace_into_hosts_tags(memory_db, host_name, tag_name, tag_value)
182
141
  end
183
142
  end
143
+
144
+ # backup in-memory db to file
145
+ FileUtils.rm_f(persistent)
146
+ persistent_db = SQLite3::Database.new(persistent)
147
+ copy_db(memory_db, persistent_db)
148
+ persistent_db.close
149
+ @db = memory_db
150
+ else
151
+ @db
184
152
  end
185
153
  end
186
154
 
187
- def update_tags(options={})
188
- if suspended?
189
- return
190
- else
191
- resume_host_tags
192
-
193
- if options[:force]
194
- @update_tags_q1 ||= @db.prepare(<<-EOS)
195
- SELECT DISTINCT hosts_tags.host_id FROM hosts_tags;
196
- EOS
197
- logger.debug("update_tags_q1()")
198
- hosts = @update_tags_q1.execute().map { |row| row.first }
199
- else
200
- @update_tags_q2 ||= @db.prepare(<<-EOS)
201
- SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
202
- WHERE hosts_tags.expires_at < ?;
203
- EOS
204
- logger.debug("update_tags_q2(%s)" % [Time.new.to_i])
205
- hosts = @update_tags_q2.execute(Time.new.to_i).map { |row| row.first }
155
+ def copy_db(src, dst)
156
+ # create index later for better insert performance
157
+ dst.transaction do
158
+ create_table_hosts(dst)
159
+ create_table_tags(dst)
160
+ create_table_hosts_tags(dst)
161
+
162
+ select_from_hosts(src).each do |host_id, host_name|
163
+ insert_into_hosts(dst, host_id, host_name)
206
164
  end
207
- hosts.each_with_index do |host_id, i|
208
- @update_tags_q3 ||= @db.prepare("DELETE FROM hosts_tags WHERE host_id = ? AND hosts_tags.expires_at < ?;")
209
- logger.debug("update_tags_q3(%s, %s)" % [host_id.inspect, Time.new.to_i])
210
- @update_tags_q3.execute(host_id, Time.new.to_i)
211
-
212
- update_host_tags(host_id, options)
213
-
214
- elapsed_time = Time.new - @started_at
215
- if 0 < options[:max_time] and options[:max_time] < elapsed_time
216
- length = hosts.length
217
- logger.info("update_host_tags: exceeded maximum time (#{options[:max_time]} < #{elapsed_time}) after #{i+1}/#{length}. will resume on next run.")
218
- suspend_host_tags
219
- break
220
- end
165
+ select_from_tags(src).each do |tag_id, tag_name, tag_value|
166
+ insert_into_tags(dst, tag_id, tag_name, tag_value)
167
+ end
168
+ select_from_hosts_tags(src).each do |host_id, tag_id|
169
+ insert_into_hosts_tags(dst, host_id, tag_id)
221
170
  end
171
+
172
+ create_index_hosts(dst)
173
+ create_index_tags(dst)
174
+ create_index_hosts_tags(dst)
222
175
  end
223
176
  end
224
177
 
225
- # it'd be better to filter out this host/tag entry on displaying...
226
- EMPTY_HOST_NAME = ""
227
- EMPTY_TAG_NAME = ""
228
- EMPTY_TAG_VALUE = ""
229
- EMPTY_EXPIRES_AT = Time.at(0).to_i
230
-
231
- def suspend_host_tags()
232
- @db.transaction do
233
- @suspended = true
234
- @suspend_host_tags_q1 ||= @db.prepare("INSERT OR IGNORE INTO hosts (name) VALUES (?);")
235
- logger.debug("suspend_host_tags_q1(%s)" % [EMPTY_HOST_NAME.inspect])
236
- @suspend_host_tags_q1.execute(EMPTY_HOST_NAME)
237
- @suspend_host_tags_q2 ||= @db.prepare("INSERT OR IGNORE INTO tags (name, value) VALUES (?, ?);")
238
- logger.debug("suspend_host_tags_q2(%s, %s)" % [EMPTY_TAG_NAME.inspect, EMPTY_TAG_VALUE.inspect])
239
- @suspend_host_tags_q2.execute(EMPTY_TAG_NAME, EMPTY_TAG_VALUE)
240
- @suspend_host_tags_q3 ||= @db.prepare(<<-EOS)
241
- INSERT OR REPLACE INTO hosts_tags (host_id, tag_id, expires_at)
242
- SELECT host.id, tag.id, ? FROM
243
- ( SELECT id FROM hosts WHERE name = ?) AS host,
244
- ( SELECT id FROM tags WHERE name = ? AND value = ? ) AS tag;
245
- EOS
246
- logger.debug("suspend_host_tags_q3(%s, %s, %s, %s)" % [EMPTY_EXPIRES_AT.inspect, EMPTY_HOST_NAME.inspect, EMPTY_TAG_NAME.inspect, EMPTY_TAG_VALUE.inspect])
247
- @suspend_host_tags_q3.execute(EMPTY_EXPIRES_AT, EMPTY_HOST_NAME, EMPTY_TAG_NAME, EMPTY_TAG_VALUE)
248
- end
178
+ def select_from_hosts(db)
179
+ logger.debug("select_from_hosts()")
180
+ db.execute("SELECT id, name FROM hosts")
249
181
  end
250
182
 
251
- def resume_host_tags()
252
- @resume_host_tags_q1 ||= @db.prepare(<<-EOS)
253
- DELETE FROM hosts_tags
254
- WHERE host_id IN ( SELECT id FROM hosts WHERE name = ? ) AND tag_id IN ( SELECT id FROM tags WHERE name = ? AND value = ? );
183
+ def select_from_tags(db)
184
+ logger.debug("select_from_tags()")
185
+ db.execute("SELECT id, name, value FROM tags")
186
+ end
187
+
188
+ def select_from_hosts_tags(db)
189
+ logger.debug("select_from_hosts_tags()")
190
+ db.execute("SELECT host_id, tag_id FROM hosts_tags")
191
+ end
192
+
193
+ def create_table_hosts(db)
194
+ logger.debug("create_table_hosts()")
195
+ db.execute(<<-EOS)
196
+ CREATE TABLE IF NOT EXISTS hosts (
197
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
198
+ name VARCHAR(255) NOT NULL
199
+ );
255
200
  EOS
256
- logger.debug("resume_host_tags_q1(%s, %s, %s)" % [EMPTY_HOST_NAME.inspect, EMPTY_TAG_NAME.inspect, EMPTY_TAG_VALUE.inspect])
257
- @resume_host_tags_q1.execute(EMPTY_HOST_NAME, EMPTY_TAG_NAME, EMPTY_TAG_VALUE)
258
201
  end
259
202
 
260
- def update_host_tags(host_name, options={})
261
- if suspended?
262
- # stop updating if the `update_host_tags` has already been suspended
263
- return
264
- else
265
- if Integer === host_name
266
- host_id = host_name
267
- @update_host_tags_q1 ||= @db.prepare("SELECT name FROM hosts WHERE id = ? LIMIT 1;")
268
- logger.debug("update_host_tags_q1(%s)" % [host_id.inspect])
269
- host_name = @update_host_tags_q1.execute(host_id).map { |row| row.first }.first
270
- else
271
- @update_host_tags_q2 ||= @db.prepare("SELECT id, name FROM hosts WHERE LOWER(name) = LOWER(?) LIMIT 1;")
272
- logger.debug("update_host_tags_q2(%s)" % [host_name.inspect])
273
- host_id, host_name = @update_host_tags_q2.execute(host_name).map { |row| row }.first
274
- end
203
+ def create_index_hosts(db)
204
+ logger.debug("create_index_hosts()")
205
+ db.execute("CREATE UNIQUE INDEX IF NOT EXISTS hosts_name ON hosts ( name )")
206
+ end
275
207
 
276
- if not options[:force]
277
- # Update host tags less frequently.
278
- # Don't need to run updates on every expiration.
279
- @update_host_tags_q3 ||= @db.prepare("SELECT AVG(expires_at) FROM hosts_tags WHERE host_id = ?;")
280
- logger.debug("update_host_tags_q3(%s)" % [host_id.inspect])
281
- if expires_at = @update_host_tags_q3.execute(host_id).map { |row| row.first }.first
282
- if Time.new.to_i < expires_at
283
- logger.debug("%s: next update will run after %s." % [host_name, Time.at(expires_at)])
284
- return
285
- else
286
- logger.debug("%s: average expires_at was %s. start updating." % [host_name, Time.at(expires_at)])
287
- end
288
- else
289
- logger.debug("%s: expires_at not found. start updateing." % [host_name])
290
- end
291
- end
208
+ def create_table_tags(db)
209
+ logger.debug("create_table_tags()")
210
+ db.execute(<<-EOS)
211
+ CREATE TABLE IF NOT EXISTS tags (
212
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
213
+ name VARCHAR(200) NOT NULL,
214
+ value VARCHAR(200) NOT NULL DEFAULT ""
215
+ );
216
+ EOS
217
+ end
292
218
 
293
- code, result = @dog.host_tags(host_name)
294
- logger.debug("dog.host_tags(%s) #==> [%s, %s]" % [host_name.inspect, code.inspect, result.inspect])
295
- if code.to_i / 100 != 2
296
- case code.to_i
297
- when 404 # host not found on datadog
298
- @update_host_tags_q7 ||= @db.prepare("DELETE FROM hosts_tags WHERE host_id IN ( SELECT id FROM hosts WHERE LOWER(name) = LOWER(?) );")
299
- logger.debug("update_host_tags_q7(%s)" % [host_name.inspect])
300
- @update_host_tags_q7.execute(host_name)
301
- end
302
- raise("dog.host_tags(%s) returns (%s: %s)" % [host_name.inspect, code.inspect, result.inspect])
303
- end
219
+ def create_index_tags(db)
220
+ logger.debug("create_index_tags()")
221
+ db.execute("CREATE UNIQUE INDEX IF NOT EXISTS tags_name_value ON tags ( name, value )")
222
+ end
304
223
 
305
- expires_at = Time.new.to_i + (options[:minimum_expiry] + rand(options[:random_expiry]))
306
- logger.debug("%s: expires_at=%s" % [host_name, Time.at(expires_at)])
224
+ def create_table_hosts_tags(db)
225
+ logger.debug("create_table_hosts_tags()")
226
+ db.execute(<<-EOS)
227
+ CREATE TABLE IF NOT EXISTS hosts_tags (
228
+ host_id INTEGER NOT NULL,
229
+ tag_id INTEGER NOT NULL
230
+ );
231
+ EOS
232
+ end
307
233
 
308
- @db.transaction do
309
- result["tags"].each do |tag|
310
- tag_name, tag_value = tag.split(":", 2)
311
- tag_value ||= ""
234
+ def create_index_hosts_tags(db)
235
+ logger.debug("create_index_hosts_tags()")
236
+ db.execute("CREATE UNIQUE INDEX IF NOT EXISTS hosts_tags_host_id_tag_id ON hosts_tags ( host_id, tag_id )")
237
+ end
312
238
 
313
- if options.has_key?(:tags) and not options[:tags].empty? and not options[:tags].index(tag_name)
314
- next
315
- else
316
- @update_host_tags_q4 ||= @db.prepare("INSERT OR IGNORE INTO tags (name, value) VALUES (?, ?);")
317
- logger.debug("update_host_tags_q4(%s, %s)" % [tag_name.inspect, tag_value.inspect])
318
- @update_host_tags_q4.execute(tag_name, tag_value)
319
- @update_host_tags_q5 ||= @db.prepare(<<-EOS)
320
- INSERT OR REPLACE INTO hosts_tags (host_id, tag_id, expires_at)
321
- SELECT host.id, tag.id, ? FROM
322
- ( SELECT id FROM hosts WHERE name = ? ) AS host,
323
- ( SELECT id FROM tags WHERE name = ? AND value = ? ) AS tag;
324
- EOS
325
- logger.debug("update_host_tags_q5(%s, %s)" % [expires_at, host_name, tag_name, tag_value])
326
- @update_host_tags_q5.execute(expires_at, host_name, tag_name, tag_value)
327
- end
328
- end
239
+ def insert_into_tags(db, tag_id, tag_name, tag_value)
240
+ logger.debug("insert_into_tags(%s, %s, %s)" % [tag_id.inspect, tag_name.inspect, tag_value.inspect])
241
+ db.execute("INSERT INTO tags (id, name, value) VALUES (?, ?, ?)", tag_id, tag_name, tag_value)
242
+ end
329
243
 
330
- @update_host_tags_q6 ||= @db.prepare(<<-EOS)
331
- DELETE FROM hosts_tags WHERE host_id = ? and expires_at <= ?;
332
- EOS
333
- logger.debug("update_host_tags_q6(%s, %s)" % [host_id.inspect, Time.new.to_i.inspect])
334
- @update_host_tags_q6.execute(host_id, Time.new.to_i)
335
- end
336
- end
244
+ def insert_or_ignore_into_tags(db, tag_name, tag_value)
245
+ logger.debug("insert_or_ignore_into_tags(%s, %s)" % [tag_name.inspect, tag_value.inspect])
246
+ db.execute("INSERT OR IGNORE INTO tags (name, value) VALUES (?, ?)", tag_name, tag_value)
247
+ end
248
+
249
+ def insert_into_hosts(db, host_id, host_name)
250
+ logger.debug("insert_into_hosts(%s, %s)" % [host_id.inspect, host_name.inspect])
251
+ db.execute("INSERT INTO hosts (id, name) VALUES (?, ?)", host_id, host_name)
252
+ end
253
+
254
+ def insert_or_ignore_into_hosts(db, host_name)
255
+ logger.debug("insert_or_ignore_into_hosts(%s)" % [host_name.inspect])
256
+ db.execute("INSERT OR IGNORE INTO hosts (name) VALUES (?)", host_name)
257
+ end
258
+
259
+ def insert_into_hosts_tags(db, host_id, tag_id)
260
+ logger.debug("insert_into_hosts_tags(%s, %s)" % [host_id.inspect, tag_id.inspect])
261
+ db.execute("INSERT INTO hosts_tags (host_id, tag_id) VALUES (?, ?)", host_id, tag_id)
262
+ end
263
+
264
+ def insert_or_replace_into_hosts_tags(db, host_name, tag_name, tag_value)
265
+ logger.debug("insert_or_replace_into_hosts_tags(%s, %s, %s)" % [host_name.inspect, tag_name.inspect, tag_value.inspect])
266
+ db.execute(<<-EOS, host_name, tag_name, tag_value)
267
+ INSERT OR REPLACE INTO hosts_tags (host_id, tag_id)
268
+ SELECT host.id, tag.id FROM
269
+ ( SELECT id FROM hosts WHERE name = ? ) AS host,
270
+ ( SELECT id FROM tags WHERE name = ? AND value = ? ) AS tag;
271
+ EOS
272
+ end
273
+
274
+ def select_name_from_hosts_by_id(db, host_id)
275
+ logger.debug("select_name_from_hosts_by_id(%s)" % [host_id.inspect])
276
+ db.execute("SELECT name FROM hosts WHERE id = ? LIMIT 1", host_id).map { |row| row.first }.first
277
+ end
278
+
279
+ def select_tag_values_from_hosts_tags_by_host_id_and_tag_name_glob(db, host_id, tag_name)
280
+ logger.debug("select_tag_values_from_hosts_tags_by_host_id_and_tag_name_glob(%s, %s)", host_id.inspect, tag_name.inspect)
281
+ db.execute(<<-EOS, host_id, tag_name).map { |row| row.first }.join(",")
282
+ SELECT tags.value FROM hosts_tags
283
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
284
+ WHERE hosts_tags.host_id = ? AND tags.name GLOB ?;
285
+ EOS
286
+ end
287
+
288
+ def select_tag_values_from_hosts_tags_by_host_id_and_tag_name(db, host_id, tag_name)
289
+ logger.debug("select_tag_values_from_hosts_tags_by_host_id_and_tag_name(%s, %s)" % [host_id.inspect, tag_name.inspect])
290
+ db.execute(<<-EOS, host_id, tag_name).map { |row| row.first }.join(",")
291
+ SELECT tags.value FROM hosts_tags
292
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
293
+ WHERE hosts_tags.host_id = ? AND tags.name = ?;
294
+ EOS
295
+ end
296
+
297
+ def select_tag_names_from_hosts_tags_by_host_id(db, host_id)
298
+ logger.debug("select_tag_names_from_hosts_tags_by_host_id(%s)" % [host_id.inspect])
299
+ db.execute(<<-EOS, host_id).map { |row| row.first }
300
+ SELECT DISTINCT tags.name FROM hosts_tags
301
+ INNER JOIN tags ON hosts_tags.tag_id = tags.id
302
+ WHERE hosts_tags.host_id = ?;
303
+ EOS
337
304
  end
338
305
  end
339
306
  end
@@ -4,43 +4,22 @@ module Hotdog
4
4
  module Commands
5
5
  class Hosts < BaseCommand
6
6
  def run(args=[])
7
- application.run_command("init")
8
-
9
7
  if args.empty?
10
- update_hosts(@options.dup)
11
- @hosts_q1 ||= @db.prepare(<<-EOS)
12
- SELECT DISTINCT host_id FROM hosts_tags;
13
- EOS
14
- logger.debug("hosts_q1()")
15
- result = @hosts_q1.execute().to_a.reduce(:+)
8
+ result = execute("SELECT DISTINCT host_id FROM hosts_tags").to_a.reduce(:+)
16
9
  else
17
- if args.map { |host_name| glob?(host_name) }.any?
18
- update_hosts(@options.dup)
19
- else
20
- args.each do |host_name|
21
- @hosts_q4 ||= @db.prepare("INSERT OR IGNORE INTO hosts (name) VALUES (?);")
22
- logger.debug("hosts_q4(%s)" % [host_name.inspect])
23
- @hosts_q4.execute(host_name)
24
- update_host_tags(host_name, @options.dup)
25
- end
26
- end
27
10
  result = args.map { |host_name|
28
11
  if glob?(host_name)
29
- @hosts_q2 ||= @db.prepare(<<-EOS)
12
+ execute(<<-EOS, host_name).map { |row| row.first }
30
13
  SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
31
14
  INNER JOIN hosts ON hosts_tags.host_id = hosts.id
32
15
  WHERE LOWER(hosts.name) GLOB LOWER(?);
33
16
  EOS
34
- logger.debug("hosts_q2(%s)" % [host_name.inspect])
35
- @hosts_q2.execute(host_name).map { |row| row.first }
36
17
  else
37
- @hosts_q3 ||= @db.prepare(<<-EOS)
18
+ execute(<<-EOS, host_name).map { |row| row.first }
38
19
  SELECT DISTINCT hosts_tags.host_id FROM hosts_tags
39
20
  INNER JOIN hosts ON hosts_tags.host_id = hosts.id
40
21
  WHERE LOWER(hosts.name) = LOWER(?);
41
22
  EOS
42
- logger.debug("hosts_q3(%s)" % [host_name.inspect])
43
- @hosts_q3.execute(host_name).map { |row| row.first }
44
23
  end
45
24
  }.reduce(:+)
46
25
  end
@@ -7,20 +7,18 @@ module Hotdog
7
7
  module Commands
8
8
  class Search < BaseCommand
9
9
  def run(args=[])
10
- application.run_command("init")
11
10
  expression = args.join(" ").strip
12
11
  if expression.empty?
13
12
  exit(1)
14
13
  end
15
14
 
16
- update_hosts(@options.dup)
17
-
18
15
  begin
19
16
  node = parse(expression)
20
17
  rescue Parslet::ParseFailed => error
21
18
  STDERR.puts("syntax error: " + error.cause.ascii_tree)
22
19
  exit(1)
23
20
  end
21
+
24
22
  result = evaluate(node, self).sort
25
23
  if 0 < result.length
26
24
  result, fields = get_hosts(result)
@@ -4,8 +4,6 @@ module Hotdog
4
4
  module Commands
5
5
  class Tags < BaseCommand
6
6
  def run(args=[])
7
- application.run_command("init")
8
- update_tags(@options.dup)
9
7
  if 0 < tags.length
10
8
  fields = tags.map { |tag|
11
9
  tag_name, tag_value = tag.split(":", 2)
@@ -13,21 +11,17 @@ module Hotdog
13
11
  }
14
12
  result1 = fields.map { |tag_name|
15
13
  if not glob?(tag_name)
16
- @tags_q1 ||= @db.prepare(<<-EOS)
14
+ execute(<<-EOS, tag_name).map { |row| row.join(",") }
17
15
  SELECT DISTINCT tags.value FROM hosts_tags
18
16
  INNER JOIN tags ON hosts_tags.tag_id = tags.id
19
17
  WHERE tags.name = LOWER(?);
20
18
  EOS
21
- logger.debug("tags_q1(%s)" % [tag_name.inspect])
22
- @tags_q1.execute(tag_name).map { |row| row.join(",") }
23
19
  else
24
- @tags_q2 ||= @db.prepare(<<-EOS)
20
+ execute(<<-EOS, tag_name).map { |row| row.join(",") }
25
21
  SELECT DISTINCT tags.value FROM hosts_tags
26
22
  INNER JOIN tags ON hosts_tags.tag_id = tags.id
27
23
  WHERE tags.name GLOB LOWER(?);
28
24
  EOS
29
- logger.debug("tags_q2(%s)" % [tag_name.inspect])
30
- @tags_q2.execute(tag_name).map { |row| row.join(",") }
31
25
  end
32
26
  }
33
27
  result = (0..result1.reduce(0) { |max, values| [max, values.length].max }).map { |field_index|
@@ -35,12 +29,10 @@ module Hotdog
35
29
  }
36
30
  else
37
31
  fields = ["tag"]
38
- @tags_q3 ||= @db.prepare(<<-EOS)
39
- SELECT tags.name, tags.value FROM hosts_tags
32
+ result = execute(<<-EOS).map { |name, value| [0 < value.length ? "#{name}:#{value}" : name] }
33
+ SELECT DISTINCT tags.name, tags.value FROM hosts_tags
40
34
  INNER JOIN tags ON hosts_tags.tag_id = tags.id;
41
35
  EOS
42
- logger.debug("tags_q3()")
43
- result = @tags_q3.execute().map { |name, value| [0 < value.length ? "#{name}:#{value}" : name] }
44
36
  end
45
37
  if 0 < result.length
46
38
  STDOUT.print(format(result, fields: fields))
@@ -1,3 +1,3 @@
1
1
  module Hotdog
2
- VERSION = "0.0.7"
2
+ VERSION = "0.1.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hotdog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.1.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-01-11 00:00:00.000000000 Z
11
+ date: 2015-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -97,16 +97,11 @@ files:
97
97
  - hotdog.gemspec
98
98
  - lib/hotdog/application.rb
99
99
  - lib/hotdog/commands.rb
100
- - lib/hotdog/commands/destroy.rb
101
- - lib/hotdog/commands/gc.rb
102
100
  - lib/hotdog/commands/help.rb
103
101
  - lib/hotdog/commands/hosts.rb
104
- - lib/hotdog/commands/init.rb
105
102
  - lib/hotdog/commands/ls.rb
106
- - lib/hotdog/commands/rm.rb
107
103
  - lib/hotdog/commands/search.rb
108
104
  - lib/hotdog/commands/tags.rb
109
- - lib/hotdog/commands/update.rb
110
105
  - lib/hotdog/formatters.rb
111
106
  - lib/hotdog/formatters/json.rb
112
107
  - lib/hotdog/formatters/plain.rb
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- module Hotdog
4
- module Commands
5
- class Destroy < BaseCommand
6
- def run(args=[])
7
- execute(<<-EOS)
8
- DROP TABLE IF EXISTS hosts;
9
- EOS
10
- execute(<<-EOS)
11
- DROP TABLE IF EXISTS tags;
12
- EOS
13
- execute(<<-EOS)
14
- DROP TABLE IF EXISTS hosts_tags;
15
- EOS
16
- end
17
- end
18
- end
19
- end
20
-
21
- # vim:set ft=ruby :
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- module Hotdog
4
- module Commands
5
- class Gc < BaseCommand
6
- def run(args=[])
7
- application.run_command("init")
8
- execute(<<-EOS)
9
- DELETE FROM hosts WHERE id NOT IN ( SELECT DISTINCT host_id FROM hosts_tags );
10
- EOS
11
- execute(<<-EOS)
12
- DELETE FROM tags WHERE id NOT IN ( SELECT DISTINCT tag_id FROM hosts_tags );
13
- EOS
14
- end
15
- end
16
- end
17
- end
18
-
19
- # vim:set ft=ruby :
@@ -1,47 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- module Hotdog
4
- module Commands
5
- class Init < BaseCommand
6
- def run(args=[])
7
- execute(<<-EOS)
8
- CREATE TABLE IF NOT EXISTS hosts (
9
- id INTEGER PRIMARY KEY AUTOINCREMENT,
10
- name VARCHAR(255) NOT NULL
11
- );
12
- EOS
13
- execute(<<-EOS)
14
- CREATE UNIQUE INDEX IF NOT EXISTS hosts_name ON hosts ( name );
15
- EOS
16
- execute(<<-EOS)
17
- CREATE TABLE IF NOT EXISTS tags (
18
- id INTEGER PRIMARY KEY AUTOINCREMENT,
19
- name VARCHAR(200) NOT NULL,
20
- value VARCHAR(200) NOT NULL DEFAULT ""
21
- );
22
- EOS
23
- execute(<<-EOS)
24
- CREATE UNIQUE INDEX IF NOT EXISTS tags_name_value ON tags ( name, value );
25
- EOS
26
- execute(<<-EOS)
27
- CREATE TABLE IF NOT EXISTS hosts_tags (
28
- host_id INTEGER NOT NULL,
29
- tag_id INTEGER NOT NULL,
30
- expires_at INTEGER NOT NULL
31
- );
32
- EOS
33
- execute(<<-EOS)
34
- CREATE UNIQUE INDEX IF NOT EXISTS hosts_tags_host_id_tag_id ON hosts_tags ( host_id, tag_id );
35
- EOS
36
- execute(<<-EOS)
37
- CREATE INDEX IF NOT EXISTS hosts_tags_expires_at ON hosts_tags ( expires_at );
38
- EOS
39
- execute(<<-EOS)
40
- CREATE INDEX IF NOT EXISTS hosts_tags_host_id_expires_at ON hosts_tags ( host_id, expires_at );
41
- EOS
42
- end
43
- end
44
- end
45
- end
46
-
47
- # vim:set ft=ruby :
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- module Hotdog
4
- module Commands
5
- class Rm < BaseCommand
6
- def run(args=[])
7
- execute(<<-EOS % args.map { "?" }.join(", "), args).map { |row| row.first }
8
- DELETE FROM hosts_tags
9
- WHERE host_id IN
10
- ( SELECT hosts_tags.host_id FROM hosts_tags
11
- INNER JOIN hosts ON hosts_tags.host_id = hosts.id
12
- WHERE hosts.name NOT IN (%s) );
13
- EOS
14
- end
15
- end
16
- end
17
- end
18
-
19
- # vim:set ft=ruby :
@@ -1,21 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- module Hotdog
4
- module Commands
5
- class Update < BaseCommand
6
- def run(args=[])
7
- application.run_command("init")
8
- options[:max_time] = -1
9
- if 0 < args.length
10
- args.each do |host_name|
11
- update_host_tags(host_name, @options.dup)
12
- end
13
- else
14
- update_hosts(@options.dup)
15
- end
16
- end
17
- end
18
- end
19
- end
20
-
21
- # vim:set ft=ruby :