Skip to content

Commit

Permalink
Merge pull request #12 from humio/mike/add_alerts_notifiers_etc
Browse files Browse the repository at this point in the history
Add support for alerts, notifiers and add additional ingest-token support.
  • Loading branch information
SaaldjorMike authored Feb 19, 2020
2 parents d2c5059 + 1de26b9 commit 450e51a
Show file tree
Hide file tree
Showing 34 changed files with 1,231 additions and 102 deletions.
196 changes: 196 additions & 0 deletions api/alerts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package api

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)

type HumioQuery struct {
QueryString string `yaml:"queryString" json:"queryString"`
Start string `yaml:"start" json:"start"`
End string `yaml:"end" json:"end"`
IsLive bool `yaml:"isLive" json:"isLive"`
}

type Alert struct {
ID string `yaml:"-" json:"id"`
Name string `yaml:"name" json:"name"`
Query HumioQuery `yaml:"query" json:"query"`
Description string `yaml:"description,omitempty" json:"description"`
ThrottleTimeMillis int `yaml:"throttleTimeMillis" json:"throttleTimeMillis"`
Silenced bool `yaml:"silenced" json:"silenced"`
Notifiers []string `yaml:"notifiers" json:"notifiers"`
LinkURL string `yaml:"linkURL" json:"linkURL"`
Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"`
}

type Alerts struct {
client *Client
}

func (c *Client) Alerts() *Alerts { return &Alerts{client: c} }

func (a *Alerts) List(viewName string) ([]Alert, error) {
url := fmt.Sprintf("api/v1/repositories/%s/alerts", viewName)

res, err := a.client.HTTPRequest(http.MethodGet, url, nil)
if err != nil {
log.Fatal(err)
}

return a.unmarshalToAlertList(res)
}

func (a *Alerts) Update(viewName string, alert *Alert) (*Alert, error) {
existingID, err := a.convertAlertNameToID(viewName, alert.Name)
if err != nil {
return nil, fmt.Errorf("could not convert alert name to id: %v", err)
}

jsonStr, err := a.marshalToJSON(alert)
if err != nil {
return nil, fmt.Errorf("unable to convert alert to json string: %v", err)
}

url := fmt.Sprintf("api/v1/repositories/%s/alerts/%s", viewName, existingID)

res, postErr := a.client.HTTPRequest(http.MethodPut, url, bytes.NewBuffer(jsonStr))
if postErr != nil {
return nil, fmt.Errorf("could not add alert in view %s with name %s, got: %v", viewName, alert.Name, postErr)
}
return a.unmarshalToAlert(res)
}

func (a *Alerts) Add(viewName string, alert *Alert, updateExisting bool) (*Alert, error) {
nameAlreadyInUse, err := a.alertNameInUse(viewName, alert.Name)
if err != nil {
return nil, fmt.Errorf("could not determine if alert name is in use: %v", err)
}

if nameAlreadyInUse {
if updateExisting == false {
return nil, fmt.Errorf("alert with name %s already exists", alert.Name)
}
return a.Update(viewName, alert)
}

jsonStr, err := a.marshalToJSON(alert)
if err != nil {
return nil, fmt.Errorf("unable to convert alert to json string: %v", err)
}

url := fmt.Sprintf("api/v1/repositories/%s/alerts/", viewName)

res, err := a.client.HTTPRequest(http.MethodPost, url, bytes.NewBuffer(jsonStr))
if err != nil {
return nil, fmt.Errorf("could not add alert in view %s with name %s, got: %v", viewName, alert.Name, err)
}

return a.unmarshalToAlert(res)
}

func (a *Alerts) Get(viewName, alertName string) (*Alert, error) {
alertID, err := a.convertAlertNameToID(viewName, alertName)
if err != nil {
return nil, fmt.Errorf("could not find a notifier in view %s with name: %s", viewName, alertName)
}

url := fmt.Sprintf("api/v1/repositories/%s/alerts/%s", viewName, alertID)

res, err := a.client.HTTPRequest(http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("could not get alert with id %s, got: %v", alertID, err)
}

return a.unmarshalToAlert(res)
}

func (a *Alerts) Delete(viewName, alertName string) error {
alertID, err := a.convertAlertNameToID(viewName, alertName)
if err != nil {
return fmt.Errorf("could not find a notifier in view %s with name: %s", viewName, alertName)
}

url := fmt.Sprintf("api/v1/repositories/%s/alerts/%s", viewName, alertID)

res, err := a.client.HTTPRequest(http.MethodDelete, url, nil)
if err != nil {
log.Fatal(err)
}

if err != nil || res.StatusCode != http.StatusNoContent {
return fmt.Errorf("could not delete alert in view %s with id %s, got: %v", viewName, alertID, err)
}
return nil
}

func (a *Alerts) marshalToJSON(alert *Alert) ([]byte, error) {
// Humio requires notifiers to be specified even if no notifier is desired
if alert.Notifiers == nil {
alert.Notifiers = []string{}
}

jsonStr, err := json.Marshal(alert)
if err != nil {
return nil, fmt.Errorf("unable to convert alert to json string: %v", err)
}
return jsonStr, nil
}

func (a *Alerts) unmarshalToAlertList(res *http.Response) ([]Alert, error) {
body, readErr := ioutil.ReadAll(res.Body)
if readErr != nil {
log.Fatal(readErr)
}

alertList := []Alert{}
jsonErr := json.Unmarshal(body, &alertList)
if jsonErr != nil {
log.Fatal(jsonErr)
}
return alertList, nil
}

func (a *Alerts) unmarshalToAlert(res *http.Response) (*Alert, error) {
body, readErr := ioutil.ReadAll(res.Body)
if readErr != nil {
log.Fatal(readErr)
}

alert := Alert{}
jsonErr := json.Unmarshal(body, &alert)
if jsonErr != nil {
log.Fatal(jsonErr)
}
return &alert, nil
}

func (a *Alerts) convertAlertNameToID(viewName, alertName string) (string, error) {
listOfAlerts, err := a.List(viewName)
if err != nil {
return "", fmt.Errorf("could not list all alerts for view %s: %v", viewName, err)
}
for _, v := range listOfAlerts {
if v.Name == alertName {
return v.ID, nil
}
}
return "", fmt.Errorf("could not find an alert in view %s with name: %s", viewName, alertName)
}

func (a *Alerts) alertNameInUse(viewName, alertName string) (bool, error) {
listOfAlerts, err := a.List(viewName)
if err != nil {
return true, fmt.Errorf("could not list all alerts for view %s: %v", viewName, err)
}
for _, v := range listOfAlerts {
if v.Name == alertName {
return true, nil
}
}
return false, nil
}
21 changes: 7 additions & 14 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,24 +62,17 @@ func (c *Client) Mutate(mutation interface{}, variables map[string]interface{})
return graphqlErr
}

func (c *Client) httpGET(path string) (*http.Response, error) {
url := c.Address() + path
req, reqErr := http.NewRequest("GET", url, bytes.NewBuffer([]byte("")))
req.Header.Set("Authorization", "Bearer "+c.Token())
req.Header.Set("Accept", "application/json")
var client = &http.Client{}

if reqErr != nil {
return nil, reqErr
func (c *Client) HTTPRequest(httpMethod string, path string, body *bytes.Buffer) (*http.Response, error) {
if body == nil {
body = bytes.NewBuffer([]byte(""))
}
return client.Do(req)
}

func (c *Client) HttpPOST(path string, jsonStr *bytes.Buffer) (*http.Response, error) {
url := c.Address() + path
req, reqErr := http.NewRequest("POST", url, jsonStr)
req.Header.Set("Authorization", "Bearer "+c.config.Token)

req, reqErr := http.NewRequest(httpMethod, url, body)
req.Header.Set("Authorization", "Bearer "+c.Token())
req.Header.Set("Content-Type", "application/json")

var client = &http.Client{}

if reqErr != nil {
Expand Down
2 changes: 1 addition & 1 deletion api/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ type ClusterNodes struct {
client *Client
}

func (n *Client) ClusterNodes() *ClusterNodes { return &ClusterNodes{client: n} }
func (c *Client) ClusterNodes() *ClusterNodes { return &ClusterNodes{client: c} }

func (n *ClusterNodes) List() ([]ClusterNode, error) {
var q struct {
Expand Down
33 changes: 25 additions & 8 deletions api/ingest-tokens.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package api

import (
"fmt"

"github.com/shurcooL/graphql"
)

Expand All @@ -24,7 +26,7 @@ type ingestTokenData struct {
}
}

func (p *IngestTokens) List(repo string) ([]IngestToken, error) {
func (i *IngestTokens) List(repo string) ([]IngestToken, error) {
var query struct {
Result struct {
IngestTokens []ingestTokenData
Expand All @@ -35,20 +37,35 @@ func (p *IngestTokens) List(repo string) ([]IngestToken, error) {
"repositoryName": graphql.String(repo),
}

err := p.client.Query(&query, variables)
err := i.client.Query(&query, variables)

if err != nil {
return nil, err
}

tokens := make([]IngestToken, len(query.Result.IngestTokens))
for i, tokenData := range query.Result.IngestTokens {
tokens[i] = *toIngestToken(tokenData)
for idx, tokenData := range query.Result.IngestTokens {
tokens[idx] = *toIngestToken(tokenData)
}

return tokens, nil
}

func (i *IngestTokens) Get(repoName, tokenName string) (*IngestToken, error) {
tokensInRepo, err := i.List(repoName)
if err != nil {
return nil, err
}

for _, token := range tokensInRepo {
if token.Name == tokenName {
return &token, nil
}
}

return nil, fmt.Errorf("could not find an ingest token with name '%s' in repo '%s'", tokenName, repoName)
}

func toIngestToken(data ingestTokenData) *IngestToken {
var parser string
if data.Parser != nil {
Expand All @@ -62,7 +79,7 @@ func toIngestToken(data ingestTokenData) *IngestToken {
}
}

func (p *IngestTokens) Add(repo string, name string, parserName string) (*IngestToken, error) {
func (i *IngestTokens) Add(repo string, name string, parserName string) (*IngestToken, error) {
var mutation struct {
Result struct {
IngestToken ingestTokenData
Expand All @@ -79,7 +96,7 @@ func (p *IngestTokens) Add(repo string, name string, parserName string) (*Ingest
"parser": parserNameVar,
}

err := p.client.Mutate(&mutation, variables)
err := i.client.Mutate(&mutation, variables)

if err != nil {
return nil, err
Expand All @@ -88,7 +105,7 @@ func (p *IngestTokens) Add(repo string, name string, parserName string) (*Ingest
return toIngestToken(mutation.Result.IngestToken), err
}

func (p *IngestTokens) Remove(repo string, tokenName string) error {
func (i *IngestTokens) Remove(repo string, tokenName string) error {
var mutation struct {
Result struct {
Type string `graphql:"__typename"`
Expand All @@ -100,5 +117,5 @@ func (p *IngestTokens) Remove(repo string, tokenName string) error {
"repositoryName": graphql.String(repo),
}

return p.client.Mutate(&mutation, variables)
return i.client.Mutate(&mutation, variables)
}
8 changes: 4 additions & 4 deletions api/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (l OnPremLicense) LicenseType() string {

func (c *Client) Licenses() *Licenses { return &Licenses{client: c} }

func (p *Licenses) Install(license string) error {
func (l *Licenses) Install(license string) error {

var mutation struct {
UpdateLicenseKey struct {
Expand All @@ -64,10 +64,10 @@ func (p *Licenses) Install(license string) error {
"license": graphql.String(license),
}

return p.client.Mutate(&mutation, variables)
return l.client.Mutate(&mutation, variables)
}

func (c *Licenses) Get() (License, error) {
func (l *Licenses) Get() (License, error) {
var query struct {
License struct {
ExpiresAt string
Expand All @@ -80,7 +80,7 @@ func (c *Licenses) Get() (License, error) {
}
}

err := c.client.Query(&query, nil)
err := l.client.Query(&query, nil)

if err != nil {
return nil, err
Expand Down
Loading

0 comments on commit 450e51a

Please sign in to comment.