mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2026-10-17 20:17:03 +02:00
feat: readiness check (#1839)
* feat: readiness check * fix: no need for query param * docs: add a note * chore: move the readyness check to its own endpoint * docs(cr): add godoc Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk>
This commit is contained in:
co-authored by
Joel Speed
parent
8b77c97009
commit
f753ec1ca5
@@ -0,0 +1,40 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/justinas/alice"
|
||||
)
|
||||
|
||||
// Verifiable an interface for an object that has a connection to external
|
||||
// data source and exports a function to validate that connection
|
||||
type Verifiable interface {
|
||||
VerifyConnection(context.Context) error
|
||||
}
|
||||
|
||||
// NewReadynessCheck returns a middleware that performs deep health checks
|
||||
// (verifies the connection to any underlying store) on a specific `path`
|
||||
func NewReadynessCheck(path string, verifiable Verifiable) alice.Constructor {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return readynessCheck(path, verifiable, next)
|
||||
}
|
||||
}
|
||||
|
||||
func readynessCheck(path string, verifiable Verifiable, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if path != "" && req.URL.EscapedPath() == path {
|
||||
if err := verifiable.VerifyConnection(req.Context()); err != nil {
|
||||
rw.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(rw, "error: %v", err)
|
||||
return
|
||||
}
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(rw, "OK")
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("ReadynessCheck suite", func() {
|
||||
type requestTableInput struct {
|
||||
readyPath string
|
||||
healthVerifiable Verifiable
|
||||
requestString string
|
||||
expectedStatus int
|
||||
expectedBody string
|
||||
}
|
||||
|
||||
DescribeTable("when serving a request",
|
||||
func(in *requestTableInput) {
|
||||
req := httptest.NewRequest("", in.requestString, nil)
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
|
||||
handler := NewReadynessCheck(in.readyPath, in.healthVerifiable)(http.NotFoundHandler())
|
||||
handler.ServeHTTP(rw, req)
|
||||
|
||||
Expect(rw.Code).To(Equal(in.expectedStatus))
|
||||
Expect(rw.Body.String()).To(Equal(in.expectedBody))
|
||||
},
|
||||
Entry("when requesting the readyness check path", &requestTableInput{
|
||||
readyPath: "/ready",
|
||||
healthVerifiable: &fakeVerifiable{nil},
|
||||
requestString: "http://example.com/ready",
|
||||
expectedStatus: 200,
|
||||
expectedBody: "OK",
|
||||
}),
|
||||
Entry("when requesting a different path", &requestTableInput{
|
||||
readyPath: "/ready",
|
||||
healthVerifiable: &fakeVerifiable{nil},
|
||||
requestString: "http://example.com/different",
|
||||
expectedStatus: 404,
|
||||
expectedBody: "404 page not found\n",
|
||||
}),
|
||||
Entry("when a blank string is configured as a readyness check path and the request has no specific path", &requestTableInput{
|
||||
readyPath: "",
|
||||
healthVerifiable: &fakeVerifiable{nil},
|
||||
requestString: "http://example.com",
|
||||
expectedStatus: 404,
|
||||
expectedBody: "404 page not found\n",
|
||||
}),
|
||||
Entry("with full health check and without an underlying error", &requestTableInput{
|
||||
readyPath: "/ready",
|
||||
healthVerifiable: &fakeVerifiable{nil},
|
||||
requestString: "http://example.com/ready",
|
||||
expectedStatus: 200,
|
||||
expectedBody: "OK",
|
||||
}),
|
||||
Entry("with full health check and with an underlying error", &requestTableInput{
|
||||
readyPath: "/ready",
|
||||
healthVerifiable: &fakeVerifiable{func(ctx context.Context) error { return errors.New("failed to check") }},
|
||||
requestString: "http://example.com/ready",
|
||||
expectedStatus: 500,
|
||||
expectedBody: "error: failed to check",
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
type fakeVerifiable struct {
|
||||
mock func(context.Context) error
|
||||
}
|
||||
|
||||
func (v *fakeVerifiable) VerifyConnection(ctx context.Context) error {
|
||||
if v.mock != nil {
|
||||
return v.mock(ctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ Verifiable = (*fakeVerifiable)(nil)
|
||||
@@ -793,3 +793,7 @@ func (f *fakeSessionStore) Clear(rw http.ResponseWriter, req *http.Request) erro
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeSessionStore) VerifyConnection(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user