Skip to content

Commit

Permalink
Configurable and very basic health check endpoint (#203)
Browse files Browse the repository at this point in the history
* Test show/hide preview functionality
* Simple health check endpoint and tests
---------
Co-authored-by: Eldan Goldenberg <[email protected]>
  • Loading branch information
nein09 authored Jan 18, 2024
1 parent 373b816 commit 8ed8bf4
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 1 deletion.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ After start-up you can connect to the server and explore the published tables an

To disable the web interface, supply the run time flag `--no-preview`

## Health Check Endpoint

In order to run the server in an orchestrated environment, like Docker, it can be useful to have a health check endpoint. This is `/health` by default, and returns a `200 OK` if the server is responding to requests. To use a custom URL for the health check endpoint, supply the run time flag `-e` and your path. This setting will respect the base path setting, so if you choose a base path of `/example` and a health check endpoint of `/foo`, your health check URL becomes `/example/foo`.

## Layers List

A list of layers is available in JSON at:
Expand Down
16 changes: 16 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ func init() {
viper.SetDefault("CoordinateSystem.Ymin", -20037508.3427892)
viper.SetDefault("CoordinateSystem.Xmax", 20037508.3427892)
viper.SetDefault("CoordinateSystem.Ymax", 20037508.3427892)

viper.SetDefault("HealthEndpoint", "/health")
}

func main() {
Expand All @@ -110,6 +112,7 @@ func main() {
flagHelpOn := getopt.BoolLong("help", 'h', "display help output")
flagVersionOn := getopt.BoolLong("version", 'v', "display version number")
flagHidePreview := getopt.BoolLong("no-preview", 'n', "hide web interface")
flagHealthEndpoint := getopt.StringLong("health", 'e', "", "desired path to health endpoint, e.g. \"/health\"")
getopt.Parse()

if *flagHelpOn {
Expand Down Expand Up @@ -145,6 +148,10 @@ func main() {
viper.Set("ShowPreview", false)
}

if *flagHealthEndpoint != "" {
viper.Set("HealthEndpoint", *flagHealthEndpoint)
}

// Report our status
log.Infof("%s %s", programName, programVersion)
log.Info("Run with --help parameter for commandline options")
Expand Down Expand Up @@ -391,6 +398,13 @@ func requestTiles(w http.ResponseWriter, r *http.Request) error {
return nil
}

// A simple health check endpoint
func healthCheck(w http.ResponseWriter, r *http.Request) error {
w.WriteHeader(http.StatusOK)
w.Write([]byte("200 OK"))
return nil
}

/******************************************************************************/

// tileAppError is an optional error structure functions can return
Expand Down Expand Up @@ -488,6 +502,8 @@ func tileRouter() *mux.Router {
if viper.GetBool("EnableMetrics") {
r.Handle("/metrics", promhttp.Handler())
}

r.Handle(viper.GetString("HealthEndpoint"), tileAppHandler(healthCheck)).Methods(http.MethodGet)
return r
}

Expand Down
94 changes: 94 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"os"
Expand All @@ -27,6 +28,7 @@ func TestMain(m *testing.M) {
sql := "CREATE EXTENSION IF NOT EXISTS postgis"
_, err = db.Exec(context.Background(), sql)
if err != nil {
fmt.Printf("Error creating extension: %s", err)
os.Exit(1)
}

Expand Down Expand Up @@ -67,6 +69,98 @@ func TestBasePath(t *testing.T) {
response := httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")

request, _ = http.NewRequest("GET", "/test/health", nil)
response = httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")
}

// cleanup
viper.Set("BasePath", "/")
}

// Test that the preview endpoints are hidden or shown according to the config
func TestShowPreview(t *testing.T) {
viper.Set("ShowPreview", true)
r := tileRouter()
request, _ := http.NewRequest("GET", "/index.json", nil)
response := httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")
request, _ = http.NewRequest("GET", "/index.html", nil)
response = httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")
}

// the current default behavior is to show the preview
func TestShowPreviewDefault(t *testing.T) {
r := tileRouter()
request, _ := http.NewRequest("GET", "/index.json", nil)
response := httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")
request, _ = http.NewRequest("GET", "/index.html", nil)
response = httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")
}

func TestHidePreview(t *testing.T) {
viper.Set("ShowPreview", false)
r := tileRouter()
request, _ := http.NewRequest("GET", "/index.json", nil)
response := httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 404, response.Code, "Not Found response is expected")
request, _ = http.NewRequest("GET", "/index.html", nil)
response = httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 404, response.Code, "Not Found response is expected")

// cleanup
viper.Set("ShowPreview", true)
}

// Test that the health endpoint gives a 200 if the server is running
func TestHealth(t *testing.T) {
r := tileRouter()
request, _ := http.NewRequest("GET", "/health", nil)
response := httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")
assert.Equal(t, "200 OK", string(response.Result().Status), "Response status should say ok")
}

func TestHealthCustomUrl(t *testing.T) {
viper.Set("HealthEndpoint", "/testHealthABC")
r := tileRouter()
request, _ := http.NewRequest("GET", "/health", nil)
response := httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 404, response.Code, "Not Found response is expected")
request, _ = http.NewRequest("GET", "/testHealthABC", nil)
response = httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")
assert.Equal(t, "200 OK", string(response.Result().Status), "Response status should say ok")

// cleanup
viper.Set("HealthEndpoint", "/health")
}

func TestHealthCustomUrlWithBasePath(t *testing.T) {
viper.Set("BasePath", "/foo")
viper.Set("HealthEndpoint", "/bar")
r := tileRouter()
request, _ := http.NewRequest("GET", "/foo/bar", nil)
response := httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")
assert.Equal(t, "200 OK", string(response.Result().Status), "Response status should say ok")

// cleanup
viper.Set("HealthEndpoint", "/health")
viper.Set("BasePath", "/")
}
2 changes: 1 addition & 1 deletion util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestMetrics(t *testing.T) {
viper.Set("EnableMetrics", true)

r := tileRouter()
request, _ := http.NewRequest("GET", "/test/metrics", nil)
request, _ := http.NewRequest("GET", "/metrics", nil)
response := httptest.NewRecorder()
r.ServeHTTP(response, request)
assert.Equal(t, 200, response.Code, "OK response is expected")
Expand Down

0 comments on commit 8ed8bf4

Please sign in to comment.