influxdb-api 0.0.1

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.
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