Skip to content

Commit

Permalink
Merge pull request #9 from ch-robinson/tschnbrg/idiomaticgo
Browse files Browse the repository at this point in the history
 refactor to idiomatic go
  • Loading branch information
traviisd authored Nov 15, 2018
2 parents ac72586 + 78e54cd commit 623ca33
Show file tree
Hide file tree
Showing 21 changed files with 306 additions and 334 deletions.
12 changes: 2 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
MAIN_VERSION:=$(shell git describe --always || echo "1.0")
VERSION:=${MAIN_VERSION}\#$(shell git log -n 1 --pretty=format:"%h")
PACKAGES:=$(shell go list ./... | sed -n '1!p' | grep -v -e /vendor/ -e github.com/ch-robinson/vault-elastic-plugin/plugin/interfaces)
LDFLAGS:=-ldflags "-X github.com/ch-robinson/vault-elastic-plugin/plugin.Version=${VERSION}"
PACKAGES:=$(shell go list ./... | sed -n '1!p' | grep -v -e /vendor/)
LDFLAGS:=-ldflags "-X github.com/ch-robinson/vault-elastic-plugin/main.Version=${VERSION}"

ifeq ($(OS),Windows_NT)
DETECTED_OS := Windows
Expand All @@ -20,14 +20,6 @@ default: test

test:
@echo "mode: count" > coverage-all.out
@echo
@echo "************** SKIPPING TESTS IN PACKAGES ****************"
@echo
@echo "skipping github.com/ch-robinson/vault-elastic-plugin/plugin/interfaces"
@echo
@echo "**********************************************************"
@echo

@$(foreach pkg,$(PACKAGES), \
go test -p=1 -cover -covermode=count -coverprofile=coverage.out ${pkg} || exit 1; \
tail -n +2 coverage.out >> coverage-all.out;)
Expand Down
16 changes: 8 additions & 8 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# vault-elastic-plugin

### Setup
## Setup

*** NOTE: For Mac or linux, make should be installed by default so you can skip steps 2 and 3 ***

Expand All @@ -12,33 +12,33 @@
- Mac - ```brew install glide```
5. ```go get github.com/ch-robinson/vault-elastic-plugin```

### Testing locally
## Testing locally

1. Download [Vault](https://www.vaultproject.io/downloads.html) and extract the compressed file to the location of your choosing
2. Add Vault to path
3. In a new terminal run Vault: ```make run-vault```
4. In a new terminal run:
4. In a new terminal run:
- With build and vault DB configuration: ```make test-plugin ELASTIC_BASE_URI=<uri> ELASTIC_PASSWORD=<password> ELASTIC_USERNAME=<username> INCLUDE_BUILD=true ENABLE_VAULT_DB=true```
- Without build: ```make test-plugin ELASTIC_BASE_URI=<uri> ELASTIC_PASSWORD=<password> ELASTIC_USERNAME=<username> INCLUDE_BUILD=false ENABLE_VAULT_DB=true```

### Build
## Build

- Unix based: ```make build```
- Powershell (if make is not in path): ```C:\Program Files (x86)\GnuWin32\bin\make.exe build```
- bash for Windows (if make is not in path): ```/c/Program\ Files\ \(x86\)/GnuWin32/bin/make.exe build```

The executable binary is located ../bin/run

### Unit Testing
## Unit Testing

At the root of the project, run

At the root of the project, run
- Unix based: ```make test```
- Powershell (if make is not in path): ```C:\Program Files (x86)\GnuWin32\bin\make.exe test```
- bash for Windows (if make is not in path): ```/c/Program\ Files\ \(x86\)/GnuWin32/bin/make.exe test```

### Dependencies
## Dependencies

- Unix based: ```make depends```
- Powershell (if make is not in path): ```C:\Program Files (x86)\GnuWin32\bin\make.exe depends```
- bash for Windows (if make is not in path): ```/c/Program\ Files\ \(x86\)/GnuWin32/bin/make.exe depends```

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"fmt"
"sync"

"github.com/ch-robinson/vault-elastic-plugin/plugin/interfaces"
"github.com/ch-robinson/vault-elastic-plugin/httputil"
"github.com/mitchellh/mapstructure"
)

Expand All @@ -17,7 +17,7 @@ type connectionProducer struct {
Password string `json:"password" structs:"password" mapstructure:"password"`
RawConfig map[string]interface{}
Type string
HTTPClient interfaces.IHTTPClient
HTTPClient httputil.ClientWrapperer
sync.RWMutex
}

Expand Down
File renamed without changes.
8 changes: 4 additions & 4 deletions plugin/elastic/elastic_db.go → elastic/elastic_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"fmt"
"time"

"github.com/ch-robinson/vault-elastic-plugin/plugin/interfaces"
"github.com/ch-robinson/vault-elastic-plugin/httputil"
"github.com/hashicorp/vault/api"
"github.com/hashicorp/vault/builtin/logical/database/dbplugin"
"github.com/hashicorp/vault/plugins/helper/database/credsutil"
Expand All @@ -24,7 +24,7 @@ type Database struct {
var _ dbplugin.Database = &Database{}

// New returns a new Elastic instance with provided implementation of http.Client
func New(httpClient interfaces.IHTTPClient) (interface{}, error) {
func New(httpClient httputil.ClientWrapperer) (interface{}, error) {
// setup struct
db := &Database{
connectionProducer: &connectionProducer{
Expand All @@ -47,7 +47,7 @@ func New(httpClient interfaces.IHTTPClient) (interface{}, error) {
}

// Run instantiates the Database struct, and runs the RPC server for the plugin
func Run(serve func(plugin interface{}, tlsConfig *api.TLSConfig), apiTLSConfig *api.TLSConfig, httpClient interfaces.IHTTPClient) error {
func Run(serve func(plugin interface{}, tlsConfig *api.TLSConfig), apiTLSConfig *api.TLSConfig, httpClient httputil.ClientWrapperer) error {
dbType, err := New(httpClient)

if err != nil {
Expand Down Expand Up @@ -155,7 +155,7 @@ func (m *Database) RevokeUser(ctx context.Context, statements dbplugin.Statement
// RotateRootCredentials rotates the root superuser credentials stored for the database connection
func (m *Database) RotateRootCredentials(ctx context.Context, statements []string) (map[string]interface{}, error) {
if len(m.Username) == 0 || len(m.Password) == 0 {
return nil, errors.New("Both the username and password are required.")
return nil, errors.New("both the username and password are required")
}

password, err := m.GeneratePassword()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ func TestRotateRootCredentialsFailWithoutConnectionCredentials(t *testing.T) {

_, err := db.RotateRootCredentials(testdata.NewMockVaultContext(), []string{})

assert.Equal(t, "Both the username and password are required.", err.Error())
assert.Equal(t, "both the username and password are required", err.Error())
}

func TestRotateRootCredentialsFailOnBadUsername(t *testing.T) {
Expand Down
108 changes: 108 additions & 0 deletions httputil/http_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package httputil

import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
)

// HTTP is the interface for http.Client
// https://golang.org/pkg/net/http/
type httpWrapper interface {
// Do wraps the http.Client Do function
Do(req *http.Request) (*http.Response, error)
}

// ClientWrapperer is the interface for functions relating to building http request
type ClientWrapperer interface {
// Do peforms an http request. This is just a wrapper for the http.Client function
// calls HTTP.Do for ease of testing
Do(req *http.Request) (*http.Response, error)
// BuildBasicAuthRequest creates an http.Request with basic authoriztion header.
// body must be map[string]interface{}
BuildBasicAuthRequest(requestURL, username, password, httpMethod string, body map[string]interface{}) (*http.Request, error)
// ReadHTTPResponse returns the response body as map[string]interface{}
ReadHTTPResponse(res *http.Response) (map[string]interface{}, error)
}

// ClientWrapper is the wrapper for interacting with http methods
type ClientWrapper struct {
client httpWrapper
}

// New instantiates a new ClientWrapper
func New(client httpWrapper) *ClientWrapper {
return &ClientWrapper{client}
}

// Do peforms an http request
func (c *ClientWrapper) Do(req *http.Request) (*http.Response, error) {
return c.client.Do(req)
}

// BuildBasicAuthRequest creates an http.Request with basic authoriztion header.
// body must be map[string]interface{}
func (c *ClientWrapper) BuildBasicAuthRequest(requestURL, username, password, httpMethod string, body map[string]interface{}) (*http.Request, error) {
var req *http.Request
var err error

if body != nil && len(body) > 0 {
reqBody, err := json.Marshal(body)
if err != nil {
return nil, err
}

recloser := NewClosingBuffer(bytes.NewBuffer(reqBody)).GetReadCloser().(*ClosingBuffer)

if err != nil {
return nil, err
}

req, err = http.NewRequest(httpMethod, requestURL, recloser)
} else {
req, err = http.NewRequest(httpMethod, requestURL, nil)
}

req.SetBasicAuth(username, password)

if err != nil {
return nil, err
}

c.addHeaders(&req.Header)

return req, nil
}

// ReadHTTPResponse returns the response body as map[string]interface{}
func (c *ClientWrapper) ReadHTTPResponse(res *http.Response) (map[string]interface{}, error) {
resBody, err := ioutil.ReadAll(res.Body)

defer res.Body.Close()

if err != nil {
return nil, err
}

var body map[string]interface{}

err = json.Unmarshal(resBody, &body)

if err != nil {
return nil, err
}

// Throw error if not ok. Might need to watch out for other success codes, but this should be ok.
if res.StatusCode != 200 {
return nil, err
}

return body, nil
}

// addHeaders adds http.Headers. If accessToken is provided, an Authorization header will be added with given authType (Bearer, token, etc.)
func (c *ClientWrapper) addHeaders(header *http.Header) {
header.Add("Content-Type", "application/json")
header.Add("Accept", "application/json")
}
34 changes: 17 additions & 17 deletions plugin/util/http_client_test.go → httputil/http_client_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package util
package httputil

import (
"bytes"
Expand All @@ -9,22 +9,22 @@ import (
"github.com/stretchr/testify/assert"
)

func initHTTPClient(resBody *string) *HTTPClient {
func initClient(resBody *string) *ClientWrapper {
mockHTTP := testdata.NewMockHTTP(resBody)
httpClient := NewHTTPClient(mockHTTP)
return httpClient
clientWrapper := New(mockHTTP)
return clientWrapper
}

func TestNewHTTPClient(t *testing.T) {
func TestNewClient(t *testing.T) {
mockHTTP := testdata.NewMockHTTP(nil)

httpClient := NewHTTPClient(mockHTTP)
clientWrapper := New(mockHTTP)

assert.Equal(t, mockHTTP, httpClient.client)
assert.Equal(t, mockHTTP, clientWrapper.client)
}

func TestReadHTTPResponse(t *testing.T) {
httpClient := initHTTPClient(nil)
clientWrapper := initClient(nil)

readCloser := NewClosingBuffer(bytes.NewBufferString("{\"test\":\"body\"}")).GetReadCloser()

Expand All @@ -34,49 +34,49 @@ func TestReadHTTPResponse(t *testing.T) {
Body: readCloser,
}

response, err := httpClient.ReadHTTPResponse(res)
response, err := clientWrapper.ReadHTTPResponse(res)

assert.Nil(t, err)
assert.True(t, response != nil)
}

func TestAddHeaders(t *testing.T) {
httpClient := initHTTPClient(nil)
clientWrapper := initClient(nil)
var h http.Header = make(map[string][]string)

httpClient.addHeaders(&h)
clientWrapper.addHeaders(&h)

assert.Equal(t, "application/json", h.Get("Content-Type"))
assert.Equal(t, "application/json", h.Get("Accept"))
}

func TestDo(t *testing.T) {
b := "good"
httpClient := initHTTPClient(&b)
clientWrapper := initClient(&b)

req, _ := http.NewRequest("GET", "mocked", nil)

_, err := httpClient.Do(req)
_, err := clientWrapper.Do(req)

assert.Nil(t, err)
}

func TestBuildBasicAuthRequestWithBody(t *testing.T) {
httpClient := initHTTPClient(nil)
clientWrapper := initClient(nil)
body := make(map[string]interface{})
body["test"] = true

req, err := httpClient.BuildBasicAuthRequest("http://test", "testuser", "testpassword", "POST", body)
req, err := clientWrapper.BuildBasicAuthRequest("http://test", "testuser", "testpassword", "POST", body)

assert.Nil(t, err)
assert.NotNil(t, req)
assert.Equal(t, "Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk", req.Header.Get("Authorization"))
}

func TestBuildBasicAuthRequestNilBody(t *testing.T) {
httpClient := initHTTPClient(nil)
clientWrapper := initClient(nil)

req, err := httpClient.BuildBasicAuthRequest("http://test", "testuser", "testpassword", "DELETE", nil)
req, err := clientWrapper.BuildBasicAuthRequest("http://test", "testuser", "testpassword", "DELETE", nil)

assert.Nil(t, err)
assert.NotNil(t, req)
Expand Down
2 changes: 1 addition & 1 deletion plugin/util/read_closer.go → httputil/read_closer.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package util
package httputil

import (
"bytes"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package util
package httputil

import (
"bytes"
Expand Down
8 changes: 5 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ import (
"net/http"
"os"

"github.com/ch-robinson/vault-elastic-plugin/plugin/elastic"
"github.com/ch-robinson/vault-elastic-plugin/plugin/util"
"github.com/ch-robinson/vault-elastic-plugin/elastic"
"github.com/ch-robinson/vault-elastic-plugin/httputil"
"github.com/hashicorp/vault/helper/pluginutil"
"github.com/hashicorp/vault/plugins"
)

var Version = "1.0"

func main() {
apiClientMeta := &pluginutil.APIClientMeta{}
flags := apiClientMeta.FlagSet()
flags.Parse(os.Args[1:])

clientWrapper := util.NewHTTPClient(&http.Client{})
clientWrapper := httputil.New(&http.Client{})

if err := elastic.Run(plugins.Serve, apiClientMeta.GetTLSConfig(), clientWrapper); err != nil {
log.Println(err)
Expand Down
22 changes: 0 additions & 22 deletions plugin/interfaces/http.go

This file was deleted.

Loading

0 comments on commit 623ca33

Please sign in to comment.