feat(discovery-server): add read-only API for the Control Plane#304
feat(discovery-server): add read-only API for the Control Plane#304
Conversation
Implement the Spy Server, a read-only HTTP API that provides a stable REST interface for querying Applications, ApiExposures, ApiSubscriptions, EventExposures, EventSubscriptions, and EventTypes. Key components: - OpenAPI specs (application, stargate, event) with merged uber-openapi - GoFiber HTTP server with JWT/OAuth2 security and scope-based ACL - Read-only controllers and CRD-to-API response mappers for 6 resources - Kubernetes-backed in-memory stores (BadgerDB) populated by informers - Deprecated write endpoints returning HTTP 410 Gone (directing to Rover) - Pagination, structured logging (zap), and Viper-based configuration
Co-authored-by: stefan-ctrl <stefan-ctrl@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds the new spy-server module: a read-only REST API (Fiber) for querying Control Plane resources with OpenAPI validation, ACL/security middleware, CRD→API mappers, in-memory (Badger) stores, and accompanying tests/fixtures/deploy manifests.
Changes:
- Introduces store container + pagination utilities and wires controllers/handlers/routes for 6 read-only resources (+ deprecated write endpoints returning 410).
- Adds mappers (including status mapping) and broad controller/mapper test coverage with fixtures and snapshots.
- Adds Kubernetes deployment/kustomize manifests and a new CI job to build/test the spy-server module.
Reviewed changes
Copilot reviewed 100 out of 118 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| spy-server/test/mocks/data/application.json | Adds Application CRD fixture for tests |
| spy-server/test/mocks/data/apiSubscription.json | Adds ApiSubscription CRD fixture for tests |
| spy-server/test/mocks/data/apiExposure.json | Adds ApiExposure CRD fixture for tests |
| spy-server/pkg/store/suite_test.go | Adds Ginkgo suite setup for store tests |
| spy-server/pkg/store/stores_secret_test.go | Adds SecretStore integration tests for ApiExposure/ApiSubscription |
| spy-server/pkg/store/stores.go | Adds Stores container + secret-wrapped stores + Badger/informer-backed store creation |
| spy-server/pkg/log/log.go | Adds zap/logr logger initialization |
| spy-server/internal/server/server.go | Adds server wiring: security middleware, OpenAPI validation, routes |
| spy-server/internal/server/eventtype_server.go | Adds EventType HTTP handlers |
| spy-server/internal/server/eventsubscription_server.go | Adds EventSubscription HTTP handlers |
| spy-server/internal/server/eventexposure_server.go | Adds EventExposure HTTP handlers |
| spy-server/internal/server/deprecated.go | Adds deprecated write endpoints returning 410 Gone |
| spy-server/internal/server/application_server.go | Adds Application HTTP handlers |
| spy-server/internal/server/apisubscription_server.go | Adds ApiSubscription HTTP handlers |
| spy-server/internal/server/apiexposure_server.go | Adds ApiExposure HTTP handlers |
| spy-server/internal/pagination/pagination.go | Adds cursor-drain + offset/limit pagination helpers |
| spy-server/internal/mapper/util_test.go | Adds tests for ID parsing/namespace parsing/label verification utilities |
| spy-server/internal/mapper/util.go | Adds namespace/id parsing utilities + application-label filtering helper |
| spy-server/internal/mapper/status/status_test.go | Adds tests for status condition→API status mapping |
| spy-server/internal/mapper/status/status.go | Adds Kubernetes conditions→API status mapping utilities |
| spy-server/internal/mapper/status/response_test.go | Adds tests for mapping status response timestamps/overall status |
| spy-server/internal/mapper/status/response.go | Adds mapper for ResourceStatusResponse |
| spy-server/internal/mapper/eventtype/out_test.go | Adds EventType out-mapper tests |
| spy-server/internal/mapper/eventtype/out.go | Adds EventType CRD→API mapper |
| spy-server/internal/mapper/eventsubscription/out_test.go | Adds EventSubscription out-mapper tests (incl. reference resolution) |
| spy-server/internal/mapper/eventsubscription/out.go | Adds EventSubscription CRD→API mapper |
| spy-server/internal/mapper/eventexposure/out_test.go | Adds EventExposure out-mapper tests (incl. fallbacks) |
| spy-server/internal/mapper/eventexposure/out.go | Adds EventExposure CRD→API mapper |
| spy-server/internal/mapper/application/out_test.go | Adds Application out-mapper tests |
| spy-server/internal/mapper/application/out.go | Adds Application CRD→API mapper |
| spy-server/internal/mapper/apisubscription/out.go | Adds ApiSubscription CRD→API mapper (security, gateway URL, approval, etc.) |
| spy-server/internal/mapper/apiexposure/out_test.go | Adds ApiExposure out-mapper tests |
| spy-server/internal/mapper/apiexposure/out.go | Adds ApiExposure CRD→API mapper (upstreams, security, traffic, etc.) |
| spy-server/internal/controller/suite_controller_test.go | Adds controller test harness with Fiber app + mock stores |
| spy-server/internal/controller/invalid_ids_test.go | Adds negative tests for malformed IDs / label mismatch behavior |
| spy-server/internal/controller/eventtype_test.go | Adds EventType controller endpoint tests + snapshots |
| spy-server/internal/controller/eventtype.go | Adds EventType controller implementation |
| spy-server/internal/controller/eventsubscription_test.go | Adds EventSubscription controller endpoint tests + snapshots |
| spy-server/internal/controller/eventsubscription.go | Adds EventSubscription controller implementation |
| spy-server/internal/controller/eventexposure_test.go | Adds EventExposure controller endpoint tests |
| spy-server/internal/controller/eventexposure.go | Adds EventExposure controller implementation |
| spy-server/internal/controller/deprecated_test.go | Adds tests ensuring deprecated write endpoints return 410 |
| spy-server/internal/controller/application_test.go | Adds Application controller endpoint tests + snapshots |
| spy-server/internal/controller/application.go | Adds Application controller implementation |
| spy-server/internal/controller/apisubscription_test.go | Adds ApiSubscription controller endpoint tests + snapshots |
| spy-server/internal/controller/apisubscription.go | Adds ApiSubscription controller implementation |
| spy-server/internal/controller/apiexposure_test.go | Adds ApiExposure controller endpoint tests |
| spy-server/internal/controller/apiexposure.go | Adds ApiExposure controller implementation |
| spy-server/internal/controller/snapshots/eventtype_test.snap | Adds snapshots for EventType endpoints |
| spy-server/internal/controller/snapshots/eventsubscription_test.snap | Adds snapshots for EventSubscription endpoints |
| spy-server/internal/controller/snapshots/application_test.snap | Adds snapshots for Application endpoints |
| spy-server/internal/controller/snapshots/apisubscription_test.snap | Adds snapshots for ApiSubscription endpoints |
| spy-server/internal/config/config.go | Adds Viper-based server config with defaults |
| spy-server/go.sum.license | Adds license metadata for go.sum |
| spy-server/go.mod | Adds spy-server Go module definition and dependencies |
| spy-server/docs/004_secret_manager/plan.md | Adds secret-manager integration plan |
| spy-server/docs/002_event/plan.md | Adds event resources plan (and notes current state) |
| spy-server/docs/001_application/plan.md | Adds application resource plan |
| spy-server/config/server/server.yaml | Adds Deployment/Service manifests for spy-server |
| spy-server/config/server/kustomization.yaml | Adds kustomize wiring for server resources |
| spy-server/config/rbac/service_account.yaml | Adds ServiceAccount for spy-server |
| spy-server/config/rbac/role_binding.yaml | Adds ClusterRoleBinding for spy-server |
| spy-server/config/rbac/role.yaml | Adds ClusterRole for read-only access to CRDs |
| spy-server/config/rbac/kustomization.yaml | Adds kustomize wiring for RBAC resources |
| spy-server/config/prometheus/monitor.yaml | Adds ServiceMonitor for metrics scraping |
| spy-server/config/prometheus/kustomization.yaml | Adds kustomize wiring for prometheus resources |
| spy-server/config/kustomization.yaml | Adds root kustomization referencing default |
| spy-server/config/ingress/kustomization.yaml | Adds kustomize wiring for ingress |
| spy-server/config/ingress/ingress.yaml | Adds Ingress manifest |
| spy-server/config/default/namespace_patch.yaml | Adds namespace label patch for secret-manager integration |
| spy-server/config/default/metrics_service.yaml | Adds dedicated metrics Service exposing port “http” |
| spy-server/config/default/kustomization.yaml | Adds default kustomize overlay with env configMap + patches |
| spy-server/config/default/ingress_host_patch.yaml | Adds patch to override ingress host |
| spy-server/config/default/deployment_patch.yaml | Adds projected SA token + trust bundle mounts for secret-manager |
| spy-server/cmd/main.go | Adds spy-server entrypoint wiring config/logging/stores/routes |
| spy-server/api/openapi-merge.json.license | Adds license metadata for openapi merge config |
| spy-server/api/openapi-merge.json | Adds OpenAPI merge config |
| spy-server/api/README.md | Adds API generation docs |
| spy-server/api/Makefile | Adds OpenAPI merge + codegen make target |
| spy-server/Makefile | Adds build/test targets for spy-server |
| common-server/pkg/store/secrets/obfuscator_test.go | Extends obfuscator tests to cover typed structs + nil handling |
| common-server/pkg/store/secrets/obfuscator.go | Adds JSON fallback obfuscation path for typed structs |
| .github/workflows/ci.yaml | Adds CI job for spy-server module |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| b, err := json.Marshal(obj) | ||
| if err == nil { | ||
| b, err = o.ReplaceAllFromBytes(ctx, b, jsonPaths) | ||
| if err != nil { | ||
| return nil, errors.Wrap(err, "failed to obfuscate from json") | ||
| } | ||
| err = json.Unmarshal(b, &obj) | ||
| if err != nil { | ||
| return nil, errors.Wrap(err, "failed to unmarshal obfuscated json") | ||
| } | ||
| return obj, nil | ||
| } |
There was a problem hiding this comment.
json.Unmarshal(b, &obj)unmarshals into aninterface{}and will decode intomap[string]any(not back into the original concrete struct type), so callers won’t get a typed result (and the newly added typed-struct test would fail). Prefer unmarshalling into a value of the same concrete type (e.g., ifobj` is a non-nil pointer, unmarshal into that pointer or into a freshly-allocated value of the same type and return it).
spy-server/internal/server/server.go
Outdated
| // RegisterRoutes sets up security middleware, OpenAPI validation, and all route handlers. | ||
| func (s *Server) RegisterRoutes(router fiber.Router) { | ||
| checkAccess := security.ConfigureSecurity(router, security.SecurityOpts{ | ||
| Enabled: true, |
There was a problem hiding this comment.
Security is always enabled here (Enabled: true) and ignores s.Config.Security.Enabled. This makes it impossible to disable auth/ACLs via configuration (e.g., local/dev), and contradicts the presence of security.enabled in config defaults. Use s.Config.Security.Enabled (and consider bypassing checkAccess usage when disabled) so runtime config actually controls behavior.
| Enabled: true, | |
| Enabled: s.Config.Security.Enabled, |
| scheme: http | ||
| selector: | ||
| matchLabels: | ||
| control-plane: spy-server |
There was a problem hiding this comment.
This ServiceMonitor selector matches both spy-server-service (port name https) and spy-server-metrics-service (port name http) because both carry control-plane: spy-server. Prometheus will attempt to scrape services without a matching port name and you’ll get scrape errors for the non-matching Service. Tighten the selector (e.g., add a label unique to the metrics service) or align the port naming so the selected Service(s) expose the requested port name.
| control-plane: spy-server | |
| control-plane: spy-server | |
| app.kubernetes.io/name: spy-server-metrics-service |
| - op: add | ||
| path: /spec/template/spec/volumes/0 | ||
| value: | ||
| name: secretmgr-token | ||
| projected: | ||
| sources: | ||
| - serviceAccountToken: | ||
| path: token | ||
| expirationSeconds: 600 | ||
| audience: secret-manager |
There was a problem hiding this comment.
Using JSON6902 add at /volumes/0 twice is brittle and order-dependent (the second add will be inserted before the first, and both shift any existing volume at index 0). Prefer appending with /volumes/- or switching to a strategic-merge patch keyed by name to make the patch stable even if the base Deployment’s volume ordering changes.
| - op: add | ||
| path: /spec/template/spec/volumes/0 | ||
| value: | ||
| name: trust-bundle | ||
| configMap: | ||
| name: secret-manager-trust-bundle |
There was a problem hiding this comment.
Using JSON6902 add at /volumes/0 twice is brittle and order-dependent (the second add will be inserted before the first, and both shift any existing volume at index 0). Prefer appending with /volumes/- or switching to a strategic-merge patch keyed by name to make the patch stable even if the base Deployment’s volume ordering changes.
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
ron96g
left a comment
There was a problem hiding this comment.
Cant approve as I am also author but just a nitpick comment ;)
|
|
||
| # Discovery Server | ||
|
|
||
| The Discovery Server (also known as the **Stargate API**) is a read-only HTTP API for the TARDIS Control Plane. It provides a stable, user-facing REST interface for querying the state of Applications, ApiExposures, ApiSubscriptions, EventExposures, EventSubscriptions, and EventTypes. |
There was a problem hiding this comment.
[nitpick] should avoid internal product names in docs "TARDIS".
Implement the Spy Server, a read-only HTTP API that provides a stable REST interface for querying Applications, ApiExposures, ApiSubscriptions, EventExposures, EventSubscriptions, and EventTypes.
Key components: