handler

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 6, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Overview

Package handler provides HTTP request handling for Heimdall.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FilterAlertsResponse

func FilterAlertsResponse(ctx context.Context, body []byte, matchers []*labels.Matcher) ([]byte, error)

FilterAlertsResponse filters a /api/v1/alerts response, keeping only alerts whose labels match ALL enforced matchers. If matchers is nil or empty, the body is returned unchanged (bypass fast path).

func FilterRulesResponse

func FilterRulesResponse(ctx context.Context, body []byte, matchers []*labels.Matcher) ([]byte, error)

FilterRulesResponse filters a /api/v1/rules response, keeping only rules whose labels match ALL enforced matchers. Empty groups are removed. If matchers is nil or empty, the body is returned unchanged (bypass fast path).

func JWTMiddleware

func JWTMiddleware(cfg config.JWTConfig) gin.HandlerFunc

JWTMiddleware creates a Gin middleware that validates JWTs. It extracts sub → user_id and the configured groups claim → groups. Any validation failure results in a 401.

func MatchLabels

func MatchLabels(ruleLabels map[string]string, enforced []*labels.Matcher) bool

MatchLabels checks if ALL enforced matchers match against the given label set.

func MetricsMiddleware

func MetricsMiddleware(m *Metrics) gin.HandlerFunc

MetricsMiddleware records request count and duration for every HTTP request.

func NormalizeFilters

func NormalizeFilters(filters []string) string

NormalizeFilters deduplicates, sorts, and serializes filters deterministically for use as a canonical grouping key in fan-out.

func PanicRecoveryMiddleware

func PanicRecoveryMiddleware() gin.HandlerFunc

PanicRecoveryMiddleware catches panics in request handling and converts them to 500 responses via RespondError.

func ParseFilters

func ParseFilters(filters []string) ([]*labels.Matcher, error)

ParseFilters parses filter strings (e.g., `env="prod"`) into label matchers.

func RespondError

func RespondError(c *gin.Context, status int, code, message string)

RespondError writes a standardized JSON error response. If the request context is canceled or deadline exceeded, it automatically transforms the response to a 499 Client Closed Request mapping to enforce invariants.

func RewriteMatchParams

func RewriteMatchParams(ctx context.Context, matchParams []string, matchers []*labels.Matcher) ([]string, error)

RewriteMatchParams rewrites a list of match[] parameters (used by /api/v1/series).

func RewriteQuery

func RewriteQuery(ctx context.Context, query string, matchers []*labels.Matcher) (string, error)

RewriteQuery parses a PromQL query, injects label matchers into every VectorSelector in the AST, and returns the re-rendered query string. This is the ONLY way to inject filters — never use regex or string replacement.

func SetIdentity

func SetIdentity(ctx context.Context, id *Identity) context.Context

SetIdentity stores the identity in the request context.

func TracingMiddleware

func TracingMiddleware() gin.HandlerFunc

TracingMiddleware extracts traceparent, starts a root span, and attaches trace_id and span_id to all slog logs.

Types

type Action

type Action string

Action defines an authorization action evaluated against OPA.

const (
	ActionRead        Action = "read"
	ActionWrite       Action = "write"
	ActionRulesRead   Action = "rules:read"
	ActionRulesWrite  Action = "rules:write"
	ActionAlertsRead  Action = "alerts:read"
	ActionAlertsWrite Action = "alerts:write" // reserved for future use
)

Supported Heimdall proxy actions.

type Alert

type Alert struct {
	Labels      map[string]string `json:"labels"`
	Annotations map[string]string `json:"annotations,omitempty"`
	State       string            `json:"state"`
	ActiveAt    string            `json:"activeAt,omitempty"`
	Value       string            `json:"value,omitempty"`
}

Alert represents a single alert.

type AlertsData

type AlertsData struct {
	Alerts []Alert `json:"alerts"`
}

AlertsData holds the alerts from an alerts response.

type AlertsResponse

type AlertsResponse struct {
	Status string     `json:"status"`
	Data   AlertsData `json:"data"`
}

AlertsResponse represents the Prometheus /api/v1/alerts response.

type CachedTenantLister

type CachedTenantLister struct {
	// contains filtered or unexported fields
}

CachedTenantLister adds an in-memory TTL cache to an underlying TenantLister and reports cache hits and misses via the metrics engine.

func NewCachedTenantLister

func NewCachedTenantLister(underlying TenantLister, ttl time.Duration, metrics *Metrics) *CachedTenantLister

NewCachedTenantLister wraps a TenantLister with a TTL cache and metrics tracking.

func (*CachedTenantLister) ListTenantIDs

func (c *CachedTenantLister) ListTenantIDs(ctx context.Context) ([]string, error)

ListTenantIDs returns tenant IDs from the cache if still valid; otherwise refreshes from the underlying lister.

type ErrorResponse

type ErrorResponse struct {
	Error string `json:"error"`
	Code  string `json:"code"`
}

ErrorResponse is the standard JSON error envelope. All HTTP errors MUST use this format.

type FanOutEngine

type FanOutEngine struct {
	// contains filtered or unexported fields
}

FanOutEngine orchestrates multi-tenant fan-out with bounded concurrency.

func NewFanOutEngine

func NewFanOutEngine(opaClient OPAClient, cfg config.MimirConfig, fanOutCfg config.FanOutConfig, transport http.RoundTripper, metrics *Metrics) *FanOutEngine

NewFanOutEngine creates a new FanOutEngine with the given configuration.

func (*FanOutEngine) AuthorizeWrite

func (fe *FanOutEngine) AuthorizeWrite(ctx context.Context, identity *Identity, tenantIDs []string, action Action) (string, error)

AuthorizeWrite evaluates OPA for each tenant for write actions. Returns the denied tenant ID and error, or empty string and nil on success.

func (*FanOutEngine) Dispatch

func (fe *FanOutEngine) Dispatch(ctx context.Context, groups []filterGroup, originalReq *http.Request, action Action) ([]byte, int, error)

Dispatch sends parallel upstream requests to Mimir for each filter group, using bounded concurrency. Returns merged response body.

func (*FanOutEngine) EvaluateTenants

func (fe *FanOutEngine) EvaluateTenants(ctx context.Context, identity *Identity, tenantIDs []string, action Action, resource string) ([]filterGroup, error)

EvaluateTenants runs OPA authorization for each tenant and returns allowed tenants grouped by their canonical filter signature for native federation.

func (*FanOutEngine) ForwardWrite

func (fe *FanOutEngine) ForwardWrite(ctx context.Context, tenantIDs []string, originalReq *http.Request) ([]byte, int, error)

ForwardWrite forwards a write request byte-for-byte to the upstream Mimir. No body inspection, no PromQL parsing, no filter injection — authorize only.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler holds the dependencies for HTTP request handling.

func NewHandler

func NewHandler(cfg *config.Config, fanOut *FanOutEngine, db *gorm.DB) *Handler

NewHandler creates a new Handler.

func (*Handler) RegisterRoutes

func (h *Handler) RegisterRoutes(r *gin.Engine, m *Metrics)

RegisterRoutes sets up the Gin router with all middleware and routes.

func (*Handler) WithTenantLister

func (h *Handler) WithTenantLister(tl TenantLister)

WithTenantLister enables automatic tenant resolution for read actions when the X-Scope-OrgID header is absent.

type Identity

type Identity struct {
	UserID string
	Groups []string
}

Identity holds the user identity extracted from the JWT.

func GetIdentity

func GetIdentity(ctx context.Context) (*Identity, bool)

GetIdentity retrieves the identity from the request context.

func MustGetIdentity

func MustGetIdentity(c *gin.Context) *Identity

MustGetIdentity retrieves the identity from the HTTP request context or panics.

type Metrics

type Metrics struct {
	// contains filtered or unexported fields
}

Metrics holds all Prometheus metrics for Heimdall.

func NewMetrics

func NewMetrics() (*Metrics, error)

NewMetrics registers all Heimdall metrics with the OTel meter provider.

func (*Metrics) RecordBundleRebuild

func (m *Metrics) RecordBundleRebuild(ctx context.Context)

RecordBundleRebuild increments the bundle rebuild counter (Rule 235).

func (*Metrics) UpdateActiveTenants

func (m *Metrics) UpdateActiveTenants(ctx context.Context, count int64)

UpdateActiveTenants sets the active tenants gauge (Rule 234).

type OPAClient

type OPAClient interface {
	Evaluate(ctx context.Context, input OPAInput) (*OPAResult, error)
}

OPAClient defines the interface for communicating with the OPA REST API.

func NewOPAClient

func NewOPAClient(baseURL, policyPath string, timeout time.Duration, transport http.RoundTripper, metrics *Metrics) OPAClient

NewOPAClient creates a new OPA client. If transport is non-nil, it is used for authentication injection.

type OPAInput

type OPAInput struct {
	UserID   string   `json:"user_id"`
	Groups   []string `json:"groups"`
	TenantID string   `json:"tenant_id"`
	Resource string   `json:"resource"`
	Action   Action   `json:"action"`
}

OPAInput is the input document sent to OPA for evaluation.

type OPAResponse

type OPAResponse struct {
	Result OPAResult `json:"result"`
}

OPAResponse wraps the OPA REST API response.

type OPAResult

type OPAResult struct {
	Allow             bool     `json:"allow"`
	EffectiveFilters  []string `json:"effective_filters"`
	AccessibleTenants []string `json:"accessible_tenants"`
}

OPAResult is the result from OPA policy evaluation.

type Rule

type Rule struct {
	Name        string            `json:"name"`
	Query       string            `json:"query,omitempty"`
	Duration    float64           `json:"duration,omitempty"`
	Labels      map[string]string `json:"labels,omitempty"`
	Annotations map[string]string `json:"annotations,omitempty"`
	State       string            `json:"state,omitempty"`
	Health      string            `json:"health,omitempty"`
	Type        string            `json:"type"`

	// Additional fields preserved as raw JSON
	Alerts json.RawMessage `json:"alerts,omitempty"`
}

Rule represents a single rule within a group.

type RuleGroup

type RuleGroup struct {
	Name  string `json:"name"`
	File  string `json:"file"`
	Rules []Rule `json:"rules"`
}

RuleGroup represents a group of rules.

type RulesData

type RulesData struct {
	Groups []RuleGroup `json:"groups"`
}

RulesData holds the groups from a rules response.

type RulesResponse

type RulesResponse struct {
	Status string    `json:"status"`
	Data   RulesData `json:"data"`
}

RulesResponse represents the Prometheus /api/v1/rules response.

type TenantLister

type TenantLister interface {
	ListTenantIDs(ctx context.Context) ([]string, error)
}

TenantLister resolves all known tenant IDs. Used for auto-resolving tenants on read requests when X-Scope-OrgID is absent.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL