wavefront-cli 2.8.0 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.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
|