From 8b364b735a1df39ead189110a2e453dbd0880524 Mon Sep 17 00:00:00 2001 From: priyaselvaganesan Date: Wed, 19 Aug 2026 15:42:29 -0700 Subject: [PATCH 1/4] feat(event-ledger): expose GET /info endpoint Signed-off-by: priyaselvaganesan --- .../event-ledger/cmd/api/BUILD.bazel | 5 ++ .../event-ledger/cmd/api/startup/BUILD.bazel | 9 +- .../event-ledger/cmd/api/startup/info_test.go | 87 +++++++++++++++++++ .../cmd/api/startup/run_service.go | 11 ++- .../event-ledger/go.mod | 2 +- .../event-ledger/go.sum | 4 +- 6 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 src/control-plane-services/event-ledger/cmd/api/startup/info_test.go diff --git a/src/control-plane-services/event-ledger/cmd/api/BUILD.bazel b/src/control-plane-services/event-ledger/cmd/api/BUILD.bazel index 0596cc1f9..52f557f89 100644 --- a/src/control-plane-services/event-ledger/cmd/api/BUILD.bazel +++ b/src/control-plane-services/event-ledger/cmd/api/BUILD.bazel @@ -16,6 +16,11 @@ go_library( go_binary( name = "api", embed = [":api_lib"], + x_defs = { + "github.com/NVIDIA/nvcf/src/libraries/go/lib/pkg/version.Service": "nvcf-event-ledger", + "github.com/NVIDIA/nvcf/src/libraries/go/lib/pkg/version.Version": "{STABLE_VERSION}", + "github.com/NVIDIA/nvcf/src/libraries/go/lib/pkg/version.GitHash": "{STABLE_GIT_COMMIT_FULL}", + }, visibility = ["//visibility:public"], ) diff --git a/src/control-plane-services/event-ledger/cmd/api/startup/BUILD.bazel b/src/control-plane-services/event-ledger/cmd/api/startup/BUILD.bazel index 7580e3561..52ae2bf0a 100644 --- a/src/control-plane-services/event-ledger/cmd/api/startup/BUILD.bazel +++ b/src/control-plane-services/event-ledger/cmd/api/startup/BUILD.bazel @@ -31,6 +31,7 @@ go_library( "@com_github_lestrrat_go_jwx_v2//jwk", "@com_github_nvidia_nvcf_src_libraries_go_lib//pkg/nvkit/auth", "@com_github_nvidia_nvcf_src_libraries_go_lib//pkg/nvkit/clients", + "@com_github_nvidia_nvcf_src_libraries_go_lib//pkg/version", "@com_github_spf13_cobra//:cobra", "@com_github_spf13_viper//:viper", "@com_github_uptrace_opentelemetry_go_extra_otelzap//:otelzap", @@ -47,10 +48,16 @@ alias( go_test( name = "startup_test", - srcs = ["root_cmd_test.go"], + srcs = [ + "info_test.go", + "root_cmd_test.go", + ], embed = [":startup"], deps = [ + "//src/control-plane-services/event-ledger/cmd/api/service", "//src/control-plane-services/event-ledger/internal/config", + "@com_github_gorilla_mux//:mux", + "@com_github_nvidia_nvcf_src_libraries_go_lib//pkg/version", "@com_github_spf13_cobra//:cobra", "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", diff --git a/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go b/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go new file mode 100644 index 000000000..5c2e67b74 --- /dev/null +++ b/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go @@ -0,0 +1,87 @@ +/* +SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package startup + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + golibversion "github.com/NVIDIA/nvcf/src/libraries/go/lib/pkg/version" + "github.com/gorilla/mux" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/NVIDIA/nvcf/src/control-plane-services/event-ledger/cmd/api/service" +) + +// TestRegisterUnauthenticatedRoutes_Info verifies GET /info returns the stamped +// service/version/commit as JSON. It wires /info onto the router before auth +// middleware, so an empty *service.Server is safe as long as /health is not +// exercised. +func TestRegisterUnauthenticatedRoutes_Info(t *testing.T) { + golibversion.Service = "nvcf-fnds-api" + golibversion.Version = "test-1.0.0" + golibversion.GitHash = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + t.Cleanup(func() { + golibversion.Service = "" + golibversion.Version = "" + golibversion.GitHash = "" + }) + + router := mux.NewRouter() + registerUnauthenticatedRoutes(router, &service.Server{}) + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/info", nil) + router.ServeHTTP(w, r) + + assert.Equal(t, http.StatusOK, w.Code) + assert.Equal(t, "application/json", w.Header().Get("Content-Type")) + + var info map[string]string + require.NoError(t, json.Unmarshal(w.Body.Bytes(), &info)) + assert.Equal(t, "nvcf-fnds-api", info["service"]) + assert.Equal(t, "test-1.0.0", info["version"]) + assert.Equal(t, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", info["commit"]) +} + +// TestRegisterUnauthenticatedRoutes_Info_RejectsNonGET verifies non-GET methods on +// /info return 405 with an Allow: GET header, as enforced by the go-lib handler. +func TestRegisterUnauthenticatedRoutes_Info_RejectsNonGET(t *testing.T) { + router := mux.NewRouter() + registerUnauthenticatedRoutes(router, &service.Server{}) + + for _, method := range []string{ + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + } { + t.Run(method, func(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(method, "/info", nil) + router.ServeHTTP(w, r) + + assert.Equal(t, http.StatusMethodNotAllowed, w.Code) + assert.Equal(t, http.MethodGet, w.Header().Get("Allow")) + assert.Empty(t, w.Body.String()) + }) + } +} diff --git a/src/control-plane-services/event-ledger/cmd/api/startup/run_service.go b/src/control-plane-services/event-ledger/cmd/api/startup/run_service.go index badf43ab9..e3d102ce7 100644 --- a/src/control-plane-services/event-ledger/cmd/api/startup/run_service.go +++ b/src/control-plane-services/event-ledger/cmd/api/startup/run_service.go @@ -30,6 +30,7 @@ import ( "github.com/NVIDIA/nvcf/src/libraries/go/lib/pkg/nvkit/auth" "github.com/NVIDIA/nvcf/src/libraries/go/lib/pkg/nvkit/clients" + golibversion "github.com/NVIDIA/nvcf/src/libraries/go/lib/pkg/version" "github.com/golang-jwt/jwt/v5" "github.com/gorilla/handlers" "github.com/gorilla/mux" @@ -53,6 +54,14 @@ import ( "github.com/NVIDIA/nvcf/src/control-plane-services/event-ledger/internal/registrations" ) +// registerUnauthenticatedRoutes registers routes that must be reachable before +// (and regardless of) auth middleware: /health for liveness/readiness probes, +// and /info for build-version discovery (NVCF-10975). +func registerUnauthenticatedRoutes(router *mux.Router, server *service.Server) { + router.HandleFunc("/health", server.Health) + router.Handle("/info", golibversion.Handler()) +} + func runService(cfg config.Config) error { ctx := context.Background() @@ -149,7 +158,7 @@ func runService(cfg config.Config) error { router.Use(middleware.BodyLimitMiddleware(10 * 1024 * 1024)) // 10MB limit router.Use(metricsMiddleware) - router.HandleFunc("/health", server.Health) + registerUnauthenticatedRoutes(router, server) spanNameFormatter := func(operation string, r *http.Request) string { return r.Method + " " + operation // e.g., "GET /api/resource" diff --git a/src/control-plane-services/event-ledger/go.mod b/src/control-plane-services/event-ledger/go.mod index 1a9de39bd..4c441b732 100644 --- a/src/control-plane-services/event-ledger/go.mod +++ b/src/control-plane-services/event-ledger/go.mod @@ -54,7 +54,7 @@ require ( ) require ( - github.com/NVIDIA/nvcf/src/libraries/go/lib v0.0.0-20260710034659-973443ac16c3 + github.com/NVIDIA/nvcf/src/libraries/go/lib v0.0.0-20260728185909-afca4ec2fb26 github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/src/control-plane-services/event-ledger/go.sum b/src/control-plane-services/event-ledger/go.sum index 5c07c9bb0..821a73626 100644 --- a/src/control-plane-services/event-ledger/go.sum +++ b/src/control-plane-services/event-ledger/go.sum @@ -2,8 +2,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/NVIDIA/nvcf/src/libraries/go/lib v0.0.0-20260710034659-973443ac16c3 h1:pBymv2IQia2bswak0wHracVj2kdDXB5xcCgm3BVxfmg= -github.com/NVIDIA/nvcf/src/libraries/go/lib v0.0.0-20260710034659-973443ac16c3/go.mod h1:nj3yBW2weO0qzi1ML45tBqviLa2Y/KG9qCalxQuJVB8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -369,3 +367,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +github.com/NVIDIA/nvcf/src/libraries/go/lib v0.0.0-20260728185909-afca4ec2fb26 h1:P9OSmvy6MQPqfzRgaLl0XpvK5UYHAFtNnQQb+yk+6b0= +github.com/NVIDIA/nvcf/src/libraries/go/lib v0.0.0-20260728185909-afca4ec2fb26/go.mod h1:nj3yBW2weO0qzi1ML45tBqviLa2Y/KG9qCalxQuJVB8= From 6dbf40f77738a96cc839576b8fa528e1b45e172a Mon Sep 17 00:00:00 2001 From: priyaselvaganesan Date: Wed, 19 Aug 2026 15:55:15 -0700 Subject: [PATCH 2/4] fix(event-ledger): address review on /info observability and test hygiene Signed-off-by: priyaselvaganesan --- .../event-ledger/cmd/api/startup/info_test.go | 15 +++++------ .../cmd/api/startup/run_service.go | 25 ++++++++++++------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go b/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go index 5c2e67b74..4447c0f64 100644 --- a/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go +++ b/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go @@ -36,17 +36,18 @@ import ( // middleware, so an empty *service.Server is safe as long as /health is not // exercised. func TestRegisterUnauthenticatedRoutes_Info(t *testing.T) { - golibversion.Service = "nvcf-fnds-api" + prevService, prevVersion, prevHash := golibversion.Service, golibversion.Version, golibversion.GitHash + golibversion.Service = "nvcf-event-ledger" golibversion.Version = "test-1.0.0" golibversion.GitHash = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" t.Cleanup(func() { - golibversion.Service = "" - golibversion.Version = "" - golibversion.GitHash = "" + golibversion.Service = prevService + golibversion.Version = prevVersion + golibversion.GitHash = prevHash }) router := mux.NewRouter() - registerUnauthenticatedRoutes(router, &service.Server{}) + registerUnauthenticatedRoutes(router, &service.Server{}, func(h http.Handler) http.Handler { return h }) w := httptest.NewRecorder() r := httptest.NewRequest(http.MethodGet, "/info", nil) @@ -57,7 +58,7 @@ func TestRegisterUnauthenticatedRoutes_Info(t *testing.T) { var info map[string]string require.NoError(t, json.Unmarshal(w.Body.Bytes(), &info)) - assert.Equal(t, "nvcf-fnds-api", info["service"]) + assert.Equal(t, "nvcf-event-ledger", info["service"]) assert.Equal(t, "test-1.0.0", info["version"]) assert.Equal(t, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", info["commit"]) } @@ -66,7 +67,7 @@ func TestRegisterUnauthenticatedRoutes_Info(t *testing.T) { // /info return 405 with an Allow: GET header, as enforced by the go-lib handler. func TestRegisterUnauthenticatedRoutes_Info_RejectsNonGET(t *testing.T) { router := mux.NewRouter() - registerUnauthenticatedRoutes(router, &service.Server{}) + registerUnauthenticatedRoutes(router, &service.Server{}, func(h http.Handler) http.Handler { return h }) for _, method := range []string{ http.MethodPost, diff --git a/src/control-plane-services/event-ledger/cmd/api/startup/run_service.go b/src/control-plane-services/event-ledger/cmd/api/startup/run_service.go index e3d102ce7..b06b78702 100644 --- a/src/control-plane-services/event-ledger/cmd/api/startup/run_service.go +++ b/src/control-plane-services/event-ledger/cmd/api/startup/run_service.go @@ -56,10 +56,12 @@ import ( // registerUnauthenticatedRoutes registers routes that must be reachable before // (and regardless of) auth middleware: /health for liveness/readiness probes, -// and /info for build-version discovery (NVCF-10975). -func registerUnauthenticatedRoutes(router *mux.Router, server *service.Server) { +// and /info for build-version discovery. /info is wrapped with tracing and +// request logging via infoMiddleware; /health probes are left uninstrumented to +// avoid span and log spam. +func registerUnauthenticatedRoutes(router *mux.Router, server *service.Server, infoMiddleware func(http.Handler) http.Handler) { router.HandleFunc("/health", server.Health) - router.Handle("/info", golibversion.Handler()) + router.Handle("/info", infoMiddleware(golibversion.Handler())) } func runService(cfg config.Config) error { @@ -158,16 +160,21 @@ func runService(cfg config.Config) error { router.Use(middleware.BodyLimitMiddleware(10 * 1024 * 1024)) // 10MB limit router.Use(metricsMiddleware) - registerUnauthenticatedRoutes(router, server) - spanNameFormatter := func(operation string, r *http.Request) string { return r.Method + " " + operation // e.g., "GET /api/resource" } - authRouter := router.PathPrefix("").Subrouter() - authRouter.Use(otelmux.Middleware("deployment-stages", otelmux.WithSpanNameFormatter(spanNameFormatter))) - - // Initialize request logger mw + tracingMW := otelmux.Middleware("deployment-stages", otelmux.WithSpanNameFormatter(spanNameFormatter)) loggerMW := logging.LoggerMiddleware(logger) + + // /info runs through tracing and request logging (RED metrics already apply + // on the base router). /health is left uninstrumented to avoid probe span and + // log spam. + registerUnauthenticatedRoutes(router, server, func(h http.Handler) http.Handler { + return tracingMW(loggerMW(h)) + }) + + authRouter := router.PathPrefix("").Subrouter() + authRouter.Use(tracingMW) authRouter.Use(loggerMW) // If we're not using Policy, we need to handle scope checks locally in our middleware From 77405240c8dffa12337a10e29a7ea668319b94db Mon Sep 17 00:00:00 2001 From: priyaselvaganesan Date: Wed, 19 Aug 2026 17:07:33 -0700 Subject: [PATCH 3/4] test(event-ledger): assert /info is wrapped with the injected middleware Signed-off-by: priyaselvaganesan --- .../event-ledger/cmd/api/startup/info_test.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go b/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go index 4447c0f64..444eba7c0 100644 --- a/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go +++ b/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go @@ -46,13 +46,26 @@ func TestRegisterUnauthenticatedRoutes_Info(t *testing.T) { golibversion.GitHash = prevHash }) + // A recording middleware confirms registerUnauthenticatedRoutes actually + // wraps /info with the injected middleware (tracing + logging in production). + mwApplied := false + infoMiddleware := func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + mwApplied = true + w.Header().Set("X-Info-Middleware", "applied") + h.ServeHTTP(w, r) + }) + } + router := mux.NewRouter() - registerUnauthenticatedRoutes(router, &service.Server{}, func(h http.Handler) http.Handler { return h }) + registerUnauthenticatedRoutes(router, &service.Server{}, infoMiddleware) w := httptest.NewRecorder() r := httptest.NewRequest(http.MethodGet, "/info", nil) router.ServeHTTP(w, r) + assert.True(t, mwApplied, "/info should be wrapped with the injected middleware") + assert.Equal(t, "applied", w.Header().Get("X-Info-Middleware")) assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, "application/json", w.Header().Get("Content-Type")) From 36070b730a2887f55c64d2bc7e64809830159628 Mon Sep 17 00:00:00 2001 From: priyaselvaganesan Date: Thu, 20 Aug 2026 08:45:04 -0700 Subject: [PATCH 4/4] test(event-ledger): use NewRequestWithContext in info tests Signed-off-by: priyaselvaganesan --- .../event-ledger/cmd/api/startup/info_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go b/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go index 444eba7c0..c26a64d95 100644 --- a/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go +++ b/src/control-plane-services/event-ledger/cmd/api/startup/info_test.go @@ -61,7 +61,7 @@ func TestRegisterUnauthenticatedRoutes_Info(t *testing.T) { registerUnauthenticatedRoutes(router, &service.Server{}, infoMiddleware) w := httptest.NewRecorder() - r := httptest.NewRequest(http.MethodGet, "/info", nil) + r := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/info", nil) router.ServeHTTP(w, r) assert.True(t, mwApplied, "/info should be wrapped with the injected middleware") @@ -90,7 +90,7 @@ func TestRegisterUnauthenticatedRoutes_Info_RejectsNonGET(t *testing.T) { } { t.Run(method, func(t *testing.T) { w := httptest.NewRecorder() - r := httptest.NewRequest(method, "/info", nil) + r := httptest.NewRequestWithContext(t.Context(), method, "/info", nil) router.ServeHTTP(w, r) assert.Equal(t, http.StatusMethodNotAllowed, w.Code)