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.
@@ -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
@@ -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.3.1'.freeze
1
+ WF_CLI_VERSION = '2.4.0'.freeze
@@ -1,224 +1,15 @@
1
- require 'wavefront-sdk/mixins'
2
- require_relative './base'
1
+ require_relative 'base_write'
3
2
 
4
3
  module WavefrontCli
5
4
  #
6
- # Send points to a proxy.
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 < Base
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
- private
232
-
233
- def setup_fmt(fmt)
234
- @fmt = fmt.split('')
22
+ def open_connection
23
+ wf.open
235
24
  end
236
25
 
237
- def load_data(file)
238
- raise "Cannot open file '#{file}'." unless file.exist?
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
@@ -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.2', '>= 1.2.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.3.1
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-03-24 00:00:00.000000000 Z
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.2'
33
+ version: '1.4'
34
34
  - - ">="
35
35
  - !ruby/object:Gem::Version
36
- version: 1.2.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.2'
43
+ version: '1.4'
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 1.2.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