debug

package
v0.1.0-alpha.9 Latest Latest
Warning

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

Go to latest
Published: Dec 30, 2025 License: Apache-2.0 Imports: 10 Imported by: 0

README

pkg/controller/debug

Controller-specific debug variable implementations for introspection HTTP server.

Overview

This package provides controller-specific implementations of the generic pkg/introspection.Var interface, exposing internal controller state via HTTP debug endpoints.

Use cases:

  • Production debugging without logs
  • Acceptance testing with state verification
  • Operational visibility into controller internals
  • Development troubleshooting

Installation

import "haptic/pkg/controller/debug"

Quick Start

package main

import (
    "context"
    "haptic/pkg/controller/debug"
    "haptic/pkg/introspection"
)

func main() {
    // Create introspection registry
    registry := introspection.NewRegistry()

    // Create state provider (controller implements this)
    stateCache := NewStateCache(bus, resourceWatcher)
    go stateCache.Start(ctx)

    // Create event buffer
    eventBuffer := debug.NewEventBuffer(1000, bus)
    go eventBuffer.Start(ctx)

    // Register all debug variables
    debug.RegisterVariables(registry, stateCache, eventBuffer)

    // Start HTTP server
    server := introspection.NewServer(":6060", registry)
    go server.Start(ctx)

    // Access via:
    // curl http://localhost:6060/debug/vars/config
    // curl http://localhost:6060/debug/vars/rendered
}

API Reference

StateProvider Interface

Interface for accessing controller internal state in a thread-safe manner.

type StateProvider interface {
    GetConfig() (*config.Config, string, error)
    GetCredentials() (*config.Credentials, string, error)
    GetRenderedConfig() (string, time.Time, error)
    GetAuxiliaryFiles() (*dataplane.AuxiliaryFiles, time.Time, error)
    GetResourceCounts() (map[string]int, error)
    GetResourcesByType(resourceType string) ([]interface{}, error)
}

Implementation: Controller implements this via StateCache component that subscribes to events and caches state.

Thread Safety: All methods must be thread-safe (called from HTTP handlers).

GetConfig
GetConfig() (*config.Config, string, error)

Returns the current validated configuration and its version (ConfigMap resource version).

Returns:

  • config: The current Config struct
  • version: ConfigMap resource version (e.g., "12345")
  • error: Non-nil if config not loaded yet

Example:

cfg, version, err := provider.GetConfig()
if err != nil {
    return nil, fmt.Errorf("config not ready: %w", err)
}
fmt.Printf("Config version: %s\n", version)
GetCredentials
GetCredentials() (*config.Credentials, string, error)

Returns the current credentials and their version (Secret resource version).

Security: Debug variables should NOT expose actual credential values.

Returns:

  • creds: Credentials struct
  • version: Secret resource version
  • error: Non-nil if credentials not loaded yet
GetRenderedConfig
GetRenderedConfig() (string, time.Time, error)

Returns the most recently rendered HAProxy configuration and when it was rendered.

Returns:

  • config: Rendered HAProxy config as string
  • timestamp: When this config was rendered
  • error: Non-nil if no config rendered yet
GetAuxiliaryFiles
GetAuxiliaryFiles() (*dataplane.AuxiliaryFiles, time.Time, error)

Returns the most recently used auxiliary files (SSL certificates, map files, general files).

Returns:

  • auxFiles: AuxiliaryFiles struct with SSL, maps, general files
  • timestamp: When these files were last used
  • error: Non-nil if no files cached yet
GetResourceCounts
GetResourceCounts() (map[string]int, error)

Returns a map of resource type → count.

Returns:

{
    "ingresses": 5,
    "services": 12,
    "haproxy-pods": 2
}
GetResourcesByType
GetResourcesByType(resourceType string) ([]interface{}, error)

Returns all resources of a specific type.

Parameters:

  • resourceType: Key from GetResourceCounts() (e.g., "ingresses")

Returns:

  • resources: Slice of resource objects
  • error: Non-nil if resource type not found
Debug Variables
ConfigVar

Exposes current controller configuration.

type ConfigVar struct {
    provider StateProvider
}

Endpoint: GET /debug/vars/config

Response:

{
  "config": {
    "templates": {
      "main": "global\n  maxconn {{ maxconn }}\n..."
    },
    "watched_resources": [...]
  },
  "version": "12345",
  "updated": "2025-01-15T10:30:45Z"
}

JSONPath Examples:

# Get just the version
curl 'http://localhost:6060/debug/vars/config?field={.version}'

# Get template names
curl 'http://localhost:6060/debug/vars/config?field={.config.templates}'
CredentialsVar

Exposes credential metadata (NOT actual passwords).

type CredentialsVar struct {
    provider StateProvider
}

Endpoint: GET /debug/vars/credentials

Response:

{
  "version": "67890",
  "updated": "2025-01-15T10:30:45Z",
  "has_dataplane_creds": true
}

Security: Does NOT expose actual username/password values.

RenderedVar

Exposes most recently rendered HAProxy configuration.

type RenderedVar struct {
    provider StateProvider
}

Endpoint: GET /debug/vars/rendered

Response:

{
  "config": "global\n  maxconn 2000\n  log stdout local0\n\ndefaults\n...",
  "timestamp": "2025-01-15T10:30:45Z",
  "size": 4567
}

Usage:

# Get full rendered config
curl http://localhost:6060/debug/vars/rendered

# Get just the config text
curl 'http://localhost:6060/debug/vars/rendered?field={.config}'

# Get just the timestamp
curl 'http://localhost:6060/debug/vars/rendered?field={.timestamp}'
AuxFilesVar

Exposes auxiliary files used in last deployment.

type AuxFilesVar struct {
    provider StateProvider
}

Endpoint: GET /debug/vars/auxfiles

Response:

{
  "files": {
    "ssl_certificates": [
      {
        "name": "tls-cert",
        "content": "-----BEGIN CERTIFICATE-----\n...",
        "path": "/etc/haproxy/ssl/tls-cert.pem"
      }
    ],
    "map_files": [...],
    "general_files": [...]
  },
  "timestamp": "2025-01-15T10:30:45Z",
  "summary": {
    "ssl_count": 2,
    "map_count": 1,
    "general_count": 3
  }
}
ResourcesVar

Exposes resource counts by type.

type ResourcesVar struct {
    provider StateProvider
}

Endpoint: GET /debug/vars/resources

Response:

{
  "ingresses": 5,
  "services": 12,
  "haproxy-pods": 2
}

Usage:

# Get all resource counts
curl http://localhost:6060/debug/vars/resources

# Get specific resource type count
curl 'http://localhost:6060/debug/vars/resources?field={.ingresses}'
EventsVar

Exposes recent events from event buffer.

type EventsVar struct {
    buffer       *EventBuffer
    defaultLimit int
}

Endpoint: GET /debug/vars/events

Response (array of recent events):

[
  {
    "timestamp": "2025-01-15T10:30:45Z",
    "type": "config.validated",
    "summary": "config.validated"
  },
  {
    "timestamp": "2025-01-15T10:30:46Z",
    "type": "reconciliation.triggered",
    "summary": "reconciliation.triggered"
  }
]

Default Limit: 100 events (configurable via EventsVar.defaultLimit)

FullStateVar

Exposes all controller state in a single response.

type FullStateVar struct {
    provider    StateProvider
    eventBuffer *EventBuffer
}

Endpoint: GET /debug/vars/state

Warning: Returns large response containing all state. Use with caution.

Response:

{
  "config": {
    "config": {...},
    "version": "12345"
  },
  "rendered": {
    "config": "...",
    "timestamp": "2025-01-15T10:30:45Z"
  },
  "auxfiles": {
    "files": {...},
    "timestamp": "2025-01-15T10:30:45Z"
  },
  "resources": {
    "ingresses": 5,
    "services": 12
  },
  "recent_events": [...],
  "snapshot_time": "2025-01-15T10:31:00Z"
}

Prefer: Use specific variables or JSONPath field selection instead of full state dump.

EventBuffer

Ring buffer for tracking recent events independently of EventCommentator.

type EventBuffer struct {
    buffer *ringbuffer.RingBuffer[Event]
    bus    *events.EventBus
}

func NewEventBuffer(size int, bus *events.EventBus) *EventBuffer

Creates a new event buffer with the specified capacity.

Parameters:

  • size: Maximum number of events to store (e.g., 1000)
  • bus: EventBus to subscribe to

Example:

eventBuffer := debug.NewEventBuffer(1000, bus)
go eventBuffer.Start(ctx)
Start
func (eb *EventBuffer) Start(ctx context.Context) error

Starts collecting events from the EventBus. Blocks until context is cancelled.

Usage:

go eventBuffer.Start(ctx)
GetLast
func (eb *EventBuffer) GetLast(n int) []Event

Returns the last N events in chronological order (oldest first).

Example:

recent := eventBuffer.GetLast(100)
for _, event := range recent {
    fmt.Printf("%s: %s\n", event.Timestamp, event.Type)
}
GetAll
func (eb *EventBuffer) GetAll() []Event

Returns all events currently in the buffer.

Len
func (eb *EventBuffer) Len() int

Returns the current number of events in the buffer.

Event Type

Simplified event representation for debug purposes.

type Event struct {
    Timestamp time.Time   `json:"timestamp"`
    Type      string      `json:"type"`
    Summary   string      `json:"summary"`
    Details   interface{} `json:"details,omitempty"`
}

Fields:

  • Timestamp: When the event occurred
  • Type: Event type string (e.g., "config.validated")
  • Summary: Human-readable summary
  • Details: Optional additional information (currently nil)
RegisterVariables
func RegisterVariables(
    registry *introspection.Registry,
    provider StateProvider,
    eventBuffer *EventBuffer,
)

Registers all controller debug variables with the introspection registry.

Registered Variables:

  • config: ConfigVar
  • credentials: CredentialsVar
  • rendered: RenderedVar
  • auxfiles: AuxFilesVar
  • resources: ResourcesVar
  • events: EventsVar (last 100 events)
  • state: FullStateVar (full dump)
  • uptime: Func (controller uptime)

Example:

registry := introspection.NewRegistry()
eventBuffer := debug.NewEventBuffer(1000, bus)
go eventBuffer.Start(ctx)

debug.RegisterVariables(registry, stateCache, eventBuffer)

server := introspection.NewServer(":6060", registry)
go server.Start(ctx)

HTTP Endpoints

List All Variables
GET /debug/vars

Lists all registered debug variable paths.

Response:

{
  "vars": [
    "config",
    "credentials",
    "rendered",
    "auxfiles",
    "resources",
    "events",
    "state",
    "uptime"
  ]
}
Get Variable
GET /debug/vars/{path}

Retrieves a specific variable's value.

Examples:

curl http://localhost:6060/debug/vars/config
curl http://localhost:6060/debug/vars/rendered
curl http://localhost:6060/debug/vars/resources
Get Variable Field
GET /debug/vars/{path}?field={.jsonpath}

Extracts a specific field using JSONPath (kubectl syntax).

Examples:

# Get config version
curl 'http://localhost:6060/debug/vars/config?field={.version}'

# Get rendered config size
curl 'http://localhost:6060/debug/vars/rendered?field={.size}'

# Get ingress count
curl 'http://localhost:6060/debug/vars/resources?field={.ingresses}'

JSONPath Syntax: See https://kubernetes.io/docs/reference/kubectl/jsonpath/

Access from Kubernetes

For controller running in Kubernetes pod:

# Forward debug port from pod
kubectl <cmd> haptic-xxx 6060:6060

# Access endpoints
curl http://localhost:6060/debug/vars/config
curl http://localhost:6060/debug/vars/rendered
curl http://localhost:6060/debug/pprof/heap  # Go profiling

Security Considerations

  1. Credentials: CredentialsVar exposes metadata only, NOT actual passwords
  2. Bind Address: Server binds to 0.0.0.0 (all interfaces) for kubectl port-forward compatibility
  3. Access Control: No built-in authentication - use kubectl port-forward or reverse proxy
  4. Large Responses: FullStateVar can return very large responses - use with caution

Best Practices:

  • Use kubectl port-forward for production access
  • Don't expose debug port directly to internet
  • Prefer specific variables over full state dump
  • Use JSONPath field selection to reduce response size

Examples

Controller Integration

See pkg/controller/controller.go:

// Stage 6: Debug infrastructure
registry := introspection.NewRegistry()
stateCache := NewStateCache(bus, resourceWatcher)
go stateCache.Start(ctx)

eventBuffer := debug.NewEventBuffer(1000, bus)
go eventBuffer.Start(ctx)

debug.RegisterVariables(registry, stateCache, eventBuffer)

if debugPort > 0 {
    server := introspection.NewServer(fmt.Sprintf(":%d", debugPort), registry)
    go server.Start(ctx)
}
Acceptance Testing

See tests/acceptance/debug_client.go:

// Create debug client
debugClient := NewDebugClient(cfg.Client().RESTConfig(), pod, 6060)
debugClient.Start(ctx)

// Get current config
config, err := debugClient.GetConfig(ctx)
require.NoError(t, err)

// Wait for specific config version
err = debugClient.WaitForConfigVersion(ctx, "v2", 30*time.Second)
require.NoError(t, err)

// Verify rendered config contains expected values
rendered, err := debugClient.GetRenderedConfig(ctx)
require.NoError(t, err)
assert.Contains(t, rendered, "maxconn 4000")

License

See main repository for license information.

Documentation

Overview

Package debug provides controller-specific debug variable implementations.

This package integrates the generic pkg/introspection infrastructure with controller-specific state. It defines:

  • StateProvider interface for accessing controller internal state
  • Var implementations (ConfigVar, RenderedVar, etc.)
  • Event buffer for tracking recent events
  • Registration logic for publishing variables

The controller implements StateProvider and provides thread-safe access to its internal state (config, rendered output, resources, etc.).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RegisterEventsHandler

func RegisterEventsHandler(server *introspection.Server, eventBuffer *EventBuffer)

RegisterEventsHandler registers the /debug/events endpoint with correlation query support.

This handler supports:

  • GET /debug/events - Returns last 100 events
  • GET /debug/events?limit=N - Returns last N events
  • GET /debug/events?correlation_id=<id> - Returns events with matching correlation ID

Example:

debug.RegisterEventsHandler(server, eventBuffer)
go server.Start(ctx)

func RegisterVariables

func RegisterVariables(
	registry *introspection.Registry,
	provider StateProvider,
	eventBuffer *EventBuffer,
)

RegisterVariables registers all controller debug variables with the registry.

This function should be called during controller initialization, after components are set up but before the debug server starts.

Registered variables:

  • config: Current controller configuration
  • credentials: Credential metadata (not actual values)
  • rendered: Last rendered HAProxy config
  • auxfiles: Auxiliary files (SSL, maps, etc.)
  • resources: Resource counts by type
  • events: Recent events (default: last 100)
  • state: Full state dump (use carefully - large response)
  • uptime: Time since controller started
  • pipeline: Reconciliation pipeline status (trigger, render, validate, deploy)
  • validated: Last successfully validated HAProxy config
  • errors: Aggregated error summary across all phases

Example:

registry := introspection.NewRegistry()
eventBuffer := debug.NewEventBuffer(1000, bus)
debug.RegisterVariables(registry, controller, eventBuffer)

server := introspection.NewServer(":6060", registry)
go server.Start(ctx)

Types

type AuxFilesVar

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

AuxFilesVar exposes the auxiliary files used in the last deployment.

Returns a JSON object containing:

  • files: the AuxiliaryFiles struct with SSL certs, maps, general files
  • timestamp: when these files were last used
  • summary: counts of each file type

Example response:

{
  "files": {
    "ssl_certificates": [...],
    "map_files": [...],
    "general_files": [...]
  },
  "timestamp": "2025-01-15T10:30:45Z",
  "summary": {
    "ssl_count": 2,
    "map_count": 1,
    "general_count": 3
  }
}

func (*AuxFilesVar) Get

func (v *AuxFilesVar) Get() (interface{}, error)

Get implements introspection.Var.

type ComponentStatus

type ComponentStatus struct {
	// Running indicates if the component is currently active
	Running bool `json:"running"`

	// LastSeen is the timestamp of the last activity from this component
	LastSeen time.Time `json:"last_seen"`

	// ErrorRate is the percentage of errors (0.0 to 1.0)
	// Optional - may be 0 if not tracked
	ErrorRate float64 `json:"error_rate,omitempty"`

	// Details provides additional component-specific information
	// Optional - may be nil
	Details map[string]interface{} `json:"details,omitempty"`
}

ComponentStatus represents the status of a controller component.

Used by GetComponentStatus() to provide insight into component health.

type ConfigVar

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

ConfigVar exposes the current controller configuration.

Returns a JSON object containing:

  • config: the full Config struct
  • version: the ConfigMap resource version
  • updated: timestamp when the config was last updated

Example response:

{
  "config": {
    "templates": {"main": "..."},
    "watched_resources": [...]
  },
  "version": "v123",
  "updated": "2025-01-15T10:30:45Z"
}

func (*ConfigVar) Get

func (v *ConfigVar) Get() (interface{}, error)

Get implements introspection.Var.

type CredentialsVar

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

CredentialsVar exposes the current controller credentials.

Returns a JSON object containing:

  • version: the Secret resource version
  • updated: timestamp when credentials were last updated

Note: Does NOT expose actual credential values for security.

Example response:

{
  "version": "v456",
  "updated": "2025-01-15T10:30:45Z",
  "has_dataplane_creds": true
}

func (*CredentialsVar) Get

func (v *CredentialsVar) Get() (interface{}, error)

Get implements introspection.Var.

type DeploymentStatus

type DeploymentStatus struct {
	Status             string           `json:"status"` // "succeeded" | "failed" | "skipped" | "pending"
	Reason             string           `json:"reason,omitempty"`
	Timestamp          time.Time        `json:"timestamp"`
	DurationMs         int64            `json:"duration_ms,omitempty"`
	EndpointsTotal     int              `json:"endpoints_total"`
	EndpointsSucceeded int              `json:"endpoints_succeeded"`
	EndpointsFailed    int              `json:"endpoints_failed"`
	FailedEndpoints    []FailedEndpoint `json:"failed_endpoints,omitempty"`
}

DeploymentStatus represents the deployment phase status.

type ErrorInfo

type ErrorInfo struct {
	Timestamp time.Time `json:"timestamp"`
	Errors    []string  `json:"errors"`
}

ErrorInfo contains details about a specific error.

type ErrorSummary

type ErrorSummary struct {
	ConfigParseError       *ErrorInfo  `json:"config_parse_error,omitempty"`
	TemplateRenderError    *ErrorInfo  `json:"template_render_error,omitempty"`
	HAProxyValidationError *ErrorInfo  `json:"haproxy_validation_error,omitempty"`
	DeploymentErrors       []ErrorInfo `json:"deployment_errors,omitempty"`
	LastErrorTimestamp     time.Time   `json:"last_error_timestamp,omitempty"`
}

ErrorSummary provides an aggregated view of recent errors across all phases.

type ErrorsVar

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

ErrorsVar exposes an aggregated view of recent errors across all phases.

Returns a JSON object containing errors from each phase:

  • config_parse_error: config parsing errors
  • template_render_error: template rendering errors
  • haproxy_validation_error: HAProxy validation errors
  • deployment_errors: per-endpoint deployment errors
  • last_error_timestamp: when the most recent error occurred

Example response:

{
  "haproxy_validation_error": {
    "timestamp": "2025-01-15T10:30:47Z",
    "errors": ["[ALERT] parsing [haproxy.cfg:3] : unknown keyword..."]
  },
  "last_error_timestamp": "2025-01-15T10:30:47Z"
}

func (*ErrorsVar) Get

func (v *ErrorsVar) Get() (interface{}, error)

Get implements introspection.Var.

type Event

type Event struct {
	Timestamp     time.Time   `json:"timestamp"`
	Type          string      `json:"type"`
	Summary       string      `json:"summary"`
	Details       interface{} `json:"details,omitempty"`
	CorrelationID string      `json:"correlation_id,omitempty"`
}

Event represents a debug event with timestamp and details.

This is a simplified representation of controller events for debug purposes. It captures the essential information without exposing internal event structures.

type EventBuffer

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

EventBuffer maintains a ring buffer of recent events for debug purposes.

This is separate from the EventCommentator's ring buffer to avoid coupling debug functionality to the commentator component. It subscribes to the EventBus and stores simplified event representations.

func NewEventBuffer

func NewEventBuffer(size int, eventBus *busevents.EventBus) *EventBuffer

NewEventBuffer creates a new event buffer with the specified capacity.

The buffer subscribes to all events from the EventBus and stores the last N events (where N is the size parameter).

Note: The buffer subscribes during construction per CLAUDE.md guidelines to ensure subscription happens before EventBus.Start() is called.

Example:

eventBuffer := debug.NewEventBuffer(1000, eventBus)
go eventBuffer.Start(ctx)

func (*EventBuffer) FindByCorrelationID

func (eb *EventBuffer) FindByCorrelationID(correlationID string) []Event

FindByCorrelationID returns events matching the specified correlation ID.

This method searches through all events in the buffer and returns those that have a matching correlation ID. Events are returned in chronological order.

Example:

events := eventBuffer.FindByCorrelationID("550e8400-e29b-41d4-a716-446655440000")

func (*EventBuffer) GetAll

func (eb *EventBuffer) GetAll() []Event

GetAll returns all events in the buffer.

func (*EventBuffer) GetLast

func (eb *EventBuffer) GetLast(n int) []Event

GetLast returns the last n events in chronological order.

Example:

recent := eventBuffer.GetLast(100)  // Last 100 events

func (*EventBuffer) Len

func (eb *EventBuffer) Len() int

Len returns the current number of events in the buffer.

func (*EventBuffer) Start

func (eb *EventBuffer) Start(ctx context.Context) error

Start begins collecting events from the EventBus.

This method blocks until the context is cancelled. It processes events from the pre-subscribed channel. It should be run in a goroutine.

Example:

go eventBuffer.Start(ctx)

type EventsVar

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

EventsVar exposes recent events as a debug variable.

Returns a JSON array of recent events.

Example response:

[
  {
    "timestamp": "2025-01-15T10:30:45Z",
    "type": "config.validated",
    "summary": "config.validated"
  },
  {
    "timestamp": "2025-01-15T10:30:46Z",
    "type": "reconciliation.triggered",
    "summary": "reconciliation.triggered"
  }
]

func (*EventsVar) Get

func (v *EventsVar) Get() (interface{}, error)

Get implements introspection.Var.

type FailedEndpoint

type FailedEndpoint struct {
	URL   string `json:"url"`
	Error string `json:"error"`
}

FailedEndpoint contains details about a failed deployment endpoint.

type FullStateVar

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

FullStateVar exposes all controller state in a single dump.

Warning: This can return very large responses. Use with caution. Prefer querying specific variables or using field selection.

Returns a JSON object containing:

  • config: current config and version
  • rendered: last rendered config
  • auxfiles: auxiliary files
  • resources: resource counts
  • recent_events: last 100 events
  • snapshot_time: when this snapshot was taken

func (*FullStateVar) Get

func (v *FullStateVar) Get() (interface{}, error)

Get implements introspection.Var.

type PipelineStatus

type PipelineStatus struct {
	LastTrigger *TriggerStatus    `json:"last_trigger"`
	Rendering   *RenderingStatus  `json:"rendering"`
	Validation  *ValidationStatus `json:"validation"`
	Deployment  *DeploymentStatus `json:"deployment"`
}

PipelineStatus represents the complete status of the last reconciliation pipeline.

This provides visibility into each stage of the pipeline: trigger, rendering, validation, and deployment.

type PipelineVar

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

PipelineVar exposes the reconciliation pipeline status.

Returns a JSON object containing the status of each pipeline phase:

  • last_trigger: what triggered the reconciliation
  • rendering: template rendering status
  • validation: HAProxy validation status
  • deployment: deployment to HAProxy instances status

Example response:

{
  "last_trigger": {"timestamp": "2025-01-15T10:30:45Z", "reason": "config_change"},
  "rendering": {"status": "succeeded", ...},
  "validation": {"status": "failed", "errors": [...]},
  "deployment": {"status": "skipped", "reason": "validation_failed"}
}

func (*PipelineVar) Get

func (v *PipelineVar) Get() (interface{}, error)

Get implements introspection.Var.

type RenderedVar

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

RenderedVar exposes the most recently rendered HAProxy configuration.

Returns a JSON object containing:

  • config: the rendered HAProxy config string
  • timestamp: when it was rendered
  • size: length of the config in bytes

Example response:

{
  "config": "global\n  maxconn 2000\n...",
  "timestamp": "2025-01-15T10:30:45Z",
  "size": 4567
}

func (*RenderedVar) Get

func (v *RenderedVar) Get() (interface{}, error)

Get implements introspection.Var.

type RenderingStatus

type RenderingStatus struct {
	Status      string    `json:"status"` // "succeeded" | "failed"
	Timestamp   time.Time `json:"timestamp"`
	DurationMs  int64     `json:"duration_ms"`
	ConfigBytes int       `json:"config_bytes"`
	Error       string    `json:"error,omitempty"`
}

RenderingStatus represents the template rendering phase status.

type ResourcesVar

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

ResourcesVar exposes resource counts by type.

Returns a map of resource type → count.

Example response:

{
  "ingresses": 5,
  "services": 12,
  "haproxy-pods": 2
}

func (*ResourcesVar) Get

func (v *ResourcesVar) Get() (interface{}, error)

Get implements introspection.Var.

type StateProvider

type StateProvider interface {
	// GetConfig returns the current validated configuration and its version.
	//
	// Returns error if config is not yet loaded.
	//
	// Example return:
	//   config: &config.Config{...}
	//   version: "v123"
	//   error: nil
	GetConfig() (*config.Config, string, error)

	// GetCredentials returns the current credentials and their version.
	//
	// Returns error if credentials are not yet loaded.
	//
	// Example return:
	//   creds: &config.Credentials{...}
	//   version: "v456"
	//   error: nil
	GetCredentials() (*config.Credentials, string, error)

	// GetRenderedConfig returns the most recently rendered HAProxy configuration
	// and the timestamp when it was rendered.
	//
	// Returns error if no config has been rendered yet.
	//
	// Example return:
	//   rendered: "global\n  maxconn 2000\n..."
	//   timestamp: 2025-01-15 10:30:45
	//   error: nil
	GetRenderedConfig() (string, time.Time, error)

	// GetAuxiliaryFiles returns the most recently used auxiliary files
	// (SSL certificates, map files, general files) and the timestamp.
	//
	// Returns error if no auxiliary files have been cached yet.
	//
	// Example return:
	//   auxFiles: &dataplane.AuxiliaryFiles{SSLCertificates: [...], ...}
	//   timestamp: 2025-01-15 10:30:45
	//   error: nil
	GetAuxiliaryFiles() (*dataplane.AuxiliaryFiles, time.Time, error)

	// GetResourceCounts returns a map of resource type → count.
	//
	// The keys are resource names as defined in the controller configuration
	// (e.g., "ingresses", "services", "haproxy-pods").
	//
	// Example return:
	//   {
	//     "ingresses": 5,
	//     "services": 12,
	//     "haproxy-pods": 2
	//   }
	GetResourceCounts() (map[string]int, error)

	// GetResourcesByType returns all resources of a specific type.
	//
	// The resourceType parameter should match a key from GetResourceCounts().
	//
	// Returns error if the resource type is not found.
	//
	// Example:
	//   resources, err := provider.GetResourcesByType("ingresses")
	GetResourcesByType(resourceType string) ([]interface{}, error)

	// GetPipelineStatus returns the complete status of the last reconciliation pipeline.
	//
	// Returns error if no pipeline has run yet.
	//
	// Example return:
	//   status: &PipelineStatus{
	//     LastTrigger: &TriggerStatus{Timestamp: ..., Reason: "config_change"},
	//     Rendering:   &RenderingStatus{Status: "succeeded", ...},
	//     Validation:  &ValidationStatus{Status: "failed", Errors: [...]},
	//     Deployment:  &DeploymentStatus{Status: "skipped", Reason: "validation_failed"},
	//   }
	GetPipelineStatus() (*PipelineStatus, error)

	// GetValidatedConfig returns the last successfully validated HAProxy configuration.
	//
	// Unlike GetRenderedConfig(), this only returns configs that passed validation.
	// Returns error if no config has been validated successfully yet.
	//
	// Example return:
	//   info: &ValidatedConfigInfo{
	//     Config:               "global\n  maxconn 2000\n...",
	//     Timestamp:            2025-01-15 10:30:45,
	//     ConfigBytes:          4567,
	//     ValidationDurationMs: 200,
	//   }
	GetValidatedConfig() (*ValidatedConfigInfo, error)

	// GetErrors returns an aggregated summary of recent errors across all phases.
	//
	// This is useful for quick diagnosis of configuration issues.
	//
	// Example return:
	//   summary: &ErrorSummary{
	//     HAProxyValidationError: &ErrorInfo{
	//       Timestamp: 2025-01-15 10:30:47,
	//       Errors:    ["[ALERT] parsing [haproxy.cfg:3] : unknown keyword..."],
	//     },
	//     LastErrorTimestamp: 2025-01-15 10:30:47,
	//   }
	GetErrors() (*ErrorSummary, error)
}

StateProvider provides access to controller internal state.

This interface is implemented by the controller to expose its state to debug variables in a thread-safe manner. The controller caches state by subscribing to events (ConfigValidatedEvent, RenderCompletedEvent, etc.) and updates the cached values accordingly.

All methods must be thread-safe as they may be called concurrently from HTTP request handlers.

type TriggerStatus

type TriggerStatus struct {
	Timestamp time.Time `json:"timestamp"`
	Reason    string    `json:"reason"`
}

TriggerStatus represents what triggered the last reconciliation.

type ValidatedConfigInfo

type ValidatedConfigInfo struct {
	Config               string    `json:"config"`
	Timestamp            time.Time `json:"timestamp"`
	ConfigBytes          int       `json:"config_bytes"`
	ValidationDurationMs int64     `json:"validation_duration_ms"`
}

ValidatedConfigInfo contains information about the last successfully validated config.

type ValidatedVar

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

ValidatedVar exposes the last successfully validated HAProxy configuration.

Unlike RenderedVar, this only shows configs that passed validation. This is useful to see what config is actually safe to deploy.

Example response:

{
  "config": "global\n  maxconn 2000\n...",
  "timestamp": "2025-01-15T10:30:45Z",
  "config_bytes": 4567,
  "validation_duration_ms": 200
}

func (*ValidatedVar) Get

func (v *ValidatedVar) Get() (interface{}, error)

Get implements introspection.Var.

type ValidationStatus

type ValidationStatus struct {
	Status     string    `json:"status"` // "succeeded" | "failed" | "pending"
	Timestamp  time.Time `json:"timestamp"`
	DurationMs int64     `json:"duration_ms"`
	Errors     []string  `json:"errors,omitempty"`
	Warnings   []string  `json:"warnings,omitempty"`
}

ValidationStatus represents the HAProxy validation phase status.

Jump to

Keyboard shortcuts

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