hotdog 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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 :