influxdb-api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +12 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +208 -0
  8. data/Rakefile +6 -0
  9. data/influxdb-api.gemspec +27 -0
  10. data/lib/influxdb-api.rb +2 -0
  11. data/lib/influxdb.rb +4 -0
  12. data/lib/influxdb/api.rb +38 -0
  13. data/lib/influxdb/api/client.rb +99 -0
  14. data/lib/influxdb/api/client/connection.rb +55 -0
  15. data/lib/influxdb/api/client/connection_pool.rb +52 -0
  16. data/lib/influxdb/api/client/errors.rb +62 -0
  17. data/lib/influxdb/api/client/response.rb +14 -0
  18. data/lib/influxdb/api/client/selector.rb +27 -0
  19. data/lib/influxdb/api/configuration.rb +116 -0
  20. data/lib/influxdb/api/database.rb +29 -0
  21. data/lib/influxdb/api/extensions.rb +15 -0
  22. data/lib/influxdb/api/namespaces.rb +50 -0
  23. data/lib/influxdb/api/namespaces/base.rb +61 -0
  24. data/lib/influxdb/api/namespaces/cluster_admins.rb +14 -0
  25. data/lib/influxdb/api/namespaces/continuous_queries.rb +13 -0
  26. data/lib/influxdb/api/namespaces/databases.rb +13 -0
  27. data/lib/influxdb/api/namespaces/series.rb +52 -0
  28. data/lib/influxdb/api/namespaces/servers.rb +12 -0
  29. data/lib/influxdb/api/namespaces/shard_spaces.rb +33 -0
  30. data/lib/influxdb/api/namespaces/shards.rb +10 -0
  31. data/lib/influxdb/api/namespaces/users.rb +18 -0
  32. data/lib/influxdb/api/namespaces/with_database.rb +20 -0
  33. data/lib/influxdb/api/server_version.rb +62 -0
  34. data/lib/influxdb/api/version.rb +5 -0
  35. data/spec/lib/influxdb/api/client/connection_pool_spec.rb +61 -0
  36. data/spec/lib/influxdb/api/client/connection_spec.rb +72 -0
  37. data/spec/lib/influxdb/api/client/response_spec.rb +11 -0
  38. data/spec/lib/influxdb/api/client/selector_spec.rb +24 -0
  39. data/spec/lib/influxdb/api/client_spec.rb +110 -0
  40. data/spec/lib/influxdb/api/configuration_spec.rb +54 -0
  41. data/spec/lib/influxdb/api/database_spec.rb +32 -0
  42. data/spec/lib/influxdb/api/namespaces/cluster_admins_spec.rb +46 -0
  43. data/spec/lib/influxdb/api/namespaces/continuous_queries_spec.rb +42 -0
  44. data/spec/lib/influxdb/api/namespaces/databases_spec.rb +36 -0
  45. data/spec/lib/influxdb/api/namespaces/series_spec.rb +119 -0
  46. data/spec/lib/influxdb/api/namespaces/servers_spec.rb +30 -0
  47. data/spec/lib/influxdb/api/namespaces/shard_spaces_spec.rb +55 -0
  48. data/spec/lib/influxdb/api/namespaces/shards_spec.rb +52 -0
  49. data/spec/lib/influxdb/api/namespaces/users_spec.rb +55 -0
  50. data/spec/lib/influxdb/api/namespaces_spec.rb +65 -0
  51. data/spec/lib/influxdb/api/server_version_spec.rb +51 -0
  52. data/spec/spec_helper.rb +28 -0
  53. metadata +183 -0
@@ -0,0 +1,14 @@
1
+ module Influxdb
2
+ module Api
3
+ module Namespaces
4
+ class ClusterAdmins < Base
5
+ resource_path '/cluster_admins'
6
+
7
+ def update(name, attributes)
8
+ perform_post(resource_path(name), {}, attributes)
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Influxdb
2
+ module Api
3
+ module Namespaces
4
+ class ContinuousQueries < WithDatabase
5
+ resource_path '/continuous_queries'
6
+
7
+ def create(query)
8
+ super(query: query)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Influxdb
2
+ module Api
3
+ module Namespaces
4
+ class Databases < Base
5
+ resource_path '/db'
6
+
7
+ def create(name)
8
+ super(name: name)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,52 @@
1
+ module Influxdb
2
+ module Api
3
+ module Namespaces
4
+ class Series < WithDatabase
5
+ resource_path '/series'
6
+
7
+ undef_method(:create) if method_defined?(:create)
8
+
9
+ def all
10
+ raw_execute('list series').map{|s| s['name']}
11
+ end
12
+
13
+ def write(*series)
14
+ series = if series.first.is_a?(String) || series.first.is_a?(Symbol)
15
+ { series.shift => series.shift }
16
+ else
17
+ series.shift
18
+ end
19
+
20
+ series = series.map{|name, points| writing_block(name, Array.wrap(points)) }
21
+ perform_writing(series)
22
+ end
23
+
24
+ def execute(*args)
25
+ raw_execute(*args).each_with_object({}) do |block_of_series, result|
26
+ result[block_of_series['name']] = block_of_series['points'].map do |point|
27
+ Hash[block_of_series['columns'].zip(point)]
28
+ end
29
+ end
30
+ end
31
+
32
+ def raw_execute(query_string, time_precision = nil)
33
+ params = { q: query_string }
34
+ params[:time_precision] = time_precision if time_precision
35
+ perform_get(resource_path, params)
36
+ end
37
+
38
+ private
39
+
40
+ def perform_writing(series, time_precision = nil)
41
+ params = { time_precision: time_precision } if time_precision
42
+ perform_post(resource_path, params, series)
43
+ true
44
+ end
45
+
46
+ def writing_block(name, points)
47
+ { name: name, columns: points.first.keys, points: points.map(&:values) }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,12 @@
1
+ module Influxdb
2
+ module Api
3
+ module Namespaces
4
+ class Servers < Base
5
+ resource_path '/cluster/servers'
6
+
7
+ undef_method(:create) if method_defined?(:create)
8
+
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ module Influxdb
2
+ module Api
3
+ module Namespaces
4
+ class ShardSpaces < WithDatabase
5
+ resource_path '/cluster/shard_spaces'
6
+
7
+ def all
8
+ perform_get(resource_path)
9
+ end
10
+
11
+ def create(attributes)
12
+ perform_post(resource_path(database_name), {}, attributes)
13
+ true
14
+ end
15
+
16
+ def update(name, attributes)
17
+ perform_post(resource_path(database_name, name), {}, attributes)
18
+ true
19
+ end
20
+
21
+ def delete(name)
22
+ perform_delete(resource_path(database_name, name))
23
+ true
24
+ end
25
+
26
+ protected
27
+
28
+ def path_prefix
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ module Influxdb
2
+ module Api
3
+ module Namespaces
4
+ class Shards < Base
5
+ resource_path '/cluster/shards'
6
+
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ module Influxdb
2
+ module Api
3
+ module Namespaces
4
+ class Users < WithDatabase
5
+ resource_path '/users'
6
+
7
+ def find(name)
8
+ perform_get(resource_path(name))
9
+ end
10
+
11
+ def update(name, attributes)
12
+ perform_post(resource_path(name), {}, attributes)
13
+ true
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module Influxdb
2
+ module Api
3
+ module Namespaces
4
+ class WithDatabase < Base
5
+ attr_reader :database_name
6
+
7
+ def initialize(client, database_name)
8
+ @client = client
9
+ @database_name = database_name
10
+ end
11
+
12
+ protected
13
+
14
+ def path_prefix
15
+ "/db/#{database_name}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,62 @@
1
+ module Influxdb
2
+ module Api
3
+ class ServerVersion
4
+ include Comparable
5
+
6
+ class Engine
7
+ attr_reader :name, :major, :minor, :patch
8
+
9
+ def initialize(name, major, minor, patch)
10
+ @name, @major, @minor, @patch = name, major, minor, patch
11
+ end
12
+
13
+ def to_s
14
+ "%s: %s.%s.%s" % [name, major, minor, patch]
15
+ end
16
+
17
+ def inspect
18
+ to_s.inspect
19
+ end
20
+ end
21
+
22
+ # InfluxDB v0.7.3 (git: 023abcdef) (leveldb: 1.7)
23
+ REGEXP = /^InfluxDB\sv(\d+)\.(\d+)\.(\d+)\s\(git:\s([0-9abcdef]+)\)\s\((\w+):\s(\d+)\.(\d+)\)$/
24
+
25
+ attr_reader :source, :git, :engine, :major, :minor, :patch
26
+
27
+ def initialize(source)
28
+ @source = source
29
+ parse!
30
+ end
31
+
32
+ def <=>(other)
33
+ other_major, other_minor, other_patch = (other.to_s.split('.', 3) + [0] * 3).first(3).map(&:to_i)
34
+ [major <=> other_major, minor <=> other_minor, patch <=> other_patch].detect{|c| c != 0 } || 0
35
+ end
36
+
37
+ def to_s
38
+ source
39
+ end
40
+
41
+ def inspect
42
+ to_s.inspect
43
+ end
44
+
45
+ private
46
+
47
+ def parse!
48
+ matched = REGEXP.match(source)
49
+
50
+ @major = matched[1].to_i
51
+ @minor = matched[2].to_i
52
+ @patch = matched[3].to_i
53
+ @git = matched[4]
54
+ @engine = build_engine(matched)
55
+ end
56
+
57
+ def build_engine(matched)
58
+ Engine.new(matched[5], matched[6].to_i, matched[7].to_i, matched[8].to_i)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,5 @@
1
+ module Influxdb
2
+ module Api
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Influxdb::Api::Client::ConnectionPool do
4
+ let(:config){ Influxdb::Api::Configuration.new }
5
+
6
+ subject{ Influxdb::Api::Client::ConnectionPool.new(config) }
7
+
8
+ describe '#initialize' do
9
+ before do
10
+ config.hosts = ['localhost', 'influxdb.server.com']
11
+ config.connection_block{|conn| }
12
+ config.connection_options = { key: :value }
13
+ end
14
+
15
+ let(:conn){ double(:conn) }
16
+
17
+ specify do
18
+ expect(::Faraday::Connection).to receive(:new).with(
19
+ 'http://localhost:8086', { key: :value }, &config.connection_block
20
+ ).and_return(conn)
21
+
22
+ expect(Influxdb::Api::Client::Connection).to receive(:new).with('http://localhost:8086', conn, config).
23
+ and_return(conn)
24
+
25
+ expect(::Faraday::Connection).to receive(:new).with(
26
+ 'http://influxdb.server.com:8086', { key: :value }, &config.connection_block
27
+ ).and_return(conn)
28
+
29
+ expect(Influxdb::Api::Client::Connection).to receive(:new).with('http://influxdb.server.com:8086', conn, config).
30
+ and_return(conn)
31
+
32
+ expect(subject.all).to eq([conn, conn])
33
+ end
34
+ end
35
+
36
+ describe '#get_connection' do
37
+ let(:conn){ double(:conn) }
38
+
39
+ before{ allow(config.selector).to receive(:select_from).and_return(conn) }
40
+
41
+ specify{ expect(subject.get_connection).to eq(conn) }
42
+ end
43
+
44
+ describe '#connections' do
45
+ before do
46
+ config.hosts = ['localhost', 'influxdb01.server.com', 'influxdb02.server.com']
47
+ subject.all.first.dead!
48
+ end
49
+
50
+ specify{ expect(subject.connections.size).to eq(2) }
51
+ end
52
+
53
+ describe '#dead' do
54
+ before do
55
+ config.hosts = ['localhost', 'influxdb01.server.com', 'influxdb02.server.com']
56
+ subject.all.first.dead!
57
+ end
58
+
59
+ specify{ expect(subject.dead.size).to eq(1) }
60
+ end
61
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe Influxdb::Api::Client::Connection do
4
+ let(:config){ Influxdb::Api::Configuration.new }
5
+
6
+ subject{ Influxdb::Api::Client::Connection.new('http://localhost:8086', nil, config) }
7
+
8
+ describe '#full_path' do
9
+ let(:path){ '/some/path' }
10
+ let(:params){ { key1: 'value1', key2: 'value2' } }
11
+ let(:result){ '/some/path?key1=value1&key2=value2' }
12
+
13
+ specify{ expect(subject.full_path(path, params)).to eq(result) }
14
+ end
15
+
16
+ describe '#dead?' do
17
+ specify{ expect(subject.dead?).to be_falsy }
18
+
19
+ context do
20
+ before{ subject.dead! }
21
+ specify{ expect(subject.dead?).to be_truthy }
22
+ end
23
+ end
24
+
25
+ describe '#dead!', time_freeze: '16.9.2014 00:00:00 +0700' do
26
+ let(:now){ Time.parse('16.9.2014 00:00:00 +0700') }
27
+
28
+ specify{ expect{ subject.dead! }.to change{ subject.failures }.from(0).to(1) }
29
+ specify{ expect{ subject.dead! }.to change{ subject.dead? }.from(false).to(true) }
30
+ specify{ expect{ subject.dead! }.to change{ subject.dead_since }.from(nil).to(now) }
31
+ specify{ expect(subject.dead!).to eq(subject) }
32
+ end
33
+
34
+ describe '#alive!' do
35
+ before{ subject.dead! }
36
+
37
+ specify{ expect{ subject.alive! }.to change{ subject.dead? }.from(true).to(false) }
38
+ specify{ expect{ subject.alive! }.not_to change{ subject.failures } }
39
+ specify{ expect(subject.alive!).to eq(subject) }
40
+ end
41
+
42
+ describe '#healthy!' do
43
+ before{ subject.dead! }
44
+
45
+ specify{ expect{ subject.healthy! }.to change{ subject.dead? }.from(true).to(false) }
46
+ specify{ expect{ subject.healthy! }.to change{ subject.failures }.from(1).to(0) }
47
+ specify{ expect(subject.healthy!).to eq(subject) }
48
+ end
49
+
50
+ describe '#resurrect!' do
51
+ before{ subject.dead! }
52
+
53
+ context 'when connection is resurrectable' do
54
+ before{ allow(subject).to receive(:resurrectable?).and_return(true) }
55
+
56
+ specify{ expect{ subject.resurrect! }.to change{ subject.dead? }.from(true).to(false) }
57
+ specify{ expect(subject.resurrect!).to eq(subject) }
58
+ end
59
+
60
+ context 'when connection is not resurrectable' do
61
+ before{ allow(subject).to receive(:resurrectable?).and_return(false) }
62
+
63
+ specify{ expect{ subject.resurrect! }.not_to change{ subject.dead? } }
64
+ specify{ expect(subject.resurrect!).to eq(subject) }
65
+ end
66
+ end
67
+
68
+ describe '#resurrectable?' do
69
+
70
+
71
+ end
72
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Influxdb::Api::Client::Response do
4
+ describe '#initialize' do
5
+ let(:iso_8859){ 'Hello Encoding!'.encode(Encoding::ISO_8859_1) }
6
+
7
+ subject{ Influxdb::Api::Client::Response.new(200, iso_8859, {}) }
8
+
9
+ specify{ expect(subject.body.encoding.name).to eq('UTF-8') }
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Influxdb::Api::Client::Selector do
4
+ describe 'Random' do
5
+ subject{ Influxdb::Api::Client::Selector::Random.new }
6
+
7
+ describe '#select_from' do
8
+ specify{ expect(subject.select_from([1, 2, 3])).not_to be_nil }
9
+ end
10
+ end
11
+
12
+ describe 'RoundRobin' do
13
+ subject{ Influxdb::Api::Client::Selector::RoundRobin.new }
14
+
15
+ describe '#select_from' do
16
+ specify do
17
+ expect(subject.select_from([1, 2, 3])).to eq(1)
18
+ expect(subject.select_from([1, 2, 3])).to eq(2)
19
+ expect(subject.select_from([1, 2, 3])).to eq(3)
20
+ expect(subject.select_from([1, 2, 3])).to eq(1)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,110 @@
1
+ require 'spec_helper'
2
+
3
+ describe Influxdb::Api::Client do
4
+ let(:config){ Influxdb::Api::Configuration.new }
5
+ let(:client){ Influxdb::Api::Client.new(config) }
6
+
7
+ describe '#perform_request' do
8
+ let(:pool){ client.send(:connection_pool) }
9
+
10
+ context 'perform GET request without params' do
11
+ before do
12
+ stub_request(:get, 'http://root:root@localhost:8086/path').
13
+ to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' })
14
+ end
15
+
16
+ subject{ client.perform_request('GET', '/path') }
17
+
18
+ specify{ expect(subject.status).to eq(200) }
19
+ specify{ expect(subject.body).to eq({}) }
20
+ specify{ expect(subject.headers).to include('content-type' => 'application/json') }
21
+ end
22
+
23
+ context 'perform GET request with params' do
24
+ before do
25
+ stub_request(:get, 'http://root:root@localhost:8086/path?key1=value1').
26
+ to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' })
27
+ end
28
+
29
+ subject{ client.perform_request('GET', '/path', key1: 'value1') }
30
+
31
+ specify{ expect(subject.status).to eq(200) }
32
+ specify{ expect(subject.body).to eq({}) }
33
+ specify{ expect(subject.headers).to include('content-type' => 'application/json') }
34
+ end
35
+
36
+ context 'perform GET request with HTTP error' do
37
+ before do
38
+ stub_request(:get, 'http://root:root@localhost:8086/path').
39
+ to_return(status: 404, body: '', headers: { 'Content-Type' => 'application/json' })
40
+ end
41
+
42
+ subject{ client.perform_request('GET', '/path') }
43
+
44
+ specify{ expect{ subject }.to raise_error(Influxdb::Api::Client::Errors::NotFound) }
45
+ end
46
+
47
+ context 'perform GET request with transport error if connection pool more then retry number' do
48
+ before do
49
+ config.hosts = ['localhost', 'influxdb1.server.com', 'influxdb2.server.com']
50
+ config.retry_on_failure = 2
51
+
52
+ stub_request(:get, 'http://root:root@localhost:8086/path').
53
+ to_raise(::Faraday::Error::ConnectionFailed)
54
+ stub_request(:get, 'http://root:root@influxdb1.server.com:8086/path').
55
+ to_raise(::Faraday::Error::ConnectionFailed)
56
+ stub_request(:get, 'http://root:root@influxdb2.server.com:8086/path').
57
+ to_raise(::Faraday::Error::ConnectionFailed)
58
+ end
59
+
60
+ subject{ client.perform_request('GET', '/path') }
61
+
62
+ specify do
63
+ expect{ subject }.to raise_error(Faraday::Error::ConnectionFailed)
64
+ expect(pool.alive.size).to eq(1)
65
+ end
66
+ end
67
+
68
+ context 'perform GET request with transport error if connection pool less then retry number' do
69
+ before do
70
+ config.hosts = ['localhost', 'influxdb1.server.com', 'influxdb2.server.com']
71
+ config.retry_on_failure = 4
72
+
73
+ stub_request(:get, 'http://root:root@localhost:8086/path').
74
+ to_raise(::Faraday::Error::ConnectionFailed)
75
+ stub_request(:get, 'http://root:root@influxdb1.server.com:8086/path').
76
+ to_raise(::Faraday::Error::ConnectionFailed)
77
+ stub_request(:get, 'http://root:root@influxdb2.server.com:8086/path').
78
+ to_raise(::Faraday::Error::ConnectionFailed)
79
+ end
80
+
81
+ subject{ client.perform_request('GET', '/path') }
82
+
83
+ specify do
84
+ expect{ subject }.to raise_error(Faraday::Error::ConnectionFailed)
85
+ expect(pool.alive.size).to eq(0)
86
+ end
87
+ end
88
+
89
+ context 'perform GET request with 2 transport errors and success' do
90
+ before do
91
+ config.hosts = ['localhost', 'influxdb1.server.com', 'influxdb2.server.com']
92
+ config.retry_on_failure = 4
93
+
94
+ stub_request(:get, 'http://root:root@localhost:8086/path').
95
+ to_raise(::Faraday::Error::ConnectionFailed)
96
+ stub_request(:get, 'http://root:root@influxdb2.server.com:8086/path').
97
+ to_raise(::Faraday::Error::ConnectionFailed)
98
+ stub_request(:get, 'http://root:root@influxdb1.server.com:8086/path').
99
+ to_return(status: 200, body: '{}', headers: { 'Content-Type' => 'application/json' })
100
+ end
101
+
102
+ subject{ client.perform_request('GET', '/path') }
103
+
104
+ specify{ expect(subject.status).to eq(200) }
105
+ specify{ expect(subject.body).to eq({}) }
106
+ specify{ expect(subject.headers).to include('content-type' => 'application/json') }
107
+ specify{ expect{ subject }.to change{ pool.alive.size }.from(3).to(1) }
108
+ end
109
+ end
110
+ end