From 35e6069f5eff1732b6d2563deda995d0b5c6dd39 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Thu, 19 Oct 2023 15:43:49 +0100 Subject: [PATCH] Add the healtcheck checks on the end server. --- Makefile | 2 +- README.md | 13 +++++++++++-- go.mod | 2 +- go.sum | 4 ++-- main.go | 3 ++- server.go | 29 ++++++++++++++++++----------- struct_config.go | 17 +++++++++-------- 7 files changed, 44 insertions(+), 26 deletions(-) diff --git a/Makefile b/Makefile index c88e991..3cb04f2 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ help: ## display this help .PHONY: run run: build ## run application - @LOG_LEVEL=debug BLOCK_SCHEMA_INTROSPECTION=false CACHE_TTL=10 JWT_ROLE_RATE_LIMIT=false JWT_ROLE_CLAIM_PATH="Hasura.x-hasura-default-role" JWT_USER_CLAIM_PATH="Hasura.x-hasura-user-id" HOST_GRAPHQL=https://hasura8.lan/ ./graphql-proxy + @LOG_LEVEL=debug BLOCK_SCHEMA_INTROSPECTION=false CACHE_TTL=10 JWT_ROLE_RATE_LIMIT=false JWT_ROLE_CLAIM_PATH="Hasura.x-hasura-default-role" JWT_USER_CLAIM_PATH="Hasura.x-hasura-user-id" HOST_GRAPHQL=https://hasura8.lan/ HEALTHCHECK_GRAPHQL_URL=https://hasura8.lan/v1/graphql ./graphql-proxy .PHONY: build build: ## build the binary diff --git a/README.md b/README.md index 01517d1..67d0a57 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,9 @@ You can find the example of the Kubernetes manifest in the [example deployment]( - [Blocking introspection](#blocking-introspection) - [API endpoints](#api-endpoints) - [Ban or unban the user](#ban-or-unban-the-user) - - [Monitoring endpoint](#monitoring-endpoint) + - [General](#general) + - [Healthcheck](#healthcheck) + - [Monitoring endpoint](#monitoring-endpoint) ### Why this project exists @@ -60,6 +62,7 @@ I wanted to monitor the queries and responses of our graphql endpoint. Still, we | `MONITORING_PORT` | The port to expose the metrics endpoint | `9393` | | `PORT_GRAPHQL` | The port to expose the graphql endpoint | `8080` | | `HOST_GRAPHQL` | The host to proxy the graphql endpoint | `http://localhost/` | +| `HEALTHCHECK_GRAPHQL_URL` | The URL to check the health of the graphql endpoint | `` | | `JWT_USER_CLAIM_PATH` | Path to the user claim in the JWT token | `` | | `JWT_ROLE_CLAIM_PATH` | Path to the role claim in the JWT token | `` | | `ROLE_FROM_HEADER` | Header name to extract the role from | `` | @@ -173,7 +176,13 @@ curl -X POST \ Ban details will be stored in the `banned_users.json` file, which you can mount as a file or configmap to the `/go/src/app/banned_users.json` path ( or use `BANNED_USERS_FILE` environment variable to specify the path to the file). The file operation is important if you have multiple instances of the proxy running, as it will allow you to ban the user from accessing the application on all instances. -### Monitoring endpoint +### General + +#### Healthcheck + +If you'd like the `/healthz` endpoint to perform actual check for the connectivity to the graphql endpoint - set the `HEALTHCHECK_GRAPHQL_URL` environment variable to the exact URL of the graphql endpoint. The query executed will be `query { __typename }` and if the response is not `200 OK` - the healthcheck will fail. + +#### Monitoring endpoint Example metrics produced by the proxy: diff --git a/go.mod b/go.mod index c47bb09..2b18253 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/lukaszraczylo/ask v0.0.0-20230927103145-2ff1123b4415 github.com/lukaszraczylo/go-ratecounter v0.1.8 - github.com/lukaszraczylo/go-simple-graphql v1.1.34 + github.com/lukaszraczylo/go-simple-graphql v1.1.35 github.com/rs/zerolog v1.31.0 github.com/stretchr/testify v1.8.4 github.com/valyala/fasthttp v1.50.0 diff --git a/go.sum b/go.sum index 2ba5af7..c93ba3a 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/lukaszraczylo/ask v0.0.0-20230927103145-2ff1123b4415 h1:lvI8Wlbg4PxkR github.com/lukaszraczylo/ask v0.0.0-20230927103145-2ff1123b4415/go.mod h1:M+UVdyqZs++xtEPrascaVmZdOMhCnxjZ2SgH+xHpR0c= github.com/lukaszraczylo/go-ratecounter v0.1.8 h1:ZYm6Wkn58ZAlFWRmC7PaD4oAYHWcu8/0MUDWGe3PnJQ= github.com/lukaszraczylo/go-ratecounter v0.1.8/go.mod h1:TqXEOCtFJStk1i0tkipprv1kiDHGon1MVUisjSTBSKM= -github.com/lukaszraczylo/go-simple-graphql v1.1.34 h1:ozDRcZEovXJ61/v+4LvF0dIyk5d+Y0Ar6Ne5rbocxkg= -github.com/lukaszraczylo/go-simple-graphql v1.1.34/go.mod h1:LywSrNo3Otp69z1pTZ7FwE8qIa3mxHBUEPBMGRfOHRk= +github.com/lukaszraczylo/go-simple-graphql v1.1.35 h1:51agVc1C5p9VxiZuvk8TwsEAWU+ieNXnmAgRdRXuqFk= +github.com/lukaszraczylo/go-simple-graphql v1.1.35/go.mod h1:YWMelAXnFs8uknj3Pv2gO8Svzv5k4cAL940MI0n/R0k= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= diff --git a/main.go b/main.go index 211b628..eaa0506 100644 --- a/main.go +++ b/main.go @@ -38,8 +38,9 @@ func parseConfig() { return strings.Split(urls, ",") }() c.Logger = libpack_logging.NewLogger() + c.Server.HealthcheckGraphQL = envutil.Getenv("HEALTHCHECK_GRAPHQL_URL", "") c.Client.GQLClient = graphql.NewConnection() - c.Client.GQLClient.SetEndpoint(c.Server.HostGraphQL) + c.Client.GQLClient.SetEndpoint(c.Server.HealthcheckGraphQL) c.Server.AccessLog = envutil.GetBool("ENABLE_ACCESS_LOG", false) c.Server.ReadOnlyMode = envutil.GetBool("READ_ONLY_MODE", false) c.Server.AllowURLs = func() []string { diff --git a/server.go b/server.go index d2b3037..15bef81 100644 --- a/server.go +++ b/server.go @@ -25,12 +25,13 @@ func StartHTTPProxy() { AllowOrigins: "*", })) - server.Post("/*", processGraphQLRequest) - server.Get("/*", proxyTheRequest) - server.Get("/healthz", healthCheck) server.Get("/livez", healthCheck) + server.Post("/*", processGraphQLRequest) + server.Get("/*", proxyTheRequest) + + cfg.Logger.Info("GraphQL query proxy started", map[string]interface{}{"port": cfg.Server.PortGraphQL}) err := server.Listen(fmt.Sprintf(":%d", cfg.Server.PortGraphQL)) if err != nil { cfg.Logger.Critical("Can't start the service", map[string]interface{}{"error": err.Error()}) @@ -50,14 +51,20 @@ func checkAllowedURLs(c *fiber.Ctx) bool { } func healthCheck(c *fiber.Ctx) error { - // query := `{ __typename }` - // _, err := cfg.Client.GQLClient.Query(query, nil, nil) - // if err != nil { - // cfg.Logger.Error("Can't reach the GraphQL server", map[string]interface{}{"error": err.Error()}) - // cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil) - // return c.SendStatus(500) - // } - return c.SendStatus(200) + if len(cfg.Server.HealthcheckGraphQL) > 0 { + cfg.Logger.Debug("Health check enabled", map[string]interface{}{"url": cfg.Server.HealthcheckGraphQL}) + query := `{ __typename }` + _, err := cfg.Client.GQLClient.Query(query, nil, nil) + if err != nil { + cfg.Logger.Error("Can't reach the GraphQL server", map[string]interface{}{"error": err.Error()}) + cfg.Monitoring.Increment(libpack_monitoring.MetricsFailed, nil) + c.Status(500).SendString("Can't reach the GraphQL server with {__typename} query") + return err + } + } + cfg.Logger.Debug("Health check returning OK") + c.Status(200).SendString("Health check OK") + return nil } func processGraphQLRequest(c *fiber.Ctx) error { diff --git a/struct_config.go b/struct_config.go index bdc0cc7..7c4f961 100644 --- a/struct_config.go +++ b/struct_config.go @@ -15,14 +15,15 @@ type config struct { // Server holds the configuration of the server _ONLY_. Server struct { - PortGraphQL int - PortMonitoring int - HostGraphQL string - AccessLog bool - ReadOnlyMode bool - AllowURLs []string - EnableApi bool - ApiPort int + PortGraphQL int + PortMonitoring int + HostGraphQL string + HealthcheckGraphQL string + AccessLog bool + ReadOnlyMode bool + AllowURLs []string + EnableApi bool + ApiPort int } Client struct {