Skip to content

Commit

Permalink
Re-add service level objectives support (#224)
Browse files Browse the repository at this point in the history
* Revert "Revert "add service level objectives support (#188)" (#198)"

This reverts commit 71222f5.

* [slo] Convert to ruby1.9 compatible syntax.

* [lint] Enforce 1.9 compatible arrays of hashes.
  • Loading branch information
phillip-dd authored Feb 11, 2020
1 parent 69b403a commit 8be8d0f
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -520,3 +520,6 @@ Style/YodaCondition:
Style/ZeroLengthPredicate:
Exclude:
- 'lib/capistrano/datadog.rb'

Style/SymbolArray:
EnforcedStyle: brackets # Since ruby1.9 is supported
10 changes: 10 additions & 0 deletions lib/dogapi/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,14 @@ def Dogapi.validate_tags(tags)
raise ArgumentError, "Each tag needs to be a string. Current value: #{tag}" unless tag.is_a? String
end
end

# Very simplified hash with indifferent access - access to string or symbol
# keys via symbols. E.g.:
# my_hash = { 'foo' => 1 }
# Dogapi.symbolized_access(my_hash)
# my_hash[:foo] # => 1
def Dogapi.symbolized_access(hash)
hash.default_proc = proc { |h, k| h.key?(k.to_s) ? h[k.to_s] : nil }
hash
end
end
43 changes: 43 additions & 0 deletions lib/dogapi/facade.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ def initialize(api_key, application_key=nil, host=nil, device=nil, silent=true,
@usage_svc = Dogapi::V1::UsageService.new(@api_key, @application_key, silent, timeout, @datadog_host)
@azure_integration_svc = Dogapi::V1::AzureIntegrationService.new(@api_key, @application_key, silent, timeout, @datadog_host)
@gcp_integration_svc = Dogapi::V1::GcpIntegrationService.new(@api_key, @application_key, silent, timeout, @datadog_host)
@service_level_objective_svc = Dogapi::V1::ServiceLevelObjectiveService.new(@api_key, @application_key, silent,
timeout, @datadog_host)

# Support for Dashboard List API v2.
@v2 = Dogapi::ClientV2.new(@api_key, @application_key, true, true, @datadog_host)

Expand Down Expand Up @@ -673,6 +676,46 @@ def unmute_host(hostname)
@monitor_svc.unmute_host(hostname)
end

#
# SERVICE LEVEL OBJECTIVES
#

def create_service_level_objective(type, slo_name, thresholds, options = {})
@service_level_objective_svc.create_service_level_objective(type, slo_name, thresholds, options)
end

def update_service_level_objective(slo_id, type, options = {})
@service_level_objective_svc.update_service_level_objective(slo_id, type, options)
end

def get_service_level_objective(slo_id)
@service_level_objective_svc.get_service_level_objective(slo_id)
end

def get_service_level_objective_history(slo_id, from_ts, to_ts)
@service_level_objective_svc.get_service_level_objective_history(slo_id, from_ts, to_ts)
end

def search_service_level_objective(slo_ids = nil, query = nil, offset = nil, limit = nil)
@service_level_objective_svc.search_service_level_objective(slo_ids, query, offset, limit)
end

def can_delete_service_level_objective(slo_ids)
@service_level_objective_svc.can_delete_service_level_objective(slo_ids)
end

def delete_service_level_objective(slo_id)
@service_level_objective_svc.delete_service_level_objective(slo_id)
end

def delete_many_service_level_objective(slo_ids)
@service_level_objective_svc.delete_many_service_level_objective(slo_ids)
end

def delete_timeframes_service_level_objective(ops)
@service_level_objective_svc.delete_timeframes_service_level_objective(ops)
end

#
# SERVICE CHECKS
#
Expand Down
1 change: 1 addition & 0 deletions lib/dogapi/v1.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
require 'dogapi/v1/screenboard'
require 'dogapi/v1/search'
require 'dogapi/v1/service_check'
require 'dogapi/v1/service_level_objective'
require 'dogapi/v1/snapshot'
require 'dogapi/v1/synthetics'
require 'dogapi/v1/tag'
Expand Down
113 changes: 113 additions & 0 deletions lib/dogapi/v1/service_level_objective.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
require 'dogapi'

module Dogapi
class V1 # for namespacing

# Implements Service Level Objectives endpoints
class ServiceLevelObjectiveService < Dogapi::APIService

API_VERSION = 'v1'

def create_service_level_objective(type, slo_name, thresholds, options = {})
body = {
type: type,
name: slo_name,
thresholds: thresholds
}

symbolized_options = Dogapi.symbolized_access(options)
if type.to_s == 'metric'
body[:query] = {
numerator: symbolized_options[:numerator],
denominator: symbolized_options[:denominator]
}
else
body[:monitor_search] = symbolized_options[:monitor_search] if symbolized_options[:monitor_search]
body[:monitor_ids] = symbolized_options[:monitor_ids] if symbolized_options[:monitor_ids]
body[:groups] = symbolized_options[:groups] if symbolized_options[:groups]
end
body[:tags] = symbolized_options[:tags] if symbolized_options[:tags]
body[:description] = symbolized_options[:description] if symbolized_options[:description]

request(Net::HTTP::Post, "/api/#{API_VERSION}/slo", nil, body, true)
end

def update_service_level_objective(slo_id, type, options = {})
body = {
type: type
}

symbolized_options = Dogapi.symbolized_access(options)
if type == 'metric'
if symbolized_options[:numerator] && symbolized_options[:denominator]
body[:query] = {
numerator: symbolized_options[:numerator],
denominator: symbolized_options[:denominator]
}
end
else
body[:monitor_search] = symbolized_options[:monitor_search] if symbolized_options[:monitor_search]
body[:monitor_ids] = symbolized_options[:monitor_ids] if symbolized_options[:monitor_ids]
body[:groups] = symbolized_options[:groups] if symbolized_options[:groups]
end
[:name, :thresholds, :tags, :description].each do |a|
body[a] = symbolized_options[a] if symbolized_options[a]
end

request(Net::HTTP::Put, "/api/#{API_VERSION}/slo/#{slo_id}", nil, body, true)
end

def get_service_level_objective(slo_id)
request(Net::HTTP::Get, "/api/#{API_VERSION}/slo/#{slo_id}", nil, nil, false)
end

def search_service_level_objective(slo_ids, query, offset, limit)
params = {}
params[:offset] = offset unless offset.nil?
params[:limit] = limit unless limit.nil?
if !slo_ids.nil?
params[:ids] = slo_ids.join(',')
else
params[:query] = query
end

request(Net::HTTP::Get, "/api/#{API_VERSION}/slo/", params, nil, false)
end

def delete_service_level_objective(slo_id)
request(Net::HTTP::Delete, "/api/#{API_VERSION}/slo/#{slo_id}", nil, nil, false)
end

def delete_many_service_level_objective(slo_ids)
body = {
ids: slo_ids
}
request(Net::HTTP::Delete, "/api/#{API_VERSION}/slo/", nil, body, true)
end

def delete_timeframes_service_level_objective(ops)
# ops is a hash of slo_id: [<timeframe>] to delete
request(Net::HTTP::Post, "/api/#{API_VERSION}/slo/bulk_delete", nil, ops, true)
end

def get_service_level_objective_history(slo_id, from_ts, to_ts)
params = {
from_ts: from_ts,
to_ts: to_ts
}
request(Net::HTTP::Get, "/api/#{API_VERSION}/slo/#{slo_id}/history", params, nil, false)
end

def can_delete_service_level_objective(slo_ids)
params = {}
params[:ids] = if slo_ids.is_a? Array
slo_ids.join(',')
else
slo_ids
end
request(Net::HTTP::Get, "/api/#{API_VERSION}/slo/can_delete", params, nil, false)
end

end
end
end
73 changes: 73 additions & 0 deletions spec/integration/service_level_objective_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
require_relative '../spec_helper'

describe Dogapi::Client do
SLO_ID = '42424242424242424242424242424242'.freeze
SLO_TYPE = 'metric'.freeze
SLO_NAME = 'test slo'.freeze
SLO_DESCRIPTION = 'test slo description'.freeze
SLO_QUERY_NUMERATOR = 'sum:test.metric.metric{type:good}.as_count()'.freeze
SLO_QUERY_DENOMINATOR = 'sum:test.metric.metric{*}.as_count()'.freeze
SLO_TAGS = ['type:test'].freeze
SLO_THRESHOLDS = [{ timeframe: '7d', target: 90 }, { timeframe: '30d', target: 95 }].freeze

describe '#create_service_level_objective' do
it_behaves_like 'an api method',
:create_service_level_objective, [SLO_TYPE, SLO_NAME, SLO_THRESHOLDS, {
description: SLO_DESCRIPTION,
tags: SLO_TAGS,
numerator: SLO_QUERY_NUMERATOR,
denominator: SLO_QUERY_DENOMINATOR
}],
:post, '/slo', 'type' => SLO_TYPE, 'name' => SLO_NAME, 'thresholds' => SLO_THRESHOLDS,
'query' => { numerator: SLO_QUERY_NUMERATOR, denominator: SLO_QUERY_DENOMINATOR },
'tags' => SLO_TAGS, 'description' => SLO_DESCRIPTION
end

describe '#update_service_level_objective' do
it_behaves_like 'an api method',
:update_service_level_objective, [SLO_ID, SLO_TYPE, { name: SLO_NAME }],
:put, "/slo/#{SLO_ID}", 'type' => SLO_TYPE, 'name' => SLO_NAME
end

describe '#get_service_level_objective' do
it_behaves_like 'an api method',
:get_service_level_objective, [SLO_ID],
:get, "/slo/#{SLO_ID}"
end

describe '#get_service_level_objective_history' do
it_behaves_like 'an api method with params',
:get_service_level_objective_history, [SLO_ID],
:get, "/slo/#{SLO_ID}/history", 'from_ts' => 0, 'to_ts' => 1_000_000
end

describe '#can_delete_service_level_objective' do
it_behaves_like 'an api method with params',
:can_delete_service_level_objective, [],
:get, '/slo/can_delete', 'ids' => [SLO_ID]
end

describe '#search_service_level_objective' do
it_behaves_like 'an api method with optional params',
:search_service_level_objective, [[SLO_ID]],
:get, '/slo/', 'ids' => SLO_ID
end

describe '#delete_service_level_objective' do
it_behaves_like 'an api method',
:delete_service_level_objective, [SLO_ID],
:delete, "/slo/#{SLO_ID}"
end

describe '#delete_many_service_level_objective' do
it_behaves_like 'an api method',
:delete_many_service_level_objective, [[SLO_ID]],
:delete, '/slo/', 'ids' => [SLO_ID]
end

describe '#delete_timeframes_service_level_objective' do
it_behaves_like 'an api method',
:delete_timeframes_service_level_objective, [{ SLO_ID => ['7d'] }],
:post, '/slo/bulk_delete', SLO_ID => ['7d']
end
end

0 comments on commit 8be8d0f

Please sign in to comment.