Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adds health check #465

Merged
merged 7 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,4 @@ ENV LOCAL_AUTH_ENABLED=false
EXPOSE 8080

# run binary
# Disable API auth when running UDS Runtime in-cluster
CMD ["./app/uds-runtime"]
22 changes: 21 additions & 1 deletion src/pkg/api/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3041,7 +3041,7 @@ const docTemplate = `{
}
}
},
"/health": {
"/cluster-check": {
"get": {
"description": "Get Cluster Connection Status",
"produces": [
Expand All @@ -3057,6 +3057,26 @@ const docTemplate = `{
}
}
},
"/healthz": {
"get": {
"description": "check the health of the application",
"produces": [
"application/json"
],
"tags": [
"health"
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/monitor/pepr/{stream}": {
"get": {
"description": "Get Pepr data",
Expand Down
22 changes: 21 additions & 1 deletion src/pkg/api/docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -3030,7 +3030,7 @@
}
}
},
"/health": {
"/cluster-check": {
UncleGedd marked this conversation as resolved.
Show resolved Hide resolved
"get": {
"description": "Get Cluster Connection Status",
"produces": [
Expand All @@ -3046,6 +3046,26 @@
}
}
},
"/healthz": {
"get": {
"description": "check the health of the application",
"produces": [
"application/json"
],
"tags": [
"health"
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"/monitor/pepr/{stream}": {
"get": {
"description": "Get Pepr data",
Expand Down
15 changes: 14 additions & 1 deletion src/pkg/api/docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2029,7 +2029,7 @@ paths:
description: OK
tags:
- workloads
/health:
/cluster-check:
get:
description: Get Cluster Connection Status
produces:
Expand All @@ -2039,6 +2039,19 @@ paths:
description: OK
tags:
- cluster-connection-status
/healthz:
get:
description: check the health of the application
produces:
- application/json
responses:
"200":
description: OK
schema:
additionalProperties: true
type: object
tags:
- health
/monitor/pepr/{stream}:
get:
consumes:
Expand Down
27 changes: 26 additions & 1 deletion src/pkg/api/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
package api

import (
"encoding/json"
"log/slog"
"net/http"
"time"

"github.com/defenseunicorns/uds-runtime/src/pkg/api/auth/local"
_ "github.com/defenseunicorns/uds-runtime/src/pkg/api/docs" //nolint:staticcheck
Expand Down Expand Up @@ -867,7 +870,7 @@ func getStorageClass(cache *resources.Cache) func(w http.ResponseWriter, r *http
// @Tags cluster-connection-status
// @Produce text/event-stream
// @Success 200
// @Router /health [get]
// @Router /cluster-check [get]
func checkClusterConnection(k8sSession *session.K8sSession) http.HandlerFunc {
return k8sSession.ServeConnStatus()
}
Expand Down Expand Up @@ -910,3 +913,25 @@ func getCRD(cache *resources.Cache) func(w http.ResponseWriter, r *http.Request)
func authHandler(w http.ResponseWriter, r *http.Request) {
local.AuthHandler(w, r)
}

// @Description check the health of the application
// @Tags health
// @Produce json
// @Success 200 {object} map[string]interface{}
// @Router /healthz [get]
func healthz(w http.ResponseWriter, _ *http.Request) {
slog.Debug("Health check called")

response := map[string]interface{}{
"status": "UP",
"timestamp": time.Now().UTC().Format(time.RFC3339),
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)

if err := json.NewEncoder(w).Encode(response); err != nil {
slog.Error("Failed to encode health response", "error", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}
4 changes: 2 additions & 2 deletions src/pkg/api/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ func Setup(assets *embed.FS) (*chi.Mux, bool, error) {
r.Use(udsMiddleware.Auth)
r.Use(udsMiddleware.ConditionalCompress)

// Add Swagger UI routes
r.Get("/healthz", healthz)
r.Get("/swagger", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/swagger/index.html", http.StatusMovedPermanently)
})
r.Get("/swagger/*", httpSwagger.WrapHandler)
r.Get("/health", checkClusterConnection(k8sSession))
r.Get("/cluster-check", checkClusterConnection(k8sSession))
r.Route("/api/v1", func(r chi.Router) {
r.Head("/auth", authHandler)
r.Route("/monitor", func(r chi.Router) {
Expand Down
21 changes: 20 additions & 1 deletion src/test/e2e/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,13 +318,32 @@ func TestClusterHealth(t *testing.T) {

defer teardown()

t.Run("healthz", func(t *testing.T) {
// Create a new context with a timeout
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()

rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/healthz", nil)

// Start serving the request for 1 second
go func(ctx context.Context) {
r.ServeHTTP(rr, req)
}(ctx)

// wait for the context to be done
<-ctx.Done()
require.Equal(t, http.StatusOK, rr.Code)
require.Contains(t, rr.Body.String(), "\"status\":\"UP\"")
})

t.Run("cluster connected", func(t *testing.T) {
// Create a new context with a timeout
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()

rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/health", nil)
req := httptest.NewRequest("GET", "/cluster-check", nil)

// Start serving the request for 1 second
go func(ctx context.Context) {
Expand Down
2 changes: 1 addition & 1 deletion ui/src/lib/utils/cluster-check/cluster-check.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('cluster check', () => {

new ClusterCheck()

expect(urlAssertionMock).toHaveBeenCalledWith('/health')
expect(urlAssertionMock).toHaveBeenCalledWith('/cluster-check')

vi.advanceTimersByTime(1000)
expect(get(toast)).toHaveLength(0)
Expand Down
2 changes: 1 addition & 1 deletion ui/src/lib/utils/cluster-check/cluster-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class ClusterCheck {
#disconnected = false

constructor() {
this.#clusterCheck = new EventSource('/health')
this.#clusterCheck = new EventSource('/cluster-check')

this.#clusterCheck.addEventListener('close', function () {
this.close()
Expand Down
4 changes: 4 additions & 0 deletions ui/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ export default defineConfig(({ mode }) => ({
target: 'https://runtime-local.uds.dev:8443',
changeOrigin: true,
},
'/cluster-check': {
target: 'https://runtime-local.uds.dev:8443',
changeOrigin: true,
},
},
},
test: {
Expand Down
Loading