wavefront-cli 2.3.1 → 2.4.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/.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
|