Documentation
¶
Overview ¶
Package dataplane provides a simple, high-level API for synchronizing HAProxy configurations via the Dataplane API.
The library handles all complexity internally:
- Fetches current configuration from the Dataplane API
- Parses both current and desired configurations
- Generates fine-grained operations to transform current → desired
- Executes operations with automatic retry on version conflicts (409 errors)
- Falls back to raw config push on non-recoverable errors
- Returns detailed results including applied changes and reload information
Basic Usage (Recommended) ¶
For production use, create a Client to reuse connections across multiple operations:
endpoint := dataplane.Endpoint{
URL: "http://haproxy:5555/v2",
Username: "admin",
Password: "secret",
}
// Create client once, reuse for multiple operations
client, err := dataplane.NewClient(context.Background(), endpoint)
if err != nil {
slog.Error("failed to create client", "error", err)
os.Exit(1)
}
defer client.Close()
desiredConfig := `
global
daemon
defaults
mode http
timeout client 30s
timeout server 30s
timeout connect 5s
backend web
balance roundrobin
server srv1 192.168.1.10:80 check
`
result, err := client.Sync(ctx, desiredConfig, nil, nil)
if err != nil {
slog.Error("sync failed", "error", err)
os.Exit(1)
}
fmt.Printf("Applied %d operations\n", len(result.AppliedOperations))
if result.ReloadTriggered {
fmt.Printf("HAProxy reloaded (ID: %s)\n", result.ReloadID)
}
Simple One-Off Operations ¶
For quick scripts, use the convenience functions (creates client internally):
result, err := dataplane.Sync(ctx, endpoint, desiredConfig, nil, nil)
Custom Options ¶
Configure sync behavior with options:
client, err := dataplane.NewClient(ctx, endpoint)
if err != nil {
return err
}
defer client.Close()
opts := &dataplane.SyncOptions{
MaxRetries: 5, // Retry 409 conflicts up to 5 times
Timeout: 3 * time.Minute, // Overall timeout
ContinueOnError: false, // Stop on first error
FallbackToRaw: true, // Fall back to raw push on errors
}
result, err := client.Sync(ctx, desiredConfig, nil, opts)
Dry Run ¶
Preview changes without applying them:
client, err := dataplane.NewClient(ctx, endpoint)
if err != nil {
return err
}
defer client.Close()
diff, err := client.DryRun(ctx, desiredConfig)
if err != nil {
slog.Error("dry run failed", "error", err)
os.Exit(1)
}
fmt.Printf("Would apply %d operations:\n", len(diff.PlannedOperations))
for _, op := range diff.PlannedOperations {
fmt.Printf(" - %s\n", op.Description)
}
Diff Only ¶
Get detailed diff information:
client, err := dataplane.NewClient(ctx, endpoint)
if err != nil {
return err
}
defer client.Close()
diff, err := client.Diff(ctx, desiredConfig)
if err != nil {
slog.Error("diff failed", "error", err)
os.Exit(1)
}
fmt.Printf("Backends added: %v\n", diff.Details.BackendsAdded)
fmt.Printf("Servers modified: %d\n", len(diff.Details.ServersModified))
Error Handling ¶
The library provides detailed, actionable error messages:
client, err := dataplane.NewClient(ctx, endpoint)
if err != nil {
return err
}
defer client.Close()
result, err := client.Sync(ctx, desiredConfig, nil, nil)
if err != nil {
var syncErr *dataplane.SyncError
if errors.As(err, &syncErr) {
fmt.Printf("Stage: %s\n", syncErr.Stage)
fmt.Printf("Error: %s\n", syncErr.Message)
for _, hint := range syncErr.Hints {
fmt.Printf(" Hint: %s\n", hint)
}
}
}
Index ¶
- Variables
- func ComputeContentChecksum(haproxyConfig string, auxFiles *AuxiliaryFiles) string
- func SimplifyRenderingError(err error) string
- func SimplifyValidationError(err error) string
- func ValidateConfiguration(mainConfig string, auxFiles *AuxiliaryFiles, paths *ValidationPaths, ...) (*parser.StructuredConfig, error)
- func ValidateSemantics(mainConfig string, auxFiles *AuxiliaryFiles, paths *ValidationPaths, ...) error
- func ValidateSyntaxAndSchema(config string, version *Version) (*parser.StructuredConfig, error)
- type AppliedOperation
- type AuxiliaryFiles
- type Capabilities
- type Client
- func (c *Client) Close() error
- func (c *Client) Diff(ctx context.Context, desiredConfig string) (*DiffResult, error)
- func (c *Client) DryRun(ctx context.Context, desiredConfig string) (*DiffResult, error)
- func (c *Client) Sync(ctx context.Context, desiredConfig string, auxFiles *AuxiliaryFiles, ...) (*SyncResult, error)
- type ConfigParser
- type ConflictError
- type ConnectionError
- type DiffDetails
- type DiffResult
- type Endpoint
- type FallbackError
- type OperationError
- type ParseError
- type PathConfig
- type PlannedOperation
- type ResolvedPaths
- type SyncError
- func NewConflictError(retries int, expectedVersion int64, actualVersion string) *SyncError
- func NewConnectionError(endpoint string, cause error) *SyncError
- func NewFallbackError(originalErr, fallbackCause error) *SyncError
- func NewOperationError(opType, section, resource string, cause error) *SyncError
- func NewParseError(configType, configSnippet string, cause error) *SyncError
- func NewValidationError(message string, cause error) *SyncError
- type SyncMode
- type SyncOptions
- type SyncResult
- type ValidationError
- type ValidationPaths
- type Version
Constants ¶
This section is empty.
Variables ¶
var ErrValidationCacheHit = errors.New("validation cache hit")
ErrValidationCacheHit is returned when validation is skipped because the same configuration was already validated successfully. Callers should use the parser cache to obtain the parsed configuration if needed.
Functions ¶
func ComputeContentChecksum ¶
func ComputeContentChecksum(haproxyConfig string, auxFiles *AuxiliaryFiles) string
ComputeContentChecksum generates a SHA256 checksum covering the main HAProxy config and all auxiliary files (general files, map files, SSL certificates, CRT-list files).
The checksum is used for content deduplication to skip redundant processing when config content hasn't changed. Auxiliary file slices must be pre-sorted (by AuxiliaryFiles.Sort()) to ensure deterministic results regardless of insertion order.
Returns a hex-encoded 8-byte (16 character) checksum for brevity.
func SimplifyRenderingError ¶
SimplifyRenderingError extracts meaningful error messages from template rendering failures.
Handles template-level validation errors from the fail() function which are buried in the template engine's execution stack trace.
Input format:
"failed to render haproxy.cfg: failed to render template 'haproxy.cfg': unable to execute template: ... invalid call to function 'fail': <message>"
Output: "<message>" (the user-provided error message from fail() call)
If the error doesn't match this pattern (e.g., syntax errors, missing variables), returns the original error string.
func SimplifyValidationError ¶
SimplifyValidationError parses HAProxy validation errors and extracts the key information for user-friendly error messages.
Handles two types of validation errors:
Schema validation errors - OpenAPI spec violations: Input: "schema validation failed: configuration violates API schema constraints: ... Error at "/field": constraint" Output: "field constraint (got value)"
Semantic validation errors - HAProxy binary validation failures: Input: "semantic validation failed: configuration has semantic errors: haproxy validation failed: <context>" Output: "<context>" (preserves parseHAProxyError output with context lines)
Returns original error string if parsing fails.
func ValidateConfiguration ¶
func ValidateConfiguration(mainConfig string, auxFiles *AuxiliaryFiles, paths *ValidationPaths, version *Version, skipDNSValidation bool) (*parser.StructuredConfig, error)
ValidateConfiguration performs three-phase HAProxy configuration validation.
Phase 1: Syntax validation using client-native parser Phase 1.5: API schema validation using OpenAPI spec (patterns, formats, required fields) Phase 2: Semantic validation using haproxy binary (-c flag)
The validation writes files to the directories specified in paths. Callers must ensure that paths are isolated (e.g., per-worker temp directories) to allow parallel execution.
Validation result caching: If the same config (main + aux files + version) has been successfully validated before, the cached result is returned immediately. This is safe because HAProxy config validation is deterministic - the same inputs always produce the same result.
Parameters:
- mainConfig: The rendered HAProxy configuration (haproxy.cfg content)
- auxFiles: All auxiliary files (maps, certificates, general files)
- paths: Filesystem paths for validation (must be isolated for parallel execution)
- version: HAProxy/DataPlane API version for schema selection (nil uses default v3.0)
- skipDNSValidation: If true, adds -dr flag to skip DNS resolution failures. Use true for runtime validation (permissive, prevents blocking when DNS fails) and false for webhook validation (strict, catches DNS issues before resource admission).
Returns:
- *parser.StructuredConfig: The pre-parsed configuration from syntax validation (nil on cache hit or error)
- error: nil if validation succeeds, ValidationError with phase information if validation fails
func ValidateSemantics ¶
func ValidateSemantics(mainConfig string, auxFiles *AuxiliaryFiles, paths *ValidationPaths, skipDNSValidation bool) error
ValidateSemantics performs semantic validation using the haproxy binary (-c flag).
This function runs only Phase 2 (semantic validation) and assumes syntax/schema validation has already been done. Use this after ValidateSyntaxAndSchema() when you need to validate a modified config (e.g., with temp paths) separately from parsing.
Parameters:
- mainConfig: The HAProxy configuration content (may have modified paths for temp directory)
- auxFiles: All auxiliary files (maps, certificates, general files)
- paths: Filesystem paths for validation (must be isolated for parallel execution)
- skipDNSValidation: If true, adds -dr flag to skip DNS resolution failures
Returns:
- error: ValidationError with phase "semantic" if validation fails
func ValidateSyntaxAndSchema ¶
func ValidateSyntaxAndSchema(config string, version *Version) (*parser.StructuredConfig, error)
ValidateSyntaxAndSchema performs syntax and schema validation on HAProxy configuration.
This function runs Phase 1 (syntax validation) and Phase 1.5 (API schema validation) but NOT Phase 2 (semantic validation with haproxy binary).
Use this when you need to parse the config without needing file I/O or the haproxy binary. The primary use case is when you need to parse the original config (before path modifications) for downstream reuse, while semantic validation is done separately with a modified config.
Parameters:
- config: The HAProxy configuration content to validate
- version: HAProxy/DataPlane API version for schema selection (nil uses default v3.0)
Returns:
- *parser.StructuredConfig: The parsed configuration
- error: ValidationError with phase information if validation fails
Types ¶
type AppliedOperation ¶
type AppliedOperation struct {
// Type is the operation type: "create", "update", or "delete"
Type string
// Section is the configuration section: "backend", "server", "frontend", "acl", "http-rule", etc.
Section string
// Resource is the resource name or identifier (e.g., backend name, server name)
Resource string
// Description is a human-readable description of what was changed
Description string
}
AppliedOperation represents a single applied configuration change.
type AuxiliaryFiles ¶
type AuxiliaryFiles struct {
// GeneralFiles contains general-purpose files (error pages, custom response files, etc.)
GeneralFiles []auxiliaryfiles.GeneralFile
// SSLCertificates contains SSL certificates to sync to HAProxy SSL storage
SSLCertificates []auxiliaryfiles.SSLCertificate
// SSLCaFiles contains SSL CA certificate files for client/backend certificate verification
SSLCaFiles []auxiliaryfiles.SSLCaFile
// MapFiles contains map files for backend routing and other map-based features
MapFiles []auxiliaryfiles.MapFile
// CRTListFiles contains crt-list files for SSL certificate lists with per-certificate options
CRTListFiles []auxiliaryfiles.CRTListFile
}
AuxiliaryFiles contains files to synchronize before configuration changes. These files are synced in two phases:
- Phase 1 (pre-config): Creates and updates are applied before config sync
- Phase 2 (post-config): Deletes are applied after successful config sync
func DefaultAuxiliaryFiles ¶
func DefaultAuxiliaryFiles() *AuxiliaryFiles
DefaultAuxiliaryFiles returns an empty auxiliary files struct.
func (*AuxiliaryFiles) Sort ¶
func (a *AuxiliaryFiles) Sort()
Sort sorts all auxiliary file slices in-place by their path/filename. This establishes a deterministic order so that downstream consumers (checksum, diff, etc.) can iterate directly without cloning or sorting.
type Capabilities ¶
type Capabilities = client.Capabilities
Capabilities defines which features are available for a given HAProxy/DataPlane API version. This type is re-exported from pkg/dataplane/client for convenience.
func CapabilitiesFromVersion ¶
func CapabilitiesFromVersion(v *Version) Capabilities
CapabilitiesFromVersion computes capabilities based on a HAProxy version. This is used for local HAProxy binary detection (haproxy -v).
Capability thresholds (verified against OpenAPI specs):
- SupportsCrtList: v3.2+ (CRT-list storage endpoint)
- SupportsMapStorage: v3.0+ (Map file storage endpoint)
- SupportsGeneralStorage: v3.0+ (General file storage)
- SupportsSslCaFiles: v3.2+ (SSL CA file runtime endpoint)
- SupportsSslCrlFiles: v3.2+ (SSL CRL file runtime endpoint)
- SupportsLogProfiles: v3.1+ (Log profiles configuration endpoint)
- SupportsTraces: v3.1+ (Traces configuration endpoint)
- SupportsAcmeProviders: v3.2+ (ACME provider configuration endpoint)
- SupportsQUIC: v3.0+ (QUIC/HTTP3 configuration options)
- SupportsQUICInitialRules: v3.1+ (QUIC initial rules endpoints)
- SupportsHTTP2: v3.0+ (HTTP/2 configuration)
- SupportsRuntimeMaps: v3.0+ (Runtime map operations)
- SupportsRuntimeServers: v3.0+ (Runtime server operations)
type Client ¶
type Client struct {
// Endpoint contains connection information
Endpoint Endpoint
// contains filtered or unexported fields
}
Client manages a persistent connection to the HAProxy Dataplane API. It reuses connections for multiple operations, making it efficient for repeated sync operations.
For simple one-off operations, use the package-level convenience functions (Sync, DryRun, Diff) which create a client internally.
For production use with multiple operations, create a Client explicitly:
client, err := dataplane.NewClient(ctx, endpoint)
if err != nil {
return err
}
defer client.Close()
// Reuse client for multiple operations
result1, err := client.Sync(ctx, config1, auxFiles1, opts)
result2, err := client.Sync(ctx, config2, auxFiles2, opts)
func NewClient ¶
NewClient creates a new Client for the given endpoint. The client reuses connections for multiple operations.
Example:
endpoint := dataplane.Endpoint{
URL: "http://haproxy:5555/v2",
Username: "admin",
Password: "secret",
}
client, err := dataplane.NewClient(ctx, endpoint)
if err != nil {
return fmt.Errorf("failed to create client: %w", err)
}
defer client.Close()
result, err := client.Sync(ctx, desiredConfig, nil, nil)
func (*Client) Close ¶
Close cleans up client resources. Currently a no-op, but provided for future resource cleanup needs.
func (*Client) Diff ¶
Diff compares the current and desired configurations and returns detailed differences.
This is an alias for DryRun - both methods perform the same operation. Use whichever name makes more sense in your context.
Parameters:
- ctx: Context for cancellation and timeout
- desiredConfig: The desired HAProxy configuration as a string
Returns:
- *DiffResult: Detailed information about differences
- error: Error if comparison fails
Example:
client, err := dataplane.NewClient(ctx, endpoint)
if err != nil {
return err
}
defer client.Close()
diff, err := client.Diff(ctx, desiredConfig)
if err != nil {
return fmt.Errorf("diff failed: %w", err)
}
fmt.Printf("Backends added: %v\n", diff.Details.BackendsAdded)
fmt.Printf("Backends modified: %v\n", diff.Details.BackendsModified)
fmt.Printf("Servers deleted: %d total\n", len(diff.Details.ServersDeleted))
func (*Client) DryRun ¶
DryRun previews what changes would be applied without actually applying them.
This method performs all the same steps as Sync except for the actual application:
- Fetches the current configuration from the Dataplane API
- Parses both current and desired configurations
- Compares them to generate a list of planned operations
- Returns the diff without executing any operations
This is useful for:
- Previewing changes before applying them
- Validating configurations
- Understanding what would change
Parameters:
- ctx: Context for cancellation and timeout
- desiredConfig: The desired HAProxy configuration as a string
Returns:
- *DiffResult: Detailed information about planned changes
- error: Error if comparison fails
Example:
client, err := dataplane.NewClient(ctx, endpoint)
if err != nil {
return err
}
defer client.Close()
diff, err := client.DryRun(ctx, desiredConfig)
if err != nil {
return fmt.Errorf("dry run failed: %w", err)
}
if diff.HasChanges {
fmt.Printf("Would apply %d operations:\n", len(diff.PlannedOperations))
for _, op := range diff.PlannedOperations {
fmt.Printf(" - %s %s %s\n", op.Type, op.Section, op.Resource)
}
}
func (*Client) Sync ¶
func (c *Client) Sync(ctx context.Context, desiredConfig string, auxFiles *AuxiliaryFiles, opts *SyncOptions) (*SyncResult, error)
Sync synchronizes the desired HAProxy configuration using this client.
This method:
- Fetches the current configuration from the Dataplane API
- Parses both current and desired configurations
- Compares them to generate fine-grained operations
- Executes operations with automatic retry on 409 version conflicts
- Falls back to raw config push on non-recoverable errors (if enabled)
- Returns detailed results including applied changes and reload information
Parameters:
- ctx: Context for cancellation and timeout
- desiredConfig: The desired HAProxy configuration as a string
- auxFiles: Auxiliary files to sync (use nil for defaults)
- opts: Sync options (use nil for defaults)
Returns:
- *SyncResult: Detailed information about the sync operation
- error: Detailed error with actionable hints if the sync fails
Example:
client, err := dataplane.NewClient(ctx, endpoint)
if err != nil {
return err
}
defer client.Close()
result, err := client.Sync(ctx, desiredConfig, nil, nil)
if err != nil {
return fmt.Errorf("sync failed: %w", err)
}
fmt.Printf("Applied %d operations in %v\n", len(result.AppliedOperations), result.Duration)
type ConfigParser ¶
type ConfigParser interface {
ParseFromString(config string) (*parserconfig.StructuredConfig, error)
}
ConfigParser defines the interface for HAProxy configuration parsing. Both CE (parser.Parser) and EE (enterprise.Parser) parsers implement this interface.
type ConflictError ¶
type ConflictError struct {
// Retries is the number of retry attempts made
Retries int
// ExpectedVersion is the version we tried to use
ExpectedVersion int64
// ActualVersion is the version that exists on the server
ActualVersion string
}
ConflictError represents unresolved version conflicts after exhausting retries.
func (*ConflictError) Error ¶
func (e *ConflictError) Error() string
Error implements the error interface.
type ConnectionError ¶
type ConnectionError struct {
// Endpoint is the URL that failed to connect
Endpoint string
// Cause is the underlying connection error
Cause error
}
ConnectionError represents a failure to connect to the Dataplane API.
func (*ConnectionError) Error ¶
func (e *ConnectionError) Error() string
Error implements the error interface.
func (*ConnectionError) Unwrap ¶
func (e *ConnectionError) Unwrap() error
Unwrap returns the underlying cause for error unwrapping.
type DiffDetails ¶
type DiffDetails struct {
// Total operation counts
TotalOperations int
Creates int
Updates int
Deletes int
// Global and defaults changes
GlobalChanged bool
DefaultsChanged bool
// Frontend changes
FrontendsAdded []string
FrontendsModified []string
FrontendsDeleted []string
// Backend changes
BackendsAdded []string
BackendsModified []string
BackendsDeleted []string
// Server changes (map of backend -> server names)
ServersAdded map[string][]string
ServersModified map[string][]string
ServersDeleted map[string][]string
// ACL changes (map of parent resource -> ACL names)
ACLsAdded map[string][]string
ACLsModified map[string][]string
ACLsDeleted map[string][]string
// HTTP rule changes (map of parent resource -> count)
HTTPRulesAdded map[string]int
HTTPRulesModified map[string]int
HTTPRulesDeleted map[string]int
// Auxiliary file changes
MapsAdded int
MapsModified int
MapsDeleted int
SSLCertsAdded int
SSLCertsModified int
SSLCertsDeleted int
SSLCaFilesAdded int
SSLCaFilesModified int
SSLCaFilesDeleted int
GeneralFilesAdded int
GeneralFilesModified int
GeneralFilesDeleted int
}
DiffDetails contains detailed diff information about configuration changes.
func NewDiffDetails ¶
func NewDiffDetails() DiffDetails
NewDiffDetails creates an empty DiffDetails with initialized maps.
func (*DiffDetails) String ¶
func (d *DiffDetails) String() string
String returns a human-readable summary of the diff details.
type DiffResult ¶
type DiffResult struct {
// HasChanges indicates whether any differences were detected
HasChanges bool
// PlannedOperations contains structured information about operations that would be executed
PlannedOperations []PlannedOperation
// Details contains detailed diff information
Details DiffDetails
}
DiffResult contains comparison results without applying changes.
func Diff ¶
Diff compares the current and desired configurations and returns detailed differences.
This is a convenience function that creates a client internally for one-off operations. This is an alias for DryRun. For production use with multiple operations, create a Client explicitly.
Parameters:
- ctx: Context for cancellation and timeout
- endpoint: Dataplane API connection information
- desiredConfig: The desired HAProxy configuration as a string
Returns:
- *DiffResult: Detailed information about differences
- error: Error if comparison fails
func DryRun ¶
DryRun previews what changes would be applied without actually applying them.
This is a convenience function that creates a client internally for one-off operations. For production use with multiple operations, create a Client explicitly.
Parameters:
- ctx: Context for cancellation and timeout
- endpoint: Dataplane API connection information
- desiredConfig: The desired HAProxy configuration as a string
Returns:
- *DiffResult: Detailed information about planned changes
- error: Error if comparison fails
func (*DiffResult) String ¶
func (r *DiffResult) String() string
String returns a human-readable summary of the diff result.
type Endpoint ¶
type Endpoint struct {
// URL is the Dataplane API endpoint (e.g., "http://haproxy:5555/v2")
URL string
// Username for basic authentication
Username string
// Password for basic authentication
Password string
// PodName is the Kubernetes pod name (for observability)
PodName string
// PodNamespace is the Kubernetes pod namespace (for observability)
PodNamespace string
// Version info (cached after discovery admission, avoids redundant /v3/info calls)
// Zero values indicate version not yet detected.
DetectedMajorVersion int // Major version (e.g., 3)
DetectedMinorVersion int // Minor version (e.g., 2)
DetectedFullVersion string // Full version string (e.g., "v3.2.6 87ad0bcf")
}
Endpoint represents HAProxy Dataplane API connection information.
func (*Endpoint) HasCachedVersion ¶
HasCachedVersion returns true if version info has been cached on this endpoint.
type FallbackError ¶
type FallbackError struct {
// OriginalError is the error that triggered the fallback
OriginalError error
// FallbackCause is the error that occurred during fallback
FallbackCause error
}
FallbackError represents a failure during raw config fallback.
func (*FallbackError) Error ¶
func (e *FallbackError) Error() string
Error implements the error interface.
func (*FallbackError) Unwrap ¶
func (e *FallbackError) Unwrap() error
Unwrap returns the fallback cause for error unwrapping.
type OperationError ¶
type OperationError struct {
// OperationType is "create", "update", or "delete"
OperationType string
// Section is the configuration section (e.g., "backend", "server")
Section string
// Resource is the resource identifier (e.g., backend name, server name)
Resource string
// Cause is the underlying error
Cause error
}
OperationError represents a failure of a specific configuration operation.
func (*OperationError) Error ¶
func (e *OperationError) Error() string
Error implements the error interface.
func (*OperationError) Unwrap ¶
func (e *OperationError) Unwrap() error
Unwrap returns the underlying cause for error unwrapping.
type ParseError ¶
type ParseError struct {
// ConfigType indicates which config failed: "current" or "desired"
ConfigType string
// ConfigSnippet contains the first 200 characters of the problematic config
ConfigSnippet string
// Line indicates the approximate line number where parsing failed (if available)
Line int
// Cause is the underlying parsing error
Cause error
}
ParseError represents a configuration parsing failure.
func (*ParseError) Error ¶
func (e *ParseError) Error() string
Error implements the error interface.
func (*ParseError) Unwrap ¶
func (e *ParseError) Unwrap() error
Unwrap returns the underlying cause for error unwrapping.
type PathConfig ¶
type PathConfig struct {
// MapsDir is the base path for HAProxy map files (e.g., /etc/haproxy/maps).
MapsDir string
// SSLDir is the base path for HAProxy SSL certificates (e.g., /etc/haproxy/ssl).
SSLDir string
// GeneralDir is the base path for HAProxy general files (e.g., /etc/haproxy/general).
GeneralDir string
// ConfigFile is the path to the HAProxy configuration file (e.g., /tmp/haproxy.cfg).
// Only used in validation contexts; can be empty for production paths.
ConfigFile string
}
PathConfig contains the base directory configuration for HAProxy auxiliary files. These are the raw filesystem paths before capability-based resolution.
type PlannedOperation ¶
type PlannedOperation struct {
// Type is the operation type: "create", "update", or "delete"
Type string
// Section is the configuration section: "backend", "server", "frontend", "acl", "http-rule", etc.
Section string
// Resource is the resource name or identifier
Resource string
// Description is a human-readable description of what would be changed
Description string
// Priority indicates execution order (lower = earlier for creates, higher = earlier for deletes)
Priority int
}
PlannedOperation represents an operation that would be executed.
type ResolvedPaths ¶
type ResolvedPaths struct {
// MapsDir is the resolved path for HAProxy map files.
MapsDir string
// SSLDir is the resolved path for HAProxy SSL certificates.
SSLDir string
// CRTListDir is the resolved path for CRT-list files.
// Always equals GeneralDir because CRT-list files are stored as general files
// to avoid reload on create (native CRT-list API doesn't support skip_reload).
CRTListDir string
// GeneralDir is the resolved path for HAProxy general files.
GeneralDir string
// ConfigFile is the path to the HAProxy configuration file.
ConfigFile string
}
ResolvedPaths contains capability-aware resolved paths for HAProxy auxiliary files. This is the result of applying capability-based resolution to a PathConfig.
The key difference from PathConfig is that CRTListDir is always set to GeneralDir because CRT-list files are stored as general files to avoid reload on create.
func ResolvePaths ¶
func ResolvePaths(base PathConfig, _ Capabilities) *ResolvedPaths
ResolvePaths applies capability-based path resolution to base paths. This is the SINGLE SOURCE OF TRUTH for all capability-dependent path logic.
Currently handles:
- CRT-list storage: Always uses general directory because the native CRT-list API triggers reload on create without supporting skip_reload parameter. General file storage returns 201 without triggering reload.
Future capability-dependent paths should be added here to ensure consistent resolution across all components.
func (*ResolvedPaths) ToValidationPaths ¶
func (r *ResolvedPaths) ToValidationPaths() *ValidationPaths
ToValidationPaths converts ResolvedPaths to ValidationPaths. Use this when you need ValidationPaths for HAProxy configuration validation. TempDir is derived from ConfigFile's parent directory (e.g., /tmp/validate-xxx/haproxy.cfg -> /tmp/validate-xxx).
type SyncError ¶
type SyncError struct {
// Stage indicates where the failure occurred:
// "connect", "fetch", "parse-current", "parse-desired", "compare", "apply", "commit", "fallback"
Stage string
// Message provides a detailed error description
Message string
// Cause is the underlying error that caused the failure
Cause error
// Hints provides actionable suggestions for fixing the problem
Hints []string
}
SyncError represents a synchronization failure with actionable context. It provides detailed information about what stage failed and suggestions for how to fix the problem.
func NewConflictError ¶
NewConflictError creates a ConflictError.
func NewConnectionError ¶
NewConnectionError creates a ConnectionError.
func NewFallbackError ¶
NewFallbackError creates a FallbackError.
func NewOperationError ¶
NewOperationError creates an OperationError.
func NewParseError ¶
NewParseError creates a ParseError.
func NewValidationError ¶
NewValidationError creates a ValidationError.
type SyncMode ¶
type SyncMode string
SyncMode indicates which sync strategy was used.
const ( // SyncModeFineGrained indicates fine-grained API operations were used. SyncModeFineGrained SyncMode = "fine_grained" // SyncModeRawInitial indicates raw push was used for initial configuration (version=1). SyncModeRawInitial SyncMode = "raw_initial" // SyncModeRawThreshold indicates raw push was used because changes exceeded threshold. SyncModeRawThreshold SyncMode = "raw_threshold" // SyncModeRawFallback indicates raw push was used as fallback after fine-grained failure. SyncModeRawFallback SyncMode = "raw_fallback" )
type SyncOptions ¶
type SyncOptions struct {
// MaxRetries for 409 version conflict errors (default: 3)
// These are always retried as they're recoverable errors.
MaxRetries int
// Timeout for the entire sync operation (default: 2 minutes)
Timeout time.Duration
// ContinueOnError continues applying operations even if some fail (default: false)
// When false, the first error stops execution.
ContinueOnError bool
// FallbackToRaw enables automatic fallback to raw config push on non-409 errors (default: true)
// When enabled, if fine-grained sync fails with non-recoverable errors,
// the library automatically falls back to pushing the complete raw configuration.
FallbackToRaw bool
// VerifyReload enables async reload verification after sync (default: true)
// When true, polls the reload status endpoint until succeeded/failed/timeout.
// Disable for dry-run or when reload verification is not needed.
VerifyReload bool
// ReloadVerificationTimeout is the maximum time to wait for reload verification (default: 10s)
// This should be set higher than the DataPlane API's reload-delay setting.
// Only used when VerifyReload is true.
ReloadVerificationTimeout time.Duration
// MaxParallel limits concurrent Dataplane API operations during sync.
// This prevents overwhelming the API when syncing large configurations.
// 0 means unlimited (not recommended for large configs).
MaxParallel int
// RawPushThreshold triggers raw config push when change count exceeds this value.
// When set to 0, this threshold check is disabled (fine-grained sync always used).
// Note: Version 1 (initial state) always triggers raw push regardless of this setting.
RawPushThreshold int
// PreParsedConfig is an optional pre-parsed desired configuration.
// If provided, sync skips parsing the desiredConfig string, saving CPU and allocations.
// If nil, the config will be parsed normally (backward compatible).
// This is typically set by the validation pipeline which already parsed the config.
PreParsedConfig *parserconfig.StructuredConfig
// CachedCurrentConfig is an optional cached parsed current configuration from a previous sync.
// When set together with CachedConfigVersion, the orchestrator calls GetVersion() first
// and skips the expensive GetRawConfiguration() + parse if the version matches.
// On mismatch or error, falls through to the full fetch+parse path.
CachedCurrentConfig *parserconfig.StructuredConfig
// CachedConfigVersion is the expected config version on the pod.
// Only used when CachedCurrentConfig is also set.
CachedConfigVersion int64
// ContentChecksum is the pre-computed checksum of the desired config + aux files.
// When set together with LastDeployedChecksum, the orchestrator skips the
// expensive auxiliary file comparison (which downloads content from HAProxy via
// Dataplane API) if both checksums match — the desired state hasn't changed
// since the last successful sync.
ContentChecksum string
// LastDeployedChecksum is the content checksum from the last successful sync
// to this endpoint. When it matches ContentChecksum, auxiliary file comparison
// is skipped because the desired state is identical to what was last deployed.
// Drift prevention syncs should leave this empty to force comparison.
LastDeployedChecksum string
}
SyncOptions configures synchronization behavior.
func DefaultSyncOptions ¶
func DefaultSyncOptions() *SyncOptions
DefaultSyncOptions returns sensible default sync options.
func DryRunOptions ¶
func DryRunOptions() *SyncOptions
DryRunOptions returns options configured for dry-run mode.
type SyncResult ¶
type SyncResult struct {
// Success indicates whether the sync completed successfully
Success bool
// AppliedOperations contains structured information about operations that were applied
AppliedOperations []AppliedOperation
// ReloadTriggered indicates whether a HAProxy reload was triggered
// true when commit status is 202, false when 200
ReloadTriggered bool
// ReloadID is the reload identifier from the Reload-ID response header
// Only set when ReloadTriggered is true
ReloadID string
// ReloadVerified indicates whether the reload was verified as successful.
// Only set when VerifyReload option is enabled and ReloadTriggered is true.
ReloadVerified bool
// ReloadVerificationError contains an error message if reload verification failed.
// This includes timeout errors or explicit reload failures from HAProxy.
ReloadVerificationError string
// SyncMode indicates which sync strategy was used.
// See SyncMode* constants for possible values.
SyncMode SyncMode
// Duration of the sync operation
Duration time.Duration
// Retries indicates how many times operations were retried (for 409 conflicts)
Retries int
// Details contains detailed diff information
// This field is always populated regardless of SyncMode
Details DiffDetails
// Message provides additional context about the result
Message string
// PostSyncVersion is the config version on the pod after a successful sync.
// Callers can cache this alongside the desired parsed config to skip
// redundant GetRawConfiguration() + parse on subsequent syncs when the
// pod's version hasn't changed. Zero means version was not captured.
PostSyncVersion int64
}
SyncResult contains detailed information about a sync operation.
func Sync ¶
func Sync(ctx context.Context, endpoint *Endpoint, desiredConfig string, auxFiles *AuxiliaryFiles, opts *SyncOptions) (*SyncResult, error)
Sync synchronizes the desired HAProxy configuration to the dataplane endpoint.
This is a convenience function that creates a client internally for one-off operations. For production use with multiple operations, create a Client explicitly to reuse connections:
client, err := dataplane.NewClient(ctx, endpoint)
if err != nil {
return err
}
defer client.Close()
result, err := client.Sync(ctx, desiredConfig, auxFiles, opts)
Parameters:
- ctx: Context for cancellation and timeout
- endpoint: Dataplane API connection information
- desiredConfig: The desired HAProxy configuration as a string
- auxFiles: Auxiliary files to sync (use nil for defaults)
- opts: Sync options (use nil for defaults)
Returns:
- *SyncResult: Detailed information about the sync operation
- error: Detailed error with actionable hints if the sync fails
func (*SyncResult) String ¶
func (r *SyncResult) String() string
String returns a human-readable summary of the sync result.
func (*SyncResult) UsedRawPush ¶
func (r *SyncResult) UsedRawPush() bool
UsedRawPush returns true if raw configuration push was used instead of fine-grained sync. This is a convenience helper for code that needs to know whether any form of raw push was used.
type ValidationError ¶
type ValidationError struct {
// Phase indicates which validation phase failed: "syntax" or "semantic"
Phase string
// Message is the validation error message
Message string
// Cause is the underlying error
Cause error
}
ValidationError represents semantic validation failure from HAProxy.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
Error implements the error interface.
func (*ValidationError) Unwrap ¶
func (e *ValidationError) Unwrap() error
Unwrap returns the underlying error for error unwrapping.
type ValidationPaths ¶
type ValidationPaths struct {
// TempDir is the root temp directory for validation files.
// The validator is responsible for cleaning this up after validation completes.
// This prevents race conditions where the renderer's cleanup runs before
// the async validator can use the validation files.
TempDir string
MapsDir string
SSLCertsDir string
CRTListDir string // Directory for CRT-list files (may differ from SSLCertsDir on HAProxy < 3.2)
GeneralStorageDir string
ConfigFile string
}
ValidationPaths holds the filesystem paths for HAProxy validation. These paths must match the HAProxy Dataplane API server's resource configuration.
type Version ¶
Version represents HAProxy or DataPlane API version with major.minor components. Only major and minor are used for compatibility comparison.
func DetectLocalVersion ¶
DetectLocalVersion runs "haproxy -v" and returns the local HAProxy version. Returns an error if haproxy is not found or version cannot be parsed.
func ParseHAProxyVersionOutput ¶
ParseHAProxyVersionOutput parses the output of "haproxy -v" command. Expected format: "HAProxy version 3.2.9 2025/11/21 - https://haproxy.org/\n..." Returns extracted major.minor version.
func VersionFromAPIInfo ¶
func VersionFromAPIInfo(info *client.VersionInfo) (*Version, error)
VersionFromAPIInfo converts client.VersionInfo (from /v3/info) to Version. The API version string format is "vX.Y.Z commit" (e.g., "v3.2.6 87ad0bcf").
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package auxiliaryfiles provides functionality for synchronizing auxiliary files (general files, SSL certificates, map files, crt-lists) with the HAProxy Dataplane API.
|
Package auxiliaryfiles provides functionality for synchronizing auxiliary files (general files, SSL certificates, map files, crt-lists) with the HAProxy Dataplane API. |
|
Package client provides a high-level wrapper around the generated HAProxy Dataplane API client.
|
Package client provides a high-level wrapper around the generated HAProxy Dataplane API client. |
|
enterprise
Package enterprise provides client operations for HAProxy Enterprise-only DataPlane API endpoints.
|
Package enterprise provides client operations for HAProxy Enterprise-only DataPlane API endpoints. |
|
Package comparator provides comparison functions for HAProxy Enterprise Edition sections.
|
Package comparator provides comparison functions for HAProxy Enterprise Edition sections. |
|
sections
Package sections provides factory functions for creating HAProxy configuration operations.
|
Package sections provides factory functions for creating HAProxy configuration operations. |
|
sections/executors
Package executors provides pre-built executor functions for HAProxy configuration operations.
|
Package executors provides pre-built executor functions for HAProxy configuration operations. |
|
Package parser provides HAProxy configuration parsing using client-native library.
|
Package parser provides HAProxy configuration parsing using client-native library. |
|
enterprise
Package enterprise provides HAProxy Enterprise Edition configuration parsing.
|
Package enterprise provides HAProxy Enterprise Edition configuration parsing. |
|
enterprise/directives
Package types provides parser implementations for HAProxy Enterprise Edition section-specific directives.
|
Package types provides parser implementations for HAProxy Enterprise Edition section-specific directives. |
|
enterprise/parsers
Package parsers provides wrapper parsers for HAProxy Enterprise Edition directives.
|
Package parsers provides wrapper parsers for HAProxy Enterprise Edition directives. |
|
parserconfig
Package parserconfig provides canonical configuration types for HAProxy parsing.
|
Package parserconfig provides canonical configuration types for HAProxy parsing. |
|
Package synchronizer provides configuration synchronization between desired state and HAProxy via the Dataplane API.
|
Package synchronizer provides configuration synchronization between desired state and HAProxy via the Dataplane API. |
|
Package validators provides zero-allocation OpenAPI validation for HAProxy models.
|
Package validators provides zero-allocation OpenAPI validation for HAProxy models. |