diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 3232dd4..2c8670b 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -59,6 +59,7 @@ jobs: shell: bash env: KUBECONFIG: ${{ runner.temp }}/k3s-kubeconfig + HEAD_SHA: ${{ github.event.workflow_run.head_sha }} run: | set -euo pipefail kubectl port-forward --namespace alisten service/alisten-serve 18090:9090 & @@ -69,4 +70,11 @@ jobs: --retry-delay 2 \ --retry-connrefused \ --max-time 10 \ - http://127.0.0.1:18090/healthz + http://127.0.0.1:18090/readyz + # CI 构建发布镜像时把 tag+commit 写进版本号。 + expected="${TAG}+${HEAD_SHA}" + actual="$(curl --silent --show-error --fail --max-time 10 http://127.0.0.1:18090/version)" + if [[ "$actual" != "$expected" ]]; then + printf '线上版本是 %s,期望 %s\n' "$actual" "$expected" >&2 + exit 1 + fi diff --git a/internal/adapter/in/ops/ops.go b/internal/adapter/in/ops/ops.go index 422424b..8b14c80 100644 --- a/internal/adapter/in/ops/ops.go +++ b/internal/adapter/in/ops/ops.go @@ -8,7 +8,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) -func NewHandler(ready func() bool, metrics prometheus.Gatherer) http.Handler { +func NewHandler(ready func() bool, metrics prometheus.Gatherer, version string) http.Handler { mux := http.NewServeMux() mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, _ *http.Request) { _, _ = io.WriteString(w, "ok\n") @@ -20,6 +20,9 @@ func NewHandler(ready func() bool, metrics prometheus.Gatherer) http.Handler { } _, _ = io.WriteString(w, "ok\n") }) + mux.HandleFunc("GET /version", func(w http.ResponseWriter, _ *http.Request) { + _, _ = io.WriteString(w, version+"\n") + }) mux.Handle("GET /metrics", promhttp.HandlerFor(metrics, promhttp.HandlerOpts{})) return mux } diff --git a/internal/adapter/in/ops/ops_test.go b/internal/adapter/in/ops/ops_test.go index b37990f..b7c4336 100644 --- a/internal/adapter/in/ops/ops_test.go +++ b/internal/adapter/in/ops/ops_test.go @@ -15,7 +15,7 @@ func TestHandler(t *testing.T) { registry := prometheus.NewRegistry() registry.MustRegister(prometheus.NewGauge(prometheus.GaugeOpts{Name: "test_gauge", Help: "测试用指标。"})) var ready atomic.Bool - handler := NewHandler(ready.Load, registry) + handler := NewHandler(ready.Load, registry, "v1.2.3+abc") get := func(path string) *httptest.ResponseRecorder { rec := httptest.NewRecorder() @@ -28,6 +28,12 @@ func TestHandler(t *testing.T) { t.Errorf("/healthz 状态码 = %d,期望 200", rec.Code) } }) + t.Run("version 返回构建版本", func(t *testing.T) { + rec := get("/version") + if body := rec.Body.String(); rec.Code != http.StatusOK || body != "v1.2.3+abc\n" { + t.Errorf("/version 状态码 = %d,响应 = %q,期望 200 和 \"v1.2.3+abc\\n\"", rec.Code, body) + } + }) t.Run("metrics 输出注册的指标", func(t *testing.T) { rec := get("/metrics") body, _ := io.ReadAll(rec.Body) diff --git a/internal/bootstrap/healthcheck_test.go b/internal/bootstrap/healthcheck_test.go index b94f9b9..83d44f3 100644 --- a/internal/bootstrap/healthcheck_test.go +++ b/internal/bootstrap/healthcheck_test.go @@ -12,7 +12,7 @@ import ( func TestHealthcheckFollowsReadiness(t *testing.T) { var ready atomic.Bool - server := httptest.NewServer(ops.NewHandler(ready.Load, prometheus.NewRegistry())) + server := httptest.NewServer(ops.NewHandler(ready.Load, prometheus.NewRegistry(), version)) defer server.Close() t.Setenv("ALISTEN_OPS_LISTEN", server.Listener.Addr().String()) diff --git a/internal/bootstrap/serve.go b/internal/bootstrap/serve.go index 385e91f..b371b61 100644 --- a/internal/bootstrap/serve.go +++ b/internal/bootstrap/serve.go @@ -182,7 +182,7 @@ func runServe(base *slog.Logger) (err error) { opsLogger := base.With("component", "ops") publicServer := newHTTPServer(p.handler, base.With("component", "http")) - opsServer := newHTTPServer(recoverPanics(ops.NewHandler(ready, metrics), opsLogger), opsLogger) + opsServer := newHTTPServer(recoverPanics(ops.NewHandler(ready, metrics, version), opsLogger), opsLogger) go func() { fatal <- fmt.Errorf("HTTP 服务异常退出:%w", publicServer.Serve(publicListener)) }() go func() { fatal <- fmt.Errorf("HTTP 服务异常退出:%w", opsServer.Serve(opsListener)) }() logger.Info("服务已启动", "listen", publicListener.Addr().String(), "ops_listen", opsListener.Addr().String(), "instance", instance) diff --git a/internal/bootstrap/worker.go b/internal/bootstrap/worker.go index bf28b61..04fbf37 100644 --- a/internal/bootstrap/worker.go +++ b/internal/bootstrap/worker.go @@ -275,7 +275,7 @@ func (w mediaWorker) run(ctx context.Context, listener net.Listener) error { }), ) opsLogger := w.base.With("component", "ops") - opsServer := newHTTPServer(recoverPanics(ops.NewHandler(ready, w.metrics), opsLogger), opsLogger) + opsServer := newHTTPServer(recoverPanics(ops.NewHandler(ready, w.metrics, version), opsLogger), opsLogger) go func() { fatal <- fmt.Errorf("运维端口异常退出:%w", opsServer.Serve(listener)) }() w.logger.Info("worker 已启动", "ops_listen", listener.Addr().String(), "instance", w.instance)