tsdb_time_series 4.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # @see Opower::TimeSeries::TSDBClient
3
+ module Opower
4
+ # @see Opower::TimeSeries::TSDBClient
5
+ module TimeSeries
6
+ # Controls the version of the TimeSeries gem.
7
+ VERSION = '4.1.2'
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'time_series'
5
+
6
+ describe Opower::TimeSeries::TSClient do
7
+ describe '#write' do
8
+ subject { Opower::TimeSeries::TSClient.new('127.0.0.1', 60000) }
9
+ let(:config) { { name: 'test1.test2', timestamp: 12132342, value: 1, tags: { host: 'localhost' } } }
10
+ let(:metric) { Opower::TimeSeries::Metric.new(config) }
11
+
12
+ context 'in dry run mode' do
13
+ it 'returns the put string' do
14
+ subject.configure(dry_run: true)
15
+ call = subject.write(metric)
16
+ expect(call).to eq("echo \"put test1.test2 12132342 1 host=localhost\" | nc -w 30 127.0.0.1 60000")
17
+ end
18
+ end
19
+
20
+ context 'in normal mode' do
21
+ it 'errors on failing to insert data' do
22
+ message = "Failed to insert metric #{metric.name} with value of #{metric.value} into OpenTSDB."
23
+ expect { subject.write(metric) }.to raise_error(IOError, message)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'time_series'
5
+
6
+ describe Opower::TimeSeries::TSClient do
7
+ describe '#run_synthetic_query' do
8
+ subject { Opower::TimeSeries::TSClient.new('opentsdb.foo.com', 4242) }
9
+
10
+ it 'computes a simple formula correctly' do
11
+ m = [{ metric: 'sys.numa.allocation', tags: { host: 'opentsdb.foo.com' } }]
12
+ config = { format: :json, start: 1421676714, finish: 1421676774, m: m }
13
+ @query_one = Opower::TimeSeries::Query.new(config)
14
+
15
+ m = [{ metric: 'sys.numa.zoneallocs', tags: { host: 'opentsdb.foo.com' } }]
16
+ config = { format: :json, start: 1421676714, finish: 1421676774, m: m }
17
+ @query_two = Opower::TimeSeries::Query.new(config)
18
+
19
+ # stub requests
20
+ stub_request(:get, subject.query_uri(@query_one)).to_return(status: 200, body: Fixtures::SYS_ALLOCATION)
21
+ stub_request(:get, subject.query_uri(@query_two)).to_return(status: 200, body: Fixtures::SYS_ZONE_ALLOCS)
22
+
23
+ synthetic_results = subject.run_synthetic_query('test', 'x / y', x: @query_one, y: @query_two)
24
+ expect(synthetic_results.length).not_to eq(0)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,146 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'time_series'
5
+
6
+ describe Opower::TimeSeries::TSClient do
7
+ describe '#suggest' do
8
+ subject { Opower::TimeSeries::TSClient.new('opentsdb.foo.com', 4242) }
9
+
10
+ before do
11
+ stub_request(:get, subject.suggest_uri('mtest')).to_return(body: '[]')
12
+ stub_request(:get, subject.suggest_uri('sys')).to_return(body: Fixtures::SUGGEST_SYS)
13
+ end
14
+
15
+ context 'in dry run mode' do
16
+ subject do
17
+ super().configure(dry_run: true)
18
+ super()
19
+ end
20
+
21
+ it 'returns the proper URI' do
22
+ url = subject.suggest_uri('mtest')
23
+ expect(url).to eq('http://opentsdb.foo.com:4242/api/suggest?type=metrics&q=mtest&max=25')
24
+ end
25
+ end
26
+
27
+ context 'in normal mode' do
28
+ subject do
29
+ super().configure(dry_run: false)
30
+ super()
31
+ end
32
+
33
+ it 'returns an empty array for a query with no expected results' do
34
+ suggestions = subject.suggest('mtest')
35
+ expect(suggestions).to eq([])
36
+ end
37
+
38
+ it 'returns data for a query with expected results' do
39
+ suggestions = subject.suggest('sys')
40
+ expect(suggestions).to eq(JSON.parse(Fixtures::SUGGEST_SYS))
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#run_query' do
46
+ subject { Opower::TimeSeries::TSClient.new('opentsdb.foo.com', 4242) }
47
+
48
+ context 'with invalid input' do
49
+ it 'raises an error for a bad metric name' do
50
+ m = [{ metric: 'mtest' }]
51
+ config = { format: :json, start: 1421676714, finish: 1421676774, m: m }
52
+ query = Opower::TimeSeries::Query.new(config)
53
+ stub_request(:get, subject.query_uri(query)).to_return(status: 500, body: Fixtures::BAD_METRIC)
54
+
55
+ results = subject.run_query(query).results
56
+ expect(results).to include(JSON.parse(Fixtures::BAD_METRIC))
57
+ end
58
+
59
+ it 'raises an error for a bad tagk name ' do
60
+ m = [{ metric: 'sys.numa.allocation', tags: { bad_tagk: 'opentsdb.foo.com' } }]
61
+ config = { format: :json, start: 1421676714, finish: 1421676774, m: m }
62
+ query = Opower::TimeSeries::Query.new(config)
63
+ stub_request(:get, subject.query_uri(query)).to_return(status: 500, body: Fixtures::BAD_TAGK)
64
+
65
+ results = subject.run_query(query).results
66
+ expect(results).to include(JSON.parse(Fixtures::BAD_TAGK))
67
+ end
68
+ end
69
+
70
+ context 'with valid input' do
71
+ it 'returns an empty JSON array for a query with no expected results' do
72
+ m = [{ metric: 'sys.numa.allocation' }]
73
+ config = { format: :json, start: 1420676714, finish: 1420676774, m: m }
74
+ query = Opower::TimeSeries::Query.new(config)
75
+ stub_request(:get, subject.query_uri(query)).to_return(status: 200, body: '[]')
76
+
77
+ results = subject.run_query(query).results
78
+ expect(results).to eq([])
79
+ end
80
+
81
+ it 'returns data for a query in JSON format' do
82
+ m = [{ metric: 'sys.numa.allocation', tags: { host: 'opentsdb.foo.com' } }]
83
+ config = { format: :json, start: 1420676714, finish: 1420676774, m: m }
84
+ query = Opower::TimeSeries::Query.new(config)
85
+ stub_request(:get, subject.query_uri(query)).to_return(body: Fixtures::SYS_ALLOCATION)
86
+
87
+ results = subject.run_query(query).results
88
+ expect(results).to eq(JSON.parse(Fixtures::SYS_ALLOCATION))
89
+ end
90
+
91
+ it 'returns data for a rate query in JSON format' do
92
+ m = [{ metric: 'sys.numa.allocation', rate: true, tags: { host: 'opentsdb.foo.com' } }]
93
+ config = { format: :json, start: 1420676714, finish: 1420676774, m: m }
94
+ query = Opower::TimeSeries::Query.new(config)
95
+ stub_request(:get, subject.query_uri(query)).to_return(body: Fixtures::SYS_ALLOC_RATE)
96
+
97
+ results = subject.run_query(query).results
98
+ expect(results).to eq(JSON.parse(Fixtures::SYS_ALLOC_RATE))
99
+ end
100
+
101
+ it 'returns a URL for a query in PNG format' do
102
+ m = [{ metric: 'sys.numa.allocation', tags: { host: 'opentsdb.foo.com' } }]
103
+ config = { format: :png, start: 1420676714, finish: 1420676774, m: m }
104
+ query = Opower::TimeSeries::Query.new(config)
105
+ results = subject.run_query(query)
106
+ expect(results).not_to eq('')
107
+ expect(results).not_to include('Internal Server Error')
108
+ end
109
+
110
+ it 'returns data for multiple queries' do
111
+ queries = []
112
+ 3.times do
113
+ m = [{ metric: 'sys.numa.allocation', tags: { host: 'opentsdb.foo.com' } }]
114
+ config = { format: :json, start: '1h-ago', m: m }
115
+ query = Opower::TimeSeries::Query.new(config)
116
+ stub_request(:get, subject.query_uri(query)).to_return(body: Fixtures::SYS_ALLOCATION)
117
+ queries << query
118
+ end
119
+
120
+ results = subject.run_queries(queries)
121
+ expect(results.length).to eq(3)
122
+ results.each do |r|
123
+ expect(r.results).not_to eq('')
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ describe '#valid?' do
130
+ subject { Opower::TimeSeries::TSClient.new('opentsdb.foo.com', 4242) }
131
+
132
+ context 'with a valid connection' do
133
+ it 'returns true' do
134
+ stub_request(:get, "#{subject.client}api/version").to_return(status: 200)
135
+ expect(subject.valid?).to be_truthy
136
+ end
137
+ end
138
+
139
+ context 'with an invalid connection' do
140
+ it 'returns false' do
141
+ stub_request(:get, "#{subject.client}api/version").to_timeout
142
+ expect(subject.valid?).to be_falsey
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,17 @@
1
+ # Fixtures wrapper - contains raw data used by webmocks
2
+ module Fixtures
3
+ def self.fixture(filename)
4
+ File.expand_path("../fixtures/#{filename}", __FILE__)
5
+ end
6
+
7
+ def self.read_file(filename)
8
+ IO.read(fixture(filename))
9
+ end
10
+
11
+ BAD_METRIC = read_file('errors/no_metric.json')
12
+ BAD_TAGK = read_file('errors/no_tag_key.json')
13
+ SUGGEST_SYS = read_file('suggest/sys.json')
14
+ SYS_ALLOCATION = read_file('query/sys.numa.allocation.json')
15
+ SYS_ALLOC_RATE = read_file('query/sys.numa.allocation.rate.json')
16
+ SYS_ZONE_ALLOCS = read_file('query/sys.numa.zoneallocs.json')
17
+ end
@@ -0,0 +1,6 @@
1
+ {
2
+ "error": {
3
+ "code": 500,
4
+ "message": "No such name for 'metrics': 'mtest'"
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "error": {
3
+ "code": 500,
4
+ "message": "No such name for 'tagk': 'bad_tagk'"
5
+ }
6
+ }
@@ -0,0 +1,17 @@
1
+ [
2
+ {
3
+ "metric": "sys.numa.allocation",
4
+ "tags": { "host": "opentsdb.foo.com" },
5
+ "aggregateTags": [
6
+ "type",
7
+ "node"
8
+ ],
9
+ "dps": {
10
+ "1421676714": 15,
11
+ "1421676729": 16,
12
+ "1421676744": 0,
13
+ "1421676759": 5,
14
+ "1421676774": 13
15
+ }
16
+ }
17
+ ]
@@ -0,0 +1,17 @@
1
+ [
2
+ {
3
+ "metric": "sys.numa.allocation",
4
+ "tags": { "host": "opentsdb.foo.com" },
5
+ "aggregateTags": [
6
+ "type",
7
+ "node"
8
+ ],
9
+ "dps": {
10
+ "1421676714": 0.02,
11
+ "1421676729": 0.05,
12
+ "1421676744": 0,
13
+ "1421676759": 0.01,
14
+ "1421676774": 0.02
15
+ }
16
+ }
17
+ ]
@@ -0,0 +1,17 @@
1
+ [
2
+ {
3
+ "metric": "sys.numa.zoneallocs",
4
+ "tags": {"host": "opentsdb.foo.com"},
5
+ "aggregateTags": [
6
+ "type",
7
+ "node"
8
+ ],
9
+ "dps": {
10
+ "1421676714": 3,
11
+ "1421676729": 2,
12
+ "1421676744": 55,
13
+ "1421676759": 1,
14
+ "1421676774": 3
15
+ }
16
+ }
17
+ ]
@@ -0,0 +1 @@
1
+ ["sys.numa.allocation","sys.numa.foreign_allocs","sys.numa.interleave","sys.numa.zoneallocs","system.context_switches_per_second","system.interrupts_per_second"]
@@ -0,0 +1,129 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'time_series'
5
+ require 'docker'
6
+
7
+ describe Opower::TimeSeries::TSClient do
8
+ # Integration tests run inside a Docker container
9
+ before :all do
10
+ # allow HTTP during integration tests
11
+ WebMock.allow_net_connect!
12
+
13
+ @container = Docker::Container.create('Image' => 'opower/opentsdb')
14
+ @container.start('PortBindings' => { '4242/tcp' => [{ 'HostPort' => '48000' }] })
15
+
16
+ # check if we're runing boot2docker
17
+ if ENV['DOCKER_HOST'].nil?
18
+ network_settings = @container.json.fetch('NetworkSettings')
19
+ @ip = network_settings.fetch('IPAddress') # TODO: verify this works for boot2docker
20
+ @port = 4242
21
+ else
22
+ @ip = `boot2docker ip 2> /dev/null`
23
+ @port = 48000
24
+ end
25
+
26
+ sleep 30
27
+ attempts = 0
28
+ client = Opower::TimeSeries::TSClient.new(@ip, @port)
29
+
30
+ while client.valid? == false && attempts < 10
31
+ sleep 5
32
+ attempts += 1
33
+ end
34
+
35
+ fail RemoteError('Failed to start Docker container!') if attempts > 10 && !client.valid?
36
+
37
+ # prewrite expected metrics
38
+ metrics = [{ name: 'cpu.load', timestamp: 1420676750, value: 1, tags: { host: 'localhost' } },
39
+ { name: 'test1.test2', timestamp: 12132343, value: 1, tags: { host: 'localhost' } },
40
+ { name: 'metric1', timestamp: 1421676714, value: 1, tags: { host: 'localhost' } },
41
+ { name: 'metric2', timestamp: 1421676714, value: 2, tags: { host: 'localhost' } }]
42
+
43
+ metrics.each { |metric| client.write(Opower::TimeSeries::Metric.new(metric)) }
44
+
45
+ # wait for OpenTSDB to synch
46
+ sleep 3
47
+ end
48
+
49
+ after :all do
50
+ @container.stop
51
+ @container.delete(force: true)
52
+
53
+ # resume blocking afterwards (in case acceptance tests afterwards)
54
+ WebMock.disable_net_connect!
55
+ end
56
+
57
+ describe '#suggest' do
58
+ subject { Opower::TimeSeries::TSClient.new(@ip, @port) }
59
+
60
+ context 'with no expected results' do
61
+ it 'returns an empty array' do
62
+ suggestions = subject.suggest('mtest')
63
+ expect(suggestions).to eq([])
64
+ end
65
+ end
66
+
67
+ context 'with expected results' do
68
+ it 'returns data' do
69
+ suggestions = subject.suggest('test1.test2')
70
+ expect(suggestions).to eq(['test1.test2'])
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#run_query' do
76
+ subject { Opower::TimeSeries::TSClient.new(@ip, @port) }
77
+
78
+ context 'with bad input' do
79
+ it 'raises an error for a bad metric name' do
80
+ m = [{ metric: 'mtest' }]
81
+ config = { format: :json, start: 1421676714, finish: 1421676774, m: m }
82
+ query = Opower::TimeSeries::Query.new(config)
83
+
84
+ results = subject.run_query(query)
85
+ expect(results.errors?).to be_truthy
86
+ expect(results.error_message).to eq("No such name for 'metrics': 'mtest'")
87
+ end
88
+
89
+ it 'raises an error for a bad tagk name ' do
90
+ m = [{ metric: 'test1.test2', tags: { bad_tagk: 'opentsdb.foo.com' } }]
91
+ config = { format: :json, start: 1421676714, finish: 1421676774, m: m }
92
+ query = Opower::TimeSeries::Query.new(config)
93
+
94
+ results = subject.run_query(query)
95
+ expect(results.errors?).to be_truthy
96
+ expect(results.error_message).to eq("No such name for 'tagk': 'bad_tagk'")
97
+ end
98
+ end
99
+
100
+ context 'with valid input' do
101
+ it 'returns data' do
102
+ m = [{ metric: 'cpu.load' }]
103
+ config = { format: :json, start: 1420676714, finish: 1420676774, m: m }
104
+ query = Opower::TimeSeries::Query.new(config)
105
+
106
+ results = subject.run_query(query).results
107
+ expect(results.length).not_to eq(0)
108
+ expect(results[0].fetch('dps')).to include('1420676750' => 1)
109
+ end
110
+ end
111
+ end
112
+
113
+ describe '#run_synthetic_query' do
114
+ subject { Opower::TimeSeries::TSClient.new(@ip, @port) }
115
+
116
+ it 'computes a simple formula correctly' do
117
+ m = [{ metric: 'metric1' }]
118
+ config = { format: :json, start: 1421676000, finish: 1421676774, m: m }
119
+ query_one = Opower::TimeSeries::Query.new(config)
120
+
121
+ m = [{ metric: 'metric2' }]
122
+ config = { format: :json, start: 1421676000, finish: 1421676774, m: m }
123
+ query_two = Opower::TimeSeries::Query.new(config)
124
+
125
+ synthetic_results = subject.run_synthetic_query('test', 'x / y', x: query_one, y: query_two)
126
+ expect(synthetic_results.length).not_to eq(0)
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,15 @@
1
+ require 'simplecov'
2
+ require 'webmock/rspec'
3
+
4
+ SimpleCov.start 'rails' do
5
+ coverage_dir 'metrics/coverage'
6
+ end
7
+
8
+ pid = Process.pid
9
+ SimpleCov.at_exit do
10
+ SimpleCov.result.format! if Process.pid == pid
11
+ end
12
+
13
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
14
+
15
+ require File.expand_path('../fixtures.rb', __FILE__)