hotdog 0.2.2 → 0.3.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: 12b1b97f8c77f181a665ba52db0725c613d638a7
4
- data.tar.gz: 73be8bb8526df6c4bb95811e6e56de196fae0a73
3
+ metadata.gz: fccd372d49246c2c85768c9244a29746fafcfb40
4
+ data.tar.gz: 9b4d490218a3d93af4806b12d47dfca28f8d90f8
5
5
  SHA512:
6
- metadata.gz: 602156cf61f52c8f8c14937dcf324b93516ac16b6f0e61c971bcccb124a7827902e313c400fdcb98eb54d83f8ae2cf7a21783d4f1937df18899c20782380224a
7
- data.tar.gz: eb9ab65fad8b6b25e59eec751cc02b4a99cc0d69fc5f2fc61a7c6df2c9fbce5055928ed2ab8f2c85b7af2a8580f9db51e5534c277fac89a100ab7e9b7064eb27
6
+ metadata.gz: 8e21eb61e7ccfc2b533e005eb1f494ead1c16c12cd21e0384d46228e88f3a5d6771a62f9006fca59bc538e680e4a7370f1a1905c3e1aa314ea368e4b97146a92
7
+ data.tar.gz: 654ccf67781c3c1a7ebc1f59fac07a5dd6319231474e6ffadcd08309d32b062efe7e543c1db83f7d821bb054a6ea31c111b7c717f63f0692b3929a6fc79df971
data/README.md CHANGED
@@ -100,24 +100,33 @@ $ hotdog ssh availability-zone:us-east-1b and 'name:web-*' -t public_ipv4 -u use
100
100
  Acceptable expressions in pseudo BNF.
101
101
 
102
102
  ```
103
- expression: binary_expression
104
- | term
103
+ expression: expression0
105
104
  ;
106
105
 
107
- binary_expression: term "&&" term
108
- | term "||" term
109
- | term "and" term
110
- | term "or" term
111
- ;
112
-
113
- unary_expression: '!' expression
114
- | '~' expression
115
- | "not" expression
116
- ;
117
-
118
- term: unary_expression
119
- | atom
120
- ;
106
+ expression0: expression1 "and" expression
107
+ | expression1 "or" expression
108
+ | expression1
109
+ ;
110
+
111
+ expression1: "not" expression
112
+ | expression2
113
+ ;
114
+
115
+ expression2: expression3 expression
116
+ | expression3
117
+ ;
118
+
119
+ expression3: expression4 "&&" expression
120
+ | expression4 "||" expression
121
+ | expression4
122
+ ;
123
+
124
+ expression4: '!' atom
125
+ | '~' atom
126
+ | '!' expression
127
+ | '~' expression
128
+ | atom
129
+ ;
121
130
 
122
131
  atom: '(' expression ')'
123
132
  | IDENTIFIER separator ATTRIBUTE
@@ -79,38 +79,49 @@ module Hotdog
79
79
  class ExpressionParser < Parslet::Parser
80
80
  root(:expression)
81
81
  rule(:expression) {
82
- ( binary_expression \
83
- | term \
82
+ ( expression0 \
84
83
  )
85
84
  }
86
- rule(:binary_op) {
87
- ( str('&') >> str('&').maybe \
88
- | str('|') >> str('|').maybe \
89
- | match('[Aa]') >> match('[Nn]') >> match('[Dd]') \
90
- | match('[Oo]') >> match('[Rr]') \
85
+ rule(:expression0) {
86
+ ( expression1.as(:left) >> spacing.maybe >> binary_op.as(:binary_op) >> spacing.maybe >> expression.as(:right) \
87
+ | expression1 \
91
88
  )
92
89
  }
93
- rule(:binary_expression) {
94
- ( term.as(:left) >> spacing.maybe >> binary_op.as(:binary_op) >> spacing.maybe >> expression.as(:right) \
95
- | term.as(:left) >> spacing.maybe.as(:binary_op) >> expression.as(:right) \
90
+ rule(:expression1) {
91
+ ( unary_op.as(:unary_op) >> spacing.maybe >> expression.as(:expression) \
92
+ | expression2 \
96
93
  )
97
94
  }
98
- rule(:unary_op) {
99
- ( str('!') \
100
- | str('~') \
101
- | match('[Nn]') >> match('[Oo]') >> match('[Tt]') \
95
+ rule(:expression2) {
96
+ ( expression3.as(:left) >> spacing.maybe.as(:binary_op) >> expression.as(:right) \
97
+ | expression3 \
102
98
  )
103
99
  }
104
- rule(:unary_expression) {
105
- ( spacing.maybe >> unary_op.as(:unary_op) >> term.as(:expression) \
106
- | spacing.maybe >> unary_op.as(:unary_op) >> expression.as(:expression) \
100
+ rule(:expression3) {
101
+ ( expression4.as(:left) >> spacing.maybe >> str('&&').as(:binary_op) >> spacing.maybe >> expression.as(:right) \
102
+ | expression4.as(:left) >> spacing.maybe >> str('||').as(:binary_op) >> spacing.maybe >> expression.as(:right) \
103
+ | expression4.as(:left) >> spacing.maybe >> str('&').as(:binary_op) >> spacing.maybe >> expression.as(:right) \
104
+ | expression4.as(:left) >> spacing.maybe >> str('|').as(:binary_op) >> spacing.maybe >> expression.as(:right) \
105
+ | expression4 \
107
106
  )
108
107
  }
109
- rule(:term) {
110
- ( unary_expression \
108
+ rule(:expression4) {
109
+ ( str('!').as(:unary_op) >> spacing.maybe >> atom.as(:expression) \
110
+ | str('~').as(:unary_op) >> spacing.maybe >> atom.as(:expression) \
111
+ | str('!').as(:unary_op) >> spacing.maybe >> expression.as(:expression) \
112
+ | str('~').as(:unary_op) >> spacing.maybe >> expression.as(:expression) \
111
113
  | atom \
112
114
  )
113
115
  }
116
+ rule(:binary_op) {
117
+ ( str('and') \
118
+ | str('or') \
119
+ )
120
+ }
121
+ rule(:unary_op) {
122
+ ( str('not') \
123
+ )
124
+ }
114
125
  rule(:atom) {
115
126
  ( spacing.maybe >> str('(') >> expression >> str(')') >> spacing.maybe \
116
127
  | spacing.maybe >> identifier_regexp.as(:identifier_regexp) >> separator >> attribute_regexp.as(:attribute_regexp) >> spacing.maybe \
@@ -137,11 +148,11 @@ module Hotdog
137
148
  )
138
149
  }
139
150
  rule(:identifier_glob) {
140
- ( unary_op.absent? >> binary_op.absent? >> identifier.repeat(0) >> (glob >> identifier.maybe).repeat(1) \
151
+ ( binary_op.absent? >> unary_op.absent? >> identifier.repeat(0) >> (glob >> identifier.maybe).repeat(1) \
141
152
  )
142
153
  }
143
154
  rule(:identifier) {
144
- ( unary_op.absent? >> binary_op.absent? >> match('[A-Za-z]') >> match('[-./0-9A-Z_a-z]').repeat(0) \
155
+ ( binary_op.absent? >> unary_op.absent? >> match('[A-Za-z]') >> match('[-./0-9A-Z_a-z]').repeat(0) \
145
156
  )
146
157
  }
147
158
  rule(:separator) {
@@ -154,11 +165,11 @@ module Hotdog
154
165
  )
155
166
  }
156
167
  rule(:attribute_glob) {
157
- ( unary_op.absent? >> binary_op.absent? >> attribute.repeat(0) >> (glob >> attribute.maybe).repeat(1) \
168
+ ( binary_op.absent? >> unary_op.absent? >> attribute.repeat(0) >> (glob >> attribute.maybe).repeat(1) \
158
169
  )
159
170
  }
160
171
  rule(:attribute) {
161
- ( unary_op.absent? >> binary_op.absent? >> match('[-./0-9:A-Z_a-z]').repeat(1) \
172
+ ( binary_op.absent? >> unary_op.absent? >> match('[-./0-9:A-Z_a-z]').repeat(1) \
162
173
  )
163
174
  }
164
175
  rule(:glob) {
@@ -80,36 +80,7 @@ module Hotdog
80
80
  tag_name, tag_value = split_tag(tag)
81
81
  tag_name
82
82
  }
83
- fields_without_host = fields.reject { |tag_name| tag_name == "host" }
84
- if fields == fields_without_host
85
- host_names = {}
86
- else
87
- host_names = Hash[
88
- host_ids.each_slice(SQLITE_LIMIT_COMPOUND_SELECT).flat_map { |host_ids|
89
- execute("SELECT id, name FROM hosts WHERE id IN (%s)" % host_ids.map { "?" }.join(", "), host_ids).map { |row| row.to_a }
90
- }
91
- ]
92
- end
93
- q1 = "SELECT tags.name, GROUP_CONCAT(tags.value, ',') FROM hosts_tags " \
94
- "INNER JOIN hosts ON hosts_tags.host_id = hosts.id " \
95
- "INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
96
- "WHERE hosts_tags.host_id = ? AND tags.name IN (%s) " \
97
- "GROUP BY tags.name;"
98
- result = host_ids.map { |host_id|
99
- tag_values = Hash[
100
- fields_without_host.each_slice(SQLITE_LIMIT_COMPOUND_SELECT - 1).flat_map { |fields_without_host|
101
- execute(q1 % fields_without_host.map { "?" }.join(", "), [host_id] + fields_without_host).map { |row| row.to_a }
102
- }
103
- ]
104
- fields.map { |tag_name|
105
- if tag_name == "host"
106
- host_names.fetch(host_id, "")
107
- else
108
- tag_values.fetch(tag_name, "")
109
- end
110
- }
111
- }
112
- [result, fields]
83
+ get_hosts_fields(host_ids, fields)
113
84
  else
114
85
  if @options[:listing]
115
86
  q1 = "SELECT DISTINCT tags.name FROM hosts_tags " \
@@ -124,61 +95,62 @@ module Hotdog
124
95
  tag_name == @options[:primary_tag]
125
96
  }
126
97
  }
98
+ get_hosts_fields(host_ids, fields)
127
99
  else
128
100
  fields = [
129
101
  "host",
130
102
  ] + host_ids.each_slice(SQLITE_LIMIT_COMPOUND_SELECT).flat_map { |host_ids|
131
103
  execute(q1 % host_ids.map { "?" }.join(", "), host_ids).map { |row| row.first }
132
104
  }
105
+ get_hosts_fields(host_ids, fields)
133
106
  end
134
- host_names = Hash[
135
- host_ids.each_slice(SQLITE_LIMIT_COMPOUND_SELECT).flat_map { |host_ids|
136
- execute("SELECT id, name FROM hosts WHERE id IN (%s)" % host_ids.map { "?" }.join(", "), host_ids).map { |row| row.to_a }
137
- }
138
- ]
139
- q2 = "SELECT tags.name, GROUP_CONCAT(tags.value, ',') FROM hosts_tags " \
140
- "INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
141
- "WHERE hosts_tags.host_id = ? AND tags.name IN (%s) " \
142
- "GROUP BY tags.name;"
143
- fields_without_host = fields.reject { |tag_name| tag_name == "host" }
144
- result = host_ids.map { |host_id|
145
- tag_values = Hash[
146
- fields_without_host.each_slice(SQLITE_LIMIT_COMPOUND_SELECT - 1).flat_map { |fields_without_host|
147
- execute(q2 % fields_without_host.map { "?" }.join(", "), [host_id] + fields_without_host).map { |row| row.to_a }
148
- }
149
- ]
150
- fields.map { |tag_name|
151
- if tag_name == "host"
152
- host_names.fetch(host_id, "")
153
- else
154
- tag_values.fetch(tag_name, "")
155
- end
156
- }
157
- }
158
- [result, fields]
159
107
  else
160
108
  if @options[:primary_tag]
161
- fields = [@options[:primary_tag]]
162
- q1 = "SELECT tags.value FROM hosts_tags " \
163
- "INNER JOIN hosts ON hosts_tags.host_id = hosts.id " \
164
- "INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
165
- "WHERE hosts_tags.host_id IN (%s) AND tags.name = ?;"
166
- result = host_ids.each_slice(SQLITE_LIMIT_COMPOUND_SELECT - 1).flat_map { |host_ids|
167
- execute(q1 % host_ids.map { "?" }.join(", "), host_ids + [@options[:primary_tag]]).map { |row| row.to_a }
168
- }
169
- [result, fields]
109
+ get_hosts_fields(host_ids, [@options[:primary_tag]])
170
110
  else
171
- fields = ["host"]
172
- result = host_ids.each_slice(SQLITE_LIMIT_COMPOUND_SELECT).flat_map { |host_ids|
173
- execute("SELECT name FROM hosts WHERE id IN (%s)" % host_ids.map { "?" }.join(", "), host_ids).map { |row| row.to_a }
174
- }
175
- [result, fields]
111
+ get_hosts_fields(host_ids, ["host"])
176
112
  end
177
113
  end
178
114
  end
179
115
  end
180
116
  end
181
117
 
118
+ def get_hosts_fields(host_ids, fields)
119
+ if fields.empty?
120
+ [[], fields]
121
+ else
122
+ fields_without_host = fields.reject { |tag_name| tag_name == "host" }
123
+ if fields == fields_without_host
124
+ host_names = {}
125
+ else
126
+ host_names = Hash[
127
+ host_ids.each_slice(SQLITE_LIMIT_COMPOUND_SELECT).flat_map { |host_ids|
128
+ execute("SELECT id, name FROM hosts WHERE id IN (%s)" % host_ids.map { "?" }.join(", "), host_ids).map { |row| row.to_a }
129
+ }
130
+ ]
131
+ end
132
+ q1 = "SELECT tags.name, GROUP_CONCAT(tags.value, ',') FROM hosts_tags " \
133
+ "INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
134
+ "WHERE hosts_tags.host_id = ? AND tags.name IN (%s) " \
135
+ "GROUP BY tags.name;"
136
+ result = host_ids.map { |host_id|
137
+ tag_values = Hash[
138
+ fields_without_host.each_slice(SQLITE_LIMIT_COMPOUND_SELECT - 1).flat_map { |fields_without_host|
139
+ execute(q1 % fields_without_host.map { "?" }.join(", "), [host_id] + fields_without_host).map { |row| row.to_a }
140
+ }
141
+ ]
142
+ fields.map { |tag_name|
143
+ if tag_name == "host"
144
+ host_names.fetch(host_id, "")
145
+ else
146
+ tag_values.fetch(tag_name, "")
147
+ end
148
+ }
149
+ }
150
+ [result, fields]
151
+ end
152
+ end
153
+
182
154
  def close_db(db, options={})
183
155
  @prepared_statements = @prepared_statements.reject { |k, statement|
184
156
  (db.hash & MASK_DATABASE == k & MASK_DATABASE).tap do |delete_p|
@@ -1,3 +1,3 @@
1
1
  module Hotdog
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1,122 @@
1
+ require "spec_helper"
2
+ require "hotdog/application"
3
+ require "hotdog/commands"
4
+
5
+ describe "commands" do
6
+ let(:cmd) {
7
+ Hotdog::Commands::Search.new(Hotdog::Application.new)
8
+ }
9
+
10
+ before(:each) do
11
+ ENV["DATADOG_API_KEY"] = "DATADOG_API_KEY"
12
+ ENV["DATADOG_APPLICATION_KEY"] = "DATADOG_APPLICATION_KEY"
13
+ end
14
+
15
+ it "get empty hosts" do
16
+ cmd.options[:listing] = false
17
+ cmd.options[:primary_tag] = nil
18
+ allow(cmd).to receive(:update_db)
19
+ expect(cmd.__send__(:get_hosts, [], [])).to eq([[], []])
20
+ end
21
+
22
+ it "get hosts" do
23
+ cmd.options[:listing] = false
24
+ cmd.options[:primary_tag] = nil
25
+ allow(cmd).to receive(:update_db)
26
+ allow(cmd).to receive(:get_hosts_fields).with([1, 2, 3], ["host"])
27
+ expect(cmd.__send__(:get_hosts, [1, 2, 3], []))
28
+ end
29
+
30
+ it "get hosts with primary tag" do
31
+ cmd.options[:listing] = false
32
+ cmd.options[:primary_tag] = "foo"
33
+ allow(cmd).to receive(:update_db)
34
+ allow(cmd).to receive(:get_hosts_fields).with([1, 2, 3], ["foo"])
35
+ expect(cmd.__send__(:get_hosts, [1, 2, 3], []))
36
+ end
37
+
38
+ it "get hosts with tags" do
39
+ cmd.options[:listing] = false
40
+ cmd.options[:primary_tag] = nil
41
+ allow(cmd).to receive(:update_db)
42
+ allow(cmd).to receive(:get_hosts_fields).with([1, 2, 3], ["foo", "bar", "baz"])
43
+ expect(cmd.__send__(:get_hosts, [1, 2, 3], ["foo", "bar", "baz"]))
44
+ end
45
+
46
+ it "get hosts with all tags" do
47
+ cmd.options[:listing] = true
48
+ cmd.options[:primary_tag] = nil
49
+ allow(cmd).to receive(:update_db)
50
+ q1 = [
51
+ "SELECT DISTINCT tags.name FROM hosts_tags",
52
+ "INNER JOIN tags ON hosts_tags.tag_id = tags.id",
53
+ "WHERE hosts_tags.host_id IN (?, ?, ?);",
54
+ ]
55
+ allow(cmd).to receive(:execute).with(q1.join(" "), [1, 2, 3]) {
56
+ [["foo"], ["bar"], ["baz"]]
57
+ }
58
+ allow(cmd).to receive(:get_hosts_fields).with([1, 2, 3], ["host", "foo", "bar", "baz"])
59
+ expect(cmd.__send__(:get_hosts, [1, 2, 3], []))
60
+ end
61
+
62
+ it "get hosts with all tags with primary tag" do
63
+ cmd.options[:listing] = true
64
+ cmd.options[:primary_tag] = "bar"
65
+ allow(cmd).to receive(:update_db)
66
+ q1 = [
67
+ "SELECT DISTINCT tags.name FROM hosts_tags",
68
+ "INNER JOIN tags ON hosts_tags.tag_id = tags.id",
69
+ "WHERE hosts_tags.host_id IN (?, ?, ?);",
70
+ ]
71
+ allow(cmd).to receive(:execute).with(q1.join(" "), [1, 2, 3]) {
72
+ [["foo"], ["bar"], ["baz"]]
73
+ }
74
+ allow(cmd).to receive(:get_hosts_fields).with([1, 2, 3], ["bar", "host", "foo", "baz"])
75
+ expect(cmd.__send__(:get_hosts, [1, 2, 3], []))
76
+ end
77
+
78
+ it "get empty host fields" do
79
+ expect(cmd.__send__(:get_hosts_fields, [1, 2, 3], [])).to eq([[], []])
80
+ end
81
+
82
+ it "get host fields without host" do
83
+ q1 = [
84
+ "SELECT tags.name, GROUP_CONCAT(tags.value, ',') FROM hosts_tags",
85
+ "INNER JOIN tags ON hosts_tags.tag_id = tags.id",
86
+ "WHERE hosts_tags.host_id = ? AND tags.name IN (?, ?, ?)",
87
+ "GROUP BY tags.name;",
88
+ ]
89
+ allow(cmd).to receive(:execute).with(q1.join(" "), [1, "foo", "bar", "baz"]) {
90
+ [["foo", "foo1"], ["bar", "bar1"], ["baz", "baz1"]]
91
+ }
92
+ allow(cmd).to receive(:execute).with(q1.join(" "), [2, "foo", "bar", "baz"]) {
93
+ [["foo", "foo2"], ["bar", "bar2"], ["baz", "baz2"]]
94
+ }
95
+ allow(cmd).to receive(:execute).with(q1.join(" "), [3, "foo", "bar", "baz"]) {
96
+ [["foo", "foo3"], ["bar", "bar3"], ["baz", "baz3"]]
97
+ }
98
+ expect(cmd.__send__(:get_hosts_fields, [1, 2, 3], ["foo", "bar", "baz"])).to eq([[["foo1", "bar1", "baz1"], ["foo2", "bar2", "baz2"], ["foo3", "bar3", "baz3"]], ["foo", "bar", "baz"]])
99
+ end
100
+
101
+ it "get host fields with host" do
102
+ allow(cmd).to receive(:execute).with("SELECT id, name FROM hosts WHERE id IN (?, ?, ?)", [1, 2, 3]) {
103
+ [[1, "host1"], [2, "host2"], [3, "host3"]]
104
+ }
105
+ q1 = [
106
+ "SELECT tags.name, GROUP_CONCAT(tags.value, ',') FROM hosts_tags",
107
+ "INNER JOIN tags ON hosts_tags.tag_id = tags.id",
108
+ "WHERE hosts_tags.host_id = ? AND tags.name IN (?, ?)",
109
+ "GROUP BY tags.name;",
110
+ ]
111
+ allow(cmd).to receive(:execute).with(q1.join(" "), [1, "foo", "bar"]) {
112
+ [["foo", "foo1"], ["bar", "bar1"]]
113
+ }
114
+ allow(cmd).to receive(:execute).with(q1.join(" "), [2, "foo", "bar"]) {
115
+ [["foo", "foo2"], ["bar", "bar2"]]
116
+ }
117
+ allow(cmd).to receive(:execute).with(q1.join(" "), [3, "foo", "bar"]) {
118
+ [["foo", "foo3"], ["bar", "bar3"]]
119
+ }
120
+ expect(cmd.__send__(:get_hosts_fields, [1, 2, 3], ["foo", "bar", "host"])).to eq([[["foo1", "bar1", "host1"], ["foo2", "bar2", "host2"], ["foo3", "bar3", "host3"]], ["foo", "bar", "host"]])
121
+ end
122
+ end
@@ -235,7 +235,7 @@ describe "parser" do
235
235
  end
236
236
 
237
237
  it "parses 'not foo and bar'" do
238
- expect(cmd.parse("not foo and bar")).to eq({left: {unary_op: "not", expression: {identifier: "foo"}}, binary_op: "and", right: {identifier: "bar"}})
238
+ expect(cmd.parse("not foo and bar")).to eq({unary_op: "not", expression: {left: {identifier: "foo"}, binary_op: "and", right: {identifier: "bar"}}})
239
239
  end
240
240
 
241
241
  it "parses '! foo and bar'" do
@@ -243,7 +243,7 @@ describe "parser" do
243
243
  end
244
244
 
245
245
  it "parses 'not foo && bar'" do
246
- expect(cmd.parse("not foo && bar")).to eq({left: {unary_op: "not", expression: {identifier: "foo"}}, binary_op: "&&", right: {identifier: "bar"}})
246
+ expect(cmd.parse("not foo && bar")).to eq({unary_op: "not", expression: {left: {identifier: "foo"}, binary_op: "&&", right: {identifier: "bar"}}})
247
247
  end
248
248
 
249
249
  it "parses '! foo && bar'" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hotdog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yamashita Yuu
@@ -145,6 +145,7 @@ files:
145
145
  - lib/hotdog/formatters/yaml.rb
146
146
  - lib/hotdog/version.rb
147
147
  - spec/core/application_spec.rb
148
+ - spec/core/commands_spec.rb
148
149
  - spec/formatter/csv_spec.rb
149
150
  - spec/formatter/json_spec.rb
150
151
  - spec/formatter/ltsv_spec.rb
@@ -183,6 +184,7 @@ specification_version: 4
183
184
  summary: Yet another command-line tool for Datadog
184
185
  test_files:
185
186
  - spec/core/application_spec.rb
187
+ - spec/core/commands_spec.rb
186
188
  - spec/formatter/csv_spec.rb
187
189
  - spec/formatter/json_spec.rb
188
190
  - spec/formatter/ltsv_spec.rb