Skip to content

Commit fa0b2cb

Browse files
Update service health check to use newer patterns. (#27)
1 parent eecb76d commit fa0b2cb

File tree

3 files changed

+131
-12
lines changed

3 files changed

+131
-12
lines changed

CHANGELOG.md

+15-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,22 @@
22

33
## Unreleased
44

5+
## v0.3.0 (2021-06-16)
6+
### Added
7+
* Health check tests.
8+
* More linting.
9+
10+
### Fixed
11+
* Issues reported by linter.
12+
13+
### Changed
14+
* Health check uses newer env var pattern.
15+
* Health check uses newer logging pattern.
16+
* Health check now responds with the same for success/failure (was json for
17+
failures).
18+
519
## v0.2.1 (2021-06-16)
6-
### Add
20+
### Added
721
* CLI tests.
822
* More linting.
923

service/handlers.go

+15-11
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88
"io/ioutil"
99
"net/http"
10-
"os"
1110
"regexp"
1211
"strings"
1312

@@ -103,15 +102,18 @@ func (h handler) validateWorkflowParameters(parameters map[string]string) error
103102
}
104103

105104
// Service HealthCheck
106-
func (h handler) healthCheck(w http.ResponseWriter, r *http.Request) {
107-
level.Debug(h.logger).Log("message", "executing health check")
108-
vaultEndpoint := fmt.Sprintf("%s/v1/sys/health", os.Getenv("VAULT_ADDR"))
105+
func (h *handler) healthCheck(w http.ResponseWriter, r *http.Request) {
106+
vaultEndpoint := fmt.Sprintf("%s/v1/sys/health", h.env.VaultAddress)
107+
108+
l := h.requestLogger(r, "op", "health-check", "vault-endpoint", vaultEndpoint)
109+
level.Debug(l).Log("message", "executing")
109110

110111
// #nosec
111112
response, err := http.Get(vaultEndpoint)
112113
if err != nil {
113-
level.Error(h.logger).Log("message", fmt.Sprintf("received error connecting to vault endpoint %s", vaultEndpoint))
114-
h.errorResponse(w, "Health check failed", http.StatusServiceUnavailable)
114+
level.Error(h.logger).Log("message", "received error connecting to vault", "error", err)
115+
w.WriteHeader(http.StatusServiceUnavailable)
116+
fmt.Fprintln(w, "Health check failed")
115117
return
116118
}
117119

@@ -121,16 +123,18 @@ func (h handler) healthCheck(w http.ResponseWriter, r *http.Request) {
121123
defer response.Body.Close()
122124
_, err = ioutil.ReadAll(response.Body)
123125
if err != nil {
124-
level.Error(h.logger).Log("message", "unable to read body; continuing", "error", err)
126+
level.Warn(h.logger).Log("message", "unable to read vault body; continuing", "error", err)
125127
// Continue on and handle the actual response code from Vault accordingly.
126128
}
127129

128130
if response.StatusCode != 200 && response.StatusCode != 429 {
129-
level.Error(h.logger).Log("message", fmt.Sprintf("received code %d which is not 200 (initialized, unsealed, and active) or 429 (unsealed and standby) when connecting to vault endpoint %s", response.StatusCode, vaultEndpoint))
130-
h.errorResponse(w, "Health check failed", http.StatusServiceUnavailable)
131-
} else {
132-
fmt.Fprintln(w, "Health check succeeded")
131+
level.Error(h.logger).Log("message", fmt.Sprintf("received code %d which is not 200 (initialized, unsealed, and active) or 429 (unsealed and standby) when connecting to vault", response.StatusCode))
132+
w.WriteHeader(http.StatusServiceUnavailable)
133+
fmt.Fprintln(w, "Health check failed")
134+
return
133135
}
136+
137+
fmt.Fprintln(w, "Health check succeeded")
134138
}
135139

136140
// Lists workflows

service/handlers_test.go

+101
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"encoding/json"
77
"fmt"
8+
"io"
89
"io/ioutil"
910
"net/http"
1011
"net/http/httptest"
@@ -18,6 +19,7 @@ import (
1819

1920
"github.com/go-kit/kit/log"
2021
vault "github.com/hashicorp/vault/api"
22+
"github.com/stretchr/testify/assert"
2123
)
2224

2325
const (
@@ -545,6 +547,105 @@ func TestListWorkflows(t *testing.T) {
545547
runTests(t, tests)
546548
}
547549

550+
func TestHealthCheck(t *testing.T) {
551+
tests := []struct {
552+
name string
553+
endpoint string // Used to cause a connection error.
554+
vaultStatusCode int
555+
writeBadContentLength bool // Used to create response body error.
556+
wantResponseBody string
557+
wantStatusCode int
558+
}{
559+
{
560+
name: "good_vault_200",
561+
vaultStatusCode: http.StatusOK,
562+
wantResponseBody: "Health check succeeded\n",
563+
wantStatusCode: http.StatusOK,
564+
},
565+
{
566+
name: "good_vault_429",
567+
vaultStatusCode: http.StatusTooManyRequests,
568+
wantResponseBody: "Health check succeeded\n",
569+
wantStatusCode: http.StatusOK,
570+
},
571+
{
572+
// We want successful health check in this vault error scenario.
573+
name: "error_vault_read_response",
574+
vaultStatusCode: http.StatusOK,
575+
writeBadContentLength: true,
576+
wantResponseBody: "Health check succeeded\n",
577+
wantStatusCode: http.StatusOK,
578+
},
579+
{
580+
name: "error_vault_connection",
581+
endpoint: string('\f'),
582+
wantResponseBody: "Health check failed\n",
583+
wantStatusCode: http.StatusServiceUnavailable,
584+
},
585+
{
586+
name: "error_vault_unhealthy_status_code",
587+
vaultStatusCode: http.StatusInternalServerError,
588+
wantResponseBody: "Health check failed\n",
589+
wantStatusCode: http.StatusServiceUnavailable,
590+
},
591+
}
592+
593+
for _, tt := range tests {
594+
t.Run(tt.name, func(t *testing.T) {
595+
wantURL := "/v1/sys/health"
596+
597+
vaultSvc := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
598+
if r.URL.Path != wantURL {
599+
http.NotFound(w, r)
600+
}
601+
602+
if r.Method != http.MethodGet {
603+
w.WriteHeader(http.StatusMethodNotAllowed)
604+
return
605+
}
606+
607+
if tt.writeBadContentLength {
608+
w.Header().Set("Content-Length", "1")
609+
}
610+
611+
w.WriteHeader(tt.vaultStatusCode)
612+
}))
613+
defer vaultSvc.Close()
614+
615+
vaultEndpoint := vaultSvc.URL
616+
if tt.endpoint != "" {
617+
vaultEndpoint = tt.endpoint
618+
}
619+
620+
h := handler{
621+
logger: log.NewNopLogger(),
622+
env: env.Vars{
623+
VaultAddress: vaultEndpoint,
624+
},
625+
}
626+
627+
// Dummy request.
628+
req, err := http.NewRequest("", "", nil)
629+
if err != nil {
630+
assert.Nil(t, err)
631+
}
632+
633+
resp := httptest.NewRecorder()
634+
635+
h.healthCheck(resp, req)
636+
637+
respResult := resp.Result()
638+
defer respResult.Body.Close()
639+
640+
body, err := io.ReadAll(respResult.Body)
641+
assert.Nil(t, err)
642+
643+
assert.Equal(t, tt.wantStatusCode, respResult.StatusCode)
644+
assert.Equal(t, tt.wantResponseBody, string(body))
645+
})
646+
}
647+
}
648+
548649
// Serialize a type to JSON-encoded byte buffer.
549650
func serialize(toMarshal interface{}) *bytes.Buffer {
550651
jsonStr, _ := json.Marshal(toMarshal)

0 commit comments

Comments
 (0)