wavefront-cli 2.3.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -0
- data/lib/wavefront-cli/base.rb +26 -10
- data/lib/wavefront-cli/base_write.rb +231 -0
- data/lib/wavefront-cli/commands/report.rb +35 -0
- data/lib/wavefront-cli/commands/write.rb +5 -2
- data/lib/wavefront-cli/controller.rb +8 -4
- data/lib/wavefront-cli/display/report.rb +17 -0
- data/lib/wavefront-cli/display/write.rb +7 -4
- data/lib/wavefront-cli/output/base.rb +10 -0
- data/lib/wavefront-cli/output/hcl.rb +20 -0
- data/lib/wavefront-cli/output/hcl/alert.rb +14 -0
- data/lib/wavefront-cli/output/hcl/base.rb +92 -0
- data/lib/wavefront-cli/output/hcl/dashboard.rb +114 -0
- data/lib/wavefront-cli/output/hcl/notificant.rb +30 -0
- data/lib/wavefront-cli/output/json.rb +12 -0
- data/lib/wavefront-cli/output/ruby.rb +12 -0
- data/lib/wavefront-cli/output/wavefront.tf +257 -0
- data/lib/wavefront-cli/output/yaml.rb +15 -0
- data/lib/wavefront-cli/report.rb +15 -0
- data/lib/wavefront-cli/string.rb +12 -1
- data/lib/wavefront-cli/version.rb +1 -1
- data/lib/wavefront-cli/write.rb +9 -221
- data/spec/wavefront-cli/string_spec.rb +24 -0
- data/wavefront-cli.gemspec +1 -1
- metadata +20 -6
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
module WavefrontOutput
|
4
|
+
#
|
5
|
+
# Display as YAML
|
6
|
+
#
|
7
|
+
class Yaml < Base
|
8
|
+
# We don't want the YAML keys to be symbols, so we load it as
|
9
|
+
# JSON and turn *that* into YAML.
|
10
|
+
#
|
11
|
+
def run
|
12
|
+
puts JSON.parse(resp.to_json).to_yaml
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'base_write'
|
2
|
+
|
3
|
+
module WavefrontCli
|
4
|
+
class Report < BaseWrite
|
5
|
+
def send_point(p)
|
6
|
+
wf.write(p)
|
7
|
+
rescue Wavefront::Exception::InvalidEndpoint
|
8
|
+
abort 'could not speak to API'
|
9
|
+
end
|
10
|
+
|
11
|
+
def open_connection; end
|
12
|
+
|
13
|
+
def close_connection; end
|
14
|
+
end
|
15
|
+
end
|
data/lib/wavefront-cli/string.rb
CHANGED
@@ -34,7 +34,6 @@ class String
|
|
34
34
|
#
|
35
35
|
def fold(tw = TW, indent = 10, prefix = '')
|
36
36
|
chunks = scan_line(tw - 8)
|
37
|
-
|
38
37
|
first_line = format("%s%s\n", prefix, chunks.shift)
|
39
38
|
|
40
39
|
return first_line if chunks.empty?
|
@@ -65,8 +64,20 @@ class String
|
|
65
64
|
number.to_i * unit_factor(unit.to_sym)
|
66
65
|
end
|
67
66
|
|
67
|
+
# How many seconds in the given unit
|
68
|
+
# @param unit [Symbol]
|
69
|
+
# @return [Integer]
|
70
|
+
#
|
68
71
|
def unit_factor(unit)
|
69
72
|
factors = { s: 1, m: 60, h: 3600, d: 86_400, w: 604_800 }
|
70
73
|
factors[unit] || 1
|
71
74
|
end
|
75
|
+
|
76
|
+
# Make a camelCase string be snake_case
|
77
|
+
# @return [String]
|
78
|
+
#
|
79
|
+
def to_snake
|
80
|
+
self.gsub(/(.)([A-Z])/) { Regexp.last_match[1] + '_' +
|
81
|
+
Regexp.last_match[2].downcase }
|
82
|
+
end
|
72
83
|
end
|
@@ -1 +1 @@
|
|
1
|
-
WF_CLI_VERSION = '2.
|
1
|
+
WF_CLI_VERSION = '2.4.0'.freeze
|
data/lib/wavefront-cli/write.rb
CHANGED
@@ -1,224 +1,15 @@
|
|
1
|
-
|
2
|
-
require_relative './base'
|
1
|
+
require_relative 'base_write'
|
3
2
|
|
4
3
|
module WavefrontCli
|
5
4
|
#
|
6
|
-
# Send points
|
5
|
+
# Send points via a proxy. This inherits from the same base class
|
6
|
+
# as Report, but has to do a couple of things differently, as it
|
7
|
+
# speaks to a proxy rather than to the API.
|
7
8
|
#
|
8
|
-
class Write <
|
9
|
-
attr_reader :fmt
|
10
|
-
include Wavefront::Mixins
|
11
|
-
|
9
|
+
class Write < BaseWrite
|
12
10
|
def mk_creds
|
13
11
|
{ proxy: options[:proxy], port: options[:port] || 2878 }
|
14
12
|
end
|
15
|
-
def do_point
|
16
|
-
p = { path: options[:'<metric>'],
|
17
|
-
value: options[:'<value>'].delete('\\').to_f,
|
18
|
-
tags: tags_to_hash(options[:tag]) }
|
19
|
-
|
20
|
-
p[:source] = options[:host] if options[:host]
|
21
|
-
p[:ts] = parse_time(options[:time]) if options[:time]
|
22
|
-
|
23
|
-
begin
|
24
|
-
wf.write(p)
|
25
|
-
rescue Wavefront::Exception::InvalidEndpoint
|
26
|
-
abort 'could not speak to proxy ' \
|
27
|
-
"'#{options[:proxy]}:#{options[:port]}'."
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def do_file
|
32
|
-
valid_format?(options[:infileformat])
|
33
|
-
setup_fmt(options[:infileformat] || 'tmv')
|
34
|
-
process_input(options[:'<file>'])
|
35
|
-
end
|
36
|
-
|
37
|
-
# Read the input, from a file or from STDIN, and turn each line
|
38
|
-
# into Wavefront points.
|
39
|
-
#
|
40
|
-
def process_input(file)
|
41
|
-
if file == '-'
|
42
|
-
read_stdin
|
43
|
-
else
|
44
|
-
data = load_data(Pathname.new(file)).split("\n").map do |l|
|
45
|
-
process_line(l)
|
46
|
-
end
|
47
|
-
|
48
|
-
wf.write(data)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
# Read from standard in and stream points through an open
|
53
|
-
# socket. If the user hits ctrl-c, close the socket and exit
|
54
|
-
# politely.
|
55
|
-
#
|
56
|
-
def read_stdin
|
57
|
-
wf.open
|
58
|
-
STDIN.each_line { |l| wf.write(process_line(l.strip), false) }
|
59
|
-
wf.close
|
60
|
-
rescue SystemExit, Interrupt
|
61
|
-
puts 'ctrl-c. Exiting.'
|
62
|
-
wf.close
|
63
|
-
exit 0
|
64
|
-
end
|
65
|
-
|
66
|
-
# Find and return the value in a chunked line of input
|
67
|
-
#
|
68
|
-
# param chunks [Array] a chunked line of input from #process_line
|
69
|
-
# return [Float] the value
|
70
|
-
# raise TypeError if field does not exist
|
71
|
-
# raise Wavefront::Exception::InvalidValue if it's not a value
|
72
|
-
#
|
73
|
-
def extract_value(chunks)
|
74
|
-
v = chunks[fmt.index('v')]
|
75
|
-
v.to_f
|
76
|
-
end
|
77
|
-
|
78
|
-
# Find and return the source in a chunked line of input.
|
79
|
-
#
|
80
|
-
# param chunks [Array] a chunked line of input from #process_line
|
81
|
-
# return [Float] the timestamp, if it is there, or the current
|
82
|
-
# UTC time if it is not.
|
83
|
-
# raise TypeError if field does not exist
|
84
|
-
#
|
85
|
-
def extract_ts(chunks)
|
86
|
-
ts = chunks[fmt.index('t')]
|
87
|
-
return parse_time(ts) if valid_timestamp?(ts)
|
88
|
-
rescue TypeError
|
89
|
-
Time.now.utc.to_i
|
90
|
-
end
|
91
|
-
|
92
|
-
def extract_tags(chunks)
|
93
|
-
tags_to_hash(chunks.last.split(/\s(?=(?:[^"]|"[^"]*")*$)/))
|
94
|
-
end
|
95
|
-
|
96
|
-
# Find and return the metric path in a chunked line of input.
|
97
|
-
# The path can be in the data, or passed as an option, or both.
|
98
|
-
# If the latter, then we assume the option is a prefix, and
|
99
|
-
# concatenate the value in the data.
|
100
|
-
#
|
101
|
-
# param chunks [Array] a chunked line of input from #process_line
|
102
|
-
# return [String] the metric path
|
103
|
-
# raise TypeError if field does not exist
|
104
|
-
#
|
105
|
-
def extract_path(chunks)
|
106
|
-
m = chunks[fmt.index('m')]
|
107
|
-
return options[:metric] ? [options[:metric], m].join('.') : m
|
108
|
-
rescue TypeError
|
109
|
-
return options[:metric] if options[:metric]
|
110
|
-
raise
|
111
|
-
end
|
112
|
-
|
113
|
-
# Find and return the source in a chunked line of input.
|
114
|
-
#
|
115
|
-
# param chunks [Array] a chunked line of input from #process_line
|
116
|
-
# return [String] the source, if it is there, or if not, the
|
117
|
-
# value passed through by -H, or the local hostname.
|
118
|
-
#
|
119
|
-
def extract_source(chunks)
|
120
|
-
return chunks[fmt.index('s')]
|
121
|
-
rescue TypeError
|
122
|
-
options[:source] || Socket.gethostname
|
123
|
-
end
|
124
|
-
|
125
|
-
# Process a line of input, as described by the format string
|
126
|
-
# held in @fmt. Produces a hash suitable for the SDK to send on.
|
127
|
-
#
|
128
|
-
# We let the user define most of the fields, but anything beyond
|
129
|
-
# what they define is always assumed to be point tags. This is
|
130
|
-
# because you can have arbitrarily many of those for each point.
|
131
|
-
#
|
132
|
-
def process_line(l)
|
133
|
-
return true if l.empty?
|
134
|
-
chunks = l.split(/\s+/, fmt.length)
|
135
|
-
raise 'wrong number of fields' unless enough_fields?(l)
|
136
|
-
|
137
|
-
begin
|
138
|
-
point = { path: extract_path(chunks),
|
139
|
-
value: extract_value(chunks) }
|
140
|
-
point[:ts] = extract_ts(chunks) if fmt.include?('t')
|
141
|
-
point[:source] = extract_source(chunks) if fmt.include?('s')
|
142
|
-
point[:tags] = line_tags(chunks)
|
143
|
-
rescue TypeError
|
144
|
-
raise "could not process #{l}"
|
145
|
-
end
|
146
|
-
|
147
|
-
point
|
148
|
-
end
|
149
|
-
|
150
|
-
# We can get tags from the file, from the -T option, or both.
|
151
|
-
# Merge them, making the -T win if there is a collision.
|
152
|
-
#
|
153
|
-
def line_tags(chunks)
|
154
|
-
file_tags = fmt.last == 'T' ? extract_tags(chunks) : {}
|
155
|
-
opt_tags = tags_to_hash(options[:tag])
|
156
|
-
file_tags.merge(opt_tags)
|
157
|
-
end
|
158
|
-
|
159
|
-
# Takes an array of key=value tags (as produced by docopt) and
|
160
|
-
# turns it into a hash of key: value tags. Anything not of the
|
161
|
-
# form key=val is dropped. If key or value are quoted, we
|
162
|
-
# remove the quotes.
|
163
|
-
#
|
164
|
-
# @param tags [Array]
|
165
|
-
# return Hash
|
166
|
-
#
|
167
|
-
def tags_to_hash(tags)
|
168
|
-
return nil unless tags
|
169
|
-
|
170
|
-
[tags].flatten.each_with_object({}) do |t, ret|
|
171
|
-
k, v = t.split('=', 2)
|
172
|
-
k.gsub!(/^["']|["']$/, '')
|
173
|
-
ret[k] = v.to_s.gsub(/^["']|["']$/, '') if v
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
# The format string must contain a 'v'. It must not contain
|
178
|
-
# anything other than 'm', 't', 'T', 's', or 'v', and the 'T',
|
179
|
-
# if there, must be at the end. No letter must appear more than
|
180
|
-
# once.
|
181
|
-
#
|
182
|
-
# @param fmt [String] format of input file
|
183
|
-
#
|
184
|
-
def valid_format?(fmt)
|
185
|
-
if fmt.include?('v') && fmt.match(/^[mstv]+T?$/) &&
|
186
|
-
fmt == fmt.split('').uniq.join
|
187
|
-
return true
|
188
|
-
end
|
189
|
-
|
190
|
-
raise 'Invalid format string.'
|
191
|
-
end
|
192
|
-
|
193
|
-
# Make sure we have the right number of columns, according to
|
194
|
-
# the format string. We want to take every precaution we can to
|
195
|
-
# stop users accidentally polluting their metric namespace with
|
196
|
-
# junk.
|
197
|
-
#
|
198
|
-
# If the format string says we are expecting point tags, we
|
199
|
-
# may have more columns than the length of the format string.
|
200
|
-
#
|
201
|
-
def enough_fields?(l)
|
202
|
-
ncols = l.split.length
|
203
|
-
|
204
|
-
if fmt.include?('T')
|
205
|
-
return false unless ncols >= fmt.length
|
206
|
-
else
|
207
|
-
return false unless ncols == fmt.length
|
208
|
-
end
|
209
|
-
|
210
|
-
true
|
211
|
-
end
|
212
|
-
|
213
|
-
# Although the SDK does value checking, we'll add another layer
|
214
|
-
# of input checing here. See if the time looks valid. We'll
|
215
|
-
# assume anything before 2000/01/01 or after a year from now is
|
216
|
-
# wrong. Arbitrary, but there has to be a cut-off somewhere.
|
217
|
-
#
|
218
|
-
def valid_timestamp?(ts)
|
219
|
-
(ts.is_a?(Integer) || ts.match(/^\d+$/)) &&
|
220
|
-
ts.to_i > 946_684_800 && ts.to_i < (Time.now.to_i + 31_557_600)
|
221
|
-
end
|
222
13
|
|
223
14
|
def validate_opts
|
224
15
|
unless options[:metric] || options[:format].include?('m')
|
@@ -228,15 +19,12 @@ module WavefrontCli
|
|
228
19
|
raise 'Please supply a proxy address.' unless options[:proxy]
|
229
20
|
end
|
230
21
|
|
231
|
-
|
232
|
-
|
233
|
-
def setup_fmt(fmt)
|
234
|
-
@fmt = fmt.split('')
|
22
|
+
def open_connection
|
23
|
+
wf.open
|
235
24
|
end
|
236
25
|
|
237
|
-
def
|
238
|
-
|
239
|
-
IO.read(file)
|
26
|
+
def close_connection
|
27
|
+
wf.close
|
240
28
|
end
|
241
29
|
end
|
242
30
|
end
|
@@ -20,6 +20,17 @@ class StringTest < MiniTest::Test
|
|
20
20
|
'alpha] [-b beta] [-c gamma] <id>')
|
21
21
|
end
|
22
22
|
|
23
|
+
def test_opt_fold
|
24
|
+
assert_equal('short string'.opt_fold, " short string\n")
|
25
|
+
|
26
|
+
str = '-o, --option PARAMETER a rather pointless option with a ' \
|
27
|
+
'needlessly wordy description string'
|
28
|
+
pad = "\n" + ' ' * 12
|
29
|
+
assert_equal(" -o, --option PARAMETER a#{pad}rather pointless" \
|
30
|
+
"#{pad}option with a#{pad}needlessly wordy#{pad}" \
|
31
|
+
"description#{pad}string\n",str.opt_fold(30, 10))
|
32
|
+
end
|
33
|
+
|
23
34
|
def test_fold_options
|
24
35
|
str = '-l, --longoption a long option with a quite long ' \
|
25
36
|
'description which needs folding'
|
@@ -42,4 +53,17 @@ class StringTest < MiniTest::Test
|
|
42
53
|
assert_raises(ArgumentError) { 'm'.to_seconds }
|
43
54
|
assert_raises(ArgumentError) { '3m5s'.to_seconds }
|
44
55
|
end
|
56
|
+
|
57
|
+
def test_unit_factor
|
58
|
+
assert_equal(60, '1'.unit_factor(:m))
|
59
|
+
assert_equal(1, '1'.unit_factor('m'))
|
60
|
+
assert_equal(1, '1'.unit_factor(:t))
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_to_snake
|
64
|
+
assert_equal('snake_case', 'snakeCase'.to_snake)
|
65
|
+
assert_equal('lots_and_lots_of_words', 'lotsAndLotsOfWords'.to_snake)
|
66
|
+
assert_equal('unchanged', 'unchanged'.to_snake)
|
67
|
+
assert_equal('Unchanged', 'Unchanged'.to_snake)
|
68
|
+
end
|
45
69
|
end
|
data/wavefront-cli.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |gem|
|
|
23
23
|
gem.require_paths = %w(lib)
|
24
24
|
|
25
25
|
gem.add_dependency 'docopt', '~> 0.6.0'
|
26
|
-
gem.add_runtime_dependency 'wavefront-sdk', '~> 1.
|
26
|
+
gem.add_runtime_dependency 'wavefront-sdk', '~> 1.4', '>= 1.4.0'
|
27
27
|
|
28
28
|
gem.add_development_dependency 'bundler', '~> 1.3'
|
29
29
|
gem.add_development_dependency 'rake', '~> 12.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wavefront-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Fisher
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: docopt
|
@@ -30,20 +30,20 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
33
|
+
version: '1.4'
|
34
34
|
- - ">="
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version: 1.
|
36
|
+
version: 1.4.0
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
41
|
- - "~>"
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: '1.
|
43
|
+
version: '1.4'
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: 1.
|
46
|
+
version: 1.4.0
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: bundler
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -166,6 +166,7 @@ files:
|
|
166
166
|
- bin/wf
|
167
167
|
- lib/wavefront-cli/alert.rb
|
168
168
|
- lib/wavefront-cli/base.rb
|
169
|
+
- lib/wavefront-cli/base_write.rb
|
169
170
|
- lib/wavefront-cli/cloudintegration.rb
|
170
171
|
- lib/wavefront-cli/commands/alert.rb
|
171
172
|
- lib/wavefront-cli/commands/base.rb
|
@@ -179,6 +180,7 @@ files:
|
|
179
180
|
- lib/wavefront-cli/commands/notificant.rb
|
180
181
|
- lib/wavefront-cli/commands/proxy.rb
|
181
182
|
- lib/wavefront-cli/commands/query.rb
|
183
|
+
- lib/wavefront-cli/commands/report.rb
|
182
184
|
- lib/wavefront-cli/commands/savedsearch.rb
|
183
185
|
- lib/wavefront-cli/commands/source.rb
|
184
186
|
- lib/wavefront-cli/commands/user.rb
|
@@ -204,6 +206,7 @@ files:
|
|
204
206
|
- lib/wavefront-cli/display/printer/terse.rb
|
205
207
|
- lib/wavefront-cli/display/proxy.rb
|
206
208
|
- lib/wavefront-cli/display/query.rb
|
209
|
+
- lib/wavefront-cli/display/report.rb
|
207
210
|
- lib/wavefront-cli/display/savedsearch.rb
|
208
211
|
- lib/wavefront-cli/display/source.rb
|
209
212
|
- lib/wavefront-cli/display/user.rb
|
@@ -218,8 +221,19 @@ files:
|
|
218
221
|
- lib/wavefront-cli/metric.rb
|
219
222
|
- lib/wavefront-cli/notificant.rb
|
220
223
|
- lib/wavefront-cli/opt_handler.rb
|
224
|
+
- lib/wavefront-cli/output/base.rb
|
225
|
+
- lib/wavefront-cli/output/hcl.rb
|
226
|
+
- lib/wavefront-cli/output/hcl/alert.rb
|
227
|
+
- lib/wavefront-cli/output/hcl/base.rb
|
228
|
+
- lib/wavefront-cli/output/hcl/dashboard.rb
|
229
|
+
- lib/wavefront-cli/output/hcl/notificant.rb
|
230
|
+
- lib/wavefront-cli/output/json.rb
|
231
|
+
- lib/wavefront-cli/output/ruby.rb
|
232
|
+
- lib/wavefront-cli/output/wavefront.tf
|
233
|
+
- lib/wavefront-cli/output/yaml.rb
|
221
234
|
- lib/wavefront-cli/proxy.rb
|
222
235
|
- lib/wavefront-cli/query.rb
|
236
|
+
- lib/wavefront-cli/report.rb
|
223
237
|
- lib/wavefront-cli/savedsearch.rb
|
224
238
|
- lib/wavefront-cli/source.rb
|
225
239
|
- lib/wavefront-cli/string.rb
|