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,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,15 @@
1
+ class Array
2
+ class << self
3
+ unless method_defined?(:wrap)
4
+ def wrap(object)
5
+ if object.nil?
6
+ []
7
+ elsif object.respond_to?(:to_ary)
8
+ object.to_ary || [object]
9
+ else
10
+ [object]
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -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