influxdb-0.2.3/0000755000175600017570000000000012646433657012410 5ustar pravipraviinfluxdb-0.2.3/spec/0000755000175600017570000000000012646433657013342 5ustar pravipraviinfluxdb-0.2.3/spec/spec_helper.rb0000644000175600017570000000023212646433657016155 0ustar pravipravirequire "influxdb" require "webmock/rspec" begin require "pry-byebug" rescue LoadError end InfluxDB::Logging.logger = Logger.new(STDOUT) if ENV['LOG'] influxdb-0.2.3/spec/influxdb/0000755000175600017570000000000012646433657015155 5ustar pravipraviinfluxdb-0.2.3/spec/influxdb/cases/0000755000175600017570000000000012646433657016253 5ustar pravipraviinfluxdb-0.2.3/spec/influxdb/cases/query_database_spec.rb0000644000175600017570000000302212646433657022600 0ustar pravipravirequire "spec_helper" require "json" describe InfluxDB::Client do let(:subject) do described_class.new( "database", { host: "influxdb.test", port: 9999, username: "username", password: "password", time_precision: "s" }.merge(args) ) end let(:args) { {} } describe "#create_database" do before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: "CREATE DATABASE foo" } ) end it "should GET to create a new database" do expect(subject.create_database("foo")).to be_a(Net::HTTPOK) end end describe "#delete_database" do before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: "DROP DATABASE foo" } ) end it "should GET to remove a database" do expect(subject.delete_database("foo")).to be_a(Net::HTTPOK) end end describe "#list_databases" do let(:response) { { "results" => [{ "series" => [{ "name" => "databases", "columns" => ["name"], "values" => [["foobar"]] }] }] } } let(:expected_result) { [{ "name" => "foobar" }] } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: "SHOW DATABASES" } ).to_return(body: JSON.generate(response), status: 200) end it "should GET a list of databases" do expect(subject.list_databases).to eq(expected_result) end end end influxdb-0.2.3/spec/influxdb/cases/retry_requests_spec.rb0000644000175600017570000000600712646433657022715 0ustar pravipravirequire "spec_helper" require "json" describe InfluxDB::Client do let(:client) do described_class.new( "database", { host: "influxdb.test", port: 9999, username: "username", password: "password", time_precision: "s" }.merge(args) ) end let(:args) { {} } let(:database) { client.config.database } describe "retrying requests" do let(:series) { "cpu" } let(:data) do { tags: { region: 'us', host: 'server_1' }, values: { temp: 88, value: 54 } } end let(:body) do InfluxDB::PointValue.new(data.merge(series: series)).dump end subject { client.write_point(series, data) } before do allow(client).to receive(:log) stub_request(:post, "http://influxdb.test:9999/write").with( query: { u: "username", p: "password", precision: 's', db: database }, headers: { "Content-Type" => "application/octet-stream" }, body: body ).to_raise(Timeout::Error) end it "raises when stopped" do client.stop! expect(client).not_to receive(:sleep) expect { subject }.to raise_error(InfluxDB::ConnectionError) do |e| expect(e.cause).to be_an_instance_of(Timeout::Error) end end context "when retry is 0" do let(:args) { { retry: 0 } } it "raise error directly" do expect(client).not_to receive(:sleep) expect { subject }.to raise_error(InfluxDB::ConnectionError) do |e| expect(e.cause).to be_an_instance_of(Timeout::Error) end end end context "when retry is 'n'" do let(:args) { { retry: 3 } } it "raise error after 'n' attemps" do expect(client).to receive(:sleep).exactly(3).times expect { subject }.to raise_error(InfluxDB::ConnectionError) do |e| expect(e.cause).to be_an_instance_of(Timeout::Error) end end end context "when retry is -1" do let(:args) { { retry: -1 } } before do stub_request(:post, "http://influxdb.test:9999/write").with( query: { u: "username", p: "password", precision: 's', db: database }, headers: { "Content-Type" => "application/octet-stream" }, body: body ).to_raise(Timeout::Error).then .to_raise(Timeout::Error).then .to_raise(Timeout::Error).then .to_raise(Timeout::Error).then .to_return(status: 200) end it "keep trying until get the connection" do expect(client).to receive(:sleep).exactly(4).times expect { subject }.to_not raise_error end end it "raise an exception if the server didn't return 200" do stub_request(:post, "http://influxdb.test:9999/write").with( query: { u: "username", p: "password", precision: 's', db: database }, headers: { "Content-Type" => "application/octet-stream" }, body: body ).to_return(status: 401) expect { client.write_point(series, data) }.to raise_error(InfluxDB::AuthenticationError) end end end influxdb-0.2.3/spec/influxdb/cases/async_client_spec.rb0000644000175600017570000000211212646433657022261 0ustar pravipravirequire "spec_helper" require "timeout" describe InfluxDB::Client do let(:subject) { described_class.new(async: true) } let(:stub_url) { "http://localhost:8086/write?db=&p=root&precision=s&u=root" } let(:worker_klass) { InfluxDB::Writer::Async::Worker } specify { expect(subject.writer).to be_a(InfluxDB::Writer::Async) } describe "#write_point" do let(:payload) { "responses,region=eu value=5" } it "sends writes to client" do post_request = stub_request(:post, stub_url) (worker_klass::MAX_POST_POINTS + 100).times do subject.write_point('a', {}) end Timeout.timeout(2 * worker_klass::SLEEP_INTERVAL) do subject.stop! # ensure threads exit subject.writer.worker.threads.each(&:join) # flush queue (we cannot test `at_exit`) subject.writer.worker.check_background_queue end # exact times can be 2 or 3 (because we have 3 worker threads), # but cannot be less than 2 due to MAX_POST_POINTS limit expect(post_request).to have_been_requested.at_least_times(2) end end end influxdb-0.2.3/spec/influxdb/cases/query_cluster_spec.rb0000644000175600017570000000346512646433657022530 0ustar pravipravirequire "spec_helper" require "json" describe InfluxDB::Client do let(:subject) do described_class.new( "database", { host: "influxdb.test", port: 9999, username: "username", password: "password", time_precision: "s" }.merge(args) ) end let(:args) { {} } describe "#create_cluster_admin" do let(:user) { 'adminadmin' } let(:pass) { 'passpass' } let(:query) { "CREATE USER #{user} WITH PASSWORD '#{pass}' WITH ALL PRIVILEGES" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: query } ) end it "should GET to create a new cluster admin" do expect(subject.create_cluster_admin(user, pass)).to be_a(Net::HTTPOK) end end describe "#list_cluster_admins" do let(:response) { { "results" => [{ "series" => [{ "columns" => %w(user admin), "values" => [["dbadmin", true], ["foobar", false]] }] }] } } let(:expected_result) { ["dbadmin"] } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: "SHOW USERS" } ).to_return(body: JSON.generate(response, status: 200)) end it "should GET a list of cluster admins" do expect(subject.list_cluster_admins).to eq(expected_result) end end describe "#revoke_cluster_admin_privileges" do let(:user) { 'useruser' } let(:query) { "REVOKE ALL PRIVILEGES FROM #{user}" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: query } ) end it "should GET to revoke cluster admin privileges from a user" do expect(subject.revoke_cluster_admin_privileges(user)).to be_a(Net::HTTPOK) end end end influxdb-0.2.3/spec/influxdb/cases/query_shard_space_spec.rb0000644000175600017570000000643712646433657023325 0ustar pravipravi# TODO: support 0.9.x # require "spec_helper" # require "json" # describe InfluxDB::Client do # let(:subject) do # described_class.new( # "database", # { # host: "influxdb.test", # port: 9999, # username: "username", # password: "password", # time_precision: "s" # }.merge(args) # ) # end # let(:args) { {} } # let(:url) { "http://influxdb.test:9999/cluster/shard_spaces" } # let(:req_query) { { u: "username", p: "password" } } # let(:req_body) { nil } # let(:request_params) { { query: req_query, body: req_body } } # let(:response) { { body: JSON.generate(shard_spaces), status: 200 } } # let(:shard_spaces) { [subject.default_shard_space_options.merge("database" => "foo")] } # context "GET methods" do # before { stub_request(:get, url).with(request_params).to_return(response) } # describe "GET #list_shard_spaces" do # it 'returns OK' do # expect(subject.list_shard_spaces).to eq shard_spaces # end # end # describe "GET #shard_space_info" do # context "non-empty list" do # it "returns shard space info" do # expect(subject.shard_space_info('foo', 'default')).to eq shard_spaces.first # end # end # context "returns an empty list" do # let(:shard_spaces) { [] } # it "returns no shard space" do # expect(subject.shard_space_info('foo', 'default')).to be_nil # end # end # end # end # describe "POST #create_shard_space" do # let(:url) { "http://influxdb.test:9999/cluster/shard_spaces/foo" } # let(:req_body) { subject.default_shard_space_options } # let(:response) { { status: 200 } } # before { stub_request(:post, url).with(request_params).to_return(response) } # it 'returns OK' do # expect(subject.create_shard_space("foo", subject.default_shard_space_options)) # .to be_a(Net::HTTPOK) # end # end # describe "DELETE #delete_shard_space" do # let(:url) { "http://influxdb.test:9999/cluster/shard_spaces/foo/default" } # let(:response) { { status: 200 } } # before { stub_request(:delete, url).with(request_params).to_return(response) } # it 'returns OK' do # expect(subject.delete_shard_space("foo", "default")).to be_a(Net::HTTPOK) # end # end # describe "#update_shard_space" do # let(:post_url) { "http://influxdb.test:9999/cluster/shard_spaces/foo/default" } # let(:post_request_params) do # { # query: req_query, # body: subject.default_shard_space_options.merge("shardDuration" => "30d") # } # end # it 'gets the shard space and updates the shard space' do # stub_request(:get, url).with(request_params).to_return(response) # stub_request(:post, post_url).with(post_request_params) # expect(subject.update_shard_space("foo", "default", "shardDuration" => "30d")).to be_a(Net::HTTPOK) # end # end # describe "POST #configure_database" do # let(:url) { "http://influxdb.test:9999/cluster/database_configs/foo" } # let(:req_body) { subject.default_database_configuration } # before { stub_request(:post, url).with(request_params) } # it "returns OK" do # expect(subject.configure_database("foo")).to be_a(Net::HTTPOK) # end # end # end influxdb-0.2.3/spec/influxdb/cases/query_shard_spec.rb0000644000175600017570000000210412646433657022135 0ustar pravipravirequire "spec_helper" require "json" describe InfluxDB::Client do let(:subject) do described_class.new( "database", { host: "influxdb.test", port: 9999, username: "username", password: "password", time_precision: "s" }.merge(args) ) end let(:args) { {} } ### TODO ### # describe "GET #list_shards" do # it "returns a list of shards" do # shard_list = { "longTerm" => [], "shortTerm" => [] } # stub_request(:get, "http://influxdb.test:9999/cluster/shards").with( # query: { u: "username", p: "password" } # ).to_return(body: JSON.generate(shard_list, status: 200)) # expect(subject.list_shards).to eq shard_list # end # end # describe "DELETE #delete_shard" do # it "removes shard by id" do # shard_id = 1 # stub_request(:delete, "http://influxdb.test:9999/cluster/shards/#{shard_id}").with( # query: { u: "username", p: "password" } # ) # expect(subject.delete_shard(shard_id, [1, 2])).to be_a(Net::HTTPOK) # end # end end influxdb-0.2.3/spec/influxdb/cases/query_core.rb0000644000175600017570000000211212646433657020751 0ustar pravipravirequire 'spec_helper' require 'json' describe InfluxDB::Client do let(:subject) do described_class.new( 'database', { host: 'influxdb.test', port: 9999, username: 'username', password: 'password', time_precision: 's' }.merge(args) ) end let(:args) { {} } describe '#query' do it 'should handle responses with no values' do # Some requests (such as trying to retrieve values from the future) # return a result with no 'values' key set. query = 'SELECT value FROM requests_per_minute WHERE time > 1437019900' response = {'results'=>[{'series'=>[{'name'=>'requests_per_minute' ,'columns' => ['time','value']}]}]} stub_request(:get, 'http://influxdb.test:9999/query').with( query: { db: 'database', precision: 's', u: 'username', p: 'password', q: query } ).to_return(body: JSON.generate(response), status: 200) expected_result = [{'name'=>'requests_per_minute', 'tags'=>nil, 'values'=>[]}] expect(subject.query(query)).to eq(expected_result) end end end influxdb-0.2.3/spec/influxdb/cases/query_retention_policy_spec.rb0000644000175600017570000000713312646433657024431 0ustar pravipravirequire "spec_helper" require "json" describe InfluxDB::Client do let(:subject) do described_class.new( "database", { host: "influxdb.test", port: 9999, username: "username", password: "password", time_precision: "s" }.merge(args) ) end let(:args) { {} } describe "#list_retention_policies" do let(:response) { { "results" => [{ "series" => [{ "columns" => %w(name duration replicaN default), "values" => [["default", "0", 1, true], ["another", "1", 2, false]] }] }] } } let(:expected_result) { [{ "name" => "default", "duration" => "0", "replicaN" => 1, "default" => true }, { "name" => "another", "duration" => "1", "replicaN" => 2, "default" => false }] } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: "SHOW RETENTION POLICIES ON \"database\"" } ).to_return(body: JSON.generate(response), status: 200) end it "should GET a list of retention policies" do expect(subject.list_retention_policies('database')).to eq(expected_result) end end describe "#create_retention_policy" do context "default" do before do stub_request(:get, "http://influxdb.test:9999/query") .with( query: { u: "username", p: "password", q: "CREATE RETENTION POLICY \"1h.cpu\" ON foo DURATION 1h REPLICATION 2 DEFAULT" } ) end it "should GET to create a new database" do expect(subject.create_retention_policy('1h.cpu', 'foo', '1h', 2, true)).to be_a(Net::HTTPOK) end end context "non-default" do before do stub_request(:get, "http://influxdb.test:9999/query") .with( query: { u: "username", p: "password", q: "CREATE RETENTION POLICY \"1h.cpu\" ON foo DURATION 1h REPLICATION 2" } ) end it "should GET to create a new database" do expect(subject.create_retention_policy('1h.cpu', 'foo', '1h', 2)).to be_a(Net::HTTPOK) end end end describe "#delete_retention_policy" do before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: "DROP RETENTION POLICY \"1h.cpu\" ON foo" } ) end it "should GET to remove a database" do expect(subject.delete_retention_policy('1h.cpu', 'foo')).to be_a(Net::HTTPOK) end end describe "#alter_retention_policy" do context "default" do before do stub_request(:get, "http://influxdb.test:9999/query") .with( query: { u: "username", p: "password", q: "ALTER RETENTION POLICY \"1h.cpu\" ON foo DURATION 1h REPLICATION 2 DEFAULT" } ) end it "should GET to alter a new database" do expect(subject.alter_retention_policy('1h.cpu', 'foo', '1h', 2, true)).to be_a(Net::HTTPOK) end end context "non-default" do before do stub_request(:get, "http://influxdb.test:9999/query") .with( query: { u: "username", p: "password", q: "ALTER RETENTION POLICY \"1h.cpu\" ON foo DURATION 1h REPLICATION 2" } ) end it "should GET to alter a new database" do expect(subject.alter_retention_policy('1h.cpu', 'foo', '1h', 2)).to be_a(Net::HTTPOK) end end end end influxdb-0.2.3/spec/influxdb/cases/query_series_spec.rb0000644000175600017570000000227612646433657022340 0ustar pravipravirequire "spec_helper" require "json" describe InfluxDB::Client do let(:subject) do described_class.new( "database", { host: "influxdb.test", port: 9999, username: "username", password: "password", time_precision: "s" }.merge(args) ) end let(:args) { {} } ### TODO ### # describe "DELETE #delete_series" do # it "removes a series" do # stub_request(:delete, "http://influxdb.test:9999/db/database/series/foo").with( # query: { u: "username", p: "password" } # ) # expect(subject.delete_series("foo")).to be_a(Net::HTTPOK) # end # end # describe "GET #list_series" do # it "returns a list of all series names" do # data = [ # { "name" => "list_series_result", # "columns" => %w(time name), # "points" => [[0, 'a'], [0, 'b']] # } # ] # stub_request(:get, "http://influxdb.test:9999/db/database/series").with( # query: { u: "username", p: "password", q: "list series", time_precision: "s" } # ).to_return( # body: JSON.generate(data) # ) # expect(subject.list_series).to eq %w(a b) # end # end end influxdb-0.2.3/spec/influxdb/cases/udp_client_spec.rb0000644000175600017570000000105012646433657021734 0ustar pravipravirequire "spec_helper" describe InfluxDB::Client do let(:client) { described_class.new(udp: { host: "localhost", port: 44_444 }) } specify { expect(client.writer).to be_a(InfluxDB::Writer::UDP) } describe "#write" do let(:message) { 'responses,region=eu value=5' } it "sends a UPD packet" do s = UDPSocket.new s.bind("localhost", 44_444) client.write_point("responses", values: { value: 5 }, tags: { region: 'eu' }) rec_message = s.recvfrom(30).first expect(rec_message).to eq message end end end influxdb-0.2.3/spec/influxdb/cases/query_continuous_query_spec.rb0000644000175600017570000000601512646433657024474 0ustar pravipravirequire "spec_helper" require "json" describe InfluxDB::Client do let(:subject) do described_class.new( "database", { host: "influxdb.test", port: 9999, username: "username", password: "password", time_precision: "s" }.merge(args) ) end let(:args) { {} } describe "#list_continuous_queries" do let(:query) { "SHOW CONTINUOUS QUERIES" } let(:database) { "testdb" } let(:response) do { "results" => [{ "series" => [{ "name" => "otherdb", "columns" => %w(name query), "values" => [["clicks_per_hour", "CREATE CONTINUOUS QUERY clicks_per_hour ON otherdb BEGIN SELECT count(name) INTO \"otherdb\".\"default\".clicksCount_1h FROM \"otherdb\".\"default\".clicks GROUP BY time(1h) END"]] }, { "name" => "testdb", "columns" => %w(name query), "values" => [["event_counts", "CREATE CONTINUOUS QUERY event_counts ON testdb BEGIN SELECT count(type) INTO \"testdb\".\"default\".typeCount_10m_byType FROM \"testdb\".\"default\".events GROUP BY time(10m), type END"]] }] }] } end let(:expected_result) do [{ "name" => "event_counts", "query" => "CREATE CONTINUOUS QUERY event_counts ON testdb BEGIN SELECT count(type) INTO \"testdb\".\"default\".typeCount_10m_byType FROM \"testdb\".\"default\".events GROUP BY time(10m), type END" }] end before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: query } ).to_return(body: JSON.generate(response), status: 200) end it "should GET a list of continuous queries for specified db only" do expect(subject.list_continuous_queries(database)).to eq(expected_result) end end describe "#create_continuous_query" do let(:name) { "event_counts_per_10m_by_type" } let(:database) { "testdb" } let(:query) do "SELECT COUNT(type) INTO typeCount_10m_byType FROM events GROUP BY time(10m), type" end let(:clause) do ["CREATE CONTINUOUS QUERY #{name} ON #{database} BEGIN", query, "END"].join("\n") end before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: clause } ) end it "should GET to create a new continuous query" do expect(subject.create_continuous_query(name, database, query)).to be_a(Net::HTTPOK) end end describe "#delete_continuous_query" do let(:name) { "event_counts_per_10m_by_type" } let(:database) { "testdb" } let(:query) { "DROP CONTINUOUS QUERY #{name} ON #{database}" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: query } ) end it "should GET to remove continuous query" do expect(subject.delete_continuous_query(name, database)).to be_a(Net::HTTPOK) end end end influxdb-0.2.3/spec/influxdb/cases/write_points_spec.rb0000644000175600017570000001104012646433657022334 0ustar pravipravirequire "spec_helper" require "json" describe InfluxDB::Client do let(:subject) do described_class.new( "database", { host: "influxdb.test", port: 9999, username: "username", password: "password", time_precision: "s" }.merge(args) ) end let(:args) { {} } let(:database) { subject.config.database } describe "#write_point" do let(:series) { "cpu" } let(:data) do { tags: { region: 'us', host: 'server_1' }, values: { temp: 88, value: 54 } } end let(:body) do InfluxDB::PointValue.new(data.merge(series: series)).dump end before do stub_request(:post, "http://influxdb.test:9999/write").with( query: { u: "username", p: "password", precision: 's', db: database }, headers: { "Content-Type" => "application/octet-stream" }, body: body ) end it "should POST to add single point" do expect(subject.write_point(series, data)).to be_a(Net::HTTPOK) end it "should not mutate data object" do original_data = data subject.write_point(series, data) expect(data[:series]).to be_nil expect(original_data).to eql(data) end end describe "#write_points" do context "with multiple series" do let(:data) do [{ series: 'cpu', tags: { region: 'us', host: 'server_1' }, values: { temp: 88, value: 54 } }, { series: 'gpu', tags: { region: 'uk', host: 'server_5' }, values: { value: 0.5435345 } }] end let(:body) do data.map do |point| InfluxDB::PointValue.new(point).dump end.join("\n") end before do stub_request(:post, "http://influxdb.test:9999/write").with( query: { u: "username", p: "password", precision: 's', db: database }, headers: { "Content-Type" => "application/octet-stream" }, body: body ) end it "should POST multiple points" do expect(subject.write_points(data)).to be_a(Net::HTTPOK) end end context "with no tags" do let(:data) do [{ series: 'cpu', values: { temp: 88, value: 54 } }, { series: 'gpu', values: { value: 0.5435345 } }] end let(:body) do data.map do |point| InfluxDB::PointValue.new(point).dump end.join("\n") end before do stub_request(:post, "http://influxdb.test:9999/write").with( query: { u: "username", p: "password", precision: 's', db: database }, headers: { "Content-Type" => "application/octet-stream" }, body: body ) end it "should POST multiple points" do expect(subject.write_points(data)).to be_a(Net::HTTPOK) end end context "with time precision set to milisceconds" do let(:data) do [{ series: 'cpu', values: { temp: 88, value: 54 }, timestamp: (Time.now.to_f * 1000).to_i }, { series: 'gpu', values: { value: 0.5435345 }, timestamp: (Time.now.to_f * 1000).to_i }] end let(:body) do data.map do |point| InfluxDB::PointValue.new(point).dump end.join("\n") end before do stub_request(:post, "http://influxdb.test:9999/write").with( query: { u: "username", p: "password", precision: 'm', db: database }, headers: { "Content-Type" => "application/octet-stream" }, body: body ) end it "should POST multiple points" do expect(subject.write_points(data, 'm')).to be_a(Net::HTTPOK) end end context "with retention policy" do let(:data) do [{ series: 'cpu', values: { temp: 88, value: 54 } }, { series: 'gpu', values: { value: 0.5435345 } }] end let(:body) do data.map do |point| InfluxDB::PointValue.new(point).dump end.join("\n") end before do stub_request(:post, "http://influxdb.test:9999/write").with( query: { u: "username", p: "password", precision: 's', db: database, rp: 'rp_1_hour' }, headers: { "Content-Type" => "application/octet-stream" }, body: body ) end it "should POST multiple points" do expect(subject.write_points(data, nil, 'rp_1_hour')).to be_a(Net::HTTPOK) end end end end influxdb-0.2.3/spec/influxdb/cases/querying_spec.rb0000644000175600017570000002263112646433657021461 0ustar pravipravirequire "spec_helper" require "json" describe InfluxDB::Client do let(:subject) do described_class.new( "database", { host: "influxdb.test", port: 9999, username: "username", password: "password", time_precision: "s" }.merge(args) ) end let(:args) { {} } let(:database) { subject.config.database } describe "#query" do context "with single series with multiple points" do let(:response) do { "results" => [{ "series" => [{ "name" => "cpu", "tags" => { "region" => "us" }, "columns" => %w(time temp value), "values" => [["2015-07-07T14:58:37Z", 92, 0.3445], ["2015-07-07T14:59:09Z", 68, 0.8787]] }] }] } end let(:expected_result) do [{ "name" => "cpu", "tags" => { "region" => "us" }, "values" => [{ "time" => "2015-07-07T14:58:37Z", "temp" => 92, "value" => 0.3445 }, { "time" => "2015-07-07T14:59:09Z", "temp" => 68, "value" => 0.8787 }] }] end let(:query) { 'SELECT * FROM cpu' } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { q: query, u: "username", p: "password", precision: 's', db: database } ).to_return(body: JSON.generate(response)) end it "should return array with single hash containing multiple values" do expect(subject.query(query)).to eq(expected_result) end end context "with series with different tags" do let(:response) do { "results" => [{ "series" => [{ "name" => "cpu", "tags" => { "region" => "pl" }, "columns" => %w(time temp value), "values" => [["2015-07-07T15:13:04Z", 34, 0.343443]] }, { "name" => "cpu", "tags" => { "region" => "us" }, "columns" => %w(time temp value), "values" => [["2015-07-07T14:58:37Z", 92, 0.3445], ["2015-07-07T14:59:09Z", 68, 0.8787]] }] }] } end let(:expected_result) do [{ "name" => "cpu", "tags" => { "region" => "pl" }, "values" => [{ "time" => "2015-07-07T15:13:04Z", "temp" => 34, "value" => 0.343443 }] }, { "name" => "cpu", "tags" => { "region" => "us" }, "values" => [{ "time" => "2015-07-07T14:58:37Z", "temp" => 92, "value" => 0.3445 }, { "time" => "2015-07-07T14:59:09Z", "temp" => 68, "value" => 0.8787 }] }] end let(:query) { 'SELECT * FROM cpu' } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { q: query, u: "username", p: "password", precision: 's', db: database } ).to_return(body: JSON.generate(response)) end it "should return array with 2 elements grouped by tags" do expect(subject.query(query)).to eq(expected_result) end end context "with multiple series with different tags" do let(:response) do { "results" => [{ "series" => [{ "name" => "access_times.service_1", "tags" => { "code" => "200", "result" => "failure", "status" => "OK" }, "columns" => %w(time value), "values" => [["2015-07-08T07:15:22Z", 327]] }, { "name" => "access_times.service_1", "tags" => { "code" => "500", "result" => "failure", "status" => "Internal Server Error" }, "columns" => %w(time value), "values" => [["2015-07-08T06:15:22Z", 873]] }, { "name" => "access_times.service_2", "tags" => { "code" => "200", "result" => "failure", "status" => "OK" }, "columns" => %w(time value), "values" => [["2015-07-08T07:15:22Z", 943]] }, { "name" => "access_times.service_2", "tags" => { "code" => "500", "result" => "failure", "status" => "Internal Server Error" }, "columns" => %w(time value), "values" => [["2015-07-08T06:15:22Z", 606]] }] }] } end let(:expected_result) do [{ "name" => "access_times.service_1", "tags" => { "code" => "200", "result" => "failure", "status" => "OK" }, "values" => [{ "time" => "2015-07-08T07:15:22Z", "value" => 327 }] }, { "name" => "access_times.service_1", "tags" => { "code" => "500", "result" => "failure", "status" => "Internal Server Error" }, "values" => [{ "time" => "2015-07-08T06:15:22Z", "value" => 873 }] }, { "name" => "access_times.service_2", "tags" => { "code" => "200", "result" => "failure", "status" => "OK" }, "values" => [{ "time" => "2015-07-08T07:15:22Z", "value" => 943 }] }, { "name" => "access_times.service_2", "tags" => { "code" => "500", "result" => "failure", "status" => "Internal Server Error" }, "values" => [{ "time" => "2015-07-08T06:15:22Z", "value" => 606 }] }] end let(:query) { "SELECT * FROM /access_times.*/" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { q: query, u: "username", p: "password", precision: 's', db: database } ).to_return(body: JSON.generate(response)) end it "should return array with 4 elements grouped by name and tags" do expect(subject.query(query)).to eq(expected_result) end end context "with multiple series for explicit value only" do let(:response) do { "results" => [{ "series" => [{ "name" => "access_times.service_1", "columns" => %w(time value), "values" => [["2015-07-08T06:15:22Z", 873], ["2015-07-08T07:15:22Z", 327]] }, { "name" => "access_times.service_2", "columns" => %w(time value), "values" => [["2015-07-08T06:15:22Z", 606], ["2015-07-08T07:15:22Z", 943]] }] }] } end let(:expected_result) do [{ "name" => "access_times.service_1", "tags" => nil, "values" => [{ "time" => "2015-07-08T06:15:22Z", "value" => 873 }, { "time" => "2015-07-08T07:15:22Z", "value" => 327 }] }, { "name" => "access_times.service_2", "tags" => nil, "values" => [{ "time" => "2015-07-08T06:15:22Z", "value" => 606 }, { "time" => "2015-07-08T07:15:22Z", "value" => 943 }] }] end let(:query) { "SELECT value FROM /access_times.*/" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { q: query, u: "username", p: "password", precision: 's', db: database } ).to_return(body: JSON.generate(response)) end it "should return array with 2 elements grouped by name only and no tags" do expect(subject.query(query)).to eq(expected_result) end end context "with a block" do let(:response) do { "results" => [{ "series" => [{ "name" => "cpu", "tags" => { "region" => "pl" }, "columns" => %w(time temp value), "values" => [["2015-07-07T15:13:04Z", 34, 0.343443]] }, { "name" => "cpu", "tags" => { "region" => "us" }, "columns" => %w(time temp value), "values" => [["2015-07-07T14:58:37Z", 92, 0.3445], ["2015-07-07T14:59:09Z", 68, 0.8787]] }] }] } end let(:expected_result) do [{ "name" => "cpu", "tags" => { "region" => "pl" }, "values" => [{ "time" => "2015-07-07T15:13:04Z", "temp" => 34, "value" => 0.343443 }] }, { "name" => "cpu", "tags" => { "region" => "us" }, "values" => [{ "time" => "2015-07-07T14:58:37Z", "temp" => 92, "value" => 0.3445 }, { "time" => "2015-07-07T14:59:09Z", "temp" => 68, "value" => 0.8787 }] }] end let(:query) { 'SELECT * FROM cpu' } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { q: query, u: "username", p: "password", precision: 's', db: database } ).to_return(body: JSON.generate(response)) end it "should accept a block and yield name, tags and points" do results = [] subject.query(query) do |name, tags, points| results << { 'name' => name, 'tags' => tags, 'values' => points } end expect(results).to eq(expected_result) end end context "with epoch set to seconds" do let(:args) { {epoch: 's'} } let(:response) do { "results" => [{ "series" => [{ "name" => "cpu", "tags" => { "region" => "pl" }, "columns" => %w(time temp value), "values" => [[1438580576, 34, 0.343443]] }, { "name" => "cpu", "tags" => { "region" => "us" }, "columns" => %w(time temp value), "values" => [[1438612976, 92, 0.3445], [1438612989, 68, 0.8787]] } ] }] } end let(:expected_result) do [{ "name" => "cpu", "tags" => { "region" => "pl" }, "values" => [{ "time" => 1438580576, "temp" => 34, "value" => 0.343443 }] }, { "name" => "cpu", "tags" => { "region" => "us" }, "values" => [{ "time" => 1438612976, "temp" => 92, "value" => 0.3445 }, { "time" => 1438612989, "temp" => 68, "value" => 0.8787 }] }] end let(:query) { 'SELECT * FROM cpu' } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { q: query, u: "username", p: "password", precision: 's', db: database, epoch: 's' } ).to_return(body: JSON.generate(response)) end it "should return results with integer timestamp" do expect(subject.query(query)).to eq(expected_result) end end end end influxdb-0.2.3/spec/influxdb/cases/query_user_spec.rb0000644000175600017570000001114612646433657022020 0ustar pravipravirequire "spec_helper" require "json" describe InfluxDB::Client do let(:subject) do described_class.new( "database", { host: "influxdb.test", port: 9999, username: "username", password: "password", time_precision: "s" }.merge(args) ) end let(:args) { {} } describe "#update user password" do let(:user) { 'useruser' } let(:pass) { 'passpass' } let(:query) { "SET PASSWORD FOR #{user} = '#{pass}'" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: query } ) end it "should GET to update user password" do expect(subject.update_user_password(user, pass)).to be_a(Net::HTTPOK) end end describe "#grant_user_privileges" do let(:user) { 'useruser' } let(:perm) { :write } let(:db) { 'foo' } let(:query) { "GRANT #{perm.to_s.upcase} ON #{db} TO #{user}" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: query } ) end it "should GET to grant privileges for a user on a database" do expect(subject.grant_user_privileges(user, db, perm)).to be_a(Net::HTTPOK) end end describe "#grant_user_admin_privileges" do let(:user) { 'useruser' } let(:query) { "GRANT ALL PRIVILEGES TO #{user}" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: query } ) end it "should GET to grant privileges for a user on a database" do expect(subject.grant_user_admin_privileges(user)).to be_a(Net::HTTPOK) end end describe "#revoke_user_privileges" do let(:user) { 'useruser' } let(:perm) { :write } let(:db) { 'foo' } let(:query) { "REVOKE #{perm.to_s.upcase} ON #{db} FROM #{user}" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: query } ) end it "should GET to revoke privileges from a user on a database" do expect(subject.revoke_user_privileges(user, db, perm)).to be_a(Net::HTTPOK) end end describe "#create_database_user" do let(:user) { 'useruser' } let(:pass) { 'passpass' } let(:db) { 'foo' } let(:query) { "CREATE user #{user} WITH PASSWORD '#{pass}'; GRANT ALL ON #{db} TO #{user}" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: query } ) end context "without specifying permissions" do it "should GET to create a new database user with all permissions" do expect(subject.create_database_user(db, user, pass)).to be_a(Net::HTTPOK) end end context "with passing permission as argument" do let(:permission) { :read } let(:query) { "CREATE user #{user} WITH PASSWORD '#{pass}'; GRANT #{permission.to_s.upcase} ON #{db} TO #{user}" } it "should GET to create a new database user with permission set" do expect(subject.create_database_user(db, user, pass, permissions: permission)).to be_a(Net::HTTPOK) end end end describe "#delete_user" do let(:user) { 'useruser' } let(:query) { "DROP USER #{user}" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: query } ) end it "should GET to delete a user" do expect(subject.delete_user(user)).to be_a(Net::HTTPOK) end end describe "#list_users" do let(:response) { { "results" => [{ "series" => [{ "columns" => %w(user admin), "values" => [["dbadmin", true], ["foobar", false]] }] }] } } let(:expected_result) { [{ "username" => "dbadmin", "admin" => true }, { "username" => "foobar", "admin" => false }] } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: "SHOW USERS" } ).to_return(body: JSON.generate(response, status: 200)) end it "should GET a list of database users" do expect(subject.list_users).to eq(expected_result) end end describe "#list_user_grants" do let(:user) { 'useruser' } let(:list_query) { "SHOW GRANTS FOR #{user}" } before do stub_request(:get, "http://influxdb.test:9999/query").with( query: { u: "username", p: "password", q: list_query}, ).to_return(:status => 200, :body => "", :headers => {}) end it "should GET for a user" do expect(subject.list_user_grants(user)).to be_a(Net::HTTPOK) end end end influxdb-0.2.3/spec/influxdb/point_value_spec.rb0000644000175600017570000000602712646433657021046 0ustar pravipravirequire "spec_helper" describe InfluxDB::PointValue do describe "whitespace escaping" do it 'should escape series name' do point = InfluxDB::PointValue.new(series: "Some Long String", values: { value: 5 }) expect(point.series).to eq("Some\\ Long\\ String") end it 'should escape keys of passed value keys' do point = InfluxDB::PointValue.new(series: "responses", values: { 'some string key' => 5 }) expect(point.values.split("=").first).to eq("some\\ string\\ key") end it 'should escape passed values' do point = InfluxDB::PointValue.new(series: "responses", values: { response_time: 0.34343 }, tags: { city: "Twin Peaks" }) expect(point.tags.split("=").last).to eq("Twin\\ Peaks") end end describe "comma escaping" do it 'should escape series name' do point = InfluxDB::PointValue.new(series: "Some Long String,", values: { value: 5 }) expect(point.series).to eq("Some\\ Long\\ String\\,") end it 'should escape keys of passed value keys' do point = InfluxDB::PointValue.new(series: "responses", values: { 'some string key,' => 5 }) expect(point.values.split("=").first).to eq("some\\ string\\ key\\,") end it 'should escape passed values' do point = InfluxDB::PointValue.new(series: "responses", values: { response_time: 0.34343 }, tags: { city: "Twin Peaks," }) expect(point.tags.split("=").last).to eq("Twin\\ Peaks\\,") end end describe 'dump' do context "with all possible data passed" do let(:expected_value) do 'responses,region=eu,status=200 value=5,threshold=0.54 1436349652' end it 'should have proper form' do point = InfluxDB::PointValue.new(series: "responses", values: { value: 5, threshold: 0.54 }, tags: { region: 'eu', status: 200 }, timestamp: 1_436_349_652) expect(point.dump).to eq(expected_value) end end context "with no tags" do let(:expected_value) do "responses value=5,threshold=0.54 1436349652" end it 'should have proper form' do point = InfluxDB::PointValue.new(series: "responses", values: { value: 5, threshold: 0.54 }, timestamp: 1_436_349_652) expect(point.dump).to eq(expected_value) end end context "with values only" do let(:expected_value) do "responses value=5,threshold=0.54" end it 'should have proper form' do point = InfluxDB::PointValue.new(series: "responses", values: { value: 5, threshold: 0.54 }) expect(point.dump).to eq(expected_value) end end end end influxdb-0.2.3/spec/influxdb/config_spec.rb0000644000175600017570000000743712646433657017774 0ustar pravipravirequire 'spec_helper' describe InfluxDB::Config do let(:conf) do InfluxDB::Client.new(*args).config end let(:args) { {} } context "with no parameters specified" do specify { expect(conf.database).to be_nil } specify { expect(conf.hosts).to eq ["localhost"] } specify { expect(conf.port).to eq 8086 } specify { expect(conf.username).to eq "root" } specify { expect(conf.password).to eq "root" } specify { expect(conf.use_ssl).to be_falsey } specify { expect(conf.time_precision).to eq "s" } specify { expect(conf.auth_method).to eq "params" } specify { expect(conf.denormalize).to be_truthy } specify { expect(conf).not_to be_udp } specify { expect(conf).not_to be_async } specify { expect(conf.epoch).to be_falsey } end context "with no database specified" do let(:args) do [{ host: "host", port: "port", username: "username", password: "password", time_precision: "m" }] end specify { expect(conf.database).to be_nil } specify { expect(conf.hosts).to eq ["host"] } specify { expect(conf.port).to eq "port" } specify { expect(conf.username).to eq "username" } specify { expect(conf.password).to eq "password" } specify { expect(conf.time_precision).to eq "m" } specify { expect(conf.epoch).to be_falsey } end context "with both a database and options specified" do let(:args) do [ "database", host: "host", port: "port", username: "username", password: "password", time_precision: "m" ] end specify { expect(conf.database).to eq "database" } specify { expect(conf.hosts).to eq ["host"] } specify { expect(conf.port).to eq "port" } specify { expect(conf.username).to eq "username" } specify { expect(conf.password).to eq "password" } specify { expect(conf.time_precision).to eq "m" } specify { expect(conf.epoch).to be_falsey } end context "with ssl option specified" do let(:args) { [{ use_ssl: true }] } specify { expect(conf.database).to be_nil } specify { expect(conf.hosts).to eq ["localhost"] } specify { expect(conf.port).to eq 8086 } specify { expect(conf.username).to eq "root" } specify { expect(conf.password).to eq "root" } specify { expect(conf.use_ssl).to be_truthy } end context "with multiple hosts specified" do let(:args) { [{ hosts: ["1.1.1.1", "2.2.2.2"] }] } specify { expect(conf.database).to be_nil } specify { expect(conf.port).to eq 8086 } specify { expect(conf.username).to eq "root" } specify { expect(conf.password).to eq "root" } specify { expect(conf.hosts).to eq ["1.1.1.1", "2.2.2.2"] } end context "with auth_method basic auth specified" do let(:args) { [{ auth_method: 'basic_auth' }] } specify { expect(conf.database).to be_nil } specify { expect(conf.hosts).to eq ["localhost"] } specify { expect(conf.port).to eq 8086 } specify { expect(conf.username).to eq "root" } specify { expect(conf.password).to eq "root" } specify { expect(conf.auth_method).to eq "basic_auth" } end context "with udp specified with params" do let(:args) { [{ udp: { host: 'localhost', port: 4444 } }] } specify { expect(conf).to be_udp } end context "with udp specified as true" do let(:args) { [{ udp: true }] } specify { expect(conf).to be_udp } end context "with async specified with params" do let(:args) { [{ async: { max_queue: 20_000 } }] } specify { expect(conf).to be_async } end context "with async specified as true" do let(:args) { [{ async: true }] } specify { expect(conf).to be_async } end context "with epoch specified as seconds" do let(:args) { [{ epoch: 's' }] } specify { expect(conf.epoch).to eq 's' } end end influxdb-0.2.3/spec/influxdb/logging_spec.rb0000644000175600017570000000227212646433657020145 0ustar pravipravirequire 'spec_helper' require 'logger' describe InfluxDB::Logging do class LoggerTest # :nodoc: include InfluxDB::Logging def write_to_log(level, message) log(level, message) end end before { @old_logger = InfluxDB::Logging.logger } after { InfluxDB::Logging.logger = @old_logger } it "has a default logger" do expect(InfluxDB::Logging.logger).to be_a(Logger) end it "allows setting of a logger" do new_logger = Logger.new(STDOUT) InfluxDB::Logging.logger = new_logger expect(InfluxDB::Logging.logger).to eq(new_logger) end it "allows disabling of a logger" do InfluxDB::Logging.logger = false expect(InfluxDB::Logging.logger).to eql false end context "when logging is disabled" do subject { LoggerTest.new } it "does not log" do InfluxDB::Logging.logger = false expect(InfluxDB::Logging.logger).not_to receive(:debug) subject.write_to_log(:debug, 'test') end end context "when included in classes" do subject { LoggerTest.new } it "logs" do expect(InfluxDB::Logging.logger).to receive(:debug).with(an_instance_of(String)).once subject.write_to_log(:debug, 'test') end end end influxdb-0.2.3/spec/influxdb/worker_spec.rb0000644000175600017570000000136312646433657020030 0ustar pravipravirequire "spec_helper" require 'timeout' describe InfluxDB::Writer::Async::Worker do let(:fake_client) { double(stopped?: false) } let(:worker) { described_class.new(fake_client, {}) } describe "#push" do let(:payload1) { "responses,region=eu value=5" } let(:payload2) { "responses,region=eu value=6" } let(:aggregate) { "#{payload1}\n#{payload2}" } it "writes aggregate payload to the client" do queue = Queue.new allow(fake_client).to receive(:write) do |data, _precision| queue.push(data) end worker.push(payload1) worker.push(payload2) Timeout.timeout(described_class::SLEEP_INTERVAL) do result = queue.pop expect(result).to eq aggregate end end end end influxdb-0.2.3/spec/influxdb/max_queue_spec.rb0000644000175600017570000000126312646433657020507 0ustar pravipravirequire 'spec_helper' describe InfluxDB::MaxQueue do specify { is_expected.to be_a(Queue) } context "#new" do it "allows max_depth to be set" do expect(described_class.new(500).max).to eq 500 end end context "#push" do let(:queue) { described_class.new(5) } it "allows an item to be added if the queue is not full" do expect(queue.size).to be_zero queue.push(1) expect(queue.size).to eq 1 end it "doesn't allow items to be added if the queue is full" do expect(queue.size).to be_zero 5.times { |n| queue.push(n) } expect(queue.size).to eq 5 queue.push(6) expect(queue.size).to eq 5 end end end influxdb-0.2.3/spec/influxdb/client_spec.rb0000644000175600017570000000307312646433657017775 0ustar pravipravirequire "spec_helper" require "json" describe InfluxDB::Client do let(:subject) do described_class.new( "database", { host: "influxdb.test", port: 9999, username: "username", password: "password", time_precision: "s" }.merge(args) ) end let(:args) { {} } specify { is_expected.not_to be_stopped } context "with basic auth" do let(:args) { { auth_method: 'basic_auth' } } let(:stub_url) { "http://username:password@influxdb.test:9999/" } let(:url) { subject.send(:full_url, '/') } it "GET" do stub_request(:get, stub_url).to_return(body: '[]') expect(subject.get(url, parse: true)).to eq [] end it "POST" do stub_request(:post, stub_url) expect(subject.post(url, {})).to be_a(Net::HTTPOK) end end describe "#full_url" do it "returns String" do expect(subject.send(:full_url, "/unknown")).to be_a String end it "escapes params" do url = subject.send(:full_url, "/unknown", value: ' !@#$%^&*()/\\_+-=?|`~') expect(url).to include("value=+%21%40%23%24%25%5E%26%2A%28%29%2F%5C_%2B-%3D%3F%7C%60%7E") end context "with prefix" do let(:args) { { prefix: '/dev' } } it "returns path with prefix" do expect(subject.send(:full_url, "/series")).to start_with("/dev") end end end describe "GET #ping" do it "returns OK" do stub_request(:get, "http://influxdb.test:9999/ping") .to_return(status: 204) expect(subject.ping).to be_a(Net::HTTPNoContent) end end end influxdb-0.2.3/LICENSE.txt0000644000175600017570000000205412646433657014234 0ustar pravipraviCopyright (c) 2013 Todd Persen MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. influxdb-0.2.3/.rubocop.yml0000644000175600017570000000114412646433657014662 0ustar pravipraviAllCops: # Include gemspec and Rakefile Include: - 'lib/**/*.rb' - 'lib/**/*.rake' - 'spec/**/*.rb' Exclude: - 'bin/**/*' RunRailsCops: false DisplayCopNames: true StyleGuideCopsOnly: false Style/Documentation: Exclude: - 'spec/**/*.rb' Style/StringLiterals: Enabled: false Style/BlockDelimiters: Exclude: - 'spec/**/*.rb' Style/RescueModifier: Enabled: false Lint/HandleExceptions: Enabled: false Lint/Void: Exclude: - 'spec/**/*.rb' Metrics/LineLength: Max: 100 Exclude: - 'spec/**/*.rb' Metrics/MethodLength: Exclude: - 'spec/**/*.rb'influxdb-0.2.3/.travis.yml0000644000175600017570000000023012646433657014514 0ustar pravipravilanguage: ruby before_install: - gem update bundler rvm: - 2.0.0 - 2.1.0 - 2.2.0 - ruby-head - jruby-20mode - jruby-21mode - jruby-head influxdb-0.2.3/README.md0000644000175600017570000002670312646433657013677 0ustar pravipraviinfluxdb-ruby ============= [![Build Status](https://travis-ci.org/influxdb/influxdb-ruby.png?branch=master)](https://travis-ci.org/influxdb/influxdb-ruby) The official ruby client library for [InfluxDB](https://influxdb.com/). > **Support for InfluxDB v0.8.x is now deprecated**. The final version of this library that will support the older InfluxDB interface is `v0.1.9`, which is available as a gem and tagged on this repository. If you're reading this message, then you should only expect support for InfluxDB v0.9.1 and higher. Install ------- ``` $ [sudo] gem install influxdb ``` Or add it to your `Gemfile`, etc. Usage ----- Connecting to a single host: ``` ruby require 'influxdb' influxdb = InfluxDB::Client.new host: "influxdb.domain.com" # or influxdb = InfluxDB::Client.new # no host given defaults connecting to localhost ``` Connecting to multiple hosts (with built-in load balancing and failover): ``` ruby require 'influxdb' influxdb = InfluxDB::Client.new hosts: ["influxdb1.domain.com", "influxdb2.domain.com"] ``` Create a database: ``` ruby database = 'site_development' influxdb.create_database(database) ``` Delete a database: ``` ruby database = 'site_development' influxdb.delete_database(database) ``` List databases: ``` ruby influxdb.list_databases ``` Create a user for a database: ``` ruby database = 'site_development' new_username = 'foo' new_password = 'bar' permission = :write # with all permissions influxdb.create_database_user(database, new_username, new_password) # with specified permission - options are: :read, :write, :all influxdb.create_database_user(database, new_username, new_password, permissions: permission) ``` Update a user password: ``` ruby username = 'foo' new_password = 'bar' influxdb.update_user_password(username, new_password) ``` Grant user privileges on database: ``` ruby username = 'foobar' database = 'foo' permission = :read # options are :read, :write, :all influxdb.grant_user_privileges(username, database, permission) ``` Revoke user privileges from database: ``` ruby username = 'foobar' database = 'foo' permission = :write # options are :read, :write, :all influxdb.revoke_user_privileges(username, database, permission) ``` Delete a user: ``` ruby username = 'foobar' influxdb.delete_user(username) ``` List users: ``` ruby influxdb.list_users ``` Create cluster admin: ``` ruby username = 'foobar' password = 'pwd' influxdb.create_cluster_admin(username, password) ``` List cluster admins: ``` ruby influxdb.list_cluster_admins ``` Revoke cluster admin privileges from user: ``` ruby username = 'foobar' influxdb.revoke_cluster_admin_privileges(username) ``` List continuous queries of a database: ``` ruby database = 'foo' influxdb.list_continuous_queries(database) ``` Create a continuous query for a database: ``` ruby database = 'foo' name = 'clicks_count' query = 'SELECT COUNT(name) INTO clicksCount_1h FROM clicks GROUP BY time(1h)' influxdb.create_continuous_query(name, database, query) ``` Delete a continuous query from a database: ``` ruby database = 'foo' name = 'clicks_count' influxdb.delete_continuous_query(name, database) ``` List retention policies of a database: ``` ruby database = 'foo' influxdb.list_retention_policies(database) ``` Create a retention policy for a database: ``` ruby database = 'foo' name = '1h.cpu' duration = '10m' replication = 2 influxdb.create_retention_policy(name, database, duration, replication) ``` Delete a retention policy from a database: ``` ruby database = 'foo' name = '1h.cpu' influxdb.delete_retention_policy(name, database) ``` Alter a retention policy for a database: ``` ruby database = 'foo' name = '1h.cpu' duration = '10m' replication = 2 influxdb.alter_retention_policy(name, database, duration, replication) ``` Write some data: ``` ruby username = 'foo' password = 'bar' database = 'site_development' name = 'foobar' influxdb = InfluxDB::Client.new database, username: username, password: password # Enumerator that emits a sine wave Value = (0..360).to_a.map {|i| Math.send(:sin, i / 10.0) * 10 }.each loop do data = { values: { value: Value.next }, tags: { wave: 'sine' } # tags are optional } influxdb.write_point(name, data) sleep 1 end ``` Write data with time precision (precision can be set in 2 ways): ``` ruby require 'influxdb' username = 'foo' password = 'bar' database = 'site_development' name = 'foobar' time_precision = 's' # either in the client initialization: influxdb = InfluxDB::Client.new database, username: username, password: password, time_precision: time_precision data = { values: { value: 0 }, timestamp: Time.now.to_i # timestamp is optional, if not provided point will be saved with current time } influxdb.write_point(name, data) # or in a method call: influxdb.write_point(name, data, time_precision) ``` Write data with a specific retention policy: ``` ruby require 'influxdb' username = 'foo' password = 'bar' database = 'site_development' name = 'foobar' precision = 's' retention = '1h.cpu' influxdb = InfluxDB::Client.new database, username: username, password: password data = { values: { value: 0 }, tags: { foo: 'bar', bar: 'baz' } timestamp: Time.now.to_i } influxdb.write_point(name, data, precision, retention) ``` Write multiple points in a batch (performance boost): ``` ruby data = [ { series: 'cpu', tags: { host: 'server_1', regios: 'us' }, values: {internal: 5, external: 0.453345} }, { series: 'gpu', values: {value: 0.9999}, } ] influxdb.write_points(data) # you can also specify precision in method call precision = 'm' influxdb.write_points(data, precision) ``` Write multiple points in a batch with a specific retention policy: ``` ruby data = [ { series: 'cpu', tags: { host: 'server_1', regios: 'us' }, values: {internal: 5, external: 0.453345} }, { series: 'gpu', values: {value: 0.9999}, } ] precision = 'm' retention = '1h.cpu' influxdb.write_points(data, precision, retention) ``` Write asynchronously (note that a retention policy cannot be specified for asynchronous writes): ``` ruby require 'influxdb' username = 'foo' password = 'bar' database = 'site_development' name = 'foobar' influxdb = InfluxDB::Client.new database, username: username, password: password, async: true data = { values: { value: 0 }, tags: { foo: 'bar', bar: 'baz' } timestamp: Time.now.to_i } influxdb.write_point(name, data) ``` Write data via UDP (note that a retention policy cannot be specified for UDP writes): ``` ruby require 'influxdb' host = '127.0.0.1' port = 4444 influxdb = InfluxDB::Client.new udp: { host: host, port: port } name = 'hitchhiker' data = { values: { value: 666 }, tags: { foo: 'bar', bar: 'baz' } } influxdb.write_point(name, data) ``` Querying: ``` ruby username = 'foo' password = 'bar' database = 'site_development' influxdb = InfluxDB::Client.new database, username: username, password: password # without a block: influxdb.query 'select * from time_series_1' # results are grouped by name, but also their tags # result: # [{"name"=>"time_series_1", "tags"=>{"region"=>"uk"}, "values"=>[{"time"=>"2015-07-09T09:03:31Z", "count"=>32, "value"=>0.9673}, {"time"=>"2015-07-09T09:03:49Z", "count"=>122, "value"=>0.4444}]}, # {"name"=>"time_series_1", "tags"=>{"region"=>"us"}, "values"=>[{"time"=>"2015-07-09T09:02:54Z", "count"=>55, "value"=>0.4343}]}] # with a block: influxdb.query 'select * from time_series_1' do |name, tags, points| puts "#{name} [ #{tags} ] => #{points}" end # result: # time_series_1 [ {"region"=>"uk"} ] => [{"time"=>"2015-07-09T09:03:31Z", "count"=>32, "value"=>0.9673}, {"time"=>"2015-07-09T09:03:49Z", "count"=>122, "value"=>0.4444}] # time_series_1 [ {"region"=>"us"} ] => [{"time"=>"2015-07-09T09:02:54Z", "count"=>55, "value"=>0.4343}] ``` If you would rather receive points with integer timestamp, it's possible to set `epoch` parameter: ``` ruby # globally, on client initialization: influxdb = InfluxDB::Client.new database, epoch: 's' influxdb.query 'select * from time_series' # result: # [{"name"=>"time_series", "tags"=>{"region"=>"uk"}, "values"=>[{"time"=>1438411376, "count"=>32, "value"=>0.9673}]}] # or for a specific query call: influxdb.query 'select * from time_series', epoch: 'ms' # result: # [{"name"=>"time_series", "tags"=>{"region"=>"uk"}, "values"=>[{"time"=>1438411376000, "count"=>32, "value"=>0.9673}]}] ``` By default, InfluxDB::Client will denormalize points (received from InfluxDB as columns and rows), if you want to get _raw_ data add `denormalize: false` to initialization options or to query itself: ``` ruby influxdb.query 'select * from time_series_1', denormalize: false # result [{"name"=>"time_series_1", "tags"=>{"region"=>"uk"}, "columns"=>["time", "count", "value"], "values"=>[["2015-07-09T09:03:31Z", 32, 0.9673], ["2015-07-09T09:03:49Z", 122, 0.4444]]}, {"name"=>"time_series_1", "tags"=>{"region"=>"us"}, "columns"=>["time", "count", "value"], "values"=>[["2015-07-09T09:02:54Z", 55, 0.4343]]}] influxdb.query 'select * from time_series_1', denormalize: false do |name, tags, points| puts "#{name} [ #{tags} ] => #{points}" end # result: # time_series_1 [ {"region"=>"uk"} ] => {"columns"=>["time", "count", "value"], "values"=>[["2015-07-09T09:03:31Z", 32, 0.9673], ["2015-07-09T09:03:49Z", 122, 0.4444]]} # time_series_1 [ {"region"=>"us"} ] => {"columns"=>["time", "count", "value"], "values"=>[["2015-07-09T09:02:54Z", 55, 0.4343]]} ``` By default, InfluxDB::Client will keep trying to connect to the database when it gets connection denied, if you want to retry a finite number of times (or disable retries altogether), you should pass the `:retry` value. `:retry` can be either `true`, `false` or an `Integer` to retry infinite times, disable retries or retry a finite number of times, respectively. `0` is equivalent to `false` ``` > require 'influxdb' => true > influxdb = InfluxDB::Client.new 'database', :retry => 4 => # > influxdb.query 'select * from serie limit 1' E, [2014-06-02T11:04:13.416209 #22825] ERROR -- : [InfluxDB] Failed to contact host localhost: # - retrying in 0.01s. E, [2014-06-02T11:04:13.433646 #22825] ERROR -- : [InfluxDB] Failed to contact host localhost: # - retrying in 0.02s. E, [2014-06-02T11:04:13.462566 #22825] ERROR -- : [InfluxDB] Failed to contact host localhost: # - retrying in 0.04s. E, [2014-06-02T11:04:13.510853 #22825] ERROR -- : [InfluxDB] Failed to contact host localhost: # - retrying in 0.08s. SocketError: Tried 4 times to reconnect but failed. ``` If you pass `:retry => -1` it will keep trying forever until it gets the connection. Testing ------- ``` git clone git@github.com:influxdb/influxdb-ruby.git cd influxdb-ruby bundle bundle exec rake ``` influxdb-0.2.3/Gemfile0000644000175600017570000000025012646433657013700 0ustar pravipravisource "https://rubygems.org" gemspec local_gemfile = 'Gemfile.local' if File.exist?(local_gemfile) eval(File.read(local_gemfile)) # rubocop:disable Lint/Eval end influxdb-0.2.3/metadata.yml0000644000175600017570000001234712646433657014722 0ustar pravipravi--- !ruby/object:Gem::Specification name: influxdb version: !ruby/object:Gem::Version version: 0.2.3 platform: ruby authors: - Todd Persen autorequire: bindir: bin cert_chain: [] date: 2015-10-27 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: json requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: cause requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: bundler requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.3' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.3' - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 3.0.0 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 3.0.0 - !ruby/object:Gem::Dependency name: webmock requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 1.21.0 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: 1.21.0 description: This is the official Ruby library for InfluxDB. email: - influxdb@googlegroups.com executables: [] extensions: [] extra_rdoc_files: [] files: - ".gitignore" - ".rubocop.yml" - ".travis.yml" - Gemfile - LICENSE.txt - README.md - Rakefile - influxdb.gemspec - lib/influxdb.rb - lib/influxdb/client.rb - lib/influxdb/client/http.rb - lib/influxdb/config.rb - lib/influxdb/errors.rb - lib/influxdb/logging.rb - lib/influxdb/max_queue.rb - lib/influxdb/point_value.rb - lib/influxdb/query/cluster.rb - lib/influxdb/query/continuous_query.rb - lib/influxdb/query/core.rb - lib/influxdb/query/database.rb - lib/influxdb/query/retention_policy.rb - lib/influxdb/query/user.rb - lib/influxdb/version.rb - lib/influxdb/writer/async.rb - lib/influxdb/writer/udp.rb - spec/influxdb/cases/async_client_spec.rb - spec/influxdb/cases/query_cluster_spec.rb - spec/influxdb/cases/query_continuous_query_spec.rb - spec/influxdb/cases/query_core.rb - spec/influxdb/cases/query_database_spec.rb - spec/influxdb/cases/query_retention_policy_spec.rb - spec/influxdb/cases/query_series_spec.rb - spec/influxdb/cases/query_shard_space_spec.rb - spec/influxdb/cases/query_shard_spec.rb - spec/influxdb/cases/query_user_spec.rb - spec/influxdb/cases/querying_spec.rb - spec/influxdb/cases/retry_requests_spec.rb - spec/influxdb/cases/udp_client_spec.rb - spec/influxdb/cases/write_points_spec.rb - spec/influxdb/client_spec.rb - spec/influxdb/config_spec.rb - spec/influxdb/logging_spec.rb - spec/influxdb/max_queue_spec.rb - spec/influxdb/point_value_spec.rb - spec/influxdb/worker_spec.rb - spec/spec_helper.rb homepage: http://influxdb.org licenses: - MIT metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.4.5 signing_key: specification_version: 4 summary: Ruby library for InfluxDB. test_files: - spec/influxdb/cases/async_client_spec.rb - spec/influxdb/cases/query_cluster_spec.rb - spec/influxdb/cases/query_continuous_query_spec.rb - spec/influxdb/cases/query_core.rb - spec/influxdb/cases/query_database_spec.rb - spec/influxdb/cases/query_retention_policy_spec.rb - spec/influxdb/cases/query_series_spec.rb - spec/influxdb/cases/query_shard_space_spec.rb - spec/influxdb/cases/query_shard_spec.rb - spec/influxdb/cases/query_user_spec.rb - spec/influxdb/cases/querying_spec.rb - spec/influxdb/cases/retry_requests_spec.rb - spec/influxdb/cases/udp_client_spec.rb - spec/influxdb/cases/write_points_spec.rb - spec/influxdb/client_spec.rb - spec/influxdb/config_spec.rb - spec/influxdb/logging_spec.rb - spec/influxdb/max_queue_spec.rb - spec/influxdb/point_value_spec.rb - spec/influxdb/worker_spec.rb - spec/spec_helper.rb influxdb-0.2.3/lib/0000755000175600017570000000000012646433657013156 5ustar pravipraviinfluxdb-0.2.3/lib/influxdb.rb0000644000175600017570000000076312646433657015324 0ustar pravipravirequire "influxdb/version" require "influxdb/errors" require "influxdb/logging" require "influxdb/max_queue" require "influxdb/point_value" require "influxdb/config" require "influxdb/writer/async" require "influxdb/writer/udp" require "influxdb/query/core" require "influxdb/query/cluster" require "influxdb/query/database" require "influxdb/query/user" require "influxdb/query/continuous_query" require "influxdb/query/retention_policy" require "influxdb/client/http" require "influxdb/client" influxdb-0.2.3/lib/influxdb/0000755000175600017570000000000012646433657014771 5ustar pravipraviinfluxdb-0.2.3/lib/influxdb/errors.rb0000644000175600017570000000140512646433657016632 0ustar pravipravirequire "net/http" require "zlib" module InfluxDB # :nodoc: class Error < StandardError end class AuthenticationError < Error end class ConnectionError < Error end class SeriesNotFound < Error end class JSONParserError < Error end class QueryError < Error end # Taken from: https://github.com/lostisland/faraday/blob/master/lib/faraday/adapter/net_http.rb NET_HTTP_EXCEPTIONS = [ EOFError, Errno::ECONNABORTED, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH, Errno::EINVAL, Errno::ENETUNREACH, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, SocketError, Zlib::GzipFile::Error ] NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL) end influxdb-0.2.3/lib/influxdb/query/0000755000175600017570000000000012646433657016136 5ustar pravipraviinfluxdb-0.2.3/lib/influxdb/query/retention_policy.rb0000644000175600017570000000215712646433657022056 0ustar pravipravimodule InfluxDB module Query module RetentionPolicy # :nodoc: def create_retention_policy(name, database, duration, replication, default = false) execute( "CREATE RETENTION POLICY \"#{name}\" ON #{database} "\ "DURATION #{duration} REPLICATION #{replication}#{default ? ' DEFAULT' : ''}") end def list_retention_policies(database) resp = execute("SHOW RETENTION POLICIES ON \"#{database}\"", parse: true) data = fetch_series(resp).fetch(0) data['values'].map do |policy| policy.each.with_index.inject({}) do |hash, (value, index)| hash.tap { |h| h[data['columns'][index]] = value } end end end def delete_retention_policy(name, database) execute("DROP RETENTION POLICY \"#{name}\" ON #{database}") end def alter_retention_policy(name, database, duration, replication, default = false) execute( "ALTER RETENTION POLICY \"#{name}\" ON #{database} "\ "DURATION #{duration} REPLICATION #{replication}#{default ? ' DEFAULT' : ''}") end end end end influxdb-0.2.3/lib/influxdb/query/cluster.rb0000644000175600017570000000071712646433657020151 0ustar pravipravimodule InfluxDB module Query module Cluster # :nodoc: def create_cluster_admin(username, password) execute("CREATE USER #{username} WITH PASSWORD '#{password}' WITH ALL PRIVILEGES") end def list_cluster_admins list_users.select { |u| u['admin'] }.map { |u| u['username'] } end def revoke_cluster_admin_privileges(username) execute("REVOKE ALL PRIVILEGES FROM #{username}") end end end end influxdb-0.2.3/lib/influxdb/query/continuous_query.rb0000644000175600017570000000127612646433657022124 0ustar pravipravimodule InfluxDB module Query module ContinuousQuery # :nodoc: def list_continuous_queries(database) resp = execute("SHOW CONTINUOUS QUERIES", parse: true) fetch_series(resp).select { |v| v['name'] == database } .fetch(0, {}) .fetch('values', []) .map { |v| { 'name' => v.first, 'query' => v.last } } end def create_continuous_query(name, database, query) clause = ["CREATE CONTINUOUS QUERY #{name} ON #{database} BEGIN", query, "END"].join("\n") execute(clause) end def delete_continuous_query(name, database) execute("DROP CONTINUOUS QUERY #{name} ON #{database}") end end end end influxdb-0.2.3/lib/influxdb/query/user.rb0000644000175600017570000000331012646433657017436 0ustar pravipravimodule InfluxDB module Query module User # :nodoc: # create_database_user('testdb', 'user', 'pass') - grants all privileges by default # create_database_user('testdb', 'user', 'pass', permissions: :read) - use [:read|:write|:all] def create_database_user(database, username, password, options = {}) permissions = options.fetch(:permissions, :all) execute( "CREATE user #{username} WITH PASSWORD '#{password}'; "\ "GRANT #{permissions.to_s.upcase} ON #{database} TO #{username}" ) end def update_user_password(username, password) execute("SET PASSWORD FOR #{username} = '#{password}'") end # permission => [:all] def grant_user_admin_privileges(username) execute("GRANT ALL PRIVILEGES TO #{username}") end # permission => [:read|:write|:all] def grant_user_privileges(username, database, permission) execute("GRANT #{permission.to_s.upcase} ON #{database} TO #{username}") end def list_user_grants(username) execute("SHOW GRANTS FOR #{username}") end # permission => [:read|:write|:all] def revoke_user_privileges(username, database, permission) execute("REVOKE #{permission.to_s.upcase} ON #{database} FROM #{username}") end def delete_user(username) execute("DROP USER #{username}") end # => [{"username"=>"usr", "admin"=>true}, {"username"=>"justauser", "admin"=>false}] def list_users resp = execute("SHOW USERS", parse: true) fetch_series(resp).fetch(0, {}) .fetch('values', []) .map { |v| { 'username' => v.first, 'admin' => v.last } } end end end end influxdb-0.2.3/lib/influxdb/query/database.rb0000644000175600017570000000072312646433657020231 0ustar pravipravimodule InfluxDB module Query module Database # :nodoc: def create_database(name) execute("CREATE DATABASE #{name}") end def delete_database(name) execute("DROP DATABASE #{name}") end def list_databases resp = execute("SHOW DATABASES", parse: true) fetch_series(resp).fetch(0, {}) .fetch('values', []) .flatten .map { |v| { 'name' => v } } end end end end influxdb-0.2.3/lib/influxdb/query/core.rb0000644000175600017570000000655012646433657017421 0ustar pravipravimodule InfluxDB module Query # :nodoc: all # rubocop:disable Metrics/AbcSize module Core def ping get "/ping" end # rubocop:disable Metrics/MethodLength def query(query, opts = {}) denormalize = opts.fetch(:denormalize, config.denormalize) params = query_params(query, opts) url = full_url("/query", params) series = fetch_series(get(url, parse: true)) if block_given? series.each do |s| yield s['name'], s['tags'], denormalize ? denormalize_series(s) : raw_values(s) end else denormalize ? denormalized_series_list(series) : series end end # rubocop:enable Metrics/MethodLength # Example: # write_points([ # { # series: 'cpu', # tags: { host: 'server_nl', regios: 'us' }, # values: {internal: 5, external: 6}, # timestamp: 1422568543702900257 # }, # { # series: 'gpu', # values: {value: 0.9999}, # } # ]) def write_points(data, precision = nil, retention_policy = nil) data = data.is_a?(Array) ? data : [data] payload = generate_payload(data) writer.write(payload, precision, retention_policy) end # Example: # write_point('cpu', tags: {region: 'us'}, values: {internal: 60}) def write_point(series, data, precision = nil, retention_policy = nil) write_points(data.merge(series: series), precision, retention_policy) end def write(data, precision, retention_policy = nil) precision ||= config.time_precision params = { db: config.database, precision: precision } params[:rp] = retention_policy if retention_policy url = full_url("/write", params) post(url, data) end private def query_params(query, opts) precision = opts.fetch(:precision, config.time_precision) epoch = opts.fetch(:epoch, config.epoch) params = { q: query, db: config.database, precision: precision } params.merge!(epoch: epoch) if epoch params end def denormalized_series_list(series) series.map do |s| { 'name' => s['name'], 'tags' => s['tags'], 'values' => denormalize_series(s) } end end def fetch_series(response) response.fetch('results', []) .fetch(0, {}) .fetch('series', []) end def generate_payload(data) data.map do |point| InfluxDB::PointValue.new(point).dump end.join("\n") end def execute(query, options = {}) url = full_url("/query", q: query) get(url, options) end def denormalize_series(series) Array(series["values"]).map do |values| Hash[series["columns"].zip(values)] end end def raw_values(series) series.select { |k, _| %w(columns values).include?(k) } end def full_url(path, params = {}) unless basic_auth? params[:u] = config.username params[:p] = config.password end query = params.map { |k, v| [CGI.escape(k.to_s), "=", CGI.escape(v.to_s)].join }.join("&") URI::Generic.build(path: File.join(config.prefix, path), query: query).to_s end end end end influxdb-0.2.3/lib/influxdb/version.rb0000644000175600017570000000006212646433657017001 0ustar pravipravimodule InfluxDB # :nodoc: VERSION = "0.2.3" end influxdb-0.2.3/lib/influxdb/config.rb0000644000175600017570000000365412646433657016573 0ustar pravipravimodule InfluxDB # InfluxDB client configuration class Config AUTH_METHODS = %w(params basic_auth) attr_accessor :hosts, :port, :username, :password, :database, :time_precision, :use_ssl, :verify_ssl, :ssl_ca_cert, :auth_method, :initial_delay, :max_delay, :open_timeout, :read_timeout, :retry, :prefix, :denormalize, :epoch attr_reader :async, :udp # rubocop:disable all def initialize(opts = {}) @database = opts[:database] @hosts = Array(opts[:hosts] || opts[:host] || ["localhost"]) @port = opts.fetch(:port, 8086) @prefix = opts.fetch(:prefix, '') @username = opts.fetch(:username, "root") @password = opts.fetch(:password, "root") @auth_method = AUTH_METHODS.include?(opts[:auth_method]) ? opts[:auth_method] : "params" @use_ssl = opts.fetch(:use_ssl, false) @verify_ssl = opts.fetch(:verify_ssl, true) @ssl_ca_cert = opts.fetch(:ssl_ca_cert, false) @time_precision = opts.fetch(:time_precision, "s") @initial_delay = opts.fetch(:initial_delay, 0.01) @max_delay = opts.fetch(:max_delay, 30) @open_timeout = opts.fetch(:write_timeout, 5) @read_timeout = opts.fetch(:read_timeout, 300) @async = opts.fetch(:async, false) @udp = opts.fetch(:udp, false) @retry = opts.fetch(:retry, nil) @denormalize = opts.fetch(:denormalize, true) @epoch = opts.fetch(:epoch, false) @retry = case @retry when Integer @retry when true, nil -1 when false 0 end end def udp? !!udp end def async? !!async end end end influxdb-0.2.3/lib/influxdb/max_queue.rb0000644000175600017570000000050212646433657017304 0ustar pravipravirequire "thread" module InfluxDB # Queue with max length limit class MaxQueue < Queue attr_reader :max def initialize(max = 10_000) fail ArgumentError, "queue size must be positive" unless max > 0 @max = max super() end def push(obj) super if length < @max end end end influxdb-0.2.3/lib/influxdb/writer/0000755000175600017570000000000012646433657016305 5ustar pravipraviinfluxdb-0.2.3/lib/influxdb/writer/udp.rb0000644000175600017570000000106012646433657017417 0ustar pravipravimodule InfluxDB module Writer # Writes data to InfluxDB through UDP class UDP attr_accessor :socket attr_reader :host, :port def initialize(client, config) @client = client config = config.is_a?(Hash) ? config : {} @host = config.fetch(:host, 'localhost') @port = config.fetch(:port, 4444) self.socket = UDPSocket.new socket.connect(host, port) end def write(payload, _precision = nil, _retention_policy = nil) socket.send(payload, 0) end end end end influxdb-0.2.3/lib/influxdb/writer/async.rb0000644000175600017570000000600312646433657017746 0ustar pravipravirequire 'thread' require "net/http" require "uri" module InfluxDB module Writer # :nodoc: all class Async attr_reader :config, :client def initialize(client, config) @client = client @config = config end def write(data, _precision = nil, _retention_policy = nil) data = data.is_a?(Array) ? data : [data] data.map { |p| worker.push(p) } end WORKER_MUTEX = Mutex.new def worker return @worker if @worker WORKER_MUTEX.synchronize do # this return is necessary because the previous mutex holder # might have already assigned the @worker return @worker if @worker @worker = Worker.new(client, config) end end class Worker attr_reader :client, :queue, :threads include InfluxDB::Logging MAX_POST_POINTS = 1000 MAX_QUEUE_SIZE = 10_000 NUM_WORKER_THREADS = 3 SLEEP_INTERVAL = 5 def initialize(client, config) @client = client config = config.is_a?(Hash) ? config : {} @queue = InfluxDB::MaxQueue.new config.fetch(:max_queue, MAX_QUEUE_SIZE) spawn_threads! at_exit do log :debug, "Thread exiting, flushing queue." check_background_queue until queue.empty? end end def push(payload) queue.push(payload) end def current_threads Thread.list.select { |t| t[:influxdb] == object_id } end def current_thread_count Thread.list.count { |t| t[:influxdb] == object_id } end # rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/MethodLength # rubocop:disable Metrics/AbcSize def spawn_threads! @threads = [] NUM_WORKER_THREADS.times do |thread_num| log :debug, "Spawning background worker thread #{thread_num}." @threads << Thread.new do Thread.current[:influxdb] = object_id until client.stopped? check_background_queue(thread_num) sleep rand(SLEEP_INTERVAL) end log :debug, "Exit background worker thread #{thread_num}." end end end def check_background_queue(thread_num = 0) log :debug, "Checking background queue on thread #{thread_num} (#{current_thread_count} active)" loop do data = [] while data.size < MAX_POST_POINTS && !queue.empty? p = queue.pop(true) rescue next data.push p end return if data.empty? begin log :debug, "Found data in the queue! (#{data.length} points)" client.write(data.join("\n"), nil) rescue => e puts "Cannot write data: #{e.inspect}" end break if queue.length > MAX_POST_POINTS end end end end end end influxdb-0.2.3/lib/influxdb/client/0000755000175600017570000000000012646433657016247 5ustar pravipraviinfluxdb-0.2.3/lib/influxdb/client/http.rb0000644000175600017570000000666612646433657017571 0ustar pravipravirequire 'uri' require 'cgi' require 'net/http' require 'net/https' module InfluxDB # rubocop:disable Metrics/MethodLength # rubocop:disable Metrics/AbcSize module HTTP # :nodoc: def get(url, options = {}) connect_with_retry do |http| response = do_request http, Net::HTTP::Get.new(url) if response.is_a? Net::HTTPSuccess handle_successful_response(response, options) elsif response.is_a? Net::HTTPUnauthorized fail InfluxDB::AuthenticationError, response.body else resolve_error(response.body) end end end def post(url, data) headers = { "Content-Type" => "application/octet-stream" } connect_with_retry do |http| response = do_request http, Net::HTTP::Post.new(url, headers), data if response.is_a? Net::HTTPSuccess return response elsif response.is_a? Net::HTTPUnauthorized fail InfluxDB::AuthenticationError, response.body else resolve_error(response.body) end end end private def connect_with_retry(&block) hosts = config.hosts.dup delay = config.initial_delay retry_count = 0 begin hosts.push(host = hosts.shift) http = Net::HTTP.new(host, config.port) http.open_timeout = config.open_timeout http.read_timeout = config.read_timeout http = setup_ssl(http) block.call(http) rescue Timeout::Error, *InfluxDB::NET_HTTP_EXCEPTIONS => e retry_count += 1 if (config.retry == -1 || retry_count <= config.retry) && !stopped? log :error, "Failed to contact host #{host}: #{e.inspect} - retrying in #{delay}s." sleep delay delay = [config.max_delay, delay * 2].min retry else raise InfluxDB::ConnectionError, "Tried #{retry_count - 1} times to reconnect but failed." end ensure http.finish if http.started? end end def do_request(http, req, data = nil) req.basic_auth config.username, config.password if basic_auth? req.body = data if data http.request(req) end def basic_auth? config.auth_method == 'basic_auth' end def resolve_error(response) if response =~ /Couldn\'t find series/ fail InfluxDB::SeriesNotFound, response else fail InfluxDB::Error, response end end def handle_successful_response(response, options) parsed_response = JSON.parse(response.body) if response.body errors = errors_from_response(parsed_response) if parsed_response fail InfluxDB::QueryError, errors if errors options.fetch(:parse, false) ? parsed_response : response end def errors_from_response(parsed_resp) parsed_resp.is_a?(Hash) && parsed_resp.fetch('results', []) .fetch(0, {}) .fetch('error', nil) end def setup_ssl(http) http.use_ssl = config.use_ssl http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless config.verify_ssl return http unless config.use_ssl http.cert_store = generate_cert_store http end def generate_cert_store store = OpenSSL::X509::Store.new store.set_default_paths if config.ssl_ca_cert if File.directory?(config.ssl_ca_cert) store.add_path(config.ssl_ca_cert) else store.add_file(config.ssl_ca_cert) end end store end end end influxdb-0.2.3/lib/influxdb/client.rb0000644000175600017570000000424112646433657016575 0ustar pravipravirequire 'json' require 'cause' module InfluxDB # rubocop:disable Metrics/MethodLength # rubocop:disable Metrics/AbcSize # InfluxDB client class class Client attr_reader :config, :writer include InfluxDB::Logging include InfluxDB::HTTP include InfluxDB::Query::Core include InfluxDB::Query::Cluster include InfluxDB::Query::Database include InfluxDB::Query::User include InfluxDB::Query::ContinuousQuery include InfluxDB::Query::RetentionPolicy # Initializes a new InfluxDB client # # === Examples: # # # connect to localhost using root/root # # as the credentials and doesn't connect to a db # # InfluxDB::Client.new # # # connect to localhost using root/root # # as the credentials and 'db' as the db name # # InfluxDB::Client.new 'db' # # # override username, other defaults remain unchanged # # InfluxDB::Client.new username: 'username' # # # override username, use 'db' as the db name # Influxdb::Client.new 'db', username: 'username' # # === Valid options in hash # # +:host+:: the hostname to connect to # +:port+:: the port to connect to # +:prefix+:: the specified path prefix when building the url e.g.: /prefix/db/dbname... # +:username+:: the username to use when executing commands # +:password+:: the password associated with the username # +:use_ssl+:: use ssl to connect # +:verify_ssl+:: verify ssl server certificate? # +:ssl_ca_cert+:: ssl CA certificate, chainfile or CA path. # The system CA path is automatically included def initialize(*args) opts = args.last.is_a?(Hash) ? args.last : {} opts[:database] = args.first if args.first.is_a? String @config = InfluxDB::Config.new(opts) @stopped = false @writer = self if config.async? @writer = InfluxDB::Writer::Async.new(self, config.async) elsif config.udp? @writer = InfluxDB::Writer::UDP.new(self, config.udp) end at_exit { stop! } if config.retry > 0 end def stop! @stopped = true end def stopped? @stopped end end end influxdb-0.2.3/lib/influxdb/point_value.rb0000644000175600017570000000227112646433657017645 0ustar pravipravimodule InfluxDB # Convert data point to string using Line protocol class PointValue attr_reader :series, :values, :tags, :timestamp def initialize(data) @series = data[:series].gsub(/\s/, '\ ').gsub(',', '\,') @values = data_to_string(data[:values], true) @tags = data_to_string(data[:tags]) @timestamp = data[:timestamp] end def dump dump = "#{@series}" dump << ",#{@tags}" if @tags dump << " #{@values}" dump << " #{@timestamp}" if @timestamp dump end private def data_to_string(data, quote_escape = false) return nil unless data && !data.empty? mappings = map(data, quote_escape) mappings.join(',') end def map(data, quote_escape) data.map do |k, v| key = escape_key(k) val = v.is_a?(String) ? escape_value(v, quote_escape) : v "#{key}=#{val}" end end def escape_value(value, quote_escape) val = value. gsub(/\s/, '\ '). gsub(',', '\,'). gsub('"', '\"') val = %("#{val}") if quote_escape val end def escape_key(key) key.to_s.gsub(/\s/, '\ ').gsub(',', '\,') end end end influxdb-0.2.3/lib/influxdb/logging.rb0000644000175600017570000000070112646433657016742 0ustar pravipravirequire 'logger' module InfluxDB module Logging # :nodoc: PREFIX = "[InfluxDB] " class << self attr_writer :logger end def self.logger return false if @logger == false @logger ||= ::Logger.new(STDERR).tap { |logger| logger.level = Logger::INFO } end private def log(level, message) InfluxDB::Logging.logger.send(level.to_sym, PREFIX + message) if InfluxDB::Logging.logger end end end influxdb-0.2.3/Rakefile0000644000175600017570000000066412646433657014063 0ustar pravipravirequire "bundler/gem_tasks" targeted_files = ARGV.drop(1) file_pattern = targeted_files.empty? ? 'spec/**/*_spec.rb' : targeted_files require 'rspec/core' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) do |t| t.pattern = FileList[file_pattern] end RSpec.configure do |config| config.color = true config.formatter = :documentation end task default: :spec task :console do sh 'pry -r ./lib/influxdb.rb' end influxdb-0.2.3/influxdb.gemspec0000644000175600017570000000203712646433657015572 0ustar pravipravi# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'influxdb/version' Gem::Specification.new do |spec| spec.name = "influxdb" spec.version = InfluxDB::VERSION spec.authors = ["Todd Persen"] spec.email = ["influxdb@googlegroups.com"] spec.description = %q{This is the official Ruby library for InfluxDB.} spec.summary = %q{Ruby library for InfluxDB.} spec.homepage = "http://influxdb.org" spec.license = "MIT" spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] spec.add_runtime_dependency "json" spec.add_runtime_dependency "cause" spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency "rake" spec.add_development_dependency "rspec", "~> 3.0.0" spec.add_development_dependency "webmock", "~> 1.21.0" end influxdb-0.2.3/.gitignore0000644000175600017570000000031112646433657014373 0ustar pravipravi*.gem *.rbc .bundle .config coverage InstalledFiles lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp Gemfile.lock .rvmrc .ruby-version # YARD artifacts .yardoc _yardoc doc/ *.local