Skip to content

Commit

Permalink
Remove multiple endpoints support
Browse files Browse the repository at this point in the history
This library exists to interact with the API (mute monitors, create
dashboards, ...), and for most of these actions it doesn't make sense to
send them to multiple accounts.

It makes sense for metrics/events/service_checks, and for this usage
dogstatsd-ruby makes more sense. (as it uses the forwarder included with
the agent which can easily send to multiple accounts)
  • Loading branch information
degemer committed Aug 23, 2016
1 parent c8c7ec5 commit f347d48
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 188 deletions.
38 changes: 14 additions & 24 deletions lib/dogapi/common.rb
Expand Up @@ -91,23 +91,14 @@ def connect
end
end

results = []

@endpoints.each do |api_host, keys_couples|
keys_couples.each do |keys_couple|
api_key, application_key = keys_couple
uri = URI.parse(api_host)
session = connection.new(uri.host, uri.port)
session.open_timeout = @timeout
session.use_ssl = uri.scheme == 'https'
session.start do |conn|
conn.read_timeout = @timeout
results << yield(conn, api_key, application_key)
end
end
uri = URI.parse(@api_host)
session = connection.new(uri.host, uri.port)
session.open_timeout = @timeout
session.use_ssl = uri.scheme == 'https'
session.start do |conn|
conn.read_timeout = @timeout
yield conn
end
results = results[0] if results.length == 1
results
end

def suppress_error_if_silent(e)
Expand All @@ -124,10 +115,9 @@ def suppress_error_if_silent(e)
# +params+ is a Hash that will be converted to request parameters
def request(method, url, extra_params, body, send_json, with_app_key=true)
resp = nil
connect do |conn, api_key, app_key|
connect do |conn|
begin
app_key = nil unless with_app_key
current_url = url + prepare_params(extra_params, api_key, app_key)
current_url = url + prepare_params(extra_params, with_app_key)
req = method.new(current_url)

if send_json
Expand All @@ -136,16 +126,16 @@ def request(method, url, extra_params, body, send_json, with_app_key=true)
end

resp = conn.request(req)
next handle_response(resp)
return handle_response(resp)
rescue Exception => e
next suppress_error_if_silent e
suppress_error_if_silent e
end
end
end

def prepare_params(extra_params, api_key, app_key)
params = { api_key: api_key }
params[:application_key] = app_key unless app_key.nil?
def prepare_params(extra_params, with_app_key)
params = { api_key: @api_key }
params[:application_key] = @application_key if with_app_key
params = extra_params.merge params unless extra_params.nil?
qs_params = params.map { |k, v| k.to_s + '=' + v.to_s }
qs = '?' + qs_params.join('&')
Expand Down
57 changes: 14 additions & 43 deletions lib/dogapi/facade.rb
Expand Up @@ -10,13 +10,7 @@ module Dogapi
# Class methods return a tuple of (+response_code+, +response_body+). Unless otherwise noted, the response body is deserialized JSON. Up-to-date information about the JSON object structure is available in the HTTP API documentation, here[https://github.com/DataDog/dogapi/wiki].
class Client

# Create a new Client optionally specifying a default host and device
# extra_endpoints can be an array or a hash
# Array to use the same Datadog API endpoint:
# [[api_key1, application_key1], [api_key2, application_key2]]
# Hash to be able to specify different endpoints:
# {'https://app.example.com' => [[api_key1, application_key1], [api_key2, application_key2]]}
def initialize(api_key, application_key=nil, host=nil, device=nil, silent=true, timeout=nil, extra_endpoints=nil)
def initialize(api_key, application_key=nil, host=nil, device=nil, silent=true, timeout=nil)

if api_key
@api_key = api_key
Expand All @@ -29,22 +23,20 @@ def initialize(api_key, application_key=nil, host=nil, device=nil, silent=true,
@host = host ||= Dogapi.find_localhost()
@device = device

@endpoints = format_endpoints(api_key, application_key, @datadog_host, extra_endpoints)

# FIXME: refactor to avoid all this code duplication
@metric_svc = Dogapi::V1::MetricService.new(@api_key, @application_key, silent, timeout, @endpoints)
@event_svc = Dogapi::V1::EventService.new(@api_key, @application_key, silent, timeout, @endpoints)
@tag_svc = Dogapi::V1::TagService.new(@api_key, @application_key, silent, timeout, @endpoints)
@comment_svc = Dogapi::V1::CommentService.new(@api_key, @application_key, silent, timeout, @endpoints)
@search_svc = Dogapi::V1::SearchService.new(@api_key, @application_key, silent, timeout, @endpoints)
@dash_service = Dogapi::V1::DashService.new(@api_key, @application_key, silent, timeout, @endpoints)
@alert_svc = Dogapi::V1::AlertService.new(@api_key, @application_key, silent, timeout, @endpoints)
@user_svc = Dogapi::V1::UserService.new(@api_key, @application_key, silent, timeout, @endpoints)
@snapshot_svc = Dogapi::V1::SnapshotService.new(@api_key, @application_key, silent, timeout, @endpoints)
@embed_svc = Dogapi::V1::EmbedService.new(@api_key, @application_key, silent, timeout, @endpoints)
@screenboard_svc = Dogapi::V1::ScreenboardService.new(@api_key, @application_key, silent, timeout, @endpoints)
@monitor_svc = Dogapi::V1::MonitorService.new(@api_key, @application_key, silent, timeout, @endpoints)
@service_check_svc = Dogapi::V1::ServiceCheckService.new(@api_key, @application_key, silent, timeout, @endpoints)
@metric_svc = Dogapi::V1::MetricService.new(@api_key, @application_key, silent, timeout)
@event_svc = Dogapi::V1::EventService.new(@api_key, @application_key, silent, timeout)
@tag_svc = Dogapi::V1::TagService.new(@api_key, @application_key, silent, timeout)
@comment_svc = Dogapi::V1::CommentService.new(@api_key, @application_key, silent, timeout)
@search_svc = Dogapi::V1::SearchService.new(@api_key, @application_key, silent, timeout)
@dash_service = Dogapi::V1::DashService.new(@api_key, @application_key, silent, timeout)
@alert_svc = Dogapi::V1::AlertService.new(@api_key, @application_key, silent, timeout)
@user_svc = Dogapi::V1::UserService.new(@api_key, @application_key, silent, timeout)
@snapshot_svc = Dogapi::V1::SnapshotService.new(@api_key, @application_key, silent, timeout)
@embed_svc = Dogapi::V1::EmbedService.new(@api_key, @application_key, silent, timeout)
@screenboard_svc = Dogapi::V1::ScreenboardService.new(@api_key, @application_key, silent, timeout)
@monitor_svc = Dogapi::V1::MonitorService.new(@api_key, @application_key, silent, timeout)
@service_check_svc = Dogapi::V1::ServiceCheckService.new(@api_key, @application_key, silent, timeout)
@legacy_event_svc = Dogapi::EventService.new(@datadog_host)
end

Expand Down Expand Up @@ -475,26 +467,5 @@ def override_scope(options= {})
options = defaults.merge(options)
Scope.new(options[:host], options[:device])
end

def format_endpoints(api_key, app_key, datadog_host, extra_endpoints)
endpoints = Hash.new []
endpoints[datadog_host] = [[api_key, app_key]]
if extra_endpoints.nil?
return endpoints
end

if extra_endpoints.is_a?(Array)
extra_endpoints.each do |keys|
endpoints[datadog_host] += [keys]
end
elsif extra_endpoints.is_a?(Hash)
extra_endpoints.each do |host, keys|
endpoints[host] += keys
end
else
raise "extra_endpoints has to be an Array or a Hash: #{extra_endpoints}"
end
endpoints
end
end
end
83 changes: 0 additions & 83 deletions spec/integration/common_spec.rb
Expand Up @@ -28,88 +28,5 @@
end
end
end

context 'when multiple endpoints are used' do
let(:endpoints) do
{ 'http://app.datadoghq.com' => [[api_key, app_key]],
'http://app.example.com' => [%w(api_key2 app_key2)] }
end
let(:service) { Dogapi::APIService.new api_key, app_key, true, nil, endpoints }
context 'and both are up' do
it 'queries both endpoints' do
stub_request(:get, /#{url}/).to_return(body: '{}').then.to_raise(StandardError)
stub_request(:get, /#{second_url}/).to_return(body: '{}').then.to_raise(StandardError)
expect(service.request(Net::HTTP::Get, '/api/v1/awesome', nil, nil, true, true)).to eq(
[['200', {}], ['200', {}]]
)

expect(WebMock).to have_requested(:get, url).with(
query: default_query
)
expect(WebMock).to have_requested(:get, second_url).with(
query: { api_key: 'api_key2', application_key: 'app_key2' }
)
end
end
context 'and one is down' do
it 'queries both endpoints' do
stub_request(:get, /#{url}/).to_return(body: '{}').then.to_raise(StandardError)
stub_request(:get, /#{second_url}/).to_timeout
expect(service.request(Net::HTTP::Get, '/api/v1/awesome', nil, nil, true, true)).to eq(
[['200', {}], [-1, {}]]
)

expect(WebMock).to have_requested(:get, url).with(
query: default_query
)
expect(WebMock).to have_requested(:get, second_url).with(
query: { api_key: 'api_key2', application_key: 'app_key2' }
)
end
end
end

context 'when multiple keys are used' do
it 'queries both endpoints' do
endpoints = { 'http://app.datadoghq.com' => [[api_key, app_key], %w(api_key2 app_key2)] }
service = Dogapi::APIService.new api_key, app_key, true, nil, endpoints

stub_request(:get, /#{url}/).to_return(body: '{}').times(2).then.to_raise(StandardError)
expect(service.request(Net::HTTP::Get, '/api/v1/awesome', nil, nil, true, true)).to eq(
[['200', {}], ['200', {}]]
)

expect(WebMock).to have_requested(:get, url).with(
query: default_query
)
expect(WebMock).to have_requested(:get, url).with(
query: { api_key: 'api_key2', application_key: 'app_key2' }
)
end
end

context 'when multiple endpoints and multiple keys are used' do
it 'queries both endpoints' do
endpoints = { 'http://app.datadoghq.com' => [[api_key, app_key], %w(api_key2 app_key2)],
'http://app.example.com' => [%w(api_key3 app_key3)] }
service = Dogapi::APIService.new api_key, app_key, true, nil, endpoints

stub_request(:get, /#{url}/).to_return(body: '{}').times(2).then.to_raise(StandardError)
stub_request(:get, /#{second_url}/).to_return(body: '{}').then.to_raise(StandardError)
expect(service.request(Net::HTTP::Get, '/api/v1/awesome', nil, nil, true, true)).to eq(
[['200', {}], ['200', {}], ['200', {}]]
)

expect(WebMock).to have_requested(:get, url).with(
query: default_query
)
expect(WebMock).to have_requested(:get, url).with(
query: { api_key: 'api_key2', application_key: 'app_key2' }
)
expect(WebMock).to have_requested(:get, second_url).with(
query: { api_key: 'api_key3', application_key: 'app_key3' }
)
end
end
end
end
38 changes: 0 additions & 38 deletions spec/unit/facade_spec.rb
Expand Up @@ -76,42 +76,4 @@ def @service.upload(payload)
expect(buffer).to be nil
end
end

describe '#format_endpoints' do
context 'when no extra_endpoints is given' do
it 'returns the default endpoint and api keys' do
client = Dogapi::Client.new api_key, app_key
expected_endpoints = { 'http://app.datadoghq.com' => [[api_key, app_key]] }
expect(client.instance_variable_get(:@endpoints)).to eq(expected_endpoints)
end
end
context 'when an Array extra_endpoints is given' do
it 'returns the endpoint with all the keys' do
extra_endpoints = [%w(api_key1 app_key2), ['api_key3', nil]]
client = Dogapi::Client.new api_key, app_key, nil, nil, true, nil, extra_endpoints
expected_endpoints = { 'http://app.datadoghq.com' => [[api_key, app_key]] + extra_endpoints }
expect(client.instance_variable_get(:@endpoints)).to eq(expected_endpoints)
end
end
context 'when a Hash extra_endpoints is given' do
it 'returns the endpoints with all the keys' do
extra_keys = [%w(api_key1 app_key2), ['api_key3', nil]]
extra_endpoints = { 'http://app.datadoghq.com' => [%w(api_key2 app_key2)],
'http://app.example.com' => extra_keys }
client = Dogapi::Client.new api_key, app_key, nil, nil, true, nil, extra_endpoints
expected_endpoints = { 'http://app.datadoghq.com' => [[api_key, app_key]] + [%w(api_key2 app_key2)],
'http://app.example.com' => extra_keys }
expect(client.instance_variable_get(:@endpoints)).to eq(expected_endpoints)
end
end
context 'when a bad extra_endpoints is given' do
it 'raises an error' do
extra_endpoints = '{}'
expect { Dogapi::Client.new api_key, app_key, nil, nil, true, nil, extra_endpoints }.to raise_error(
RuntimeError,
'extra_endpoints has to be an Array or a Hash: {}'
)
end
end
end
end

0 comments on commit f347d48

Please sign in to comment.