From 144c63163cdd66e9fc27d8169c3010369ad8bb43 Mon Sep 17 00:00:00 2001 From: luk3skyw4lker Date: Sat, 17 Jun 2023 13:01:50 -0300 Subject: [PATCH 01/21] :sparkles: feat: add liveness and readiness checkers --- app.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/app.go b/app.go index 8bd21739f6..151b3e7318 100644 --- a/app.go +++ b/app.go @@ -35,6 +35,9 @@ const Version = "2.46.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error +// Probe Checker defines a function to check liveness or readiness of the application +type ProbeChecker = func(*Ctx) bool + // Map is a shortcut for map[string]interface{}, useful for JSON returns type Map map[string]interface{} @@ -390,6 +393,26 @@ type Config struct { // // Optional. Default: DefaultMethods RequestMethods []string + + // Config for liveness probe of the container engine being used + // + // Optional. Default: func(c *Ctx) bool { return true } + IsLive ProbeChecker + + // Config for liveness probe of the container engine being used + // + // Optional. Default: /liveness + IsLiveEndpoint string + + // Config for readiness probe of the container engine being used + // + // Optional. Default: func(c *Ctx) bool { return true } + IsReady ProbeChecker + + // Config for readiness probe of the container engine being used + // + // Optional. Default: /readiness + IsReadyEndpoint string } // Static defines configuration options when defining static assets. @@ -453,8 +476,22 @@ const ( DefaultReadBufferSize = 4096 DefaultWriteBufferSize = 4096 DefaultCompressedFileSuffix = ".fiber.gz" + DefaultLivenessEndpoint = "/liveness" + DefaultReadinessEndpoint = "/readiness" ) +var DefaultHealthFunction = func(c *Ctx) bool { return true } + +var ProbeCheckerHandler = func(checker ProbeChecker) Handler { + return func(c *Ctx) error { + if checker(c) { + return c.SendStatus(StatusOK) + } + + return c.SendStatus(StatusServiceUnavailable) + } +} + // HTTP methods enabled by default var DefaultMethods = []string{ MethodGet, @@ -564,6 +601,15 @@ func New(config ...Config) *App { if len(app.config.RequestMethods) == 0 { app.config.RequestMethods = DefaultMethods } + if app.config.IsLiveEndpoint == "" { + app.config.IsLiveEndpoint = DefaultLivenessEndpoint + } + if app.config.IsReadyEndpoint == "" { + app.config.IsReadyEndpoint = DefaultReadinessEndpoint + } + if app.config.IsLive == nil { + app.config.IsLive = DefaultHealthFunction + } app.config.trustedProxiesMap = make(map[string]struct{}, len(app.config.TrustedProxies)) for _, ipAddress := range app.config.TrustedProxies { @@ -580,6 +626,12 @@ func New(config ...Config) *App { // Init app app.init() + if app.config.IsReady != nil { + app.Get(app.config.IsReadyEndpoint, ProbeCheckerHandler(app.config.IsReady)) + } + + app.Get(app.config.IsLiveEndpoint, ProbeCheckerHandler(app.config.IsLive)) + // Return app return app } From fed6c5ede562f62ca1fa97b004b372f9f2bad9b6 Mon Sep 17 00:00:00 2001 From: luk3skyw4lker Date: Sat, 17 Jun 2023 13:15:31 -0300 Subject: [PATCH 02/21] :memo: docs: add docs for liveness and readiness --- docs/api/app.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/api/app.md b/docs/api/app.md index 5f788ca356..4256ed8187 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -655,3 +655,26 @@ Hooks is a method to return [hooks](../guide/hooks.md) property. ```go title="Signature" func (app *App) Hooks() *Hooks ``` + +## Liveness And Readiness Checks + +Fiber comes with an out of the box way of implementing your liveness and readiness checking, when creating a new app, you can pass your own handlers for liveness and readiness checks: + +```go title="Examples" +// Create route with GET method for test: +app := fiber.New(fiber.Config{ + IsLive: func (c *fiber.Ctx) bool { + return true + }, + IsLiveEndpoint: "/liveness", + IsReady: func (c *fiber.Ctx) bool { + return serviceA.Ready() && serviceB.Ready() && ... + } + IsReadyEndpoint: "/readiness", +}) + +// Listen on port :8080 +app.Listen(":8080") +``` + +The endpoint values default to `/liveness` and `/readiness`. Both functions are optional, the liveness endpoint will return `true` right when the server is up and running but the readiness endpoint will not answer any requests if an `IsReady` function isn't provided. \ No newline at end of file From 96d39448abddb1c9a0215d1bb16dcbd4d84834ae Mon Sep 17 00:00:00 2001 From: luk3skyw4lker Date: Sat, 17 Jun 2023 13:21:23 -0300 Subject: [PATCH 03/21] :sparkles: feat: add options method for probe checkers --- app.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app.go b/app.go index 151b3e7318..406e309d7e 100644 --- a/app.go +++ b/app.go @@ -628,9 +628,11 @@ func New(config ...Config) *App { if app.config.IsReady != nil { app.Get(app.config.IsReadyEndpoint, ProbeCheckerHandler(app.config.IsReady)) + app.Options(app.config.IsReadyEndpoint, ProbeCheckerHandler(app.config.IsReady)) } app.Get(app.config.IsLiveEndpoint, ProbeCheckerHandler(app.config.IsLive)) + app.Options(app.config.IsLiveEndpoint, ProbeCheckerHandler(app.config.IsLive)) // Return app return app From ec98691b4fbab8f0333f78f4311d96d30f859550 Mon Sep 17 00:00:00 2001 From: luk3skyw4lker Date: Sat, 17 Jun 2023 13:30:26 -0300 Subject: [PATCH 04/21] :white_check_mark: tests: add tests for liveness and readiness --- app_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/app_test.go b/app_test.go index 09600b72be..c41e14ef91 100644 --- a/app_test.go +++ b/app_test.go @@ -1862,3 +1862,47 @@ func Test_Middleware_Route_Naming_With_Use(t *testing.T) { } } } + +func Test_Default_Liveness_Probe(t *testing.T) { + app := New() + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/liveness", nil)) + + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) +} + +func Test_Custom_Liveness_Probe(t *testing.T) { + app := New(Config{ + IsLive: func(c *Ctx) bool { return true }, + IsLiveEndpoint: "/live", + }) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/live", nil)) + + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) +} + +func Test_Default_Readiness_Probe(t *testing.T) { + app := New(Config{ + IsReady: func(c *Ctx) bool { return true }, + }) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/readiness", nil)) + + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) +} + +func Test_Custom_Readiness_Probe(t *testing.T) { + app := New(Config{ + IsReady: func(c *Ctx) bool { return true }, + IsReadyEndpoint: "/ready", + }) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/ready", nil)) + + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) +} From 7615bec8e4080dd34aed10d83803177220d85db3 Mon Sep 17 00:00:00 2001 From: luk3skyw4lker Date: Sat, 17 Jun 2023 13:55:32 -0300 Subject: [PATCH 05/21] :recycle: refactor: change default endpoint values --- app.go | 4 ++-- app_test.go | 4 ++-- docs/api/app.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app.go b/app.go index 406e309d7e..1085fba81a 100644 --- a/app.go +++ b/app.go @@ -476,8 +476,8 @@ const ( DefaultReadBufferSize = 4096 DefaultWriteBufferSize = 4096 DefaultCompressedFileSuffix = ".fiber.gz" - DefaultLivenessEndpoint = "/liveness" - DefaultReadinessEndpoint = "/readiness" + DefaultLivenessEndpoint = "/healthz" + DefaultReadinessEndpoint = "/readyz" ) var DefaultHealthFunction = func(c *Ctx) bool { return true } diff --git a/app_test.go b/app_test.go index c41e14ef91..f73776d608 100644 --- a/app_test.go +++ b/app_test.go @@ -1866,7 +1866,7 @@ func Test_Middleware_Route_Naming_With_Use(t *testing.T) { func Test_Default_Liveness_Probe(t *testing.T) { app := New() - resp, err := app.Test(httptest.NewRequest(MethodGet, "/liveness", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/healthz", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) @@ -1889,7 +1889,7 @@ func Test_Default_Readiness_Probe(t *testing.T) { IsReady: func(c *Ctx) bool { return true }, }) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/readiness", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/readyz", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) diff --git a/docs/api/app.md b/docs/api/app.md index 4256ed8187..3d4864fd1a 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -677,4 +677,4 @@ app := fiber.New(fiber.Config{ app.Listen(":8080") ``` -The endpoint values default to `/liveness` and `/readiness`. Both functions are optional, the liveness endpoint will return `true` right when the server is up and running but the readiness endpoint will not answer any requests if an `IsReady` function isn't provided. \ No newline at end of file +The endpoint values default to `/healthz` for liveness and `/readyz` for readiness. Both functions are optional, the liveness endpoint will return `true` right when the server is up and running but the readiness endpoint will not answer any requests if an `IsReady` function isn't provided. \ No newline at end of file From b00ee64b1a26fb8d61636d69548b374181a8db15 Mon Sep 17 00:00:00 2001 From: luk3skyw4lker Date: Sat, 17 Jun 2023 16:13:28 -0300 Subject: [PATCH 06/21] :recycle: refactor: change default value for liveness endpoint --- app.go | 46 +++++++++++++++++++++++----------------------- app_test.go | 2 +- docs/api/app.md | 6 +++--- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/app.go b/app.go index 1085fba81a..fc8ec1d483 100644 --- a/app.go +++ b/app.go @@ -35,7 +35,7 @@ const Version = "2.46.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error -// Probe Checker defines a function to check liveness or readiness of the application +// ProbeChecker defines a function to check liveness or readiness of the application type ProbeChecker = func(*Ctx) bool // Map is a shortcut for map[string]interface{}, useful for JSON returns @@ -399,19 +399,19 @@ type Config struct { // Optional. Default: func(c *Ctx) bool { return true } IsLive ProbeChecker - // Config for liveness probe of the container engine being used + // HTTP endpoint of the liveness probe // - // Optional. Default: /liveness + // Optional. Default: /livez IsLiveEndpoint string // Config for readiness probe of the container engine being used // - // Optional. Default: func(c *Ctx) bool { return true } + // Optional. Default: nil IsReady ProbeChecker - // Config for readiness probe of the container engine being used + // HTTP endpoint of the readiness probe // - // Optional. Default: /readiness + // Optional. Default: /readyz IsReadyEndpoint string } @@ -476,21 +476,11 @@ const ( DefaultReadBufferSize = 4096 DefaultWriteBufferSize = 4096 DefaultCompressedFileSuffix = ".fiber.gz" - DefaultLivenessEndpoint = "/healthz" + DefaultLivenessEndpoint = "/livez" DefaultReadinessEndpoint = "/readyz" ) -var DefaultHealthFunction = func(c *Ctx) bool { return true } - -var ProbeCheckerHandler = func(checker ProbeChecker) Handler { - return func(c *Ctx) error { - if checker(c) { - return c.SendStatus(StatusOK) - } - - return c.SendStatus(StatusServiceUnavailable) - } -} +var DefaultLiveFunc = func(c *Ctx) bool { return true } // HTTP methods enabled by default var DefaultMethods = []string{ @@ -608,7 +598,7 @@ func New(config ...Config) *App { app.config.IsReadyEndpoint = DefaultReadinessEndpoint } if app.config.IsLive == nil { - app.config.IsLive = DefaultHealthFunction + app.config.IsLive = DefaultLiveFunc } app.config.trustedProxiesMap = make(map[string]struct{}, len(app.config.TrustedProxies)) @@ -627,17 +617,27 @@ func New(config ...Config) *App { app.init() if app.config.IsReady != nil { - app.Get(app.config.IsReadyEndpoint, ProbeCheckerHandler(app.config.IsReady)) - app.Options(app.config.IsReadyEndpoint, ProbeCheckerHandler(app.config.IsReady)) + app.Get(app.config.IsReadyEndpoint, probeCheckerHandler(app.config.IsReady)) + app.Options(app.config.IsReadyEndpoint, probeCheckerHandler(app.config.IsReady)) } - app.Get(app.config.IsLiveEndpoint, ProbeCheckerHandler(app.config.IsLive)) - app.Options(app.config.IsLiveEndpoint, ProbeCheckerHandler(app.config.IsLive)) + app.Get(app.config.IsLiveEndpoint, probeCheckerHandler(app.config.IsLive)) + app.Options(app.config.IsLiveEndpoint, probeCheckerHandler(app.config.IsLive)) // Return app return app } +func probeCheckerHandler(checker ProbeChecker) Handler { + return func(c *Ctx) error { + if checker(c) { + return c.SendStatus(StatusOK) + } + + return c.SendStatus(StatusServiceUnavailable) + } +} + // Adds an ip address to trustedProxyRanges or trustedProxiesMap based on whether it is an IP range or not func (app *App) handleTrustedProxy(ipAddress string) { if strings.Contains(ipAddress, "/") { diff --git a/app_test.go b/app_test.go index f73776d608..121b79f308 100644 --- a/app_test.go +++ b/app_test.go @@ -1866,7 +1866,7 @@ func Test_Middleware_Route_Naming_With_Use(t *testing.T) { func Test_Default_Liveness_Probe(t *testing.T) { app := New() - resp, err := app.Test(httptest.NewRequest(MethodGet, "/healthz", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/livez", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) diff --git a/docs/api/app.md b/docs/api/app.md index 3d4864fd1a..74516fea2a 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -666,15 +666,15 @@ app := fiber.New(fiber.Config{ IsLive: func (c *fiber.Ctx) bool { return true }, - IsLiveEndpoint: "/liveness", + IsLiveEndpoint: "/livez", IsReady: func (c *fiber.Ctx) bool { return serviceA.Ready() && serviceB.Ready() && ... } - IsReadyEndpoint: "/readiness", + IsReadyEndpoint: "/readyz", }) // Listen on port :8080 app.Listen(":8080") ``` -The endpoint values default to `/healthz` for liveness and `/readyz` for readiness. Both functions are optional, the liveness endpoint will return `true` right when the server is up and running but the readiness endpoint will not answer any requests if an `IsReady` function isn't provided. \ No newline at end of file +The endpoint values default to `/livez` for liveness and `/readyz` for readiness. Both functions are optional, the liveness endpoint will return `true` right when the server is up and running but the readiness endpoint will not answer any requests if an `IsReady` function isn't provided. \ No newline at end of file From 87faa6abea4ad7db3d30d45571e0d11f6b86dc0d Mon Sep 17 00:00:00 2001 From: luk3skyw4lker Date: Sat, 17 Jun 2023 16:37:09 -0300 Subject: [PATCH 07/21] :memo: docs: add return status for liveness and readiness probes --- docs/api/app.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/api/app.md b/docs/api/app.md index 74516fea2a..fb55ad3c66 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -677,4 +677,6 @@ app := fiber.New(fiber.Config{ app.Listen(":8080") ``` -The endpoint values default to `/livez` for liveness and `/readyz` for readiness. Both functions are optional, the liveness endpoint will return `true` right when the server is up and running but the readiness endpoint will not answer any requests if an `IsReady` function isn't provided. \ No newline at end of file +The endpoint values default to `/livez` for liveness and `/readyz` for readiness. Both functions are optional, the liveness endpoint will return `true` right when the server is up and running but the readiness endpoint will not answer any requests if an `IsReady` function isn't provided. + +The HTTP status returned to the containerized environment are: 200 OK if the checker function returns true and 503 Service Unavailable if the checker function returns false. \ No newline at end of file From 789b882a268c56c5cd64e1cec052fb479d1e2b65 Mon Sep 17 00:00:00 2001 From: luk3skyw4lker Date: Mon, 26 Jun 2023 21:32:23 -0300 Subject: [PATCH 08/21] :recycle: refactor: change probechecker to middleware --- app.go | 52 ------------- app_test.go | 44 ----------- middleware/probechecker/probechecker.go | 98 +++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 96 deletions(-) create mode 100644 middleware/probechecker/probechecker.go diff --git a/app.go b/app.go index fc8ec1d483..e2948ff9d8 100644 --- a/app.go +++ b/app.go @@ -35,9 +35,6 @@ const Version = "2.46.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error -// ProbeChecker defines a function to check liveness or readiness of the application -type ProbeChecker = func(*Ctx) bool - // Map is a shortcut for map[string]interface{}, useful for JSON returns type Map map[string]interface{} @@ -393,26 +390,6 @@ type Config struct { // // Optional. Default: DefaultMethods RequestMethods []string - - // Config for liveness probe of the container engine being used - // - // Optional. Default: func(c *Ctx) bool { return true } - IsLive ProbeChecker - - // HTTP endpoint of the liveness probe - // - // Optional. Default: /livez - IsLiveEndpoint string - - // Config for readiness probe of the container engine being used - // - // Optional. Default: nil - IsReady ProbeChecker - - // HTTP endpoint of the readiness probe - // - // Optional. Default: /readyz - IsReadyEndpoint string } // Static defines configuration options when defining static assets. @@ -480,8 +457,6 @@ const ( DefaultReadinessEndpoint = "/readyz" ) -var DefaultLiveFunc = func(c *Ctx) bool { return true } - // HTTP methods enabled by default var DefaultMethods = []string{ MethodGet, @@ -591,15 +566,6 @@ func New(config ...Config) *App { if len(app.config.RequestMethods) == 0 { app.config.RequestMethods = DefaultMethods } - if app.config.IsLiveEndpoint == "" { - app.config.IsLiveEndpoint = DefaultLivenessEndpoint - } - if app.config.IsReadyEndpoint == "" { - app.config.IsReadyEndpoint = DefaultReadinessEndpoint - } - if app.config.IsLive == nil { - app.config.IsLive = DefaultLiveFunc - } app.config.trustedProxiesMap = make(map[string]struct{}, len(app.config.TrustedProxies)) for _, ipAddress := range app.config.TrustedProxies { @@ -616,28 +582,10 @@ func New(config ...Config) *App { // Init app app.init() - if app.config.IsReady != nil { - app.Get(app.config.IsReadyEndpoint, probeCheckerHandler(app.config.IsReady)) - app.Options(app.config.IsReadyEndpoint, probeCheckerHandler(app.config.IsReady)) - } - - app.Get(app.config.IsLiveEndpoint, probeCheckerHandler(app.config.IsLive)) - app.Options(app.config.IsLiveEndpoint, probeCheckerHandler(app.config.IsLive)) - // Return app return app } -func probeCheckerHandler(checker ProbeChecker) Handler { - return func(c *Ctx) error { - if checker(c) { - return c.SendStatus(StatusOK) - } - - return c.SendStatus(StatusServiceUnavailable) - } -} - // Adds an ip address to trustedProxyRanges or trustedProxiesMap based on whether it is an IP range or not func (app *App) handleTrustedProxy(ipAddress string) { if strings.Contains(ipAddress, "/") { diff --git a/app_test.go b/app_test.go index 121b79f308..09600b72be 100644 --- a/app_test.go +++ b/app_test.go @@ -1862,47 +1862,3 @@ func Test_Middleware_Route_Naming_With_Use(t *testing.T) { } } } - -func Test_Default_Liveness_Probe(t *testing.T) { - app := New() - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/livez", nil)) - - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) -} - -func Test_Custom_Liveness_Probe(t *testing.T) { - app := New(Config{ - IsLive: func(c *Ctx) bool { return true }, - IsLiveEndpoint: "/live", - }) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/live", nil)) - - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) -} - -func Test_Default_Readiness_Probe(t *testing.T) { - app := New(Config{ - IsReady: func(c *Ctx) bool { return true }, - }) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/readyz", nil)) - - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) -} - -func Test_Custom_Readiness_Probe(t *testing.T) { - app := New(Config{ - IsReady: func(c *Ctx) bool { return true }, - IsReadyEndpoint: "/ready", - }) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/ready", nil)) - - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) -} diff --git a/middleware/probechecker/probechecker.go b/middleware/probechecker/probechecker.go new file mode 100644 index 0000000000..02fad605ae --- /dev/null +++ b/middleware/probechecker/probechecker.go @@ -0,0 +1,98 @@ +package probechecker + +import ( + "fmt" + + "github.com/gofiber/fiber/v2" +) + +// ProbeChecker defines a function to check liveness or readiness of the application +type ProbeChecker func(*fiber.Ctx) bool + +// ProbeCheckerHandler defines a function that returns a ProbeChecker +type ProbeCheckerHandler func(ProbeChecker) fiber.Handler + +// Config is the config struct for the probechecker middleware +type Config struct { + // Config for liveness probe of the container engine being used + // + // Optional. Default: func(c *Ctx) bool { return true } + IsLive ProbeChecker + + // HTTP endpoint of the liveness probe + // + // Optional. Default: /livez + IsLiveEndpoint string + + // Config for readiness probe of the container engine being used + // + // Optional. Default: nil + IsReady ProbeChecker + + // HTTP endpoint of the readiness probe + // + // Optional. Default: /readyz + IsReadyEndpoint string +} + +var DefaultLiveFunc = func(c *fiber.Ctx) bool { return true } + +const ( + DefaultLivenessEndpoint = "/livez" + DefaultReadinessEndpoint = "/readyz" +) + +func probeCheckerHandler(checker ProbeChecker) fiber.Handler { + return func(c *fiber.Ctx) error { + if checker(c) { + return c.SendStatus(fiber.StatusOK) + } + + return c.SendStatus(fiber.StatusServiceUnavailable) + } +} + +func checkRoute(path string, config *Config) string { + switch path { + case DefaultLivenessEndpoint, config.IsLiveEndpoint: + return "liveness" + case DefaultReadinessEndpoint, config.IsReadyEndpoint: + return "readiness" + default: + return "" + } +} + +func New(config *Config) fiber.Handler { + if config.IsLiveEndpoint == "" { + config.IsLiveEndpoint = DefaultLivenessEndpoint + } + if config.IsReadyEndpoint == "" { + config.IsReadyEndpoint = DefaultReadinessEndpoint + } + if config.IsLive == nil { + config.IsLive = DefaultLiveFunc + } + + var checkers = map[string]fiber.Handler{ + "liveness": probeCheckerHandler(config.IsLive), + "readiness": probeCheckerHandler(config.IsReady), + } + + return func(c *fiber.Ctx) error { + route := c.Route() + routeType := checkRoute(route.Path, config) + + if routeType != "" || route.Method != fiber.MethodGet { + handler, ok := checkers[routeType] + + if !ok { + return fmt.Errorf("routeType of %s not found in checkers", routeType) + } + + return handler(c) + } + + return c.Next() + } +} From df23f6871fe906ed6dbe28ca85bbdcf285d8f00a Mon Sep 17 00:00:00 2001 From: luk3skyw4lker Date: Fri, 30 Jun 2023 19:45:44 -0300 Subject: [PATCH 09/21] :memo: docs: move docs to middleware session --- docs/api/app.md | 27 +---------- docs/api/middleware/probechecker.md | 75 +++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 26 deletions(-) create mode 100644 docs/api/middleware/probechecker.md diff --git a/docs/api/app.md b/docs/api/app.md index fb55ad3c66..dd0e97dc20 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -654,29 +654,4 @@ Hooks is a method to return [hooks](../guide/hooks.md) property. ```go title="Signature" func (app *App) Hooks() *Hooks -``` - -## Liveness And Readiness Checks - -Fiber comes with an out of the box way of implementing your liveness and readiness checking, when creating a new app, you can pass your own handlers for liveness and readiness checks: - -```go title="Examples" -// Create route with GET method for test: -app := fiber.New(fiber.Config{ - IsLive: func (c *fiber.Ctx) bool { - return true - }, - IsLiveEndpoint: "/livez", - IsReady: func (c *fiber.Ctx) bool { - return serviceA.Ready() && serviceB.Ready() && ... - } - IsReadyEndpoint: "/readyz", -}) - -// Listen on port :8080 -app.Listen(":8080") -``` - -The endpoint values default to `/livez` for liveness and `/readyz` for readiness. Both functions are optional, the liveness endpoint will return `true` right when the server is up and running but the readiness endpoint will not answer any requests if an `IsReady` function isn't provided. - -The HTTP status returned to the containerized environment are: 200 OK if the checker function returns true and 503 Service Unavailable if the checker function returns false. \ No newline at end of file +``` \ No newline at end of file diff --git a/docs/api/middleware/probechecker.md b/docs/api/middleware/probechecker.md new file mode 100644 index 0000000000..510ba46d31 --- /dev/null +++ b/docs/api/middleware/probechecker.md @@ -0,0 +1,75 @@ +--- +id: probecheker +title: probecheker +--- + +Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/fiber) that provides two endpoints for checking the health and ready state of any HTTP application. + +The endpoint values default to `/livez` for liveness and `/readyz` for readiness. Both functions are optional, the liveness endpoint will return `true` right when the server is up and running but the readiness endpoint will not answer any requests if an `IsReady` function isn't provided. + +The HTTP status returned to the containerized environment are: 200 OK if the checker function returns true and 503 Service Unavailable if the checker function returns false. + +## Signatures + +```go +func New() fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/probechecker" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Initializing with default config +app.Use(probechecker.New()) + +// Initialize with custom config +app.Use( + probechecker.New( + IsLive: func (c *fiber.Ctx) bool { + return true + }, + IsLiveEndpoint: "/livez", + IsReady: func (c *fiber.Ctx) bool { + return serviceA.Ready() && serviceB.Ready() && ... + } + IsReadyEndpoint: "/readyz", + ) +) + +``` + +## Config + +```go +type Config struct { + // Config for liveness probe of the container engine being used + // + // Optional. Default: func(c *Ctx) bool { return true } + IsLive ProbeChecker + + // HTTP endpoint of the liveness probe + // + // Optional. Default: /livez + IsLiveEndpoint string + + // Config for readiness probe of the container engine being used + // + // Optional. Default: nil + IsReady ProbeChecker + + // HTTP endpoint of the readiness probe + // + // Optional. Default: /readyz + IsReadyEndpoint string +} +``` \ No newline at end of file From 4ad217e4e3b1899886f9c619c2cac6de5c155f21 Mon Sep 17 00:00:00 2001 From: luk3skyw4lker Date: Tue, 15 Aug 2023 15:03:46 -0300 Subject: [PATCH 10/21] :recycle: refactor: apply gofumpt formatting --- middleware/probechecker/probechecker.go | 52 +++++++++++++++++-------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/middleware/probechecker/probechecker.go b/middleware/probechecker/probechecker.go index 02fad605ae..08f265551d 100644 --- a/middleware/probechecker/probechecker.go +++ b/middleware/probechecker/probechecker.go @@ -35,15 +35,19 @@ type Config struct { IsReadyEndpoint string } -var DefaultLiveFunc = func(c *fiber.Ctx) bool { return true } - const ( DefaultLivenessEndpoint = "/livez" DefaultReadinessEndpoint = "/readyz" ) +func defaultLiveFunc(c *fiber.Ctx) bool { return true } + func probeCheckerHandler(checker ProbeChecker) fiber.Handler { return func(c *fiber.Ctx) error { + if checker == nil { + return c.Next() + } + if checker(c) { return c.SendStatus(fiber.StatusOK) } @@ -63,25 +67,17 @@ func checkRoute(path string, config *Config) string { } } -func New(config *Config) fiber.Handler { - if config.IsLiveEndpoint == "" { - config.IsLiveEndpoint = DefaultLivenessEndpoint - } - if config.IsReadyEndpoint == "" { - config.IsReadyEndpoint = DefaultReadinessEndpoint - } - if config.IsLive == nil { - config.IsLive = DefaultLiveFunc - } +func New(config ...Config) fiber.Handler { + cfg := defaultConfig(config...) - var checkers = map[string]fiber.Handler{ - "liveness": probeCheckerHandler(config.IsLive), - "readiness": probeCheckerHandler(config.IsReady), + checkers := map[string]fiber.Handler{ + "liveness": probeCheckerHandler(cfg.IsLive), + "readiness": probeCheckerHandler(cfg.IsReady), } return func(c *fiber.Ctx) error { route := c.Route() - routeType := checkRoute(route.Path, config) + routeType := checkRoute(route.Path, &cfg) if routeType != "" || route.Method != fiber.MethodGet { handler, ok := checkers[routeType] @@ -96,3 +92,27 @@ func New(config *Config) fiber.Handler { return c.Next() } } + +func defaultConfig(config ...Config) Config { + if len(config) < 1 { + return Config{ + IsLive: defaultLiveFunc, + IsLiveEndpoint: DefaultLivenessEndpoint, + IsReadyEndpoint: DefaultReadinessEndpoint, + } + } + + cfg := config[0] + + if cfg.IsLiveEndpoint == "" { + cfg.IsLiveEndpoint = DefaultLivenessEndpoint + } + if cfg.IsReadyEndpoint == "" { + cfg.IsReadyEndpoint = DefaultReadinessEndpoint + } + if cfg.IsLive == nil { + cfg.IsLive = defaultLiveFunc + } + + return cfg +} From 2c41068c4b93588fa2d73047f89cdafe77f9c37c Mon Sep 17 00:00:00 2001 From: luk3skyw4lker Date: Tue, 15 Aug 2023 15:09:40 -0300 Subject: [PATCH 11/21] :recycle: refactor: remove unused parameter --- middleware/probechecker/probechecker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/probechecker/probechecker.go b/middleware/probechecker/probechecker.go index 08f265551d..d380f5c44a 100644 --- a/middleware/probechecker/probechecker.go +++ b/middleware/probechecker/probechecker.go @@ -40,7 +40,7 @@ const ( DefaultReadinessEndpoint = "/readyz" ) -func defaultLiveFunc(c *fiber.Ctx) bool { return true } +func defaultLiveFunc(*fiber.Ctx) bool { return true } func probeCheckerHandler(checker ProbeChecker) fiber.Handler { return func(c *fiber.Ctx) error { From 7fdb8aa541ccd62e08afab9f05b6730bf9ff7064 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Sat, 23 Dec 2023 21:37:00 +0300 Subject: [PATCH 12/21] split config and apply a review --- middleware/probechecker/config.go | 63 +++++++++++++++++++++++++ middleware/probechecker/probechecker.go | 54 --------------------- 2 files changed, 63 insertions(+), 54 deletions(-) create mode 100644 middleware/probechecker/config.go diff --git a/middleware/probechecker/config.go b/middleware/probechecker/config.go new file mode 100644 index 0000000000..42b68b4022 --- /dev/null +++ b/middleware/probechecker/config.go @@ -0,0 +1,63 @@ +package probechecker + +import "github.com/gofiber/fiber/v2" + +// Config is the config struct for the probechecker middleware +type Config struct { + // Config for liveness probe of the container engine being used + // + // Optional. Default: func(c *Ctx) bool { return true } + IsLive ProbeChecker + + // HTTP endpoint of the liveness probe + // + // Optional. Default: /livez + IsLiveEndpoint string + + // Config for readiness probe of the container engine being used + // + // Optional. Default: nil + IsReady ProbeChecker + + // HTTP endpoint of the readiness probe + // + // Optional. Default: /readyz + IsReadyEndpoint string +} + +const ( + DefaultLivenessEndpoint = "/livez" + DefaultReadinessEndpoint = "/readyz" +) + +func defaultLiveFunc(*fiber.Ctx) bool { return true } + +// ConfigDefault is the default config +var ConfigDefault = Config{ + IsLive: defaultLiveFunc, + IsLiveEndpoint: DefaultLivenessEndpoint, + IsReadyEndpoint: DefaultReadinessEndpoint, +} + +// defaultConfig returns a default config for the probechecker middleware. +func defaultConfig(config ...Config) Config { + if len(config) < 1 { + return ConfigDefault + } + + cfg := config[0] + + if cfg.IsLive == nil { + cfg.IsLive = defaultLiveFunc + } + + if cfg.IsLiveEndpoint == "" { + cfg.IsLiveEndpoint = DefaultLivenessEndpoint + } + + if cfg.IsReadyEndpoint == "" { + cfg.IsReadyEndpoint = DefaultReadinessEndpoint + } + + return cfg +} diff --git a/middleware/probechecker/probechecker.go b/middleware/probechecker/probechecker.go index d380f5c44a..1bd90bb383 100644 --- a/middleware/probechecker/probechecker.go +++ b/middleware/probechecker/probechecker.go @@ -12,36 +12,6 @@ type ProbeChecker func(*fiber.Ctx) bool // ProbeCheckerHandler defines a function that returns a ProbeChecker type ProbeCheckerHandler func(ProbeChecker) fiber.Handler -// Config is the config struct for the probechecker middleware -type Config struct { - // Config for liveness probe of the container engine being used - // - // Optional. Default: func(c *Ctx) bool { return true } - IsLive ProbeChecker - - // HTTP endpoint of the liveness probe - // - // Optional. Default: /livez - IsLiveEndpoint string - - // Config for readiness probe of the container engine being used - // - // Optional. Default: nil - IsReady ProbeChecker - - // HTTP endpoint of the readiness probe - // - // Optional. Default: /readyz - IsReadyEndpoint string -} - -const ( - DefaultLivenessEndpoint = "/livez" - DefaultReadinessEndpoint = "/readyz" -) - -func defaultLiveFunc(*fiber.Ctx) bool { return true } - func probeCheckerHandler(checker ProbeChecker) fiber.Handler { return func(c *fiber.Ctx) error { if checker == nil { @@ -92,27 +62,3 @@ func New(config ...Config) fiber.Handler { return c.Next() } } - -func defaultConfig(config ...Config) Config { - if len(config) < 1 { - return Config{ - IsLive: defaultLiveFunc, - IsLiveEndpoint: DefaultLivenessEndpoint, - IsReadyEndpoint: DefaultReadinessEndpoint, - } - } - - cfg := config[0] - - if cfg.IsLiveEndpoint == "" { - cfg.IsLiveEndpoint = DefaultLivenessEndpoint - } - if cfg.IsReadyEndpoint == "" { - cfg.IsReadyEndpoint = DefaultReadinessEndpoint - } - if cfg.IsLive == nil { - cfg.IsLive = defaultLiveFunc - } - - return cfg -} From 931526aa5ef591d2ba13614d468c8e758c00593c Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Sat, 23 Dec 2023 22:09:28 +0300 Subject: [PATCH 13/21] apply reviews and add testcases --- docs/api/middleware/probechecker.md | 83 +++++++++++------- middleware/probechecker/config.go | 9 ++ middleware/probechecker/probechecker.go | 40 +++------ middleware/probechecker/probechecker_test.go | 89 ++++++++++++++++++++ 4 files changed, 165 insertions(+), 56 deletions(-) create mode 100644 middleware/probechecker/probechecker_test.go diff --git a/docs/api/middleware/probechecker.md b/docs/api/middleware/probechecker.md index 510ba46d31..99d520fa45 100644 --- a/docs/api/middleware/probechecker.md +++ b/docs/api/middleware/probechecker.md @@ -34,42 +34,65 @@ app.Use(probechecker.New()) // Initialize with custom config app.Use( - probechecker.New( - IsLive: func (c *fiber.Ctx) bool { - return true - }, - IsLiveEndpoint: "/livez", - IsReady: func (c *fiber.Ctx) bool { - return serviceA.Ready() && serviceB.Ready() && ... - } - IsReadyEndpoint: "/readyz", - ) -) + probechecker.New(Config{ + IsLive: func(c *fiber.Ctx) bool { + return true + }, + IsLiveEndpoint: "/live", + IsReady: func (c *fiber.Ctx) bool { + return serviceA.Ready() && serviceB.Ready() && ... + }, + IsReadyEndpoint: "/ready", +})) ``` ## Config ```go +// Config is the config struct for the probechecker middleware type Config struct { - // Config for liveness probe of the container engine being used - // - // Optional. Default: func(c *Ctx) bool { return true } - IsLive ProbeChecker - - // HTTP endpoint of the liveness probe - // - // Optional. Default: /livez - IsLiveEndpoint string - - // Config for readiness probe of the container engine being used - // - // Optional. Default: nil - IsReady ProbeChecker - - // HTTP endpoint of the readiness probe - // - // Optional. Default: /readyz - IsReadyEndpoint string + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Config for liveness probe of the container engine being used + // + // Optional. Default: func(c *Ctx) bool { return true } + IsLive ProbeChecker + + // HTTP endpoint of the liveness probe + // + // Optional. Default: /livez + IsLiveEndpoint string + + // Config for readiness probe of the container engine being used + // + // Optional. Default: nil + IsReady ProbeChecker + + // HTTP endpoint of the readiness probe + // + // Optional. Default: /readyz + IsReadyEndpoint string +} +``` + +## Default Config + +```go +const ( + DefaultLivenessEndpoint = "/livez" + DefaultReadinessEndpoint = "/readyz" +) + +func defaultLiveFunc(*fiber.Ctx) bool { return true } + +// ConfigDefault is the default config +var ConfigDefault = Config{ + IsLive: defaultLiveFunc, + IsLiveEndpoint: DefaultLivenessEndpoint, + IsReadyEndpoint: DefaultReadinessEndpoint, } ``` \ No newline at end of file diff --git a/middleware/probechecker/config.go b/middleware/probechecker/config.go index 42b68b4022..356fa60c49 100644 --- a/middleware/probechecker/config.go +++ b/middleware/probechecker/config.go @@ -4,6 +4,11 @@ import "github.com/gofiber/fiber/v2" // Config is the config struct for the probechecker middleware type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + // Config for liveness probe of the container engine being used // // Optional. Default: func(c *Ctx) bool { return true } @@ -47,6 +52,10 @@ func defaultConfig(config ...Config) Config { cfg := config[0] + if cfg.Next == nil { + cfg.Next = ConfigDefault.Next + } + if cfg.IsLive == nil { cfg.IsLive = defaultLiveFunc } diff --git a/middleware/probechecker/probechecker.go b/middleware/probechecker/probechecker.go index 1bd90bb383..066e79a173 100644 --- a/middleware/probechecker/probechecker.go +++ b/middleware/probechecker/probechecker.go @@ -1,8 +1,6 @@ package probechecker import ( - "fmt" - "github.com/gofiber/fiber/v2" ) @@ -26,37 +24,27 @@ func probeCheckerHandler(checker ProbeChecker) fiber.Handler { } } -func checkRoute(path string, config *Config) string { - switch path { - case DefaultLivenessEndpoint, config.IsLiveEndpoint: - return "liveness" - case DefaultReadinessEndpoint, config.IsReadyEndpoint: - return "readiness" - default: - return "" - } -} - func New(config ...Config) fiber.Handler { cfg := defaultConfig(config...) - checkers := map[string]fiber.Handler{ - "liveness": probeCheckerHandler(cfg.IsLive), - "readiness": probeCheckerHandler(cfg.IsReady), - } + isLiveHandler := probeCheckerHandler(cfg.IsLive) + isReadyHandler := probeCheckerHandler(cfg.IsReady) return func(c *fiber.Ctx) error { - route := c.Route() - routeType := checkRoute(route.Path, &cfg) - - if routeType != "" || route.Method != fiber.MethodGet { - handler, ok := checkers[routeType] + // Don't execute middleware if Next returns true + if cfg.Next != nil && cfg.Next(c) { + return c.Next() + } - if !ok { - return fmt.Errorf("routeType of %s not found in checkers", routeType) - } + if c.Method() != fiber.MethodGet { + return c.Next() + } - return handler(c) + switch c.Path() { + case cfg.IsReadyEndpoint: + return isReadyHandler(c) + case cfg.IsLiveEndpoint: + return isLiveHandler(c) } return c.Next() diff --git a/middleware/probechecker/probechecker_test.go b/middleware/probechecker/probechecker_test.go new file mode 100644 index 0000000000..aaae3fa64c --- /dev/null +++ b/middleware/probechecker/probechecker_test.go @@ -0,0 +1,89 @@ +package probechecker + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" + "net/http/httptest" + "testing" + "time" +) + +func Test_ProbeChecker(t *testing.T) { + t.Parallel() + + app := fiber.New() + + c1 := make(chan struct{}, 1) + go func() { + time.Sleep(1 * time.Second) + c1 <- struct{}{} + }() + + app.Use(New(Config{ + IsLive: func(c *fiber.Ctx) bool { + return true + }, + IsLiveEndpoint: "/live", + IsReady: func(c *fiber.Ctx) bool { + select { + case <-c1: + return true + default: + return false + } + }, + IsReadyEndpoint: "/ready", + })) + + // Live should return 200 with GET request + req, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/live", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, req.StatusCode) + + // Live should return 404 with POST request + req, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/live", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusNotFound, req.StatusCode) + + // Ready should return 503 with GET request before the channel is closed + req, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/ready", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusServiceUnavailable, req.StatusCode) + + time.Sleep(1 * time.Second) + + // Ready should return 200 with GET request after the channel is closed + req, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/ready", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, req.StatusCode) +} + +func Test_ProbeChecker_Next(t *testing.T) { + t.Parallel() + + app := fiber.New() + + app.Use(New(Config{ + Next: func(c *fiber.Ctx) bool { + return true + }, + })) + + req, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/livez", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusNotFound, req.StatusCode) +} + +func Test_ProbeChecker_NilChecker(t *testing.T) { + t.Parallel() + + app := fiber.New() + + app.Use(New(Config{ + IsReady: nil, + })) + + req, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/readyz", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusNotFound, req.StatusCode) +} From 508bddbb21319677506b7dccddf435d795a3680f Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Sat, 23 Dec 2023 22:13:18 +0300 Subject: [PATCH 14/21] add benchmark --- middleware/probechecker/probechecker_test.go | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/middleware/probechecker/probechecker_test.go b/middleware/probechecker/probechecker_test.go index aaae3fa64c..8785f47f44 100644 --- a/middleware/probechecker/probechecker_test.go +++ b/middleware/probechecker/probechecker_test.go @@ -3,6 +3,7 @@ package probechecker import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" "net/http/httptest" "testing" "time" @@ -87,3 +88,23 @@ func Test_ProbeChecker_NilChecker(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, req.StatusCode) } + +func Benchmark_ProbeChecker(b *testing.B) { + app := fiber.New() + + app.Use(New()) + + h := app.Handler() + fctx := &fasthttp.RequestCtx{} + fctx.Request.Header.SetMethod(fiber.MethodGet) + fctx.Request.SetRequestURI("/livez") + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + h(fctx) + } + + utils.AssertEqual(b, fiber.StatusOK, fctx.Response.Header.StatusCode()) +} From 2041679075af6addb4789e320e274edbd1f46155 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Sat, 23 Dec 2023 22:15:41 +0300 Subject: [PATCH 15/21] cleanup --- app.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/app.go b/app.go index d9f5c6fa1e..71b874b7de 100644 --- a/app.go +++ b/app.go @@ -453,8 +453,6 @@ const ( DefaultReadBufferSize = 4096 DefaultWriteBufferSize = 4096 DefaultCompressedFileSuffix = ".fiber.gz" - DefaultLivenessEndpoint = "/livez" - DefaultReadinessEndpoint = "/readyz" ) // HTTP methods enabled by default From e987a8e07e2c3405f8f3d81c0fbf3274781af3ae Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Sun, 24 Dec 2023 13:32:15 +0300 Subject: [PATCH 16/21] rename middleware --- .../middleware/{probechecker.md => healthcheck.md} | 12 ++++++------ middleware/{probechecker => healthcheck}/config.go | 10 +++++----- .../probechecker.go => healthcheck/healthcheck.go} | 14 +++++++------- .../healthcheck_test.go} | 10 +++++----- 4 files changed, 23 insertions(+), 23 deletions(-) rename docs/api/middleware/{probechecker.md => healthcheck.md} (92%) rename middleware/{probechecker => healthcheck}/config.go (87%) rename middleware/{probechecker/probechecker.go => healthcheck/healthcheck.go} (67%) rename middleware/{probechecker/probechecker_test.go => healthcheck/healthcheck_test.go} (92%) diff --git a/docs/api/middleware/probechecker.md b/docs/api/middleware/healthcheck.md similarity index 92% rename from docs/api/middleware/probechecker.md rename to docs/api/middleware/healthcheck.md index 99d520fa45..7d10d34e12 100644 --- a/docs/api/middleware/probechecker.md +++ b/docs/api/middleware/healthcheck.md @@ -1,6 +1,6 @@ --- -id: probecheker -title: probecheker +id: healthcheck +title: healthcheck --- Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/fiber) that provides two endpoints for checking the health and ready state of any HTTP application. @@ -22,7 +22,7 @@ Import the middleware package that is part of the Fiber web framework ```go import ( "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/probechecker" + "github.com/gofiber/fiber/v2/middleware/healthcheck" ) ``` @@ -50,7 +50,7 @@ app.Use( ## Config ```go -// Config is the config struct for the probechecker middleware +// Config is the config struct for the healthcheck middleware type Config struct { // Next defines a function to skip this middleware when returned true. // @@ -60,7 +60,7 @@ type Config struct { // Config for liveness probe of the container engine being used // // Optional. Default: func(c *Ctx) bool { return true } - IsLive ProbeChecker + IsLive HealthChecker // HTTP endpoint of the liveness probe // @@ -70,7 +70,7 @@ type Config struct { // Config for readiness probe of the container engine being used // // Optional. Default: nil - IsReady ProbeChecker + IsReady HealthChecker // HTTP endpoint of the readiness probe // diff --git a/middleware/probechecker/config.go b/middleware/healthcheck/config.go similarity index 87% rename from middleware/probechecker/config.go rename to middleware/healthcheck/config.go index 356fa60c49..6e2639929a 100644 --- a/middleware/probechecker/config.go +++ b/middleware/healthcheck/config.go @@ -1,8 +1,8 @@ -package probechecker +package healthcheck import "github.com/gofiber/fiber/v2" -// Config is the config struct for the probechecker middleware +// Config is the config struct for the healthcheck middleware type Config struct { // Next defines a function to skip this middleware when returned true. // @@ -12,7 +12,7 @@ type Config struct { // Config for liveness probe of the container engine being used // // Optional. Default: func(c *Ctx) bool { return true } - IsLive ProbeChecker + IsLive HealthChecker // HTTP endpoint of the liveness probe // @@ -22,7 +22,7 @@ type Config struct { // Config for readiness probe of the container engine being used // // Optional. Default: nil - IsReady ProbeChecker + IsReady HealthChecker // HTTP endpoint of the readiness probe // @@ -44,7 +44,7 @@ var ConfigDefault = Config{ IsReadyEndpoint: DefaultReadinessEndpoint, } -// defaultConfig returns a default config for the probechecker middleware. +// defaultConfig returns a default config for the healthcheck middleware. func defaultConfig(config ...Config) Config { if len(config) < 1 { return ConfigDefault diff --git a/middleware/probechecker/probechecker.go b/middleware/healthcheck/healthcheck.go similarity index 67% rename from middleware/probechecker/probechecker.go rename to middleware/healthcheck/healthcheck.go index 066e79a173..4f8effff52 100644 --- a/middleware/probechecker/probechecker.go +++ b/middleware/healthcheck/healthcheck.go @@ -1,16 +1,16 @@ -package probechecker +package healthcheck import ( "github.com/gofiber/fiber/v2" ) -// ProbeChecker defines a function to check liveness or readiness of the application -type ProbeChecker func(*fiber.Ctx) bool +// HealthChecker defines a function to check liveness or readiness of the application +type HealthChecker func(*fiber.Ctx) bool // ProbeCheckerHandler defines a function that returns a ProbeChecker -type ProbeCheckerHandler func(ProbeChecker) fiber.Handler +type HealthCheckerHandler func(HealthChecker) fiber.Handler -func probeCheckerHandler(checker ProbeChecker) fiber.Handler { +func healthCheckerHandler(checker HealthChecker) fiber.Handler { return func(c *fiber.Ctx) error { if checker == nil { return c.Next() @@ -27,8 +27,8 @@ func probeCheckerHandler(checker ProbeChecker) fiber.Handler { func New(config ...Config) fiber.Handler { cfg := defaultConfig(config...) - isLiveHandler := probeCheckerHandler(cfg.IsLive) - isReadyHandler := probeCheckerHandler(cfg.IsReady) + isLiveHandler := healthCheckerHandler(cfg.IsLive) + isReadyHandler := healthCheckerHandler(cfg.IsReady) return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true diff --git a/middleware/probechecker/probechecker_test.go b/middleware/healthcheck/healthcheck_test.go similarity index 92% rename from middleware/probechecker/probechecker_test.go rename to middleware/healthcheck/healthcheck_test.go index 8785f47f44..5efc2a37cb 100644 --- a/middleware/probechecker/probechecker_test.go +++ b/middleware/healthcheck/healthcheck_test.go @@ -1,4 +1,4 @@ -package probechecker +package healthcheck import ( "github.com/gofiber/fiber/v2" @@ -9,7 +9,7 @@ import ( "time" ) -func Test_ProbeChecker(t *testing.T) { +func Test_HealthCheck(t *testing.T) { t.Parallel() app := fiber.New() @@ -59,7 +59,7 @@ func Test_ProbeChecker(t *testing.T) { utils.AssertEqual(t, fiber.StatusOK, req.StatusCode) } -func Test_ProbeChecker_Next(t *testing.T) { +func Test_HealthCheck_Next(t *testing.T) { t.Parallel() app := fiber.New() @@ -75,7 +75,7 @@ func Test_ProbeChecker_Next(t *testing.T) { utils.AssertEqual(t, fiber.StatusNotFound, req.StatusCode) } -func Test_ProbeChecker_NilChecker(t *testing.T) { +func Test_HealthCheck_NilChecker(t *testing.T) { t.Parallel() app := fiber.New() @@ -89,7 +89,7 @@ func Test_ProbeChecker_NilChecker(t *testing.T) { utils.AssertEqual(t, fiber.StatusNotFound, req.StatusCode) } -func Benchmark_ProbeChecker(b *testing.B) { +func Benchmark_HealthCheck(b *testing.B) { app := fiber.New() app.Use(New()) From c33626b5e7260b589ef21161f3acaac898e2d752 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Sun, 24 Dec 2023 13:46:02 +0300 Subject: [PATCH 17/21] fix linter --- middleware/healthcheck/config.go | 4 +++- middleware/healthcheck/healthcheck_test.go | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/middleware/healthcheck/config.go b/middleware/healthcheck/config.go index 6e2639929a..0c6acceb1e 100644 --- a/middleware/healthcheck/config.go +++ b/middleware/healthcheck/config.go @@ -1,6 +1,8 @@ package healthcheck -import "github.com/gofiber/fiber/v2" +import ( + "github.com/gofiber/fiber/v2" +) // Config is the config struct for the healthcheck middleware type Config struct { diff --git a/middleware/healthcheck/healthcheck_test.go b/middleware/healthcheck/healthcheck_test.go index 5efc2a37cb..7bfec31c24 100644 --- a/middleware/healthcheck/healthcheck_test.go +++ b/middleware/healthcheck/healthcheck_test.go @@ -1,12 +1,13 @@ package healthcheck import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/utils" - "github.com/valyala/fasthttp" "net/http/httptest" "testing" "time" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) func Test_HealthCheck(t *testing.T) { From 013e362a361aeb41a1ab24569027678ed3f9e3e4 Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Wed, 3 Jan 2024 00:30:03 -0500 Subject: [PATCH 18/21] Update docs and config values --- docs/api/middleware/healthcheck.md | 106 ++++++++++----------- middleware/healthcheck/config.go | 30 +++--- middleware/healthcheck/healthcheck.go | 4 +- middleware/healthcheck/healthcheck_test.go | 4 +- 4 files changed, 71 insertions(+), 73 deletions(-) diff --git a/docs/api/middleware/healthcheck.md b/docs/api/middleware/healthcheck.md index 7d10d34e12..dadb56a938 100644 --- a/docs/api/middleware/healthcheck.md +++ b/docs/api/middleware/healthcheck.md @@ -3,96 +3,88 @@ id: healthcheck title: healthcheck --- -Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/fiber) that provides two endpoints for checking the health and ready state of any HTTP application. +Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/fiber) that provides two endpoints for checking the health and ready state of HTTP applications. -The endpoint values default to `/livez` for liveness and `/readyz` for readiness. Both functions are optional, the liveness endpoint will return `true` right when the server is up and running but the readiness endpoint will not answer any requests if an `IsReady` function isn't provided. +## Overview -The HTTP status returned to the containerized environment are: 200 OK if the checker function returns true and 503 Service Unavailable if the checker function returns false. +- **Liveness Probe**: Checks if the server is up and running. + - **Default Endpoint**: `/livez` + - **Behavior**: Returns `true` immediately when the server is operational. -## Signatures +- **Readiness Probe**: Assesses if the application is ready to handle requests. + - **Default Endpoint**: `/readyz` + - **Behavior**: Requires an `IsReady` function implementation. Without this function, the endpoint does not respond. -```go -func New() fiber.Handler -``` +- **HTTP Status Codes**: + - `200 OK`: Returned when the checker function evaluates to `true`. + - `503 Service Unavailable`: Returned when the checker function evaluates to `false`. -## Examples +## Usage -Import the middleware package that is part of the Fiber web framework +### Installation +First, import the healthcheck middleware package from the Fiber web framework: ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/healthcheck" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/healthcheck" ) ``` -After you initiate your Fiber app, you can use the following possibilities: +### Implementation + +After initializing your Fiber app, configure the middleware as follows: +**Default Configuration**: ```go -// Initializing with default config -app.Use(probechecker.New()) - -// Initialize with custom config -app.Use( - probechecker.New(Config{ - IsLive: func(c *fiber.Ctx) bool { - return true - }, - IsLiveEndpoint: "/live", - IsReady: func (c *fiber.Ctx) bool { - return serviceA.Ready() && serviceB.Ready() && ... - }, - IsReadyEndpoint: "/ready", -})) +app.Use(healthcheck.New()) +``` +**Custom Configuration**: +```go +app.Use(healthcheck.New(healthcheck.Config{ + IsLive: func(c *fiber.Ctx) bool { + return true + }, + LivenessEndpoint: "/live", + IsReady: func(c *fiber.Ctx) bool { + return serviceA.Ready() && serviceB.Ready() && ... + }, + ReadinessEndpoint: "/ready", +})) ``` -## Config +## Configuration Options +The `Config` struct offers the following customization options: ```go -// Config is the config struct for the healthcheck middleware type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil + // Function to skip middleware. Optional. Default: nil Next func(c *fiber.Ctx) bool - // Config for liveness probe of the container engine being used - // - // Optional. Default: func(c *Ctx) bool { return true } + // Liveness probe configuration. Optional. Default: Always true IsLive HealthChecker - // HTTP endpoint of the liveness probe - // - // Optional. Default: /livez - IsLiveEndpoint string + // Liveness probe HTTP endpoint. Optional. Default: "/livez" + LivenessEndpoint string - // Config for readiness probe of the container engine being used - // - // Optional. Default: nil + // Readiness probe configuration. Optional. Default: nil IsReady HealthChecker - // HTTP endpoint of the readiness probe - // - // Optional. Default: /readyz - IsReadyEndpoint string + // Readiness probe HTTP endpoint. Optional. Default: "/readyz" + ReadinessEndpoint string } ``` -## Default Config +## Default Configuration +The default configuration is defined as follows: ```go -const ( - DefaultLivenessEndpoint = "/livez" - DefaultReadinessEndpoint = "/readyz" -) - -func defaultLiveFunc(*fiber.Ctx) bool { return true } +func defaultLivenessFunc(*fiber.Ctx) bool { return true } -// ConfigDefault is the default config var ConfigDefault = Config{ - IsLive: defaultLiveFunc, - IsLiveEndpoint: DefaultLivenessEndpoint, - IsReadyEndpoint: DefaultReadinessEndpoint, + IsLive: defaultLivenessFunc, + LivenessEndpoint: "/livez", + ReadinessEndpoint: "/readyz", } ``` \ No newline at end of file diff --git a/middleware/healthcheck/config.go b/middleware/healthcheck/config.go index 0c6acceb1e..085e1362fd 100644 --- a/middleware/healthcheck/config.go +++ b/middleware/healthcheck/config.go @@ -19,17 +19,17 @@ type Config struct { // HTTP endpoint of the liveness probe // // Optional. Default: /livez - IsLiveEndpoint string + LivenessEndpoint string // Config for readiness probe of the container engine being used // - // Optional. Default: nil + // Optional. Default: func(c *Ctx) bool { return false } IsReady HealthChecker // HTTP endpoint of the readiness probe // // Optional. Default: /readyz - IsReadyEndpoint string + ReadinessEndpoint string } const ( @@ -37,13 +37,15 @@ const ( DefaultReadinessEndpoint = "/readyz" ) -func defaultLiveFunc(*fiber.Ctx) bool { return true } +func defaultLivenessFunc(*fiber.Ctx) bool { return true } +func defaultReadinessFunc(*fiber.Ctx) bool { return false } // ConfigDefault is the default config var ConfigDefault = Config{ - IsLive: defaultLiveFunc, - IsLiveEndpoint: DefaultLivenessEndpoint, - IsReadyEndpoint: DefaultReadinessEndpoint, + IsLive: defaultLivenessFunc, + IsReady: defaultReadinessFunc, + LivenessEndpoint: DefaultLivenessEndpoint, + ReadinessEndpoint: DefaultReadinessEndpoint, } // defaultConfig returns a default config for the healthcheck middleware. @@ -59,15 +61,19 @@ func defaultConfig(config ...Config) Config { } if cfg.IsLive == nil { - cfg.IsLive = defaultLiveFunc + cfg.IsLive = defaultLivenessFunc + } + + if cfg.IsReady == nil { + cfg.IsReady = defaultReadinessFunc } - if cfg.IsLiveEndpoint == "" { - cfg.IsLiveEndpoint = DefaultLivenessEndpoint + if cfg.LivenessEndpoint == "" { + cfg.LivenessEndpoint = DefaultLivenessEndpoint } - if cfg.IsReadyEndpoint == "" { - cfg.IsReadyEndpoint = DefaultReadinessEndpoint + if cfg.ReadinessEndpoint == "" { + cfg.ReadinessEndpoint = DefaultReadinessEndpoint } return cfg diff --git a/middleware/healthcheck/healthcheck.go b/middleware/healthcheck/healthcheck.go index 4f8effff52..c1d8d81deb 100644 --- a/middleware/healthcheck/healthcheck.go +++ b/middleware/healthcheck/healthcheck.go @@ -41,9 +41,9 @@ func New(config ...Config) fiber.Handler { } switch c.Path() { - case cfg.IsReadyEndpoint: + case cfg.ReadinessEndpoint: return isReadyHandler(c) - case cfg.IsLiveEndpoint: + case cfg.LivenessEndpoint: return isLiveHandler(c) } diff --git a/middleware/healthcheck/healthcheck_test.go b/middleware/healthcheck/healthcheck_test.go index 7bfec31c24..70f0b4aa85 100644 --- a/middleware/healthcheck/healthcheck_test.go +++ b/middleware/healthcheck/healthcheck_test.go @@ -25,7 +25,7 @@ func Test_HealthCheck(t *testing.T) { IsLive: func(c *fiber.Ctx) bool { return true }, - IsLiveEndpoint: "/live", + LivenessEndpoint: "/live", IsReady: func(c *fiber.Ctx) bool { select { case <-c1: @@ -34,7 +34,7 @@ func Test_HealthCheck(t *testing.T) { return false } }, - IsReadyEndpoint: "/ready", + ReadinessEndpoint: "/ready", })) // Live should return 200 with GET request From 5c08790b8d9a0a6756449079047a4e921ce703df Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Wed, 3 Jan 2024 00:34:34 -0500 Subject: [PATCH 19/21] Revert change to IsReady --- middleware/healthcheck/config.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/middleware/healthcheck/config.go b/middleware/healthcheck/config.go index 085e1362fd..42772fc9ae 100644 --- a/middleware/healthcheck/config.go +++ b/middleware/healthcheck/config.go @@ -38,12 +38,10 @@ const ( ) func defaultLivenessFunc(*fiber.Ctx) bool { return true } -func defaultReadinessFunc(*fiber.Ctx) bool { return false } // ConfigDefault is the default config var ConfigDefault = Config{ IsLive: defaultLivenessFunc, - IsReady: defaultReadinessFunc, LivenessEndpoint: DefaultLivenessEndpoint, ReadinessEndpoint: DefaultReadinessEndpoint, } @@ -64,10 +62,6 @@ func defaultConfig(config ...Config) Config { cfg.IsLive = defaultLivenessFunc } - if cfg.IsReady == nil { - cfg.IsReady = defaultReadinessFunc - } - if cfg.LivenessEndpoint == "" { cfg.LivenessEndpoint = DefaultLivenessEndpoint } From 92e1cac60b213ee9feb62e53ca0b0f7869a2987e Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez Date: Wed, 3 Jan 2024 09:02:43 -0500 Subject: [PATCH 20/21] Updates based on code review --- docs/api/middleware/healthcheck.md | 68 +++++++++++++++------- middleware/healthcheck/config.go | 44 +++++++++----- middleware/healthcheck/healthcheck.go | 4 +- middleware/healthcheck/healthcheck_test.go | 40 +++++++------ 4 files changed, 99 insertions(+), 57 deletions(-) diff --git a/docs/api/middleware/healthcheck.md b/docs/api/middleware/healthcheck.md index dadb56a938..8863b5bfbe 100644 --- a/docs/api/middleware/healthcheck.md +++ b/docs/api/middleware/healthcheck.md @@ -9,11 +9,11 @@ Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/ - **Liveness Probe**: Checks if the server is up and running. - **Default Endpoint**: `/livez` - - **Behavior**: Returns `true` immediately when the server is operational. + - **Behavior**: By default returns `true` immediately when the server is operational. - **Readiness Probe**: Assesses if the application is ready to handle requests. - **Default Endpoint**: `/readyz` - - **Behavior**: Requires an `IsReady` function implementation. Without this function, the endpoint does not respond. + - **Behavior**: By default returns `true` immediately when the server is operational. - **HTTP Status Codes**: - `200 OK`: Returned when the checker function evaluates to `true`. @@ -43,11 +43,11 @@ app.Use(healthcheck.New()) **Custom Configuration**: ```go app.Use(healthcheck.New(healthcheck.Config{ - IsLive: func(c *fiber.Ctx) bool { + LivenessProbe: func(c *fiber.Ctx) bool { return true }, LivenessEndpoint: "/live", - IsReady: func(c *fiber.Ctx) bool { + ReadinessProbe: func(c *fiber.Ctx) bool { return serviceA.Ready() && serviceB.Ready() && ... }, ReadinessEndpoint: "/ready", @@ -59,20 +59,35 @@ app.Use(healthcheck.New(healthcheck.Config{ The `Config` struct offers the following customization options: ```go type Config struct { - // Function to skip middleware. Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Liveness probe configuration. Optional. Default: Always true - IsLive HealthChecker - - // Liveness probe HTTP endpoint. Optional. Default: "/livez" - LivenessEndpoint string - - // Readiness probe configuration. Optional. Default: nil - IsReady HealthChecker - - // Readiness probe HTTP endpoint. Optional. Default: "/readyz" - ReadinessEndpoint string + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Function to be used for checking the liveness of the application. + // Returns true if the application is running and false if it is not. + // The liveness probe is typically used to indicate if the application is in a state where + // it can handle requests (e.g., the server is up and running). + // + // Optional. Default: func(c *fiber.Ctx) bool { return true } + LivenessProbe HealthChecker + + // HTTP endpoint at which the liveness probe will be available. + // + // Optional. Default: "/livez" + LivenessEndpoint string + + // Function to be used for checking the readiness of the application. + // Returns true if the application is ready to process requests and false otherwise. + // The readiness probe typically checks if all necessary services, databases, and other dependencies + // are available for the application to function correctly. + // + // Optional. Default: func(c *fiber.Ctx) bool { return true } + ReadinessProbe HealthChecker + + // HTTP endpoint at which the readiness probe will be available. + // Optional. Default: "/readyz" + ReadinessEndpoint string } ``` @@ -80,11 +95,20 @@ type Config struct { The default configuration is defined as follows: ```go -func defaultLivenessFunc(*fiber.Ctx) bool { return true } +const ( + DefaultLivenessEndpoint = "/livez" + DefaultReadinessEndpoint = "/readyz" +) + +func defaultLivenessProbe(*fiber.Ctx) bool { return true } + +func defaultReadinessProbe(*fiber.Ctx) bool { return true } +// ConfigDefault is the default config var ConfigDefault = Config{ - IsLive: defaultLivenessFunc, - LivenessEndpoint: "/livez", - ReadinessEndpoint: "/readyz", + LivenessProbe: defaultLivenessProbe, + ReadinessProbe: defaultReadinessProbe, + LivenessEndpoint: DefaultLivenessEndpoint, + ReadinessEndpoint: DefaultReadinessEndpoint, } ``` \ No newline at end of file diff --git a/middleware/healthcheck/config.go b/middleware/healthcheck/config.go index 42772fc9ae..66c6e46a10 100644 --- a/middleware/healthcheck/config.go +++ b/middleware/healthcheck/config.go @@ -4,31 +4,36 @@ import ( "github.com/gofiber/fiber/v2" ) -// Config is the config struct for the healthcheck middleware +// Config defines the configuration options for the healthcheck middleware. type Config struct { // Next defines a function to skip this middleware when returned true. // // Optional. Default: nil Next func(c *fiber.Ctx) bool - // Config for liveness probe of the container engine being used + // Function to be used for checking the liveness of the application. + // Returns true if the application is running and false if it is not. + // The liveness probe is typically used to indicate if the application is in a state where + // it can handle requests (e.g., the server is up and running). // - // Optional. Default: func(c *Ctx) bool { return true } - IsLive HealthChecker + // Optional. Default: func(c *fiber.Ctx) bool { return true } + LivenessProbe HealthChecker - // HTTP endpoint of the liveness probe + // HTTP endpoint at which the liveness probe will be available. // - // Optional. Default: /livez + // Optional. Default: "/livez" LivenessEndpoint string - // Config for readiness probe of the container engine being used + // Function to be used for checking the readiness of the application. + // Returns true if the application is ready to process requests and false otherwise. + // The readiness probe typically checks if all necessary services, databases, and other dependencies + // are available for the application to function correctly. // - // Optional. Default: func(c *Ctx) bool { return false } - IsReady HealthChecker + // Optional. Default: func(c *fiber.Ctx) bool { return true } + ReadinessProbe HealthChecker - // HTTP endpoint of the readiness probe - // - // Optional. Default: /readyz + // HTTP endpoint at which the readiness probe will be available. + // Optional. Default: "/readyz" ReadinessEndpoint string } @@ -37,11 +42,14 @@ const ( DefaultReadinessEndpoint = "/readyz" ) -func defaultLivenessFunc(*fiber.Ctx) bool { return true } +func defaultLivenessProbe(*fiber.Ctx) bool { return true } + +func defaultReadinessProbe(*fiber.Ctx) bool { return true } // ConfigDefault is the default config var ConfigDefault = Config{ - IsLive: defaultLivenessFunc, + LivenessProbe: defaultLivenessProbe, + ReadinessProbe: defaultReadinessProbe, LivenessEndpoint: DefaultLivenessEndpoint, ReadinessEndpoint: DefaultReadinessEndpoint, } @@ -58,8 +66,12 @@ func defaultConfig(config ...Config) Config { cfg.Next = ConfigDefault.Next } - if cfg.IsLive == nil { - cfg.IsLive = defaultLivenessFunc + if cfg.LivenessProbe == nil { + cfg.LivenessProbe = defaultLivenessProbe + } + + if cfg.ReadinessProbe == nil { + cfg.ReadinessProbe = defaultReadinessProbe } if cfg.LivenessEndpoint == "" { diff --git a/middleware/healthcheck/healthcheck.go b/middleware/healthcheck/healthcheck.go index c1d8d81deb..14ff33430c 100644 --- a/middleware/healthcheck/healthcheck.go +++ b/middleware/healthcheck/healthcheck.go @@ -27,8 +27,8 @@ func healthCheckerHandler(checker HealthChecker) fiber.Handler { func New(config ...Config) fiber.Handler { cfg := defaultConfig(config...) - isLiveHandler := healthCheckerHandler(cfg.IsLive) - isReadyHandler := healthCheckerHandler(cfg.IsReady) + isLiveHandler := healthCheckerHandler(cfg.LivenessProbe) + isReadyHandler := healthCheckerHandler(cfg.ReadinessProbe) return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true diff --git a/middleware/healthcheck/healthcheck_test.go b/middleware/healthcheck/healthcheck_test.go index 70f0b4aa85..df0165f158 100644 --- a/middleware/healthcheck/healthcheck_test.go +++ b/middleware/healthcheck/healthcheck_test.go @@ -10,7 +10,22 @@ import ( "github.com/valyala/fasthttp" ) -func Test_HealthCheck(t *testing.T) { +func Test_HealthCheck_Default(t *testing.T) { + t.Parallel() + + app := fiber.New() + app.Use(New()) + + req, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/readyz", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, req.StatusCode) + + req, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/livez", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, req.StatusCode) +} + +func Test_HealthCheck_Custom(t *testing.T) { t.Parallel() app := fiber.New() @@ -22,11 +37,11 @@ func Test_HealthCheck(t *testing.T) { }() app.Use(New(Config{ - IsLive: func(c *fiber.Ctx) bool { + LivenessProbe: func(c *fiber.Ctx) bool { return true }, LivenessEndpoint: "/live", - IsReady: func(c *fiber.Ctx) bool { + ReadinessProbe: func(c *fiber.Ctx) bool { select { case <-c1: return true @@ -47,6 +62,11 @@ func Test_HealthCheck(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, req.StatusCode) + // Ready should return 404 with POST request + req, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/ready", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusNotFound, req.StatusCode) + // Ready should return 503 with GET request before the channel is closed req, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/ready", nil)) utils.AssertEqual(t, nil, err) @@ -76,20 +96,6 @@ func Test_HealthCheck_Next(t *testing.T) { utils.AssertEqual(t, fiber.StatusNotFound, req.StatusCode) } -func Test_HealthCheck_NilChecker(t *testing.T) { - t.Parallel() - - app := fiber.New() - - app.Use(New(Config{ - IsReady: nil, - })) - - req, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/readyz", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, req.StatusCode) -} - func Benchmark_HealthCheck(b *testing.B) { app := fiber.New() From 87b182273ac21f29191ac3418e27da9e19100cc2 Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez Date: Wed, 3 Jan 2024 09:13:15 -0500 Subject: [PATCH 21/21] Update docs to match other middlewares --- docs/api/middleware/healthcheck.md | 53 +++++++++++++----------------- middleware/healthcheck/config.go | 14 ++++---- 2 files changed, 28 insertions(+), 39 deletions(-) diff --git a/docs/api/middleware/healthcheck.md b/docs/api/middleware/healthcheck.md index 8863b5bfbe..3f2bb6c5fb 100644 --- a/docs/api/middleware/healthcheck.md +++ b/docs/api/middleware/healthcheck.md @@ -3,7 +3,7 @@ id: healthcheck title: healthcheck --- -Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/fiber) that provides two endpoints for checking the health and ready state of HTTP applications. +Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/fiber) that provides two endpoints for checking the liveness and readiness state of HTTP applications. ## Overview @@ -19,11 +19,15 @@ Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/ - `200 OK`: Returned when the checker function evaluates to `true`. - `503 Service Unavailable`: Returned when the checker function evaluates to `false`. -## Usage +## Signatures -### Installation +```go +func New(config Config) fiber.Handler +``` -First, import the healthcheck middleware package from the Fiber web framework: +## Examples + +Import the middleware package that is part of the Fiber web framework ```go import ( "github.com/gofiber/fiber/v2" @@ -31,17 +35,13 @@ import ( ) ``` -### Implementation - -After initializing your Fiber app, configure the middleware as follows: +After you initiate your Fiber app, you can use the following possibilities: -**Default Configuration**: ```go +// Provide a minimal config app.Use(healthcheck.New()) -``` -**Custom Configuration**: -```go +// Or extend your config for customization app.Use(healthcheck.New(healthcheck.Config{ LivenessProbe: func(c *fiber.Ctx) bool { return true @@ -54,9 +54,8 @@ app.Use(healthcheck.New(healthcheck.Config{ })) ``` -## Configuration Options +## Config -The `Config` struct offers the following customization options: ```go type Config struct { // Next defines a function to skip this middleware when returned true. @@ -64,10 +63,9 @@ type Config struct { // Optional. Default: nil Next func(c *fiber.Ctx) bool - // Function to be used for checking the liveness of the application. - // Returns true if the application is running and false if it is not. - // The liveness probe is typically used to indicate if the application is in a state where - // it can handle requests (e.g., the server is up and running). + // Function used for checking the liveness of the application. Returns true if the application + // is running and false if it is not. The liveness probe is typically used to indicate if + // the application is in a state where it can handle requests (e.g., the server is up and running). // // Optional. Default: func(c *fiber.Ctx) bool { return true } LivenessProbe HealthChecker @@ -77,10 +75,9 @@ type Config struct { // Optional. Default: "/livez" LivenessEndpoint string - // Function to be used for checking the readiness of the application. - // Returns true if the application is ready to process requests and false otherwise. - // The readiness probe typically checks if all necessary services, databases, and other dependencies - // are available for the application to function correctly. + // Function used for checking the readiness of the application. Returns true if the application + // is ready to process requests and false otherwise. The readiness probe typically checks if all necessary + // services, databases, and other dependencies are available for the application to function correctly. // // Optional. Default: func(c *fiber.Ctx) bool { return true } ReadinessProbe HealthChecker @@ -91,24 +88,18 @@ type Config struct { } ``` -## Default Configuration +## Default Config -The default configuration is defined as follows: +The default configuration used by this middleware is defined as follows: ```go -const ( - DefaultLivenessEndpoint = "/livez" - DefaultReadinessEndpoint = "/readyz" -) - func defaultLivenessProbe(*fiber.Ctx) bool { return true } func defaultReadinessProbe(*fiber.Ctx) bool { return true } -// ConfigDefault is the default config var ConfigDefault = Config{ LivenessProbe: defaultLivenessProbe, ReadinessProbe: defaultReadinessProbe, - LivenessEndpoint: DefaultLivenessEndpoint, - ReadinessEndpoint: DefaultReadinessEndpoint, + LivenessEndpoint: "/livez", + ReadinessEndpoint: "/readyz", } ``` \ No newline at end of file diff --git a/middleware/healthcheck/config.go b/middleware/healthcheck/config.go index 66c6e46a10..d4e5fac1b0 100644 --- a/middleware/healthcheck/config.go +++ b/middleware/healthcheck/config.go @@ -11,10 +11,9 @@ type Config struct { // Optional. Default: nil Next func(c *fiber.Ctx) bool - // Function to be used for checking the liveness of the application. - // Returns true if the application is running and false if it is not. - // The liveness probe is typically used to indicate if the application is in a state where - // it can handle requests (e.g., the server is up and running). + // Function used for checking the liveness of the application. Returns true if the application + // is running and false if it is not. The liveness probe is typically used to indicate if + // the application is in a state where it can handle requests (e.g., the server is up and running). // // Optional. Default: func(c *fiber.Ctx) bool { return true } LivenessProbe HealthChecker @@ -24,10 +23,9 @@ type Config struct { // Optional. Default: "/livez" LivenessEndpoint string - // Function to be used for checking the readiness of the application. - // Returns true if the application is ready to process requests and false otherwise. - // The readiness probe typically checks if all necessary services, databases, and other dependencies - // are available for the application to function correctly. + // Function used for checking the readiness of the application. Returns true if the application + // is ready to process requests and false otherwise. The readiness probe typically checks if all necessary + // services, databases, and other dependencies are available for the application to function correctly. // // Optional. Default: func(c *fiber.Ctx) bool { return true } ReadinessProbe HealthChecker