wavefront-client 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
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