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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +208 -0
- data/Rakefile +6 -0
- data/influxdb-api.gemspec +27 -0
- data/lib/influxdb-api.rb +2 -0
- data/lib/influxdb.rb +4 -0
- data/lib/influxdb/api.rb +38 -0
- data/lib/influxdb/api/client.rb +99 -0
- data/lib/influxdb/api/client/connection.rb +55 -0
- data/lib/influxdb/api/client/connection_pool.rb +52 -0
- data/lib/influxdb/api/client/errors.rb +62 -0
- data/lib/influxdb/api/client/response.rb +14 -0
- data/lib/influxdb/api/client/selector.rb +27 -0
- data/lib/influxdb/api/configuration.rb +116 -0
- data/lib/influxdb/api/database.rb +29 -0
- data/lib/influxdb/api/extensions.rb +15 -0
- data/lib/influxdb/api/namespaces.rb +50 -0
- data/lib/influxdb/api/namespaces/base.rb +61 -0
- data/lib/influxdb/api/namespaces/cluster_admins.rb +14 -0
- data/lib/influxdb/api/namespaces/continuous_queries.rb +13 -0
- data/lib/influxdb/api/namespaces/databases.rb +13 -0
- data/lib/influxdb/api/namespaces/series.rb +52 -0
- data/lib/influxdb/api/namespaces/servers.rb +12 -0
- data/lib/influxdb/api/namespaces/shard_spaces.rb +33 -0
- data/lib/influxdb/api/namespaces/shards.rb +10 -0
- data/lib/influxdb/api/namespaces/users.rb +18 -0
- data/lib/influxdb/api/namespaces/with_database.rb +20 -0
- data/lib/influxdb/api/server_version.rb +62 -0
- data/lib/influxdb/api/version.rb +5 -0
- data/spec/lib/influxdb/api/client/connection_pool_spec.rb +61 -0
- data/spec/lib/influxdb/api/client/connection_spec.rb +72 -0
- data/spec/lib/influxdb/api/client/response_spec.rb +11 -0
- data/spec/lib/influxdb/api/client/selector_spec.rb +24 -0
- data/spec/lib/influxdb/api/client_spec.rb +110 -0
- data/spec/lib/influxdb/api/configuration_spec.rb +54 -0
- data/spec/lib/influxdb/api/database_spec.rb +32 -0
- data/spec/lib/influxdb/api/namespaces/cluster_admins_spec.rb +46 -0
- data/spec/lib/influxdb/api/namespaces/continuous_queries_spec.rb +42 -0
- data/spec/lib/influxdb/api/namespaces/databases_spec.rb +36 -0
- data/spec/lib/influxdb/api/namespaces/series_spec.rb +119 -0
- data/spec/lib/influxdb/api/namespaces/servers_spec.rb +30 -0
- data/spec/lib/influxdb/api/namespaces/shard_spaces_spec.rb +55 -0
- data/spec/lib/influxdb/api/namespaces/shards_spec.rb +52 -0
- data/spec/lib/influxdb/api/namespaces/users_spec.rb +55 -0
- data/spec/lib/influxdb/api/namespaces_spec.rb +65 -0
- data/spec/lib/influxdb/api/server_version_spec.rb +51 -0
- data/spec/spec_helper.rb +28 -0
- metadata +183 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
module Influxdb
|
2
|
+
module Api
|
3
|
+
class Client
|
4
|
+
class Connection
|
5
|
+
attr_reader :host, :connection, :failures, :dead_since, :config
|
6
|
+
|
7
|
+
def initialize(host, connection, config = Influxdb::Api.config)
|
8
|
+
@config = config
|
9
|
+
@host = host
|
10
|
+
@connection = connection
|
11
|
+
@failures = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def full_path(path, params={})
|
15
|
+
path + (params.empty? ? '' : "?#{::Faraday::Utils::ParamsHash[params].to_query}")
|
16
|
+
end
|
17
|
+
|
18
|
+
def dead?
|
19
|
+
!!@dead
|
20
|
+
end
|
21
|
+
|
22
|
+
def dead!
|
23
|
+
@dead = true
|
24
|
+
@failures += 1
|
25
|
+
@dead_since = Time.now
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def alive!
|
30
|
+
@dead = false
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def healthy!
|
35
|
+
@dead = false
|
36
|
+
@failures = 0
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def resurrect!
|
41
|
+
alive! if resurrectable?
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def resurrectable?
|
46
|
+
Time.now > dead_since + (config.resurrect_timeout * 2 ** (failures - 1))
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
"<#{self.class.name} host: #{host} (#{dead? ? 'dead since ' + dead_since.to_s : 'alive'})>"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Influxdb
|
2
|
+
module Api
|
3
|
+
class Client
|
4
|
+
class ConnectionPool
|
5
|
+
attr_reader :config
|
6
|
+
|
7
|
+
def initialize(config = Influxdb::Api.config)
|
8
|
+
@config = config
|
9
|
+
@connections = build_connections
|
10
|
+
end
|
11
|
+
|
12
|
+
def connections
|
13
|
+
@connections.reject{|c| c.dead? }
|
14
|
+
end
|
15
|
+
alias :alive :connections
|
16
|
+
|
17
|
+
def dead
|
18
|
+
@connections.select{|c| c.dead? }
|
19
|
+
end
|
20
|
+
|
21
|
+
def all
|
22
|
+
@connections
|
23
|
+
end
|
24
|
+
|
25
|
+
def each(&block)
|
26
|
+
connections.each(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_connection
|
30
|
+
if connections.empty? && dead_connection = dead.sort{|a, b| a.failures <=> b.failures }.first
|
31
|
+
dead_connection.alive!
|
32
|
+
end
|
33
|
+
config.selector.select_from(alive)
|
34
|
+
end
|
35
|
+
|
36
|
+
def build_connections
|
37
|
+
config.hosts.map do |host|
|
38
|
+
Connection.new(
|
39
|
+
host,
|
40
|
+
::Faraday::Connection.new(
|
41
|
+
host,
|
42
|
+
config.connection_options,
|
43
|
+
&config.connection_block
|
44
|
+
),
|
45
|
+
config
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Influxdb
|
2
|
+
module Api
|
3
|
+
class Client
|
4
|
+
class Error < StandardError; end
|
5
|
+
class ServerError < StandardError; end
|
6
|
+
|
7
|
+
module Errors; end
|
8
|
+
|
9
|
+
HTTP_STATUSES = {
|
10
|
+
300 => 'MultipleChoices',
|
11
|
+
301 => 'MovedPermanently',
|
12
|
+
302 => 'Found',
|
13
|
+
303 => 'SeeOther',
|
14
|
+
304 => 'NotModified',
|
15
|
+
305 => 'UseProxy',
|
16
|
+
307 => 'TemporaryRedirect',
|
17
|
+
308 => 'PermanentRedirect',
|
18
|
+
|
19
|
+
400 => 'BadRequest',
|
20
|
+
401 => 'Unauthorized',
|
21
|
+
402 => 'PaymentRequired',
|
22
|
+
403 => 'Forbidden',
|
23
|
+
404 => 'NotFound',
|
24
|
+
405 => 'MethodNotAllowed',
|
25
|
+
406 => 'NotAcceptable',
|
26
|
+
407 => 'ProxyAuthenticationRequired',
|
27
|
+
408 => 'RequestTimeout',
|
28
|
+
409 => 'Conflict',
|
29
|
+
410 => 'Gone',
|
30
|
+
411 => 'LengthRequired',
|
31
|
+
412 => 'PreconditionFailed',
|
32
|
+
413 => 'RequestEntityTooLarge',
|
33
|
+
414 => 'RequestURITooLong',
|
34
|
+
415 => 'UnsupportedMediaType',
|
35
|
+
416 => 'RequestedRangeNotSatisfiable',
|
36
|
+
417 => 'ExpectationFailed',
|
37
|
+
418 => 'ImATeapot',
|
38
|
+
421 => 'TooManyConnectionsFromThisIP',
|
39
|
+
426 => 'UpgradeRequired',
|
40
|
+
450 => 'BlockedByWindowsParentalControls',
|
41
|
+
494 => 'RequestHeaderTooLarge',
|
42
|
+
497 => 'HTTPToHTTPS',
|
43
|
+
499 => 'ClientClosedRequest',
|
44
|
+
|
45
|
+
500 => 'InternalServerError',
|
46
|
+
501 => 'NotImplemented',
|
47
|
+
502 => 'BadGateway',
|
48
|
+
503 => 'ServiceUnavailable',
|
49
|
+
504 => 'GatewayTimeout',
|
50
|
+
505 => 'HTTPVersionNotSupported',
|
51
|
+
506 => 'VariantAlsoNegotiates',
|
52
|
+
510 => 'NotExtended'
|
53
|
+
}
|
54
|
+
|
55
|
+
ERRORS = HTTP_STATUSES.inject({}) do |sum, error|
|
56
|
+
status, name = error
|
57
|
+
sum[status] = Errors.const_set(name, Class.new(ServerError))
|
58
|
+
sum
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Influxdb
|
2
|
+
module Api
|
3
|
+
class Client
|
4
|
+
class Response
|
5
|
+
attr_reader :status, :body, :headers
|
6
|
+
|
7
|
+
def initialize(status, body, headers = {})
|
8
|
+
@status, @body, @headers = status, body, headers
|
9
|
+
@body = body.force_encoding('UTF-8') if body.respond_to?(:force_encoding)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Influxdb
|
2
|
+
module Api
|
3
|
+
class Client
|
4
|
+
module Selector
|
5
|
+
class Base
|
6
|
+
def select_from
|
7
|
+
raise NoMethodError, "Implement this method in the selector implementation."
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Random < Base
|
12
|
+
def select_from(connections)
|
13
|
+
connections.to_a.sample
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class RoundRobin < Base
|
18
|
+
def select_from(connections)
|
19
|
+
@current = @current.nil? ? 0 : @current + 1
|
20
|
+
@current = 0 if @current >= connections.size
|
21
|
+
connections[@current]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Influxdb
|
2
|
+
module Api
|
3
|
+
class Configuration
|
4
|
+
attr_accessor :resurrect_timeout, :retry_on_failure, :connection_options,
|
5
|
+
:serializer, :log, :selector, :user, :password
|
6
|
+
|
7
|
+
attr_writer :logger
|
8
|
+
attr_reader :hosts
|
9
|
+
|
10
|
+
class Host
|
11
|
+
DEFAULT = ['http://localhost:8086'].freeze
|
12
|
+
DEFAULT_PORT = 8086
|
13
|
+
DEFAULT_PROTOCOL = 'http'.freeze
|
14
|
+
|
15
|
+
def initialize(source)
|
16
|
+
@source = to_hash(source)
|
17
|
+
end
|
18
|
+
|
19
|
+
def build
|
20
|
+
result = "#{protocol}://"
|
21
|
+
result << "#{user}:#{password}@" if user
|
22
|
+
result << "#{host}:#{port}"
|
23
|
+
result << path if path
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :source
|
30
|
+
|
31
|
+
def to_hash(source)
|
32
|
+
case source
|
33
|
+
when String
|
34
|
+
source =~ /^[a-z]+\:\/\// ? to_hash(URI.parse(source)) : Hash[[:host, :port].zip(source.split(?:))]
|
35
|
+
when URI
|
36
|
+
{
|
37
|
+
scheme: source.scheme,
|
38
|
+
user: source.user,
|
39
|
+
password: source.password,
|
40
|
+
host: source.host,
|
41
|
+
path: source.path,
|
42
|
+
port: source.port.to_s
|
43
|
+
}
|
44
|
+
when Hash
|
45
|
+
source
|
46
|
+
else
|
47
|
+
raise ArgumentError, "Please pass host as a String, URI or Hash -- #{source.class} given."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def protocol
|
52
|
+
source[:scheme] || DEFAULT_PROTOCOL
|
53
|
+
end
|
54
|
+
|
55
|
+
def port
|
56
|
+
source[:port] || DEFAULT_PORT
|
57
|
+
end
|
58
|
+
|
59
|
+
[:host, :user, :password, :path].each do |method|
|
60
|
+
define_method(method) do
|
61
|
+
source[method]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
DEFAULT_LOGGER = ->{
|
67
|
+
require 'logger'
|
68
|
+
logger = Logger.new(STDERR)
|
69
|
+
logger.progname = 'influxdb'
|
70
|
+
logger.formatter = ->(severity, datetime, progname, msg){ "#{datetime}: #{msg}\n" }
|
71
|
+
logger
|
72
|
+
}
|
73
|
+
|
74
|
+
def initialize
|
75
|
+
@log = false
|
76
|
+
@user = 'root'
|
77
|
+
@password = 'root'
|
78
|
+
@serializer = MultiJson
|
79
|
+
@connection_options = {}
|
80
|
+
@connection_block = nil
|
81
|
+
@retry_on_failure = false
|
82
|
+
@resurrect_timeout = 60
|
83
|
+
@hosts = Host::DEFAULT
|
84
|
+
@selector = Client::Selector::RoundRobin.new
|
85
|
+
end
|
86
|
+
|
87
|
+
def logger
|
88
|
+
@logger ||= log ? DEFAULT_LOGGER.call : nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def hosts=(value)
|
92
|
+
@hosts = Array.wrap(value).map(&method(:normalize_host))
|
93
|
+
end
|
94
|
+
|
95
|
+
def connection_block(&block)
|
96
|
+
if block_given?
|
97
|
+
@connection_block = block
|
98
|
+
else
|
99
|
+
@connection_block
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def dup
|
104
|
+
clone = super
|
105
|
+
clone.connection_options = connection_options.dup
|
106
|
+
clone
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def normalize_host(host)
|
112
|
+
Host.new(host).build
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Influxdb
|
2
|
+
module Api
|
3
|
+
class Database
|
4
|
+
attr_reader :client, :name
|
5
|
+
|
6
|
+
def initialize(client, name)
|
7
|
+
@client = client
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def series
|
12
|
+
@series ||= Namespaces::Series.new(client, name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def users
|
16
|
+
@users ||= Namespaces::Users.new(client, name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def continuous_queries
|
20
|
+
@continuous_queries ||= Namespaces::ContinuousQueries.new(client, name)
|
21
|
+
end
|
22
|
+
|
23
|
+
def shard_spaces
|
24
|
+
@shard_spaces ||= Namespaces::ShardSpaces.new(client, name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative 'namespaces/base'
|
2
|
+
require_relative 'namespaces/with_database'
|
3
|
+
require_relative 'namespaces/databases'
|
4
|
+
require_relative 'namespaces/series'
|
5
|
+
require_relative 'namespaces/users'
|
6
|
+
require_relative 'namespaces/continuous_queries'
|
7
|
+
require_relative 'namespaces/shard_spaces'
|
8
|
+
require_relative 'namespaces/cluster_admins'
|
9
|
+
require_relative 'namespaces/servers'
|
10
|
+
require_relative 'namespaces/shards'
|
11
|
+
|
12
|
+
module Influxdb
|
13
|
+
module Api
|
14
|
+
module Namespaces
|
15
|
+
def databases(name = nil)
|
16
|
+
if name
|
17
|
+
Database.new(self, name)
|
18
|
+
else
|
19
|
+
@databases ||= Databases.new(self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
alias_method :dbs, :databases
|
23
|
+
|
24
|
+
def cluster_admins
|
25
|
+
@cluster_admins ||= ClusterAdmins.new(self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def servers
|
29
|
+
@servers ||= Servers.new(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
def shards
|
33
|
+
@shards ||= Shards.new(self)
|
34
|
+
end
|
35
|
+
|
36
|
+
def version
|
37
|
+
ServerVersion.new(perform_request('get', '/ping').headers['x-influxdb-version'])
|
38
|
+
end
|
39
|
+
|
40
|
+
def sync?
|
41
|
+
config.serializer.load(perform_request('get', '/sync').body)
|
42
|
+
end
|
43
|
+
alias_method :cluster_synchronized?, :sync?
|
44
|
+
|
45
|
+
def interfaces
|
46
|
+
perform_request('get', '/interfaces').body
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Influxdb
|
2
|
+
module Api
|
3
|
+
module Namespaces
|
4
|
+
class Base
|
5
|
+
attr_reader :client
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :_resource_path
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def resource_path(value)
|
13
|
+
self._resource_path = value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(client)
|
18
|
+
@client = client
|
19
|
+
end
|
20
|
+
|
21
|
+
def all
|
22
|
+
perform_get(resource_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def create(attributes)
|
26
|
+
perform_post(resource_path, {}, attributes)
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete(name)
|
31
|
+
perform_delete(resource_path(name))
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def resource_path(*args)
|
38
|
+
[path_prefix, self.class._resource_path, path_postfix, args].compact.flatten.join(?/).squeeze(?/)
|
39
|
+
end
|
40
|
+
|
41
|
+
def path_prefix
|
42
|
+
end
|
43
|
+
|
44
|
+
def path_postfix
|
45
|
+
end
|
46
|
+
|
47
|
+
def perform_get(path, params = {})
|
48
|
+
client.perform_request('get', path, params || {}).body
|
49
|
+
end
|
50
|
+
|
51
|
+
def perform_post(path, params, body)
|
52
|
+
client.perform_request('post', path, params || {}, body).body
|
53
|
+
end
|
54
|
+
|
55
|
+
def perform_delete(path, params = {}, body = nil)
|
56
|
+
client.perform_request('delete', path, params || {}, body).body
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|