Skip to content
This repository has been archived by the owner on Feb 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request #65 from remyLemeunier/fix_nr_hook
Browse files Browse the repository at this point in the history
Improve NewRelic hook
  • Loading branch information
obiesmans authored Sep 12, 2017
2 parents 556acff + 945f546 commit f499fac
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 40 deletions.
1 change: 0 additions & 1 deletion context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ func NewContext(cfg *utils.Config, manifest *utils.Manifest) (*Context, error) {
newRelic, err := hooks.NewNewRelicClient(
cfg.HookConfig.NewRelicConfig,
manifest.HookManifest.NewRelicManifest)

if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion examples/manifest.sample.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ hooks:
slack:
channel: channel
newRelic:
applicationId: "123"
application: "Webhooks - {{.env}}"
execCommand:
onPredeploy:
- { command: "ls", args: ["-lah"] }
Expand Down
133 changes: 107 additions & 26 deletions hooks/newrelic.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@ import (
"encoding/json"
"errors"
"fmt"
"html/template"
"io"
"io/ioutil"
"net/http"
"strings"

"github.com/remyLemeunier/contactkey/utils"
log "github.com/sirupsen/logrus"
)

type NewRelicClient struct {
Url string
ApiKey string
ApplicationId string
Stop bool
HttpClient *http.Client
Url string
ApiKey string
ApplicationFilter string
ApplicationId int
Stop bool
}

type NewRelicDeployment struct {
Expand All @@ -25,10 +31,33 @@ type NewRelicDeployment struct {
user string
}

type NewRelicApplicationList struct {
Applications []struct {
Id int `json:"id"`
Name string `json:"Name"`
} `json:"applications"`
}

func (c NewRelicClient) PreDeployment(userName string, env string, service string, podVersion string) error {
var filter bytes.Buffer
filterTmpl, err := template.New("filter").Parse(c.ApplicationFilter)
if err != nil {
return err
}

if err := filterTmpl.Execute(&filter, struct{ env string }{env}); err != nil {
}

appId, err := c.findApplicationId(filter.String())
if err != nil {
return err
}
c.ApplicationId = appId

description := fmt.Sprintf("Deploying %s %s on %s", service, podVersion, env)
d := &NewRelicDeployment{
description: description,
revision: podVersion,
user: userName,
}
return c.CreateDeployment(d)
Expand All @@ -51,47 +80,99 @@ func NewNewRelicClient(cfg utils.NewRelicConfig, manifest utils.NewRelicManifest
return nil, errors.New("You need to define an apiKey for newrelic in the config.")
}

if manifest.ApplicationId == "" {
if manifest.ApplicationFilter == "" {
return nil, errors.New("You need to define an applicationId for newrelic in the manifest.")
}

return &NewRelicClient{
Url: cfg.Url,
ApiKey: cfg.ApiKey,
ApplicationId: manifest.ApplicationId,
Stop: manifest.StopOnError,
}, nil
c := &NewRelicClient{
HttpClient: &http.Client{},
Url: cfg.Url,
ApiKey: cfg.ApiKey,
Stop: manifest.StopOnError,
ApplicationFilter: manifest.ApplicationFilter,
}

return c, nil
}

// https://rpm.newrelic.com/api/explore/application_deployments/create
func (c NewRelicClient) CreateDeployment(d *NewRelicDeployment) error {
client := &http.Client{}
url := fmt.Sprintf("%s/v2/applications/%s/deployments.json",
c.Url,
c.ApplicationId,
)
func (c NewRelicClient) findApplicationId(nameFilter string) (int, error) {
var applications NewRelicApplicationList

filter := strings.NewReader(fmt.Sprintf("filter[name]=%s", nameFilter))
request, err := c.NewRequest("GET", "v2/applications.json", filter)

if err != nil {
return 0, err
}

response, err := c.HttpClient.Do(request)
if err != nil {
return 0, err
}
if response.StatusCode != http.StatusOK {
return 0, errors.New("HTTP error from NewRelic")
}

defer response.Body.Close()
bodyJson, err := ioutil.ReadAll(response.Body)
if err != nil {
return 0, err
}

err = json.Unmarshal(bodyJson, &applications)
if err != nil {
return 0, err
}
log.WithFields(log.Fields{
"url": url,
}).Debug("Creating NewRelic deployment.")
"statusCode": response.StatusCode,
}).Debug("NewRelic response")

body := &bytes.Buffer{}
if err := json.NewEncoder(body).Encode(d); err != nil {
return err
if len(applications.Applications) == 0 {
return 0, fmt.Errorf("application %s not found", nameFilter)
}

return applications.Applications[0].Id, nil
}

func (c NewRelicClient) NewRequest(method string, route string, body io.Reader) (*http.Request, error) {
url := fmt.Sprintf("%s/%s", c.Url, route)

request, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}

request, err := http.NewRequest("POST", url, nil)
request.Header.Add("X-Api-Key", c.ApiKey)
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Accept", "application/json")

response, err := client.Do(request)
log.WithFields(log.Fields{
"url": request.URL,
"method": request.Method,
}).Debug("NewRelic request")
return request, nil
}

// https://rpm.newrelic.com/api/explore/application_deployments/create
func (c NewRelicClient) CreateDeployment(d *NewRelicDeployment) error {
body := &bytes.Buffer{}
if err := json.NewEncoder(body).Encode(d); err != nil {
return err
}

request, err := c.NewRequest("POST", fmt.Sprintf("v2/applications/%d/deployments.json", c.ApplicationId), body)
if err != nil {
return err
}

response, err := c.HttpClient.Do(request)
if err != nil {
return err
}

log.WithFields(log.Fields{
"statusCode": response.StatusCode,
}).Debug("NewRelic response status code.")
}).Debug("NewRelic response")

if response.StatusCode != http.StatusCreated {
return errors.New(fmt.Sprintf("NewRelic status code: %d", response.StatusCode))
Expand Down
59 changes: 50 additions & 9 deletions hooks/newrelic_test.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,77 @@
package hooks

import (
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)

var GetApplications = []byte(`{
"applications": [
{
"id": 456,
"name": "Webhooks"
}
]
}`)

var GetApplicationsEmpty = []byte(`{"applications": []}`)

var apiStub = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.RequestURI {
case "/v2/applications/456/deployments.json":
if r.Header.Get("X-Api-Key") != "123" {
w.WriteHeader(http.StatusUnauthorized)
} else {
body, _ := ioutil.ReadAll(r.Body)

if r.Header.Get("X-Api-Key") != "123" {
w.WriteHeader(http.StatusUnauthorized)
} else {
switch r.RequestURI {
case "/v2/applications.json":
w.WriteHeader(http.StatusOK)
if string(body) == "filter[name]=webhook" {
w.Write(GetApplications)
} else {
w.Write(GetApplicationsEmpty)
}

case "/v2/applications/456/deployments.json":
w.WriteHeader(http.StatusCreated)

default:
w.WriteHeader(http.StatusBadRequest)
}
default:
w.WriteHeader(http.StatusBadRequest)
}
}))

func TestCreateDeployment(t *testing.T) {
c := &NewRelicClient{
HttpClient: &http.Client{},
Url: apiStub.URL,
ApplicationId: 456,
ApiKey: "123",
ApplicationId: "456",
Stop: false,
}
//c.Log.SetLevel(log.DebugLevel)
d := &NewRelicDeployment{}

err := c.CreateDeployment(d)
if err != nil {
t.Errorf("Unexpected err : %q", err)
}
}

func TestFindApplicationId(t *testing.T) {
c := &NewRelicClient{
HttpClient: &http.Client{},
Url: apiStub.URL,
ApiKey: "123",
Stop: false,
}
// log.SetLevel(log.DebugLevel)

appId, err := c.findApplicationId("webhook")
if err != nil {
t.Errorf("Unexpected err : %q", err)
}
if appId != 456 {
t.Errorf("Unexpected appId : %q", appId)
}
}
4 changes: 2 additions & 2 deletions utils/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ type SlackManifest struct {
}

type NewRelicManifest struct {
ApplicationId string `mapstructure:"applicationId"`
StopOnError bool `mapstructure:"stopOnError"`
ApplicationFilter string `mapstructure:"applicationFilter"`
StopOnError bool `mapstructure:"stopOnError"`
}

type ExecCommandManifest struct {
Expand Down
2 changes: 1 addition & 1 deletion utils/testdata/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ hooks:
slack:
channel: channel
newRelic:
applicationId: "456"
applicationFilter: "Webhooks - {{.env}}"
execCommand:
onPredeploy:
- { command: "ls", args: ["-lah"] }
Expand Down

0 comments on commit f499fac

Please sign in to comment.