wavefront-cli 2.8.0 → 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -0
- data/HISTORY.md +17 -0
- data/Rakefile +1 -1
- data/bin/wf +1 -1
- data/lib/wavefront-cli/base.rb +46 -18
- data/lib/wavefront-cli/base_write.rb +49 -33
- data/lib/wavefront-cli/commands/.rubocop.yml +7 -0
- data/lib/wavefront-cli/commands/alert.rb +0 -1
- data/lib/wavefront-cli/commands/base.rb +1 -1
- data/lib/wavefront-cli/commands/link.rb +5 -0
- data/lib/wavefront-cli/commands/window.rb +1 -0
- data/lib/wavefront-cli/commands/write.rb +1 -1
- data/lib/wavefront-cli/controller.rb +51 -21
- data/lib/wavefront-cli/derivedmetric.rb +4 -0
- data/lib/wavefront-cli/display/alert.rb +2 -0
- data/lib/wavefront-cli/display/base.rb +18 -11
- data/lib/wavefront-cli/display/metric.rb +13 -5
- data/lib/wavefront-cli/display/printer/long.rb +12 -8
- data/lib/wavefront-cli/display/printer/sparkline.rb +2 -2
- data/lib/wavefront-cli/display/printer/terse.rb +3 -1
- data/lib/wavefront-cli/display/query.rb +7 -3
- data/lib/wavefront-cli/display/write.rb +2 -0
- data/lib/wavefront-cli/event.rb +27 -16
- data/lib/wavefront-cli/exception.rb +8 -0
- data/lib/wavefront-cli/externallink.rb +30 -0
- data/lib/wavefront-cli/maintenancewindow.rb +2 -2
- data/lib/wavefront-cli/output/hcl/base.rb +21 -18
- data/lib/wavefront-cli/output/hcl/dashboard.rb +13 -38
- data/lib/wavefront-cli/output/hcl/notificant.rb +4 -2
- data/lib/wavefront-cli/output/hcl/stdlib/array.rb +22 -0
- data/lib/wavefront-cli/output/hcl/stdlib/string.rb +9 -0
- data/lib/wavefront-cli/output/wavefront/base.rb +3 -0
- data/lib/wavefront-cli/output/wavefront/query.rb +2 -2
- data/lib/wavefront-cli/query.rb +2 -0
- data/lib/wavefront-cli/report.rb +5 -2
- data/lib/wavefront-cli/{string.rb → stdlib/string.rb} +16 -12
- data/lib/wavefront-cli/version.rb +1 -1
- data/lib/wavefront-cli/write.rb +12 -4
- data/spec/spec_helper.rb +41 -24
- data/spec/wavefront-cli/commands/link_spec.rb +1 -1
- data/spec/wavefront-cli/derivedmetric_spec.rb +2 -2
- data/spec/wavefront-cli/display/base_spec.rb +2 -0
- data/spec/wavefront-cli/display/printer/long_spec.rb +1 -0
- data/spec/wavefront-cli/externallink_spec.rb +52 -4
- data/spec/wavefront-cli/output/hcl_spec.rb +3 -1
- data/spec/wavefront-cli/output/ruby_spec.rb +5 -5
- data/spec/wavefront-cli/output/wavefront/query_spec.rb +14 -9
- data/spec/wavefront-cli/output/wavefront_spec.rb +3 -1
- data/spec/wavefront-cli/query_spec.rb +2 -0
- data/spec/wavefront-cli/{string_spec.rb → stdlib/string_spec.rb} +3 -3
- data/wavefront-cli.gemspec +7 -7
- metadata +34 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ceaedf1118ace74decdfe51ad1e4c1c019e532b492c66fba499106c15f8ac021
|
4
|
+
data.tar.gz: 016ef2f6fdd75a958dc17b70375e74ac35b33acfe5d1296c75c68ce1bfbf1de9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51931ddb036a749b9989c35999be1e276b2d1832f5fe7d04a02cb897bceeed7b960e627667e79dff7bd3240c4a3b22352d23a4baba2ca4feca86659ade6b0c8b
|
7
|
+
data.tar.gz: e6f40465692ae30d9d4fdd8ef68d6d6f21443127237ba14fdc726096af0bb8a3b7f2562e2c6342bbc84f24fd5bd25ec3a272b4b012827c2e612761068cc98ead
|
data/.rubocop.yml
ADDED
data/HISTORY.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 2.9.0 (22/08/2018)
|
4
|
+
* Create external links with new `link create` sub-command.
|
5
|
+
* Fix bug which stopped you writing points without a `.wavefront`
|
6
|
+
configuration file.
|
7
|
+
* Improved error reporting.
|
8
|
+
* Bugfix on external link searching.
|
9
|
+
* Modify external link filters.
|
10
|
+
* Use 1.6.1 of [wavefront-sdk](https://github.com/snltd/wavefront-sdk).
|
11
|
+
|
12
|
+
## 2.8.0 (08/08/2018)
|
13
|
+
* Add `wavefront` format to the `query` command. This outputs the
|
14
|
+
result of a raw or timeseries query in a format which can be fed
|
15
|
+
back into Wavefront via a proxy.
|
16
|
+
* Use 1.6.0 of [wavefront-sdk](https://github.com/snltd/wavefront-sdk).
|
17
|
+
* Restructure the way different output formats are handled in a
|
18
|
+
better, more flexible way.
|
19
|
+
|
3
20
|
## 2.7.0 (04/07/2018)
|
4
21
|
|
5
22
|
* Add a `-i` option to the `report` command, to send delta metrics.
|
data/Rakefile
CHANGED
data/bin/wf
CHANGED
data/lib/wavefront-cli/base.rb
CHANGED
@@ -84,7 +84,8 @@ module WavefrontCli
|
|
84
84
|
begin
|
85
85
|
send(:wf_tag?, t)
|
86
86
|
rescue Wavefront::Exception::InvalidTag
|
87
|
-
|
87
|
+
raise(WavefrontCli::Exception::InvalidInput,
|
88
|
+
"'#{t}' is not a valid tag.")
|
88
89
|
end
|
89
90
|
end
|
90
91
|
end
|
@@ -135,9 +136,10 @@ module WavefrontCli
|
|
135
136
|
# method, and displays whatever it returns.
|
136
137
|
#
|
137
138
|
# @return [nil]
|
138
|
-
# @raise
|
139
|
-
# `do_` method.
|
139
|
+
# @raise WavefrontCli::Exception::UnhandledCommand if the
|
140
|
+
# command does not match a `do_` method.
|
140
141
|
#
|
142
|
+
# rubocop:disable Metrics/AbcSize
|
141
143
|
def dispatch
|
142
144
|
#
|
143
145
|
# Take a list of do_ methods, remove the 'do_' from their name,
|
@@ -166,6 +168,7 @@ module WavefrontCli
|
|
166
168
|
|
167
169
|
raise WavefrontCli::Exception::UnhandledCommand
|
168
170
|
end
|
171
|
+
# rubocop:enable Metrics/AbcSize
|
169
172
|
|
170
173
|
# Display a Ruby object as JSON, YAML, or human-readable. We
|
171
174
|
# provide a default method to format human-readable output, but
|
@@ -180,6 +183,7 @@ module WavefrontCli
|
|
180
183
|
# @param method [String] the name of the method which produced
|
181
184
|
# this output. Used to find a suitable humanize method.
|
182
185
|
#
|
186
|
+
# rubocop:disable Metrics/AbcSize
|
183
187
|
def display(data, method)
|
184
188
|
if no_api_response.include?(method)
|
185
189
|
return display_no_api_response(data, method)
|
@@ -198,6 +202,7 @@ module WavefrontCli
|
|
198
202
|
|
199
203
|
handle_response(data.response, format_var, method)
|
200
204
|
end
|
205
|
+
# rubocop:enable Metrics/AbcSize
|
201
206
|
|
202
207
|
# @param status [Map] status object from SDK response
|
203
208
|
# @return System exit
|
@@ -232,19 +237,20 @@ module WavefrontCli
|
|
232
237
|
end
|
233
238
|
end
|
234
239
|
|
240
|
+
# rubocop:disable Metrics/AbcSize
|
235
241
|
def parseable_output(format, resp)
|
236
242
|
options[:class] = klass_word
|
237
243
|
options[:hcl_fields] = hcl_fields
|
238
244
|
require_relative File.join('output', format.to_s)
|
239
245
|
oclass = Object.const_get(format('WavefrontOutput::%s',
|
240
|
-
|
246
|
+
format.to_s.capitalize))
|
241
247
|
oclass.new(resp, options).run
|
242
248
|
rescue LoadError
|
243
|
-
raise
|
244
|
-
|
245
|
-
|
246
|
-
)
|
249
|
+
raise(WavefrontCli::Exception::UnsupportedOutput,
|
250
|
+
format("The '%s' command does not support '%s' output.",
|
251
|
+
options[:class], format))
|
247
252
|
end
|
253
|
+
# rubocop:enable Metrics/AbcSize
|
248
254
|
|
249
255
|
def hcl_fields
|
250
256
|
[]
|
@@ -262,8 +268,14 @@ module WavefrontCli
|
|
262
268
|
# writer, for instance, uses a proxy and has no token.
|
263
269
|
#
|
264
270
|
def validate_opts
|
265
|
-
|
266
|
-
|
271
|
+
unless options[:token]
|
272
|
+
raise(WavefrontCli::Exception::CredentialError,
|
273
|
+
'Missing API token.')
|
274
|
+
end
|
275
|
+
|
276
|
+
return true if options[:endpoint]
|
277
|
+
raise(WavefrontCli::Exception::CredentialError,
|
278
|
+
'Missing API endpoint.')
|
267
279
|
end
|
268
280
|
|
269
281
|
# Give it a path to a file (as a string) and it will return the
|
@@ -273,23 +285,27 @@ module WavefrontCli
|
|
273
285
|
#
|
274
286
|
# @param path [String] the file to load
|
275
287
|
# @return [Hash] a Ruby object of the loaded file
|
276
|
-
# @raise
|
288
|
+
# @raise WavefrontCli::Exception::UnsupportedFileFormat if the
|
289
|
+
# filetype is unknown.
|
277
290
|
# @raise pass through any error loading or parsing the file
|
278
291
|
#
|
292
|
+
# rubocop:disable Metrics/AbcSize
|
279
293
|
def load_file(path)
|
280
294
|
return load_from_stdin if path == '-'
|
281
295
|
|
282
296
|
file = Pathname.new(path)
|
283
|
-
|
297
|
+
|
298
|
+
raise WavefrontCli::Exception::FileNotFound unless file.exist?
|
284
299
|
|
285
300
|
if file.extname == '.json'
|
286
301
|
JSON.parse(IO.read(file))
|
287
302
|
elsif file.extname == '.yaml' || file.extname == '.yml'
|
288
303
|
YAML.safe_load(IO.read(file))
|
289
304
|
else
|
290
|
-
raise
|
305
|
+
raise WavefrontCli::Exception::UnsupportedFileFormat
|
291
306
|
end
|
292
307
|
end
|
308
|
+
# rubocop:enable Metrics/AbcSize
|
293
309
|
|
294
310
|
# Read STDIN and return a Ruby object, assuming that STDIN is
|
295
311
|
# valid JSON or YAML. This is a dumb method, it does no
|
@@ -297,7 +313,8 @@ module WavefrontCli
|
|
297
313
|
# appears to be a valid assumption for use-cases of this CLI.
|
298
314
|
#
|
299
315
|
# @return [Object]
|
300
|
-
# @raise
|
316
|
+
# @raise Wavefront::Exception::UnparseableInput if the input
|
317
|
+
# does not parse
|
301
318
|
#
|
302
319
|
def load_from_stdin
|
303
320
|
raw = STDIN.read
|
@@ -307,8 +324,8 @@ module WavefrontCli
|
|
307
324
|
else
|
308
325
|
JSON.parse(raw)
|
309
326
|
end
|
310
|
-
rescue
|
311
|
-
raise
|
327
|
+
rescue RuntimeError
|
328
|
+
raise Wavefront::Exception::UnparseableInput
|
312
329
|
end
|
313
330
|
|
314
331
|
# Below here are common methods. Most are used by most classes,
|
@@ -331,7 +348,7 @@ module WavefrontCli
|
|
331
348
|
prepped = import_to_create(raw)
|
332
349
|
rescue StandardError => e
|
333
350
|
puts e if options[:debug]
|
334
|
-
raise
|
351
|
+
raise WavefrontCli::Exception::UnparseableInput
|
335
352
|
end
|
336
353
|
|
337
354
|
wf.create(prepped)
|
@@ -348,6 +365,10 @@ module WavefrontCli
|
|
348
365
|
def do_update
|
349
366
|
k, v = options[:'<key=value>'].split('=', 2)
|
350
367
|
wf.update(options[:'<id>'], k => v)
|
368
|
+
rescue NoMethodError
|
369
|
+
raise(WavefrontCli::Exception::UnsupportedOperation,
|
370
|
+
'Updates require two API calls. We cannot do the second ' \
|
371
|
+
'when -n is set.')
|
351
372
|
end
|
352
373
|
|
353
374
|
def do_search(cond = options[:'<condition>'])
|
@@ -356,11 +377,18 @@ module WavefrontCli
|
|
356
377
|
|
357
378
|
query = conds_to_query(cond)
|
358
379
|
|
359
|
-
wfs.search(
|
380
|
+
wfs.search(search_key, query, limit: options[:limit],
|
360
381
|
offset: options[:offset] ||
|
361
382
|
options[:cursor])
|
362
383
|
end
|
363
384
|
|
385
|
+
# The search URI pattern doesn't always match the command name,
|
386
|
+
# or class name. Override this method if this is the case.
|
387
|
+
#
|
388
|
+
def search_key
|
389
|
+
klass_word
|
390
|
+
end
|
391
|
+
|
364
392
|
# Turn a list of search conditions into an API query
|
365
393
|
#
|
366
394
|
def conds_to_query(conds)
|
@@ -9,6 +9,7 @@ module WavefrontCli
|
|
9
9
|
attr_reader :fmt
|
10
10
|
include Wavefront::Mixins
|
11
11
|
|
12
|
+
# rubocop:disable Metrics/AbcSize
|
12
13
|
def do_point
|
13
14
|
p = { path: options[:'<metric>'],
|
14
15
|
value: options[:'<value>'].delete('\\').to_f,
|
@@ -18,9 +19,10 @@ module WavefrontCli
|
|
18
19
|
p[:ts] = parse_time(options[:time]) if options[:time]
|
19
20
|
send_point(p)
|
20
21
|
end
|
22
|
+
# rubocop:enable Metrics/AbcSize
|
21
23
|
|
22
|
-
def send_point(
|
23
|
-
call_write(
|
24
|
+
def send_point(point)
|
25
|
+
call_write(point)
|
24
26
|
rescue Wavefront::Exception::InvalidEndpoint
|
25
27
|
abort "could not speak to proxy #{options[:proxy]}:#{options[:port]}."
|
26
28
|
end
|
@@ -38,14 +40,22 @@ module WavefrontCli
|
|
38
40
|
if file == '-'
|
39
41
|
read_stdin
|
40
42
|
else
|
41
|
-
data = load_data(Pathname.new(file)).split("\n")
|
42
|
-
process_line(l)
|
43
|
-
end
|
44
|
-
|
43
|
+
data = process_input_file(load_data(Pathname.new(file)).split("\n"))
|
45
44
|
call_write(data)
|
46
45
|
end
|
47
46
|
end
|
48
47
|
|
48
|
+
def process_input_file(data)
|
49
|
+
data.each_with_object([]) do |l, a|
|
50
|
+
begin
|
51
|
+
a.<< process_line(l)
|
52
|
+
rescue WavefrontCli::Exception::UnparseableInput => e
|
53
|
+
puts "Bad input. #{e.message}."
|
54
|
+
next
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
49
59
|
# A wrapper which lets us send normal points or deltas
|
50
60
|
#
|
51
61
|
def call_write(data)
|
@@ -80,10 +90,9 @@ module WavefrontCli
|
|
80
90
|
|
81
91
|
# Find and return the source in a chunked line of input.
|
82
92
|
#
|
83
|
-
# param chunks [Array] a chunked line of input from #process_line
|
84
|
-
# return [Float] the timestamp, if it is there, or the current
|
93
|
+
# @param chunks [Array] a chunked line of input from #process_line
|
94
|
+
# @return [Float] the timestamp, if it is there, or the current
|
85
95
|
# UTC time if it is not.
|
86
|
-
# raise TypeError if field does not exist
|
87
96
|
#
|
88
97
|
def extract_ts(chunks)
|
89
98
|
ts = chunks[fmt.index('t')]
|
@@ -107,7 +116,7 @@ module WavefrontCli
|
|
107
116
|
#
|
108
117
|
def extract_path(chunks)
|
109
118
|
m = chunks[fmt.index('m')]
|
110
|
-
|
119
|
+
options[:metric] ? [options[:metric], m].join('.') : m
|
111
120
|
rescue TypeError
|
112
121
|
return options[:metric] if options[:metric]
|
113
122
|
raise
|
@@ -120,7 +129,7 @@ module WavefrontCli
|
|
120
129
|
# value passed through by -H, or the local hostname.
|
121
130
|
#
|
122
131
|
def extract_source(chunks)
|
123
|
-
|
132
|
+
chunks[fmt.index('s')]
|
124
133
|
rescue TypeError
|
125
134
|
options[:source] || Socket.gethostname
|
126
135
|
end
|
@@ -132,23 +141,32 @@ module WavefrontCli
|
|
132
141
|
# what they define is always assumed to be point tags. This is
|
133
142
|
# because you can have arbitrarily many of those for each point.
|
134
143
|
#
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
144
|
+
# @raise WavefrontCli::Exception::UnparseableInput if the line
|
145
|
+
# doesn't look right
|
146
|
+
#
|
147
|
+
# rubocop:disable Metrics/AbcSize
|
148
|
+
# rubocop:disable Metrics/MethodLength
|
149
|
+
def process_line(line)
|
150
|
+
return true if line.empty?
|
151
|
+
chunks = line.split(/\s+/, fmt.length)
|
152
|
+
enough_fields?(line) # can raise exception
|
139
153
|
|
140
154
|
begin
|
141
155
|
point = { path: extract_path(chunks),
|
156
|
+
tags: line_tags(chunks),
|
142
157
|
value: extract_value(chunks) }
|
143
|
-
|
158
|
+
|
159
|
+
point[:ts] = extract_ts(chunks) if fmt.include?('t')
|
144
160
|
point[:source] = extract_source(chunks) if fmt.include?('s')
|
145
|
-
point[:tags] = line_tags(chunks)
|
146
161
|
rescue TypeError
|
147
|
-
raise
|
162
|
+
raise(WavefrontCli::Exception::UnparseableInput,
|
163
|
+
"could not process #{line}")
|
148
164
|
end
|
149
165
|
|
150
166
|
point
|
151
167
|
end
|
168
|
+
# rubocop:enable Metrics/MethodLength
|
169
|
+
# rubocop:enable Metrics/AbcSize
|
152
170
|
|
153
171
|
# We can get tags from the file, from the -T option, or both.
|
154
172
|
# Merge them, making the -T win if there is a collision.
|
@@ -190,7 +208,8 @@ module WavefrontCli
|
|
190
208
|
return true
|
191
209
|
end
|
192
210
|
|
193
|
-
raise
|
211
|
+
raise(WavefrontCli::Exception::UnparseableInput,
|
212
|
+
'Invalid format string.')
|
194
213
|
end
|
195
214
|
|
196
215
|
# Make sure we have the right number of columns, according to
|
@@ -201,16 +220,11 @@ module WavefrontCli
|
|
201
220
|
# If the format string says we are expecting point tags, we
|
202
221
|
# may have more columns than the length of the format string.
|
203
222
|
#
|
204
|
-
def enough_fields?(
|
205
|
-
ncols =
|
206
|
-
|
207
|
-
if fmt.
|
208
|
-
|
209
|
-
else
|
210
|
-
return false unless ncols == fmt.length
|
211
|
-
end
|
212
|
-
|
213
|
-
true
|
223
|
+
def enough_fields?(line)
|
224
|
+
ncols = line.split.length
|
225
|
+
return true if fmt.include?('T') && ncols >= fmt.length
|
226
|
+
return true if ncols == fmt.length
|
227
|
+
raise WavefrontCli::Exception::UnparseableInput, 'wrong number of fields'
|
214
228
|
end
|
215
229
|
|
216
230
|
# Although the SDK does value checking, we'll add another layer
|
@@ -218,9 +232,10 @@ module WavefrontCli
|
|
218
232
|
# assume anything before 2000/01/01 or after a year from now is
|
219
233
|
# wrong. Arbitrary, but there has to be a cut-off somewhere.
|
220
234
|
#
|
221
|
-
def valid_timestamp?(
|
222
|
-
(
|
223
|
-
|
235
|
+
def valid_timestamp?(timestamp)
|
236
|
+
(timestamp.is_a?(Integer) || timestamp.match(/^\d+$/)) &&
|
237
|
+
timestamp.to_i > 946_684_800 &&
|
238
|
+
timestamp.to_i < (Time.now.to_i + 31_557_600)
|
224
239
|
end
|
225
240
|
|
226
241
|
private
|
@@ -230,8 +245,9 @@ module WavefrontCli
|
|
230
245
|
end
|
231
246
|
|
232
247
|
def load_data(file)
|
233
|
-
raise "Cannot open file '#{file}'." unless file.exist?
|
234
248
|
IO.read(file)
|
249
|
+
rescue StandardError
|
250
|
+
raise WavefrontCli::Exception::FileNotFound
|
235
251
|
end
|
236
252
|
end
|
237
253
|
end
|
@@ -18,6 +18,8 @@ class WavefrontCommandLink < WavefrontCommandBase
|
|
18
18
|
def _commands
|
19
19
|
["list #{CMN} [-l] [-f format] [-o offset] [-L limit]",
|
20
20
|
"describe #{CMN} [-f format] <id>",
|
21
|
+
"create #{CMN} [-m regex] [-s regex] [-p str=regex...] <name> " \
|
22
|
+
'<description> <template>',
|
21
23
|
"delete #{CMN} <id>",
|
22
24
|
"import #{CMN} <file>",
|
23
25
|
"update #{CMN} <key=value> <id>",
|
@@ -29,6 +31,9 @@ class WavefrontCommandLink < WavefrontCommandBase
|
|
29
31
|
'-l, --long list external links in detail',
|
30
32
|
'-o, --offset=n start from nth external link',
|
31
33
|
'-L, --limit=COUNT number of external link to list',
|
34
|
+
'-m, --metric-regex=REGEX metric filter regular expression',
|
35
|
+
'-s, --source-regex=REGEX source filter regular expression',
|
36
|
+
'-p, --point-regex=REGEX point filter regular expression',
|
32
37
|
'-f, --format=STRING output format']
|
33
38
|
end
|
34
39
|
end
|