Skip to content

Commit

Permalink
slo: add methods related to slo resource
Browse files Browse the repository at this point in the history
  • Loading branch information
dwalasek committed Jan 5, 2024
1 parent 9e307c0 commit e0a3c2b
Show file tree
Hide file tree
Showing 7 changed files with 526 additions and 0 deletions.
71 changes: 71 additions & 0 deletions slo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package signalfx

import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/signalfx/signalfx-go/slo"
"io"
"net/http"
)

const SloAPIURL = "/v2/slo"

func (c *Client) GetSlo(ctx context.Context, id string) (*slo.SloObject, error) {
return c.executeSloRequest(ctx, SloAPIURL+"/"+id, http.MethodGet, http.StatusOK, nil)
}

func (c *Client) CreateSlo(ctx context.Context, sloRequest *slo.SloObject) (*slo.SloObject, error) {
return c.executeSloRequest(ctx, SloAPIURL, http.MethodPost, http.StatusOK, sloRequest)
}

func (c *Client) ValidateSlo(ctx context.Context, sloRequest *slo.SloObject) error {
_, err := c.executeSloRequest(ctx, SloAPIURL+"/validate", http.MethodPost, http.StatusNoContent, sloRequest)
return err
}

func (c *Client) UpdateSlo(ctx context.Context, id string, sloRequest *slo.SloObject) (*slo.SloObject, error) {
return c.executeSloRequest(ctx, SloAPIURL+"/"+id, http.MethodPut, http.StatusOK, sloRequest)
}

func (c *Client) DeleteSlo(ctx context.Context, id string) error {
_, err := c.executeSloRequest(ctx, SloAPIURL+"/"+id, http.MethodDelete, http.StatusNoContent, nil)
return err
}

func (c *Client) executeSloRequest(ctx context.Context, url string, method string, expectedValidStatus int, sloRequest *slo.SloObject) (*slo.SloObject, error) {
var body io.Reader

if sloRequest != nil {
payload, err := json.Marshal(sloRequest)
if err != nil {
return nil, err
}

body = bytes.NewReader(payload)
}

resp, err := c.doRequest(ctx, method, url, nil, body)
if resp != nil {
defer resp.Body.Close()
}

if err != nil {
return nil, err
}

if resp.StatusCode != expectedValidStatus {
message, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("Bad status %d: %s", resp.StatusCode, message)
}

if resp.Body != nil {
returnedSlo := &slo.SloObject{}
err = json.NewDecoder(resp.Body).Decode(returnedSlo)
return returnedSlo, nil
} else {
_, _ = io.Copy(io.Discard, resp.Body)
return nil, nil
}
}
112 changes: 112 additions & 0 deletions slo/model_slo_alert_rule_object.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package slo

import (
"encoding/json"
"fmt"
"github.com/signalfx/signalfx-go/detector"
)

const (
BreachRule = "BREACH"
ErrorBudgetLeftRule = "ERROR_BUDGET_LEFT"
BurnRateRule = "BURN_RATE"
)

type BreachSloAlertRule struct {
Rules []*BreachDetectorRule `json:"rules,omitempty"`
}

type BreachDetectorRule struct {
detector.Rule
Parameters *BreachDetectorParameters `json:"parameters,omitempty"`
}

type BreachDetectorParameters struct {
FireLasting string `json:"fireLasting,omitempty"`
PercentOfLasting float64 `json:"percentOfLasting,omitempty"`
}

type ErrorBudgetLeftSloAlertRule struct {
Rules []*ErrorBudgetLeftDetectorRule `json:"rules,omitempty"`
}

type ErrorBudgetLeftDetectorRule struct {
detector.Rule
Parameters *ErrorBudgetLeftDetectorParameters `json:"parameters,omitempty"`
}

type ErrorBudgetLeftDetectorParameters struct {
FireLasting string `json:"fireLasting,omitempty"`
PercentOfLasting float64 `json:"percentOfLasting,omitempty"`
PercentErrorBudgetLeft float64 `json:"percentErrorBudgetLeft,omitempty"`
}

type BurnRateSloAlertRule struct {
Rules []*BurnRateDetectorRule `json:"rules,omitempty"`
}

type BurnRateDetectorRule struct {
detector.Rule
Parameters *BurnRateDetectorParameters `json:"parameters,omitempty"`
}

type BurnRateDetectorParameters struct {
ShortWindow1 string `json:"shortWindow1,omitempty"`
LongWindow1 string `json:"longWindow1,omitempty"`
ShortWindow2 string `json:"shortWindow2,omitempty"`
LongWindow2 string `json:"longWindow2,omitempty"`
BurnRateThreshold1 float64 `json:"burnRateThreshold1,omitempty"`
BurnRateThreshold2 float64 `json:"burnRateThreshold2,omitempty"`
}

type BaseSloAlertRule struct {
Type string `json:"type,omitempty"`
}

type SloAlertRule struct {
BaseSloAlertRule
*BreachSloAlertRule
*ErrorBudgetLeftSloAlertRule
*BurnRateSloAlertRule
}

func (rule *SloAlertRule) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &rule.BaseSloAlertRule); err != nil {
return err
}
switch rule.Type {
case BreachRule:
rule.BreachSloAlertRule = &BreachSloAlertRule{}
return json.Unmarshal(data, rule.BreachSloAlertRule)
case ErrorBudgetLeftRule:
rule.ErrorBudgetLeftSloAlertRule = &ErrorBudgetLeftSloAlertRule{}
return json.Unmarshal(data, rule.ErrorBudgetLeftSloAlertRule)
case BurnRateRule:
rule.BurnRateSloAlertRule = &BurnRateSloAlertRule{}
return json.Unmarshal(data, rule.BurnRateSloAlertRule)
default:
return fmt.Errorf("unrecognized SLO alert rule type %s", rule.Type)
}
}

func (rule *SloAlertRule) MarshalJSON() ([]byte, error) {
switch rule.Type {
case BreachRule:
return json.Marshal(struct {
BaseSloAlertRule
*BreachSloAlertRule
}{rule.BaseSloAlertRule, rule.BreachSloAlertRule})
case ErrorBudgetLeftRule:
return json.Marshal(struct {
BaseSloAlertRule
*ErrorBudgetLeftSloAlertRule
}{rule.BaseSloAlertRule, rule.ErrorBudgetLeftSloAlertRule})
case BurnRateRule:
return json.Marshal(struct {
BaseSloAlertRule
*BurnRateSloAlertRule
}{rule.BaseSloAlertRule, rule.BurnRateSloAlertRule})
default:
return nil, fmt.Errorf("unrecognized SLO alert rule type %s", rule.Type)
}
}
81 changes: 81 additions & 0 deletions slo/model_slo_object.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package slo

import (
"encoding/json"
"fmt"
)

const (
RequestBased = "RequestBased"
WindowsBased = "WindowsBased"
)

type BaseSlo struct {
Creator string `json:"creator,omitempty"`
LastUpdatedBy string `json:"lastUpdatedBy,omitempty"`
Created int64 `json:"created,omitempty"`
LastUpdated int64 `json:"lastUpdated,omitempty"`
Id string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
Targets []SloTarget `json:"targets,omitempty"`
Type string `json:"type,omitempty"`
Metadata []string `json:"metadata,omitempty"`
}

type SloObject struct {
BaseSlo
*RequestBasedSlo
*WindowBasedSlo
}

type RequestBasedSlo struct {
Inputs *RequestBasedSloInput `json:"inputs,omitempty"`
}

type WindowBasedSlo struct {
Inputs *WindowBasedSloInput `json:"inputs,omitempty"`
}

type RequestBasedSloInput struct {
ProgramText string `json:"programText,omitempty"`
GoodEventsLabel string `json:"goodEventsLabel,omitempty"`
TotalEventsLabel string `json:"totalEventsLabel,omitempty"`
}

type WindowBasedSloInput struct {
ProgramText string `json:"programText,omitempty"`
}

func (slo *SloObject) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &slo.BaseSlo); err != nil {
return err
}
switch slo.Type {
case RequestBased:
slo.RequestBasedSlo = &RequestBasedSlo{}
return json.Unmarshal(data, slo.RequestBasedSlo)
case WindowsBased:
slo.WindowBasedSlo = &WindowBasedSlo{}
return json.Unmarshal(data, slo.WindowBasedSlo)
default:
return fmt.Errorf("unrecognized SLO type %s", slo.Type)
}
}

func (slo SloObject) MarshalJSON() ([]byte, error) {
switch slo.Type {
case RequestBased:
return json.Marshal(struct {
BaseSlo
*RequestBasedSlo
}{slo.BaseSlo, slo.RequestBasedSlo})
case WindowsBased:
return json.Marshal(struct {
BaseSlo
*WindowBasedSlo
}{slo.BaseSlo, slo.WindowBasedSlo})
default:
return nil, fmt.Errorf("unrecognized SLO type %s", slo.Type)
}
}
65 changes: 65 additions & 0 deletions slo/model_slo_target.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package slo

import (
"encoding/json"
"fmt"
)

const (
RollingWindowTarget = "RollingWindow"
CalendarWindowTarget = "CalendarWindow"
)

type SloTarget struct {
BaseSloTarget
*RollingWindowSloTarget
*CalendarWindowSloTarget
}

type BaseSloTarget struct {
Slo float64 `json:"slo,omitempty"`
SloAlertRules []SloAlertRule `json:"sloAlertRules,omitempty"`
Type string `json:"type,omitempty"`
}

type RollingWindowSloTarget struct {
CompliancePeriod string `json:"compliancePeriod,omitempty"`
}

type CalendarWindowSloTarget struct {
CycleType string `json:"cycleType,omitempty"`
CycleStart string `json:"cycleStart,omitempty"`
}

func (target *SloTarget) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &target.BaseSloTarget); err != nil {
return err
}
switch target.Type {
case RollingWindowTarget:
target.RollingWindowSloTarget = &RollingWindowSloTarget{}
return json.Unmarshal(data, target.RollingWindowSloTarget)
case CalendarWindowTarget:
target.CalendarWindowSloTarget = &CalendarWindowSloTarget{}
return json.Unmarshal(data, target.CalendarWindowSloTarget)
default:
return fmt.Errorf("unrecognized SLO target type %s", target.Type)
}
}

func (target *SloTarget) MarshalJSON() ([]byte, error) {
switch target.Type {
case RollingWindowTarget:
return json.Marshal(struct {
BaseSloTarget
*RollingWindowSloTarget
}{target.BaseSloTarget, target.RollingWindowSloTarget})
case CalendarWindowTarget:
return json.Marshal(struct {
BaseSloTarget
*CalendarWindowSloTarget
}{target.BaseSloTarget, target.CalendarWindowSloTarget})
default:
return nil, fmt.Errorf("unrecognized SLO target type %s", target.Type)
}
}
Loading

0 comments on commit e0a3c2b

Please sign in to comment.