From 50a122368442593d9f955d8cd017c11b0fd7508f Mon Sep 17 00:00:00 2001 From: Oluwaseun Obajobi Date: Thu, 2 May 2019 19:07:30 +0400 Subject: [PATCH] add alerts plugins conditions --- api/alert_plugins_conditions.go | 121 ++++++++++ api/alert_plugins_conditions_test.go | 344 +++++++++++++++++++++++++++ api/types.go | 21 ++ cmd/alert_plugins_conditions.go | 43 ++++ 4 files changed, 529 insertions(+) create mode 100755 api/alert_plugins_conditions.go create mode 100644 api/alert_plugins_conditions_test.go create mode 100644 cmd/alert_plugins_conditions.go diff --git a/api/alert_plugins_conditions.go b/api/alert_plugins_conditions.go new file mode 100755 index 00000000..7982f178 --- /dev/null +++ b/api/alert_plugins_conditions.go @@ -0,0 +1,121 @@ +package api + +import ( + "fmt" + "net/url" + "strconv" +) + +func (c *Client) queryAlertPluginsConditions(policyID int) ([]AlertPluginsCondition, error) { + conditions := []AlertPluginsCondition{} + + reqURL, err := url.Parse("/alerts_plugins_conditions.json") + if err != nil { + return nil, err + } + + qs := reqURL.Query() + qs.Set("policy_id", strconv.Itoa(policyID)) + + reqURL.RawQuery = qs.Encode() + + nextPath := reqURL.String() + + for nextPath != "" { + resp := struct { + PluginConditions []AlertPluginsCondition `json:"plugins_conditions,omitempty"` + }{} + + nextPath, err = c.Do("GET", nextPath, nil, &resp) + if err != nil { + return nil, err + } + + for _, c := range resp.PluginConditions { + c.PolicyID = policyID + } + + conditions = append(conditions, resp.PluginConditions...) + } + + return conditions, nil +} + +// GetAlertPluginsCondition gets information about a plugin alert condition given an ID and policy ID. +func (c *Client) GetAlertPluginsCondition(policyID, id int) (*AlertPluginsCondition, error) { + conditions, err := c.queryAlertPluginsConditions(policyID) + if err != nil { + return nil, err + } + + for _, condition := range conditions { + if condition.ID == id { + return &condition, nil + } + } + + return nil, ErrNotFound +} + +// ListAlertPluginsConditions returns Plugin alert conditions for the specified policy. +func (c *Client) ListAlertPluginsConditions(policyID int) ([]AlertPluginsCondition, error) { + return c.queryAlertPluginsConditions(policyID) +} + +// CreateAlertPluginsCondition creates an Plugin alert condition given the passed configuration. +func (c *Client) CreateAlertPluginsCondition(condition AlertPluginsCondition) (*AlertPluginsCondition, error) { + policyID := condition.PolicyID + + req := struct { + Condition AlertPluginsCondition `json:"plugins_condition"` + }{ + Condition: condition, + } + + resp := struct { + Condition AlertPluginsCondition `json:"plugins_condition,omitempty"` + }{} + + u := &url.URL{Path: fmt.Sprintf("/alerts_plugins_conditions/policies/%v.json", policyID)} + _, err := c.Do("POST", u.String(), req, &resp) + if err != nil { + return nil, err + } + + resp.Condition.PolicyID = policyID + + return &resp.Condition, nil +} + +// UpdateAlertPluginsCondition updates a Plugin alert condition with the specified changes. +func (c *Client) UpdateAlertPluginsCondition(condition AlertPluginsCondition) (*AlertPluginsCondition, error) { + policyID := condition.PolicyID + id := condition.ID + + req := struct { + Condition AlertPluginsCondition `json:"plugins_condition"` + }{ + Condition: condition, + } + + resp := struct { + Condition AlertPluginsCondition `json:"plugins_condition,omitempty"` + }{} + + u := &url.URL{Path: fmt.Sprintf("/alerts_plugins_conditions/%v.json", id)} + _, err := c.Do("PUT", u.String(), req, &resp) + if err != nil { + return nil, err + } + + resp.Condition.PolicyID = policyID + + return &resp.Condition, nil +} + +// DeleteAlertPluginsCondition removes the Plugin alert condition given the specified ID and policy ID. +func (c *Client) DeleteAlertPluginsCondition(policyID, id int) error { + u := &url.URL{Path: fmt.Sprintf("/alerts_plugins_conditions/%v.json", id)} + _, err := c.Do("DELETE", u.String(), nil, nil) + return err +} diff --git a/api/alert_plugins_conditions_test.go b/api/alert_plugins_conditions_test.go new file mode 100644 index 00000000..55790362 --- /dev/null +++ b/api/alert_plugins_conditions_test.go @@ -0,0 +1,344 @@ +package api + +import ( + "fmt" + "net/http" + "testing" +) + +func TestQueryAlertPluginsConditions(t *testing.T) { + c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + { + "plugins_conditions": [ + { + "id": 12345, + "name": "Plugins Condition", + "runbook_url": "https://example.com/runbook.md", + "enabled": true, + "entities": [ + "987654321" + ], + "metric_description": "Requests", + "metric": "Component/Requests[Requests/Minute]", + "terms": [ + { + "duration": "10", + "operator": "below", + "priority": "critical", + "threshold": "2", + "time_function": "all" + } + ], + "value_function": "average", + "plugin": { + "query": "11223", + "guid": "com.railsware.haproxy" + } + } + ] + } + `)) + })) + + policyID := 123 + + pluginsAlertConditions, err := c.queryAlertPluginsConditions(policyID) + if err != nil { + t.Log(err) + t.Fatal("queryAlertPluginsConditions error") + } + + if len(pluginsAlertConditions) == 0 { + t.Fatal("No Plugins Alert Conditions found") + } +} + +func TestGetAlertPluginsCondition(t *testing.T) { + c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + { + "plugins_conditions": [ + { + "id": 12345, + "name": "Plugins Condition", + "runbook_url": "https://example.com/runbook.md", + "enabled": true, + "entities": [ + "987654321" + ], + "metric_description": "Requests", + "metric": "Component/Requests[Requests/Minute]", + "terms": [ + { + "duration": "10", + "operator": "below", + "priority": "critical", + "threshold": "2", + "time_function": "all" + } + ], + "value_function": "average", + "plugin": { + "query": "11223", + "guid": "com.railsware.haproxy" + } + } + ] + } + `)) + })) + + policyID := 123 + conditionID := 12345 + + pluginsAlertCondition, err := c.GetAlertPluginsCondition(policyID, conditionID) + if err != nil { + t.Log(err) + t.Fatal("GetAlertPluginsCondition error") + } + if pluginsAlertCondition == nil { + t.Log(err) + t.Fatal("GetAlertPluginsCondition error") + } +} + +func TestListAlertPluginsConditions(t *testing.T) { + c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + { + "plugins_conditions": [ + { + "id": 12345, + "name": "Plugins Condition", + "runbook_url": "https://example.com/runbook.md", + "enabled": true, + "entities": [ + "987654321" + ], + "metric_description": "Requests", + "metric": "Component/Requests[Requests/Minute]", + "terms": [ + { + "duration": "10", + "operator": "below", + "priority": "critical", + "threshold": "2", + "time_function": "all" + } + ], + "value_function": "average", + "plugin": { + "query": "11223", + "guid": "com.railsware.haproxy" + } + } + ] + } + `)) + })) + + policyID := 123 + + pluginsAlertConditions, err := c.ListAlertPluginsConditions(policyID) + if err != nil { + t.Log(err) + t.Fatal("ListAlertPluginsConditions error") + } + if len(pluginsAlertConditions) == 0 { + t.Log(err) + t.Fatal("ListAlertNrqlConditions error") + } +} + +func TestCreateAlertPluginsCondition(t *testing.T) { + c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + { + "plugins_condition": + { + "id": 12345, + "name": "Plugins Condition", + "runbook_url": "https://example.com/runbook.md", + "enabled": true, + "entities": [ + "987654321" + ], + "metric_description": "Queued Sessions", + "metric": "Component/Sessions/Queued[Sessions]", + "terms": [ + { + "duration": "10", + "operator": "below", + "priority": "critical", + "threshold": "2", + "time_function": "all" + } + ], + "value_function": "average", + "plugin": { + "query": "11223", + "guid": "com.railsware.haproxy" + } + } + } + `)) + })) + + pluginsAlertConditionTerms := []AlertConditionTerm{ + { + Duration: 10, + Operator: "below", + Priority: "critical", + Threshold: 2.0, + TimeFunction: "all", + }, + } + + pluginsAlert := AlertPlugin{ + ID: "11223", + GUID: "com.railsware.haproxy", + } + + pluginsEntities := []string{"111222333"} + + pluginAlertCondition := AlertPluginsCondition{ + PolicyID: 123, + Name: "Test Condition", + Enabled: true, + Entities: pluginsEntities, + MetricDescription: "Queued Sessions", + Metric: "Component/Sessions/Queued[Sessions]", + RunbookURL: "https://example.com/runbook.md", + Terms: pluginsAlertConditionTerms, + ValueFunction: "all", + Plugin: pluginsAlert, + } + + pluginAlertConditionResp, err := c.CreateAlertPluginsCondition(pluginAlertCondition) + if err != nil { + t.Log(err) + t.Fatal("CreateAlertPluginsCondition error") + } + if pluginAlertConditionResp == nil { + t.Log(err) + t.Fatal("CreateAlertPluginsCondition error") + } + if pluginAlertConditionResp.ID != 12345 { + t.Fatal("Condition ID was not parsed correctly") + } + if pluginAlertConditionResp.Metric != "Component/Sessions/Queued[Sessions]" { + t.Fatal("Metric was not parsed correctly") + } +} + +func TestUpdateAlertPluginsCondition(t *testing.T) { + c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + { + "plugins_condition": + { + "id": 12345, + "name": "Plugins Condition", + "runbook_url": "https://example.com/runbook.md", + "enabled": true, + "entities": [ + "987654321" + ], + "metric_description": "Queued Sessions", + "metric": "Component/Sessions/Queued[Sessions]", + "terms": [ + { + "duration": "10", + "operator": "below", + "priority": "critical", + "threshold": "2", + "time_function": "all" + } + ], + "value_function": "average", + "plugin": { + "query": "11223", + "guid": "com.railsware.haproxy" + } + } + } + `)) + })) + + pluginsAlertConditionTerms := []AlertConditionTerm{ + { + Duration: 10, + Operator: "below", + Priority: "critical", + Threshold: 2.0, + TimeFunction: "all", + }, + } + + pluginsAlert := AlertPlugin{ + ID: "11223", + GUID: "com.railsware.haproxy", + } + + pluginsEntities := []string{"111222333"} + + pluginAlertCondition := AlertPluginsCondition{ + PolicyID: 123, + Name: "Test Condition", + Enabled: true, + Entities: pluginsEntities, + MetricDescription: "Queued Sessions", + Metric: "Component/Sessions/Queued[Sessions]", + RunbookURL: "https://example.com/runbook.md", + Terms: pluginsAlertConditionTerms, + ValueFunction: "all", + Plugin: pluginsAlert, + } + + pluginAlertConditionResp, err := c.UpdateAlertPluginsCondition(pluginAlertCondition) + if err != nil { + t.Log(err) + t.Fatal("UpdateAlertPluginsCondition error") + } + if pluginAlertConditionResp == nil { + t.Log(err) + t.Fatal("UpdateAlertPluginsCondition error") + } + if pluginAlertConditionResp.ID != 12345 { + t.Fatal("Condition ID was not parsed correctly") + } + if pluginAlertConditionResp.Metric != "Component/Sessions/Queued[Sessions]" { + t.Fatal("Metric was not parsed correctly") + } +} + +func TestDeleteAlertPluginsCondition(t *testing.T) { + policyID := 123 + conditionID := 12345 + c := newTestAPIClient(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + if r.Method != "DELETE" { + t.Fatal("DeleteAlertPluginsCondition did not use DELETE method") + } + if r.URL.Path != fmt.Sprintf("/alerts_plugins_conditions/%v.json", conditionID) { + t.Fatal("DeleteAlertPluginsCondition did not use the correct URL") + } + })) + err := c.DeleteAlertPluginsCondition(policyID, conditionID) + if err != nil { + t.Log(err) + t.Fatal("DeleteAlertPluginsCondition error") + } +} diff --git a/api/types.go b/api/types.go index 87c9f41f..dbb35f07 100755 --- a/api/types.go +++ b/api/types.go @@ -82,6 +82,27 @@ type AlertNrqlCondition struct { Nrql AlertNrqlQuery `json:"nrql,omitempty"` } +// AlertPlugin represents a plugin to use with a Plugin alert condition. +type AlertPlugin struct { + ID string `json:"id,omitempty"` + GUID string `json:"guid,omitempty"` +} + +// AlertPluginsCondition represents a New Relic Plugin Alert condition. +type AlertPluginsCondition struct { + PolicyID int `json:"-"` + ID int `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Enabled bool `json:"enabled"` + Entities []string `json:"entities,omitempty"` + Metric string `json:"metric,omitempty"` + MetricDescription string `json:"metric_description,omitempty"` + RunbookURL string `json:"runbook_url,omitempty"` + Terms []AlertConditionTerm `json:"terms,omitempty"` + ValueFunction string `json:"value_function,omitempty"` + Plugin AlertPlugin `json:"plugin,omitempty"` +} + // AlertSyntheticsCondition represents a New Relic NRQL Alert condition. type AlertSyntheticsCondition struct { PolicyID int `json:"-"` diff --git a/cmd/alert_plugins_conditions.go b/cmd/alert_plugins_conditions.go new file mode 100644 index 00000000..43861298 --- /dev/null +++ b/cmd/alert_plugins_conditions.go @@ -0,0 +1,43 @@ +package cmd + +import ( + "github.com/imdario/mergo" + "github.com/spf13/cobra" +) + +func makeAlertPluginsConditionsCmd(dst cobra.Command) *cobra.Command { + src := cobra.Command{ + Use: "plugins-conditions", + Aliases: []string{"plugin-condition", "plug-cond"}, + } + + if err := mergo.Merge(&dst, src); err != nil { + panic(err) + } + + return &dst +} + +var getAlertPluginsConditionsCmd = makeAlertPluginsConditionsCmd(cobra.Command{ + RunE: func(cmd *cobra.Command, args []string) error { + client, err := newAPIClient(cmd) + if err != nil { + return err + } + policyID, err := cmd.Flags().GetInt("policy-id") + if err != nil { + return err + } + resources, err := client.ListAlertPluginsConditions(policyID) + if err != nil { + return err + } + + return outputList(cmd, resources) + }, +}) + +func init() { + getCmd.AddCommand(getAlertPluginsConditionsCmd) + getAlertPluginsConditionsCmd.Flags().IntP("policy-id", "p", 0, "ID of policy for which to get plugins conditions") +}