Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slo: add methods related to slo resource #208

Merged
merged 1 commit into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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