Skip to content

Commit

Permalink
feat(cli): New VictorOps alert channel (#318)
Browse files Browse the repository at this point in the history
Signed-off-by: Darren Murray <darren.murray@lacework.net>
  • Loading branch information
dmurray-lacework authored Feb 16, 2021
1 parent 8e0071a commit dfcd34a
Show file tree
Hide file tree
Showing 6 changed files with 474 additions and 0 deletions.
29 changes: 29 additions & 0 deletions api/_examples/victorops-alert-channel/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package main

import (
"fmt"
"log"

"github.com/lacework/go-sdk/api"
)

func main() {
lacework, err := api.NewClient("account", api.WithApiKeys("KEY", "SECRET"))
if err != nil {
log.Fatal(err)
}

myVictorOpsChannel := api.NewVictorOpsAlertChannel("victorops-alert-from-golang",
api.VictorOpsChannelData{
WebhookURL: "https://alert.victorops.com/integrations/generic/20131114/alert/31e945ee-5cad-44e7-afb0-97c20ea80dd8/database",
},
)

response, err := lacework.Integrations.CreateVictorOpsAlertChannel(myVictorOpsChannel)
if err != nil {
log.Fatal(err)
}

// Output: VictorOps alert channel created: THE-INTEGRATION-GUID
fmt.Printf("VictorOps alert channel created: %s", response.Data[0].IntgGuid)
}
114 changes: 114 additions & 0 deletions api/integration_alert_channels_victorops.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//
// Author:: Darren Murray(<darren.murray@lacework.net>)
// Copyright:: Copyright 2020, Lacework Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package api

import "github.com/pkg/errors"

// NewVictorOpsAlertChannel returns an instance of VictorOpsAlertChannel
// with the provided name and data.
//
// Basic usage: Initialize a new VictorOpsAlertChannel struct, then
// use the new instance to do CRUD operations
//
// client, err := api.NewClient("account")
// if err != nil {
// return err
// }
//
// datadog := api.NewVictorOpsAlertChannel("foo",
// api.VictorOpsChannelData{
// WebhookURL: "https://alert.victorops.com/integrations/generic/20131114/alert/31e945ee-5cad-44e7-afb0-97c20ea80dd8/database,
// },
// )
//
// client.Integrations.CreateVictorOpsAlertChannel(datadogChannel)
//
func NewVictorOpsAlertChannel(name string, data VictorOpsChannelData) VictorOpsAlertChannel {
return VictorOpsAlertChannel{
commonIntegrationData: commonIntegrationData{
Name: name,
Type: VictorOpsChannelIntegration.String(),
Enabled: 1,
},
Data: data,
}
}

// CreateVictorOpsAlertChannel creates a datadog alert channel integration on the Lacework Server
func (svc *IntegrationsService) CreateVictorOpsAlertChannel(integration VictorOpsAlertChannel) (
response VictorOpsAlertChannelResponse,
err error,
) {
err = svc.create(integration, &response)
return
}

// GetVictorOpsAlertChannel gets a datadog alert channel integration that matches with
// the provided integration guid on the Lacework Server
func (svc *IntegrationsService) GetVictorOpsAlertChannel(guid string) (response VictorOpsAlertChannelResponse,
err error) {
err = svc.get(guid, &response)
return
}

// UpdateVictorOpsAlertChannel updates a single datadog alert channel integration
func (svc *IntegrationsService) UpdateVictorOpsAlertChannel(data VictorOpsAlertChannel) (
response VictorOpsAlertChannelResponse,
err error,
) {
err = svc.update(data.IntgGuid, data, &response)
return
}

// ListVictorOpsAlertChannel lists the datadog alert channel integrations available on the Lacework Server
func (svc *IntegrationsService) ListVictorOpsAlertChannel() (response VictorOpsAlertChannelResponse, err error) {
err = svc.listByType(VictorOpsChannelIntegration, &response)
return
}

type VictorOpsAlertChannelResponse struct {
Data []VictorOpsAlertChannel `json:"data"`
Ok bool `json:"ok"`
Message string `json:"message"`
}

// VictorOpsSite returns the datadogSite type for the corresponding string input
func VictorOpsSite(site string) (datadogSite, error) {
if val, ok := datadogSites[site]; ok {
return val, nil
}
return "", errors.Errorf("%v is not a valid VictorOps Site", site)
}

// VictorOpsService returns the datadogService type for the corresponding string input
func VictorOpsService(service string) (datadogService, error) {
if val, ok := datadogServices[service]; ok {
return val, nil
}
return "", errors.Errorf("%v is not a valid VictorOps Service", service)
}

type VictorOpsAlertChannel struct {
commonIntegrationData
Data VictorOpsChannelData `json:"DATA"`
}

type VictorOpsChannelData struct {
WebhookURL string `json:"INTG_URL" mapstructure:"INTG_URL"`
}
243 changes: 243 additions & 0 deletions api/integration_alert_channels_victorops_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
//
// Author:: Darren Murray (<darren.murray@lacework.net>)
// Copyright:: Copyright 2020, Lacework Inc.
// License:: Apache License, Version 2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package api_test

import (
"fmt"
"net/http"
"strings"
"testing"

"github.com/stretchr/testify/assert"

"github.com/lacework/go-sdk/api"
"github.com/lacework/go-sdk/internal/intgguid"
"github.com/lacework/go-sdk/internal/lacework"
)

func TestIntegrationsNewVictorOpsAlertChannel(t *testing.T) {
subject := api.NewVictorOpsAlertChannel("integration_name",
api.VictorOpsChannelData{
WebhookURL: "https://alert.victorops.com/integrations/generic/20131114/alert/31e945ee-5cad-44e7-afb0-97c20ea80dd8/database",
},
)
assert.Equal(t, api.VictorOpsChannelIntegration.String(), subject.Type)
}

func TestIntegrationsCreateVictorOpsAlertChannel(t *testing.T) {
var (
intgGUID = intgguid.New()
fakeServer = lacework.MockServer()
)
fakeServer.MockAPI("external/integrations", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method, "CreateVictorOpsAlertChannel should be a POST method")

if assert.NotNil(t, r.Body) {
body := httpBodySniffer(r)
assert.Contains(t, body, "integration_name", "integration name is missing")
assert.Contains(t, body, "VICTOR_OPS", "wrong integration type")
assert.Contains(t, body, "https://alert.victorops.com/integrations/generic/20131114/alert/31e945ee-5cad-44e7-afb0-97c20ea80dd8/database", "wrong victorops url")
assert.Contains(t, body, "ENABLED\":1", "integration is not enabled")
}

fmt.Fprintf(w, victoropsChannelIntegrationJsonResponse(intgGUID))
})
defer fakeServer.Close()

c, err := api.NewClient("test",
api.WithToken("TOKEN"),
api.WithURL(fakeServer.URL()),
)
assert.Nil(t, err)

data := api.NewVictorOpsAlertChannel("integration_name",
api.VictorOpsChannelData{
WebhookURL: "https://alert.victorops.com/integrations/generic/20131114/alert/31e945ee-5cad-44e7-afb0-97c20ea80dd8/database",
},
)
assert.Equal(t, "integration_name", data.Name, "VictorOps integration name mismatch")
assert.Equal(t, "VICTOR_OPS", data.Type, "a new VictorOps integration should match its type")
assert.Equal(t, 1, data.Enabled, "a new VictorOps integration should be enabled")

response, err := c.Integrations.CreateVictorOpsAlertChannel(data)
assert.Nil(t, err)
assert.NotNil(t, response)
assert.True(t, response.Ok)
if assert.Equal(t, 1, len(response.Data)) {
resData := response.Data[0]
assert.Equal(t, intgGUID, resData.IntgGuid)
assert.Equal(t, "integration_name", resData.Name)
assert.True(t, resData.State.Ok)
assert.Equal(t, "https://alert.victorops.com/integrations/generic/20131114/alert/31e945ee-5cad-44e7-afb0-97c20ea80dd8/database", resData.Data.WebhookURL)
}
}

func TestIntegrationsGetVictorOpsAlertChannel(t *testing.T) {
var (
intgGUID = intgguid.New()
apiPath = fmt.Sprintf("external/integrations/%s", intgGUID)
fakeServer = lacework.MockServer()
)
fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method, "GetVictorOpsAlertChannel should be a GET method")
fmt.Fprintf(w, victoropsChannelIntegrationJsonResponse(intgGUID))
})
defer fakeServer.Close()

c, err := api.NewClient("test",
api.WithToken("TOKEN"),
api.WithURL(fakeServer.URL()),
)
assert.Nil(t, err)

response, err := c.Integrations.GetVictorOpsAlertChannel(intgGUID)
assert.Nil(t, err)
assert.NotNil(t, response)
assert.True(t, response.Ok)
if assert.Equal(t, 1, len(response.Data)) {
resData := response.Data[0]
assert.Equal(t, intgGUID, resData.IntgGuid)
assert.Equal(t, "integration_name", resData.Name)
assert.True(t, resData.State.Ok)
assert.Equal(t, "https://alert.victorops.com/integrations/generic/20131114/alert/31e945ee-5cad-44e7-afb0-97c20ea80dd8/database", resData.Data.WebhookURL)
}
}

func TestIntegrationsUpdateVictorOpsAlertChannel(t *testing.T) {
var (
intgGUID = intgguid.New()
apiPath = fmt.Sprintf("external/integrations/%s", intgGUID)
fakeServer = lacework.MockServer()
)
fakeServer.MockAPI(apiPath, func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PATCH", r.Method, "UpdateVictorOpsAlertChannel should be a PATCH method")

if assert.NotNil(t, r.Body) {
body := httpBodySniffer(r)
assert.Contains(t, body, intgGUID, "INTG_GUID missing")
assert.Contains(t, body, "integration_name", "integration name is missing")
assert.Contains(t, body, "VICTOR_OPS", "wrong integration type")
assert.Contains(t, body, "https://alert.victorops.com/integrations/generic/20131114/alert/31e945ee-5cad-44e7-afb0-97c20ea80dd8/database", "wrong victorops url")
assert.Contains(t, body, "ENABLED\":1", "integration is not enabled")
}

fmt.Fprintf(w, victoropsChannelIntegrationJsonResponse(intgGUID))
})
defer fakeServer.Close()

c, err := api.NewClient("test",
api.WithToken("TOKEN"),
api.WithURL(fakeServer.URL()),
)
assert.Nil(t, err)

data := api.NewVictorOpsAlertChannel("integration_name",
api.VictorOpsChannelData{
WebhookURL: "https://alert.victorops.com/integrations/generic/20131114/alert/31e945ee-5cad-44e7-afb0-97c20ea80dd8/database",
},
)
assert.Equal(t, "integration_name", data.Name, "VictorOps integration name mismatch")
assert.Equal(t, "VICTOR_OPS", data.Type, "a new VictorOps integration should match its type")
assert.Equal(t, 1, data.Enabled, "a new VictorOps integration should be enabled")
data.IntgGuid = intgGUID

response, err := c.Integrations.UpdateVictorOpsAlertChannel(data)
assert.Nil(t, err)
assert.NotNil(t, response)
assert.Equal(t, "SUCCESS", response.Message)
assert.Equal(t, 1, len(response.Data))
assert.Equal(t, intgGUID, response.Data[0].IntgGuid)
}

func TestIntegrationsListVictorOpsAlertChannel(t *testing.T) {
var (
intgGUIDs = []string{intgguid.New(), intgguid.New(), intgguid.New()}
fakeServer = lacework.MockServer()
)
fakeServer.MockAPI("external/integrations/type/VICTOR_OPS",
func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method, "ListVictorOpsAlertChannel should be a GET method")
fmt.Fprintf(w, victoropsChanMultiIntegrationJsonResponse(intgGUIDs))
},
)
defer fakeServer.Close()

c, err := api.NewClient("test",
api.WithToken("TOKEN"),
api.WithURL(fakeServer.URL()),
)
assert.Nil(t, err)

response, err := c.Integrations.ListVictorOpsAlertChannel()
assert.Nil(t, err)
assert.NotNil(t, response)
assert.True(t, response.Ok)
assert.Equal(t, len(intgGUIDs), len(response.Data))
for _, d := range response.Data {
assert.Contains(t, intgGUIDs, d.IntgGuid)
}
}

func victoropsChannelIntegrationJsonResponse(intgGUID string) string {
return `
{
"data": [` + singleVictorOpsChanIntegration(intgGUID) + `],
"ok": true,
"message": "SUCCESS"
}
`
}

func victoropsChanMultiIntegrationJsonResponse(guids []string) string {
integrations := []string{}
for _, guid := range guids {
integrations = append(integrations, singleVictorOpsChanIntegration(guid))
}
return `
{
"data": [` + strings.Join(integrations, ", ") + `],
"ok": true,
"message": "SUCCESS"
}
`
}

func singleVictorOpsChanIntegration(id string) string {
return `
{
"INTG_GUID": "` + id + `",
"CREATED_OR_UPDATED_BY": "user@email.com",
"CREATED_OR_UPDATED_TIME": "2020-Jul-16 19:59:22 UTC",
"DATA": {
"ISSUE_GROUPING": "Events",
"INTG_URL": "https://alert.victorops.com/integrations/generic/20131114/alert/31e945ee-5cad-44e7-afb0-97c20ea80dd8/database"
},
"ENABLED": 1,
"IS_ORG": 0,
"NAME": "integration_name",
"STATE": {
"lastSuccessfulTime": "2020-Jul-16 18:26:54 UTC",
"lastUpdatedTime": "2020-Jul-16 18:26:54 UTC",
"ok": true
},
"TYPE": "VICTOR_OPS",
"TYPE_NAME": "VICTOR_OPS"
}
`
}
Loading

0 comments on commit dfcd34a

Please sign in to comment.