Help us shape the future of Go! Take the 2026 Go Developer Survey

events

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

README

pkg/controller/events

Domain event catalogue for the controller. All event types that flow through the pkg/events bus live here; the infrastructure itself (publish/subscribe, scatter-gather) lives in pkg/events and has no knowledge of domain semantics.

  • ~45 event types across ~14 categories.
  • All implement the pkg/events.Event interface (EventType() string, Timestamp() time.Time) via pointer receivers.
  • All exported constructors NewFooEvent(...) perform defensive copies of slices and maps so consumers can't mutate a published event.
  • A custom go vet-style analyzer in tools/linters/eventimmutability enforces the pointer-receiver rule at build time.

Source of Truth

One file per category. The full list as of writing, with representative types:

File Category Representative types
types.go Event-type constants and shared helpers —
correlation.go Request/response correlation metadata Correlation (struct), CorrelatedEvent (interface, exposes CorrelationID() string)
config.go CRD parsed / validated / invalid ConfigParsedEvent, ConfigValidatedEvent
credentials.go Secret ingestion and validation CredentialsUpdatedEvent
resource.go Watched-resource index changes ResourceIndexUpdatedEvent, IndexSynchronizedEvent
reconciliation.go Reconciliation pipeline lifecycle ReconciliationTriggeredEvent, ReconciliationCompletedEvent, ResourcesAppliedEvent
resources_processed.go Resource-application backpressure ResourcesProcessedEvent
template.go Rendering TemplateRenderedEvent, TemplateRenderFailedEvent
validation.go Syntax/semantic validation ValidationFailedEvent
rendergate.go Asynchronous haproxy -c verdict on a render RenderGateCompletedEvent
deployment.go HAProxy deployment scheduler + executor DeploymentScheduledEvent, InstanceDeploymentFailedEvent
discovery.go HAProxy pod discovery HAProxyPodsDiscoveredEvent, HAProxyPodRejectedEvent
leader.go Leader election BecameLeaderEvent, LostLeadershipEvent
publishing.go Output-CRD publishing (HAProxyCfg + HAProxy{General,Map,CRTList}File) and per-pod sync outcomes ConfigPublishedEvent, ConfigAppliedToPodEvent
proposal.go Admission-time proposal validation ProposalValidationRequestedEvent, ProposalValidationCompletedEvent
http.go HTTP resource fetcher HTTPResourceUpdatedEvent
status.go Status-patch application StatusUpdateCompletedEvent, StatusUpdateFailedEvent

types.go plus the event-category files enumerate every constant — if the list above looks incomplete, check grep -E "^type [A-Z].*Event " pkg/controller/events/*.go rather than trusting this README.

Publishing

import (
    "github.com/haproxy-haptic/HAPTIC/pkg/controller/events"
    busevents "github.com/haproxy-haptic/HAPTIC/pkg/events"
)

bus.Publish(events.NewConfigParsedEvent(cfg, templateConfig, version, secretVersion))

Always use the New* constructors — they copy any slices/maps to cut off the ownership chain. Don't construct events with a struct literal; you lose the defensive copy and the analyzer can't help.

Render occurrence authority

Production render lifecycle events carry one sealed *rendercycle.Occurrence from rendering through resource apply, validation, scheduling, and deployment completion. NewTemplateRenderedEventWithCycle creates it. Read it with RenderOccurrence() and pass that exact pointer to downstream WithCycle constructors or NewDeploymentResultWithOccurrence.

CycleSnapshot, OutputSnapshot, RenderProof, PlanID, and checksum fields are compatibility and diagnostic shadows. They aren't authentication inputs. Mutating or substituting them doesn't change AuthenticatedRenderIdentity(), and subscriber clones restore them from the private occurrence. Legacy constructors don't authenticate an occurrence; their occurrence accessors return an error.

Don't reconstruct identity from a snapshot and proof. A proof is diagnostic, and equal output can occur in distinct A-B-A render executions.

ResourcesProcessedEvent acknowledges that the resource applier handled one exact occurrence, including a failed or gate-held attempt. The coordinator waits for that acknowledgement before rendering again. It is not coalescible and does not replace ResourcesAppliedEvent, which alone authorizes rendered-status publication after successful convergence.

Consuming

Either accept everything and switch on type, or subscribe to a filtered set:

// Any event
eventChan := bus.Subscribe("my-component", 100)
for ev := range eventChan {
    switch e := ev.(type) {
    case *events.ConfigValidatedEvent:
        // ...
    case *events.ReconciliationTriggeredEvent:
        // ...
    }
}

// Just a few types (cheaper — filtered at the bus)
filtered := bus.SubscribeTypes(
    "my-component", 100,
    events.EventTypeReconciliationTriggered,
    events.EventTypeReconciliationCompleted,
)

Scatter-Gather (Config Validation)

ConfigValidationRequest is the single Request type in the catalogue. Usage goes through pkg/events.Request:

req := events.NewConfigValidationRequest(cfg, version)

result, err := bus.Request(ctx, req, busevents.RequestOptions{
    Timeout:            10 * time.Second,
    ExpectedResponders: []string{"basic", "template", "jsonpath"},
})
if err != nil {
    return err
}
for _, resp := range result.Responses {
    if r, ok := resp.(*events.ConfigValidationResponse); ok && !r.Valid {
        return fmt.Errorf("validator %s: %s", r.ValidatorName, strings.Join(r.Errors, "; "))
    }
}

Each responder subscribes normally, matches on request ID, and bus.Publishes a matching *ConfigValidationResponse.

Adding a New Event

  1. Add const EventTypeFoo = "foo" to types.go (or the category file if it already has its own constants block).
  2. Define the struct in the appropriate category file (or create a new file if there's no natural home).
  3. Implement EventType() string with a pointer receiver.
  4. Add a NewFooEvent(...) constructor that copy()s every incoming slice and map field.
  5. Decide pkg/controller/commentator's treatment: Give the commentator an insight case OR a level arm, unless the publisher already logs the payload — the fallback then records only that the event happened.
  6. If this event should drive a Prometheus metric, wire a case into pkg/controller/metrics.Component.HandleEvent and update pkg/controller/metrics/README.md.

See Also

  • pkg/events — generic bus (Publish, Subscribe, Request, typed subscriptions)
  • pkg/controller/commentator — logs every event, attaches recent-event context via the ring buffer
  • pkg/controller/metrics — subscribes to nearly everything in this catalogue for domain metrics
  • tools/linters/eventimmutability — custom analyzer that enforces pointer receivers
  • pkg/controller/events/CLAUDE.md — developer context (immutability rules, category file organisation, common pitfalls)

License

Apache-2.0 — see root LICENSE.

Documentation

Overview

Package events contains all domain event type definitions for the HAPTIC controller.

Event Immutability Contract

Events in this system are intended to be immutable after creation. They represent historical facts about what happened in the system and should not be modified after being published to the EventBus.

To support this immutability contract:

  1. All event types use pointer receivers for their Event interface methods. This avoids copying large structs (200+ bytes) and follows Go best practices.

  2. All event fields are exported to support JSON serialization and idiomatic Go access. This follows industry standards (Kubernetes, NATS) rather than enforcing immutability through unexported fields and getters.

  3. Constructors perform defensive copying of slices and maps to prevent mutations from affecting the published event. Publishers cannot modify events after creation.

  4. Consumers MUST NOT modify event fields. This immutability contract is enforced through: - A custom static analyzer (tools/linters/eventimmutability) that detects parameter mutations - Code review for cases not caught by the analyzer - Team discipline and documentation

This approach balances performance, Go idioms, and practical immutability for an internal project where all consumers are controlled.

Event Categories

Events are organized into separate files by category:

  • config.go: HAProxyTemplateConfig CRD changes and validation events
  • resource.go: Kubernetes resource indexing and synchronization events
  • reconciliation.go: Template rendering and deployment cycle events
  • template.go: Template rendering operation events
  • validation.go: Configuration validation (syntax and semantics) events
  • rendergate.go: Asynchronous `haproxy -c` verdict on a render
  • deployment.go: HAProxy configuration deployment events
  • discovery.go: HAProxy pod discovery events
  • credentials.go: Credentials loading and validation events
  • leader.go: Leader election events
  • publishing.go: Config publishing events (including SyncMetadata types)
  • certificate.go: Webhook certificate events
  • http.go: HTTP resource events
  • proposal.go: Speculative validation requests/responses (used by webhook and HTTP store)
  • status.go: Status patch application events

Index

Constants

View Source
const (
	// DeploymentCancelReasonEndpointAuthorityChanged supersedes the deploy: its
	// pod set is not the fleet any more and the replacement set is deployed next.
	DeploymentCancelReasonEndpointAuthorityChanged = "endpoint_authority_changed"
	DeploymentCancelReasonTimeout                  = "deployment_timeout"
)

DeploymentCancelRequestEvent reasons.

View Source
const (
	// LeadershipLostReasonLeaseLost is an unplanned loss: the Lease expired
	// or another replica holds it.
	LeadershipLostReasonLeaseLost = "lease_lost"
	// LeadershipLostReasonHandover is the planned end of an iteration's term
	// for its successor on the same replica, which keeps the Lease.
	LeadershipLostReasonHandover = "handover"
)

Reasons a LostLeadershipEvent carries.

View Source
const (
	// Configuration event types.
	EventTypeConfigParsed                 = "config.parsed"
	EventTypeConfigValidationRequest      = "config.validation.request"
	EventTypeConfigValidationResponse     = "config.validation.response"
	EventTypeConfigValidated              = "config.validated"
	EventTypeConfigInvalid                = "config.invalid"
	EventTypeConfigValidationInconclusive = "config.validation.inconclusive"
	EventTypeConfigVerdictReplayed        = "config.verdict.replayed"
	EventTypeConfigResourceChanged        = "config.resource.changed"
	EventTypeLibrarySetChanged            = "snippets.set.changed"

	// Resource event types.
	EventTypeWatchedInputsRejected = "resource.input.rejected"
	EventTypeResourceIndexUpdated  = "resource.index.updated"
	EventTypeResourceSyncComplete  = "resource.sync.complete"
	EventTypeIndexSynchronized     = "index.synchronized"

	// Reconciliation event types.
	EventTypeReconciliationTriggered = "reconciliation.triggered"
	EventTypeReconciliationStarted   = "reconciliation.started"
	EventTypeReconciliationCompleted = "reconciliation.completed"

	// EventTypeResourcesApplied is published by the ResourceApplier after a
	// cycle's rendered resources are applied; carries the cycle's status
	// patches forward so the rendered status variant applies after the
	// resources exist.
	EventTypeResourcesApplied     = "resources.applied"
	EventTypeResourcesProcessed   = "resources.processed"
	EventTypeReconciliationFailed = "reconciliation.failed"

	// Template event types.
	EventTypeTemplateRendered     = "template.rendered"
	EventTypeTemplateRenderFailed = "template.render.failed"

	// Validation event types (the render gate's `haproxy -c`).
	EventTypeValidationFailed = "validation.failed"

	// Render gate verdict: the controller's own `haproxy -c` on a render,
	// which runs off the reconcile wall clock (ADR-0022).
	EventTypeRenderGateCompleted = "rendergate.completed"

	// Deployment event types.
	EventTypeDeploymentScheduled      = "deployment.scheduled"
	EventTypeDeploymentStarted        = "deployment.started"
	EventTypeInstanceDeploymentFailed = "instance.deployment.failed"
	EventTypeDeploymentCompleted      = "deployment.completed"
	EventTypeDeploymentSkipped        = "deployment.skipped"
	EventTypeDeploymentCancelRequest  = "deployment.cancel.request"
	EventTypeDriftPreventionTriggered = "drift.prevention.triggered"

	// DeploymentSkippedEvent reasons: no deployment ran, and the data plane
	// serves the config anyway.
	SkipReasonConfigUnchanged = "config_unchanged" // the render equals the last deployed one
	SkipReasonReloadObserved  = "reload_observed"  // a later deployment's ACKs report the fleet running it

	// HAProxy pod event types.
	EventTypeHAProxyPodsDiscovered = "haproxy.pods.discovered"
	EventTypeHAProxyPodTerminated  = "haproxy.pod.terminated"
	EventTypeHAProxyPodRejected    = "haproxy.pod.rejected"

	// Config publishing event types.
	EventTypeConfigPublished              = "config.published"
	EventTypeConfigAppliedToPod           = "config.applied.to.pod"
	EventTypeDeployedConfigPublishRequest = "config.deployed.publish.request"

	// Credentials event types.
	EventTypeSecretResourceChanged = "secret.resource.changed"
	EventTypeCredentialsUpdated    = "credentials.updated"

	// Leader election event types.
	EventTypeLeaderElectionStarted = "leader.election.started"
	EventTypeBecameLeader          = "leader.became"
	EventTypeLostLeadership        = "leader.lost"
	EventTypeNewLeaderObserved     = "leader.observed"

	// HTTP resource event types.
	EventTypeHTTPResourceUpdated  = "http.resource.updated"
	EventTypeHTTPResourceAccepted = "http.resource.accepted"

	EventTypeHTTPContentAcceptanceRequested = "http.content.acceptance.requested"
	EventTypeHTTPContentRevoked             = "http.content.revoked"
	EventTypeHTTPContentRejected            = "http.content.rejected"

	// Proposal validation event types.
	// Used for validating hypothetical configuration changes before committing them.
	// See proposal.go for event definitions.
	EventTypeProposalValidationRequested = "proposal.validation.requested"
	EventTypeProposalValidationCompleted = "proposal.validation.completed"

	// Status update event types.
	// Published by StatusApplier after applying template-driven status patches to Kubernetes resources.
	EventTypeStatusUpdateCompleted = "status.update.completed"
	EventTypeStatusUpdateFailed    = "status.update.failed"
)
View Source
const EventTypeRetainedConfig = "config.retained"
View Source
const (
	TriggerReasonDriftPrevention = "drift_prevention"
)

TriggerReason constants for reconciliation events. These are propagated through the event chain via TriggerReason fields.

Variables

This section is empty.

Functions

This section is empty.

Types

type BecameLeaderEvent

type BecameLeaderEvent struct {
	Identity string
	// contains filtered or unexported fields
}

BecameLeaderEvent is published when this replica becomes the leader.

func NewBecameLeaderEvent

func NewBecameLeaderEvent(identity string) *BecameLeaderEvent

NewBecameLeaderEvent creates a new BecameLeaderEvent.

func (*BecameLeaderEvent) EventType

func (e *BecameLeaderEvent) EventType() string

func (*BecameLeaderEvent) Timestamp

func (t *BecameLeaderEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ConfigAppliedToPodEvent

type ConfigAppliedToPodEvent struct {
	RuntimeConfigName      string
	RuntimeConfigNamespace string
	PodName                string
	PodNamespace           string
	PodUID                 string
	PodRuntimeID           string
	Checksum               string

	// IsDriftCheck indicates whether this was a drift prevention check (GET-only)
	// or an actual sync operation (POST/PUT/DELETE).
	//
	// True:  Drift check - no actual changes were made, just verified config is current
	// False: Actual sync - configuration was written to HAProxy
	IsDriftCheck bool

	// SyncMetadata contains detailed information about the sync operation.
	// Only populated for actual syncs (IsDriftCheck=false).
	SyncMetadata *SyncMetadata
	// contains filtered or unexported fields
}

ConfigAppliedToPodEvent is published after configuration is successfully applied to a HAProxy pod.

This triggers updating the deployment status in runtime config resources.

func NewConfigAppliedToPodEvent

func NewConfigAppliedToPodEvent(runtimeConfigName, runtimeConfigNamespace, podName, podNamespace, podUID, podRuntimeID, checksum string, isDriftCheck bool, syncMetadata *SyncMetadata) *ConfigAppliedToPodEvent

NewConfigAppliedToPodEvent creates a new ConfigAppliedToPodEvent.

func (*ConfigAppliedToPodEvent) EventType

func (e *ConfigAppliedToPodEvent) EventType() string

func (*ConfigAppliedToPodEvent) Timestamp

func (t *ConfigAppliedToPodEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ConfigInvalidEvent

type ConfigInvalidEvent struct {
	Version string

	// TemplateConfig is the original HAProxyTemplateConfig CRD.
	// Type: any to avoid circular dependencies.
	// Used by status updater to set validation errors on the CRD status.
	TemplateConfig any

	// ValidationErrors maps validator names to their error messages.
	ValidationErrors map[string][]string

	// VerdictSequence is as on ConfigValidatedEvent.
	VerdictSequence uint64

	// Sources names every config of the merged set, in merge order.
	Sources []ConfigSourceRef
	// contains filtered or unexported fields
}

ConfigInvalidEvent is published when config validation fails.

The controller will continue running with the previous valid config and wait for the next HAProxyTemplateConfig CRD update.

func NewConfigInvalidEvent

func NewConfigInvalidEvent(version string, templateConfig any, validationErrors map[string][]string) *ConfigInvalidEvent

NewConfigInvalidEvent creates a new ConfigInvalidEvent. Performs defensive copy of the validation errors map and its slice values.

func (*ConfigInvalidEvent) EventType

func (e *ConfigInvalidEvent) EventType() string

func (*ConfigInvalidEvent) Timestamp

func (t *ConfigInvalidEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ConfigParsedEvent

type ConfigParsedEvent struct {
	// Config contains the parsed configuration.
	// Type: any to avoid circular dependencies.
	// Consumers should type-assert to their expected config type.
	Config any

	// TemplateConfig is the original HAProxyTemplateConfig CRD.
	// Type: any to avoid circular dependencies.
	// Needed by ConfigPublisher to extract Kubernetes metadata (name, namespace, UID).
	TemplateConfig any

	// Version is the resourceVersion of the HAProxyTemplateConfig CRD.
	Version string

	// SecretVersion is the resourceVersion of the credentials Secret.
	SecretVersion string

	// Sources names every config of the merged set, in merge order.
	Sources []ConfigSourceRef
	// contains filtered or unexported fields
}

func NewConfigParsedEvent

func NewConfigParsedEvent(config, templateConfig any, version, secretVersion string) *ConfigParsedEvent

NewConfigParsedEvent creates a new ConfigParsedEvent.

func (*ConfigParsedEvent) EventType

func (e *ConfigParsedEvent) EventType() string

func (*ConfigParsedEvent) Timestamp

func (t *ConfigParsedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ConfigPublishedEvent

type ConfigPublishedEvent struct {
	RuntimeConfigName      string
	RuntimeConfigNamespace string
	MapFileCount           int
	SecretCount            int
	// contains filtered or unexported fields
}

ConfigPublishedEvent is published after runtime configuration resources are created/updated.

This is a non-critical event - publishing failures do not affect controller operation.

func NewConfigPublishedEvent

func NewConfigPublishedEvent(runtimeConfigName, runtimeConfigNamespace string, mapFileCount, secretCount int) *ConfigPublishedEvent

NewConfigPublishedEvent creates a new ConfigPublishedEvent.

func (*ConfigPublishedEvent) EventType

func (e *ConfigPublishedEvent) EventType() string

func (*ConfigPublishedEvent) Timestamp

func (t *ConfigPublishedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ConfigResourceChangedEvent

type ConfigResourceChangedEvent struct {
	// Resource contains the raw HAProxyTemplateConfig CRD resource.
	// Type: any to avoid circular dependencies.
	// Consumers should type-assert to *unstructured.Unstructured.
	Resource any
	// contains filtered or unexported fields
}

ConfigResourceChangedEvent is published when the HAProxyTemplateConfig CRD resource is added, updated, or deleted.

This is a low-level event published directly by the SingleWatcher callback in the controller package. The ConfigLoaderComponent subscribes to this event and handles parsing.

func NewConfigResourceChangedEvent

func NewConfigResourceChangedEvent(resource any) *ConfigResourceChangedEvent

NewConfigResourceChangedEvent creates a new ConfigResourceChangedEvent.

func (*ConfigResourceChangedEvent) EventType

func (e *ConfigResourceChangedEvent) EventType() string

func (*ConfigResourceChangedEvent) Timestamp

func (t *ConfigResourceChangedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ConfigSourceRef

type ConfigSourceRef struct {
	Namespace       string
	Name            string
	Generation      int64
	UID             types.UID
	ResourceVersion string
}

ConfigParsedEvent is published when the HAProxyTemplateConfig CRD has been successfully parsed into a Config structure.

This event does not mean the config is valid - only that it could be parsed. Validation occurs in a subsequent step. ConfigSourceRef identifies one HAProxyTemplateConfig of the merged set, at the generation the merge observed. Status is stamped on EVERY source: the verdict is a property of the merged set, and observedGeneration is only meaningful against the same object's metadata.generation — a designated primary could not even represent "your shard edit was observed" (ADR-0016).

type ConfigValidatedEvent

type ConfigValidatedEvent struct {
	Config any

	// TemplateConfig is the original HAProxyTemplateConfig CRD.
	// Type: any to avoid circular dependencies.
	// Needed by ConfigPublisher to extract Kubernetes metadata (name, namespace, UID).
	TemplateConfig any

	Version       string
	SecretVersion string

	// CandidateGeneration identifies a live config-change validation owned by
	// ConfigChangeHandler. Zero denotes state published outside that handler.
	CandidateGeneration uint64

	// ActiveSnapshotRestore replays the running iteration's state after a newer
	// parsed generation retires an accepted candidate. State consumers apply it;
	// validation and reload consumers must not treat it as a new verdict.
	ActiveSnapshotRestore bool

	// VerdictSequence orders the verdicts one iteration reaches on the
	// configuration: a larger value is newer, and zero is not a verdict.
	VerdictSequence uint64

	// Sources names every config of the merged set, in merge order.
	Sources []ConfigSourceRef
	// contains filtered or unexported fields
}

ConfigValidatedEvent is published when all validators have confirmed the config is valid.

After receiving this event, the controller proceeds to start resource watchers. with the validated configuration.

func NewConfigValidatedEvent

func NewConfigValidatedEvent(config, templateConfig any, version, secretVersion string) *ConfigValidatedEvent

NewConfigValidatedEvent creates a new ConfigValidatedEvent.

func (*ConfigValidatedEvent) EventType

func (e *ConfigValidatedEvent) EventType() string

func (*ConfigValidatedEvent) Timestamp

func (t *ConfigValidatedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ConfigValidationInconclusiveEvent

type ConfigValidationInconclusiveEvent struct {
	Version        string
	TemplateConfig any
	// Reasons maps validator names to why they could not reach a verdict.
	Reasons map[string][]string
	RetryIn time.Duration
	// VerdictSequence is as on ConfigValidatedEvent.
	VerdictSequence uint64
	Sources         []ConfigSourceRef
	// contains filtered or unexported fields
}

ConfigValidationInconclusiveEvent is published when validators could not judge a config version. The running config stays active and the same version is validated again after RetryIn.

func NewConfigValidationInconclusiveEvent

func NewConfigValidationInconclusiveEvent(version string, templateConfig any, reasons map[string][]string, retryIn time.Duration) *ConfigValidationInconclusiveEvent

NewConfigValidationInconclusiveEvent creates a new ConfigValidationInconclusiveEvent.

func (*ConfigValidationInconclusiveEvent) EventType

func (*ConfigValidationInconclusiveEvent) Timestamp

func (t *ConfigValidationInconclusiveEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ConfigValidationRequest

type ConfigValidationRequest struct {

	// Config contains the configuration to validate.
	Config any

	// Version is the resourceVersion being validated.
	Version string
	// contains filtered or unexported fields
}

ConfigValidationRequest is published to request validation of a parsed config.

This is a Request event used in the scatter-gather pattern. Multiple validators (basic, template, jsonpath) will respond with ConfigValidationResponse events.

func NewConfigValidationRequest

func NewConfigValidationRequest(config any, version string) *ConfigValidationRequest

NewConfigValidationRequest creates a new ConfigValidationRequest.

func (*ConfigValidationRequest) EventType

func (e *ConfigValidationRequest) EventType() string

func (*ConfigValidationRequest) RequestID

func (e *ConfigValidationRequest) RequestID() string

func (*ConfigValidationRequest) Timestamp

func (t *ConfigValidationRequest) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ConfigValidationResponse

type ConfigValidationResponse struct {

	// ValidatorName identifies which validator produced this response (basic, template, jsonpath).
	ValidatorName string

	// Valid is true if this validator found no errors.
	Valid bool

	// Errors contains validation error messages, empty if Valid is true.
	Errors []string

	// Inconclusive means the validator could not judge the config (for
	// example, schemas were unavailable); Errors then say why. Never set
	// together with Valid.
	Inconclusive bool
	// contains filtered or unexported fields
}

ConfigValidationResponse is sent by validators in response to ConfigValidationRequest.

This is a Response event used in the scatter-gather pattern. pkg/controller/configchange.ConfigChangeHandler issues the request, collects all responses, and determines if the config is valid overall.

func NewConfigValidationInconclusiveResponse

func NewConfigValidationInconclusiveResponse(requestID, validatorName string, reasons []string) *ConfigValidationResponse

NewConfigValidationInconclusiveResponse reports that a validator could not reach a verdict, so the config is neither accepted nor rejected.

func NewConfigValidationResponse

func NewConfigValidationResponse(requestID, validatorName string, valid bool, errors []string) *ConfigValidationResponse

NewConfigValidationResponse creates a new ConfigValidationResponse. Performs defensive copy of the errors slice.

func (*ConfigValidationResponse) EventType

func (e *ConfigValidationResponse) EventType() string

func (*ConfigValidationResponse) RequestID

func (e *ConfigValidationResponse) RequestID() string

func (*ConfigValidationResponse) Responder

func (e *ConfigValidationResponse) Responder() string

func (*ConfigValidationResponse) Timestamp

func (t *ConfigValidationResponse) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ConfigVerdict added in v0.7.0

type ConfigVerdict interface {
	busevents.Event
	// contains filtered or unexported methods
}

ConfigVerdict is a ConfigValidatedEvent, ConfigInvalidEvent or ConfigValidationInconclusiveEvent.

type ConfigVerdictReplayedEvent added in v0.7.0

type ConfigVerdictReplayedEvent struct {
	Verdict ConfigVerdict
	// contains filtered or unexported fields
}

ConfigVerdictReplayedEvent restores status without counting or applying a verdict again.

func NewConfigVerdictReplayedEvent added in v0.7.0

func NewConfigVerdictReplayedEvent(verdict ConfigVerdict) *ConfigVerdictReplayedEvent

NewConfigVerdictReplayedEvent creates a new ConfigVerdictReplayedEvent.

func (*ConfigVerdictReplayedEvent) EventType added in v0.7.0

func (e *ConfigVerdictReplayedEvent) EventType() string

func (*ConfigVerdictReplayedEvent) Timestamp added in v0.7.0

func (t *ConfigVerdictReplayedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type CorrelatedEvent

type CorrelatedEvent interface {
	// EventID returns a unique identifier for this specific event.
	// Each event instance has its own unique EventID.
	// This is used as the CausationID for downstream events.
	EventID() string

	// CorrelationID returns a unique identifier that links related events.
	// All events in the same reconciliation cycle share the same correlation ID.
	// Returns empty string if correlation is not set.
	CorrelationID() string

	// CausationID returns the EventID of the event that triggered this one.
	// This enables building causal chains for debugging.
	// Returns empty string if causation is not set.
	CausationID() string
}

CorrelatedEvent is an interface for events that support correlation tracking.

Events that implement this interface can be linked together to trace causal chains through the reconciliation pipeline. This enables:

  • Querying all events for a specific reconciliation cycle
  • Building event graphs for debugging
  • Measuring end-to-end latency for pipelines
  • OpenTelemetry integration

Not all events need to implement this interface. It's optional and primarily useful for events in the reconciliation pipeline.

type Correlation

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

Correlation holds event ID, correlation ID, and causation ID for event tracing.

This struct is embedded in event types that need correlation support. It's designed to be optional - events can work without correlation, and consumers can check for empty IDs.

The IDs serve different purposes:

  • eventID: Unique identifier for THIS specific event instance
  • correlationID: Shared ID linking all events in a reconciliation cycle
  • causationID: The eventID of the event that triggered this one

func (*Correlation) CausationID

func (c *Correlation) CausationID() string

CausationID returns the causation ID, or empty string if not set.

func (*Correlation) CorrelationID

func (c *Correlation) CorrelationID() string

CorrelationID returns the correlation ID, or empty string if not set.

func (*Correlation) EventID

func (c *Correlation) EventID() string

EventID returns the unique identifier for this specific event.

type CorrelationOption

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

CorrelationOption holds data for setting correlation on events. This is a data struct rather than a closure to ensure events are fully initialized in their struct literals without post-creation mutation.

func PropagateCorrelation

func PropagateCorrelation(source any) CorrelationOption

PropagateCorrelation extracts correlation context from a source event and returns a CorrelationOption that can be applied to a new event. If the source event doesn't implement CorrelatedEvent, returns an empty option.

The correlation ID is copied from the source event (maintaining pipeline identity). The causation ID is set to the source event's EventID (creating causal chain).

Example:

func handleEvent(sourceEvent Event) {
    newEvent := events.NewTemplateRenderedEvent(...,
        events.PropagateCorrelation(sourceEvent))
}

func WithCorrelation

func WithCorrelation(correlationID, causationID string) CorrelationOption

WithCorrelation sets both correlation and causation IDs on an event. Use this when propagating correlation context from a triggering event.

Example:

triggeredEvent := events.NewReconciliationTriggeredEvent("config_change", true, events.WithNewCorrelation())
renderedEvent := events.NewTemplateRenderedEvent(...,
    events.WithCorrelation(triggeredEvent.CorrelationID(), triggeredEvent.EventID()))

func WithNewCorrelation

func WithNewCorrelation() CorrelationOption

WithNewCorrelation generates a new correlation ID and sets it on an event. Use this at the start of a new pipeline (e.g., ReconciliationTriggeredEvent).

Example:

event := events.NewReconciliationTriggeredEvent("config_change", true,
    events.WithNewCorrelation())

type CredentialsUpdatedEvent

type CredentialsUpdatedEvent struct {
	// Credentials contains the validated credentials.
	// Type: any to avoid circular dependencies.
	// Consumers should type-assert to their expected credentials type.
	Credentials any

	// SecretVersion is the resourceVersion of the Secret.
	SecretVersion string
	// contains filtered or unexported fields
}

CredentialsUpdatedEvent is published when credentials have been successfully. loaded and validated from the Secret.

func NewCredentialsUpdatedEvent

func NewCredentialsUpdatedEvent(credentials any, secretVersion string) *CredentialsUpdatedEvent

NewCredentialsUpdatedEvent creates a new CredentialsUpdatedEvent.

func (*CredentialsUpdatedEvent) EventType

func (e *CredentialsUpdatedEvent) EventType() string

func (*CredentialsUpdatedEvent) Timestamp

func (t *CredentialsUpdatedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type DeployPhases

type DeployPhases struct {
	Pod         string
	PrepareMs   int64 // the deployment before any pod was contacted
	SettleMs    int64 // the deployment after the last pod answered
	TotalMs     int64 // the whole apply, state read to result
	StateMs     int64 // reading the agent's state
	DiffMs      int64 // composing the decision
	SendMs      int64 // the apply round trips, upload included
	BlobWaitMs  int64 // waiting for the plan blob's encode before the apply that carries it
	UploadBytes int64 // file content sent, which the agent had not proved
	Agent       api.ApplyTiming
}

DeployPhases is where the slowest pod's apply spent its time. Pods apply in parallel, so this pod's split is what the deployment's duration is made of.

type DeployedConfigPublishRequest

type DeployedConfigPublishRequest struct {
	CycleSnapshot          *rendercycle.Snapshot
	OutputSnapshot         *renderoutput.Snapshot
	RuntimeConfigName      string
	RuntimeConfigNamespace string
	Config                 string
	AuxiliaryFiles         *dataplane.AuxiliaryFiles
	ContentChecksum        string
	ConfirmedPod           *v1.PodDeploymentStatus
	// contains filtered or unexported fields
}

DeployedConfigPublishRequest publishes the exact render occurrence acknowledged by a pod.

func NewDeployedConfigPublishRequestWithCycle

func NewDeployedConfigPublishRequestWithCycle(
	runtimeConfigName, runtimeConfigNamespace string,
	occurrence *rendercycle.Occurrence,
	confirmed ...v1.PodDeploymentStatus,
) (*DeployedConfigPublishRequest, error)

NewDeployedConfigPublishRequestWithCycle publishes one exact deployed occurrence.

func (DeployedConfigPublishRequest) AuthenticatedRenderIdentity

func (c DeployedConfigPublishRequest) AuthenticatedRenderIdentity() (*rendercycle.Snapshot, string, error)

AuthenticatedRenderIdentity returns the exact cycle and diagnostic proof.

func (*DeployedConfigPublishRequest) CloneForSubscriber

func (e *DeployedConfigPublishRequest) CloneForSubscriber() busevents.Event

CloneForSubscriber restores authenticated shadows and isolates legacy files.

func (*DeployedConfigPublishRequest) EventType

func (e *DeployedConfigPublishRequest) EventType() string

func (DeployedConfigPublishRequest) RenderOccurrence

func (c DeployedConfigPublishRequest) RenderOccurrence() (*rendercycle.Occurrence, error)

RenderOccurrence returns the exact opaque occurrence carried by the event.

func (*DeployedConfigPublishRequest) Timestamp

func (t *DeployedConfigPublishRequest) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type DeploymentCancelRequestEvent

type DeploymentCancelRequestEvent struct {
	// DeploymentID identifies the exact DeploymentScheduledEvent to cancel.
	DeploymentID string

	// Reason describes why the deployment is being cancelled.
	Reason string

	Correlation
	// contains filtered or unexported fields
}

DeploymentCancelRequestEvent is published when the scheduler requests cancellation of an in-progress deployment (e.g., due to timeout).

Published by: DeploymentScheduler (on timeout) Consumed by: Deployer (to cancel running deployment)

DeploymentID must match the scheduled event being cancelled.

func NewDeploymentCancelRequestEvent

func NewDeploymentCancelRequestEvent(deploymentID, reason string, opts ...CorrelationOption) *DeploymentCancelRequestEvent

NewDeploymentCancelRequestEvent creates a new DeploymentCancelRequestEvent.

func (*DeploymentCancelRequestEvent) EventType

func (e *DeploymentCancelRequestEvent) EventType() string

func (*DeploymentCancelRequestEvent) Timestamp

func (t *DeploymentCancelRequestEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type DeploymentCompletedEvent

type DeploymentCompletedEvent struct {

	// CycleSnapshot is the exact render cycle this deployment applied.
	CycleSnapshot *rendercycle.Snapshot

	// OutputSnapshot is the exact render this deployment applied.
	OutputSnapshot *renderoutput.Snapshot

	// DeploymentID identifies the exact DeploymentScheduledEvent that finished.
	DeploymentID string

	Total              int // Total number of instances
	Succeeded          int // Number of successful deployments
	Failed             int // Number of failed deployments
	PendingReloads     int // Pods holding the render behind a paced reload
	PendingReloadUntil time.Time
	DurationMs         int64 // Total deployment duration in milliseconds
	ReloadsTriggered   int   // Count of instances that triggered HAProxy reload
	TotalAPIOperations int   // Sum of API operations across all instances
	// Phases is the slowest accepted pod's split of DurationMs; nil when no
	// pod accepted.
	Phases *DeployPhases

	// OperationBreakdown provides a generic breakdown of operations performed.
	// Keys are formatted as "section_type" (e.g., "backend_create", "server_update", "global_update").
	// Values are the count of operations of that type.
	// Aggregated across all successfully deployed instances.
	OperationBreakdown map[string]int

	// BackendDiffFields summarizes which BackendBase fields caused backend updates.
	// Empty when no backend attribute diffs were detected.
	// Example: "[GUID] (48 backends)" or "[Mode, Balance] (3 backends)"
	BackendDiffFields string

	// StatusPatches are the chart-rendered status patches that correspond to
	// the configuration this deployment carried. The StatusApplier reads them
	// from this event and applies the "deployed" variant — guaranteeing that
	// the status conditions it writes describe the config the data plane is
	// actually serving (no side-channel cache, no LATEST-vs-deployed race).
	//
	// Threaded through unchanged from the DeploymentScheduledEvent that
	// triggered this deployment.
	StatusPatches []templating.StatusPatch

	// StatusPatchSnapshot carries the immutable patch set of this deployment.
	StatusPatchSnapshot *templating.StatusPatchSnapshot

	// ContentChecksum is the checksum of the config + auxiliary files THIS
	// deployment actually pushed to the data plane. Threaded through
	// unchanged from the DeploymentScheduledEvent so the DeploymentScheduler
	// can update its lastDeployedConfigHash from a value that's tied to the
	// completing deployment, not from the latest render (which a parallel
	// reconcile may have overwritten while this deployment was in flight).
	//
	// Without this, the scheduler reads s.lastContentChecksum at completion
	// time and mis-records the in-flight render's checksum as "what was just
	// deployed" — silently making future deployments with the same hash
	// hit the unchanged-skip branch and never reach HAProxy. Symptom in CI:
	// a freshly-added Ingress's redirect/auth directive never appears in
	// the live haproxy.cfg even though the controller did render it.
	//
	// Empty string for the zero-endpoint code path (nothing was deployed).
	ContentChecksum string
	RenderProof     string
	Plan            *renderplan.Plan

	// PodSetHash identifies the endpoint authorities THIS deployment targeted.
	// It is captured from DeploymentScheduledEvent.Endpoints before execution.
	PodSetHash string

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

DeploymentCompletedEvent is published when deployment to all HAProxy instances completes.

This event propagates the correlation ID from DeploymentStartedEvent.

func NewDeploymentCompletedEvent

func NewDeploymentCompletedEvent(result *DeploymentResult, opts ...CorrelationOption) *DeploymentCompletedEvent

NewDeploymentCompletedEvent creates a new DeploymentCompletedEvent.

`result` is taken by pointer because DeploymentResult is large enough (≥96 bytes) that gocritic flags pass-by-value as `hugeParam`.

`result.StatusPatches` should be forwarded unchanged from the DeploymentScheduledEvent that triggered the deployment so the StatusApplier reads the patches that correspond exactly to the configuration that just shipped (the chart's "deployed" variant).

Use PropagateCorrelation() to propagate correlation from the triggering event:

event := events.NewDeploymentCompletedEvent(&events.DeploymentResult{
    Total:              len(endpoints),
    Succeeded:          successCount,
    Failed:             failureCount,
    DurationMs:         totalDurationMs,
    ReloadsTriggered:   reloads,
    TotalAPIOperations: ops,
    OperationBreakdown: breakdown,
    StatusPatches:      scheduledEvent.StatusPatches, // forward unchanged
}, events.PropagateCorrelation(startedEvent))

func NewDeploymentCompletedEventWithCycle

func NewDeploymentCompletedEventWithCycle(result *DeploymentResult, opts ...CorrelationOption) (*DeploymentCompletedEvent, error)

NewDeploymentCompletedEventWithCycle creates a production completion.

func NewDeploymentCompletedEventWithOutputSnapshot

func NewDeploymentCompletedEventWithOutputSnapshot(result *DeploymentResult, opts ...CorrelationOption) (*DeploymentCompletedEvent, error)

NewDeploymentCompletedEventWithOutputSnapshot creates a production completion.

func (DeploymentCompletedEvent) AuthenticatedRenderIdentity

func (c DeploymentCompletedEvent) AuthenticatedRenderIdentity() (*rendercycle.Snapshot, string, error)

AuthenticatedRenderIdentity returns the exact cycle and diagnostic proof.

func (*DeploymentCompletedEvent) CloneForSubscriber

func (e *DeploymentCompletedEvent) CloneForSubscriber() busevents.Event

CloneForSubscriber restores authenticated shadows and isolates legacy payloads.

func (*DeploymentCompletedEvent) Coalescible

func (e *DeploymentCompletedEvent) Coalescible() bool

Coalescible implements busevents.CoalescibleEvent. A completed event is a full-state notification: it carries the complete status patch set of the config the deploy shipped, so for consumers that declare it in their CoalescesOn list (only the status applier) the newest of an uninterrupted run supersedes the older ones. Consumers with per-event bookkeeping (the deployer clears its in-flight flag per completion) must simply not declare this type — coalescing is always per-subscriber opt-in.

func (*DeploymentCompletedEvent) EventType

func (e *DeploymentCompletedEvent) EventType() string

func (DeploymentCompletedEvent) RenderOccurrence

func (c DeploymentCompletedEvent) RenderOccurrence() (*rendercycle.Occurrence, error)

RenderOccurrence returns the exact opaque occurrence carried by the event.

func (*DeploymentCompletedEvent) Timestamp

func (t *DeploymentCompletedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type DeploymentResult

type DeploymentResult struct {

	// CycleSnapshot is the exact render cycle this deployment applied.
	CycleSnapshot *rendercycle.Snapshot

	// OutputSnapshot is the exact render this deployment applied.
	OutputSnapshot *renderoutput.Snapshot

	// DeploymentID is the EventID of the DeploymentScheduledEvent being completed.
	DeploymentID string

	Total              int   // Total number of instances
	Succeeded          int   // Number of successful deployments
	Failed             int   // Number of failed deployments
	DurationMs         int64 // Total deployment duration in milliseconds
	ReloadsTriggered   int   // Count of instances that triggered HAProxy reload
	TotalAPIOperations int   // Sum of API operations across all instances
	// PendingReloads are pods that accepted the render but hold it behind a
	// paced reload; PendingReloadUntil is when the latest of them fires.
	PendingReloads     int
	PendingReloadUntil time.Time

	// OperationBreakdown provides a generic breakdown of operations performed.
	// Keys are formatted as "section_type" (e.g., "backend_create", "server_update", "global_update").
	// Values are the count of operations of that type.
	OperationBreakdown map[string]int

	// BackendDiffFields summarizes which BackendBase fields caused backend updates.
	// Empty when no backend attribute diffs were detected.
	BackendDiffFields string

	// StatusPatches are the chart-rendered status patches for the
	// configuration this deployment carried. Forwarded from the
	// DeploymentScheduledEvent and surfaced on DeploymentCompletedEvent for
	// the StatusApplier to consume.
	StatusPatches []templating.StatusPatch

	// StatusPatchSnapshot carries the immutable patch set of this deployment.
	StatusPatchSnapshot *templating.StatusPatchSnapshot

	// ContentChecksum is the checksum of the config + auxiliary files
	// THIS deployment pushed (forwarded from DeploymentScheduledEvent).
	// See DeploymentCompletedEvent.ContentChecksum for the full rationale.
	// Empty when no deployment occurred (zero-endpoint path).
	ContentChecksum string
	RenderProof     string
	Plan            *renderplan.Plan

	// PodSetHash identifies the endpoint authorities THIS deployment targeted.
	// Empty when no deployment occurred (zero-endpoint path).
	PodSetHash string

	// Phases is the slowest accepted pod's split; nil when no pod accepted.
	Phases *DeployPhases
	// contains filtered or unexported fields
}

DeploymentResult contains the outcome of a deployment operation. Used with NewDeploymentCompletedEvent for cleaner parameter passing.

func NewDeploymentResultWithOccurrence

func NewDeploymentResultWithOccurrence(occurrence *rendercycle.Occurrence) (*DeploymentResult, error)

NewDeploymentResultWithOccurrence creates a production result identity.

func (DeploymentResult) AuthenticatedRenderIdentity

func (c DeploymentResult) AuthenticatedRenderIdentity() (*rendercycle.Snapshot, string, error)

AuthenticatedRenderIdentity returns the exact cycle and diagnostic proof.

func (DeploymentResult) RenderOccurrence

func (c DeploymentResult) RenderOccurrence() (*rendercycle.Occurrence, error)

RenderOccurrence returns the exact opaque occurrence carried by the event.

type DeploymentScheduledEvent

type DeploymentScheduledEvent struct {

	// CycleSnapshot is the exact render cycle to deploy.
	CycleSnapshot *rendercycle.Snapshot

	// OutputSnapshot is the exact render to deploy.
	OutputSnapshot *renderoutput.Snapshot

	// Config is the rendered HAProxy configuration to deploy.
	Config string

	// AuxiliaryFiles contains all rendered auxiliary files.
	AuxiliaryFiles *dataplane.AuxiliaryFiles

	// Endpoints is the list of HAProxy endpoints to deploy to.
	Endpoints []dataplane.Endpoint

	// RuntimeConfigName is the name of the HAProxyCfg resource.
	// Used for publishing ConfigAppliedToPodEvent after successful deployment.
	RuntimeConfigName string

	// RuntimeConfigNamespace is the namespace of the HAProxyCfg resource.
	// Used for publishing ConfigAppliedToPodEvent after successful deployment.
	RuntimeConfigNamespace string

	// ContentChecksum is the pre-computed content checksum covering config + aux files.
	// Propagated from TemplateRenderedEvent to enable aux file comparison caching
	// in the deployer — when the checksum matches the last-deployed checksum for
	// an endpoint, the expensive aux file comparison is skipped.
	ContentChecksum string

	// Reason describes why this deployment was scheduled.
	// Examples: "config_validation", "pod_discovery", "drift_prevention"
	Reason string

	// Plan is the structure of the render being deployed, propagated from
	// TemplateRenderedEvent. Nil when the render produced none.
	Plan *renderplan.Plan

	// PlanID is the digest identifying Plan; it becomes the pod's applied plan
	// id once the deployment lands.
	PlanID string

	// RenderProof is the controller-local witness for this exact render.
	RenderProof string

	// StatusPatches are the chart-rendered status patches for this
	// configuration. The Deployer forwards them unchanged into
	// DeploymentCompletedEvent so the StatusApplier can apply the
	// "deployed" variant with the patches that correspond exactly to
	// the config this deployment shipped.
	StatusPatches []templating.StatusPatch

	// StatusPatchSnapshot carries the immutable patch set for this configuration.
	StatusPatchSnapshot *templating.StatusPatchSnapshot

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

DeploymentScheduledEvent is published when the deployment scheduler has decided. to execute a deployment. This event contains all necessary data for the deployer to execute the deployment without maintaining state.

Published by: DeploymentScheduler. Consumed by: Deployer component.

This event propagates the correlation ID from TemplateRenderedEvent.

This event implements CoalescibleEvent. The coalescible flag is propagated from TemplateRenderedEvent to enable coalescing throughout the reconciliation pipeline.

func NewDeploymentScheduledEvent

func NewDeploymentScheduledEvent(config string, auxFiles *dataplane.AuxiliaryFiles, endpoints []dataplane.Endpoint, runtimeConfigName, runtimeConfigNamespace, reason, contentChecksum string, plan *renderplan.Plan, planID string, statusPatches []templating.StatusPatch, coalescible bool, opts ...CorrelationOption) *DeploymentScheduledEvent

NewDeploymentScheduledEvent creates a new DeploymentScheduledEvent. Performs defensive copy of endpoints slice.

The coalescible parameter should be propagated from TemplateRenderedEvent.Coalescible() to enable coalescing throughout the reconciliation pipeline.

The contentChecksum is the pre-computed checksum of config + aux files, propagated from TemplateRenderedEvent. It enables the deployer to skip expensive aux file comparison when the content hasn't changed since the last successful sync to an endpoint.

statusPatches is the chart-rendered patch set for this configuration. The Deployer forwards it unchanged into DeploymentCompletedEvent so the StatusApplier can apply the "deployed" variant with the patches that correspond exactly to the config this deployment shipped. The outer slice is defensively cloned.

Use PropagateCorrelation() to propagate correlation from the triggering event:

event := events.NewDeploymentScheduledEvent(config, auxFiles, endpoints, name, ns, reason, contentChecksum, plan, planID, statusPatches, coalescible,
    events.PropagateCorrelation(validationEvent))

func NewDeploymentScheduledEventWithCycle

func NewDeploymentScheduledEventWithCycle(
	occurrence *rendercycle.Occurrence,
	endpoints []dataplane.Endpoint,
	runtimeConfigName, runtimeConfigNamespace, reason string,
	coalescible bool,
	opts ...CorrelationOption,
) (*DeploymentScheduledEvent, error)

NewDeploymentScheduledEventWithCycle creates a production deployment.

func NewDeploymentScheduledEventWithOutputSnapshot

func NewDeploymentScheduledEventWithOutputSnapshot(
	occurrence *rendercycle.Occurrence,
	endpoints []dataplane.Endpoint,
	runtimeConfigName, runtimeConfigNamespace, reason string,
	coalescible bool,
	opts ...CorrelationOption,
) (*DeploymentScheduledEvent, error)

NewDeploymentScheduledEventWithOutputSnapshot creates a production deployment.

func NewDeploymentScheduledEventWithStatusSnapshot

func NewDeploymentScheduledEventWithStatusSnapshot(config string, auxFiles *dataplane.AuxiliaryFiles, endpoints []dataplane.Endpoint, runtimeConfigName, runtimeConfigNamespace, reason, contentChecksum string, plan *renderplan.Plan, planID, renderProof string, statusPatchSnapshot *templating.StatusPatchSnapshot, coalescible bool, opts ...CorrelationOption) *DeploymentScheduledEvent

NewDeploymentScheduledEventWithStatusSnapshot preserves immutable status payloads.

func (DeploymentScheduledEvent) AuthenticatedRenderIdentity

func (c DeploymentScheduledEvent) AuthenticatedRenderIdentity() (*rendercycle.Snapshot, string, error)

AuthenticatedRenderIdentity returns the exact cycle and diagnostic proof.

func (*DeploymentScheduledEvent) CloneForSubscriber

func (e *DeploymentScheduledEvent) CloneForSubscriber() busevents.Event

CloneForSubscriber restores authenticated shadows and isolates legacy payloads.

func (*DeploymentScheduledEvent) Coalescible

func (e *DeploymentScheduledEvent) Coalescible() bool

Coalescible returns true if this event can be safely skipped when a newer event of the same type is available. This implements the CoalescibleEvent interface.

func (*DeploymentScheduledEvent) EventType

func (e *DeploymentScheduledEvent) EventType() string

func (DeploymentScheduledEvent) RenderOccurrence

func (c DeploymentScheduledEvent) RenderOccurrence() (*rendercycle.Occurrence, error)

RenderOccurrence returns the exact opaque occurrence carried by the event.

func (*DeploymentScheduledEvent) Timestamp

func (t *DeploymentScheduledEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type DeploymentSkippedEvent

type DeploymentSkippedEvent struct {

	// CycleSnapshot is the exact render cycle already served by the fleet.
	CycleSnapshot *rendercycle.Snapshot

	// OutputSnapshot is the exact render already served by the fleet.
	OutputSnapshot *renderoutput.Snapshot

	// Total is the number of HAProxy endpoints already serving the rendered
	// configuration. Mirrors DeploymentCompletedEvent.Total so subscribers
	// can apply the same "is there actually a data plane to talk to?" guard.
	Total int

	// Reason is a short tag describing why no deployment ran for this
	// config: SkipReasonConfigUnchanged or SkipReasonReloadObserved.
	Reason string

	// ConfigHash is the content checksum of the rendered HAProxy
	// configuration that matched the last successful deployment. Useful
	// for debugging / correlation across the deployer's logs.
	ConfigHash string

	// PodSetHash is the hash of the endpoint set that matched the last
	// successful deployment. Useful for debugging / correlation.
	PodSetHash string

	// StatusPatches are the chart-rendered status patches for the
	// already-deployed configuration. The StatusApplier reads them from
	// this event to write the "deployed" variant — the data plane is
	// serving this exact config, so conditions gated on data-plane
	// readiness should reflect the current generation.
	StatusPatches []templating.StatusPatch

	// StatusPatchSnapshot carries the immutable patch set already deployed.
	StatusPatchSnapshot *templating.StatusPatchSnapshot
	RenderProof         string
	Plan                *renderplan.Plan

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

DeploymentSkippedEvent is published when the deployment scheduler determines that the data plane is already at the just-rendered configuration and no deployment work needs to be performed (typically: rendered config hash and pod-set hash both match the last successful deployment).

Semantically this is NOT a deployment — nothing was pushed, no reload was triggered, no API operations were issued. It exists as its own event type so that downstream consumers can distinguish "the controller is converged" from "the controller just completed work."

Currently consumed by:

  • statusapplier, which treats this equivalently to DeploymentCompletedEvent for the purpose of applying the "deployed" status-patch variant — the data plane is serving the latest config, so Kubernetes status conditions gated on data-plane readiness should reflect the current generation.

Other consumers (metrics, commentator, drift_monitor, scheduler, statecache) do not subscribe by design — skipped deployments are a steady-state signal and bursting through those consumers would either produce log spam (commentator) or misleading counters (metrics). They can opt in later if there's a concrete need.

This event propagates the correlation ID from the triggering event (typically TemplateRenderedEvent) so the converged path remains observable in correlation-based tracing.

func NewDeploymentSkippedEvent

func NewDeploymentSkippedEvent(total int, reason, configHash, podSetHash string, statusPatches []templating.StatusPatch, opts ...CorrelationOption) *DeploymentSkippedEvent

NewDeploymentSkippedEvent creates a new DeploymentSkippedEvent.

statusPatches is the chart-rendered patch set for the already-deployed configuration; the StatusApplier reads it from the event to write the "deployed" variant. The outer slice is defensively cloned per the immutability contract documented in events/CLAUDE.md.

Use PropagateCorrelation() to propagate correlation from the triggering event so the skip remains correlated with the originating reconciliation:

event := events.NewDeploymentSkippedEvent(
    len(endpoints),
    "config_unchanged",
    configHash,
    podSetHash,
    statusPatches,
    events.PropagateCorrelation(scheduledEvent),
)

func NewDeploymentSkippedEventWithCycle

func NewDeploymentSkippedEventWithCycle(
	occurrence *rendercycle.Occurrence,
	total int,
	reason string,
	podSetHash string,
	opts ...CorrelationOption,
) (*DeploymentSkippedEvent, error)

NewDeploymentSkippedEventWithCycle carries one exact render cycle.

func NewDeploymentSkippedEventWithIdentity

func NewDeploymentSkippedEventWithIdentity(
	total int,
	reason string,
	configHash string,
	podSetHash string,
	statusPatches []templating.StatusPatch,
	renderProof string,
	plan *renderplan.Plan,
	opts ...CorrelationOption,
) *DeploymentSkippedEvent

NewDeploymentSkippedEventWithIdentity carries the exact render the fleet is already serving.

func NewDeploymentSkippedEventWithOutputSnapshot

func NewDeploymentSkippedEventWithOutputSnapshot(
	occurrence *rendercycle.Occurrence,
	total int,
	reason string,
	podSetHash string,
	opts ...CorrelationOption,
) (*DeploymentSkippedEvent, error)

NewDeploymentSkippedEventWithOutputSnapshot carries exact occurrence identity.

func NewDeploymentSkippedEventWithStatusSnapshot

func NewDeploymentSkippedEventWithStatusSnapshot(
	total int,
	reason string,
	configHash string,
	podSetHash string,
	statusPatchSnapshot *templating.StatusPatchSnapshot,
	renderProof string,
	plan *renderplan.Plan,
	opts ...CorrelationOption,
) *DeploymentSkippedEvent

NewDeploymentSkippedEventWithStatusSnapshot carries an immutable patch set.

func (DeploymentSkippedEvent) AuthenticatedRenderIdentity

func (c DeploymentSkippedEvent) AuthenticatedRenderIdentity() (*rendercycle.Snapshot, string, error)

AuthenticatedRenderIdentity returns the exact cycle and diagnostic proof.

func (*DeploymentSkippedEvent) CloneForSubscriber

func (e *DeploymentSkippedEvent) CloneForSubscriber() busevents.Event

CloneForSubscriber restores authenticated shadows and isolates legacy payloads.

func (*DeploymentSkippedEvent) Coalescible

func (e *DeploymentSkippedEvent) Coalescible() bool

Coalescible implements busevents.CoalescibleEvent — same full-state latest-wins rationale as DeploymentCompletedEvent.Coalescible.

func (*DeploymentSkippedEvent) EventType

func (e *DeploymentSkippedEvent) EventType() string

func (DeploymentSkippedEvent) RenderOccurrence

func (c DeploymentSkippedEvent) RenderOccurrence() (*rendercycle.Occurrence, error)

RenderOccurrence returns the exact opaque occurrence carried by the event.

func (*DeploymentSkippedEvent) Timestamp

func (t *DeploymentSkippedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type DeploymentStartedEvent

type DeploymentStartedEvent struct {
	// EndpointCount is the number of HAProxy instances this deploy targets.
	// Only the count is carried: subscribers (statecache, commentator) never
	// read more than len(), and carrying the full slice forced a defensive
	// deep-copy of every endpoint's address and credentials on each publish.
	EndpointCount int

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

DeploymentStartedEvent is published when deployment to HAProxy instances begins.

This event propagates the correlation ID from DeploymentScheduledEvent.

func NewDeploymentStartedEvent

func NewDeploymentStartedEvent(endpointCount int, opts ...CorrelationOption) *DeploymentStartedEvent

NewDeploymentStartedEvent creates a new DeploymentStartedEvent.

Use PropagateCorrelation() to propagate correlation from the triggering event:

event := events.NewDeploymentStartedEvent(len(endpoints),
    events.PropagateCorrelation(scheduledEvent))

func (*DeploymentStartedEvent) EventType

func (e *DeploymentStartedEvent) EventType() string

func (*DeploymentStartedEvent) Timestamp

func (t *DeploymentStartedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type DriftPreventionTriggeredEvent

type DriftPreventionTriggeredEvent struct {
	// TimeSinceLastDeployment is the duration since the last deployment completed.
	TimeSinceLastDeployment time.Duration
	// contains filtered or unexported fields
}

DriftPreventionTriggeredEvent is published when the drift prevention monitor. detects that no deployment has occurred within the configured interval and triggers a deployment to prevent configuration drift.

Published by: DriftPreventionMonitor. Consumed by: DeploymentScheduler (which then schedules a deployment).

func NewDriftPreventionTriggeredEvent

func NewDriftPreventionTriggeredEvent(timeSinceLast time.Duration) *DriftPreventionTriggeredEvent

NewDriftPreventionTriggeredEvent creates a new DriftPreventionTriggeredEvent.

func (*DriftPreventionTriggeredEvent) EventType

func (e *DriftPreventionTriggeredEvent) EventType() string

func (*DriftPreventionTriggeredEvent) Timestamp

func (t *DriftPreventionTriggeredEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type HAProxyPodRejectedEvent

type HAProxyPodRejectedEvent struct {
	// PodName is the rejected pod's name (used for correlation with
	// k8s events / pod logs).
	PodName string
	// Reason categorises the rejection. Stable identifiers used as a
	// Prometheus label, so prefer a fixed enum:
	//   - "version_mismatch_older" — the probed version is older than supported
	//   - "version_mismatch_newer" — the probed version is newer than supported
	//   - "version_check_failed"   — could not probe remote version (transient)
	Reason string
	// contains filtered or unexported fields
}

HAProxyPodRejectedEvent is published by the discovery component when a candidate HAProxy pod is refused admission. The most common cause is an unreachable agent or an HAProxy series mismatch with the controller's bundled binary. Surfaced via Prometheus (haptic_haproxy_pods_rejected_total{reason}) so operators can alert on "controller refuses to talk to N HAProxy pods" without log-grepping.

func NewHAProxyPodRejectedEvent

func NewHAProxyPodRejectedEvent(podName, reason string) *HAProxyPodRejectedEvent

NewHAProxyPodRejectedEvent creates a new HAProxyPodRejectedEvent.

func (*HAProxyPodRejectedEvent) EventType

func (e *HAProxyPodRejectedEvent) EventType() string

func (*HAProxyPodRejectedEvent) Timestamp

func (t *HAProxyPodRejectedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type HAProxyPodTerminatedEvent

type HAProxyPodTerminatedEvent struct {
	PodName      string
	PodNamespace string
	PodUID       string
	// contains filtered or unexported fields
}

HAProxyPodTerminatedEvent is published when a HAProxy pod authority retires.

This triggers cleanup of the pod from all runtime config status fields.

func NewHAProxyPodTerminatedEvent

func NewHAProxyPodTerminatedEvent(podName, podNamespace, podUID string) *HAProxyPodTerminatedEvent

NewHAProxyPodTerminatedEvent creates a new HAProxyPodTerminatedEvent.

func (*HAProxyPodTerminatedEvent) EventType

func (e *HAProxyPodTerminatedEvent) EventType() string

func (*HAProxyPodTerminatedEvent) Timestamp

func (t *HAProxyPodTerminatedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type HAProxyPodsDiscoveredEvent

type HAProxyPodsDiscoveredEvent struct {
	// Endpoints is the list of discovered HAProxy pods, by their agent endpoint.
	Endpoints []dataplane.Endpoint
	Count     int
	// contains filtered or unexported fields
}

HAProxyPodsDiscoveredEvent is published when HAProxy pods are discovered or updated. This event is always coalescible since it represents endpoint state where only the latest set of endpoints matters.

func NewHAProxyPodsDiscoveredEvent

func NewHAProxyPodsDiscoveredEvent(endpoints []dataplane.Endpoint, count int) *HAProxyPodsDiscoveredEvent

NewHAProxyPodsDiscoveredEvent creates a new HAProxyPodsDiscoveredEvent. Performs defensive copy of the endpoints slice.

func (*HAProxyPodsDiscoveredEvent) Coalescible

func (e *HAProxyPodsDiscoveredEvent) Coalescible() bool

Coalescible returns true because endpoint discovery events represent state where only the latest set of endpoints matters. Older discoveries can be safely skipped during high-frequency pod churn (scaling, rolling updates).

func (*HAProxyPodsDiscoveredEvent) EventType

func (e *HAProxyPodsDiscoveredEvent) EventType() string

func (*HAProxyPodsDiscoveredEvent) Timestamp

func (t *HAProxyPodsDiscoveredEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type HTTPContentAcceptanceRequestedEvent

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

HTTPContentAcceptanceRequestedEvent is published when a deploying render left out HTTP content no render has accepted yet. The leader answers it with an acceptance attempt outside the reconcile loop.

func NewHTTPContentAcceptanceRequestedEvent

func NewHTTPContentAcceptanceRequestedEvent() *HTTPContentAcceptanceRequestedEvent

NewHTTPContentAcceptanceRequestedEvent creates a new HTTPContentAcceptanceRequestedEvent.

func (*HTTPContentAcceptanceRequestedEvent) EventType

func (*HTTPContentAcceptanceRequestedEvent) Timestamp

func (t *HTTPContentAcceptanceRequestedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type HTTPContentRejectedEvent

type HTTPContentRejectedEvent struct {
	URL string // Credentials, query, and fragment are redacted.
	// contains filtered or unexported fields
}

HTTPContentRejectedEvent reports pending content refused before acceptance.

func NewHTTPContentRejectedEvent

func NewHTTPContentRejectedEvent(url string) *HTTPContentRejectedEvent

func (*HTTPContentRejectedEvent) EventType

func (e *HTTPContentRejectedEvent) EventType() string

func (*HTTPContentRejectedEvent) Timestamp

func (t *HTTPContentRejectedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type HTTPContentRevokedEvent

type HTTPContentRevokedEvent struct {
	URL             string // The URL whose content was revoked (credentials redacted)
	ContentChecksum string // SHA256 checksum of the revoked content
	Restored        bool   // The URL went back to its previous accepted content
	Critical        bool   // Renders fail until the URL has accepted content again
	// contains filtered or unexported fields
}

HTTPContentRevokedEvent is published when accepted HTTP content was taken back because HAProxy refused a render containing it.

func NewHTTPContentRevokedEvent

func NewHTTPContentRevokedEvent(url, checksum string, restored, critical bool) *HTTPContentRevokedEvent

NewHTTPContentRevokedEvent creates a new HTTPContentRevokedEvent.

func (*HTTPContentRevokedEvent) EventType

func (e *HTTPContentRevokedEvent) EventType() string

func (*HTTPContentRevokedEvent) Timestamp

func (t *HTTPContentRevokedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type HTTPResourceAcceptedEvent

type HTTPResourceAcceptedEvent struct {
	URL             string // The URL whose content was accepted
	ContentChecksum string // SHA256 checksum of accepted content
	ContentSize     int    // Size of accepted content in bytes
	// contains filtered or unexported fields
}

HTTPResourceAcceptedEvent is published when pending HTTP content passes validation. The content has been promoted from "pending" to "accepted" state.

func NewHTTPResourceAcceptedEvent

func NewHTTPResourceAcceptedEvent(url, checksum string, size int) *HTTPResourceAcceptedEvent

NewHTTPResourceAcceptedEvent creates a new HTTPResourceAcceptedEvent.

func (*HTTPResourceAcceptedEvent) EventType

func (e *HTTPResourceAcceptedEvent) EventType() string

func (*HTTPResourceAcceptedEvent) Timestamp

func (t *HTTPResourceAcceptedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type HTTPResourceUpdatedEvent

type HTTPResourceUpdatedEvent struct {
	URL             string // The URL that was refreshed
	ContentChecksum string // SHA256 checksum of new content
	ContentSize     int    // Size of new content in bytes
	// contains filtered or unexported fields
}

HTTPResourceUpdatedEvent is published when HTTP resource content has changed. This triggers a reconciliation cycle with the new content as "pending". The content must pass validation before being promoted to "accepted". This event is always coalescible since it represents content state where only the latest content for a URL matters.

func NewHTTPResourceUpdatedEvent

func NewHTTPResourceUpdatedEvent(url, checksum string, size int) *HTTPResourceUpdatedEvent

NewHTTPResourceUpdatedEvent creates a new HTTPResourceUpdatedEvent.

func (*HTTPResourceUpdatedEvent) Coalescible

func (e *HTTPResourceUpdatedEvent) Coalescible() bool

Coalescible returns true because HTTP resource update events represent state where only the latest content matters. If the same URL updates multiple times before reconciliation completes, older updates can be safely skipped.

func (*HTTPResourceUpdatedEvent) EventType

func (e *HTTPResourceUpdatedEvent) EventType() string

func (*HTTPResourceUpdatedEvent) Timestamp

func (t *HTTPResourceUpdatedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type IndexSynchronizedEvent

type IndexSynchronizedEvent struct {
	// ResourceCounts maps resource types to their counts.
	ResourceCounts map[string]int
	// contains filtered or unexported fields
}

IndexSynchronizedEvent is published when all resource watchers have completed. their initial sync and the system has a complete view of all resources.

This is a critical milestone - the controller waits for this event before. starting reconciliation to ensure it has complete data.

func NewIndexSynchronizedEvent

func NewIndexSynchronizedEvent(resourceCounts map[string]int) *IndexSynchronizedEvent

NewIndexSynchronizedEvent creates a new IndexSynchronizedEvent. Performs defensive copy of the resource counts map.

func (*IndexSynchronizedEvent) EventType

func (e *IndexSynchronizedEvent) EventType() string

func (*IndexSynchronizedEvent) Timestamp

func (t *IndexSynchronizedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type InputRejection

type InputRejection struct {
	Object  corev1.ObjectReference
	Deleted bool
	Reason  string
}

InputRejection identifies a resource revision excluded from accepted inputs.

type InstanceDeploymentFailedEvent

type InstanceDeploymentFailedEvent struct {
	Endpoint  any
	Error     string
	Retryable bool // Whether this failure is retryable

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

InstanceDeploymentFailedEvent is published when deployment to a single HAProxy instance fails.

This event propagates the correlation ID from DeploymentStartedEvent.

func NewInstanceDeploymentFailedEvent

func NewInstanceDeploymentFailedEvent(endpoint any, err string, retryable bool, opts ...CorrelationOption) *InstanceDeploymentFailedEvent

NewInstanceDeploymentFailedEvent creates a new InstanceDeploymentFailedEvent.

Use PropagateCorrelation() to propagate correlation from the triggering event:

event := events.NewInstanceDeploymentFailedEvent(endpoint, err, retryable,
    events.PropagateCorrelation(startedEvent))

func (*InstanceDeploymentFailedEvent) EventType

func (e *InstanceDeploymentFailedEvent) EventType() string

func (*InstanceDeploymentFailedEvent) Timestamp

func (t *InstanceDeploymentFailedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type LeaderElectionStartedEvent

type LeaderElectionStartedEvent struct {
	Identity       string
	LeaseName      string
	LeaseNamespace string
	// contains filtered or unexported fields
}

LeaderElectionStartedEvent is published when leader election is initiated.

func NewLeaderElectionStartedEvent

func NewLeaderElectionStartedEvent(identity, leaseName, leaseNamespace string) *LeaderElectionStartedEvent

NewLeaderElectionStartedEvent creates a new LeaderElectionStartedEvent.

func (*LeaderElectionStartedEvent) EventType

func (e *LeaderElectionStartedEvent) EventType() string

func (*LeaderElectionStartedEvent) Timestamp

func (t *LeaderElectionStartedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type LibrarySetChangedEvent

type LibrarySetChangedEvent struct {
	// Snippets holds the raw HAProxyTemplateLibrary resources.
	// Type: []any to avoid circular dependencies.
	// Consumers should type-assert elements to *unstructured.Unstructured.
	Snippets []any
	// contains filtered or unexported fields
}

LibrarySetChangedEvent carries every HAProxyTemplateLibrary the controller can currently see, whenever any of them changes.

It is a whole-set snapshot rather than a per-object delta because the loader needs the complete set to decide whether a config's references resolve, and because a deletion then needs no representation of its own: the object is simply absent from the next snapshot.

func NewLibrarySetChangedEvent

func NewLibrarySetChangedEvent(snippets []any) *LibrarySetChangedEvent

NewLibrarySetChangedEvent creates a new LibrarySetChangedEvent. Performs a defensive copy of the slice.

func (*LibrarySetChangedEvent) EventType

func (e *LibrarySetChangedEvent) EventType() string

func (*LibrarySetChangedEvent) Timestamp

func (t *LibrarySetChangedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type LostLeadershipEvent

type LostLeadershipEvent struct {
	Identity string
	Reason   string // graceful_shutdown, lease_expired, etc.
	// contains filtered or unexported fields
}

LostLeadershipEvent is published when this replica loses leadership.

func NewLostLeadershipEvent

func NewLostLeadershipEvent(identity, reason string) *LostLeadershipEvent

NewLostLeadershipEvent creates a new LostLeadershipEvent.

func (*LostLeadershipEvent) EventType

func (e *LostLeadershipEvent) EventType() string

func (*LostLeadershipEvent) Timestamp

func (t *LostLeadershipEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type NewLeaderObservedEvent

type NewLeaderObservedEvent struct {
	NewLeaderIdentity string
	IsSelf            bool // true if this replica is the new leader
	// contains filtered or unexported fields
}

NewLeaderObservedEvent is published when a new leader is observed.

func NewNewLeaderObservedEvent

func NewNewLeaderObservedEvent(newLeaderIdentity string, isSelf bool) *NewLeaderObservedEvent

NewNewLeaderObservedEvent creates a new NewLeaderObservedEvent.

func (*NewLeaderObservedEvent) EventType

func (e *NewLeaderObservedEvent) EventType() string

func (*NewLeaderObservedEvent) Timestamp

func (t *NewLeaderObservedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type OperationCounts

type OperationCounts struct {
	// Config operations
	TotalAPIOperations int
	BackendsAdded      int
	BackendsRemoved    int
	BackendsModified   int
	ServersAdded       int
	ServersRemoved     int
	ServersModified    int
	FrontendsAdded     int
	FrontendsRemoved   int
	FrontendsModified  int

	// Auxiliary file operations
	MapsAdded            int
	MapsRemoved          int
	MapsModified         int
	SSLCertsAdded        int
	SSLCertsRemoved      int
	SSLCertsModified     int
	GeneralFilesAdded    int
	GeneralFilesRemoved  int
	GeneralFilesModified int
}

OperationCounts provides statistics about sync operations.

type ProposalValidationCompletedEvent

type ProposalValidationCompletedEvent struct {
	// RequestID correlates this response to the original request.
	RequestID string

	// Valid is true if the proposed configuration passed all validation phases.
	Valid bool

	// Phase indicates which validation phase failed (syntax, schema, semantic).
	// Empty if Valid is true.
	Phase string

	// Error contains the validation error message if Valid is false.
	// Empty if Valid is true.
	Error string

	// DurationMs is the total validation duration in milliseconds.
	DurationMs int64
	// contains filtered or unexported fields
}

ProposalValidationCompletedEvent is published when proposal validation completes.

This event indicates whether the proposed configuration changes would result in a valid HAProxy configuration.

Contract:

  • Published by: ProposalValidator
  • Consumed by: HTTPStore, Webhook (via event subscription or sync call result)

func NewProposalValidationCompletedEvent

func NewProposalValidationCompletedEvent(requestID string, durationMs int64) *ProposalValidationCompletedEvent

NewProposalValidationCompletedEvent creates a successful validation completion event.

Parameters:

  • requestID: ID from the corresponding ProposalValidationRequestedEvent
  • durationMs: Total validation duration in milliseconds

Returns:

  • Immutable ProposalValidationCompletedEvent indicating success.

func NewProposalValidationFailedEvent

func NewProposalValidationFailedEvent(requestID, phase string, err error, durationMs int64) *ProposalValidationCompletedEvent

NewProposalValidationFailedEvent creates a failed validation completion event.

Parameters:

  • requestID: ID from the corresponding ProposalValidationRequestedEvent
  • phase: Validation phase that failed (syntax, schema, semantic, render)
  • err: The validation error
  • durationMs: Total validation duration in milliseconds

Returns:

  • Immutable ProposalValidationCompletedEvent indicating failure.

func (*ProposalValidationCompletedEvent) EventType

EventType implements the Event interface.

func (*ProposalValidationCompletedEvent) Timestamp

func (t *ProposalValidationCompletedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ProposalValidationRequestedEvent

type ProposalValidationRequestedEvent struct {
	// ID uniquely identifies this validation request for response correlation.
	ID string

	// Overlays maps store names to their proposed changes.
	// The ProposalValidator will create a CompositeStoreProvider using these.
	// Key: store name (e.g., "ingresses", "services")
	// Value: proposed changes for that store
	Overlays map[string]*stores.StoreOverlay

	// HTTPOverlay contains pending HTTP content changes for validation.
	// When present, the ProposalValidator includes this in the ValidationContext
	// so the render pipeline sees pending HTTP content.
	// Nil when validating K8s-only changes (e.g., webhook admission).
	HTTPOverlay stores.HTTPContentOverlay

	// Source identifies where this request originated from.
	// Examples: "httpstore", "webhook"
	Source string

	// SourceContext provides additional context about the source.
	// For httpstore: resource URL
	// For webhook: resource GVK and namespace/name
	SourceContext string
	// contains filtered or unexported fields
}

ProposalValidationRequestedEvent is published when a component wants to validate a hypothetical configuration change without deploying it.

This is used for: - HTTP store content validation (validate before accepting new content) - Webhook admission validation (validate resource changes before admission)

Contract:

  • Published by: HTTPStore, Webhook
  • Consumed by: ProposalValidator
  • Response: ProposalValidationCompletedEvent with matching RequestID

func NewProposalValidationRequestedEvent

func NewProposalValidationRequestedEvent(overlays map[string]*stores.StoreOverlay, httpOverlay stores.HTTPContentOverlay, source, sourceContext string) *ProposalValidationRequestedEvent

NewProposalValidationRequestedEvent creates a new proposal validation request.

Parameters:

  • overlays: Map of store name to proposed changes (can be nil for HTTP-only validation)
  • httpOverlay: HTTP content overlay (can be nil for K8s-only validation)
  • source: Identifier for the request source (e.g., "httpstore", "webhook")
  • sourceContext: Additional context about the source

Returns:

  • Immutable ProposalValidationRequestedEvent with unique ID.

func (*ProposalValidationRequestedEvent) EventType

EventType implements the Event interface.

func (*ProposalValidationRequestedEvent) RequestID

RequestID returns the unique request identifier for correlation.

func (*ProposalValidationRequestedEvent) Timestamp

func (t *ProposalValidationRequestedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ReconciliationCompletedEvent

type ReconciliationCompletedEvent struct {
	DurationMs int64

	// CycleSnapshot binds the complete output and effects of this reconciliation.
	CycleSnapshot *rendercycle.Snapshot

	// RenderProof identifies this occurrence of the authenticated cycle.
	RenderProof string

	// RenderedResources are the Kubernetes resources the templates declared
	// under spec.k8sResources in this cycle. The
	// ResourceApplier reads them directly from the event so it stays
	// stateless on the success path — patches/resources travel with the
	// event that triggers their apply, never via a side-channel cache. May
	// be nil when the render didn't emit any K8s resources.
	RenderedResources []templating.RenderedResource

	// RenderedResourceSnapshot is the authenticated immutable production representation.
	RenderedResourceSnapshot *templating.RenderedResourceSnapshot

	// StatusPatches are the chart-rendered status patches of this cycle.
	// The ResourceApplier forwards them on ResourcesAppliedEvent after its
	// apply pass so the StatusApplier writes the "rendered" variant only
	// AFTER the same render's infrastructure resources exist (conditions
	// must describe materialized state — conformance's GatewayInfrastructure
	// lists labeled resources the moment Accepted turns True).
	StatusPatches []templating.StatusPatch

	// StatusPatchSnapshot is the authenticated immutable production representation.
	StatusPatchSnapshot *templating.StatusPatchSnapshot

	// Events are the Kubernetes Events templates asked to emit this cycle via
	// recordEvent() (e.g. a RouteConflict Warning on an Ingress). The
	// EventEmitter (leader-only) reads them directly off this event and emits
	// them via an EventRecorder. May be nil when no template recorded an event.
	// The publisher sets this from a cloned slice before Publish; treat as
	// read-only like every other event field.
	Events []templating.RenderedEvent

	// EventSnapshot is the authenticated immutable production representation.
	EventSnapshot *templating.RenderedEventSnapshot

	// PlanID is the render this cycle produced, so a consumer can pair the
	// cycle with the render gate's later verdict on it.
	PlanID string

	// ProfileCount is the number of distinct backend profiles this render
	// emitted, harvested from the plan. The metrics component sets the
	// haptic_render_profiles gauge from it. Zero when the render produced no plan.
	ProfileCount int

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

ReconciliationCompletedEvent is published when a reconciliation cycle completes successfully.

This event propagates the correlation ID from the reconciliation chain.

func NewReconciliationCompletedEvent

func NewReconciliationCompletedEvent(
	durationMs int64,
	planID string,
	renderedResources []templating.RenderedResource,
	statusPatches []templating.StatusPatch,
	opts ...CorrelationOption,
) *ReconciliationCompletedEvent

NewReconciliationCompletedEvent creates a new ReconciliationCompletedEvent.

renderedResources is the slice of resources the templates declared under spec.k8sResources in this cycle. The outer slice is defensively cloned so publishers reusing a cached slice (e.g. coordinator forwarding PipelineResult.RenderedResources) can't mutate published events.

Use WithCorrelation() to propagate correlation from the pipeline:

event := events.NewReconciliationCompletedEvent(durationMs, resources,
    events.WithCorrelation(correlationID, causationID))

func NewReconciliationCompletedEventWithCycle

func NewReconciliationCompletedEventWithCycle(
	durationMs int64,
	occurrence *rendercycle.Occurrence,
	opts ...CorrelationOption,
) (*ReconciliationCompletedEvent, error)

NewReconciliationCompletedEventWithCycle creates a production completion.

func NewReconciliationCompletedEventWithStatusSnapshot

func NewReconciliationCompletedEventWithStatusSnapshot(
	durationMs int64,
	planID string,
	renderedResources []templating.RenderedResource,
	statusPatchSnapshot *templating.StatusPatchSnapshot,
	opts ...CorrelationOption,
) *ReconciliationCompletedEvent

NewReconciliationCompletedEventWithStatusSnapshot avoids detaching status payloads.

func (ReconciliationCompletedEvent) AuthenticatedRenderIdentity

func (c ReconciliationCompletedEvent) AuthenticatedRenderIdentity() (*rendercycle.Snapshot, string, error)

AuthenticatedRenderIdentity returns the exact cycle and diagnostic proof.

func (*ReconciliationCompletedEvent) CloneForSubscriber

func (e *ReconciliationCompletedEvent) CloneForSubscriber() busevents.Event

CloneForSubscriber restores authenticated shadows and isolates legacy payloads.

func (*ReconciliationCompletedEvent) Coalescible

func (e *ReconciliationCompletedEvent) Coalescible() bool

Coalescible implements busevents.CoalescibleEvent. A completed cycle is a full-state notification — RenderedResources and StatusPatches are the COMPLETE desired set of the render — so for consumers that declare it in their CoalescesOn list only the newest of an uninterrupted run matters.

func (*ReconciliationCompletedEvent) EventType

func (e *ReconciliationCompletedEvent) EventType() string

func (ReconciliationCompletedEvent) RenderOccurrence

func (c ReconciliationCompletedEvent) RenderOccurrence() (*rendercycle.Occurrence, error)

RenderOccurrence returns the exact opaque occurrence carried by the event.

func (*ReconciliationCompletedEvent) Timestamp

func (t *ReconciliationCompletedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ReconciliationFailedEvent

type ReconciliationFailedEvent struct {
	Error string
	Phase string // Which phase failed: "render", "validate", "deploy"

	// DurationMs is how long the failed cycle ran.
	DurationMs int64

	// StatusPatches are the chart-rendered status patches from the most
	// recent SUCCESSFUL render (the failure itself rarely produces patches —
	// render failures have none; validation failures have the just-rendered
	// set). The StatusApplier reads them to write the renderFailed /
	// deployFailed variant on the affected resources. May be nil if no
	// successful render has happened yet (early bootstrap failures); the
	// applier handles nil gracefully by skipping the apply.
	StatusPatches []templating.StatusPatch

	// StatusPatchSnapshot is the most recent successful immutable patch set.
	StatusPatchSnapshot *templating.StatusPatchSnapshot

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

ReconciliationFailedEvent is published when a reconciliation cycle fails.

This event propagates the correlation ID from the reconciliation chain.

func NewReconciliationFailedEvent

func NewReconciliationFailedEvent(err, phase string, statusPatches []templating.StatusPatch, opts ...CorrelationOption) *ReconciliationFailedEvent

NewReconciliationFailedEvent creates a new ReconciliationFailedEvent.

statusPatches should be the patches from the most recent successful render, or nil if none exists yet. The outer slice is defensively cloned.

Use WithCorrelation() to propagate correlation from the pipeline:

event := events.NewReconciliationFailedEvent(err, phase, statusPatches,
    events.WithCorrelation(correlationID, causationID))

func NewReconciliationFailedEventWithStatusSnapshot

func NewReconciliationFailedEventWithStatusSnapshot(err, phase string, durationMs int64, statusPatchSnapshot *templating.StatusPatchSnapshot, opts ...CorrelationOption) *ReconciliationFailedEvent

NewReconciliationFailedEventWithStatusSnapshot carries the last successful patch set, nil before the first one, and how long the failed cycle ran.

func (*ReconciliationFailedEvent) EventType

func (e *ReconciliationFailedEvent) EventType() string

func (*ReconciliationFailedEvent) Timestamp

func (t *ReconciliationFailedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ReconciliationStartedEvent

type ReconciliationStartedEvent struct {
	// Trigger describes what triggered this reconciliation.
	Trigger string

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

ReconciliationStartedEvent is published when the Executor begins a reconciliation cycle.

This event propagates the correlation ID from ReconciliationTriggeredEvent.

func NewReconciliationStartedEvent

func NewReconciliationStartedEvent(trigger string, opts ...CorrelationOption) *ReconciliationStartedEvent

NewReconciliationStartedEvent creates a new ReconciliationStartedEvent.

Use PropagateCorrelation() to propagate correlation from the triggering event:

event := events.NewReconciliationStartedEvent(trigger,
    events.PropagateCorrelation(triggeredEvent))

func (*ReconciliationStartedEvent) EventType

func (e *ReconciliationStartedEvent) EventType() string

func (*ReconciliationStartedEvent) Timestamp

func (t *ReconciliationStartedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ReconciliationTriggeredEvent

type ReconciliationTriggeredEvent struct {
	// Reason describes why reconciliation was triggered.
	// Examples: "debounce_timer", "config_change", "manual_trigger"
	Reason string

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

ReconciliationTriggeredEvent is published when a reconciliation cycle should start.

This event is typically published by the Reconciler after the debounce timer. expires, or immediately for config changes.

This event starts a new correlation chain. Downstream events (TemplateRenderedEvent, RenderGateCompletedEvent, DeploymentScheduledEvent, etc.) should propagate the correlation ID to enable end-to-end tracing.

This event implements CoalescibleEvent. The coalescible flag is set by the emitter (Reconciler) based on the trigger context:

  • coalescible=true for state updates (debounce_timer, resource_change)
  • coalescible=false for commands (index_synchronized, drift_prevention)

func NewReconciliationTriggeredEvent

func NewReconciliationTriggeredEvent(reason string, coalescible bool, opts ...CorrelationOption) *ReconciliationTriggeredEvent

NewReconciliationTriggeredEvent creates a new ReconciliationTriggeredEvent.

The coalescible parameter is set by the emitter based on trigger context:

  • true for state updates where only the latest matters (debounce_timer, resource_change)
  • false for commands that must be processed (index_synchronized, drift_prevention)

Use WithNewCorrelation() to start a new correlation chain:

event := events.NewReconciliationTriggeredEvent("config_change", true,
    events.WithNewCorrelation())

func (*ReconciliationTriggeredEvent) Coalescible

func (e *ReconciliationTriggeredEvent) Coalescible() bool

Coalescible returns true if this event can be safely skipped when a newer event of the same type is available. This implements the CoalescibleEvent interface.

func (*ReconciliationTriggeredEvent) EventType

func (e *ReconciliationTriggeredEvent) EventType() string

func (*ReconciliationTriggeredEvent) Timestamp

func (t *ReconciliationTriggeredEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type RenderGateCompletedEvent

type RenderGateCompletedEvent struct {

	// CycleSnapshot identifies the exact output and effects judged by the gate.
	CycleSnapshot *rendercycle.Snapshot

	// OutputSnapshot identifies the exact complete render judged by the gate.
	OutputSnapshot *renderoutput.Snapshot

	// PlanID is the render this verdict describes.
	PlanID string

	// RenderProof identifies the exact render instance this verdict describes.
	RenderProof string

	// Plan carries the exact controller-local render identity.
	Plan *renderplan.Plan

	// OK is true when the check passed.
	OK bool

	// Refused distinguishes HAProxy's own verdict from a gate that could not
	// run at all (no binary, unwritable temp tree). Only a refusal is evidence
	// about the config, so only a refusal reverts the fleet.
	Refused bool

	// Newest reports that this verdict describes the render the fleet is
	// converging on, rather than a superseded plan some pod still runs. The
	// gate checks both; only the newest one may move the latch, the pinned
	// gauge, the conditions or the validated plan, because every consumer of
	// those has already moved past a straggler. A straggler's verdict still
	// travels: it is what scopes the revert to the pods carrying it.
	Newest bool

	// Message is HAProxy's own words on a refusal, or the reason the gate
	// could not run. Empty on a pass.
	Message string

	// Pinned reports that renders are held until one passes: the gate is in
	// its pessimistic state and the fleet stays on the last config it accepted.
	Pinned bool

	// DurationMs is how long the check took.
	DurationMs int64

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

RenderGateCompletedEvent carries the render gate's verdict on one plan: what the controller's own `haproxy -c` said about the render the fleet was (or is about to be) given.

It replaces ValidationCompletedEvent as the reconcile path's validation signal. Deployment is armed by TemplateRenderedEvent now, so this event is what moves the gate's latch rather than what starts a deploy — except while Pinned, where the pass named by PlanID releases the render the scheduler is holding.

Deliberately NOT coalescible: the latch is a state machine, and skipping a verdict would leave the scheduler pinned on a plan that has since passed.

func NewRenderGateCompletedEvent

func NewRenderGateCompletedEvent(
	planID string,
	ok, refused, newest bool,
	message string,
	pinned bool,
	durationMs int64,
	opts ...CorrelationOption,
) *RenderGateCompletedEvent

NewRenderGateCompletedEvent creates a new RenderGateCompletedEvent.

Use PropagateCorrelation() to propagate correlation from the render this verdict describes:

event := events.NewRenderGateCompletedEvent(planID, ok, refused, newest, message, pinned, durationMs,
    events.PropagateCorrelation(renderedEvent))

func NewRenderGateCompletedEventWithCycle

func NewRenderGateCompletedEventWithCycle(
	occurrence *rendercycle.Occurrence,
	ok, refused, newest bool,
	message string,
	pinned bool,
	durationMs int64,
	opts ...CorrelationOption,
) (*RenderGateCompletedEvent, error)

NewRenderGateCompletedEventWithCycle creates a verdict for one exact render cycle.

func NewRenderGateCompletedEventWithIdentity

func NewRenderGateCompletedEventWithIdentity(
	planID, renderProof string,
	plan *renderplan.Plan,
	ok, refused, newest bool,
	message string,
	pinned bool,
	durationMs int64,
	opts ...CorrelationOption,
) *RenderGateCompletedEvent

NewRenderGateCompletedEventWithIdentity creates a verdict with exact render identity.

func (RenderGateCompletedEvent) AuthenticatedRenderIdentity

func (c RenderGateCompletedEvent) AuthenticatedRenderIdentity() (*rendercycle.Snapshot, string, error)

AuthenticatedRenderIdentity returns the exact cycle and diagnostic proof.

func (*RenderGateCompletedEvent) CloneForSubscriber

func (e *RenderGateCompletedEvent) CloneForSubscriber() busevents.Event

CloneForSubscriber restores authenticated shadows and isolates the legacy plan.

func (*RenderGateCompletedEvent) EventType

func (e *RenderGateCompletedEvent) EventType() string

func (RenderGateCompletedEvent) RenderOccurrence

func (c RenderGateCompletedEvent) RenderOccurrence() (*rendercycle.Occurrence, error)

RenderOccurrence returns the exact opaque occurrence carried by the event.

func (*RenderGateCompletedEvent) Timestamp

func (t *RenderGateCompletedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ResourceIndexUpdatedEvent

type ResourceIndexUpdatedEvent struct {
	// ResourceTypeName identifies the watched resource type as configured in
	// spec.watchedResources (the plural form, e.g. "services").
	ResourceTypeName string

	// ChangeStats provides detailed change statistics including Created, Modified, Deleted counts
	// and whether this event occurred during initial sync.
	ChangeStats types.ChangeStats
	// contains filtered or unexported fields
}

ResourceIndexUpdatedEvent is published when a watched Kubernetes resource. has been added, updated, or deleted in the local index.

func NewResourceIndexUpdatedEvent

func NewResourceIndexUpdatedEvent(resourceTypeName string, changeStats types.ChangeStats) *ResourceIndexUpdatedEvent

NewResourceIndexUpdatedEvent creates a new ResourceIndexUpdatedEvent. Performs a value copy of ChangeStats (it's a small struct with no pointers).

func (*ResourceIndexUpdatedEvent) CoalesceWith

func (e *ResourceIndexUpdatedEvent) CoalesceWith(prev events.Event) events.Event

CoalesceWith sums the additive ChangeStats counters, so the delta payload survives the merge in full — unlike skip-older coalescing, which would lose the skipped increments. IsInitialSync ORs: a merged event that includes the bulk-load flush still reports it.

func (*ResourceIndexUpdatedEvent) EventType

func (e *ResourceIndexUpdatedEvent) EventType() string

func (*ResourceIndexUpdatedEvent) PreStartCoalesceKey

func (e *ResourceIndexUpdatedEvent) PreStartCoalesceKey() string

PreStartCoalesceKey merges per watched kind: an undebounced watcher can publish per change and outrun the pre-start buffer on a busy cluster, and a dropped index update is a critical failure that aborts the iteration. The kind stays in the key so merging never collapses distinct watch subjects — the reason this type must NOT implement the type-string-keyed CoalescibleEvent (see coalescible_inventory_test.go).

func (*ResourceIndexUpdatedEvent) Timestamp

func (t *ResourceIndexUpdatedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ResourceSyncCompleteEvent

type ResourceSyncCompleteEvent struct {
	// ResourceTypeName identifies the resource type from config (e.g., "ingresses").
	ResourceTypeName string

	// InitialCount is the number of resources loaded during initial sync.
	InitialCount int
	// contains filtered or unexported fields
}

ResourceSyncCompleteEvent is published when a resource watcher has completed. its initial sync with the Kubernetes API.

func NewResourceSyncCompleteEvent

func NewResourceSyncCompleteEvent(resourceTypeName string, initialCount int) *ResourceSyncCompleteEvent

NewResourceSyncCompleteEvent creates a new ResourceSyncCompleteEvent.

func (*ResourceSyncCompleteEvent) EventType

func (e *ResourceSyncCompleteEvent) EventType() string

func (*ResourceSyncCompleteEvent) Timestamp

func (t *ResourceSyncCompleteEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ResourcesAppliedEvent

type ResourcesAppliedEvent struct {

	// CycleSnapshot is the exact cycle whose resources now exist.
	CycleSnapshot *rendercycle.Snapshot

	// RenderProof identifies this occurrence of the authenticated cycle.
	RenderProof string

	// StatusPatches forwarded from the ReconciliationCompletedEvent that
	// triggered the apply pass.
	StatusPatches []templating.StatusPatch

	// StatusPatchSnapshot forwards the immutable patch set from the same cycle.
	StatusPatchSnapshot *templating.StatusPatchSnapshot

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

ResourcesAppliedEvent is published by the ResourceApplier after it finishes applying a cycle's rendered resources (and pruning orphans). It forwards the cycle's StatusPatches so the StatusApplier writes the "rendered" status variant strictly AFTER the same render's infrastructure resources exist. Without this ordering the two appliers race: Accepted=True could land while e.g. the per-Gateway Service is still being created, and consumers (including the Gateway API conformance GatewayInfrastructure test) that list infrastructure the moment Accepted turns True find nothing.

func NewResourcesAppliedEvent

func NewResourcesAppliedEvent(statusPatches []templating.StatusPatch, opts ...CorrelationOption) *ResourcesAppliedEvent

NewResourcesAppliedEvent creates a legacy ResourcesAppliedEvent.

func NewResourcesAppliedEventWithCycle

func NewResourcesAppliedEventWithCycle(
	occurrence *rendercycle.Occurrence,
	opts ...CorrelationOption,
) (*ResourcesAppliedEvent, error)

NewResourcesAppliedEventWithCycle forwards one exact cycle after its resources exist.

func NewResourcesAppliedEventWithStatusSnapshot

func NewResourcesAppliedEventWithStatusSnapshot(statusPatchSnapshot *templating.StatusPatchSnapshot, opts ...CorrelationOption) *ResourcesAppliedEvent

NewResourcesAppliedEventWithStatusSnapshot forwards an immutable patch set.

func (ResourcesAppliedEvent) AuthenticatedRenderIdentity

func (c ResourcesAppliedEvent) AuthenticatedRenderIdentity() (*rendercycle.Snapshot, string, error)

AuthenticatedRenderIdentity returns the exact cycle and diagnostic proof.

func (*ResourcesAppliedEvent) CloneForSubscriber

func (e *ResourcesAppliedEvent) CloneForSubscriber() busevents.Event

CloneForSubscriber restores authenticated shadows and isolates legacy payloads.

func (*ResourcesAppliedEvent) Coalescible

func (e *ResourcesAppliedEvent) Coalescible() bool

Coalescible implements busevents.CoalescibleEvent — full-state semantics, same rationale as ReconciliationCompletedEvent.Coalescible.

func (*ResourcesAppliedEvent) EventType

func (e *ResourcesAppliedEvent) EventType() string

func (ResourcesAppliedEvent) RenderOccurrence

func (c ResourcesAppliedEvent) RenderOccurrence() (*rendercycle.Occurrence, error)

RenderOccurrence returns the exact opaque occurrence carried by the event.

func (*ResourcesAppliedEvent) Timestamp

func (t *ResourcesAppliedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ResourcesProcessedEvent

type ResourcesProcessedEvent struct {
	Correlation
	// contains filtered or unexported fields
}

ResourcesProcessedEvent acknowledges handling an occurrence, not successful application.

func NewResourcesProcessedEvent

func NewResourcesProcessedEvent(occurrence *rendercycle.Occurrence, opts ...CorrelationOption) (*ResourcesProcessedEvent, error)

NewResourcesProcessedEvent acknowledges handling one authenticated reconciliation occurrence.

func (ResourcesProcessedEvent) AuthenticatedRenderIdentity

func (c ResourcesProcessedEvent) AuthenticatedRenderIdentity() (*rendercycle.Snapshot, string, error)

AuthenticatedRenderIdentity returns the exact cycle and diagnostic proof.

func (*ResourcesProcessedEvent) CloneForSubscriber

func (e *ResourcesProcessedEvent) CloneForSubscriber() busevents.Event

CloneForSubscriber isolates the acknowledgement envelope; its occurrence is immutable.

func (*ResourcesProcessedEvent) EventType

func (e *ResourcesProcessedEvent) EventType() string

func (ResourcesProcessedEvent) RenderOccurrence

func (c ResourcesProcessedEvent) RenderOccurrence() (*rendercycle.Occurrence, error)

RenderOccurrence returns the exact opaque occurrence carried by the event.

func (*ResourcesProcessedEvent) Timestamp

func (t *ResourcesProcessedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type RetainedConfigEvent

type RetainedConfigEvent struct {
	Namespace string
	Name      string
	UID       string
	Reason    string
	Message   string
	// contains filtered or unexported fields
}

RetainedConfigEvent reports recovery activity or refusal on the template owner.

func NewRetainedConfigEvent

func NewRetainedConfigEvent(namespace, name, uid, reason, message string) *RetainedConfigEvent

func (*RetainedConfigEvent) EventType

func (e *RetainedConfigEvent) EventType() string

func (*RetainedConfigEvent) Timestamp

func (t *RetainedConfigEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type SecretResourceChangedEvent

type SecretResourceChangedEvent struct {
	// Resource contains the raw Secret resource.
	// Type: any to avoid circular dependencies.
	// Consumers should type-assert to *unstructured.Unstructured or *corev1.Secret.
	Resource any
	// contains filtered or unexported fields
}

SecretResourceChangedEvent is published when the Secret resource is added, updated, or deleted.

This is a low-level event published directly by the SingleWatcher callback in the controller package. The CredentialsLoaderComponent subscribes to this event and handles parsing.

func NewSecretResourceChangedEvent

func NewSecretResourceChangedEvent(resource any) *SecretResourceChangedEvent

NewSecretResourceChangedEvent creates a new SecretResourceChangedEvent.

func (*SecretResourceChangedEvent) EventType

func (e *SecretResourceChangedEvent) EventType() string

func (*SecretResourceChangedEvent) Timestamp

func (t *SecretResourceChangedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type StatusPatchPhase

type StatusPatchPhase string

StatusPatchPhase identifies which pipeline phase triggered a status patch application.

const (
	// StatusPatchPhaseRendered is applied after successful template rendering.
	StatusPatchPhaseRendered StatusPatchPhase = "rendered"

	// StatusPatchPhaseDeployed is applied after successful HAProxy deployment.
	StatusPatchPhaseDeployed StatusPatchPhase = "deployed"

	// StatusPatchPhaseRenderFailed is applied when template rendering fails
	// before any output is produced.
	StatusPatchPhaseRenderFailed StatusPatchPhase = "renderFailed"

	// StatusPatchPhaseValidateFailed is applied when the rendered config
	// passed templating but was rejected by validation (syntax, schema, or
	// semantic checks) before any deploy attempt. Distinct from
	// renderFailed (no output produced) and deployFailed (deploy attempted
	// and rolled back). Chart templates may emit the same payload as
	// renderFailed until validation failures need a distinct surface.
	StatusPatchPhaseValidateFailed StatusPatchPhase = "validateFailed"

	// StatusPatchPhaseDeployFailed is applied when HAProxy deployment fails.
	StatusPatchPhaseDeployFailed StatusPatchPhase = "deployFailed"
)

type StatusUpdateCompletedEvent

type StatusUpdateCompletedEvent struct {
	// Phase identifies which pipeline phase triggered this status update.
	Phase StatusPatchPhase

	// AppliedCount is the number of status patches successfully applied.
	AppliedCount int

	// SkippedCount is the number of status patches skipped (checksum match).
	SkippedCount int

	// DurationMs is the total duration of status patch application.
	DurationMs int64

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

StatusUpdateCompletedEvent is published when status patch application completes.

This event propagates the correlation ID from the triggering reconciliation event.

Observability-only: consumed by the commentator's statusInsight; no business-logic subscriber reacts to it.

func NewStatusUpdateCompletedEvent

func NewStatusUpdateCompletedEvent(
	phase StatusPatchPhase,
	appliedCount int,
	skippedCount int,
	durationMs int64,
	opts ...CorrelationOption,
) *StatusUpdateCompletedEvent

NewStatusUpdateCompletedEvent creates a new StatusUpdateCompletedEvent.

func (*StatusUpdateCompletedEvent) EventType

func (e *StatusUpdateCompletedEvent) EventType() string

func (*StatusUpdateCompletedEvent) Timestamp

func (t *StatusUpdateCompletedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type StatusUpdateFailedEvent

type StatusUpdateFailedEvent struct {
	// Namespace is the namespace of the target Kubernetes resource.
	Namespace string

	// Name is the name of the target Kubernetes resource.
	Name string

	// GVR is the GroupVersionResource string of the target resource (e.g., "networking.k8s.io/v1/ingresses").
	GVR string

	// Error is the error message from the failed SSA patch.
	Error string

	// Retriable indicates whether the failure is transient and can be retried.
	Retriable bool

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

StatusUpdateFailedEvent is published when a status patch application fails for a resource.

This event propagates the correlation ID from the triggering reconciliation event.

Observability-only: consumed by the commentator's statusInsight; no business-logic subscriber reacts to it.

func NewStatusUpdateFailedEvent

func NewStatusUpdateFailedEvent(
	namespace string,
	name string,
	gvr string,
	err string,
	retriable bool,
	opts ...CorrelationOption,
) *StatusUpdateFailedEvent

NewStatusUpdateFailedEvent creates a new StatusUpdateFailedEvent.

func (*StatusUpdateFailedEvent) EventType

func (e *StatusUpdateFailedEvent) EventType() string

func (*StatusUpdateFailedEvent) Timestamp

func (t *StatusUpdateFailedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type SyncMetadata

type SyncMetadata struct {
	// ReloadTriggered indicates whether HAProxy was reloaded during this sync.
	// Reload path triggers a reload; runtime path doesn't.
	ReloadTriggered bool

	// SyncDuration is how long the sync operation took.
	SyncDuration time.Duration

	// OperationCounts provides a breakdown of operations performed.
	OperationCounts OperationCounts

	// AppliedPlanID is the render plan the pod accepted in this sync.
	AppliedPlanID      string
	AppliedPlanProof   string
	AppliedRenderProof string

	// RunningPlanID is the render plan the pod's running HAProxy serves after
	// this sync. It trails AppliedPlanID while a reload is still pending.
	RunningPlanID    string
	RunningPlanProof string

	// Mode is how the plan was applied: runtime, file_only, reload, scheduled,
	// noop or rejected. Empty until the agent reports it.
	Mode string

	// Reasons explain Mode, most significant first.
	Reasons []string

	// Error contains the error message if sync failed.
	// Empty string indicates success.
	Error string
}

SyncMetadata contains detailed information about a sync operation.

type TemplateRenderFailedEvent

type TemplateRenderFailedEvent struct {
	// TemplateName is the name of the template that failed to render.
	TemplateName string

	// Error is the error message.
	Error string

	// StackTrace provides additional debugging context.
	StackTrace string

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

TemplateRenderFailedEvent is published when template rendering fails.

This event propagates the correlation ID from ReconciliationTriggeredEvent.

func NewTemplateRenderFailedEvent

func NewTemplateRenderFailedEvent(templateName, err, stackTrace string, opts ...CorrelationOption) *TemplateRenderFailedEvent

NewTemplateRenderFailedEvent creates a new TemplateRenderFailedEvent.

Use PropagateCorrelation() to propagate correlation from the triggering event:

event := events.NewTemplateRenderFailedEvent(name, err, stackTrace,
    events.PropagateCorrelation(triggeredEvent))

func (*TemplateRenderFailedEvent) EventType

func (e *TemplateRenderFailedEvent) EventType() string

func (*TemplateRenderFailedEvent) Timestamp

func (t *TemplateRenderFailedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type TemplateRenderedEvent

type TemplateRenderedEvent struct {

	// CycleSnapshot binds this output to every effect from the same render.
	CycleSnapshot *rendercycle.Snapshot

	// OutputSnapshot binds the exact config, plan, and auxiliary artifacts.
	OutputSnapshot *renderoutput.Snapshot

	// HAProxyConfig is the rendered main HAProxy configuration.
	// Uses relative paths (maps/, ssl/, files/) that work with HAProxy's `default-path origin`.
	HAProxyConfig string

	// AuxiliaryFiles contains all rendered auxiliary files (maps, certificates, general files).
	AuxiliaryFiles *dataplane.AuxiliaryFiles

	// AuxiliaryFileSnapshot is the authenticated immutable production representation.
	AuxiliaryFileSnapshot *renderartifact.Snapshot

	// StatusPatches contains status patches registered by templates during rendering.
	// Each patch targets a Kubernetes resource and contains outcome-keyed variants
	// for different pipeline lifecycle phases (rendered, deployed, renderFailed, deployFailed).
	StatusPatches []templating.StatusPatch

	// StatusPatchSnapshot is the authenticated immutable production representation.
	StatusPatchSnapshot *templating.StatusPatchSnapshot

	// RenderedResources contains full Kubernetes resources the templates declared
	// the controller should own and reconcile (e.g. an auxiliary Service or other
	// object a template emits alongside the HAProxy config). The applier compares
	// each against the last-applied checksum and skips unchanged entries to avoid
	// hammering the API server.
	RenderedResources []templating.RenderedResource

	// ContentChecksum is the pre-computed content checksum covering config + aux files.
	// Computed once in the pipeline and propagated to downstream consumers to avoid
	// redundant hashing in config publisher and deployment scheduler.
	ContentChecksum string

	// Plan is the structure this render declared, carried to the deployer so it
	// can diff against what each pod applied. Nil for renders that produced none.
	Plan *renderplan.Plan

	// PlanID is the digest identifying Plan.
	PlanID string

	// RenderProof identifies this render instance inside one controller process.
	RenderProof string

	// Metrics for observability
	ConfigBytes        int   // Size of HAProxyConfig
	AuxiliaryFileCount int   // Number of auxiliary files
	DurationMs         int64 // Total rendering duration

	// TriggerReason is the reason that triggered this reconciliation.
	// Propagated from ReconciliationTriggeredEvent.Reason.
	// Examples: "config_change", "debounce_timer", "drift_prevention"
	// Used by downstream components (e.g., DeploymentScheduler) to determine fallback behavior.
	TriggerReason string

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

TemplateRenderedEvent is published when template rendering completes successfully.

This event carries a single rendered HAProxy configuration using relative paths (maps/, ssl/, files/) that work with HAProxy's `default-path origin` directive. The same config works in any directory where the config file is placed.

This event propagates the correlation ID from ReconciliationTriggeredEvent.

This event implements CoalescibleEvent. The coalescible flag is propagated from ReconciliationTriggeredEvent to enable coalescing throughout the reconciliation pipeline.

func NewTemplateRenderedEvent

func NewTemplateRenderedEvent(
	haproxyConfig string,
	auxiliaryFiles *dataplane.AuxiliaryFiles,
	statusPatches []templating.StatusPatch,
	renderedResources []templating.RenderedResource,
	auxFileCount int,
	durationMs int64,
	triggerReason string,
	contentChecksum string,
	plan *renderplan.Plan,
	planID string,
	coalescible bool,
	opts ...CorrelationOption,
) *TemplateRenderedEvent

NewTemplateRenderedEvent creates a new TemplateRenderedEvent.

The coalescible parameter should be propagated from ReconciliationTriggeredEvent.Coalescible() to enable coalescing throughout the reconciliation pipeline.

Use PropagateCorrelation() to propagate correlation from the triggering event:

event := events.NewTemplateRenderedEvent(..., triggerReason, trigger.Coalescible(),
    events.PropagateCorrelation(triggeredEvent))

func NewTemplateRenderedEventWithCycle

func NewTemplateRenderedEventWithCycle(
	cycleSnapshot *rendercycle.Snapshot,
	durationMs int64,
	triggerReason string,
	coalescible bool,
	opts ...CorrelationOption,
) (*TemplateRenderedEvent, error)

NewTemplateRenderedEventWithCycle creates a production event from one render cycle.

func NewTemplateRenderedEventWithOccurrence

func NewTemplateRenderedEventWithOccurrence(
	occurrence *rendercycle.Occurrence,
	durationMs int64,
	triggerReason string,
	coalescible bool,
	opts ...CorrelationOption,
) (*TemplateRenderedEvent, error)

NewTemplateRenderedEventWithOccurrence propagates an existing occurrence.

func NewTemplateRenderedEventWithSnapshots

func NewTemplateRenderedEventWithSnapshots(
	haproxyConfig string,
	auxiliaryFileSnapshot *renderartifact.Snapshot,
	statusPatchSnapshot *templating.StatusPatchSnapshot,
	renderedResources []templating.RenderedResource,
	auxFileCount int,
	durationMs int64,
	triggerReason string,
	contentChecksum string,
	plan *renderplan.Plan,
	planID string,
	coalescible bool,
	opts ...CorrelationOption,
) (*TemplateRenderedEvent, error)

NewTemplateRenderedEventWithSnapshots creates an event from authenticated immutable output.

func NewTemplateRenderedEventWithStatusSnapshot

func NewTemplateRenderedEventWithStatusSnapshot(
	haproxyConfig string,
	auxiliaryFiles *dataplane.AuxiliaryFiles,
	statusPatchSnapshot *templating.StatusPatchSnapshot,
	renderedResources []templating.RenderedResource,
	auxFileCount int,
	durationMs int64,
	triggerReason string,
	contentChecksum string,
	plan *renderplan.Plan,
	planID string,
	coalescible bool,
	opts ...CorrelationOption,
) *TemplateRenderedEvent

NewTemplateRenderedEventWithStatusSnapshot creates an event without detaching status payloads.

func (TemplateRenderedEvent) AuthenticatedRenderIdentity

func (c TemplateRenderedEvent) AuthenticatedRenderIdentity() (*rendercycle.Snapshot, string, error)

AuthenticatedRenderIdentity returns the exact cycle and diagnostic proof.

func (*TemplateRenderedEvent) CloneForSubscriber

func (e *TemplateRenderedEvent) CloneForSubscriber() busevents.Event

CloneForSubscriber restores authenticated shadows and isolates legacy payloads.

func (*TemplateRenderedEvent) Coalescible

func (e *TemplateRenderedEvent) Coalescible() bool

Coalescible returns true if this event can be safely skipped when a newer event of the same type is available. This implements the CoalescibleEvent interface.

func (*TemplateRenderedEvent) EventType

func (e *TemplateRenderedEvent) EventType() string

func (TemplateRenderedEvent) RenderOccurrence

func (c TemplateRenderedEvent) RenderOccurrence() (*rendercycle.Occurrence, error)

RenderOccurrence returns the exact opaque occurrence carried by the event.

func (*TemplateRenderedEvent) Timestamp

func (t *TemplateRenderedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type ValidationFailedEvent

type ValidationFailedEvent struct {
	Errors     []string // Validation errors from HAProxy
	DurationMs int64

	// TriggerReason is the reason that triggered this reconciliation.
	// Propagated from TemplateRenderedEvent.TriggerReason.
	// Examples: "config_change", "debounce_timer", "drift_prevention"
	// Used by DeploymentScheduler to determine fallback behavior (deploy cached config on drift prevention).
	TriggerReason string

	// Correlation embeds correlation tracking for event tracing.
	Correlation
	// contains filtered or unexported fields
}

ValidationFailedEvent is published when local configuration validation fails.

Validation is performed locally using the HAProxy binary. Endpoints are not involved.

This event propagates the correlation ID from TemplateRenderedEvent.

func NewValidationFailedEvent

func NewValidationFailedEvent(errors []string, durationMs int64, triggerReason string, opts ...CorrelationOption) *ValidationFailedEvent

NewValidationFailedEvent creates a new ValidationFailedEvent. Performs defensive copy of the errors slice.

Use PropagateCorrelation() to propagate correlation from the triggering event:

event := events.NewValidationFailedEvent(errors, durationMs, triggerReason,
    events.PropagateCorrelation(startedEvent))

func (*ValidationFailedEvent) EventType

func (e *ValidationFailedEvent) EventType() string

func (*ValidationFailedEvent) Timestamp

func (t *ValidationFailedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

type WatchedInputsRejectedEvent

type WatchedInputsRejectedEvent struct {
	Rejections []InputRejection
	// contains filtered or unexported fields
}

WatchedInputsRejectedEvent carries the complete current rejection set.

func NewWatchedInputsRejectedEvent

func NewWatchedInputsRejectedEvent(rejections []InputRejection) *WatchedInputsRejectedEvent

NewWatchedInputsRejectedEvent creates a detached rejection notification.

func (*WatchedInputsRejectedEvent) EventType

func (e *WatchedInputsRejectedEvent) EventType() string

func (*WatchedInputsRejectedEvent) Timestamp

func (t *WatchedInputsRejectedEvent) Timestamp() time.Time

Timestamp returns the time at which the event was created.

Jump to

Keyboard shortcuts

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