-
Notifications
You must be signed in to change notification settings - Fork 60
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 #7 from hellofresh/feature/checkers
Added rabbit, redis and pg checkers implementations
- Loading branch information
Showing
8 changed files
with
419 additions
and
11 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 @@ | ||
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 |
---|---|---|
@@ -1,10 +1,9 @@ | ||
all: lint test | ||
.PHONY: all | ||
|
||
lint: | ||
go vet . | ||
.PHONY: lint | ||
@go vet ./... | ||
|
||
test: | ||
go test -v -cover . | ||
.PHONY: test | ||
@go test -v -cover ./... | ||
|
||
.PHONY: all test lint |
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
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,65 @@ | ||
package http | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"net/http" | ||
"time" | ||
) | ||
|
||
// Config is the HTTP checker configuration settings container. | ||
type Config struct { | ||
// URL is the remote service health check URL. | ||
URL string | ||
// RequestTimeout is the duration that health check will try to consume published test message. | ||
// If not set - 5 seconds | ||
RequestTimeout time.Duration | ||
// LogFunc is the callback function for errors logging during check. | ||
// If not set logging is skipped. | ||
LogFunc func(err error, details string, extra ...interface{}) | ||
} | ||
|
||
// New creates new HTTP service health check that verifies the following: | ||
// - connection establishing | ||
// - getting response status from defined URL | ||
// - verifying that status code is less than 500 | ||
func New(config Config) func() error { | ||
return func() error { | ||
if config.LogFunc == nil { | ||
config.LogFunc = func(err error, details string, extra ...interface{}) {} | ||
} | ||
|
||
if config.RequestTimeout == 0 { | ||
config.RequestTimeout = time.Second * 5 | ||
} | ||
|
||
req, err := http.NewRequest(http.MethodGet, config.URL, nil) | ||
if err != nil { | ||
config.LogFunc(err, "Creating the request for the health check failed") | ||
return err | ||
} | ||
|
||
ctx, cancel := context.WithCancel(context.TODO()) | ||
|
||
// Inform remote service to close the connection after the transaction is complete | ||
req.Header.Set("Connection", "close") | ||
req = req.WithContext(ctx) | ||
|
||
time.AfterFunc(config.RequestTimeout, func() { | ||
cancel() | ||
}) | ||
|
||
res, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
config.LogFunc(err, "Making the request for the health check failed") | ||
return err | ||
} | ||
defer res.Body.Close() | ||
|
||
if res.StatusCode >= http.StatusInternalServerError { | ||
return errors.New("remote service is not available at the moment") | ||
} | ||
|
||
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,123 @@ | ||
package postgres | ||
|
||
import ( | ||
"database/sql" | ||
"errors" | ||
"fmt" | ||
"strings" | ||
|
||
_ "github.com/lib/pq" | ||
) | ||
|
||
// Config is the PostgreSQL checker configuration settings container. | ||
type Config struct { | ||
// DSN is the PostgreSQL instance connection DSN. Required. | ||
DSN string | ||
// Table is the table name used for testing, must already exist in the DB and has insert/select/delete | ||
// privileges for the connection user. Required. | ||
Table string | ||
// IDColumn is the primary column for the table used for testing. Required. | ||
IDColumn string | ||
// InsertColumnsFunc is the callback function that returns map[<column>]<value> for testing insert operation. | ||
// Required. | ||
InsertColumnsFunc func() map[string]interface{} | ||
// LogFunc is the callback function for errors logging during check. | ||
// If not set logging is skipped. | ||
LogFunc func(err error, details string, extra ...interface{}) | ||
} | ||
|
||
// New creates new PostgreSQL health check that verifies the following: | ||
// - connection establishing | ||
// - inserting a row into defined table | ||
// - selecting inserted row | ||
// - deleting inserted row | ||
func New(config Config) func() error { | ||
return func() error { | ||
if config.LogFunc == nil { | ||
config.LogFunc = func(err error, details string, extra ...interface{}) {} | ||
} | ||
|
||
db, err := sql.Open("postgres", config.DSN) | ||
if err != nil { | ||
config.LogFunc(err, "PostgreSQL health check failed during connect") | ||
return err | ||
} | ||
|
||
defer func() { | ||
if err = db.Close(); err != nil { | ||
config.LogFunc(err, "PostgreSQL health check failed during connection closing") | ||
} | ||
}() | ||
|
||
columns := config.InsertColumnsFunc() | ||
columnNames := []string{} | ||
columnPlaceholders := []string{} | ||
columnValues := []interface{}{} | ||
i := 1 | ||
for column, value := range columns { | ||
columnNames = append(columnNames, column) | ||
columnPlaceholders = append(columnPlaceholders, fmt.Sprintf("$%d", i)) | ||
columnValues = append(columnValues, value) | ||
|
||
i++ | ||
} | ||
|
||
insertQuery := fmt.Sprintf( | ||
"INSERT INTO %s (%s) VALUES (%s) RETURNING %s", | ||
config.Table, | ||
strings.Join(columnNames, ", "), | ||
strings.Join(columnPlaceholders, ", "), | ||
config.IDColumn, | ||
) | ||
|
||
var idValue interface{} | ||
err = db.QueryRow(insertQuery, columnValues...).Scan(&idValue) | ||
if err != nil { | ||
config.LogFunc(err, "PostgreSQL health check failed during insert and scan") | ||
return err | ||
} | ||
|
||
selectQuery := fmt.Sprintf( | ||
"SELECT %s FROM %s WHERE %s = $1", | ||
strings.Join(columnNames, ", "), | ||
config.Table, | ||
config.IDColumn, | ||
) | ||
selectRows, err := db.Query(selectQuery, idValue) | ||
if err != nil { | ||
config.LogFunc(err, "PostgreSQL health check failed during select") | ||
return err | ||
} | ||
if !selectRows.Next() { | ||
config.LogFunc(err, "PostgreSQL health check failed during checking select result rows") | ||
return errors.New("looks like select result has 0 rows") | ||
} | ||
err = selectRows.Close() | ||
if err != nil { | ||
config.LogFunc(err, "PostgreSQL health check failed during closing select result") | ||
return err | ||
} | ||
|
||
deleteQuery := fmt.Sprintf( | ||
"DELETE FROM %s WHERE %s = $1", | ||
config.Table, | ||
config.IDColumn, | ||
) | ||
deleteResult, err := db.Exec(deleteQuery, idValue) | ||
if err != nil { | ||
config.LogFunc(err, "PostgreSQL health check failed during delete") | ||
return err | ||
} | ||
deleted, err := deleteResult.RowsAffected() | ||
if err != nil { | ||
config.LogFunc(err, "PostgreSQL health check failed during extracting delete result") | ||
return err | ||
} | ||
if deleted < 1 { | ||
config.LogFunc(err, "PostgreSQL health check failed during checking delete result") | ||
return errors.New("looks like delete removed 0 rows") | ||
} | ||
|
||
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,33 @@ | ||
package postgres | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
"time" | ||
) | ||
|
||
const pgDSNEnv = "HEALTH_GO_PG_DSN" | ||
|
||
func TestNew(t *testing.T) { | ||
if os.Getenv(pgDSNEnv) == "" { | ||
t.SkipNow() | ||
} | ||
|
||
check := New(Config{ | ||
DSN: os.Getenv(pgDSNEnv), | ||
Table: "client", | ||
IDColumn: "id", | ||
InsertColumnsFunc: func() map[string]interface{} { | ||
return map[string]interface{}{ | ||
"id": time.Now().Format(time.RFC3339Nano), | ||
"secret": time.Now().Format(time.RFC3339Nano), | ||
"extra": time.Now().Format(time.RFC3339Nano), | ||
"redirect_uri": "http://localhost", | ||
} | ||
}, | ||
}) | ||
|
||
if err := check(); err != nil { | ||
t.Fatalf("PostgreSQL check failed: %s", err.Error()) | ||
} | ||
} |
Oops, something went wrong.