Skip to content

Commit 20e8437

Browse files
Health check endpoints (#41)
1 parent 1cc0952 commit 20e8437

File tree

23 files changed

+387
-14
lines changed

23 files changed

+387
-14
lines changed

Dockerfile

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,19 @@ COPY build/${TARGETPLATFORM} /workspace/nginx-ignition
1111

1212
FROM alpine:3
1313

14-
ARG NGINX_IGNITION_VERSION
15-
1614
EXPOSE 8090
1715

16+
HEALTHCHECK \
17+
--interval=5s \
18+
--timeout=5s \
19+
--retries=3 \
20+
CMD curl -f http://localhost:8090/api/health/liveness || exit 1
21+
1822
ENV NGINX_IGNITION_NGINX_BINARY_PATH="/usr/sbin/nginx" \
1923
NGINX_IGNITION_SERVER_FRONTEND_PATH="/opt/nginx-ignition/frontend" \
2024
NGINX_IGNITION_DATABASE_DRIVER="sqlite" \
2125
NGINX_IGNITION_DATABASE_MIGRATIONS_PATH="/opt/nginx-ignition/migrations" \
2226
NGINX_IGNITION_DATABASE_DATA_PATH="/opt/nginx-ignition/data" \
23-
NGINX_IGNITION_VERSION="${NGINX_IGNITION_VERSION}" \
2427
GOMEMLIMIT="96MiB"
2528

2629
ENTRYPOINT ["/opt/nginx-ignition/nginx-ignition"]
@@ -33,7 +36,8 @@ RUN apk update && \
3336
nginx-mod-http-js \
3437
nginx-mod-http-lua \
3538
nginx-mod-stream \
36-
ca-certificates && \
39+
ca-certificates \
40+
curl && \
3741
apk cache clean && \
3842
update-ca-certificates
3943

Makefile

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ DOCKER_IMAGE ?= dillmann/nginx-ignition
22
VERSION ?= 0.0.0
33
PR_ID ?= 0
44
SNAPSHOT_TAG_SUFFIX := $(if $(filter-out ,$(PR_ID)),$(if $(filter-out 0,$(PR_ID)),pr-$(PR_ID)-snapshot,snapshot),snapshot)
5+
LDFLAGS := -X 'dillmann.com.br/nginx-ignition/core/common/version.Number=$(VERSION)'
56

67
.prerequisites:
78
go work sync
@@ -28,23 +29,21 @@ SNAPSHOT_TAG_SUFFIX := $(if $(filter-out ,$(PR_ID)),$(if $(filter-out 0,$(PR_ID)
2829
cd frontend/ && npm run build
2930

3031
.build-backend:
31-
GOARCH=amd64 CGO_ENABLED="0" GOOS="linux" go build -o build/linux/amd64 application/main.go
32-
GOARCH=arm64 CGO_ENABLED="0" GOOS="linux" go build -o build/linux/arm64 application/main.go
33-
GOARCH=arm64 CGO_ENABLED="0" GOOS="darwin" go build -o build/macos/arm64 application/main.go
32+
GOARCH=amd64 CGO_ENABLED="0" GOOS="linux" go build -ldflags "$(LDFLAGS)" -o build/linux/amd64 application/main.go
33+
GOARCH=arm64 CGO_ENABLED="0" GOOS="linux" go build -ldflags "$(LDFLAGS)" -o build/linux/arm64 application/main.go
34+
GOARCH=arm64 CGO_ENABLED="0" GOOS="darwin" go build -ldflags "$(LDFLAGS)" -o build/macos/arm64 application/main.go
3435

3536
.build-release-docker-image:
3637
docker buildx build \
3738
--tag $(DOCKER_IMAGE):$(VERSION) \
3839
--tag $(DOCKER_IMAGE):latest \
3940
--platform linux/amd64,linux/arm64 \
40-
--build-arg NGINX_IGNITION_VERSION="$(VERSION)" \
4141
--push .
4242

4343
.build-snapshot-docker-image:
4444
docker buildx build \
4545
--tag $(DOCKER_IMAGE):$(SNAPSHOT_TAG_SUFFIX) \
4646
--platform linux/amd64,linux/arm64 \
47-
--build-arg NGINX_IGNITION_VERSION="" \
4847
--push .
4948

5049
.build-distribution-files:

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ Please note that in its default configuration the app will start using an embedd
4646
for testing and some experiments, is not recommended for a long-term scenario. For that, please refer to the
4747
configuration section below to use PostgreSQL instead.
4848

49+
### Docker compose
50+
51+
The [docker-compose.yml](docker-compose.yml) file is also available as a recommended way of starting the nginx ignition
52+
using Docker alongside a production-ready PostgreSQL database and health checks enabled. Make sure to change the
53+
`POSTGRES_PASSWORD`, `NGINX_IGNITION_DATABASE_PASSWORD` and `NGINX_IGNITION_SECURITY_JWT_SECRET` values in the compose
54+
file before deploying.
55+
4956
### Installing or running locally on a Linux or macOS machine
5057

5158
To install nginx ignition locally on your machine, you have several options:
@@ -75,6 +82,15 @@ If you're using any 1.x version of the nginx ignition and plans to upgrade to 2.
7582
Check [this documentation file](docs/configuration-properties.md) for more details about the available configuration
7683
properties and some common use-case examples.
7784

85+
## Health checks
86+
87+
Check [this documentation file](docs/health-checks.md) for more details about the available health checks endpoints
88+
that you can use on your Docker Compose file, Kubernetes cluster or monitoring platform.
89+
90+
If needed, you can disable the health check endpoints by setting the `NGINX_IGNITION_HEALTH_CHECK_ENABLED` environment
91+
variable with the `false` value (check the [configuration properties](docs/configuration-properties.md) documentation
92+
for more details).
93+
7894
## Troubleshooting
7995

8096
Check [this documentation file](docs/troubleshooting.md) for more details about how to troubleshoot the application

api/frontend/configuration_handler.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"dillmann.com.br/nginx-ignition/core/common/configuration"
1212
"dillmann.com.br/nginx-ignition/core/common/log"
13+
"dillmann.com.br/nginx-ignition/core/common/version"
1314
)
1415

1516
type configurationHandler struct {
@@ -18,16 +19,15 @@ type configurationHandler struct {
1819

1920
func (h *configurationHandler) handle(ctx *gin.Context) {
2021
codeEditorApiKey, _ := h.configuration.Get("nginx-ignition.frontend.code-editor-api-key")
21-
version, _ := h.configuration.Get("nginx-ignition.version")
2222

2323
var apiKey *string
2424
if codeEditorApiKey != "" {
2525
apiKey = &codeEditorApiKey
2626
}
2727

2828
var versionString *string
29-
if version != "" {
30-
versionString = &version
29+
if version.Number != "" && version.Number != "0.0.0" {
30+
versionString = &version.Number
3131
}
3232

3333
output := &configurationDto{

api/healthcheck/converter.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package healthcheck
2+
3+
import (
4+
"dillmann.com.br/nginx-ignition/core/common/healthcheck"
5+
)
6+
7+
func toDto(status *healthcheck.Status) *statusDto {
8+
output := &statusDto{
9+
Healthy: status.Healthy,
10+
Details: make([]*detailDto, len(status.Details)),
11+
}
12+
13+
for index, details := range status.Details {
14+
output.Details[index] = &detailDto{
15+
Component: details.ID,
16+
Healthy: details.Error == nil,
17+
}
18+
}
19+
20+
return output
21+
}

api/healthcheck/dto.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package healthcheck
2+
3+
type statusDto struct {
4+
Healthy bool `json:"healthy"`
5+
Details []*detailDto `json:"details"`
6+
}
7+
8+
type detailDto struct {
9+
Component string `json:"component"`
10+
Healthy bool `json:"healthy"`
11+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package healthcheck
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/gin-gonic/gin"
7+
8+
"dillmann.com.br/nginx-ignition/core/common/healthcheck"
9+
)
10+
11+
type livenessHandler struct {
12+
healthCheck *healthcheck.HealthCheck
13+
}
14+
15+
func (h livenessHandler) handle(ctx *gin.Context) {
16+
status := h.healthCheck.Status(ctx.Request.Context())
17+
18+
payload := toDto(status)
19+
statusCode := http.StatusOK
20+
21+
if !status.Healthy {
22+
statusCode = http.StatusServiceUnavailable
23+
}
24+
25+
ctx.JSON(statusCode, payload)
26+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package healthcheck
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/gin-gonic/gin"
7+
)
8+
9+
type readinessHandler struct{}
10+
11+
func (h readinessHandler) handle(ctx *gin.Context) {
12+
ctx.JSON(http.StatusOK, gin.H{
13+
"ready": true,
14+
"message": "I'm alive (but the cake is a lie 👀🍰)",
15+
})
16+
}

api/healthcheck/routes.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package healthcheck
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/gin-gonic/gin"
7+
8+
"dillmann.com.br/nginx-ignition/api/common/authorization"
9+
"dillmann.com.br/nginx-ignition/core/common/configuration"
10+
"dillmann.com.br/nginx-ignition/core/common/healthcheck"
11+
"dillmann.com.br/nginx-ignition/core/common/log"
12+
)
13+
14+
const (
15+
apiPath = "/api/health"
16+
)
17+
18+
func Install(
19+
router *gin.Engine,
20+
authorizer *authorization.ABAC,
21+
healthCheck *healthcheck.HealthCheck,
22+
cfg *configuration.Configuration,
23+
) {
24+
enabled, err := cfg.GetBoolean("nginx-ignition.health-check.enabled")
25+
if err != nil {
26+
log.Warnf(
27+
"Unable to check if health check endpoints should be enabled (%v). Keeping them disabled as a fallback.",
28+
err,
29+
)
30+
return
31+
}
32+
33+
if !enabled {
34+
log.Warnf("Health check endpoints disabled by configuration")
35+
return
36+
}
37+
38+
basePath := router.Group(apiPath)
39+
basePath.GET("/liveness", livenessHandler{healthCheck}.handle)
40+
basePath.GET("/readiness", readinessHandler{}.handle)
41+
42+
authorizer.AllowAnonymous(http.MethodGet, apiPath+"/liveness")
43+
authorizer.AllowAnonymous(http.MethodGet, apiPath+"/readiness")
44+
}

api/installer.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"dillmann.com.br/nginx-ignition/api/certificate"
77
"dillmann.com.br/nginx-ignition/api/common/server"
88
"dillmann.com.br/nginx-ignition/api/frontend"
9+
"dillmann.com.br/nginx-ignition/api/healthcheck"
910
"dillmann.com.br/nginx-ignition/api/host"
1011
"dillmann.com.br/nginx-ignition/api/integration"
1112
"dillmann.com.br/nginx-ignition/api/nginx"
@@ -19,6 +20,7 @@ import (
1920
func Install() error {
2021
return container.Run(
2122
server.Install,
23+
healthcheck.Install,
2224
settings.Install,
2325
accesslist.Install,
2426
certificate.Install,

0 commit comments

Comments
 (0)