This repository has been archived by the owner on Mar 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from segmentio/Fauzyy/init
Initial commit
- Loading branch information
Showing
12 changed files
with
533 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
version: 2 | ||
jobs: | ||
test: | ||
docker: | ||
- image: circleci/golang:1.14 | ||
steps: | ||
- checkout | ||
- run: | ||
name: Check modules are tidy | ||
command: | | ||
go mod tidy | ||
go mod vendor | ||
if [ "$(git status --porcelain)" != "" ]; then | ||
echo "git tree is dirty after tidying and vendoring modules" | ||
echo "ensure go.mod and go.sum are tidy" | ||
git status | ||
exit 1 | ||
fi | ||
- run: | ||
name: Test | ||
command: | | ||
make test | ||
workflows: | ||
version: 2 | ||
test: | ||
jobs: | ||
- test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
vendor/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright © 2017 Segment | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,3 @@ | ||
.PHONY: deps | ||
deps: | ||
@echo "Install Deps" | ||
|
||
.PHONY: test | ||
test: | ||
@echo "Perform Tests" | ||
|
||
.PHONY: build | ||
build: | ||
@echo "Build code" | ||
|
||
.PHONY: publish | ||
publish: | ||
@echo "Build code" | ||
go test -v ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,44 @@ | ||
# opslevel-go | ||
# opslevel-go [![CircleCI](https://circleci.com/gh/segmentio/opslevel-go.svg?style=shield)](https://circleci.com/gh/segmentio/opslevel-go) [![Go Report Card](https://goreportcard.com/badge/github.com/segmentio/opslevel-go)](https://goreportcard.com/report/github.com/segmentio/opslevel-go) [![GoDoc](https://godoc.org/github.com/segmentio/opslevel-go?status.svg)](https://godoc.org/github.com/segmentio/opslevel-go) | ||
|
||
This is a blank template for opslevel-go | ||
`opslevel-go` is a client library for the [OpsLevel](https://www.opslevel.com/) integrations API | ||
|
||
To get started create a new client: | ||
|
||
``` | ||
client := opslevel.NewClient() | ||
``` | ||
|
||
## Deploys Integration | ||
|
||
The Deploys Integration requires the following fields: | ||
|
||
``` | ||
deployRequest := opslevel.DeployRequest{ | ||
Service: "my-service", | ||
Description: "my-service was deployed", | ||
Deployer: rest.Deployer{ | ||
Email: "[email protected]", | ||
}, | ||
Environment: "env", | ||
DeployedAt: time.Now(), | ||
} | ||
err := client.Deploy(deployRequest, "my-integration-uuid") | ||
``` | ||
|
||
For a full list fields, see the docs. | ||
|
||
## Checks Integration | ||
|
||
The Deploys Integration requires the following fields: | ||
|
||
``` | ||
checkRequest := CheckRequest{ | ||
Service: "my_service", | ||
Check: "my_check", | ||
Message: "Deployed service", | ||
Status: "passed", | ||
} | ||
err := client.Check(checkRequest, "my-integration-uuid") | ||
``` | ||
|
||
The `Message` field is optional and `Status` should be one of `passed` or `failed`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package rest | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
|
||
"gopkg.in/go-playground/validator.v9" | ||
) | ||
|
||
// CheckRequest represents a structured request to the OpsLevel checks webhook endpoint | ||
type CheckRequest struct { | ||
Service string `validate:"required" json:"service"` | ||
Check string `validate:"required" json:"check"` | ||
Status string `validate:"required,oneof=passed failed" json:"status"` | ||
Message string `json:"message"` | ||
} | ||
|
||
// Check sends a CheckRequest to the OpsLevel deploy integration at integrationID | ||
func (c *Client) Check(req CheckRequest, integrationID string) error { | ||
v := validator.New() | ||
if err := v.Struct(req); err != nil { | ||
return err | ||
} | ||
|
||
b, err := json.Marshal(req) | ||
if err != nil { | ||
return err | ||
} | ||
var resp struct { | ||
Result string `json:"result"` | ||
} | ||
|
||
fullURL := fmt.Sprintf("/integrations/check/%s", integrationID) | ||
return c.do("POST", fullURL, bytes.NewReader(b), &resp) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package rest | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestCheck(t *testing.T) { | ||
t.Run("Doesn't return an error for a valid request", func(t *testing.T) { | ||
checkRequest := CheckRequest{ | ||
Service: "my_service", | ||
Check: "my_check", | ||
Message: "Deployed service", | ||
Status: "passed", | ||
} | ||
|
||
body := ` | ||
{ | ||
"result": "ok" | ||
} | ||
` | ||
client, testServer := setupTest(202, body) | ||
defer func() { testServer.Close() }() | ||
|
||
err := client.Check(checkRequest, "uuid") | ||
assert.NoError(t, err) | ||
}) | ||
|
||
t.Run("Returns a Bad Request error on a 422", func(t *testing.T) { | ||
checkRequest := CheckRequest{ | ||
Service: "my_service", | ||
Check: "my_check", | ||
Message: "Deployed service", | ||
Status: "passed", | ||
} | ||
|
||
body := ` | ||
{ | ||
"errors":[ | ||
{"status":400,"title":"Check Error","detail":"param is missing or the value is empty: status"} | ||
] | ||
} | ||
` | ||
client, testServer := setupTest(400, body) | ||
defer func() { testServer.Close() }() | ||
|
||
err := client.Check(checkRequest, "uuid") | ||
assert.Error(t, err) | ||
assert.Contains(t, err.Error(), "status 400") | ||
}) | ||
|
||
t.Run("Returns a Something Went Wrong error on all other status codes", func(t *testing.T) { | ||
checkRequest := CheckRequest{ | ||
Service: "my_service", | ||
Check: "my_check", | ||
Message: "Deployed service", | ||
Status: "passed", | ||
} | ||
|
||
body := ` | ||
{ | ||
"errors":[ | ||
{"status":503,"title":"Service Unavailable"} | ||
] | ||
} | ||
` | ||
client, testServer := setupTest(503, body) | ||
defer func() { testServer.Close() }() | ||
|
||
err := client.Check(checkRequest, "uuid") | ||
assert.Error(t, err) | ||
assert.Contains(t, err.Error(), "status 503") | ||
}) | ||
|
||
t.Run("Returns an error for an invalid request", func(t *testing.T) { | ||
checkRequest := CheckRequest{ | ||
Service: "my_service", | ||
Check: "my_check", | ||
Message: "Deployed service", | ||
Status: "bad status", | ||
} | ||
|
||
body := ` | ||
{ | ||
"result": "ok" | ||
} | ||
` | ||
client, testServer := setupTest(202, body) | ||
defer func() { testServer.Close() }() | ||
|
||
err := client.Check(checkRequest, "uuid") | ||
assert.EqualError(t, | ||
err, | ||
"Key: 'CheckRequest.Status' Error:Field validation for 'Status' failed on the 'oneof' tag", | ||
) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package rest | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/url" | ||
|
||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
var ( | ||
// ErrServiceUnavailable represents a 422 | ||
ErrServiceUnavailable = errors.New("Service Not Found") | ||
// ErrBadRequest represents a 400 | ||
ErrBadRequest = errors.New("Bad Request") | ||
// ErrSWW represents all other http errors | ||
ErrSWW = errors.New("Something Went Wrong") | ||
) | ||
|
||
// Client represents a rest http client and is used to send requests to OpsLevel integrations | ||
type Client struct { | ||
baseURL *url.URL | ||
httpClient *http.Client | ||
Logger *log.Logger | ||
} | ||
|
||
// NewClient returns a Client pointer | ||
func NewClient() *Client { | ||
baseURL, _ := url.Parse("https://app.opslevel.com") | ||
return &Client{ | ||
baseURL: baseURL, | ||
httpClient: &http.Client{}, | ||
Logger: log.StandardLogger(), | ||
} | ||
} | ||
|
||
func (c *Client) do(method string, path string, body io.Reader, recv interface{}) error { | ||
var err error | ||
url := fmt.Sprintf("%s%s", c.baseURL, path) | ||
c.Logger.Debugf("Sending request to OpsLevel endpoint %s", url) | ||
req, err := http.NewRequest(method, url, body) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
req.Header.Add("Content-Type", "application/json") | ||
resp, err := c.httpClient.Do(req) | ||
if err != nil { | ||
c.Logger.Debugf("Failed to send request to OpsLevel: %s", err.Error()) | ||
return err | ||
} | ||
defer resp.Body.Close() | ||
decoder := json.NewDecoder(resp.Body) | ||
|
||
c.Logger.Debugf("Received status code %d", resp.StatusCode) | ||
if resp.StatusCode != http.StatusAccepted { | ||
buf := new(bytes.Buffer) | ||
_, _ = buf.ReadFrom(resp.Body) | ||
s := buf.String() | ||
return fmt.Errorf("status %d; %s", resp.StatusCode, s) | ||
} | ||
|
||
err = decoder.Decode(&recv) | ||
if err != nil { | ||
c.Logger.Debugf("Failed to decode response from OpsLevel: %s", err.Error()) | ||
return err | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package rest | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"time" | ||
|
||
"gopkg.in/go-playground/validator.v9" | ||
) | ||
|
||
// Deployer represents the entity taking the action | ||
type Deployer struct { | ||
Email string `validate:"required" json:"email"` | ||
Name string `json:"name"` | ||
} | ||
|
||
// Commit represents the commit being deployed | ||
type Commit struct { | ||
SHA string `json:"sha"` | ||
Message string `json:"message"` | ||
Branch string `json:"branch"` | ||
Date time.Time `json:"date"` | ||
CommitterName string `json:"committer_name"` | ||
CommiterEmail string `json:"committer_email"` | ||
AuthorName string `json:"author_name"` | ||
AuthorEmail string `json:"author_email"` | ||
AuthoringDate time.Time `json:"authoring_date"` | ||
} | ||
|
||
// DeployRequest represents a structured request to the OpsLevel deploys webhook endpoint | ||
type DeployRequest struct { | ||
Service string `validate:"required" json:"service"` | ||
Deployer Deployer `validate:"required" json:"deployer"` | ||
Environment string `validate:"required" json:"environment"` | ||
DeployedAt time.Time `validate:"required" json:"deployed_at"` | ||
Description string `validate:"required" json:"description"` | ||
DeployURL string `json:"deploy_url"` | ||
DeployNumber string `json:"deploy_number"` | ||
Commit Commit `json:"commit"` | ||
} | ||
|
||
// Deploy sends a DeployRequest to the OpsLevel deploy integration at integrationID | ||
func (c *Client) Deploy(req DeployRequest, integrationID string) error { | ||
v := validator.New() | ||
if err := v.Struct(req); err != nil { | ||
return err | ||
} | ||
|
||
b, err := json.Marshal(req) | ||
if err != nil { | ||
return err | ||
} | ||
var resp struct { | ||
Result string `json:"result"` | ||
} | ||
|
||
fullURL := fmt.Sprintf("/integrations/deploy/%s", integrationID) | ||
return c.do("POST", fullURL, bytes.NewReader(b), &resp) | ||
} |
Oops, something went wrong.