efigence-influxdb 0.1.0

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 (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rubocop.yml +41 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +11 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +362 -0
  8. data/Rakefile +22 -0
  9. data/efigence-influxdb.gemspec +28 -0
  10. data/lib/influxdb.rb +21 -0
  11. data/lib/influxdb/client.rb +77 -0
  12. data/lib/influxdb/client/http.rb +98 -0
  13. data/lib/influxdb/config.rb +60 -0
  14. data/lib/influxdb/errors.rb +40 -0
  15. data/lib/influxdb/logging.rb +22 -0
  16. data/lib/influxdb/max_queue.rb +18 -0
  17. data/lib/influxdb/point_value.rb +31 -0
  18. data/lib/influxdb/query/cluster.rb +17 -0
  19. data/lib/influxdb/query/continuous_query.rb +36 -0
  20. data/lib/influxdb/query/core.rb +109 -0
  21. data/lib/influxdb/query/database.rb +21 -0
  22. data/lib/influxdb/query/series.rb +13 -0
  23. data/lib/influxdb/query/shard.rb +14 -0
  24. data/lib/influxdb/query/shard_space.rb +60 -0
  25. data/lib/influxdb/query/user.rb +38 -0
  26. data/lib/influxdb/version.rb +3 -0
  27. data/lib/influxdb/writer/async.rb +115 -0
  28. data/lib/influxdb/writer/udp.rb +21 -0
  29. data/spec/influxdb/cases/async_client_spec.rb +33 -0
  30. data/spec/influxdb/cases/query_cluster_spec.rb +65 -0
  31. data/spec/influxdb/cases/query_database_spec.rb +58 -0
  32. data/spec/influxdb/cases/query_series_spec.rb +50 -0
  33. data/spec/influxdb/cases/query_shard_spec.rb +43 -0
  34. data/spec/influxdb/cases/query_user_spec.rb +127 -0
  35. data/spec/influxdb/cases/querying_spec.rb +159 -0
  36. data/spec/influxdb/cases/retry_requests_spec.rb +97 -0
  37. data/spec/influxdb/cases/udp_client_spec.rb +21 -0
  38. data/spec/influxdb/cases/write_points_spec.rb +141 -0
  39. data/spec/influxdb/client_spec.rb +58 -0
  40. data/spec/influxdb/config_spec.rb +118 -0
  41. data/spec/influxdb/logging_spec.rb +48 -0
  42. data/spec/influxdb/max_queue_spec.rb +29 -0
  43. data/spec/influxdb/point_value_spec.rb +66 -0
  44. data/spec/influxdb/worker_spec.rb +23 -0
  45. data/spec/spec_helper.rb +8 -0
  46. metadata +192 -0
@@ -0,0 +1,22 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ targeted_files = ARGV.drop(1)
4
+ file_pattern = targeted_files.empty? ? 'spec/**/*_spec.rb' : targeted_files
5
+
6
+ require 'rspec/core'
7
+ require 'rspec/core/rake_task'
8
+
9
+ RSpec::Core::RakeTask.new(:spec) do |t|
10
+ t.pattern = FileList[file_pattern]
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+ config.color = true
15
+ config.formatter = :documentation
16
+ end
17
+
18
+ task default: :spec
19
+
20
+ task :console do
21
+ sh 'pry -r ./lib/influxdb.rb'
22
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'influxdb/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "efigence-influxdb"
8
+ spec.version = InfluxDB::VERSION
9
+ spec.authors = ["Jacek Grzybowski", "Todd Persen"]
10
+ spec.email = ["jgrzybowski@efigence.com"]
11
+ spec.description = %q{This is a fork of official Ruby library for InfluxDB meant for v0.9.x support.}
12
+ spec.summary = %q{Ruby library for InfluxDB.}
13
+ spec.homepage = "https://github.com/efigence/influxdb-ruby/tree/support_v0.9.x"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "json"
22
+ spec.add_runtime_dependency "cause"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec", "~> 3.0.0"
27
+ spec.add_development_dependency "webmock"
28
+ end
@@ -0,0 +1,21 @@
1
+ require "influxdb/version"
2
+ require "influxdb/errors"
3
+ require "influxdb/logging"
4
+ require "influxdb/max_queue"
5
+ require "influxdb/point_value"
6
+ require "influxdb/config"
7
+
8
+ require "influxdb/writer/async"
9
+ require "influxdb/writer/udp"
10
+
11
+ require "influxdb/query/core"
12
+ require "influxdb/query/cluster"
13
+ require "influxdb/query/database"
14
+ require "influxdb/query/series"
15
+ require "influxdb/query/shard"
16
+ require "influxdb/query/user"
17
+ require "influxdb/query/continuous_query"
18
+ require "influxdb/query/shard_space"
19
+
20
+ require "influxdb/client/http"
21
+ require "influxdb/client"
@@ -0,0 +1,77 @@
1
+ require 'json'
2
+ require 'cause'
3
+
4
+ module InfluxDB
5
+ # rubocop:disable Metrics/MethodLength
6
+ # rubocop:disable Metrics/AbcSize
7
+
8
+ # InfluxDB client class
9
+ class Client
10
+ attr_reader :config, :writer
11
+
12
+ include InfluxDB::Logging
13
+ include InfluxDB::HTTP
14
+ include InfluxDB::Query::Core
15
+ include InfluxDB::Query::Cluster
16
+ include InfluxDB::Query::Database
17
+ include InfluxDB::Query::Shard
18
+ include InfluxDB::Query::Series
19
+ include InfluxDB::Query::User
20
+ include InfluxDB::Query::ContinuousQuery
21
+ include InfluxDB::Query::ShardSpace
22
+
23
+ # Initializes a new InfluxDB client
24
+ #
25
+ # === Examples:
26
+ #
27
+ # # connect to localhost using root/root
28
+ # # as the credentials and doesn't connect to a db
29
+ #
30
+ # InfluxDB::Client.new
31
+ #
32
+ # # connect to localhost using root/root
33
+ # # as the credentials and 'db' as the db name
34
+ #
35
+ # InfluxDB::Client.new 'db'
36
+ #
37
+ # # override username, other defaults remain unchanged
38
+ #
39
+ # InfluxDB::Client.new username: 'username'
40
+ #
41
+ # # override username, use 'db' as the db name
42
+ # Influxdb::Client.new 'db', username: 'username'
43
+ #
44
+ # === Valid options in hash
45
+ #
46
+ # +:host+:: the hostname to connect to
47
+ # +:port+:: the port to connect to
48
+ # +:path+:: the specified path prefix when building the url e.g.: /prefix/db/dbname...
49
+ # +:username+:: the username to use when executing commands
50
+ # +:password+:: the password associated with the username
51
+ # +:use_ssl+:: use ssl to connect
52
+ def initialize(*args)
53
+ opts = args.last.is_a?(Hash) ? args.last : {}
54
+ opts[:database] = args.first if args.first.is_a? String
55
+ @config = InfluxDB::Config.new(opts)
56
+ @stopped = false
57
+
58
+ @writer = self
59
+
60
+ if config.async?
61
+ @writer = InfluxDB::Writer::Async.new(self, config.async)
62
+ elsif config.udp?
63
+ @writer = InfluxDB::Writer::UDP.new(self, config.udp)
64
+ end
65
+
66
+ at_exit { stop! } if config.retry > 0
67
+ end
68
+
69
+ def stop!
70
+ @stopped = true
71
+ end
72
+
73
+ def stopped?
74
+ @stopped
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,98 @@
1
+ require 'uri'
2
+ require 'cgi'
3
+ require 'net/http'
4
+ require 'net/https'
5
+
6
+ module InfluxDB
7
+ # rubocop:disable Metrics/MethodLength
8
+ # rubocop:disable Metrics/AbcSize
9
+ module HTTP # :nodoc:
10
+ def get(url, options = {})
11
+ connect_with_retry do |http|
12
+ response = do_request http, Net::HTTP::Get.new(url)
13
+ if response.is_a? Net::HTTPSuccess
14
+ handle_successful_response(response, options)
15
+ elsif response.is_a? Net::HTTPUnauthorized
16
+ fail InfluxDB::AuthenticationError, response.body
17
+ else
18
+ resolve_error(response.body)
19
+ end
20
+ end
21
+ end
22
+
23
+ def post(url, data)
24
+ headers = {"Content-Type" => "application/octet-stream"}
25
+ connect_with_retry do |http|
26
+ response = do_request http, Net::HTTP::Post.new(url, headers), data
27
+ if response.is_a? Net::HTTPSuccess
28
+ return response
29
+ elsif response.is_a? Net::HTTPUnauthorized
30
+ fail InfluxDB::AuthenticationError, response.body
31
+ else
32
+ resolve_error(response.body)
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def connect_with_retry(&block)
40
+ hosts = config.hosts.dup
41
+ delay = config.initial_delay
42
+ retry_count = 0
43
+
44
+ begin
45
+ hosts.push(host = hosts.shift)
46
+ http = Net::HTTP.new(host, config.port)
47
+ http.open_timeout = config.open_timeout
48
+ http.read_timeout = config.read_timeout
49
+ http.use_ssl = config.use_ssl
50
+ block.call(http)
51
+
52
+ rescue Timeout::Error, *InfluxDB::NET_HTTP_EXCEPTIONS => e
53
+ retry_count += 1
54
+ if (config.retry == -1 || retry_count <= config.retry) && !stopped?
55
+ log :error, "Failed to contact host #{host}: #{e.inspect} - retrying in #{delay}s."
56
+ sleep delay
57
+ delay = [config.max_delay, delay * 2].min
58
+ retry
59
+ else
60
+ raise e, "Tried #{retry_count - 1} times to reconnect but failed."
61
+ end
62
+ ensure
63
+ http.finish if http.started?
64
+ end
65
+ end
66
+
67
+ def do_request(http, req, data = nil)
68
+ req.basic_auth config.username, config.password if basic_auth?
69
+ req.body = data if data
70
+ http.request(req)
71
+ end
72
+
73
+ def basic_auth?
74
+ config.auth_method == 'basic_auth'
75
+ end
76
+
77
+ def resolve_error(response)
78
+ if response =~ /Couldn\'t find series/
79
+ fail InfluxDB::SeriesNotFound, response
80
+ else
81
+ fail InfluxDB::Error, response
82
+ end
83
+ end
84
+
85
+ def handle_successful_response(response, options)
86
+ parsed_response = JSON.parse(response.body) if response.body
87
+ errors = errors_from_response(parsed_response) if parsed_response
88
+ raise InfluxDB::QueryError.new errors if errors
89
+ options.fetch(:parse, false) ? parsed_response : response
90
+ end
91
+
92
+ def errors_from_response(parsed_resp)
93
+ parsed_resp.is_a?(Hash) && parsed_resp.fetch('results', [])
94
+ .fetch(0, {})
95
+ .fetch('error', nil)
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,60 @@
1
+ module InfluxDB
2
+ # InfluxDB client configuration
3
+ class Config
4
+ AUTH_METHODS = %w(params basic_auth)
5
+
6
+ attr_accessor :hosts,
7
+ :port,
8
+ :username,
9
+ :password,
10
+ :database,
11
+ :time_precision,
12
+ :use_ssl,
13
+ :auth_method,
14
+ :initial_delay,
15
+ :max_delay,
16
+ :open_timeout,
17
+ :read_timeout,
18
+ :retry,
19
+ :denormalize
20
+
21
+ attr_reader :async, :udp
22
+
23
+ # rubocop:disable all
24
+ def initialize(opts = {})
25
+ @database = opts[:database]
26
+ @hosts = Array(opts[:hosts] || opts[:host] || ["localhost"])
27
+ @port = opts.fetch(:port, 8086)
28
+ @username = opts.fetch(:username, "root")
29
+ @password = opts.fetch(:password, "root")
30
+ @auth_method = AUTH_METHODS.include?(opts[:auth_method]) ? opts[:auth_method] : "params"
31
+ @use_ssl = opts.fetch(:use_ssl, false)
32
+ @time_precision = opts.fetch(:time_precision, "s")
33
+ @initial_delay = opts.fetch(:initial_delay, 0.01)
34
+ @max_delay = opts.fetch(:max_delay, 30)
35
+ @open_timeout = opts.fetch(:write_timeout, 5)
36
+ @read_timeout = opts.fetch(:read_timeout, 300)
37
+ @async = opts.fetch(:async, false)
38
+ @udp = opts.fetch(:udp, false)
39
+ @retry = opts.fetch(:retry, nil)
40
+ @denormalize = opts.fetch(:denormalize, true)
41
+ @retry =
42
+ case @retry
43
+ when Integer
44
+ @retry
45
+ when true, nil
46
+ -1
47
+ when false
48
+ 0
49
+ end
50
+ end
51
+
52
+ def udp?
53
+ !!udp
54
+ end
55
+
56
+ def async?
57
+ !!async
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,40 @@
1
+ require "net/http"
2
+ require "zlib"
3
+
4
+ module InfluxDB # :nodoc:
5
+ class Error < StandardError
6
+ end
7
+
8
+ class AuthenticationError < Error
9
+ end
10
+
11
+ class ConnectionError < Error
12
+ end
13
+
14
+ class SeriesNotFound < Error
15
+ end
16
+
17
+ class JSONParserError < Error
18
+ end
19
+
20
+ class QueryError < Error
21
+ end
22
+
23
+ # Taken from: https://github.com/lostisland/faraday/blob/master/lib/faraday/adapter/net_http.rb
24
+ NET_HTTP_EXCEPTIONS = [
25
+ EOFError,
26
+ Errno::ECONNABORTED,
27
+ Errno::ECONNREFUSED,
28
+ Errno::ECONNRESET,
29
+ Errno::EHOSTUNREACH,
30
+ Errno::EINVAL,
31
+ Errno::ENETUNREACH,
32
+ Net::HTTPBadResponse,
33
+ Net::HTTPHeaderSyntaxError,
34
+ Net::ProtocolError,
35
+ SocketError,
36
+ Zlib::GzipFile::Error
37
+ ]
38
+
39
+ NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL)
40
+ end
@@ -0,0 +1,22 @@
1
+ require 'logger'
2
+
3
+ module InfluxDB
4
+ module Logging # :nodoc:
5
+ PREFIX = "[InfluxDB] "
6
+
7
+ class << self
8
+ attr_writer :logger
9
+ end
10
+
11
+ def self.logger
12
+ return false if @logger == false
13
+ @logger ||= ::Logger.new(STDERR).tap { |logger| logger.level = Logger::INFO }
14
+ end
15
+
16
+ private
17
+
18
+ def log(level, message)
19
+ InfluxDB::Logging.logger.send(level.to_sym, PREFIX + message) if InfluxDB::Logging.logger
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ require "thread"
2
+
3
+ module InfluxDB
4
+ # Queue with max length limit
5
+ class MaxQueue < Queue
6
+ attr_reader :max
7
+
8
+ def initialize(max = 10_000)
9
+ fail ArgumentError, "queue size must be positive" unless max > 0
10
+ @max = max
11
+ super()
12
+ end
13
+
14
+ def push(obj)
15
+ super if length < @max
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ module InfluxDB
2
+ class PointValue
3
+ attr_reader :series, :values, :tags, :timestamp
4
+
5
+ def initialize(data)
6
+ @series = data[:series].gsub(/\s/, '\ ')
7
+ @values = stringify(data[:values])
8
+ @tags = stringify(data[:tags])
9
+ @timestamp = data[:timestamp]
10
+ end
11
+
12
+ def dump
13
+ dump = "#{@series}"
14
+ dump << ",#{@tags}" if @tags
15
+ dump << " #{@values}"
16
+ dump << " #{@timestamp}" if @timestamp
17
+ dump
18
+ end
19
+
20
+ private
21
+
22
+ def stringify(hash)
23
+ return nil unless hash && !hash.empty?
24
+ hash.map do |k,v|
25
+ key = k.to_s.gsub(/\s/, '\ ')
26
+ val = v.is_a?(String) ? v.gsub(/\s/, '\ ') : v
27
+ "#{key}=#{val}"
28
+ end.join(',')
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ module InfluxDB
2
+ module Query
3
+ module Cluster # :nodoc:
4
+ def create_cluster_admin(username, password)
5
+ execute("CREATE USER #{username} WITH PASSWORD '#{password}' WITH ALL PRIVILEGES")
6
+ end
7
+
8
+ def list_cluster_admins
9
+ list_users.select{|u| u['admin']}.map {|u| u.delete_if {|k,_| k == 'admin'}}
10
+ end
11
+
12
+ def revoke_cluster_admin_privileges(username)
13
+ execute("REVOKE ALL PRIVILEGES FROM #{username}")
14
+ end
15
+ end
16
+ end
17
+ end