tsdb_time_series 4.1.2

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,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__)