Files
zeekay 69b187636d Let Hanzo IAM answer who the caller is
o11y carried a second identity system: email/password sign-in, Google,
OIDC and SAML callbacks, self-registration, invites, reset-password
tokens, roles-on-users, SSO auth domains, and a pair of tokenizers that
minted and stored its own sessions. Hanzo IAM answers all of it, once,
at the edge. Two credential stores is one too many, and the one nobody
signs into is the one that rots.

What is left is Identity: WHO a request is, resolved from what the edge
asserted or from a service-account key, never issued here. The routes are
not merely unrouted — the handlers, stores, tables and templates behind
them are gone, and anonymous_test.go names each dead address so that
re-exempting one fails even though no route can come back.

The census moves with it: 366 addresses to 329, 353 typed ops to 319, ten
escape hatches to seven. zipdoc_gen.go loses the 34 descriptions whose
addresses left, because zip keys its doc registry by method and path, so
a description outliving its declaration attaches this module's prose to
whatever a host serves at that address. zipdoc_test.go pins that.

Cherry-picked from delete/o11y-identity 89854a68ae.
2026-09-08 23:24:35 -07:00

141 lines
6.2 KiB
Go

package o11yapiserver
import (
"log/slog"
"net/http"
"strings"
"testing"
"github.com/hanzoai/o11y/pkg/alertmanager"
"github.com/hanzoai/o11y/pkg/authz"
"github.com/hanzoai/o11y/pkg/factory"
"github.com/hanzoai/o11y/pkg/flagger"
"github.com/hanzoai/o11y/pkg/gateway"
"github.com/hanzoai/o11y/pkg/global"
"github.com/hanzoai/o11y/pkg/http/middleware"
"github.com/hanzoai/o11y/pkg/http/routing"
"github.com/hanzoai/o11y/pkg/modules/cloudintegration"
"github.com/hanzoai/o11y/pkg/modules/dashboard"
"github.com/hanzoai/o11y/pkg/modules/errortracking"
"github.com/hanzoai/o11y/pkg/modules/fields"
"github.com/hanzoai/o11y/pkg/modules/inframonitoring"
"github.com/hanzoai/o11y/pkg/modules/llmobs"
"github.com/hanzoai/o11y/pkg/modules/llmpricingrule"
"github.com/hanzoai/o11y/pkg/modules/metricreductionrule"
"github.com/hanzoai/o11y/pkg/modules/metricsexplorer"
"github.com/hanzoai/o11y/pkg/modules/organization"
"github.com/hanzoai/o11y/pkg/modules/preference"
"github.com/hanzoai/o11y/pkg/modules/promote"
"github.com/hanzoai/o11y/pkg/modules/rawdataexport"
"github.com/hanzoai/o11y/pkg/modules/rulestatehistory"
"github.com/hanzoai/o11y/pkg/modules/sentry"
"github.com/hanzoai/o11y/pkg/modules/serviceaccount"
"github.com/hanzoai/o11y/pkg/modules/spanmapper"
"github.com/hanzoai/o11y/pkg/modules/tracedetail"
"github.com/hanzoai/o11y/pkg/modules/user"
"github.com/hanzoai/o11y/pkg/querier"
"github.com/hanzoai/o11y/pkg/ruler"
"github.com/hanzoai/o11y/pkg/statsreporter"
"github.com/hanzoai/o11y/pkg/zeus"
"github.com/zap-proto/zip"
)
// THE API SERVER'S CENSUS.
//
// This package registers the larger half of the service's HTTP surface, and it
// registers it by calling 30 functions from one place. An uncalled func is legal
// Go, so a slice that stops being called compiles, passes every test it has, and
// serves nothing — which is exactly what this repo already shipped once, with 83
// typed ops present in the source and reaching no router at all. Only arithmetic
// catches that, so the arithmetic lives here.
//
// If this number moves, a route was added or removed and someone has to say
// which. It is not a target to satisfy by editing the constant.
//
// It was 233. Thirty-six routes left when the duplicate identity system did:
// twenty-four user routes (invites, passwords, reset tokens, member listing and
// editing, roles-on-users), seven session routes including the three /complete/*
// sign-in callbacks, and five SSO auth-domain routes. addSessionRoutes and
// addAuthDomainRoutes are not merely uncalled — the files are deleted, which is
// the only shape of removal this census can tell apart from the defect it
// exists to catch.
const wantAPIServerRoutes = 197
// every handler field satisfied by an embedded nil interface: the route table is
// a fact about REGISTRATION, and registration only takes method values off these
// — it never calls one. Standing up 33 real modules to count routes would test
// the modules, not the routes.
func censusProvider() *provider {
return &provider{
authzMiddleware: middleware.NewAuthZ(slog.Default(), nil, nil),
authzService: struct{ authz.AuthZ }{},
orgHandler: struct{ organization.Handler }{},
userHandler: struct{ user.Handler }{},
preferenceHandler: struct{ preference.Handler }{},
globalHandler: struct{ global.Handler }{},
promoteHandler: struct{ promote.Handler }{},
flaggerHandler: struct{ flagger.Handler }{},
dashboardModule: struct{ dashboard.Module }{},
dashboardHandler: struct{ dashboard.Handler }{},
metricsExplorerHandler: struct{ metricsexplorer.Handler }{},
metricReductionRuleHandler: struct{ metricreductionrule.Handler }{},
infraMonitoringHandler: struct{ inframonitoring.Handler }{},
gatewayHandler: struct{ gateway.Handler }{},
fieldsHandler: struct{ fields.Handler }{},
authzHandler: struct{ authz.Handler }{},
rawDataExportHandler: struct{ rawdataexport.Handler }{},
zeusHandler: struct{ zeus.Handler }{},
querierHandler: struct{ querier.Handler }{},
serviceAccountHandler: struct{ serviceaccount.Handler }{},
factoryHandler: struct{ factory.Handler }{},
cloudIntegrationHandler: struct{ cloudintegration.Handler }{},
ruleStateHistoryHandler: struct{ rulestatehistory.Handler }{},
spanMapperHandler: struct{ spanmapper.Handler }{},
alertmanagerHandler: struct{ alertmanager.Handler }{},
traceDetailHandler: struct{ tracedetail.Handler }{},
rulerHandler: struct{ ruler.Handler }{},
llmPricingRuleHandler: struct{ llmpricingrule.Handler }{},
llmObsHandler: struct{ llmobs.Handler }{},
errorTrackingHandler: struct{ errortracking.Handler }{},
sentryHandler: struct{ sentry.Handler }{},
statsHandler: struct{ statsreporter.Handler }{},
}
}
func TestEveryAPIServerRouteIsRegistered(t *testing.T) {
app := zip.New(zip.Config{DisableStartupMessage: true})
router := routing.New(app.Group(""), nil)
censusProvider().AddToRouter(router)
table := router.Table().Routes()
if len(table) != wantAPIServerRoutes {
t.Fatalf("AddToRouter registered %d routes, want %d", len(table), wantAPIServerRoutes)
}
// The table is what registration recorded; the router is what will answer.
// They must be the same set, or the census is measuring the wrong thing.
onRouter := 0
for _, route := range app.Fiber().GetRoutes(true) {
if route.Method == http.MethodHead || route.Method == http.MethodOptions {
continue
}
onRouter++
}
if onRouter != len(table) {
t.Fatalf("router holds %d routes, the table recorded %d", onRouter, len(table))
}
// Every route answers on one of the three public roots — the two faces and
// the ingest endpoint — and carries no leftover spelling from the tree this
// replaced.
for _, route := range table {
switch {
case strings.HasPrefix(route.Path, "/v1/o11y"):
case strings.HasPrefix(route.Path, "/v1/o11y/sentinel"):
case strings.HasPrefix(route.Path, "/v1/event"):
default:
t.Errorf("%s %s is on none of the public roots", route.Method, route.Path)
}
}
}