feat: runtime manager
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"galaxy/model/order"
|
||||
"galaxy/model/report"
|
||||
@@ -33,9 +34,25 @@ type executor struct {
|
||||
cfg controller.Configurer
|
||||
}
|
||||
|
||||
// ResolveStoragePath returns the engine storage path resolved from
|
||||
// STORAGE_PATH (preferred, historical name) or GAME_STATE_PATH (canonical
|
||||
// name written by Runtime Manager). It returns an error when neither
|
||||
// variable is set; callers are expected to fail fast at startup.
|
||||
func ResolveStoragePath() (string, error) {
|
||||
if v := strings.TrimSpace(os.Getenv("STORAGE_PATH")); v != "" {
|
||||
return v, nil
|
||||
}
|
||||
if v := strings.TrimSpace(os.Getenv("GAME_STATE_PATH")); v != "" {
|
||||
return v, nil
|
||||
}
|
||||
return "", errors.New("storage path is not set: provide STORAGE_PATH or GAME_STATE_PATH")
|
||||
}
|
||||
|
||||
func initConfig() controller.Configurer {
|
||||
return func(p *controller.Param) {
|
||||
p.StoragePath = os.Getenv("STORAGE_PATH")
|
||||
// Validated once at startup by ResolveStoragePath; the error
|
||||
// is dropped here to keep the Configurer signature simple.
|
||||
p.StoragePath, _ = ResolveStoragePath()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// HealthzHandler is the technical liveness probe used by Runtime Manager
|
||||
// and operator tooling. It returns 200 with {"status":"ok"} regardless
|
||||
// of whether the engine has been initialised through POST /api/v1/init.
|
||||
func HealthzHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package router_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"galaxy/game/internal/controller"
|
||||
"galaxy/game/internal/router"
|
||||
"galaxy/game/internal/router/handler"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHealthzReturnsOKWithoutInit(t *testing.T) {
|
||||
r := router.SetupRouter(handler.NewDefaultConfigExecutor(func(p *controller.Param) {
|
||||
p.StoragePath = ""
|
||||
}))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest(http.MethodGet, "/healthz", nil)
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code, w.Body)
|
||||
|
||||
var body map[string]string
|
||||
require.NoError(t, json.Unmarshal(w.Body.Bytes(), &body))
|
||||
assert.Equal(t, "ok", body["status"])
|
||||
}
|
||||
|
||||
func TestResolveStoragePathPrecedence(t *testing.T) {
|
||||
t.Setenv("STORAGE_PATH", "/tmp/storage")
|
||||
t.Setenv("GAME_STATE_PATH", "/tmp/state")
|
||||
|
||||
got, err := handler.ResolveStoragePath()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "/tmp/storage", got)
|
||||
}
|
||||
|
||||
func TestResolveStoragePathFallback(t *testing.T) {
|
||||
t.Setenv("STORAGE_PATH", "")
|
||||
t.Setenv("GAME_STATE_PATH", "/tmp/state")
|
||||
|
||||
got, err := handler.ResolveStoragePath()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "/tmp/state", got)
|
||||
}
|
||||
|
||||
func TestResolveStoragePathMissing(t *testing.T) {
|
||||
t.Setenv("STORAGE_PATH", "")
|
||||
t.Setenv("GAME_STATE_PATH", "")
|
||||
|
||||
_, err := handler.ResolveStoragePath()
|
||||
require.Error(t, err)
|
||||
}
|
||||
@@ -63,6 +63,8 @@ func setupRouter(executor handler.CommandExecutor) *gin.Engine {
|
||||
}
|
||||
}
|
||||
|
||||
r.GET("/healthz", handler.HealthzHandler)
|
||||
|
||||
groupV1 := r.Group("/api/v1")
|
||||
|
||||
groupV1.GET("/status", func(ctx *gin.Context) { handler.StatusHandler(ctx, executor) })
|
||||
|
||||
Reference in New Issue
Block a user