client

package
v0.1.0-alpha.9 Latest Latest
Warning

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

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

README

pkg/dataplane/client

Low-level HAProxy Dataplane API client wrapper.

Overview

Provides a wrapper around haproxytech/client-native for accessing the HAProxy Dataplane API.

Quick Start

import "haptic/pkg/dataplane/client"

client, err := client.New(client.Config{
    BaseURL:  "http://haproxy:5555",
    Username: "admin",
    Password: "password",
})

License

See main repository for license information.

Documentation

Overview

Package client provides a high-level wrapper around the generated HAProxy Dataplane API client.

This wrapper adds: - Multi-version support (v3.0, v3.1, v3.2) - Runtime version detection - Capability-based feature detection - Transaction lifecycle management - Configuration fetch/push operations - Error handling and retry logic

Package client provides a multi-version wrapper for HAProxy Dataplane API clients.

This package implements the Kubernetes-style clientset pattern to support multiple HAProxy DataPlane API versions (3.0, 3.1, 3.2) with: - Runtime version detection using /v3/info endpoint - Capability-based routing for graceful degradation - Version-specific client accessors

Index

Constants

View Source
const ReloadIDHeader = "Reload-Id"

ReloadIDHeader is the HTTP header name used by HAProxy Data Plane API to return the reload ID when an operation triggers a reload.

Variables

View Source
var ErrEnterpriseRequired = errors.New("this operation requires HAProxy Enterprise edition")

ErrEnterpriseRequired is returned when an enterprise-only operation is attempted on a HAProxy Community edition instance.

Functions

func CheckResponse

func CheckResponse(resp *http.Response, operation string) error

CheckResponse validates an HTTP response status code and logs failures with full context. It reads and logs the response body for debugging, then returns a user-friendly error.

Usage:

resp, err := c.Dispatch(ctx, callFunc)
if err != nil {
    return fmt.Errorf("failed to create backend: %w", err)
}
defer resp.Body.Close()

if err := client.CheckResponse(resp, "create backend"); err != nil {
    return err
}

func ConvertAPIMetadataToClient

func ConvertAPIMetadataToClient(apiMetadata map[string]map[string]interface{}) map[string]interface{}

ConvertAPIMetadataToClient converts Dataplane API nested metadata to client-native flat format.

This is the reverse operation of ConvertClientMetadataToAPI, used when reading configurations from the Dataplane API and converting them back to client-native models.

Dataplane API format:

map[string]map[string]interface{}{"comment": {"value": "Pod: echo-server-v2"}}

Converts to client-native format:

map[string]interface{}{"comment": "Pod: echo-server-v2"}

func ConvertClientMetadataToAPI

func ConvertClientMetadataToAPI(clientMetadata map[string]interface{}) map[string]map[string]interface{}

ConvertClientMetadataToAPI converts client-native flat metadata to Dataplane API nested format.

The client-native library uses a flat map structure for metadata:

map[string]interface{}{"comment": "Pod: echo-server-v2"}

The Dataplane API expects a nested map structure:

map[string]map[string]interface{}{"comment": {"value": "Pod: echo-server-v2"}}

func ConvertToVersioned

func ConvertToVersioned[TV32, TV31, TV30 any](jsonData []byte, versionMinor int) (interface{}, error)

ConvertToVersioned unmarshals JSON into a version-specific API model. Uses generics for type-safe conversion without needing a converter registry.

The versionMinor parameter determines which type to unmarshal into:

  • minor >= 2: uses TV32 (DataPlane API v3.2+)
  • minor >= 1: uses TV31 (DataPlane API v3.1)
  • minor < 1: uses TV30 (DataPlane API v3.0)

Usage:

apiModel, err := ConvertToVersioned[v32.Server, v31.Server, v30.Server](jsonData, version.Minor)

func DispatchCreate

func DispatchCreate[TUnified any, TV32 any, TV31 any, TV30 any, TV32EE any, TV31EE any, TV30EE any](
	ctx context.Context,
	c *DataplaneClient,
	unifiedModel TUnified,
	v32Call func(TV32) (*http.Response, error),
	v31Call func(TV31) (*http.Response, error),
	v30Call func(TV30) (*http.Response, error),
	v32eeCall func(TV32EE) (*http.Response, error),
	v31eeCall func(TV31EE) (*http.Response, error),
	v30eeCall func(TV30EE) (*http.Response, error),
) (*http.Response, error)

DispatchCreate is a generic helper for create operations that handles: - JSON marshaling of the unified API model - JSON unmarshaling into version-specific types - Dispatching to the appropriate version-specific client - Response validation

This eliminates the repetitive dispatch pattern that was duplicated 125+ times across section files.

Type parameters:

  • TUnified: The unified client-native model type (e.g., models.Backend)
  • TV32, TV31, TV30: Community edition version-specific model types
  • TV32EE, TV31EE, TV30EE: Enterprise edition version-specific model types

Each callback receives only the unmarshaled model - params should be created inside the callback. This ensures version-specific params are always created with the correct type.

Usage example:

resp, err := DispatchCreate(ctx, c, model,
    func(m v32.Backend) (*http.Response, error) {
        params := &v32.CreateBackendParams{TransactionId: &txID}
        return clientset.V32().CreateBackend(ctx, params, m)
    },
    func(m v31.Backend) (*http.Response, error) {
        params := &v31.CreateBackendParams{TransactionId: &txID}
        return clientset.V31().CreateBackend(ctx, params, m)
    },
    func(m v30.Backend) (*http.Response, error) {
        params := &v30.CreateBackendParams{TransactionId: &txID}
        return clientset.V30().CreateBackend(ctx, params, m)
    },
    func(m v32ee.Backend) (*http.Response, error) {
        params := &v32ee.CreateBackendParams{TransactionId: &txID}
        return clientset.V32EE().CreateBackend(ctx, params, m)
    },
    func(m v31ee.Backend) (*http.Response, error) {
        params := &v31ee.CreateBackendParams{TransactionId: &txID}
        return clientset.V31EE().CreateBackend(ctx, params, m)
    },
    func(m v30ee.Backend) (*http.Response, error) {
        params := &v30ee.CreateBackendParams{TransactionId: &txID}
        return clientset.V30EE().CreateBackend(ctx, params, m)
    },
)

func DispatchCreateChild

func DispatchCreateChild[TUnified any, TV32 any, TV31 any, TV30 any, TV32EE any, TV31EE any, TV30EE any](
	ctx context.Context,
	c *DataplaneClient,
	parentName string,
	index int,
	unifiedModel TUnified,
	v32Call func(string, int, TV32) (*http.Response, error),
	v31Call func(string, int, TV31) (*http.Response, error),
	v30Call func(string, int, TV30) (*http.Response, error),
	v32eeCall func(string, int, TV32EE) (*http.Response, error),
	v31eeCall func(string, int, TV31EE) (*http.Response, error),
	v30eeCall func(string, int, TV30EE) (*http.Response, error),
) (*http.Response, error)

DispatchCreateChild is a generic helper for creating child resources (e.g., binds, servers, ACLs). Child resources belong to a parent (e.g., frontend, backend) and require the parent name.

Each callback receives parent name, index, and unmarshaled model - params should be created inside the callback. This ensures version-specific params are always created with the correct type.

Usage example:

resp, err := DispatchCreateChild(ctx, c, parentName, index, model,
    func(parent string, idx int, m v32.Acl) (*http.Response, error) {
        params := &v32.CreateAclFrontendParams{TransactionId: &txID}
        return clientset.V32().CreateAclFrontend(ctx, parent, idx, params, m)
    },
    func(parent string, idx int, m v31.Acl) (*http.Response, error) {
        params := &v31.CreateAclFrontendParams{TransactionId: &txID}
        return clientset.V31().CreateAclFrontend(ctx, parent, idx, params, m)
    },
    func(parent string, idx int, m v30.Acl) (*http.Response, error) {
        params := &v30.CreateAclFrontendParams{TransactionId: &txID}
        return clientset.V30().CreateAclFrontend(ctx, parent, idx, params, m)
    },
    func(parent string, idx int, m v32ee.Acl) (*http.Response, error) {
        params := &v32ee.CreateAclFrontendParams{TransactionId: &txID}
        return clientset.V32EE().CreateAclFrontend(ctx, parent, idx, params, m)
    },
    func(parent string, idx int, m v31ee.Acl) (*http.Response, error) {
        params := &v31ee.CreateAclFrontendParams{TransactionId: &txID}
        return clientset.V31EE().CreateAclFrontend(ctx, parent, idx, params, m)
    },
    func(parent string, idx int, m v30ee.Acl) (*http.Response, error) {
        params := &v30ee.CreateAclFrontendParams{TransactionId: &txID}
        return clientset.V30EE().CreateAclFrontend(ctx, parent, idx, params, m)
    },
)

func DispatchDelete

func DispatchDelete(
	ctx context.Context,
	c *DataplaneClient,
	name string,
	v32Call func(string) (*http.Response, error),
	v31Call func(string) (*http.Response, error),
	v30Call func(string) (*http.Response, error),
	v32eeCall func(string) (*http.Response, error),
	v31eeCall func(string) (*http.Response, error),
	v30eeCall func(string) (*http.Response, error),
) (*http.Response, error)

DispatchDelete is a generic helper for delete operations. No model marshaling needed since delete only requires the resource name.

Each callback receives only the name - params should be created inside the callback. This ensures version-specific params are always created with the correct type.

Usage example:

resp, err := DispatchDelete(ctx, c, name,
    func(n string) (*http.Response, error) {
        params := &v32.DeleteBackendParams{TransactionId: &txID}
        return clientset.V32().DeleteBackend(ctx, n, params)
    },
    func(n string) (*http.Response, error) {
        params := &v31.DeleteBackendParams{TransactionId: &txID}
        return clientset.V31().DeleteBackend(ctx, n, params)
    },
    func(n string) (*http.Response, error) {
        params := &v30.DeleteBackendParams{TransactionId: &txID}
        return clientset.V30().DeleteBackend(ctx, n, params)
    },
    func(n string) (*http.Response, error) {
        params := &v32ee.DeleteBackendParams{TransactionId: &txID}
        return clientset.V32EE().DeleteBackend(ctx, n, params)
    },
    func(n string) (*http.Response, error) {
        params := &v31ee.DeleteBackendParams{TransactionId: &txID}
        return clientset.V31EE().DeleteBackend(ctx, n, params)
    },
    func(n string) (*http.Response, error) {
        params := &v30ee.DeleteBackendParams{TransactionId: &txID}
        return clientset.V30EE().DeleteBackend(ctx, n, params)
    },
)

func DispatchDeleteChild

func DispatchDeleteChild(
	ctx context.Context,
	c *DataplaneClient,
	parentName string,
	index int,
	v32Call func(string, int) (*http.Response, error),
	v31Call func(string, int) (*http.Response, error),
	v30Call func(string, int) (*http.Response, error),
	v32eeCall func(string, int) (*http.Response, error),
	v31eeCall func(string, int) (*http.Response, error),
	v30eeCall func(string, int) (*http.Response, error),
) (*http.Response, error)

DispatchDeleteChild is a generic helper for deleting child resources. No model marshaling needed since delete only requires parent name and index.

Each callback receives parent name and index - params should be created inside the callback. This ensures version-specific params are always created with the correct type.

Usage example:

resp, err := DispatchDeleteChild(ctx, c, parentName, index,
    func(parent string, idx int) (*http.Response, error) {
        params := &v32.DeleteAclFrontendParams{TransactionId: &txID}
        return clientset.V32().DeleteAclFrontend(ctx, parent, idx, params)
    },
    func(parent string, idx int) (*http.Response, error) {
        params := &v31.DeleteAclFrontendParams{TransactionId: &txID}
        return clientset.V31().DeleteAclFrontend(ctx, parent, idx, params)
    },
    func(parent string, idx int) (*http.Response, error) {
        params := &v30.DeleteAclFrontendParams{TransactionId: &txID}
        return clientset.V30().DeleteAclFrontend(ctx, parent, idx, params)
    },
    func(parent string, idx int) (*http.Response, error) {
        params := &v32ee.DeleteAclFrontendParams{TransactionId: &txID}
        return clientset.V32EE().DeleteAclFrontend(ctx, parent, idx, params)
    },
    func(parent string, idx int) (*http.Response, error) {
        params := &v31ee.DeleteAclFrontendParams{TransactionId: &txID}
        return clientset.V31EE().DeleteAclFrontend(ctx, parent, idx, params)
    },
    func(parent string, idx int) (*http.Response, error) {
        params := &v30ee.DeleteAclFrontendParams{TransactionId: &txID}
        return clientset.V30EE().DeleteAclFrontend(ctx, parent, idx, params)
    },
)

func DispatchEnterpriseOnlyGeneric

func DispatchEnterpriseOnlyGeneric[T any](
	ctx context.Context,
	clientset *Clientset,
	call EnterpriseCallFunc[T],
) (T, error)

DispatchEnterpriseOnlyGeneric is a generic version of DispatchEnterpriseOnly for non-HTTP response types. Use this when the return type is not *http.Response (e.g., for parsed data, structs, etc.).

Example (returning parsed WAF profile list):

profiles, err := DispatchEnterpriseOnlyGeneric[[]WafProfile](ctx, c.clientset, EnterpriseCallFunc[[]WafProfile]{
    V32EE: func(c *v32ee.Client) ([]WafProfile, error) {
        resp, err := c.GetWafProfiles(ctx, &v32ee.GetWafProfilesParams{})
        if err != nil {
            return nil, err
        }
        defer resp.Body.Close()
        // ... parse profiles from response ...
        return profiles, nil
    },
    // ... similar for V31EE and V30EE ...
})

func DispatchGeneric

func DispatchGeneric[T any](
	ctx context.Context,
	clientset *Clientset,
	call CallFunc[T],
) (T, error)

DispatchGeneric is a generic version of Dispatch for non-HTTP response types. Use this when the return type is not *http.Response (e.g., for string, int64, etc.).

This is a package-level function because it needs to work with any clientset, not just DataplaneClient instances.

Example (returning parsed data instead of raw response):

version, err := DispatchGeneric[int64](ctx, c.clientset, CallFunc[int64]{
    V32: func(c *v32.Client) (int64, error) {
        resp, err := c.GetConfigurationVersion(ctx, &v32.GetConfigurationVersionParams{})
        if err != nil {
            return 0, err
        }
        defer resp.Body.Close()
        // ... parse version from response ...
        return parsedVersion, nil
    },
    // ... similar for V31 and V30 ...
})

func DispatchReplaceChild

func DispatchReplaceChild[TUnified any, TV32 any, TV31 any, TV30 any, TV32EE any, TV31EE any, TV30EE any](
	ctx context.Context,
	c *DataplaneClient,
	parentName string,
	index int,
	unifiedModel TUnified,
	v32Call func(string, int, TV32) (*http.Response, error),
	v31Call func(string, int, TV31) (*http.Response, error),
	v30Call func(string, int, TV30) (*http.Response, error),
	v32eeCall func(string, int, TV32EE) (*http.Response, error),
	v31eeCall func(string, int, TV31EE) (*http.Response, error),
	v30eeCall func(string, int, TV30EE) (*http.Response, error),
) (*http.Response, error)

DispatchReplaceChild is a generic helper for replacing/updating child resources. Similar to DispatchCreateChild but for replace operations.

Each callback receives parent name, index, and unmarshaled model - params should be created inside the callback. This ensures version-specific params are always created with the correct type.

Usage example:

resp, err := DispatchReplaceChild(ctx, c, parentName, index, model,
    func(parent string, idx int, m v32.Acl) (*http.Response, error) {
        params := &v32.ReplaceAclFrontendParams{TransactionId: &txID}
        return clientset.V32().ReplaceAclFrontend(ctx, parent, idx, params, m)
    },
    func(parent string, idx int, m v31.Acl) (*http.Response, error) {
        params := &v31.ReplaceAclFrontendParams{TransactionId: &txID}
        return clientset.V31().ReplaceAclFrontend(ctx, parent, idx, params, m)
    },
    func(parent string, idx int, m v30.Acl) (*http.Response, error) {
        params := &v30.ReplaceAclFrontendParams{TransactionId: &txID}
        return clientset.V30().ReplaceAclFrontend(ctx, parent, idx, params, m)
    },
    func(parent string, idx int, m v32ee.Acl) (*http.Response, error) {
        params := &v32ee.ReplaceAclFrontendParams{TransactionId: &txID}
        return clientset.V32EE().ReplaceAclFrontend(ctx, parent, idx, params, m)
    },
    func(parent string, idx int, m v31ee.Acl) (*http.Response, error) {
        params := &v31ee.ReplaceAclFrontendParams{TransactionId: &txID}
        return clientset.V31EE().ReplaceAclFrontend(ctx, parent, idx, params, m)
    },
    func(parent string, idx int, m v30ee.Acl) (*http.Response, error) {
        params := &v30ee.ReplaceAclFrontendParams{TransactionId: &txID}
        return clientset.V30EE().ReplaceAclFrontend(ctx, parent, idx, params, m)
    },
)

func DispatchUpdate

func DispatchUpdate[TUnified any, TV32 any, TV31 any, TV30 any, TV32EE any, TV31EE any, TV30EE any](
	ctx context.Context,
	c *DataplaneClient,
	name string,
	unifiedModel TUnified,
	v32Call func(string, TV32) (*http.Response, error),
	v31Call func(string, TV31) (*http.Response, error),
	v30Call func(string, TV30) (*http.Response, error),
	v32eeCall func(string, TV32EE) (*http.Response, error),
	v31eeCall func(string, TV31EE) (*http.Response, error),
	v30eeCall func(string, TV30EE) (*http.Response, error),
) (*http.Response, error)

DispatchUpdate is a generic helper for update/replace operations. Similar to DispatchCreate but includes the resource name parameter.

Each callback receives the name and unmarshaled model - params should be created inside the callback. This ensures version-specific params are always created with the correct type.

Usage example:

resp, err := DispatchUpdate(ctx, c, name, model,
    func(n string, m v32.Backend) (*http.Response, error) {
        params := &v32.ReplaceBackendParams{TransactionId: &txID}
        return clientset.V32().ReplaceBackend(ctx, n, params, m)
    },
    func(n string, m v31.Backend) (*http.Response, error) {
        params := &v31.ReplaceBackendParams{TransactionId: &txID}
        return clientset.V31().ReplaceBackend(ctx, n, params, m)
    },
    func(n string, m v30.Backend) (*http.Response, error) {
        params := &v30.ReplaceBackendParams{TransactionId: &txID}
        return clientset.V30().ReplaceBackend(ctx, n, params, m)
    },
    func(n string, m v32ee.Backend) (*http.Response, error) {
        params := &v32ee.ReplaceBackendParams{TransactionId: &txID}
        return clientset.V32EE().ReplaceBackend(ctx, n, params, m)
    },
    func(n string, m v31ee.Backend) (*http.Response, error) {
        params := &v31ee.ReplaceBackendParams{TransactionId: &txID}
        return clientset.V31EE().ReplaceBackend(ctx, n, params, m)
    },
    func(n string, m v30ee.Backend) (*http.Response, error) {
        params := &v30ee.ReplaceBackendParams{TransactionId: &txID}
        return clientset.V30EE().ReplaceBackend(ctx, n, params, m)
    },
)

func ExecuteWithExponentialBackoff

func ExecuteWithExponentialBackoff[T any](
	ctx context.Context,
	client *DataplaneClient,
	baseDelay time.Duration,
	fn func(ctx context.Context, version int) (T, error),
) (T, error)

ExecuteWithExponentialBackoff is a convenience wrapper that uses exponential backoff.

This is useful for operations that may experience transient version conflicts and benefit from spacing out retry attempts.

func ExecuteWithVersion

func ExecuteWithVersion[T any](
	ctx context.Context,
	client *DataplaneClient,
	fn func(ctx context.Context, version int) (T, error),
) (T, error)

ExecuteWithVersion executes an API call that requires a version parameter with automatic retry on version conflicts.

This is a generic helper for API calls that use version-based optimistic locking instead of transactions. It automatically: 1. Fetches the current configuration version 2. Executes the provided function with that version 3. Retries on version conflicts (409/406 responses) by fetching the new version 4. Repeats up to 3 times

This is commonly used for runtime API operations (server updates, map updates, etc.) that don't require a full transaction.

Parameters:

  • ctx: Context for cancellation and timeout
  • client: The DataplaneClient to use for version fetching
  • fn: The function to execute that takes a version parameter and returns a result

Example:

resp, err := ExecuteWithVersion(ctx, client, func(ctx context.Context, version int) (*http.Response, error) {
    params := &dataplaneapi.CreateServerParams{
        Version: &version,
    }
    return apiClient.CreateServer(ctx, "backend1", params, server)
})

func ExecuteWithVersionAndLogger

func ExecuteWithVersionAndLogger[T any](
	ctx context.Context,
	client *DataplaneClient,
	fn func(ctx context.Context, version int) (T, error),
	logger *slog.Logger,
) (T, error)

ExecuteWithVersionAndLogger is like ExecuteWithVersion but accepts a custom logger for retry logging.

This is useful when you want retry attempts logged with component-specific context. If logger is nil, no retry logging is performed.

func ExecuteWithVersionCustom

func ExecuteWithVersionCustom[T any](
	ctx context.Context,
	client *DataplaneClient,
	config RetryConfig,
	fn func(ctx context.Context, version int) (T, error),
) (T, error)

ExecuteWithVersionTimeout is like ExecuteWithVersion but allows specifying a custom retry configuration.

This is useful when you need different retry behavior (more attempts, different backoff strategy, etc.).

func IsEnterpriseVersion

func IsEnterpriseVersion(version string) bool

func MarshalForVersion

func MarshalForVersion(model interface{}) ([]byte, error)

MarshalForVersion marshals a unified client-native model to JSON and transforms metadata from flat to nested format. Returns JSON ready for version-specific unmarshaling.

This centralizes the marshal + metadata transformation that both the dispatcher helpers and validator need when converting client-native models to API models.

func ParseVersion

func ParseVersion(version string) (major, minor int, err error)

ParseVersion extracts major and minor version numbers from version string. Example: "v3.2.6 87ad0bcf" -> (3, 2, nil). This function is exported for use by the version compatibility checking logic.

func ParseVersionFromHeader

func ParseVersionFromHeader(header string) (int64, error)

ParseVersionFromHeader extracts the version number from a Configuration-Version header.

func SanitizeSSLCertName

func SanitizeSSLCertName(name string) string

SanitizeSSLCertName sanitizes a certificate name for HAProxy Data Plane API storage. The API replaces dots in the filename (excluding the extension) with underscores. For example: "example.com.pem" becomes "example_com.pem". This function is exported for use in tests to compare certificate names.

func SanitizeStorageName

func SanitizeStorageName(name string) string

SanitizeStorageName sanitizes a filename for HAProxy storage. The API replaces dots in the filename (excluding the extension) with underscores. Example: "example.com.pem" becomes "example_com.pem".

func TransformClientMetadataInJSON

func TransformClientMetadataInJSON(jsonData []byte) ([]byte, error)

TransformClientMetadataInJSON converts metadata within a JSON object from client-native flat format to API nested format.

This is used when converting client-native models to version-specific API models via JSON marshal/unmarshal. The metadata field format differs between the two:

Input (client-native):

{"metadata": {"comment": "string value"}, ...}

Output (API format):

{"metadata": {"comment": {"value": "string value"}}, ...}

This function recursively transforms metadata in nested objects (like servers within backends, binds within frontends, etc.).

If the JSON doesn't contain a metadata field, or the metadata is not in the expected format, the original JSON is returned unchanged.

func UnsanitizeStorageName

func UnsanitizeStorageName(name string) string

UnsanitizeStorageName reverses sanitization (best-effort). Converts underscores back to dots in the basename. Example: "example_com.pem" becomes "example.com.pem". Note: This may not be perfect for filenames that originally contained underscores.

func VersionMinorFromPtr

func VersionMinorFromPtr(versionMinor *int) int

VersionMinorFromPtr extracts the minor version from a Version-like struct pointer. Returns 0 (v3.0) if version is nil, which is the safest default. This is a helper for callers that have a version pointer.

func WithRetry

func WithRetry[T any](ctx context.Context, config RetryConfig, fn func(attempt int) (T, error)) (T, error)

WithRetry executes fn with automatic retry logic based on config.

The function fn is called with the current attempt number (1-indexed). If fn returns an error and config.RetryIf returns true, the operation is retried up to config.MaxAttempts times.

Example:

config := RetryConfig{
    MaxAttempts: 3,
    RetryIf:     IsVersionConflict(),
    Backoff:     BackoffExponential,
    BaseDelay:   100 * time.Millisecond,
    Logger:      logger,
}
result, err := WithRetry(ctx, config, func(attempt int) (*Result, error) {
    return doOperation(ctx, attempt)
})

Types

type BackoffStrategy

type BackoffStrategy string

BackoffStrategy determines the delay between retry attempts.

const (
	// BackoffNone applies no delay between retries.
	BackoffNone BackoffStrategy = "none"
	// BackoffLinear applies a fixed delay between retries.
	BackoffLinear BackoffStrategy = "linear"
	// BackoffExponential applies exponentially increasing delays between retries.
	BackoffExponential BackoffStrategy = "exponential"
)

type CRTListEntry

type CRTListEntry struct {
	// File is the path to the certificate file.
	File string `json:"file,omitempty"`

	// LineNumber is the line number in the crt-list file (read-only, set by HAProxy).
	LineNumber int `json:"line_number,omitempty"`

	// SNIFilter is a list of SNI patterns for this certificate.
	SNIFilter []string `json:"sni_filter,omitempty"`

	// SSLBindConfig contains SSL bind configuration options for this certificate.
	SSLBindConfig string `json:"ssl_bind_config,omitempty"`
}

CRTListEntry represents an entry within a CRT-list file. Each entry references a certificate file with optional SNI filters and SSL bind options.

type CallFunc

type CallFunc[T any] struct {
	// Community edition clients
	// V32 is the function to call for DataPlane API v3.2+
	V32 func(*v32.Client) (T, error)

	// V31 is the function to call for DataPlane API v3.1
	V31 func(*v31.Client) (T, error)

	// V30 is the function to call for DataPlane API v3.0
	V30 func(*v30.Client) (T, error)

	// Enterprise edition clients
	// V32EE is the function to call for HAProxy Enterprise DataPlane API v3.2+
	V32EE func(*v32ee.Client) (T, error)

	// V31EE is the function to call for HAProxy Enterprise DataPlane API v3.1
	V31EE func(*v31ee.Client) (T, error)

	// V30EE is the function to call for HAProxy Enterprise DataPlane API v3.0
	V30EE func(*v30ee.Client) (T, error)
}

CallFunc represents a versioned API call function. Each field is a function that takes a version-specific client and returns a result of type T. This allows type-safe dispatch to the appropriate client version based on runtime detection.

For HAProxy Community editions, use V30, V31, V32. For HAProxy Enterprise editions, use V30EE, V31EE, V32EE.

Example usage:

resp, err := c.Dispatch(ctx, CallFunc[*http.Response]{
    V32: func(c *v32.Client) (*http.Response, error) { return c.SomeMethod(ctx, params) },
    V31: func(c *v31.Client) (*http.Response, error) { return c.SomeMethod(ctx, params) },
    V30: func(c *v30.Client) (*http.Response, error) { return c.SomeMethod(ctx, params) },
    V32EE: func(c *v32ee.Client) (*http.Response, error) { return c.SomeMethod(ctx, params) },
    V31EE: func(c *v31ee.Client) (*http.Response, error) { return c.SomeMethod(ctx, params) },
    V30EE: func(c *v30ee.Client) (*http.Response, error) { return c.SomeMethod(ctx, params) },
})

type Capabilities

type Capabilities struct {
	// Storage capabilities
	SupportsCrtList        bool // /v3/storage/ssl_crt_lists (v3.2+)
	SupportsMapStorage     bool // /v3/storage/maps (v3.0+)
	SupportsGeneralStorage bool // /v3/storage/general (v3.0+)
	SupportsSslCaFiles     bool // /v3/runtime/ssl_ca_files (v3.2+)
	SupportsSslCrlFiles    bool // /v3/runtime/ssl_crl_files (v3.2+)

	// Configuration capabilities
	SupportsHTTP2            bool // HTTP/2 configuration (v3.0+)
	SupportsQUIC             bool // QUIC/HTTP3 configuration (v3.0+)
	SupportsQUICInitialRules bool // QUIC initial rules endpoints (v3.1+)

	// Observability capabilities
	SupportsLogProfiles bool // /v3/services/haproxy/configuration/log_profiles (v3.1+)
	SupportsTraces      bool // /v3/services/haproxy/configuration/traces (v3.1+)

	// Certificate automation capabilities
	SupportsAcmeProviders bool // /v3/services/haproxy/configuration/acmes (v3.2+)

	// Model metadata capabilities
	SupportsConfigMetadata bool // Metadata field on config models like ACL, Server, etc. (v3.2+)

	// Runtime capabilities
	SupportsRuntimeMaps    bool // Runtime map operations (v3.0+)
	SupportsRuntimeServers bool // Runtime server operations (v3.0+)

	// SupportsWAF indicates WAF management endpoints are available.
	// Includes: waf_body_rules (frontend/backend), waf/rulesets
	// Note: waf_global and waf_profiles require v3.2+ (see SupportsWAFGlobal, SupportsWAFProfiles)
	SupportsWAF bool

	// SupportsWAFGlobal indicates WAF global configuration endpoint is available.
	// Only available in HAProxy Enterprise v3.2+ (waf_global endpoint)
	SupportsWAFGlobal bool

	// SupportsWAFProfiles indicates WAF profile management endpoints are available.
	// Only available in HAProxy Enterprise v3.2+ (waf_profiles endpoint)
	SupportsWAFProfiles bool

	// SupportsUDPLBACLs indicates UDP load balancer ACL endpoints are available.
	// Only available in HAProxy Enterprise v3.2+ (udp_lbs/{name}/acls endpoint)
	SupportsUDPLBACLs bool

	// SupportsUDPLBServerSwitchingRules indicates UDP load balancer server switching rule endpoints are available.
	// Only available in HAProxy Enterprise v3.2+ (udp_lbs/{name}/server_switching_rules endpoint)
	SupportsUDPLBServerSwitchingRules bool

	// SupportsKeepalived indicates Keepalived/VRRP management endpoints are available.
	// Includes: vrrp_instances, vrrp_sync_groups, vrrp_track_scripts, keepalived transactions
	SupportsKeepalived bool

	// SupportsUDPLoadBalancing indicates UDP load balancer management endpoints are available.
	// Includes: udp_lbs with ACLs, dgram_binds, log_targets, server_switching_rules
	SupportsUDPLoadBalancing bool

	// SupportsBotManagement indicates bot management endpoints are available.
	// Includes: botmgmt_profiles, captchas
	SupportsBotManagement bool

	// SupportsGitIntegration indicates Git integration endpoints are available.
	// Includes: git/settings, git/actions
	SupportsGitIntegration bool

	// SupportsDynamicUpdate indicates dynamic update endpoints are available.
	// Includes: dynamic_update_rules, dynamic_update_section
	SupportsDynamicUpdate bool

	// SupportsALOHA indicates ALOHA feature endpoints are available.
	// Includes: aloha, aloha/actions
	SupportsALOHA bool

	// SupportsAdvancedLogging indicates advanced logging endpoints are available.
	// Includes: logs/config, logs/inputs, logs/outputs
	SupportsAdvancedLogging bool

	// SupportsPing indicates the ping endpoint is available.
	// Only available in HAProxy Enterprise v3.2+ (/v3/ping endpoint)
	SupportsPing bool
}

Capabilities defines which features are available for a given DataPlane API version. Version thresholds verified against OpenAPI specs for v3.0, v3.1, v3.2.

type Clientset

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

Clientset provides access to clients for all supported HAProxy DataPlane API versions. This follows the Kubernetes clientset pattern, allowing version-specific operations while maintaining compatibility across HAProxy versions.

func NewClientset

func NewClientset(ctx context.Context, endpoint *Endpoint, logger *slog.Logger) (*Clientset, error)

NewClientset creates a new multi-version clientset for the given endpoint. It detects the server's DataPlane API version and creates appropriate clients.

Example:

clientset, err := client.NewClientset(ctx, client.Endpoint{
    URL:      "http://haproxy:5555",
    Username: "admin",
    Password: "password",
}, logger)
if err != nil {
    return err
}

// Use version-specific client
if clientset.Capabilities().SupportsCrtList {
    client := clientset.V32()
    // Use v3.2-specific features
} else {
    client := clientset.V30()
    // Fallback to v3.0-compatible operations
}

func (*Clientset) Capabilities

func (c *Clientset) Capabilities() Capabilities

Capabilities returns the feature availability map for the detected version.

func (*Clientset) DetectedVersion

func (c *Clientset) DetectedVersion() string

DetectedVersion returns the full version string detected from the server. Example: "v3.2.6 87ad0bcf" for community or "v3.0r1" for enterprise.

func (*Clientset) IsEnterprise

func (c *Clientset) IsEnterprise() bool

IsEnterprise returns true if the detected HAProxy is an Enterprise edition.

func (*Clientset) MajorVersion

func (c *Clientset) MajorVersion() int

MajorVersion returns the major version number (e.g., 3 for v3.x).

func (*Clientset) MinorVersion

func (c *Clientset) MinorVersion() int

MinorVersion returns the minor version number (e.g., 0, 1, or 2 for v3.0, v3.1, v3.2).

func (*Clientset) PreferredClient

func (c *Clientset) PreferredClient() interface{}

PreferredClient returns the most appropriate client based on detected version and edition. This is useful for code that wants to use the best available API without explicitly checking capabilities.

Returns:

  • Enterprise clients (v32ee, v31ee, v30ee) if HAProxy Enterprise is detected
  • Community clients (v32, v31, v30) for HAProxy Community

Version selection:

  • v3.2+ client if server is v3.2+
  • v3.1 client if server is v3.1
  • v3.0 client if server is v3.0 or unknown

func (*Clientset) V30

func (c *Clientset) V30() *v30.Client

V30 returns the DataPlane API v3.0 client. This client is compatible with HAProxy 2.4 and later.

func (*Clientset) V30EE

func (c *Clientset) V30EE() *v30ee.Client

V30EE returns the HAProxy Enterprise DataPlane API v3.0 client.

func (*Clientset) V31

func (c *Clientset) V31() *v31.Client

V31 returns the DataPlane API v3.1 client. This client is compatible with HAProxy 2.6 and later.

func (*Clientset) V31EE

func (c *Clientset) V31EE() *v31ee.Client

V31EE returns the HAProxy Enterprise DataPlane API v3.1 client.

func (*Clientset) V32

func (c *Clientset) V32() *v32.Client

V32 returns the DataPlane API v3.2 client. This client is compatible with HAProxy 2.8 and later.

func (*Clientset) V32EE

func (c *Clientset) V32EE() *v32ee.Client

V32EE returns the HAProxy Enterprise DataPlane API v3.2 client.

type CommitResult

type CommitResult struct {
	// StatusCode is the HTTP status code from the commit response.
	// 200 = configuration applied without reload
	// 202 = configuration applied with reload triggered
	StatusCode int

	// ReloadID is the reload identifier from the Reload-ID response header.
	// Only set when StatusCode is 202 (reload triggered).
	ReloadID string
}

CommitResult contains information about a transaction commit operation.

type Config

type Config struct {
	// BaseURL is the HAProxy Dataplane API endpoint (e.g., "http://localhost:5555/v2")
	BaseURL string

	// Username for basic authentication
	Username string

	// Password for basic authentication
	Password string

	// PodName is the Kubernetes pod name (for observability)
	PodName string

	// HTTPClient allows injecting a custom HTTP client (useful for testing)
	HTTPClient *http.Client

	// Logger for logging request/response details on errors (optional)
	// If nil, slog.Default() will be used
	Logger *slog.Logger
}

Config contains configuration options for creating a DataplaneClient.

type CrtLoad

type CrtLoad struct {
	// Certificate is the certificate filename (required, acts as identifier).
	Certificate string `json:"certificate"`

	// Acme is the ACME section name to use for automatic certificate generation.
	Acme string `json:"acme,omitempty"`

	// Alias is an optional certificate alias.
	Alias string `json:"alias,omitempty"`

	// Domains is a list of domains for ACME certificate generation.
	Domains []string `json:"domains,omitempty"`

	// Issuer is the OCSP issuer filename.
	Issuer string `json:"issuer,omitempty"`

	// Key is the private key filename.
	Key string `json:"key,omitempty"`

	// Ocsp is the OCSP response filename.
	Ocsp string `json:"ocsp,omitempty"`

	// OcspUpdate enables automatic OCSP response updates ("enabled" or "disabled").
	OcspUpdate string `json:"ocsp_update,omitempty"`

	// Sctl is the Signed Certificate Timestamp List filename.
	Sctl string `json:"sctl,omitempty"`
}

CrtLoad represents a certificate load configuration within a crt-store. It specifies how HAProxy loads and manages certificates from a certificate store.

type DataplaneClient

type DataplaneClient struct {
	Endpoint Endpoint // Embedded endpoint information
	// contains filtered or unexported fields
}

DataplaneClient wraps the multi-version Clientset with additional functionality for HAProxy Dataplane API operations. It automatically uses the appropriate client version based on runtime detection.

func New

func New(ctx context.Context, cfg *Config) (*DataplaneClient, error)

New creates a new DataplaneClient with the provided configuration. It automatically detects the server's DataPlane API version and creates an appropriate multi-version clientset.

Example:

client, err := client.New(ctx, client.Config{
    BaseURL:  "http://haproxy-dataplane:5555",
    Username: "admin",
    Password: "password",
})

func NewFromEndpoint

func NewFromEndpoint(ctx context.Context, endpoint *Endpoint, logger *slog.Logger) (*DataplaneClient, error)

NewFromEndpoint creates a new DataplaneClient from an Endpoint. This is a convenience function for creating a client with default options.

func (*DataplaneClient) AddCRTListEntry

func (c *DataplaneClient) AddCRTListEntry(ctx context.Context, crtListName string, entry *CRTListEntry) error

AddCRTListEntry adds a new entry to a CRT-list file. CRT-list entries are only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) BaseURL

func (c *DataplaneClient) BaseURL() string

BaseURL returns the configured base URL for the Dataplane API.

func (*DataplaneClient) Capabilities

func (c *DataplaneClient) Capabilities() Capabilities

Capabilities returns the feature availability for the detected version.

func (*DataplaneClient) Clientset

func (c *DataplaneClient) Clientset() *Clientset

Clientset returns the underlying multi-version clientset for advanced operations. Use this when you need version-specific features or capability checking.

Example:

if client.Clientset().Capabilities().SupportsCrtList {
    v32Client := client.Clientset().V32()
    // Use v3.2-specific crt-list operations
}

func (*DataplaneClient) CreateCRTListFile

func (c *DataplaneClient) CreateCRTListFile(ctx context.Context, name, content string) (string, error)

CreateCRTListFile creates a new crt-list file using multipart form-data. Returns the reload ID if a reload was triggered (empty string if not) and any error. The name parameter can use dots (e.g., "example.com.crtlist"), which will be sanitized automatically before calling the API. CRT-list storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) CreateCrtLoad

func (c *DataplaneClient) CreateCrtLoad(ctx context.Context, crtStoreName string, load *CrtLoad, transactionID string) error

CreateCrtLoad creates a new crt-load entry in a crt-store. CRT loads are only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) CreateGeneralFile

func (c *DataplaneClient) CreateGeneralFile(ctx context.Context, path, content string) (string, error)

CreateGeneralFile creates a new general file using multipart form-data. Returns the reload ID if a reload was triggered (empty string if not) and any error. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) CreateLogProfile

func (c *DataplaneClient) CreateLogProfile(ctx context.Context, profile *LogProfile, transactionID string) error

CreateLogProfile creates a new log profile. Log profiles are only available in HAProxy DataPlane API v3.1+.

func (*DataplaneClient) CreateMapFile

func (c *DataplaneClient) CreateMapFile(ctx context.Context, name, content string) (string, error)

CreateMapFile creates a new map file using multipart form-data. Returns the reload ID if a reload was triggered (empty string if not) and any error. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) CreateSSLCaFile

func (c *DataplaneClient) CreateSSLCaFile(ctx context.Context, name, content string) (string, error)

CreateSSLCaFile creates a new SSL CA file using multipart form-data. Returns the reload ID if a reload was triggered (empty string if not) and any error. SSL CA file storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) CreateSSLCertificate

func (c *DataplaneClient) CreateSSLCertificate(ctx context.Context, name, content string) (string, error)

CreateSSLCertificate creates a new SSL certificate using multipart form-data. Returns the reload ID if a reload was triggered (empty string if not) and any error. The name parameter can use dots (e.g., "example.com.pem"), which will be sanitized automatically before calling the API. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) CreateSSLCrlFile

func (c *DataplaneClient) CreateSSLCrlFile(ctx context.Context, name, content string) (string, error)

CreateSSLCrlFile creates a new SSL CRL file using multipart form-data. Returns the reload ID if a reload was triggered (empty string if not) and any error. SSL CRL file storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) CreateTraces

func (c *DataplaneClient) CreateTraces(ctx context.Context, traces *Traces, transactionID string) error

CreateTraces creates the traces configuration. This is used when no traces configuration exists yet. Traces configuration is only available in HAProxy DataPlane API v3.1+.

func (*DataplaneClient) CreateTransaction

func (c *DataplaneClient) CreateTransaction(ctx context.Context, version int64) (*Transaction, error)

CreateTransaction creates a new transaction with the specified configuration version.

The version parameter enables optimistic locking - if another process has modified the configuration since you fetched the version, transaction creation will fail with a 409 Conflict error. Works with all HAProxy DataPlane API versions (v3.0+).

Example:

version, _ := client.GetVersion(context.Background())
tx, err := client.CreateTransaction(context.Background(), version)
if err != nil {
    slog.Error("failed to create transaction", "error", err)
    os.Exit(1)
}
defer tx.Abort(context.Background()) // Ensure cleanup on error

// Execute operations with tx.ID
// ...

err = tx.Commit(context.Background())

func (*DataplaneClient) DeleteCRTListEntry

func (c *DataplaneClient) DeleteCRTListEntry(ctx context.Context, crtListName, certificate string, lineNumber int) error

DeleteCRTListEntry deletes an entry from a CRT-list file by certificate name and line number. CRT-list entries are only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) DeleteCRTListFile

func (c *DataplaneClient) DeleteCRTListFile(ctx context.Context, name string) error

DeleteCRTListFile deletes a crt-list file by name. The name parameter can use dots (e.g., "example.com.crtlist"), which will be sanitized automatically before calling the API. CRT-list storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) DeleteCrtLoad

func (c *DataplaneClient) DeleteCrtLoad(ctx context.Context, crtStoreName, certificate, transactionID string) error

DeleteCrtLoad deletes a crt-load entry from a crt-store. CRT loads are only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) DeleteGeneralFile

func (c *DataplaneClient) DeleteGeneralFile(ctx context.Context, path string) error

DeleteGeneralFile deletes a general file by path. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) DeleteLogProfile

func (c *DataplaneClient) DeleteLogProfile(ctx context.Context, name, transactionID string) error

DeleteLogProfile deletes a log profile by name. Log profiles are only available in HAProxy DataPlane API v3.1+.

func (*DataplaneClient) DeleteMapFile

func (c *DataplaneClient) DeleteMapFile(ctx context.Context, name string) error

DeleteMapFile deletes a map file by name. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) DeleteSSLCaFile

func (c *DataplaneClient) DeleteSSLCaFile(ctx context.Context, name string) error

DeleteSSLCaFile deletes an SSL CA file by name. SSL CA file storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) DeleteSSLCertificate

func (c *DataplaneClient) DeleteSSLCertificate(ctx context.Context, name string) error

DeleteSSLCertificate deletes an SSL certificate by name. The name parameter can use dots (e.g., "example.com.pem"), which will be sanitized automatically before calling the API. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) DeleteSSLCrlFile

func (c *DataplaneClient) DeleteSSLCrlFile(ctx context.Context, name string) error

DeleteSSLCrlFile deletes an SSL CRL file by name. SSL CRL file storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) DeleteTraces

func (c *DataplaneClient) DeleteTraces(ctx context.Context, transactionID string) error

DeleteTraces deletes the traces configuration. Traces configuration is only available in HAProxy DataPlane API v3.1+.

func (*DataplaneClient) DetectedVersion

func (c *DataplaneClient) DetectedVersion() string

DetectedVersion returns the full version string of the DataPlane API server.

func (*DataplaneClient) Dispatch

func (c *DataplaneClient) Dispatch(ctx context.Context, call CallFunc[*http.Response]) (*http.Response, error)

Dispatch executes the appropriate versioned function based on the detected API version. This is the primary method for executing API calls that work across all versions.

Returns error if:

  • The client type is unexpected
  • The version-specific function is nil
  • The versioned function itself returns an error

Example:

resp, err := c.Dispatch(ctx, CallFunc[*http.Response]{
    V32: func(c *v32.Client) (*http.Response, error) {
        return c.GetAllStorageMapFiles(ctx)
    },
    V31: func(c *v31.Client) (*http.Response, error) {
        return c.GetAllStorageMapFiles(ctx)
    },
    V30: func(c *v30.Client) (*http.Response, error) {
        return c.GetAllStorageMapFiles(ctx)
    },
})

func (*DataplaneClient) DispatchEnterpriseOnly

func (c *DataplaneClient) DispatchEnterpriseOnly(
	ctx context.Context,
	call EnterpriseCallFunc[*http.Response],
) (*http.Response, error)

DispatchEnterpriseOnly executes the appropriate versioned function for enterprise-only endpoints. Returns ErrEnterpriseRequired if connected to HAProxy Community edition.

Use this for enterprise-exclusive features like:

  • WAF management (waf_profiles, waf_body_rules, waf/rulesets)
  • Bot management (botmgmt_profiles, captchas)
  • UDP load balancing (udp_lbs)
  • Keepalived/VRRP (vrrp_instances, vrrp_sync_groups)
  • Advanced logging (logs/config, logs/inputs, logs/outputs)
  • Git integration (git/settings, git/actions)
  • Dynamic updates (dynamic_update_rules)
  • ALOHA features (aloha/actions)

Example:

resp, err := c.DispatchEnterpriseOnly(ctx, EnterpriseCallFunc[*http.Response]{
    V32EE: func(c *v32ee.Client) (*http.Response, error) {
        return c.GetWafProfiles(ctx, &v32ee.GetWafProfilesParams{TransactionId: &txID})
    },
    V31EE: func(c *v31ee.Client) (*http.Response, error) {
        return c.GetWafProfiles(ctx, &v31ee.GetWafProfilesParams{TransactionId: &txID})
    },
    V30EE: func(c *v30ee.Client) (*http.Response, error) {
        return c.GetWafProfiles(ctx, &v30ee.GetWafProfilesParams{TransactionId: &txID})
    },
})

func (*DataplaneClient) DispatchWithCapability

func (c *DataplaneClient) DispatchWithCapability(
	ctx context.Context,
	call CallFunc[*http.Response],
	capabilityCheck func(Capabilities) error,
) (*http.Response, error)

DispatchWithCapability executes the appropriate versioned function with an optional capability check. Use this for version-specific features (e.g., crt-list only available in v3.2+).

The capability check is performed before executing the versioned function. If the check fails, the function is not executed and the capability error is returned.

Parameters:

  • ctx: Context for the API call
  • call: Version-specific functions to execute
  • capabilityCheck: Optional function to verify feature availability. If nil, no check is performed.

Returns error if:

  • Capability check fails
  • The client type is unexpected
  • The version-specific function is nil
  • The versioned function itself returns an error

Example (CRT-list only in v3.2+):

resp, err := c.DispatchWithCapability(ctx, CallFunc[*http.Response]{
    V32: func(c *v32.Client) (*http.Response, error) {
        return c.GetAllStorageSSLCrtListFiles(ctx)
    },
    // V31 and V30 omitted - not supported
}, func(caps Capabilities) error {
    if !caps.SupportsCrtList {
        return fmt.Errorf("crt-list storage requires DataPlane API v3.2+")
    }
    return nil
})

func (*DataplaneClient) GetAllCRTListFiles

func (c *DataplaneClient) GetAllCRTListFiles(ctx context.Context) ([]string, error)

GetAllCRTListFiles retrieves all crt-list file names from the storage. Note: This returns only crt-list file names, not the file contents. Use GetCRTListFileContent to retrieve the actual file contents. CRT-list storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) GetAllCrtLoads

func (c *DataplaneClient) GetAllCrtLoads(ctx context.Context, crtStoreName string) ([]CrtLoad, error)

GetAllCrtLoads retrieves all crt-load entries from a specific crt-store. CRT loads are only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) GetAllGeneralFiles

func (c *DataplaneClient) GetAllGeneralFiles(ctx context.Context) ([]string, error)

GetAllGeneralFiles retrieves all general file paths from the storage. Note: This returns only file paths, not the file contents. Use GetGeneralFileContent to retrieve the actual file contents. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) GetAllLogProfiles

func (c *DataplaneClient) GetAllLogProfiles(ctx context.Context) ([]string, error)

GetAllLogProfiles retrieves all log profile names from the configuration. Log profiles are only available in HAProxy DataPlane API v3.1+.

func (*DataplaneClient) GetAllMapFiles

func (c *DataplaneClient) GetAllMapFiles(ctx context.Context) ([]string, error)

GetAllMapFiles retrieves all map file names from the storage. Note: This returns only map file names, not the file contents. Use GetMapFileContent to retrieve the actual file contents. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) GetAllSSLCaFiles

func (c *DataplaneClient) GetAllSSLCaFiles(ctx context.Context) ([]string, error)

GetAllSSLCaFiles retrieves all SSL CA file names from the runtime storage. Note: This returns only CA file names, not the file contents. Use GetSSLCaFileContent to retrieve the actual file contents. SSL CA file storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) GetAllSSLCertificates

func (c *DataplaneClient) GetAllSSLCertificates(ctx context.Context) ([]string, error)

GetAllSSLCertificates retrieves all SSL certificate names from the storage. Note: This returns only certificate names, not the certificate contents. Use GetSSLCertificateContent to retrieve the actual certificate contents. The returned names are unsanitized (dots restored) for user convenience. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) GetAllSSLCrlFiles

func (c *DataplaneClient) GetAllSSLCrlFiles(ctx context.Context) ([]string, error)

GetAllSSLCrlFiles retrieves all SSL CRL file names from the runtime storage. Note: This returns only CRL file names, not the file contents. Use GetSSLCrlFileContent to retrieve the actual file contents. SSL CRL file storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) GetCRTListEntries

func (c *DataplaneClient) GetCRTListEntries(ctx context.Context, crtListName string) ([]CRTListEntry, error)

GetCRTListEntries retrieves all entries from a specific CRT-list file. CRT-list entries are only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) GetCRTListFileContent

func (c *DataplaneClient) GetCRTListFileContent(ctx context.Context, name string) (string, error)

GetCRTListFileContent retrieves the content of a specific crt-list file by name. The name parameter can use dots (e.g., "example.com.crtlist"), which will be sanitized automatically before calling the API. CRT-list storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) GetCrtLoad

func (c *DataplaneClient) GetCrtLoad(ctx context.Context, crtStoreName, certificate string) (*CrtLoad, error)

GetCrtLoad retrieves a specific crt-load by certificate name from a crt-store. CRT loads are only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) GetGeneralFileContent

func (c *DataplaneClient) GetGeneralFileContent(ctx context.Context, path string) (string, error)

GetGeneralFileContent retrieves the content of a specific general file by path. The API returns the raw file content as application/octet-stream. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) GetLogProfile

func (c *DataplaneClient) GetLogProfile(ctx context.Context, name string) (*LogProfile, error)

GetLogProfile retrieves a specific log profile by name. Log profiles are only available in HAProxy DataPlane API v3.1+.

func (*DataplaneClient) GetMapFileContent

func (c *DataplaneClient) GetMapFileContent(ctx context.Context, name string) (string, error)

GetMapFileContent retrieves the content of a specific map file by name. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) GetRawConfiguration

func (c *DataplaneClient) GetRawConfiguration(ctx context.Context) (string, error)

GetRawConfiguration retrieves the current HAProxy configuration as a string.

This fetches the raw configuration file content from the Dataplane API. The configuration can be parsed using the parser package to get structured data. Works with all HAProxy DataPlane API versions (v3.0+).

Example:

config, err := client.GetRawConfiguration(context.Background())
if err != nil {
    slog.Error("failed to get config", "error", err)
    os.Exit(1)
}
fmt.Printf("Current config:\n%s\n", config)

func (*DataplaneClient) GetReloadStatus

func (c *DataplaneClient) GetReloadStatus(ctx context.Context, reloadID string) (*ReloadInfo, error)

GetReloadStatus retrieves the status of a specific HAProxy reload operation.

This method polls the DataPlane API to check if an async reload has completed. Use this after receiving a 202 response from configuration changes to verify the reload succeeded. Works with all HAProxy DataPlane API versions (v3.0+).

Parameters:

  • reloadID: The reload identifier from the Reload-ID header

Returns:

  • ReloadInfo: Current status and details of the reload
  • error: Error if the API call fails or reload ID not found

Example:

info, err := client.GetReloadStatus(ctx, "abc123")
if err != nil {
    log.Fatal(err)
}
switch info.Status {
case ReloadStatusSucceeded:
    log.Println("Reload completed successfully")
case ReloadStatusFailed:
    log.Printf("Reload failed: %s", info.Response)
case ReloadStatusInProgress:
    log.Println("Reload still in progress")
}

func (*DataplaneClient) GetSSLCaFileContent

func (c *DataplaneClient) GetSSLCaFileContent(ctx context.Context, name string) (string, error)

GetSSLCaFileContent retrieves the content of a specific SSL CA file by name. SSL CA file storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) GetSSLCertificateContent

func (c *DataplaneClient) GetSSLCertificateContent(ctx context.Context, name string) (string, error)

GetSSLCertificateContent retrieves the SHA256 fingerprint for a specific SSL certificate by name.

This function returns the sha256_finger_print field from the HAProxy Data Plane API, which serves as a unique identifier for the certificate content. This allows content-based comparison without needing to download the actual PEM data.

The API provides rich metadata including:

  • sha256_finger_print: SHA-256 hash of certificate content (returned by this function)
  • serial: Certificate serial number
  • issuers: Certificate issuer information
  • subject: Certificate subject information
  • not_after, not_before: Certificate validity period

The name parameter can use dots (e.g., "example.com.pem"), which will be sanitized automatically before calling the API.

Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) GetSSLCrlFileContent

func (c *DataplaneClient) GetSSLCrlFileContent(ctx context.Context, name string) (string, error)

GetSSLCrlFileContent retrieves the content of a specific SSL CRL file by name. SSL CRL file storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) GetTraces

func (c *DataplaneClient) GetTraces(ctx context.Context) (*Traces, error)

GetTraces retrieves the traces configuration. Traces configuration is only available in HAProxy DataPlane API v3.1+.

func (*DataplaneClient) GetVersion

func (c *DataplaneClient) GetVersion(ctx context.Context) (int64, error)

GetVersion retrieves the current configuration version from the Dataplane API.

The version is used for optimistic locking when making configuration changes. This prevents concurrent modifications from conflicting. Works with all HAProxy DataPlane API versions (v3.0+).

Example:

version, err := client.GetVersion(context.Background())
if err != nil {
    slog.Error("failed to get version", "error", err)
    os.Exit(1)
}
fmt.Printf("Current version: %d\n", version)

func (*DataplaneClient) PreferredClient

func (c *DataplaneClient) PreferredClient() interface{}

PreferredClient returns the most appropriate versioned client based on the detected server version. Returns one of: *v30.Client, *v31.Client, *v32.Client.

For most operations, you should use the wrapper methods instead of this. This is provided for operations that aren't wrapped yet.

func (*DataplaneClient) PushRawConfiguration

func (c *DataplaneClient) PushRawConfiguration(ctx context.Context, config string) (string, error)

PushRawConfiguration pushes a new HAProxy configuration to the Dataplane API.

WARNING: This triggers a full HAProxy reload. Use this only as a last resort when fine-grained operations are not possible. Prefer using transactions with specific API endpoints to avoid reloads. Works with all HAProxy DataPlane API versions (v3.0+).

Parameters:

  • config: The complete HAProxy configuration string

Returns:

  • reloadID: The reload identifier from the Reload-ID header (if reload triggered)
  • error: Error if the push fails

Example:

reloadID, err := client.PushRawConfiguration(context.Background(), newConfig)
if err != nil {
    slog.Error("failed to push config", "error", err)
    os.Exit(1)
}
if reloadID != "" {
    slog.Info("HAProxy reloaded", "reload_id", reloadID)
}

func (*DataplaneClient) ReplaceCrtLoad

func (c *DataplaneClient) ReplaceCrtLoad(ctx context.Context, crtStoreName, certificate string, load *CrtLoad, transactionID string) error

ReplaceCrtLoad replaces an existing crt-load entry in a crt-store. CRT loads are only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) ReplaceTraces

func (c *DataplaneClient) ReplaceTraces(ctx context.Context, traces *Traces, transactionID string) error

ReplaceTraces replaces the traces configuration. This is used to update existing traces configuration. Traces configuration is only available in HAProxy DataPlane API v3.1+.

func (*DataplaneClient) UpdateCRTListFile

func (c *DataplaneClient) UpdateCRTListFile(ctx context.Context, name, content string) (string, error)

UpdateCRTListFile updates an existing crt-list file using text/plain content-type. Returns the reload ID if a reload was triggered (empty string if not) and any error. Note: The Dataplane API requires text/plain or application/json for UPDATE operations, while CREATE operations accept multipart/form-data. The name parameter can use dots (e.g., "example.com.crtlist"), which will be sanitized automatically before calling the API. CRT-list storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) UpdateGeneralFile

func (c *DataplaneClient) UpdateGeneralFile(ctx context.Context, path, content string) (string, error)

UpdateGeneralFile updates an existing general file using multipart form-data. Returns the reload ID if a reload was triggered (empty string if not) and any error. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) UpdateLogProfile

func (c *DataplaneClient) UpdateLogProfile(ctx context.Context, name string, profile *LogProfile, transactionID string) error

UpdateLogProfile updates an existing log profile. Log profiles are only available in HAProxy DataPlane API v3.1+.

func (*DataplaneClient) UpdateMapFile

func (c *DataplaneClient) UpdateMapFile(ctx context.Context, name, content string) (string, error)

UpdateMapFile updates an existing map file using text/plain content-type. Returns the reload ID if a reload was triggered (empty string if not) and any error. Note: The Dataplane API requires text/plain or application/json for UPDATE operations, while CREATE operations accept multipart/form-data. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) UpdateSSLCaFile

func (c *DataplaneClient) UpdateSSLCaFile(ctx context.Context, name, content string) (string, error)

UpdateSSLCaFile updates an existing SSL CA file using text/plain content-type. Returns the reload ID if a reload was triggered (empty string if not) and any error. SSL CA file storage is only available in HAProxy DataPlane API v3.2+.

func (*DataplaneClient) UpdateSSLCertificate

func (c *DataplaneClient) UpdateSSLCertificate(ctx context.Context, name, content string) (string, error)

UpdateSSLCertificate updates an existing SSL certificate using text/plain content. Returns the reload ID if a reload was triggered (empty string if not) and any error. The name parameter can use dots (e.g., "example.com.pem"), which will be sanitized automatically before calling the API. Works with all HAProxy DataPlane API versions (v3.0+).

func (*DataplaneClient) UpdateSSLCrlFile

func (c *DataplaneClient) UpdateSSLCrlFile(ctx context.Context, name, content string) (string, error)

UpdateSSLCrlFile updates an existing SSL CRL file using text/plain content-type. Returns the reload ID if a reload was triggered (empty string if not) and any error. SSL CRL file storage is only available in HAProxy DataPlane API v3.2+.

type Endpoint

type Endpoint struct {
	URL      string
	Username string
	Password string
	PodName  string // Kubernetes pod name for observability

	// Cached version info (optional, avoids redundant /v3/info calls if set)
	CachedMajorVersion int
	CachedMinorVersion int
	CachedFullVersion  string
	CachedIsEnterprise bool // True if this is HAProxy Enterprise edition
}

Endpoint represents HAProxy Dataplane API connection information. This is a convenience type alias to avoid circular imports.

func (*Endpoint) HasCachedVersion

func (e *Endpoint) HasCachedVersion() bool

HasCachedVersion returns true if version info has been cached.

type EnterpriseCallFunc

type EnterpriseCallFunc[T any] struct {
	// V32EE is the function to call for HAProxy Enterprise DataPlane API v3.2+
	V32EE func(*v32ee.Client) (T, error)

	// V31EE is the function to call for HAProxy Enterprise DataPlane API v3.1
	V31EE func(*v31ee.Client) (T, error)

	// V30EE is the function to call for HAProxy Enterprise DataPlane API v3.0
	V30EE func(*v30ee.Client) (T, error)
}

EnterpriseCallFunc represents versioned API call functions for enterprise-only endpoints. Unlike CallFunc, this only includes enterprise edition clients since these endpoints are not available in HAProxy Community edition.

Example usage for WAF profile management:

resp, err := c.DispatchEnterpriseOnly(ctx, EnterpriseCallFunc[*http.Response]{
    V32EE: func(c *v32ee.Client) (*http.Response, error) {
        return c.GetWafProfiles(ctx, &v32ee.GetWafProfilesParams{})
    },
    V31EE: func(c *v31ee.Client) (*http.Response, error) {
        return c.GetWafProfiles(ctx, &v31ee.GetWafProfilesParams{})
    },
    V30EE: func(c *v30ee.Client) (*http.Response, error) {
        return c.GetWafProfiles(ctx, &v30ee.GetWafProfilesParams{})
    },
})

type LogProfile

type LogProfile struct {
	Name   string `json:"name"`
	LogTag string `json:"log_tag,omitempty"`
}

LogProfile represents a HAProxy log profile configuration.

type ReloadInfo

type ReloadInfo struct {
	// ID is the unique identifier for this reload operation.
	ID string
	// Status is the current status of the reload.
	Status ReloadStatus
	// Response contains error details if the reload failed.
	Response string
	// ReloadTimestamp is the Unix timestamp when the reload occurred.
	ReloadTimestamp int64
}

ReloadInfo contains information about an HAProxy reload operation.

type ReloadStatus

type ReloadStatus string

ReloadStatus represents the status of an HAProxy reload operation.

const (
	// ReloadStatusInProgress indicates the reload is still being processed.
	ReloadStatusInProgress ReloadStatus = "in_progress"
	// ReloadStatusSucceeded indicates the reload completed successfully.
	ReloadStatusSucceeded ReloadStatus = "succeeded"
	// ReloadStatusFailed indicates the reload failed (HAProxy reverted to previous config).
	ReloadStatusFailed ReloadStatus = "failed"
)

type RetryCondition

type RetryCondition func(err error) bool

RetryCondition determines whether an error should trigger a retry.

func IsConnectionError

func IsConnectionError() RetryCondition

IsConnectionError returns a RetryCondition that retries on transient connection errors.

This condition detects network-level connection failures such as:

  • Connection refused (ECONNREFUSED) - server not yet accepting connections
  • Connection reset (ECONNRESET) - connection lost during communication

These errors are typically transient and resolve within a few seconds as services start up or network conditions stabilize.

This condition does NOT retry on:

  • HTTP errors (4xx, 5xx status codes)
  • Authentication failures
  • Parsing errors
  • Context cancellation

Example:

retryConfig := RetryConfig{
    MaxAttempts: 3,
    RetryIf:     IsConnectionError(),
    Backoff:     BackoffExponential,
    BaseDelay:   100 * time.Millisecond,
}
result, err := WithRetry(ctx, retryConfig, func(attempt int) (*Result, error) {
    return fetchFromAPI(ctx)
})

func IsVersionConflict

func IsVersionConflict() RetryCondition

IsVersionConflict returns a RetryCondition that retries on version conflict errors.

type RetryConfig

type RetryConfig struct {
	// MaxAttempts is the maximum number of attempts (including the first one).
	// Must be >= 1. Default: 1 (no retries).
	MaxAttempts int

	// RetryIf determines whether to retry based on the error.
	// If nil, no retries are performed.
	RetryIf RetryCondition

	// Backoff strategy for delays between retries.
	// Default: BackoffNone
	Backoff BackoffStrategy

	// BaseDelay is the base delay for backoff strategies.
	// - For BackoffLinear: the fixed delay between retries
	// - For BackoffExponential: the initial delay (doubles each retry)
	// Default: 100ms
	BaseDelay time.Duration

	// Logger for retry attempts. If nil, no logging is performed.
	Logger *slog.Logger
}

RetryConfig configures retry behavior for operations.

func DefaultRetryConfig

func DefaultRetryConfig() RetryConfig

DefaultRetryConfig returns a RetryConfig with sensible defaults.

type TraceEntry

type TraceEntry struct {
	Name      string `json:"name,omitempty"`
	Event     string `json:"event,omitempty"`
	Level     string `json:"level,omitempty"`
	Lock      string `json:"lock,omitempty"`
	Sink      string `json:"sink,omitempty"`
	State     string `json:"state,omitempty"`
	Verbosity string `json:"verbosity,omitempty"`
}

TraceEntry represents an individual trace entry configuration.

type Traces

type Traces struct {
	Entries []TraceEntry `json:"entries,omitempty"`
}

Traces represents HAProxy trace events configuration. Unlike log profiles which are named resources, traces is a singleton configuration.

type Transaction

type Transaction struct {
	ID      string
	Version int64
	// contains filtered or unexported fields
}

Transaction represents an HAProxy Dataplane API transaction.

Transactions provide atomic configuration changes - all operations within a transaction are applied together or none are applied. This prevents partial configuration updates that could leave HAProxy in an inconsistent state.

Transaction lifecycle:

  1. Create transaction with current version
  2. Execute operations within transaction (passing transaction ID)
  3. Commit transaction to apply all changes
  4. OR Abort transaction to discard all changes

Thread-safety: Transaction methods are safe for concurrent use. The transaction tracks its state to prevent double commits/aborts.

func (*Transaction) Abort

func (tx *Transaction) Abort(ctx context.Context) error

Abort aborts and discards all changes made within this transaction.

All operations executed with this transaction ID are discarded and the HAProxy configuration remains unchanged. This is useful for cleanup when an error occurs during transaction execution.

This method is idempotent - calling it multiple times is safe but will log a WARNING to indicate a programming error (double abort). Calling Abort() after Commit() is silently ignored (common in defer cleanup).

func (*Transaction) Commit

func (tx *Transaction) Commit(ctx context.Context) (*CommitResult, error)

Commit commits all changes made within this transaction.

After successful commit, all operations executed with this transaction ID are applied atomically to the HAProxy configuration. If commit fails, no changes are applied.

This method is idempotent - calling it multiple times will return the cached result from the first successful commit, but will log a WARNING to indicate a programming error (double commit).

Returns CommitResult containing status code and reload ID (if reload triggered).

func (*Transaction) IsAborted

func (tx *Transaction) IsAborted() bool

IsAborted returns true if the transaction has been aborted.

This can be useful for conditional logic or debugging, but generally you should not need to check this - use proper control flow instead.

func (*Transaction) IsCommitted

func (tx *Transaction) IsCommitted() bool

IsCommitted returns true if the transaction has been committed.

This can be useful for conditional logic or debugging, but generally you should not need to check this - use proper control flow instead.

type TransactionFunc

type TransactionFunc func(ctx context.Context, tx *Transaction) error

TransactionFunc is a function that executes operations within a transaction. The function receives the transaction and should perform all desired operations. If the function returns an error, the transaction will be aborted.

type TransactionResponse

type TransactionResponse struct {
	ID      string `json:"id"`
	Version int    `json:"version"`
}

TransactionResponse represents the response when creating a transaction.

type VersionAdapter

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

VersionAdapter wraps a DataplaneClient to provide automatic version management and 409 conflict retry logic.

When a version conflict occurs (409 response), the adapter automatically: 1. Extracts the new version from the response header 2. Retries the operation with the new version 3. Repeats up to MaxRetries times

This handles the common case of concurrent configuration updates without requiring manual retry logic in application code.

func NewVersionAdapter

func NewVersionAdapter(client *DataplaneClient, maxRetries int) *VersionAdapter

NewVersionAdapter creates a new VersionAdapter with the specified client and retry limit.

Parameters:

  • client: The underlying DataplaneClient
  • maxRetries: Maximum number of retry attempts on 409 conflicts (default: 3)

Example:

client, _ := client.New(client.Config{...})
adapter := client.NewVersionAdapter(client, 3)
err := adapter.ExecuteTransaction(ctx, func(ctx context.Context, tx *Transaction) error {
    // Execute operations within transaction
    return nil
})

func (*VersionAdapter) ExecuteTransaction

func (a *VersionAdapter) ExecuteTransaction(ctx context.Context, fn TransactionFunc) (*CommitResult, error)

ExecuteTransaction executes a transactional operation with automatic 409 retry.

This method: 1. Fetches the current configuration version 2. Creates a transaction with that version 3. Executes the provided function within the transaction 4. Commits the transaction if successful 5. Aborts the transaction if an error occurs 6. Retries on 409 conflicts with the new version

Returns the CommitResult from the successful commit.

Example:

adapter := client.NewVersionAdapter(client, 3)
result, err := adapter.ExecuteTransaction(ctx, func(ctx context.Context, tx *Transaction) error {
    // Create backend
    backend := &models.Backend{Name: "web"}
    _, err := client.Client().CreateBackend(ctx, &CreateBackendParams{
        TransactionID: &tx.ID,
    }, backend)
    return err
})

func (*VersionAdapter) ExecuteTransactionWithVersion

func (a *VersionAdapter) ExecuteTransactionWithVersion(ctx context.Context, version int64, fn TransactionFunc) error

ExecuteTransactionWithVersion executes a transactional operation with a specific version.

This is similar to ExecuteTransaction but allows specifying the version explicitly instead of fetching it. Useful when you already know the current version.

Parameters:

  • ctx: Context for the operation
  • version: The configuration version to use
  • fn: The function to execute within the transaction

Returns an error if the operation fails or max retries are exceeded.

type VersionConflictError

type VersionConflictError struct {
	ExpectedVersion int64
	ActualVersion   string
}

VersionConflictError represents a 409 conflict error with version information.

func (*VersionConflictError) Error

func (e *VersionConflictError) Error() string

type VersionInfo

type VersionInfo struct {
	API struct {
		Version string `json:"version"` // e.g., "v3.2.6 87ad0bcf"
	} `json:"api"`
}

VersionInfo contains detected version information from /v3/info endpoint.

func DetectVersion

func DetectVersion(ctx context.Context, endpoint *Endpoint, _ *slog.Logger) (*VersionInfo, error)

DetectVersion queries the DataPlane API /v3/info endpoint to determine the server version. This function is exported for use by the discovery component to check remote pod versions before admitting them for deployment.

Directories

Path Synopsis
Package enterprise provides client operations for HAProxy Enterprise-only DataPlane API endpoints.
Package enterprise provides client operations for HAProxy Enterprise-only DataPlane API endpoints.

Jump to

Keyboard shortcuts

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