Skip to content
This repository was archived by the owner on Apr 24, 2023. It is now read-only.

Commit

Permalink
Merge pull request #2 from integr8ly/test-workflow
Browse files Browse the repository at this point in the history
add automated testing to the repo with github workflows
  • Loading branch information
aidenkeating authored Jan 28, 2020
2 parents d98408a + e995598 commit 4cad4c5
Show file tree
Hide file tree
Showing 14 changed files with 87 additions and 31 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
on: [push, pull_request]
name: Test
jobs:
test:
strategy:
matrix:
go-version: [1.13.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- name: Install Go
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go-version }}
- name: Checkout
uses: actions/checkout@v1
- name: Vendor check
run: make vendor/check
- name: Vet
run: make code/check
- name: Test
run: make test/unit
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ code/fix:

.PHONY: code/check
code/check:
golint -set_exit_status $$(go list ./...)
go vet ./...

.PHONY: code/gen
Expand All @@ -31,4 +30,4 @@ vendor/check: vendor/fix
.PHONY: vendor/fix
vendor/fix:
go mod tidy
go mod vendor
go mod vendor
2 changes: 1 addition & 1 deletion pkg/sendgrid/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func buildCreateSubUserBody(username, email, password string, ips []string) ([]b
return marshalRequestBody(&body, "sub user create")
}

func buildCreateApiKeyBody(id string, scopes []string) ([]byte, error) {
func buildCreateAPIKeyBody(id string, scopes []string) ([]byte, error) {
body := struct {
Name string `json:"name"`
Scopes []string `json:"scopes"`
Expand Down
6 changes: 3 additions & 3 deletions pkg/sendgrid/requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,13 @@ func Test_buildCreateApiKeyBody(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := buildCreateApiKeyBody(tt.args.id, tt.args.scopes)
got, err := buildCreateAPIKeyBody(tt.args.id, tt.args.scopes)
if (err != nil) != tt.wantErr {
t.Errorf("buildCreateApiKeyBody() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("buildCreateAPIKeyBody() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("buildCreateApiKeyBody() got = %v, want %v", got, tt.want)
t.Errorf("buildCreateAPIKeyBody() got = %v, want %v", got, tt.want)
}
})
}
Expand Down
16 changes: 11 additions & 5 deletions pkg/sendgrid/sendgrid.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import (
)

var (
//DefaultAPIKeyScopes The default API scopes given to the generated SendGrid API key
DefaultAPIKeyScopes = []string{"mail.send"}
)

var _ smtpdetails.Client = Client{}

//Client Client used to generate new API keys for OpenShift clusters, abstracting sub user creation
type Client struct {
sendgridClient APIClient
sendgridSubUserAPIKeyScopes []string
Expand All @@ -30,11 +32,12 @@ func NewDefaultClient(logger *logrus.Entry) (*Client, error) {
if err != nil {
return nil, errors.Wrap(err, "failed to create default password generator")
}
sendgridRESTClient := NewBackendRESTClient(APIHost, os.Getenv(EnvApiKey), logger)
sendgridRESTClient := NewBackendRESTClient(APIHost, os.Getenv(EnvAPIKey), logger)
sendgridClient := NewBackendAPIClient(sendgridRESTClient, logger)
return NewClient(sendgridClient, DefaultAPIKeyScopes, passGen, logger.WithField(smtpdetails.LogFieldDetailProvider, ProviderName))
}

//NewClient Create new Client
func NewClient(sendgridClient APIClient, apiKeyScopes []string, passGen smtpdetails.PasswordGenerator, logger *logrus.Entry) (*Client, error) {
if sendgridClient == nil {
return nil, errors.New("sendgridClient must be defined")
Expand All @@ -53,6 +56,7 @@ func NewClient(sendgridClient APIClient, apiKeyScopes []string, passGen smtpdeta
}, nil
}

//Create Generate new SendGrid sub user and API key for a cluster with it's ID
func (c Client) Create(id string) (*smtpdetails.SMTPDetails, error) {
// check if sub user exists
c.logger.Infof("checking if sub user %s exists", id)
Expand Down Expand Up @@ -116,6 +120,7 @@ func (c Client) Create(id string) (*smtpdetails.SMTPDetails, error) {
return defaultConnectionDetails(apiKey.Name, apiKey.Key), nil
}

//Get Retrieve the name of the SendGrid API key associated with an OpenShift cluster by it's ID
func (c Client) Get(id string) (*smtpdetails.SMTPDetails, error) {
subuser, err := c.sendgridClient.GetSubUserByUsername(id)
if err != nil {
Expand All @@ -129,19 +134,20 @@ func (c Client) Get(id string) (*smtpdetails.SMTPDetails, error) {
if len(apiKeys) < 1 {
return nil, errors.New(fmt.Sprintf("no api keys found for sub user %s", id))
}
var clusterApiKey *APIKey
var clusterAPIKey *APIKey
for _, k := range apiKeys {
if k.Name == subuser.Username {
clusterApiKey = k
clusterAPIKey = k
break
}
}
if clusterApiKey == nil {
if clusterAPIKey == nil {
return nil, &smtpdetails.NotExistError{Message: fmt.Sprintf("api key with id %s does not exist for sub user %s", subuser.Username, subuser.Username)}
}
return defaultConnectionDetails(clusterApiKey.Name, clusterApiKey.Key), nil
return defaultConnectionDetails(clusterAPIKey.Name, clusterAPIKey.Key), nil
}

//Delete Delete the SendGrid sub user associated with a cluster by the cluster ID
func (c Client) Delete(id string) error {
c.logger.Debugf("checking if sub user %s exists", id)
subuser, err := c.sendgridClient.GetSubUserByUsername(id)
Expand Down
12 changes: 10 additions & 2 deletions pkg/sendgrid/sendgridapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

var _ APIClient = &BackendAPIClient{}

//APIClient SendGrid client with utility functions for interacting with resources
//go:generate moq -out sendgridapi_moq.go . APIClient
type APIClient interface {
// ip addresses
Expand All @@ -38,11 +39,12 @@ type BackendAPIClient struct {
logger *logrus.Entry
}

//NewBackendClient Create a new BackendAPIClient with default logger labels
//NewBackendAPIClient Create a new BackendAPIClient with default logger labels
func NewBackendAPIClient(restClient RESTClient, logger *logrus.Entry) *BackendAPIClient {
return &BackendAPIClient{restClient: restClient, logger: logger.WithField(LogFieldAPIClient, ProviderName)}
}

//ListIPAddresses List the IP Addresses for the authenticated user
func (c *BackendAPIClient) ListIPAddresses() ([]*IPAddress, error) {
listReq := c.restClient.BuildRequest(APIRouteIPAddresses, rest.Get)
listResp, err := c.restClient.InvokeRequest(listReq)
Expand All @@ -56,6 +58,7 @@ func (c *BackendAPIClient) ListIPAddresses() ([]*IPAddress, error) {
return ips, nil
}

//GetAPIKeysForSubUser Get API keys on behalf of a sub user
func (c *BackendAPIClient) GetAPIKeysForSubUser(username string) ([]*APIKey, error) {
if username == "" {
return nil, errors.New("username must be a non-empty string")
Expand All @@ -73,13 +76,14 @@ func (c *BackendAPIClient) GetAPIKeysForSubUser(username string) ([]*APIKey, err
return apiKeysResp.Result, nil
}

//CreateAPIKeyForSubUser Create API key on behalf of a sub user
func (c *BackendAPIClient) CreateAPIKeyForSubUser(username string, scopes []string) (*APIKey, error) {
if username == "" {
return nil, errors.New("username must be a non-empty string")
}
createReq := c.restClient.BuildRequest(APIRouteAPIKeys, rest.Post)
createReq.Headers[HeaderOnBehalfOf] = username
createBody, err := buildCreateApiKeyBody(username, scopes)
createBody, err := buildCreateAPIKeyBody(username, scopes)
if err != nil {
return nil, errors.Wrap(err, "failed to create api key request body")
}
Expand All @@ -95,6 +99,7 @@ func (c *BackendAPIClient) CreateAPIKeyForSubUser(username string, scopes []stri
return apiKey, nil
}

//CreateSubUser Create sub user
func (c *BackendAPIClient) CreateSubUser(id, email, password string, ips []string) (*SubUser, error) {
createReq := c.restClient.BuildRequest(APIRouteSubUsers, rest.Post)
createReqBody, err := buildCreateSubUserBody(id, email, password, ips)
Expand All @@ -117,6 +122,7 @@ func (c *BackendAPIClient) CreateSubUser(id, email, password string, ips []strin
return subuser, nil
}

//DeleteSubUser Delete sub user by username
func (c *BackendAPIClient) DeleteSubUser(username string) error {
if username == "" {
return errors.New("username must be a non-empty string")
Expand All @@ -132,6 +138,7 @@ func (c *BackendAPIClient) DeleteSubUser(username string) error {
return nil
}

//ListSubUsers List all sub users for current authenticated user
func (c *BackendAPIClient) ListSubUsers(query map[string]string) ([]*SubUser, error) {
if query == nil {
query = map[string]string{}
Expand All @@ -149,6 +156,7 @@ func (c *BackendAPIClient) ListSubUsers(query map[string]string) ([]*SubUser, er
return subusers, nil
}

//GetSubUserByUsername Get sub user of current authenticated user by username
func (c *BackendAPIClient) GetSubUserByUsername(username string) (*SubUser, error) {
if username == "" {
return nil, errors.New("username must be a non-empty string")
Expand Down
7 changes: 4 additions & 3 deletions pkg/sendgrid/sendgridrest.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

var _ RESTClient = &BackendRESTClient{}

//RESTClient Thin wrapper around the SendGrid package
//go:generate moq -out sendgridrest_moq.go . RESTClient
type RESTClient interface {
BuildRequest(endpoint string, method rest.Method) rest.Request
Expand All @@ -21,7 +22,7 @@ type BackendRESTClient struct {
logger *logrus.Entry
}

//NewBackendClient Create a new BackendAPIClient with default logger labels
//NewBackendRESTClient Create a new BackendAPIClient with default logger labels
func NewBackendRESTClient(apiHost, apiKey string, logger *logrus.Entry) *BackendRESTClient {
return &BackendRESTClient{
apiHost: apiHost,
Expand All @@ -30,15 +31,15 @@ func NewBackendRESTClient(apiHost, apiKey string, logger *logrus.Entry) *Backend
}
}

//GetRequest Create a REST request that can be sent through the SendGrid API
//BuildRequest Create a REST request that can be sent through the SendGrid API
func (c *BackendRESTClient) BuildRequest(endpoint string, method rest.Method) rest.Request {
c.logger.Debugf("getting request with details, key=%s endpoint=%s host=%s", c.apiKey, endpoint, c.apiHost)
req := sg.GetRequest(c.apiKey, endpoint, c.apiHost)
req.Method = method
return req
}

//API Invoke a REST request against the SendGrid API
//InvokeRequest Invoke a REST request against the SendGrid API
func (c *BackendRESTClient) InvokeRequest(request rest.Request) (*rest.Response, error) {
c.logger.Debugf("performing api request with details, url=%s method=%s body=%s", request.BaseURL, request.Method, string(request.Body))
return sg.API(request)
Expand Down
6 changes: 3 additions & 3 deletions pkg/sendgrid/sendgridrest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

const (
testApiKey = "testApiKey"
testAPIKey = "testAPIKey"
)

func newMockLogger() *logrus.Entry {
Expand All @@ -36,7 +36,7 @@ func TestBackendRESTClient_BuildRequest(t *testing.T) {
name: "provided args should be set correctly",
fields: fields{
apiHost: APIHost,
apiKey: testApiKey,
apiKey: testAPIKey,
logger: newMockLogger(),
},
args: args{
Expand All @@ -47,7 +47,7 @@ func TestBackendRESTClient_BuildRequest(t *testing.T) {
Method: rest.Post,
BaseURL: fmt.Sprintf("%s%s", APIHost, APIRouteSubUsers),
Headers: map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", testApiKey),
"Authorization": fmt.Sprintf("Bearer %s", testAPIKey),
},
QueryParams: map[string]string{},
Body: []byte{},
Expand Down
18 changes: 11 additions & 7 deletions pkg/sendgrid/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,26 @@ package sendgrid
const (
//ProviderName Standardised name of the SendGrid provider
ProviderName = "sendgrid"
//EnvSendgridApiKey Name of the env var to retrieve the SendGrid API key
EnvApiKey = "SENDGRID_API_KEY"
//EnvAPIKey Name of the env var to retrieve the SendGrid API key
EnvAPIKey = "SENDGRID_API_KEY"
//APIHost SendGrid API default host
APIHost = "https://api.sendgrid.com"
//APIRouteSubUsers SendGrid v3 API endpoint for sub user management
APIRouteSubUsers = "/v3/subusers"
//APIRouteSubUsers SendGrid v3 API endpoint for api key management
//APIRouteAPIKeys SendGrid v3 API endpoint for api key management
APIRouteAPIKeys = "/v3/api_keys"
//APIRouteIPAddresses SendGrid v3 API endpoint for ip address management
APIRouteIPAddresses = "/v3/ips"
//HeaderOnBehalfOf SendGrid v3 header for declaring an action is on behalf of a sub user
HeaderOnBehalfOf = "on-behalf-of"
//LogFieldAPIClient Logging field name for a description of the API client
LogFieldAPIClient = "sendgrid_service_api_client"
ConnectionDetailsHost = "smtp.sendgrid.net"
ConnectionDetailsPort = 587
ConnectionDetailsTLS = true
LogFieldAPIClient = "sendgrid_service_api_client"
//ConnectionDetailsHost Default SendGrid host
ConnectionDetailsHost = "smtp.sendgrid.net"
//ConnectionDetailsPort Default SendGrid port
ConnectionDetailsPort = 587
//ConnectionDetailsTLS Default SendGrid TLS setting
ConnectionDetailsTLS = true
//ConnectionDetailsUsername Default SendGrid SMTP auth username
ConnectionDetailsUsername = "apikey"
)
6 changes: 6 additions & 0 deletions pkg/smtpdetails/errors.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
package smtpdetails

//AlreadyExistsError Error to indicate an API key already exists
type AlreadyExistsError struct {
Message string
}

//Error String representation of error
func (e *AlreadyExistsError) Error() string {
return e.Message
}

//IsAlreadyExistsError Compare check for AlreadyExistsError
func IsAlreadyExistsError(err error) bool {
_, ok := err.(*AlreadyExistsError)
return ok
}

//NotExistError Error to indicate an API key does not exist
type NotExistError struct {
Message string
}

//Error String representation of error
func (e *NotExistError) Error() string {
return e.Message
}

//IsNotExistError Compare check for NotExistError
func IsNotExistError(err error) bool {
_, ok := err.(*NotExistError)
return ok
Expand Down
1 change: 1 addition & 0 deletions pkg/smtpdetails/password.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package smtpdetails

//PasswordGenerator Mockable interface to generate passwords
//go:generate moq -out password_moq.go . PasswordGenerator
type PasswordGenerator interface {
Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error)
Expand Down
2 changes: 2 additions & 0 deletions pkg/smtpdetails/smtpdetails.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

//SMTPDetails Details about an SMTP connection
type SMTPDetails struct {
ID string
Host string
Expand All @@ -16,6 +17,7 @@ type SMTPDetails struct {
Password string
}

//Client Client to create SMTP details for an OpenShift cluster by it's ID
type Client interface {
Create(id string) (*SMTPDetails, error)
Get(id string) (*SMTPDetails, error)
Expand Down
16 changes: 11 additions & 5 deletions pkg/smtpdetails/strings.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package smtpdetails

const (
//LogFieldDetailProvider Logging key for specifying the SMTP details provider e.g. SendGrid
LogFieldDetailProvider = "smtp_service_detail_provider"
SecretKeyHost = "host"
SecretKeyPort = "port"
SecretKeyTLS = "tls"
SecretKeyUsername = "username"
SecretKeyPassword = "password"
//SecretKeyHost Default secret data key for SMTP host
SecretKeyHost = "host"
//SecretKeyPort Default secret data key for SMTP port
SecretKeyPort = "port"
//SecretKeyTLS Default secret data key for SMTP TLS
SecretKeyTLS = "tls"
//SecretKeyUsername Default secret data key for SMTP auth username
SecretKeyUsername = "username"
//SecretKeyPassword Default secret data key for SMTP auth password
SecretKeyPassword = "password"
)
1 change: 1 addition & 0 deletions version/version.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package version

var (
//Version Version of the SMTP Service
Version = "0.1.0"
)

0 comments on commit 4cad4c5

Please sign in to comment.