Documentation
¶
Overview ¶
pkg/gateway/apply/handler.go
POST /api/v1/apply
Pipeline: auth (handled by middleware) → decode body → SSA → translate response.
The Apply API does not duplicate admission logic. Webhooks enforce admission rules when enabled; the reconciler enforces them otherwise. This handler's only job is to accept a CR body, apply it via server-side apply, and return a structured result.
pkg/gateway/auth.go
Bearer token authentication for the Apply API.
At startup, LoadTokens resolves every token entry in the Katalog config:
- secretRef entries: read or self-bootstrap the Kubernetes Secret, then optionally rotate it if rotateAfter has elapsed.
- token entries: expand ${ENV_VAR} references.
The resolved values are stored in TokenSet, which the auth middleware uses to validate incoming Authorization: Bearer <token> headers.
Self-bootstrap and rotation reuse pkg/secrets helpers so the annotation-based rotation clock is identical to operator-managed secrets.
pkg/gateway/resources/handler.go
GET /api/v1/resources/{kind}/{namespace}/{name} — read one CR GET /api/v1/resources/{kind}/{namespace} — list CRs in a namespace DELETE /api/v1/resources/{kind}/{namespace}/{name} — delete a CR
These endpoints are for external callers (CI, Terraform, Slack bots) that need raw Kubernetes CR state without kubeconfig. The Control Center uses the runtime's /katalog/{crd}/cr/... endpoints (richer, informer-cached); these endpoints call the Kubernetes API directly via the dynamic client.
Deletion protection is enforced by the admission webhook when enabled. This handler does not duplicate that check — it issues the DELETE and lets Kubernetes reject it if the webhook blocks.
pkg/gateway/schema/handler.go
GET /api/v1/schema/{kind} — spec properties + idpFields for one CRD GET /api/v1/schema/ — catalog: list of all IDP-enabled CRDs
Only served for CRDs where idp.enabled: true. The Control Center uses these endpoints to render the [+ Create] form and the service catalog picker.
pkg/gateway/applyapi/server.go
ApplyAPIServer wires the Apply API handlers onto a health.HealthServer. Called from cmd/internal/gateway.go after the kubeclient is ready.
Route layout:
POST /api/v1/apply → applyHandler
GET /api/v1/resources/{kind}/{ns}[/{name}] → resourcesHandler
DELETE /api/v1/resources/{kind}/{ns}/{name} → resourcesHandler
GET /api/v1/schema/ → schemaHandler (service catalog — IDP-enabled CRDs)
GET /api/v1/schema/{kind} → schemaHandler (CRD schema + idpFields)
All routes are wrapped by AuthMiddleware before registration.
Index ¶
- func AuthMiddleware(ts *TokenSet, next http.Handler) http.Handler
- func TokenNameFromContext(ctx context.Context) string
- type ApplyAPIServer
- type ApplyResponse
- type ApplyViolation
- type CRDLookup
- type CatalogEntry
- type CatalogLister
- type CatalogResponse
- type KindMapper
- type Registrar
- type SchemaResponse
- type TokenSet
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AuthMiddleware ¶
AuthMiddleware returns an http.Handler that validates the Authorization: Bearer header against the TokenSet. On match, the token name is added to the request context. On mismatch, responds 401.
func TokenNameFromContext ¶
TokenNameFromContext returns the authenticated token name from the context. Empty string when the request was not authenticated (should not happen past middleware).
Types ¶
type ApplyAPIServer ¶
type ApplyAPIServer struct {
// contains filtered or unexported fields
}
ApplyAPIServer holds the resolved token set and wired handlers.
func NewApplyAPIServer ¶
func NewApplyAPIServer(ctx context.Context, kat *katalog.Katalog, kube kubeclient.KubeClient, ownNS string) (*ApplyAPIServer, error)
NewApplyAPIServer resolves all tokens (bootstrapping/rotating Secrets as needed) and returns a ready-to-register server.
func (*ApplyAPIServer) Register ¶
func (s *ApplyAPIServer) Register(reg Registrar)
Register wires all Apply API routes onto the given Registrar.
func (*ApplyAPIServer) ReloadTokens ¶
func (s *ApplyAPIServer) ReloadTokens(ctx context.Context) error
ReloadTokens re-resolves all secretRef tokens (recreating any missing secrets) and atomically replaces the in-memory TokenSet. Called by the housekeeper when a token secret is found to be missing during reconciliation.
type ApplyResponse ¶
type ApplyResponse struct {
// Accepted is true when the CR was applied (or would be applied, for dry runs) without error.
Accepted bool `json:"accepted"`
// DryRun is true when the request was a dry-run preview (?dryRun=true).
DryRun bool `json:"dryRun,omitempty"`
// Name is the CR name as stored in Kubernetes.
Name string `json:"name,omitempty"`
// Namespace is the CR namespace.
Namespace string `json:"namespace,omitempty"`
// Kind is the CR kind.
Kind string `json:"kind,omitempty"`
// APIVersion is the CR apiVersion.
APIVersion string `json:"apiVersion,omitempty"`
// ResourceVersion is the Kubernetes resourceVersion after apply.
ResourceVersion string `json:"resourceVersion,omitempty"`
// Message carries the rejection reason on Accepted=false.
Message string `json:"message,omitempty"`
// Warnings is a list of advisory messages returned by the admission webhook
// even when the request was accepted. Mirrors the kubectl warning experience.
Warnings []string `json:"warnings,omitempty"`
// Violations is a structured list of field-level errors from admission or
// validation. Populated when Accepted=false and kubernetes returns Status details.
Violations []ApplyViolation `json:"violations,omitempty"`
}
ApplyResponse is returned for every POST /api/v1/apply request.
type ApplyViolation ¶
type ApplyViolation struct {
// Field is the dot-notation path to the offending field (e.g. "spec.environment").
Field string `json:"field,omitempty"`
// Message is the human-readable error from Kubernetes or the admission webhook.
Message string `json:"message"`
// Severity is "error" (blocks apply) or "warning" (informational).
Severity string `json:"severity,omitempty"`
}
ApplyViolation is one field-level error returned from Kubernetes on a failed apply.
type CRDLookup ¶
CRDLookup returns the CRDEntry for a given kind, or nil if not found or not IDP-enabled.
type CatalogEntry ¶
type CatalogEntry struct {
Kind string `json:"kind"`
APIVersion string `json:"apiVersion"`
Category string `json:"category,omitempty"`
Description string `json:"description,omitempty"`
}
CatalogEntry is one row in the service catalog.
type CatalogLister ¶
CatalogLister returns all IDP-enabled CRDEntries.
type CatalogResponse ¶
type CatalogResponse struct {
Schemas []CatalogEntry `json:"schemas"`
}
CatalogResponse is returned by GET /api/v1/schema/ (no kind).
type KindMapper ¶
type KindMapper func(kind string) (schema.GroupVersionResource, error)
KindMapper resolves a kind name to a GroupVersionResource. Provided by the caller (gateway server) so this package stays pure HTTP.
type Registrar ¶
type Registrar interface {
Register(path string, handler http.HandlerFunc)
}
Registrar is the subset of health.HealthServer used here. Defined as an interface so the server package does not import pkg/health.
type SchemaResponse ¶
type SchemaResponse struct {
Kind string `json:"kind"`
APIVersion string `json:"apiVersion"`
Properties map[string]interface{} `json:"properties"`
Required []string `json:"required,omitempty"`
IDPFields map[string]orktypes.IDPFieldConfig `json:"idpFields,omitempty"`
IgnoreFields []string `json:"ignoreFields,omitempty"`
}
SchemaResponse is returned by GET /api/v1/schema/{kind}.
type TokenSet ¶
type TokenSet struct {
// contains filtered or unexported fields
}
TokenSet holds the resolved bearer token values keyed by token name. Lookups are O(n) over a small list — Apply API token counts are tiny.
func LoadTokens ¶
func LoadTokens(ctx context.Context, tokens []orktypes.ApplyAPIToken, kube kubeclient.KubeClient, ownNamespace string) (*TokenSet, error)
LoadTokens resolves all Apply API token entries from the Katalog config. secretRef entries are bootstrapped/rotated via the Kubernetes client. token entries are expanded from environment variables.