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

refactor: Bring presetalert resources calls to new style #17

Merged
merged 3 commits into from
Jun 1, 2021
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
8 changes: 3 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@ jobs:
- run:
name: "Run tests"
command: |
mkdir -p /tmp/artifacts
TF_ACC=1 go test -v -coverprofile=/tmp/artifacts/profile.out -covermode=count ./logdna
go tool cover -html=/tmp/artifacts/profile.out -o /tmp/artifacts/coverage.html
goveralls -coverprofile=/tmp/artifacts/profile.out -service=circleci -repotoken $COVERALLS_REPO_TOKEN
make testcov
goveralls -coverprofile=coverage/coverage.out -service=circleci -repotoken $COVERALLS_REPO_TOKEN
- store_artifacts:
path: /tmp/artifacts
path: coverage
workflows:
update:
jobs:
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# In case of `make build`
terraform-provider-logdna

coverage.out
coverage
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
TEST?=$$(go list ./... | grep -v 'vendor')
COVERAGEFILE?=coverage.out
COVERAGEFILE?=coverage/coverage.out
HOSTNAME=logdna.com
NAMESPACE=logdna
NAME=logdna
Expand Down Expand Up @@ -35,7 +35,8 @@ test:
TF_ACC=1 go test -v $(TESTARGS) ./logdna

testcov:
mkdir -p coverage
TF_ACC=1 go test $(TEST) -v $(TESTARGS) -coverprofile $(COVERAGEFILE)
go tool cover -html $(COVERAGEFILE)
go tool cover -html $(COVERAGEFILE) -o $(COVERAGEFILE).html

.PHONY: build release install test testacc testcov
4 changes: 2 additions & 2 deletions logdna/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

type providerConfig struct {
serviceKey string
baseURL string
baseURL string
httpClient *http.Client
}

Expand Down Expand Up @@ -41,7 +41,7 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {

return &providerConfig{
serviceKey: servicekey,
baseURL: url,
baseURL: url,
httpClient: &http.Client{Timeout: 15 * time.Second},
}, nil
}
8 changes: 8 additions & 0 deletions logdna/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,11 @@ func TestProvider(t *testing.T) {
func TestProvider_impl(t *testing.T) {
var _ *schema.Provider = Provider()
}

// testAccPreCheck validates the necessary test API keys exist
// in the testing environment
func testAccPreCheck(t *testing.T) {
if servicekey == "" {
t.Fatal("'servicekey' environment variable must be set for acceptance tests")
}
}
61 changes: 2 additions & 59 deletions logdna/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package logdna
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
Expand All @@ -30,14 +29,6 @@ type requestConfig struct {
jsonMarshal jsonMarshal
}

// AlertResponsePayload contains the keys/vals used in alert API responses
type AlertResponsePayload struct {
Channels []channelResponse `json:"channels,omitempty"`
Error string `json:"error,omitempty"`
Name string `json:"name,omitempty"`
PresetID string `json:"presetid,omitempty"`
}

// newRequestConfig abstracts the struct creation to allow for mocking
func newRequestConfig(pc *providerConfig, method string, uri string, body interface{}, mutators ...func(*requestConfig)) *requestConfig {
rc := &requestConfig{
Expand Down Expand Up @@ -76,64 +67,16 @@ func (c *requestConfig) MakeRequest() ([]byte, error) {
req.Header.Set("servicekey", c.serviceKey)
res, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("Error during HTTP request: %s, %+v", err, c)
return nil, fmt.Errorf("Error during HTTP request: %s", err)
}
defer res.Body.Close()

body, err := c.bodyReader(res.Body)
if err != nil {
return nil, fmt.Errorf("Error parsing HTTP response: %s, %+v", err, c)
return nil, fmt.Errorf("Error parsing HTTP response: %s, %s", err, string(body))
darinspivey marked this conversation as resolved.
Show resolved Hide resolved
}
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%s %s, status %d NOT OK! %s", c.method, c.apiURL, res.StatusCode, string(body))
}
return body, err
}

// MakeRequestAlert makes a HTTP request to the config-api with alert payload data and parses and returns the response
func MakeRequestAlert(c *requestConfig, url string, urlsuffix string, method string, payload viewRequest) (string, error) {
pbytes, err := json.Marshal(payload)
if err != nil {
return "", err
}
req, err := http.NewRequest(method, url+urlsuffix, bytes.NewBuffer(pbytes))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("servicekey", c.serviceKey)
resp, err := c.httpClient.Do(req)
if err != nil {
return "", fmt.Errorf(`Error with alert: %s`, err)
}
defer resp.Body.Close()
var result AlertResponsePayload
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
return "", err
}

if resp.StatusCode != 200 {
return "", errors.New(result.Error)
}

return result.PresetID, nil
}

// CreateAlert creates a Preset Alert with the provided payload
func (c *requestConfig) CreateAlert(url string, payload viewRequest) (string, error) {
result, err := MakeRequestAlert(c, url, "/v1/config/presetalert", "POST", payload)
return result, err
}

// UpdateAlert updates a Preset Alert with the provided presetID and payload
func (c *requestConfig) UpdateAlert(url string, presetID string, payload viewRequest) error {
_, err := MakeRequestAlert(c, url, "/v1/config/presetalert/"+presetID, "PUT", payload)
return err
}

// DeleteAlert deletes an alert with the provided presetID
func (c *requestConfig) DeleteAlert(url, presetID string) error {
_, err := MakeRequestAlert(c, url, "/v1/config/presetalert/"+presetID, "DELETE", viewRequest{})
return err
}
65 changes: 49 additions & 16 deletions logdna/request_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ type viewRequest struct {
Tags []string `json:"tags,omitempty"`
}

type alertRequest struct {
Name string `json:"name,omitempty"`
Channels []channelRequest `json:"channels,omitempty"`
}

type channelRequest struct {
BodyTemplate map[string]interface{} `json:"bodyTemplate,omitempty"`
Emails []string `json:"emails,omitempty"`
Expand Down Expand Up @@ -65,43 +70,71 @@ func (view *viewRequest) CreateRequestBody(d *schema.ResourceData) diag.Diagnost
}

// Complex array interfaces
view.Channels = *aggregateAllChannelsFromSchema(d, &diags)

return diags
}

func (alert *alertRequest) CreateRequestBody(d *schema.ResourceData) diag.Diagnostics {
var diags diag.Diagnostics

// Scalars
alert.Name = d.Get("name").(string)

// Complex array interfaces
alert.Channels = *aggregateAllChannelsFromSchema(d, &diags)

return diags
}

func aggregateAllChannelsFromSchema(
d *schema.ResourceData,
diags *diag.Diagnostics,
) *[]channelRequest {
allChannelEntries := make([]channelRequest, 0)

view.Channels = append(
view.Channels,
*mapChannelsFromSchema(
allChannelEntries = append(
allChannelEntries,
*iterateIntegrationType(
d.Get("email_channel").([]interface{}),
EMAIL,
&diags,
diags,
)...,
)

view.Channels = append(
view.Channels,
*mapChannelsFromSchema(
allChannelEntries = append(
allChannelEntries,
*iterateIntegrationType(
d.Get("pagerduty_channel").([]interface{}),
PAGERDUTY,
&diags,
diags,
)...,
)

view.Channels = append(
view.Channels,
*mapChannelsFromSchema(
allChannelEntries = append(
allChannelEntries,
*iterateIntegrationType(
d.Get("webhook_channel").([]interface{}),
WEBHOOK,
&diags,
diags,
)...,
)
return diags

return &allChannelEntries
}

func mapChannelsFromSchema(listEntries []interface{}, integration string, diags *diag.Diagnostics) *[]channelRequest {
func iterateIntegrationType(
listEntries []interface{},
integration string,
diags *diag.Diagnostics,
) *[]channelRequest {
var prepared interface{}
channelRequests := []channelRequest{}

if listEntries == nil {
return nil
if len(listEntries) == 0 {
return &channelRequests
}

for _, entry := range listEntries {
e := entry.(map[string]interface{})
prepared = nil
Expand Down
31 changes: 31 additions & 0 deletions logdna/request_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package logdna

import (
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/stretchr/testify/assert"
)

func TestRequestTypes_iterateIntegrationType(t *testing.T) {
assert := assert.New(t)

t.Run("Inserts a Diagnostics error for an unknown integration type", func(t *testing.T) {
var diags diag.Diagnostics
nonEmptyEntry := []interface{}{
map[string]interface{}{
"nah": "will not work",
},
}

channelRequests := iterateIntegrationType(nonEmptyEntry, "NOPE", &diags)

assert.Empty(*channelRequests, "Nothing was returned")
assert.Len(diags, 1, "There was 1 error")
assert.True(diags.HasError(), "The message is of type `Error`")

err := diags[0]
assert.Equal("Cannot format integration channel for outbound request", err.Summary, "Summary")
assert.Equal("Unrecognized integration: NOPE", err.Detail, "Detail")
})
}
Loading