wavefront-client 3.2.0 → 3.3.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.
data/lib/wavefront/cli.rb CHANGED
@@ -25,29 +25,35 @@ module Wavefront
25
25
  @options = options
26
26
  @arguments = arguments
27
27
 
28
- if @options[:help]
29
- puts @options
28
+ if options.include?(:help) && options[:help]
29
+ puts options
30
30
  exit 0
31
31
  end
32
32
  end
33
33
 
34
34
  def load_profile
35
+ #
36
+ # Load in configuration options from the (optionally) given
37
+ # section of an ini-style configuration file. If the file's
38
+ # not there, we don't consider that an error: it's not
39
+ # entirely reasonable to demand the user has one. Options
40
+ # passed on the command-line trump the same values in the
41
+ # file.
42
+ #
43
+ return unless options[:config].is_a?(String)
35
44
  cf = Pathname.new(options[:config])
36
- pf = options[:profile]
45
+ return unless cf.exist?
37
46
 
38
- if cf.exist?
39
- raw = IniFile.load(cf)
40
- profile = raw[pf]
47
+ pf = options[:profile] || 'default'
48
+ raw = IniFile.load(cf)
49
+ profile = raw[pf]
41
50
 
42
- unless profile.empty?
43
- puts "using #{pf} profile from #{cf}" if options[:debug]
44
- return profile.inject({}){|x, (k, v)| x[k.to_sym] = v; x }
45
- end
51
+ puts "using #{pf} profile from #{cf}" if options[:debug]
46
52
 
47
- else
48
- puts "no config file at '#{cf}': using options" if options[:debug]
53
+ profile.each_with_object({}) do |(k, v), memo|
54
+ k = k.to_sym
55
+ memo[k] = (options.include?(k) && options[k]) ? options[k] : v
49
56
  end
50
57
  end
51
-
52
58
  end
53
59
  end
@@ -16,6 +16,6 @@ See the License for the specific language governing permissions and
16
16
 
17
17
  module Wavefront
18
18
  class Client
19
- VERSION = "3.2.0"
19
+ VERSION = "3.3.0"
20
20
  end
21
21
  end
@@ -30,5 +30,8 @@ module Wavefront
30
30
  GRANULARITIES = %w( s m h d )
31
31
  EVENT_STATE_DIR = Pathname.new('/var/tmp/wavefront/events')
32
32
  EVENT_LEVELS = %w(info smoke warn severe)
33
+ DEFAULT_PROXY = 'wavefront'
34
+ DEFAULT_PROXY_PORT = 2878
35
+ DEFAULT_INFILE_FORMAT = 'tmv'
33
36
  end
34
37
  end
@@ -24,50 +24,71 @@ module Wavefront
24
24
  attr_reader :headers
25
25
 
26
26
  def initialize(token)
27
- @headers = {
28
- 'X-AUTH-TOKEN': token,
29
- }
27
+ @headers = { :'X-AUTH-TOKEN' => token }
30
28
  end
31
29
 
32
30
  def create(payload = {}, options = {})
31
+ make_call(create_uri(options), create_qs(payload))
32
+ end
33
+
34
+ def close(payload = {}, options = {})
35
+ make_call(close_uri(options), hash_to_qs(payload))
36
+ end
37
+
38
+ def create_uri(options = {})
39
+ #
40
+ # Build the URI we use to send a 'create' request.
41
+ #
33
42
  options[:host] ||= DEFAULT_HOST
34
43
  options[:path] ||= DEFAULT_PATH
35
44
 
36
- uri = URI::HTTPS.build(
45
+ URI::HTTPS.build(
37
46
  host: options[:host],
38
47
  path: options[:path],
39
48
  )
49
+ end
40
50
 
51
+ def create_qs(payload = {})
52
+ #
41
53
  # It seems that posting the hash means the 'host' data is
42
54
  # lost. Making a query string works though, so let's do that.
43
55
  #
44
- hosts = payload[:h]
56
+ if payload[:h].is_a?(Array)
57
+ hosts = payload[:h]
58
+ elsif payload[:h].is_a?(String)
59
+ hosts = [payload[:h]]
60
+ else
61
+ hosts = []
62
+ end
63
+
45
64
  payload.delete(:h)
46
- query = mk_qs(payload)
65
+ query = hash_to_qs(payload)
47
66
  hosts.each { |host| query.<< "&h=#{host}" }
48
- RestClient.post(uri.to_s, query, headers)
67
+ query
49
68
  end
50
69
 
51
- def close(payload = {}, options = {})
70
+ def close_uri(options = {})
71
+ #
72
+ # Build the URI we use to send a 'close' request
73
+ #
52
74
  options[:host] ||= DEFAULT_HOST
53
75
  options[:path] ||= DEFAULT_PATH
54
76
 
55
- # This request seems to need the data as a query string. I was
56
- # getting a 500 when I posted a hash. A map will do the
57
- # needful.
58
-
59
- uri = URI::HTTPS.build(
77
+ URI::HTTPS.build(
60
78
  host: options[:host],
61
79
  path: options[:path] + 'close',
62
-
63
80
  )
64
-
65
- RestClient.post(uri.to_s, mk_qs(payload), headers)
66
81
  end
67
82
 
68
- private
83
+ def make_call(uri, query)
84
+ RestClient.post(uri.to_s, query, headers)
85
+ end
69
86
 
70
- def mk_qs(payload)
87
+ def hash_to_qs(payload)
88
+ #
89
+ # Make a properly escaped query string out of a key: value
90
+ # hash.
91
+ #
71
92
  URI.escape(payload.map { |k, v| [k, v].join('=') }.join('&'))
72
93
  end
73
94
 
@@ -1,4 +1,4 @@
1
- =begin
1
+ =begin
2
2
  Copyright 2015 Wavefront Inc.
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
@@ -24,5 +24,12 @@ module Wavefront
24
24
  class EmptyMetricName < ::Exception; end
25
25
  class NotImplemented < ::Exception; end
26
26
  class InvalidPrefixLength < ::Exception; end
27
+ class InvalidMetricName < ::Exception; end
28
+ class InvalidMetricValue < ::Exception; end
29
+ class InvalidTimestamp < ::Exception; end
30
+ class InvalidTag < ::Exception; end
31
+ class InvalidHostname < ::Exception; end
32
+ class InvalidEndpoint < ::Exception; end
33
+ class InvalidSource < ::Exception; end
27
34
  end
28
35
  end
@@ -27,9 +27,15 @@ module Wavefront
27
27
  end
28
28
 
29
29
  def parse_time(t)
30
- return Time.at(t.to_i) if t.match(/^\d+$/)
30
+ #
31
+ # Return a time as an integer, however it might come in.
32
+ #
31
33
  begin
32
- return DateTime.parse("#{t} #{Time.now.getlocal.zone}").to_time.utc
34
+ return t if t.is_a?(Integer)
35
+ return t.to_i if t.is_a?(Time)
36
+ return t.to_i if t.is_a?(String) && t.match(/^\d+$/)
37
+ return DateTime.parse("#{t} #{Time.now.getlocal.zone}").
38
+ to_time.utc.to_i
33
39
  rescue
34
40
  raise "cannot parse timestamp '#{t}'."
35
41
  end
@@ -39,6 +45,7 @@ module Wavefront
39
45
  #
40
46
  # Return the time as milliseconds since the epoch
41
47
  #
48
+ return false unless t.is_a?(Integer)
42
49
  (t.to_f * 1000).round
43
50
  end
44
51
  end
@@ -57,8 +57,14 @@ module Wavefront
57
57
  append = "host=#{options[:host_name]} #{tags}"
58
58
  end
59
59
 
60
- @socket.puts [metric_name, metric_value, options[:timestamp].to_i,
60
+ str = [metric_name, metric_value, options[:timestamp].to_i,
61
61
  append].join(' ')
62
+
63
+ if options[:noop]
64
+ puts "metric to send: #{str}"
65
+ else
66
+ @socket.puts(str)
67
+ end
62
68
  end
63
69
 
64
70
  private
@@ -66,6 +72,5 @@ module Wavefront
66
72
  def get_socket(host, port)
67
73
  TCPSocket.new(host, port)
68
74
  end
69
-
70
75
  end
71
76
  end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,4 @@
1
- =begin
1
+ =begin
2
2
  Copyright 2015 Wavefront Inc.
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
@@ -19,6 +19,57 @@ require 'wavefront/client'
19
19
  require 'wavefront/writer'
20
20
  require 'wavefront/metadata'
21
21
  require 'wavefront/alerting'
22
+ require 'wavefront/events'
23
+ require 'wavefront/batch_writer'
24
+ require 'wavefront/cli'
25
+ require 'wavefront/cli/alerts'
26
+ require 'wavefront/cli/events'
27
+ require 'wavefront/cli/batch_write'
28
+ require 'wavefront/cli/write'
22
29
 
23
30
  TEST_TOKEN = "test"
24
31
  TEST_HOST = "metrics.wavefront.com"
32
+
33
+ # The following RSpec matcher is used to test things which `puts`
34
+ # (or related), which RSpec can't do by default. It works with RSpec
35
+ # 3, and was lifted wholesale from
36
+ # http://stackoverflow.com/questions/6372763/rspec-how-do-i-write-a-test-that-expects-certain-output-but-doesnt-care-about/28258747#28258747
37
+
38
+ RSpec::Matchers.define :match_stdout do |check|
39
+
40
+ @capture = nil
41
+
42
+ match do |block|
43
+
44
+ begin
45
+ stdout_saved = $stdout
46
+ $stdout = StringIO.new
47
+ block.call
48
+ ensure
49
+ @capture = $stdout
50
+ $stdout = stdout_saved
51
+ end
52
+
53
+ @capture.string.match check
54
+ end
55
+
56
+ failure_message do
57
+ "expected to #{description}"
58
+ end
59
+ failure_message_when_negated do
60
+ "expected not to #{description}"
61
+ end
62
+ description do
63
+ "match [#{check}] on stdout [#{@capture.string}]"
64
+ end
65
+
66
+ def supports_block_expectations?
67
+ true
68
+ end
69
+ end
70
+
71
+ class Mocket
72
+ def puts(str)
73
+ return true
74
+ end
75
+ end
@@ -0,0 +1,523 @@
1
+ =begin
2
+ Copyright 2016 Wavefront Inc.
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
15
+ =end
16
+
17
+ require 'spec_helper'
18
+ require 'socket'
19
+
20
+ opts = {}
21
+
22
+ describe Wavefront::BatchWriter do
23
+
24
+ describe '#initialize' do
25
+ it 'sets @opts correctly' do
26
+ k = Wavefront::BatchWriter.new({noop: true, proxy: 'myproxy'})
27
+ expect(k.instance_variable_get(:@opts)).to eq({
28
+ tags: false,
29
+ proxy: 'myproxy',
30
+ port: 2878,
31
+ noop: true,
32
+ novalidate: false,
33
+ verbose: false,
34
+ debug: false,
35
+ })
36
+ end
37
+
38
+ it 'allows unset @global_tags' do
39
+ k = Wavefront::BatchWriter.new
40
+ expect(k.instance_variable_get(:@global_tags)).to be nil
41
+ end
42
+
43
+ it 'sets @global_tags' do
44
+ k = Wavefront::BatchWriter.new(tags: { t1: 'v1', t2: 'v2' })
45
+ expect(k.instance_variable_get(:@global_tags)).to eq({t1: 'v1', t2: 'v2'})
46
+ end
47
+
48
+ end
49
+
50
+ describe '#write' do
51
+
52
+ # end-to-end test
53
+
54
+ context 'without noop set' do
55
+ it 'should write a single metric to a socket' do
56
+
57
+ socket = Mocket.new
58
+ allow(TCPSocket).to receive(:new).and_return(socket)
59
+ expect(socket).to receive(:puts).with(
60
+ 'test.metric 1234 1469987572 source=testhost t1="v1" t2="v2"')
61
+
62
+ k = Wavefront::BatchWriter.new(opts)
63
+ k.open_socket
64
+ expect(k.write(
65
+ path: 'test.metric',
66
+ value: 1234,
67
+ ts: Time.at(1469987572),
68
+ source: 'testhost',
69
+ tags: { t1: 'v1', t2: 'v2' },
70
+ )).to be(true)
71
+ expect(k.summary[:sent]).to eq(1)
72
+ expect(k.summary[:unsent]).to eq(0)
73
+ expect(k.summary[:rejected]).to eq(0)
74
+ end
75
+
76
+ it 'should write multiple metrics to a socket' do
77
+ socket = Mocket.new
78
+ allow(TCPSocket).to receive(:new).and_return(socket)
79
+ k = Wavefront::BatchWriter.new
80
+ k.open_socket
81
+ expect(socket).to receive(:puts).with(
82
+ 'test.metric_1 1234 1469987572 source=testhost t1="v1" t2="v2"')
83
+ expect(socket).to receive(:puts).with(
84
+ 'test.metric_2 2468 1469987572 source=testhost')
85
+ expect(k.write([
86
+ { path: 'test.metric_1',
87
+ value: 1234,
88
+ ts: Time.at(1469987572),
89
+ source: 'testhost',
90
+ tags: { t1: 'v1', t2: 'v2' },
91
+ },
92
+ { path: 'test.metric_2',
93
+ value: 2468,
94
+ ts: Time.at(1469987572),
95
+ source: 'testhost',
96
+ }
97
+ ])).to be(true)
98
+ expect(k.summary[:sent]).to eq(2)
99
+ expect(k.summary[:unsent]).to eq(0)
100
+ expect(k.summary[:rejected]).to eq(0)
101
+ end
102
+
103
+ it 'should let good points through and drop bad points' do
104
+ socket = Mocket.new
105
+ allow(TCPSocket).to receive(:new).and_return(socket)
106
+ k = Wavefront::BatchWriter.new(verbose: true)
107
+ k.open_socket
108
+ expect(socket).to receive(:puts).with(
109
+ 'test.metric_1 1234 1469987572 source=testhost t1="v1" t2="v2"')
110
+ expect(socket).to receive(:puts).with(
111
+ 'test.metric_2 2468 1469987572 source=testhost')
112
+ expect(k.write([
113
+ { path: 'test.metric_1',
114
+ value: 1234,
115
+ ts: Time.at(1469987572),
116
+ source: 'testhost',
117
+ tags: { t1: 'v1', t2: 'v2' },
118
+ },
119
+ { path: 'bogus_metric',
120
+ },
121
+ { path: 'test.metric_2',
122
+ value: 2468,
123
+ ts: Time.at(1469987572),
124
+ source: 'testhost',
125
+ }
126
+ ])).to be(false)
127
+ expect(k.summary[:sent]).to eq(2)
128
+ expect(k.summary[:unsent]).to eq(0)
129
+ expect(k.summary[:rejected]).to eq(1)
130
+ end
131
+ end
132
+
133
+ context 'with noop set' do
134
+ it 'should not write a metric to a socket object' do
135
+ m = 'test.metric 1234 1469987572 source=testhost t1="v1" t2="v2"'
136
+ socket = Mocket.new
137
+ allow(TCPSocket).to receive(:new).and_return(socket)
138
+ expect(socket).not_to receive(:puts).with(m)
139
+ k = Wavefront::BatchWriter.new(noop: true)
140
+ k.open_socket
141
+ k.write(
142
+ path: 'test.metric',
143
+ value: 1234,
144
+ ts: Time.at(1469987572),
145
+ source: 'testhost',
146
+ tags: { t1: 'v1', t2: 'v2' },
147
+ )
148
+ expect(k.summary[:sent]).to eq(0)
149
+ expect(k.summary[:unsent]).to eq(0)
150
+ expect(k.summary[:rejected]).to eq(0)
151
+ end
152
+ end
153
+ end
154
+
155
+ describe '#setup_options' do
156
+ defaults = {
157
+ tags: false,
158
+ proxy: 'wavefront',
159
+ port: 2878,
160
+ noop: false,
161
+ novalidate: false,
162
+ verbose: false,
163
+ debug: false,
164
+ }
165
+
166
+ it 'falls back to all defaults' do
167
+ k = Wavefront::BatchWriter.new
168
+ expect(k.setup_options({}, defaults)).to eq(defaults)
169
+ end
170
+
171
+ it 'allows overriding of defaults' do
172
+ k = Wavefront::BatchWriter.new
173
+ expect(k.setup_options({noop: true, proxy: 'myproxy'},
174
+ defaults)).to eq({ tags: false,
175
+ proxy: 'myproxy',
176
+ port: 2878,
177
+ noop: true,
178
+ novalidate: false,
179
+ verbose: false,
180
+ debug: false,
181
+ })
182
+ end
183
+ end
184
+
185
+ describe '#valid_point?' do
186
+ context 'novalidate is true' do
187
+ k = Wavefront::BatchWriter.new({novalidate: true})
188
+
189
+ it 'lets through an invalid point' do
190
+ expect(k.valid_point?({junk: true})).to be(true)
191
+ end
192
+ end
193
+
194
+ context 'novalidate is false' do
195
+ opts[:novalidate] = false
196
+ k = Wavefront::BatchWriter.new(opts)
197
+
198
+ it 'lets through a valid point with all members' do
199
+ expect(k.valid_point?(
200
+ path: 'test.metric',
201
+ value: 123456,
202
+ ts: Time.now,
203
+ source: 'testhost',
204
+ tags: { tag1: 'value 1', tag2: 'value 2' },
205
+ )).to be(true)
206
+ end
207
+
208
+ it 'lets through a valid point with no timestamp' do
209
+ expect(k.valid_point?(
210
+ path: 'test.metric',
211
+ value: 123456,
212
+ source: 'testhost',
213
+ tags: { tag1: 'value 1', tag2: 'value 2' },
214
+ )).to be(true)
215
+ end
216
+
217
+ it 'lets through a valid point with no tags' do
218
+ expect(k.valid_point?(
219
+ path: 'test.metric',
220
+ value: 123456,
221
+ ts: Time.now,
222
+ source: 'testhost',
223
+ )).to be(true)
224
+ end
225
+
226
+ it 'raises InvalidMetricName on invalid metric name' do
227
+ expect{k.valid_point?(
228
+ path: '!n\/@1!d_metric',
229
+ value: 123456,
230
+ ts: Time.now,
231
+ source: 'testhost',
232
+ tags: { tag1: 'value 1', tag2: 'value 2' },
233
+ )}.to raise_exception(Wavefront::Exception::InvalidMetricName)
234
+ end
235
+
236
+ it 'raises InvalidMetricValue on invalid metric value' do
237
+ expect{k.valid_point?(
238
+ path: 'test.metric',
239
+ value: 'three_point_one_four',
240
+ ts: Time.now,
241
+ source: 'testhost',
242
+ tags: { tag1: 'value 1', tag2: 'value 2' },
243
+ )}.to raise_exception(Wavefront::Exception::InvalidMetricValue)
244
+ end
245
+
246
+ it 'raises InvalidTimestamp on invalid timestamp' do
247
+ expect{k.valid_point?(
248
+ path: 'test.metric',
249
+ value: 123456,
250
+ ts: 'half_past_eleven',
251
+ source: 'testhost',
252
+ tags: { tag1: 'value 1', tag2: 'value 2' },
253
+ )}.to raise_exception(Wavefront::Exception::InvalidTimestamp)
254
+ end
255
+
256
+ it 'raises InvalidHostname on invalid source' do
257
+ expect{k.valid_point?(
258
+ path: 'test.metric',
259
+ value: 123456,
260
+ ts: Time.now,
261
+ source: ['source1', 'source2'],
262
+ tags: { tag1: 'value 1', tag2: 'value 2' },
263
+ )}.to raise_exception(Wavefront::Exception::InvalidSource)
264
+ end
265
+
266
+ it 'raises InvalidTag on invalid tag' do
267
+ expect{k.valid_point?(
268
+ path: 'test.metric',
269
+ value: 123456,
270
+ ts: Time.now,
271
+ source: 'testhost',
272
+ tags: { :tag1 => 'value 1', :'invalid tag' => 'value 2' },
273
+ )}.to raise_exception(Wavefront::Exception::InvalidTag)
274
+ end
275
+ end
276
+
277
+ end
278
+
279
+ describe '#tag_hash_to_str' do
280
+ k = Wavefront::BatchWriter.new(opts)
281
+
282
+ it 'converts multiple tags to a string' do
283
+ expect(k.tag_hash_to_str({tag1: 'value 1', tag2: 'value 2' })).
284
+ to eq('tag1="value 1" tag2="value 2"')
285
+ end
286
+
287
+ it 'converts an empty hash to an empty string' do
288
+ expect(k.tag_hash_to_str({})).to eq('')
289
+ end
290
+ end
291
+
292
+ describe '#hash_to_wf' do
293
+ context 'when global tags are not defined' do
294
+ k = Wavefront::BatchWriter.new(opts)
295
+
296
+ it 'converts a known, full, point hash to a known metric' do
297
+ expect(k.hash_to_wf(
298
+ path: 'test.metric',
299
+ value: 123456,
300
+ ts: Time.at(1469987572),
301
+ source: 'testhost',
302
+ tags: { t1: 'v1', t2: 'v2' },
303
+ )).to eq(
304
+ 'test.metric 123456 1469987572 source=testhost t1="v1" t2="v2"')
305
+ end
306
+
307
+ it 'converts a tag-less point hash to a known metric' do
308
+ expect(k.hash_to_wf(
309
+ path: 'test.metric',
310
+ value: 123456,
311
+ ts: Time.at(1469987572),
312
+ source: 'testhost',
313
+ )).to eq('test.metric 123456 1469987572 source=testhost')
314
+ end
315
+
316
+ it 'converts a timestamp-less point hash to a known metric' do
317
+ expect(k.hash_to_wf(
318
+ path: 'test.metric',
319
+ value: 123456,
320
+ source: 'testhost',
321
+ tags: { t1: 'v1', t2: 'v2' },
322
+ )).to eq('test.metric 123456 source=testhost t1="v1" t2="v2"')
323
+ end
324
+
325
+ it 'raises ArgumentError if vital components are missing' do
326
+ expect{k.hash_to_wf({})}.to raise_exception(ArgumentError)
327
+ end
328
+ end
329
+
330
+ context 'when global tags are defined' do
331
+ k = Wavefront::BatchWriter.new(tags: { gtag1: 'gval1', gtag2:
332
+ 'gval2'})
333
+
334
+ it 'converts a known, full, point hash to a known metric' do
335
+ expect(k.hash_to_wf(
336
+ path: 'test.metric',
337
+ value: 123456,
338
+ ts: Time.at(1469987572),
339
+ source: 'testhost',
340
+ tags: { t1: 'v1', t2: 'v2' },
341
+ )).to eq(
342
+ 'test.metric 123456 1469987572 source=testhost t1="v1" ' +
343
+ 't2="v2" gtag1="gval1" gtag2="gval2"')
344
+ end
345
+ end
346
+ end
347
+
348
+ describe '#send_point' do
349
+ context 'without noop set' do
350
+ it 'should write a metric to a socket object' do
351
+ m = 'test.metric 1234 1469987572 source=testhost t1="v1" t2="v2"'
352
+ socket = Mocket.new
353
+ allow(TCPSocket).to receive(:new).and_return(socket)
354
+ expect(socket).to receive(:puts).with(m)
355
+ k = Wavefront::BatchWriter.new(opts)
356
+ k.open_socket
357
+ k.send_point(m)
358
+ end
359
+ end
360
+
361
+ context 'with noop set' do
362
+ it 'should not write a metric to a socket object' do
363
+ m = 'test.metric 1234 1469987572 source=testhost t1="v1" t2="v2"'
364
+ socket = Mocket.new
365
+ allow(TCPSocket).to receive(:new).and_return(socket)
366
+ expect(socket).not_to receive(:puts).with(m)
367
+ k = Wavefront::BatchWriter.new(noop: true)
368
+ k.open_socket
369
+ k.send_point(m)
370
+ expect{k.send_point(m)}.to match_stdout("Would send: #{m}")
371
+ end
372
+ end
373
+ end
374
+
375
+ describe '#open_socket' do
376
+
377
+ context 'without noop set' do
378
+ it 'creates a socket object with the default parameters' do
379
+ allow(TCPSocket).to receive(:new)
380
+ expect(TCPSocket).to receive(:new).with('wfp', 2878)
381
+ allow_any_instance_of(Wavefront::BatchWriter).to receive(:new)
382
+ k = Wavefront::BatchWriter.new
383
+ k.instance_variable_set(:@opts, proxy: 'wfp', port: 2878)
384
+ k.open_socket
385
+ end
386
+
387
+ it 'raises an exception on an invalid endpoint' do
388
+ allow_any_instance_of(Wavefront::BatchWriter).to receive(:new)
389
+ k = Wavefront::BatchWriter.new
390
+ k.instance_variable_set(:@opts, proxy: 'wfp', port: 2879)
391
+ expect{k.open_socket}.to raise_exception(
392
+ Wavefront::Exception::InvalidEndpoint)
393
+ end
394
+ end
395
+
396
+ context 'with noop set' do
397
+ it 'prints a message but does not open a socket' do
398
+ allow_any_instance_of(Wavefront::BatchWriter).to receive(:new)
399
+ k = Wavefront::BatchWriter.new
400
+ k.instance_variable_set(:@opts, proxy: 'wfp', port:
401
+ 2878, noop: true)
402
+ expect(k.open_socket).to be(true)
403
+ expect{k.open_socket}.to match_stdout(
404
+ 'No-op requested. Not opening connection to proxy.')
405
+ end
406
+ end
407
+ end
408
+
409
+ describe '#close_socket' do
410
+ end
411
+
412
+ describe '#valid_path?' do
413
+ k = Wavefront::BatchWriter.new(opts)
414
+
415
+ it 'accepts a-z, 0-9, _ and -' do
416
+ expect(k.valid_path?('a.l33t.metric_path-passes')).to be(true)
417
+ end
418
+
419
+ it 'accepts nearly very long paths' do
420
+ expect(k.valid_path?('a' * 1023)).to be(true)
421
+ end
422
+
423
+ it 'rejects very long paths' do
424
+ expect{k.valid_path?('a' * 1024)}.to raise_exception(
425
+ Wavefront::Exception::InvalidMetricName)
426
+ end
427
+
428
+ it 'rejects upper-case letters' do
429
+ expect{k.valid_path?('NO.NEED.TO.SHOUT')}.to raise_exception(
430
+ Wavefront::Exception::InvalidMetricName)
431
+ end
432
+
433
+ it 'rejects odd characters' do
434
+ expect{k.valid_path?('metric.is.(>_<)')}.to raise_exception(
435
+ Wavefront::Exception::InvalidMetricName)
436
+ end
437
+ end
438
+
439
+ describe '#valid_value?' do
440
+ k = Wavefront::BatchWriter.new(opts)
441
+
442
+ it 'accepts integers' do
443
+ expect(k.valid_value?(123456)).to be(true)
444
+ end
445
+
446
+ it 'accepts 0' do
447
+ expect(k.valid_value?(0)).to be(true)
448
+ end
449
+
450
+ it 'accepts negative integers' do
451
+ expect(k.valid_value?(-10)).to be(true)
452
+ end
453
+
454
+ it 'accepts decimals' do
455
+ expect(k.valid_value?(1.2345678)).to be(true)
456
+ end
457
+
458
+ it 'accepts exponential notation' do
459
+ expect(k.valid_value?(1.23e04)).to be(true)
460
+ end
461
+
462
+ it 'rejects strings which look like numbers' do
463
+ expect { k.valid_value?('1.23')}.to raise_exception(
464
+ Wavefront::Exception::InvalidMetricValue)
465
+ end
466
+ end
467
+
468
+ describe '#valid_ts?' do
469
+ k = Wavefront::BatchWriter.new(opts)
470
+
471
+ it 'rejects integers' do
472
+ expect { k.valid_ts?(Time.now.to_i) }.to raise_exception(
473
+ Wavefront::Exception::InvalidTimestamp)
474
+ end
475
+
476
+ it 'accepts Times' do
477
+ expect(k.valid_ts?(Time.now)).to be(true)
478
+ end
479
+
480
+ it 'accepts DateTimes' do
481
+ expect(k.valid_ts?(DateTime.now)).to be(true)
482
+ end
483
+
484
+ it 'accepts Dates' do
485
+ expect(k.valid_ts?(Date.today)).to be(true)
486
+ end
487
+ end
488
+
489
+ describe '#valid_tags?' do
490
+ k = Wavefront::BatchWriter.new(opts)
491
+
492
+ it 'accepts zero tags' do
493
+ expect(k.valid_tags?({})).to be(true)
494
+ end
495
+
496
+ it 'accepts nice sensible tags' do
497
+ expect(k.valid_tags?({tag1: 'val1', tag2: 'val2'})).to be(true)
498
+ end
499
+
500
+ it 'accepts spaces and symbols in values' do
501
+ expect(k.valid_tags?({tag1: 'val 1', tag2: 'val 2'})).to be(true)
502
+ expect(k.valid_tags?({tag1: '(>_<)', tag2: '^_^'})).to be(true)
503
+ end
504
+
505
+ it 'rejects spaces and symbols in keys' do
506
+ expect { k.valid_tags?({'tag 1' => 'val1',
507
+ 'tag 2' => 'val2'}) }.to raise_exception(
508
+ Wavefront::Exception::InvalidTag)
509
+ expect { k.valid_tags?({'(>_<)' => 'val1',
510
+ '^_^' => 'val2'}) }.to raise_exception(
511
+ Wavefront::Exception::InvalidTag)
512
+ end
513
+
514
+ it 'rejects long keys and/or values' do
515
+ expect { k.valid_tags?({tag1: 'v' * 255}) }.to raise_exception(
516
+ Wavefront::Exception::InvalidTag)
517
+ expect { k.valid_tags?({'k' * 255 => 'val1'}) }.to raise_exception(
518
+ Wavefront::Exception::InvalidTag)
519
+ expect { k.valid_tags?({'k' * 130 => 'v' * 130}) }.to raise_exception(
520
+ Wavefront::Exception::InvalidTag)
521
+ end
522
+ end
523
+ end