influxdb 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 760707db17ad8199b2159d6e1e0f28e420f60f2f
4
- data.tar.gz: 1fb214ed28fe1978c7fc7a0b4a1aa9b2d6572988
3
+ metadata.gz: 79714dbe624e4485dfefc69e334b3234daf4c5c6
4
+ data.tar.gz: 8a36045de479f5406d65b332c191edb3cfe327bb
5
5
  SHA512:
6
- metadata.gz: 7348efe2e8cf889cfcb6f4fa5557eb217438abb7c87a43813b3da1290412107f7b73f0959cd7a590172ed0158ef9663716c3ac1de733dcb6e26038e90ddbacfd
7
- data.tar.gz: 4fbceddc8faa2e02bc7ef272c17c63d4fc81f73c9f964f51003cbd96769786d84d07369d52617737c5618f15a66fa67eaf55aaf9cbf76beb54ec8921a4373935
6
+ metadata.gz: f29ae07c03cd04314bfc82af8510334c7a6e829a33ef1eaa1a7d8a5be802bf53883bab852075d64b3fd142b319e7ea9ecfb5a4f447671bd5629af0be4087dda3
7
+ data.tar.gz: 478b0d0002b481bed502b6682f795bad4a61f69eaa4c459dacc9f8050023ba1ed78d15bd27187914d0c6b6d2ea0b6e6989945f5b399f97c0ffefb0596e2d9514
data/.gitignore CHANGED
@@ -12,6 +12,8 @@ test/tmp
12
12
  test/version_tmp
13
13
  tmp
14
14
  Gemfile.lock
15
+ .rvmrc
16
+ .ruby-version
15
17
 
16
18
  # YARD artifacts
17
19
  .yardoc
data/.travis.yml CHANGED
@@ -2,9 +2,9 @@ language: ruby
2
2
  before_install:
3
3
  - gem update bundler
4
4
  rvm:
5
- - 1.8.7-p374
6
- - ree
7
5
  - 1.9.3
8
6
  - 2.0.0
7
+ - 2.1.0
8
+ - 2.2.0
9
9
  - ruby-head
10
10
  - jruby
data/README.md CHANGED
@@ -1,6 +1,14 @@
1
1
  influxdb-ruby
2
2
  =============
3
3
 
4
+ # This library is not updated for InfluxDB 0.9.0. There are breaking changes to the API, use at your own risk.
5
+
6
+ We encourage you to submit a pull request if you have a contribution.
7
+
8
+ ----------
9
+
10
+
11
+
4
12
  [![Build Status](https://travis-ci.org/influxdb/influxdb-ruby.png?branch=master)](https://travis-ci.org/influxdb/influxdb-ruby)
5
13
 
6
14
  Ruby client library for [InfluxDB](http://influxdb.org/).
@@ -23,6 +31,8 @@ Connecting to a single host:
23
31
  require 'influxdb'
24
32
 
25
33
  influxdb = InfluxDB::Client.new host: "influxdb.domain.com"
34
+ # or
35
+ influxdb = InfluxDB::Client.new # no host given defaults connecting to localhost
26
36
  ```
27
37
 
28
38
  Connecting to multiple hosts (with built-in load balancing and failover):
@@ -40,6 +50,8 @@ require 'influxdb'
40
50
 
41
51
  influxdb = InfluxDB::Client.new
42
52
 
53
+ database = 'site_development'
54
+
43
55
  influxdb.create_database(database)
44
56
  ```
45
57
 
@@ -137,6 +149,25 @@ data = {
137
149
  influxdb.write_point(name, data, false, time_precision)
138
150
  ```
139
151
 
152
+ Write data via UDP:
153
+
154
+ ``` ruby
155
+ require 'influxdb'
156
+ host = '127.0.0.1'
157
+ port = 4444
158
+
159
+ influxdb = InfluxDB::Client.new :udp => { :host => host, :port => port }
160
+
161
+ name = 'hitchhiker'
162
+
163
+ data = {
164
+ :answer => 42,
165
+ :question => "life the universe and everything?"
166
+ }
167
+
168
+ influxdb.write_point(name, data)
169
+ ```
170
+
140
171
 
141
172
  List cluster admins:
142
173
 
data/influxdb.gemspec CHANGED
@@ -19,9 +19,10 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_runtime_dependency "json"
22
+ spec.add_runtime_dependency "cause"
22
23
 
23
24
  spec.add_development_dependency "bundler", "~> 1.3"
24
25
  spec.add_development_dependency "rake"
25
- spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "rspec", "~> 3.0.0"
26
27
  spec.add_development_dependency "webmock"
27
28
  end
data/lib/influxdb.rb CHANGED
@@ -5,4 +5,5 @@ require "influxdb/logger"
5
5
  require "influxdb/max_queue"
6
6
  require "influxdb/worker"
7
7
  require "influxdb/client"
8
+ require "influxdb/udp_client"
8
9
  require "influxdb/point_value"
@@ -3,6 +3,7 @@ require 'cgi'
3
3
  require 'net/http'
4
4
  require 'net/https'
5
5
  require 'json'
6
+ require 'cause'
6
7
 
7
8
  module InfluxDB
8
9
  class Client
@@ -12,10 +13,13 @@ module InfluxDB
12
13
  :password,
13
14
  :database,
14
15
  :time_precision,
16
+ :auth_method,
15
17
  :use_ssl,
18
+ :verify_ssl,
19
+ :ssl_ca_cert,
16
20
  :stopped
17
21
 
18
- attr_accessor :queue, :worker
22
+ attr_accessor :queue, :worker, :udp_client
19
23
 
20
24
  include InfluxDB::Logging
21
25
 
@@ -33,21 +37,30 @@ module InfluxDB
33
37
  #
34
38
  # Influxdb::Client.new 'db', :username => 'username' # override username, use 'db' as the db name
35
39
  #
40
+ # Influxdb::Client.new 'db', :path => '/prefix' # use the specified path prefix when building the
41
+ # # url e.g.: /prefix/db/dbname...
42
+ #
36
43
  # === Valid options in hash
37
44
  #
38
45
  # +:host+:: the hostname to connect to
39
46
  # +:port+:: the port to connect to
40
47
  # +:username+:: the username to use when executing commands
41
48
  # +:password+:: the password associated with the username
42
- # +:use_ssl+:: use ssl to connect
49
+ # +:use_ssl+:: use ssl to connect?
50
+ # +:verify_ssl+:: verify ssl server certificate?
51
+ # +:ssl_ca_cert+:: ssl CA certificate, chainfile or CA path. The system CA path is automatically included.
43
52
  def initialize *args
44
53
  @database = args.first if args.first.is_a? String
45
54
  opts = args.last.is_a?(Hash) ? args.last : {}
46
55
  @hosts = Array(opts[:hosts] || opts[:host] || ["localhost"])
47
56
  @port = opts[:port] || 8086
57
+ @path = opts[:path] || ""
48
58
  @username = opts[:username] || "root"
49
59
  @password = opts[:password] || "root"
60
+ @auth_method = %w{params basic_auth}.include?(opts[:auth_method]) ? opts[:auth_method] : "params"
50
61
  @use_ssl = opts[:use_ssl] || false
62
+ @verify_ssl = opts.fetch(:verify_ssl, true)
63
+ @ssl_ca_cert = opts[:ssl_ca_cert] || false
51
64
  @time_precision = opts[:time_precision] || "s"
52
65
  @initial_delay = opts[:initial_delay] || 0.01
53
66
  @max_delay = opts[:max_delay] || 30
@@ -65,14 +78,18 @@ module InfluxDB
65
78
  end
66
79
 
67
80
  @worker = InfluxDB::Worker.new(self) if @async
81
+ self.udp_client = opts[:udp] ? InfluxDB::UDPClient.new(opts[:udp][:host], opts[:udp][:port]) : nil
82
+
83
+ at_exit { stop! } if @retry > 0
84
+ end
68
85
 
69
- at_exit { stop! }
86
+ def ping
87
+ get "/ping"
70
88
  end
71
89
 
72
90
  ## allow options, e.g. influxdb.create_database('foo', replicationFactor: 3)
73
91
  def create_database(name, options = {})
74
- url = full_url("/db")
75
- options[:name] = name
92
+ url = full_url("/cluster/database_configs/#{name}")
76
93
  data = JSON.generate(options)
77
94
  post(url, data)
78
95
  end
@@ -85,6 +102,10 @@ module InfluxDB
85
102
  get full_url("/db")
86
103
  end
87
104
 
105
+ def authenticate_cluster_admin
106
+ get(full_url('/cluster_admins/authenticate'), true)
107
+ end
108
+
88
109
  def create_cluster_admin(username, password)
89
110
  url = full_url("/cluster_admins")
90
111
  data = JSON.generate({:name => username, :password => password})
@@ -105,9 +126,13 @@ module InfluxDB
105
126
  get full_url("/cluster_admins")
106
127
  end
107
128
 
108
- def create_database_user(database, username, password)
129
+ def authenticate_database_user(database)
130
+ get(full_url("/db/#{database}/authenticate"), true)
131
+ end
132
+
133
+ def create_database_user(database, username, password, options={})
109
134
  url = full_url("/db/#{database}/users")
110
- data = JSON.generate({:name => username, :password => password})
135
+ data = JSON.generate({:name => username, :password => password}.merge(options))
111
136
  post(url, data)
112
137
  end
113
138
 
@@ -133,6 +158,7 @@ module InfluxDB
133
158
  update_database_user(database, username, :admin => admin)
134
159
  end
135
160
 
161
+ # NOTE: Only cluster admin can call this
136
162
  def continuous_queries(database)
137
163
  get full_url("/db/#{database}/continuous_queries")
138
164
  end
@@ -141,12 +167,126 @@ module InfluxDB
141
167
  get full_url("/cluster/shards")
142
168
  end
143
169
 
144
- def delete_shard(shard_id, server_ids, username, password)
170
+ def delete_shard(shard_id, server_ids)
145
171
  data = JSON.generate({"serverIds" => server_ids})
146
- delete full_url("/cluster/shards/#{shard_id}", :u => username, :p => password), data
172
+ delete full_url("/cluster/shards/#{shard_id}"), data
173
+ end
174
+
175
+ # EXAMPLE:
176
+ #
177
+ # db.create_continuous_query(
178
+ # "select mean(sys) as sys, mean(usr) as usr from cpu group by time(15m)",
179
+ # "cpu.15m",
180
+ # )
181
+ #
182
+ # NOTE: Only cluster admin can call this
183
+ def create_continuous_query(query, name)
184
+ query("#{query} into #{name}")
185
+ end
186
+
187
+ # NOTE: Only cluster admin can call this
188
+ def get_continuous_query_list
189
+ query("list continuous queries")
190
+ end
191
+
192
+ # NOTE: Only cluster admin can call this
193
+ def delete_continuous_query(id)
194
+ query("drop continuous query #{id}")
195
+ end
196
+
197
+ def get_shard_space_list
198
+ get full_url("/cluster/shard_spaces")
199
+ end
200
+
201
+ def get_shard_space(database_name, shard_space_name)
202
+ get_shard_space_list.find do |shard_space|
203
+ shard_space["database"] == database_name &&
204
+ shard_space["name"] == shard_space_name
205
+ end
206
+ end
207
+
208
+ def create_shard_space(database_name, options = {})
209
+ url = full_url("/cluster/shard_spaces/#{database_name}")
210
+ data = JSON.generate(default_shard_space_options.merge(options))
211
+
212
+ post(url, data)
213
+ end
214
+
215
+ def delete_shard_space(database_name, shard_space_name)
216
+ delete full_url("/cluster/shard_spaces/#{database_name}/#{shard_space_name}")
217
+ end
218
+
219
+ ## Get the shard space first, so the user doesn't have to specify the existing options
220
+ def update_shard_space(database_name, shard_space_name, options)
221
+ shard_space_options = get_shard_space(database_name, shard_space_name)
222
+ shard_space_options.delete("database")
223
+
224
+ url = full_url("/cluster/shard_spaces/#{database_name}/#{shard_space_name}")
225
+ data = JSON.generate(shard_space_options.merge(options))
226
+
227
+ post(url, data)
228
+ end
229
+
230
+ def default_shard_space_options
231
+ {
232
+ "name" => "default",
233
+ "regEx" => "/.*/",
234
+ "retentionPolicy" => "inf",
235
+ "shardDuration" => "7d",
236
+ "replicationFactor" => 1,
237
+ "split" => 1
238
+ }
239
+ end
240
+
241
+ def configure_database(database_name, options = {})
242
+ url = full_url("/cluster/database_configs/#{database_name}")
243
+ data = JSON.generate(default_database_configuration.merge(options))
244
+
245
+ post(url, data)
246
+ end
247
+
248
+ def default_database_configuration
249
+ {:spaces => [default_shard_space_options]}
147
250
  end
148
251
 
149
252
  def write_point(name, data, async=@async, time_precision=@time_precision)
253
+ write_points([{:name => name, :data => data}], async, time_precision)
254
+ end
255
+
256
+ # Example:
257
+ # db.write_points(
258
+ # [
259
+ # {
260
+ # name: 'first_name',
261
+ # data: {
262
+ # value: 'val1'
263
+ # }
264
+ # },
265
+ # {
266
+ # name: 'first_name',
267
+ # data: {
268
+ # value: 'val1'
269
+ # }
270
+ # }
271
+ # ]
272
+ # )
273
+ def write_points(name_data_hashes_array, async=@async, time_precision=@time_precision)
274
+
275
+ payloads = []
276
+ name_data_hashes_array.each do |attrs|
277
+ payloads << generate_payload(attrs[:name], attrs[:data])
278
+ end
279
+
280
+ if async
281
+ worker.push(payloads)
282
+ elsif udp_client
283
+ udp_client.send(payloads)
284
+ else
285
+ _write(payloads, time_precision)
286
+ end
287
+ end
288
+
289
+ def generate_payload(name, data)
150
290
  data = data.is_a?(Array) ? data : [data]
151
291
  columns = data.reduce(:merge).keys.sort {|a,b| a.to_s <=> b.to_s}
152
292
  payload = {:name => name, :points => [], :columns => columns}
@@ -157,11 +297,7 @@ module InfluxDB
157
297
  end
158
298
  end
159
299
 
160
- if async
161
- worker.push(payload)
162
- else
163
- _write([payload], time_precision)
164
- end
300
+ payload
165
301
  end
166
302
 
167
303
  def _write(payload, time_precision=@time_precision)
@@ -201,19 +337,31 @@ module InfluxDB
201
337
  private
202
338
 
203
339
  def full_url(path, params={})
204
- params[:u] = @username
205
- params[:p] = @password
340
+ unless basic_auth?
341
+ params[:u] = @username
342
+ params[:p] = @password
343
+ end
206
344
 
207
345
  query = params.map { |k, v| [CGI.escape(k.to_s), "=", CGI.escape(v.to_s)].join }.join("&")
208
346
 
209
- URI::Generic.build(:path => path, :query => query).to_s
347
+ URI::Generic.build(:path => "#{@path}#{path}", :query => query).to_s
210
348
  end
211
349
 
212
- def get(url)
350
+ def basic_auth?
351
+ @auth_method == 'basic_auth'
352
+ end
353
+
354
+ def get(url, return_response = false)
213
355
  connect_with_retry do |http|
214
- response = http.request(Net::HTTP::Get.new(url))
356
+ request = Net::HTTP::Get.new(url)
357
+ request.basic_auth @username, @password if basic_auth?
358
+ response = http.request(request)
215
359
  if response.kind_of? Net::HTTPSuccess
216
- return JSON.parse(response.body)
360
+ if return_response
361
+ return response
362
+ else
363
+ return JSON.parse(response.body)
364
+ end
217
365
  elsif response.kind_of? Net::HTTPUnauthorized
218
366
  raise InfluxDB::AuthenticationError.new response.body
219
367
  else
@@ -225,7 +373,9 @@ module InfluxDB
225
373
  def post(url, data)
226
374
  headers = {"Content-Type" => "application/json"}
227
375
  connect_with_retry do |http|
228
- response = http.request(Net::HTTP::Post.new(url, headers), data)
376
+ request = Net::HTTP::Post.new(url, headers)
377
+ request.basic_auth @username, @password if basic_auth?
378
+ response = http.request(request, data)
229
379
  if response.kind_of? Net::HTTPSuccess
230
380
  return response
231
381
  elsif response.kind_of? Net::HTTPUnauthorized
@@ -239,7 +389,9 @@ module InfluxDB
239
389
  def delete(url, data = nil)
240
390
  headers = {"Content-Type" => "application/json"}
241
391
  connect_with_retry do |http|
242
- response = http.request(Net::HTTP::Delete.new(url, headers), data)
392
+ request = Net::HTTP::Delete.new(url, headers)
393
+ request.basic_auth @username, @password if basic_auth?
394
+ response = http.request(request, data)
243
395
  if response.kind_of? Net::HTTPSuccess
244
396
  return response
245
397
  elsif response.kind_of? Net::HTTPUnauthorized
@@ -261,6 +413,21 @@ module InfluxDB
261
413
  http.open_timeout = @open_timeout
262
414
  http.read_timeout = @read_timeout
263
415
  http.use_ssl = @use_ssl
416
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @verify_ssl
417
+
418
+ if @use_ssl
419
+ store = OpenSSL::X509::Store.new
420
+ store.set_default_paths
421
+ if @ssl_ca_cert
422
+ if File.directory?(@ssl_ca_cert)
423
+ store.add_path(@ssl_ca_cert)
424
+ else
425
+ store.add_file(@ssl_ca_cert)
426
+ end
427
+ end
428
+ http.cert_store = store
429
+ end
430
+
264
431
  block.call(http)
265
432
 
266
433
  rescue Timeout::Error, *InfluxDB::NET_HTTP_EXCEPTIONS => e
@@ -272,7 +439,7 @@ module InfluxDB
272
439
  delay = [@max_delay, delay * 2].min
273
440
  retry
274
441
  else
275
- raise e, "Tried #{retry_count-1} times to reconnect but failed."
442
+ raise InfluxDB::ConnectionError, "Tried #{retry_count-1} times to reconnect but failed."
276
443
  end
277
444
  ensure
278
445
  http.finish if http.started?
@@ -9,12 +9,13 @@ module InfluxDB
9
9
  end
10
10
 
11
11
  def self.logger
12
- @logger ||= ::Logger.new(STDERR).tap {|logger| logger.level = Logger::INFO}
12
+ return @logger unless @logger.nil?
13
+ @logger = ::Logger.new(STDERR).tap {|logger| logger.level = Logger::INFO}
13
14
  end
14
15
 
15
16
  private
16
17
  def log(level, message)
17
- InfluxDB::Logging.logger.send(level.to_sym, PREFIX + message)
18
+ InfluxDB::Logging.logger.send(level.to_sym, PREFIX + message) if InfluxDB::Logging.logger
18
19
  end
19
20
  end
20
21
  end
@@ -0,0 +1,16 @@
1
+ require "json"
2
+
3
+ module InfluxDB
4
+ class UDPClient
5
+ attr_accessor :socket
6
+ def initialize(host, port)
7
+ self.socket = UDPSocket.new
8
+ self.socket.connect(host, port)
9
+ end
10
+
11
+ def send(payload)
12
+ socket.send(JSON.generate(payload), 0)
13
+ rescue
14
+ end
15
+ end
16
+ end
@@ -1,3 +1,3 @@
1
1
  module InfluxDB
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.9"
3
3
  end
@@ -17,6 +17,11 @@ module InfluxDB
17
17
  @queue = InfluxDB::MaxQueue.new
18
18
  @client = client
19
19
  spawn_threads!
20
+
21
+ at_exit do
22
+ log :debug, "Thread exiting, flushing queue."
23
+ check_background_queue until @queue.empty?
24
+ end
20
25
  end
21
26
 
22
27
  def current_threads
@@ -28,7 +33,11 @@ module InfluxDB
28
33
  end
29
34
 
30
35
  def push(payload)
31
- queue.push(payload)
36
+ if payload.is_a? Array
37
+ payload.each {|p| queue.push(p) }
38
+ else
39
+ queue.push(payload)
40
+ end
32
41
  end
33
42
 
34
43
  def spawn_threads!
@@ -38,11 +47,6 @@ module InfluxDB
38
47
  Thread.new do
39
48
  Thread.current[:influxdb] = self.object_id
40
49
 
41
- at_exit do
42
- log :debug, "Thread exiting, flushing queue."
43
- check_background_queue(thread_num) until @queue.empty?
44
- end
45
-
46
50
  while !client.stopped?
47
51
  self.check_background_queue(thread_num)
48
52
  sleep rand(SLEEP_INTERVAL)
@@ -22,6 +22,7 @@ describe InfluxDB::Client do
22
22
  @influxdb.password.should == "root"
23
23
  @influxdb.use_ssl.should be_falsey
24
24
  @influxdb.time_precision.should == "s"
25
+ @influxdb.auth_method.should == "params"
25
26
  end
26
27
  end
27
28
 
@@ -101,16 +102,70 @@ describe InfluxDB::Client do
101
102
  @influxdb.password.should == "root"
102
103
  end
103
104
  end
105
+
106
+ describe "with auth_method basic auth specified" do
107
+ it "should be initialized with basic auth enabled" do
108
+ @influxdb = InfluxDB::Client.new :auth_method => 'basic_auth'
109
+
110
+ @influxdb.should be_a(InfluxDB::Client)
111
+ @influxdb.auth_method.should == 'basic_auth'
112
+ @influxdb.username.should == "root"
113
+ @influxdb.password.should == "root"
114
+ end
115
+ end
116
+
117
+ describe "with udp specified" do
118
+ it "should initialize a udp client" do
119
+ @influxdb = InfluxDB::Client.new :udp => { :host => 'localhost', :port => 4444 }
120
+ expect(@influxdb.udp_client).to be_a(InfluxDB::UDPClient)
121
+ end
122
+
123
+ context "without udp specfied" do
124
+ it "does not initialize a udp client" do
125
+ @influxdb = InfluxDB::Client.new
126
+ expect(@influxdb.udp_client).to be_nil
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+
133
+ context "with basic auth enabled" do
134
+ let(:args) { { :auth_method => 'basic_auth' } }
135
+ it "should use basic authorization for get" do
136
+ stub_request(:get, "http://username:password@influxdb.test:9999/").to_return(:body => '[]')
137
+ @influxdb.send(:get , @influxdb.send(:full_url,'/')).should == []
138
+ end
139
+ it "should use basic authorization for post" do
140
+ stub_request(:post, "http://username:password@influxdb.test:9999/")
141
+ @influxdb.send(:post , @influxdb.send(:full_url,'/'), {}).should be_a(Net::HTTPOK)
142
+ end
143
+ it "should use basic authorization for delete" do
144
+ stub_request(:delete, "http://username:password@influxdb.test:9999/")
145
+ @influxdb.send(:delete , @influxdb.send(:full_url,'/')).should be_a(Net::HTTPOK)
146
+ end
147
+ end
148
+
149
+ describe "#ping" do
150
+ it "should return status ok" do
151
+ status_ok = {"status" => "ok"}
152
+ stub_request(:get, "http://influxdb.test:9999/ping"
153
+ ).to_return(:body => JSON.generate(status_ok), :status => 200)
154
+
155
+ @influxdb.ping.should == status_ok
156
+ end
104
157
  end
105
158
 
106
159
  describe "#create_database" do
107
160
  it "should POST to create a new database" do
108
- stub_request(:post, "http://influxdb.test:9999/db").with(
161
+ stub_request(
162
+ :post, 'http://influxdb.test:9999/cluster/database_configs/foo'
163
+ ).with(
109
164
  :query => {:u => "username", :p => "password"},
110
- :body => {:name => "foo"}
165
+ :body => {:spaces => []}
111
166
  )
112
167
 
113
- @influxdb.create_database("foo").should be_a(Net::HTTPOK)
168
+ @influxdb.create_database("foo", {:spaces => []}).should be_a(Net::HTTPOK)
114
169
  end
115
170
  end
116
171
 
@@ -135,6 +190,16 @@ describe InfluxDB::Client do
135
190
  end
136
191
  end
137
192
 
193
+ describe "#authenticate_cluster_admin" do
194
+ it "should GET to authenticate a cluster admin" do
195
+ stub_request(:get, "http://influxdb.test:9999/cluster_admins/authenticate").with(
196
+ :query => {:u => "username", :p => "password"}
197
+ )
198
+
199
+ @influxdb.authenticate_cluster_admin.should be_a(Net::HTTPOK)
200
+ end
201
+ end
202
+
138
203
  describe "#create_cluster_admin" do
139
204
  it "should POST to create a new cluster admin" do
140
205
  stub_request(:post, "http://influxdb.test:9999/cluster_admins").with(
@@ -178,6 +243,16 @@ describe InfluxDB::Client do
178
243
  end
179
244
  end
180
245
 
246
+ describe "#authenticate_database_user" do
247
+ it "should GET to authenticate a database database" do
248
+ stub_request(:get, "http://influxdb.test:9999/db/foo/authenticate").with(
249
+ :query => {:u => "username", :p => "password"}
250
+ ).to_return(:body => '', :status => 200)
251
+
252
+ @influxdb.authenticate_database_user("foo").should be_a(Net::HTTPOK)
253
+ end
254
+ end
255
+
181
256
  describe "#create_database_user" do
182
257
  it "should POST to create a new database user" do
183
258
  stub_request(:post, "http://influxdb.test:9999/db/foo/users").with(
@@ -187,6 +262,20 @@ describe InfluxDB::Client do
187
262
 
188
263
  @influxdb.create_database_user("foo", "useruser", "passpass").should be_a(Net::HTTPOK)
189
264
  end
265
+
266
+ it "should POST to create a new database user with permissions" do
267
+ stub_request(:post, "http://influxdb.test:9999/db/foo/users").with(
268
+ :query => {:u => "username", :p => "password"},
269
+ :body => {:name => "useruser", :password => "passpass", :readFrom => "/read*/", :writeTo => "/write*/"}
270
+ )
271
+
272
+ @influxdb.create_database_user(
273
+ "foo",
274
+ "useruser",
275
+ "passpass",
276
+ {:readFrom => "/read*/", :writeTo => "/write*/"}
277
+ ).should be_a(Net::HTTPOK)
278
+ end
190
279
  end
191
280
 
192
281
  describe "#update_database_user" do
@@ -261,7 +350,174 @@ describe InfluxDB::Client do
261
350
  :query => {:u => "username", :p => "password"}
262
351
  )
263
352
 
264
- @influxdb.delete_shard(shard_id, [1, 2], "username", "password").should be_a(Net::HTTPOK)
353
+ @influxdb.delete_shard(shard_id, [1, 2]).should be_a(Net::HTTPOK)
354
+ end
355
+ end
356
+
357
+ describe "#create_continuous_query" do
358
+ it "should GET to create a continuous query" do
359
+ stub_request(:get, "http://influxdb.test:9999/db/database/series").with(
360
+ :query => { :q => "select sys from cpu into sys", :u => "username", :p => "password", :time_precision => "s"}
361
+ ).to_return(:body => JSON.generate({}))
362
+
363
+ @influxdb.create_continuous_query("select sys from cpu", "sys").should == {}
364
+ end
365
+ end
366
+
367
+ describe "#get_continuous_query_list" do
368
+ it "should GET to get continuous query list" do
369
+ body = [{
370
+ "name"=>"continuous queries",
371
+ "columns"=>["time", "sequence_number", "id", "query"],
372
+ "points"=>[
373
+ [1399, 1, 1, "select sys from cpu into sys"]
374
+ ]
375
+ }]
376
+ expected = {
377
+ "continuous queries" => [
378
+ {
379
+ "time" => 1399,
380
+ "sequence_number" => 1,
381
+ "id" => 1,
382
+ "query" => "select sys from cpu into sys"
383
+ }
384
+ ]
385
+ }
386
+ stub_request(:get, "http://influxdb.test:9999/db/database/series").with(
387
+ :query => { :q => "list continuous queries", :u => "username", :p => "password", :time_precision => "s"}
388
+ ).to_return(:body => JSON.generate(body))
389
+
390
+ @influxdb.get_continuous_query_list.should == expected
391
+ end
392
+ end
393
+
394
+ describe "#delete_continuous_query" do
395
+ it "should GET to delete continuous query" do
396
+ id = 1
397
+ stub_request(:get, "http://influxdb.test:9999/db/database/series").with(
398
+ :query => { :q => "drop continuous query #{id}", :u => "username", :p => "password", :time_precision => "s"}
399
+ ).to_return(:body => JSON.generate({}))
400
+
401
+ @influxdb.delete_continuous_query(id).should == {}
402
+ end
403
+ end
404
+
405
+ describe "#get_shard_space_list" do
406
+ let(:url) { "http://influxdb.test:9999/cluster/shard_spaces" }
407
+ let(:request_params) { {:query => {:u => "username", :p => "password"}} }
408
+ let(:response) { {:body => JSON.generate(shard_spaces, :status => 200)} }
409
+ let(:shard_spaces) { [@influxdb.default_shard_space_options.merge(:database => "foo")] }
410
+
411
+ it 'should GET a list of shard spaces' do
412
+ request = stub_request(:get, url).with(request_params).to_return(response)
413
+
414
+ @influxdb.get_shard_space_list
415
+
416
+ expect(request).to have_been_requested
417
+ end
418
+ end
419
+
420
+ describe "#get_shard_space" do
421
+ let(:url) { "http://influxdb.test:9999/cluster/shard_spaces" }
422
+ let(:request_params) { {:query => {:u => "username", :p => "password"}} }
423
+ let(:response) { {:body => JSON.generate(shard_spaces, :status => 200)} }
424
+
425
+ describe "#get_shard_space_list returns a non-empty list" do
426
+ let(:shard_spaces) { [@influxdb.default_shard_space_options.merge("database" => "foo")] }
427
+
428
+ it "finds the indicated shard space" do
429
+ request = stub_request(:get, url).with(request_params).to_return(response)
430
+
431
+ expect(@influxdb.get_shard_space('foo', 'default')).to eq shard_spaces.first
432
+
433
+ expect(request).to have_been_requested
434
+ end
435
+ end
436
+
437
+ describe "#get_shard_space_list returns an empty list" do
438
+ let(:shard_spaces) { [] }
439
+
440
+ it "finds no shard space" do
441
+ request = stub_request(:get, url).with(request_params).to_return(response)
442
+
443
+ expect(@influxdb.get_shard_space('foo', 'default')).to eq nil
444
+
445
+ expect(request).to have_been_requested
446
+ end
447
+ end
448
+ end
449
+
450
+ describe "#create_shard_space" do
451
+ let(:url) { "http://influxdb.test:9999/cluster/shard_spaces/foo" }
452
+ let(:request_params) do
453
+ {
454
+ :query => {:u => "username", :p => "password"},
455
+ :body => @influxdb.default_shard_space_options
456
+ }
457
+ end
458
+
459
+ it 'should POST to create a shard space' do
460
+ request = stub_request(:post, url).with(request_params)
461
+
462
+ @influxdb.create_shard_space("foo", @influxdb.default_shard_space_options)
463
+
464
+ expect(request).to have_been_requested
465
+ end
466
+ end
467
+
468
+ describe "#delete_shard_space" do
469
+ let(:url) { "http://influxdb.test:9999/cluster/shard_spaces/foo/default" }
470
+ let(:request_params) { {:query => {:u => "username", :p => "password"}} }
471
+
472
+ it 'should DELETE to delete the shard space' do
473
+ request = stub_request(:delete, url).with(request_params)
474
+
475
+ @influxdb.delete_shard_space("foo", "default")
476
+
477
+ expect(request).to have_been_requested
478
+ end
479
+ end
480
+
481
+ describe "#update_shard_space" do
482
+ let(:get_url) { "http://influxdb.test:9999/cluster/shard_spaces" }
483
+ let(:get_request_params) { {:query => {:u => "username", :p => "password"}} }
484
+ let(:get_response) { {:body => JSON.generate(shard_spaces, :status => 200)} }
485
+ let(:shard_spaces) { [@influxdb.default_shard_space_options.merge("database" => "foo")] }
486
+
487
+ let(:post_url) { "http://influxdb.test:9999/cluster/shard_spaces/foo/default" }
488
+ let(:post_request_params) do
489
+ {
490
+ :query => {:u => "username", :p => "password"},
491
+ :body => @influxdb.default_shard_space_options.merge("shardDuration" => "30d")
492
+ }
493
+ end
494
+
495
+ it 'should GET the shard space and then POST to update the shard space' do
496
+ get_request = stub_request(:get, get_url).with(get_request_params).to_return(get_response)
497
+ post_request = stub_request(:post, post_url).with(post_request_params)
498
+
499
+ @influxdb.update_shard_space("foo", "default", {"shardDuration" => "30d"})
500
+
501
+ expect(get_request).to have_been_requested
502
+ expect(post_request).to have_been_requested
503
+ end
504
+ end
505
+
506
+ describe "#configure_database" do
507
+ let(:url) { "http://influxdb.test:9999/cluster/database_configs/foo" }
508
+ let(:request_params) do
509
+ {
510
+ :query => {:u => "username", :p => "password"},
511
+ :body => @influxdb.default_database_configuration
512
+ }
513
+ end
514
+
515
+ it "should POST to create a new database config" do
516
+ request = stub_request(:post, url).with(request_params)
517
+
518
+ @influxdb.configure_database("foo").should be_a(Net::HTTPOK)
519
+
520
+ expect(request).to have_been_requested
265
521
  end
266
522
  end
267
523
 
@@ -307,14 +563,18 @@ describe InfluxDB::Client do
307
563
  it "raises when stopped" do
308
564
  @influxdb.stop!
309
565
  @influxdb.should_not_receive :sleep
310
- expect { subject }.to raise_error(Timeout::Error)
566
+ expect { subject }.to raise_error(InfluxDB::ConnectionError) do |e|
567
+ expect(e.cause).to be_an_instance_of(Timeout::Error)
568
+ end
311
569
  end
312
570
 
313
571
  context "when retry is 0" do
314
572
  let(:args) { { :retry => 0 } }
315
573
  it "raise error directly" do
316
574
  @influxdb.should_not_receive :sleep
317
- expect { subject }.to raise_error(Timeout::Error)
575
+ expect { subject }.to raise_error(InfluxDB::ConnectionError) do |e|
576
+ expect(e.cause).to be_an_instance_of(Timeout::Error)
577
+ end
318
578
  end
319
579
  end
320
580
 
@@ -323,7 +583,9 @@ describe InfluxDB::Client do
323
583
 
324
584
  it "raise error after 'n' attemps" do
325
585
  @influxdb.should_receive(:sleep).exactly(3).times
326
- expect { subject }.to raise_error(Timeout::Error)
586
+ expect { subject }.to raise_error(InfluxDB::ConnectionError) do |e|
587
+ expect(e.cause).to be_an_instance_of(Timeout::Error)
588
+ end
327
589
  end
328
590
  end
329
591
 
@@ -471,21 +733,41 @@ describe InfluxDB::Client do
471
733
  it "should push to the worker with payload if client is async" do
472
734
  @influxdb = InfluxDB::Client.new "database", :host => "influxdb.test", :async => true
473
735
 
474
- data = {:name => "juan", :age => 87, :time => Time.now.to_i}
475
- @influxdb.stub_chain(:worker, :push).with(hash_including({:name => 'seriez'})).and_return(:ok)
736
+ time = Time.now.to_i
737
+ data = {:name => "juan", :age => 87, :time => time}
738
+ expected_data = [{:name => 'seriez', :points => [[87, 'juan', time]], :columns => [:age, :name, :time]}]
739
+ @influxdb.stub_chain(:worker, :push).with(expected_data).and_return(:ok)
476
740
  @influxdb.write_point("seriez", data).should eq(:ok)
477
741
  end
478
742
 
479
743
  it "should push to the worker with payload if write_point call is async" do
480
744
  @influxdb = InfluxDB::Client.new "database", :host => "influxdb.test", :async => false
481
745
 
482
- data = {:name => "juan", :age => 87, :time => Time.now.to_i}
483
- @influxdb.stub_chain(:worker, :push).with(hash_including({:name => 'seriez'})).and_return(:ok)
746
+ time = Time.now.to_i
747
+ data = {:name => "juan", :age => 87, :time => time}
748
+ expected_data = [{:name => 'seriez', :points => [[87, 'juan', time]], :columns => [:age, :name, :time]}]
749
+ @influxdb.stub_chain(:worker, :push).with(expected_data).and_return(:ok)
484
750
  @influxdb.write_point("seriez", data, true).should eq(:ok)
485
751
  end
486
752
 
487
753
  end
488
754
 
755
+ describe "udp" do
756
+ let(:udp_client) { double }
757
+ let(:time) { Time.now.to_i }
758
+ let(:influxdb) { InfluxDB::Client.new(:udp => { :host => "localhost", :port => 44444 }) }
759
+
760
+ before do
761
+ allow(InfluxDB::UDPClient).to receive(:new).with('localhost', 44444).and_return(udp_client)
762
+ end
763
+
764
+ it "should send payload via udp if udp client is set up" do
765
+ expect(udp_client).to receive(:send).with([{:name=>"seriez", :points=>[[87, "juan", time]], :columns=>[:age, :name, :time]}])
766
+ data = {:name => "juan", :age => 87, :time => time}
767
+ influxdb.write_point("seriez", data)
768
+ end
769
+ end
770
+
489
771
  end
490
772
 
491
773
  describe "#execute_queries" do
@@ -23,6 +23,22 @@ describe InfluxDB::Logging do
23
23
  InfluxDB::Logging.logger = new_logger
24
24
  expect(InfluxDB::Logging.logger).to eq(new_logger)
25
25
  end
26
+
27
+ it "allows disabling of a logger" do
28
+ InfluxDB::Logging.logger = false
29
+ expect(InfluxDB::Logging.logger).to eql false
30
+ end
31
+
32
+ context "when logging is disabled" do
33
+
34
+ subject { LoggerTest.new }
35
+
36
+ it "does not log" do
37
+ InfluxDB::Logging.logger = false
38
+ expect(InfluxDB::Logging.logger).not_to receive(:debug)
39
+ subject.write_to_log(:debug, 'test')
40
+ end
41
+ end
26
42
 
27
43
  context "when included in classes" do
28
44
 
@@ -0,0 +1,33 @@
1
+ require "spec_helper"
2
+ require "json"
3
+
4
+ describe InfluxDB::UDPClient do
5
+ subject { described_class.new("localhost", 44444) }
6
+ let(:message) { [{ "foo" => "bar" }] }
7
+
8
+ describe "#send" do
9
+ it "sends a UPD packet" do
10
+ s = UDPSocket.new
11
+ s.bind("localhost", 44444)
12
+ subject.send(message)
13
+ rec_mesage = JSON.parse(s.recvfrom(15).first)
14
+ expect(rec_mesage).to eq message
15
+ end
16
+
17
+ context "it can't connect" do
18
+ before do
19
+ subject.socket = FailingSocket.new
20
+ end
21
+
22
+ it "doesn't blow up" do
23
+ expect { subject.send(message) }.to_not raise_error
24
+ end
25
+ end
26
+
27
+ class FailingSocket < UDPSocket
28
+ def send(*args)
29
+ fail
30
+ end
31
+ end
32
+ end
33
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,11 @@
1
- require "influxdb"
2
- require "webmock/rspec"
1
+ require 'influxdb'
2
+ require 'webmock/rspec'
3
+
4
+ RSpec.configure do |config|
5
+ config.mock_with :rspec do |mocks|
6
+ mocks.syntax = [:should, :expect]
7
+ end
8
+ config.expect_with :rspec do |expectations|
9
+ expectations.syntax = [:should, :expect]
10
+ end
11
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: influxdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Todd Persen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-06 00:00:00.000000000 Z
11
+ date: 2015-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: cause
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,16 +70,16 @@ dependencies:
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - '>='
73
+ - - ~>
60
74
  - !ruby/object:Gem::Version
61
- version: '0'
75
+ version: 3.0.0
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - '>='
80
+ - - ~>
67
81
  - !ruby/object:Gem::Version
68
- version: '0'
82
+ version: 3.0.0
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: webmock
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -100,11 +114,13 @@ files:
100
114
  - lib/influxdb/logger.rb
101
115
  - lib/influxdb/max_queue.rb
102
116
  - lib/influxdb/point_value.rb
117
+ - lib/influxdb/udp_client.rb
103
118
  - lib/influxdb/version.rb
104
119
  - lib/influxdb/worker.rb
105
120
  - spec/influxdb/client_spec.rb
106
121
  - spec/influxdb/logger_spec.rb
107
122
  - spec/influxdb/point_value_spec.rb
123
+ - spec/influxdb/udp_client_spec.rb
108
124
  - spec/influxdb/worker_spec.rb
109
125
  - spec/influxdb_spec.rb
110
126
  - spec/max_queue_spec.rb
@@ -129,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
145
  version: '0'
130
146
  requirements: []
131
147
  rubyforge_project:
132
- rubygems_version: 2.1.11
148
+ rubygems_version: 2.0.14
133
149
  signing_key:
134
150
  specification_version: 4
135
151
  summary: Ruby library for InfluxDB.
@@ -137,6 +153,7 @@ test_files:
137
153
  - spec/influxdb/client_spec.rb
138
154
  - spec/influxdb/logger_spec.rb
139
155
  - spec/influxdb/point_value_spec.rb
156
+ - spec/influxdb/udp_client_spec.rb
140
157
  - spec/influxdb/worker_spec.rb
141
158
  - spec/influxdb_spec.rb
142
159
  - spec/max_queue_spec.rb