mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
597 lines
20 KiB
Go
597 lines
20 KiB
Go
/*
|
|
* Datadog API for Go
|
|
*
|
|
* Please see the included LICENSE file for licensing information.
|
|
*
|
|
* Copyright 2017 by authors and contributors.
|
|
*/
|
|
|
|
package datadog
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Define the available machine-readable SLO types
|
|
const (
|
|
ServiceLevelObjectiveTypeMonitorID int = 0
|
|
ServiceLevelObjectiveTypeMetricID int = 1
|
|
)
|
|
|
|
// Define the available human-readable SLO types
|
|
var (
|
|
ServiceLevelObjectiveTypeMonitor = "monitor"
|
|
ServiceLevelObjectiveTypeMetric = "metric"
|
|
)
|
|
|
|
// ServiceLevelObjectiveTypeFromID maps machine-readable type to human-readable type
|
|
var ServiceLevelObjectiveTypeFromID = map[int]string{
|
|
ServiceLevelObjectiveTypeMonitorID: ServiceLevelObjectiveTypeMonitor,
|
|
ServiceLevelObjectiveTypeMetricID: ServiceLevelObjectiveTypeMetric,
|
|
}
|
|
|
|
// ServiceLevelObjectiveTypeToID maps human-readable type to machine-readable type
|
|
var ServiceLevelObjectiveTypeToID = map[string]int{
|
|
ServiceLevelObjectiveTypeMonitor: ServiceLevelObjectiveTypeMonitorID,
|
|
ServiceLevelObjectiveTypeMetric: ServiceLevelObjectiveTypeMetricID,
|
|
}
|
|
|
|
// ServiceLevelObjectiveThreshold defines an SLO threshold and timeframe
|
|
// For example it's the `<SLO: ex 99.999%> of <SLI> within <TimeFrame: ex 7d>
|
|
type ServiceLevelObjectiveThreshold struct {
|
|
TimeFrame *string `json:"timeframe,omitempty"`
|
|
Target *float64 `json:"target,omitempty"`
|
|
TargetDisplay *string `json:"target_display,omitempty"` // Read-Only for monitor type
|
|
Warning *float64 `json:"warning,omitempty"`
|
|
WarningDisplay *string `json:"warning_display,omitempty"` // Read-Only for monitor type
|
|
}
|
|
|
|
const thresholdTolerance float64 = 1e-8
|
|
|
|
// Equal check if one threshold is equal to another.
|
|
func (s *ServiceLevelObjectiveThreshold) Equal(o interface{}) bool {
|
|
other, ok := o.(*ServiceLevelObjectiveThreshold)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return s.GetTimeFrame() == other.GetTimeFrame() &&
|
|
Float64AlmostEqual(s.GetTarget(), other.GetTarget(), thresholdTolerance) &&
|
|
Float64AlmostEqual(s.GetWarning(), other.GetWarning(), thresholdTolerance)
|
|
}
|
|
|
|
// String implements Stringer
|
|
func (s ServiceLevelObjectiveThreshold) String() string {
|
|
return fmt.Sprintf("Threshold{timeframe=%s target=%f target_display=%s warning=%f warning_display=%s",
|
|
s.GetTimeFrame(), s.GetTarget(), s.GetTargetDisplay(), s.GetWarning(), s.GetWarningDisplay())
|
|
}
|
|
|
|
// ServiceLevelObjectiveMetricQuery represents a metric-based SLO definition query
|
|
// Numerator is the sum of the `good` events
|
|
// Denominator is the sum of the `total` events
|
|
type ServiceLevelObjectiveMetricQuery struct {
|
|
Numerator *string `json:"numerator,omitempty"`
|
|
Denominator *string `json:"denominator,omitempty"`
|
|
}
|
|
|
|
// ServiceLevelObjectiveThresholds is a sortable array of ServiceLevelObjectiveThreshold(s)
|
|
type ServiceLevelObjectiveThresholds []*ServiceLevelObjectiveThreshold
|
|
|
|
// Len implements sort.Interface length
|
|
func (s ServiceLevelObjectiveThresholds) Len() int {
|
|
return len(s)
|
|
}
|
|
|
|
// Less implements sort.Interface less comparator
|
|
func (s ServiceLevelObjectiveThresholds) Less(i, j int) bool {
|
|
iDur, _ := ServiceLevelObjectiveTimeFrameToDuration(s[i].GetTimeFrame())
|
|
jDur, _ := ServiceLevelObjectiveTimeFrameToDuration(s[j].GetTimeFrame())
|
|
return iDur < jDur
|
|
}
|
|
|
|
// Swap implements sort.Interface swap method
|
|
func (s ServiceLevelObjectiveThresholds) Swap(i, j int) {
|
|
s[i], s[j] = s[j], s[i]
|
|
}
|
|
|
|
// Equal check if one set of thresholds is equal to another.
|
|
func (s ServiceLevelObjectiveThresholds) Equal(o interface{}) bool {
|
|
other, ok := o.(ServiceLevelObjectiveThresholds)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
if len(s) != len(other) {
|
|
// easy case
|
|
return false
|
|
}
|
|
|
|
// compare one set from another
|
|
sSet := make(map[string]*ServiceLevelObjectiveThreshold, 0)
|
|
for _, t := range s {
|
|
sSet[t.GetTimeFrame()] = t
|
|
}
|
|
oSet := make(map[string]*ServiceLevelObjectiveThreshold, 0)
|
|
for _, t := range other {
|
|
oSet[t.GetTimeFrame()] = t
|
|
}
|
|
|
|
for timeframe, t := range oSet {
|
|
threshold, ok := sSet[timeframe]
|
|
if !ok {
|
|
// other contains more
|
|
return false
|
|
}
|
|
|
|
if !threshold.Equal(t) {
|
|
// they differ
|
|
return false
|
|
}
|
|
// drop from sSet for efficiency
|
|
delete(sSet, timeframe)
|
|
}
|
|
// if there are any remaining then they differ
|
|
if len(sSet) > 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// ServiceLevelObjective defines the Service Level Objective entity
|
|
type ServiceLevelObjective struct {
|
|
// Common
|
|
ID *string `json:"id,omitempty"`
|
|
Name *string `json:"name,omitempty"`
|
|
Description *string `json:"description,omitempty"`
|
|
Tags []string `json:"tags,omitempty"`
|
|
Thresholds ServiceLevelObjectiveThresholds `json:"thresholds,omitempty"`
|
|
Type *string `json:"type,omitempty"`
|
|
TypeID *int `json:"type_id,omitempty"` // Read-Only
|
|
// SLI definition
|
|
Query *ServiceLevelObjectiveMetricQuery `json:"query,omitempty"`
|
|
MonitorIDs []int `json:"monitor_ids,omitempty"`
|
|
MonitorSearch *string `json:"monitor_search,omitempty"`
|
|
Groups []string `json:"groups,omitempty"`
|
|
|
|
// Informational
|
|
MonitorTags []string `json:"monitor_tags,omitempty"` // Read-Only
|
|
Creator *Creator `json:"creator,omitempty"` // Read-Only
|
|
CreatedAt *int `json:"created_at,omitempty"` // Read-Only
|
|
ModifiedAt *int `json:"modified_at,omitempty"` // Read-Only
|
|
}
|
|
|
|
// MarshalJSON implements custom marshaler to ignore some fields
|
|
func (s *ServiceLevelObjective) MarshalJSON() ([]byte, error) {
|
|
var output struct {
|
|
ID *string `json:"id,omitempty"`
|
|
Name *string `json:"name,omitempty"`
|
|
Description *string `json:"description,omitempty"`
|
|
Tags []string `json:"tags,omitempty"`
|
|
Thresholds ServiceLevelObjectiveThresholds `json:"thresholds,omitempty"`
|
|
Type *string `json:"type,omitempty"`
|
|
// SLI definition
|
|
Query *ServiceLevelObjectiveMetricQuery `json:"query,omitempty"`
|
|
MonitorIDs []int `json:"monitor_ids,omitempty"`
|
|
MonitorSearch *string `json:"monitor_search,omitempty"`
|
|
Groups []string `json:"groups,omitempty"`
|
|
}
|
|
|
|
output.ID = s.ID
|
|
output.Name = s.Name
|
|
output.Description = s.Description
|
|
output.Tags = s.Tags
|
|
output.Thresholds = s.Thresholds
|
|
output.Type = s.Type
|
|
output.Query = s.Query
|
|
output.MonitorIDs = s.MonitorIDs
|
|
output.MonitorSearch = s.MonitorSearch
|
|
output.Groups = s.Groups
|
|
return json.Marshal(&output)
|
|
}
|
|
|
|
var sloTimeFrameToDurationRegex = regexp.MustCompile(`(?P<quantity>\d+)(?P<unit>(d))`)
|
|
|
|
// ServiceLevelObjectiveTimeFrameToDuration will convert a timeframe into a duration
|
|
func ServiceLevelObjectiveTimeFrameToDuration(timeframe string) (time.Duration, error) {
|
|
match := sloTimeFrameToDurationRegex.FindStringSubmatch(timeframe)
|
|
result := make(map[string]string)
|
|
for i, name := range sloTimeFrameToDurationRegex.SubexpNames() {
|
|
if i != 0 && name != "" {
|
|
result[name] = match[i]
|
|
}
|
|
}
|
|
if len(result) != 2 {
|
|
return 0, fmt.Errorf("invalid timeframe specified: '%s'", timeframe)
|
|
}
|
|
qty, err := json.Number(result["quantity"]).Int64()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid timeframe specified, could not convert quantity to number")
|
|
}
|
|
if qty <= 0 {
|
|
return 0, fmt.Errorf("invalid timeframe specified, quantity must be a positive number")
|
|
}
|
|
|
|
switch result["unit"] {
|
|
// FUTURE: will support more time frames, hence the switch here.
|
|
default:
|
|
// only matches on `d` currently, so this is simple
|
|
return time.Hour * 24 * time.Duration(qty), nil
|
|
}
|
|
}
|
|
|
|
// CreateServiceLevelObjective adds a new service level objective to the system. This returns a pointer
|
|
// to the service level objective so you can pass that to UpdateServiceLevelObjective or DeleteServiceLevelObjective
|
|
// later if needed.
|
|
func (client *Client) CreateServiceLevelObjective(slo *ServiceLevelObjective) (*ServiceLevelObjective, error) {
|
|
var out reqServiceLevelObjectives
|
|
|
|
if slo == nil {
|
|
return nil, fmt.Errorf("no SLO specified")
|
|
}
|
|
|
|
if err := client.doJsonRequest("POST", "/v1/slo", slo, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
if out.Error != "" {
|
|
return nil, fmt.Errorf(out.Error)
|
|
}
|
|
|
|
return out.Data[0], nil
|
|
}
|
|
|
|
// UpdateServiceLevelObjective takes a service level objective that was previously retrieved through some method
|
|
// and sends it back to the server.
|
|
func (client *Client) UpdateServiceLevelObjective(slo *ServiceLevelObjective) (*ServiceLevelObjective, error) {
|
|
var out reqServiceLevelObjectives
|
|
|
|
if slo == nil {
|
|
return nil, fmt.Errorf("no SLO specified")
|
|
}
|
|
|
|
if _, ok := slo.GetIDOk(); !ok {
|
|
return nil, fmt.Errorf("SLO must be created first")
|
|
}
|
|
|
|
if err := client.doJsonRequest("PUT", fmt.Sprintf("/v1/slo/%s", slo.GetID()), slo, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if out.Error != "" {
|
|
return nil, fmt.Errorf(out.Error)
|
|
}
|
|
|
|
return out.Data[0], nil
|
|
|
|
}
|
|
|
|
type reqServiceLevelObjectives struct {
|
|
Data []*ServiceLevelObjective `json:"data"`
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
// SearchServiceLevelObjectives searches for service level objectives by search criteria.
|
|
// limit will limit the amount of SLO's returned, the API will enforce a maximum and default to a minimum if not specified
|
|
func (client *Client) SearchServiceLevelObjectives(limit int, offset int, query string, ids []string) ([]*ServiceLevelObjective, error) {
|
|
var out reqServiceLevelObjectives
|
|
uriValues := make(url.Values, 0)
|
|
if limit > 0 {
|
|
uriValues.Set("limit", fmt.Sprintf("%d", limit))
|
|
}
|
|
if offset >= 0 {
|
|
uriValues.Set("offset", fmt.Sprintf("%d", offset))
|
|
}
|
|
// Either use `query` or use `ids`
|
|
hasQuery := strings.TrimSpace(query) != ""
|
|
hasIDs := len(ids) > 0
|
|
if hasQuery && hasIDs {
|
|
return nil, fmt.Errorf("invalid search: must specify either ids OR query, not both")
|
|
}
|
|
|
|
// specify by query
|
|
if hasQuery {
|
|
uriValues.Set("query", query)
|
|
}
|
|
// specify by `ids`
|
|
if hasIDs {
|
|
uriValues.Set("ids", strings.Join(ids, ","))
|
|
}
|
|
|
|
uri := "/v1/slo?" + uriValues.Encode()
|
|
|
|
if err := client.doJsonRequest("GET", uri, nil, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if out.Error != "" {
|
|
return nil, fmt.Errorf(out.Error)
|
|
}
|
|
|
|
return out.Data, nil
|
|
}
|
|
|
|
type reqSingleServiceLevelObjective struct {
|
|
Data *ServiceLevelObjective `json:"data"`
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
// GetServiceLevelObjective retrieves an service level objective by identifier.
|
|
func (client *Client) GetServiceLevelObjective(id string) (*ServiceLevelObjective, error) {
|
|
var out reqSingleServiceLevelObjective
|
|
|
|
if id == "" {
|
|
return nil, fmt.Errorf("no SLO specified")
|
|
}
|
|
|
|
if err := client.doJsonRequest("GET", fmt.Sprintf("/v1/slo/%s", id), nil, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
if out.Error != "" {
|
|
return nil, fmt.Errorf(out.Error)
|
|
}
|
|
|
|
return out.Data, nil
|
|
}
|
|
|
|
type reqDeleteResp struct {
|
|
Data []string `json:"data"`
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
// DeleteServiceLevelObjective removes an service level objective from the system.
|
|
func (client *Client) DeleteServiceLevelObjective(id string) error {
|
|
var out reqDeleteResp
|
|
|
|
if id == "" {
|
|
return fmt.Errorf("no SLO specified")
|
|
}
|
|
|
|
if err := client.doJsonRequest("DELETE", fmt.Sprintf("/v1/slo/%s", id), nil, &out); err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.Error != "" {
|
|
return fmt.Errorf(out.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteServiceLevelObjectives removes multiple service level objective from the system by id.
|
|
func (client *Client) DeleteServiceLevelObjectives(ids []string) error {
|
|
var out reqDeleteResp
|
|
|
|
if len(ids) == 0 {
|
|
return fmt.Errorf("no SLOs specified")
|
|
}
|
|
|
|
if err := client.doJsonRequest("DELETE", "/v1/slo", ids, &out); err != nil {
|
|
return err
|
|
}
|
|
|
|
if out.Error != "" {
|
|
return fmt.Errorf(out.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ServiceLevelObjectiveDeleteTimeFramesResponse is the response unique to the delete individual time-frames request
|
|
// this is read-only
|
|
type ServiceLevelObjectiveDeleteTimeFramesResponse struct {
|
|
DeletedIDs []string `json:"deleted"`
|
|
UpdatedIDs []string `json:"updated"`
|
|
}
|
|
|
|
// ServiceLevelObjectiveDeleteTimeFramesError is the error specific to deleting individual time frames.
|
|
// It contains more detailed information than the standard error.
|
|
type ServiceLevelObjectiveDeleteTimeFramesError struct {
|
|
ID *string `json:"id"`
|
|
TimeFrame *string `json:"timeframe"`
|
|
Message *string `json:"message"`
|
|
}
|
|
|
|
// Error computes the human readable error
|
|
func (e ServiceLevelObjectiveDeleteTimeFramesError) Error() string {
|
|
return fmt.Sprintf("error=%s id=%s for timeframe=%s", e.GetMessage(), e.GetID(), e.GetTimeFrame())
|
|
}
|
|
|
|
type timeframesDeleteResp struct {
|
|
Data *ServiceLevelObjectiveDeleteTimeFramesResponse `json:"data"`
|
|
Errors []*ServiceLevelObjectiveDeleteTimeFramesError `json:"errors"`
|
|
}
|
|
|
|
// DeleteServiceLevelObjectiveTimeFrames will delete SLO timeframes individually.
|
|
// This is useful if you have a SLO with 3 time windows and only need to delete some of the time windows.
|
|
// It will do a full delete if all time windows are removed as a result.
|
|
//
|
|
// Example:
|
|
// SLO `12345678901234567890123456789012` was defined with 2 time frames: "7d" and "30d"
|
|
// SLO `abcdefabcdefabcdefabcdefabcdefab` was defined with 2 time frames: "30d" and "90d"
|
|
//
|
|
// When we delete `7d` from `12345678901234567890123456789012` we still have `30d` timeframe remaining, hence this is "updated"
|
|
// When we delete `30d` and `90d` from `abcdefabcdefabcdefabcdefabcdefab` we are left with 0 time frames, hence this is "deleted"
|
|
// and the entire SLO config is deleted
|
|
func (client *Client) DeleteServiceLevelObjectiveTimeFrames(timeframeByID map[string][]string) (*ServiceLevelObjectiveDeleteTimeFramesResponse, error) {
|
|
var out timeframesDeleteResp
|
|
|
|
if len(timeframeByID) == 0 {
|
|
return nil, fmt.Errorf("nothing specified")
|
|
}
|
|
|
|
if err := client.doJsonRequest("POST", "/v1/slo/bulk_delete", &timeframeByID, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if out.Errors != nil && len(out.Errors) > 0 {
|
|
errMsgs := make([]string, 0)
|
|
for _, e := range out.Errors {
|
|
errMsgs = append(errMsgs, e.Error())
|
|
}
|
|
return nil, fmt.Errorf("errors deleting timeframes: %s", strings.Join(errMsgs, ","))
|
|
}
|
|
|
|
return out.Data, nil
|
|
}
|
|
|
|
// ServiceLevelObjectivesCanDeleteResponse is the response for a check can delete SLO endpoint.
|
|
type ServiceLevelObjectivesCanDeleteResponse struct {
|
|
Data struct {
|
|
OK []string `json:"ok"`
|
|
} `json:"data"`
|
|
Errors map[string]string `json:"errors"`
|
|
}
|
|
|
|
// CheckCanDeleteServiceLevelObjectives checks if the SLO is referenced within Datadog.
|
|
// This is useful to prevent accidental deletion.
|
|
func (client *Client) CheckCanDeleteServiceLevelObjectives(ids []string) (*ServiceLevelObjectivesCanDeleteResponse, error) {
|
|
var out ServiceLevelObjectivesCanDeleteResponse
|
|
|
|
if len(ids) == 0 {
|
|
return nil, fmt.Errorf("nothing specified")
|
|
}
|
|
|
|
uriValues := make(url.Values, 0)
|
|
uriValues.Set("ids", strings.Join(ids, ","))
|
|
uri := "/v1/slo/can_delete?" + uriValues.Encode()
|
|
|
|
if err := client.doJsonRequest("GET", uri, nil, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &out, nil
|
|
}
|
|
|
|
// ServiceLevelObjectiveHistorySeriesPoint is a convenient wrapper for (timestamp, value) history data response.
|
|
type ServiceLevelObjectiveHistorySeriesPoint [2]json.Number
|
|
|
|
// ServiceLevelObjectiveHistoryMetricSeriesData contains the `batch_query` like history data for `metric` based SLOs
|
|
type ServiceLevelObjectiveHistoryMetricSeriesData struct {
|
|
Count int64 `json:"count"`
|
|
Sum json.Number `json:"sum"`
|
|
MetaData struct {
|
|
QueryIndex int `json:"query_index"`
|
|
Aggregator string `json:"aggr"`
|
|
Scope string `json:"scope"`
|
|
Metric string `json:"metric"`
|
|
Expression string `json:"expression"`
|
|
Unit *string `json:"unit"`
|
|
} `json:"metadata"`
|
|
Values []json.Number `json:"values"`
|
|
Times []int64 `json:"times"`
|
|
}
|
|
|
|
// ValuesAsFloats will transform all the values into a slice of float64
|
|
func (d *ServiceLevelObjectiveHistoryMetricSeriesData) ValuesAsFloats() ([]float64, error) {
|
|
out := make([]float64, len(d.Values))
|
|
for i := 0; i < len(d.Values); i++ {
|
|
v, err := d.Values[i].Float64()
|
|
if err != nil {
|
|
return out, fmt.Errorf("could not deserialize value at index %d: %s", i, err)
|
|
}
|
|
out[i] = v
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// ValuesAsInt64s will transform all the values into a slice of int64
|
|
func (d *ServiceLevelObjectiveHistoryMetricSeriesData) ValuesAsInt64s() ([]int64, error) {
|
|
out := make([]int64, len(d.Values))
|
|
for i := 0; i < len(d.Values); i++ {
|
|
v, err := d.Values[i].Int64()
|
|
if err != nil {
|
|
return out, fmt.Errorf("could not deserialize value at index %d: %s", i, err)
|
|
}
|
|
out[i] = v
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// ServiceLevelObjectiveHistoryMetricSeries defines the SLO history data response for `metric` type SLOs
|
|
type ServiceLevelObjectiveHistoryMetricSeries struct {
|
|
ResultType string `json:"res_type"`
|
|
Interval int `json:"interval"`
|
|
ResponseVersion json.Number `json:"resp_version"`
|
|
Query string `json:"query"` // a CSV of <numerator>, <denominator> queries
|
|
Message string `json:"message"` // optional message if there are specific query issues/warnings
|
|
|
|
Numerator *ServiceLevelObjectiveHistoryMetricSeriesData `json:"numerator"`
|
|
Denominator *ServiceLevelObjectiveHistoryMetricSeriesData `json:"denominator"`
|
|
}
|
|
|
|
// ServiceLevelObjectiveHistoryMonitorSeries defines the SLO history data response for `monitor` type SLOs
|
|
type ServiceLevelObjectiveHistoryMonitorSeries struct {
|
|
Uptime float32 `json:"uptime"`
|
|
SpanPrecision json.Number `json:"span_precision"`
|
|
Name string `json:"name"`
|
|
Precision map[string]json.Number `json:"precision"`
|
|
Preview bool `json:"preview"`
|
|
History []ServiceLevelObjectiveHistorySeriesPoint `json:"history"`
|
|
}
|
|
|
|
// ServiceLevelObjectiveHistoryOverall defines the overall SLO history data response
|
|
// for `monitor` type SLOs there is an additional `History` property that rolls up the overall state correctly.
|
|
type ServiceLevelObjectiveHistoryOverall struct {
|
|
Uptime float32 `json:"uptime"`
|
|
SpanPrecision json.Number `json:"span_precision"`
|
|
Name string `json:"name"`
|
|
Precision map[string]json.Number `json:"precision"`
|
|
Preview bool `json:"preview"`
|
|
|
|
// Monitor extension
|
|
History []ServiceLevelObjectiveHistorySeriesPoint `json:"history"`
|
|
}
|
|
|
|
// ServiceLevelObjectiveHistoryResponseData contains the SLO history data response.
|
|
// for `monitor` based SLOs use the `Groups` property for historical data along with the `Overall.History`
|
|
// for `metric` based SLOs use the `Metrics` property for historical data. This contains `batch_query` like response
|
|
// data
|
|
type ServiceLevelObjectiveHistoryResponseData struct {
|
|
Errors []string `json:"errors"`
|
|
ToTs int64 `json:"to_ts"`
|
|
FromTs int64 `json:"from_ts"`
|
|
Thresholds map[string]ServiceLevelObjectiveThreshold `json:"thresholds"`
|
|
Overall *ServiceLevelObjectiveHistoryOverall `json:"overall"`
|
|
|
|
// metric based SLO
|
|
Metrics *ServiceLevelObjectiveHistoryMetricSeries `json:"series"`
|
|
|
|
// monitor based SLO
|
|
Groups []*ServiceLevelObjectiveHistoryMonitorSeries `json:"groups"`
|
|
}
|
|
|
|
// ServiceLevelObjectiveHistoryResponse is the canonical response for SLO history data.
|
|
type ServiceLevelObjectiveHistoryResponse struct {
|
|
Data *ServiceLevelObjectiveHistoryResponseData `json:"data"`
|
|
Error *string `json:"error"`
|
|
}
|
|
|
|
// GetServiceLevelObjectiveHistory will retrieve the history data for a given SLO and provided from/to times
|
|
func (client *Client) GetServiceLevelObjectiveHistory(id string, fromTs time.Time, toTs time.Time) (*ServiceLevelObjectiveHistoryResponse, error) {
|
|
var out ServiceLevelObjectiveHistoryResponse
|
|
|
|
if id == "" {
|
|
return nil, fmt.Errorf("nothing specified")
|
|
}
|
|
|
|
if !toTs.After(fromTs) {
|
|
return nil, fmt.Errorf("toTs must be after fromTs")
|
|
}
|
|
|
|
uriValues := make(url.Values, 0)
|
|
uriValues.Set("from_ts", fmt.Sprintf("%d", fromTs.Unix()))
|
|
uriValues.Set("to_ts", fmt.Sprintf("%d", toTs.Unix()))
|
|
|
|
uri := fmt.Sprintf("/v1/slo/%s/history?%s", id, uriValues.Encode())
|
|
|
|
if err := client.doJsonRequest("GET", uri, nil, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &out, nil
|
|
}
|