cli

package
v0.8.1 Latest Latest
Warning

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

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

Documentation

Overview

Package cli turns the agent's typed ops into HAProxy runtime commands and runs them against the worker stats socket. It knows command strings and success strings; it makes no decisions and parses no configuration.

Index

Constants

View Source
const PayloadTerminator = "HAPTIC"

PayloadTerminator ends a payload block. HAProxy's default terminator is an empty line, which a blank line inside a certificate or a map would trip; a custom pattern (max 7 characters) is what makes the content irrelevant to the framing.

View Source
const VersionPlaceholder = "\x01ver\x01"

VersionPlaceholder stands in for the map version `prepare map` allocates. The executor substitutes it in the rest of the program once the allocating command answered.

Variables

View Source
var (
	// ErrNameCollision means a backend of that name already exists. The shape
	// behind the name is unreadable at runtime, so the apply stops and reloads.
	ErrNameCollision = errors.New("backend name already in use")
	// ErrWaitExpired means the object still has attached connections.
	ErrWaitExpired = errors.New("wait delay expired")
	// ErrRejected is HAProxy refusing a command.
	ErrRejected = errors.New("haproxy rejected the command")
	// ErrUnreadableResponse means the batch answered something the matcher
	// cannot attribute. The baseline is unknown from here on.
	ErrUnreadableResponse = errors.New("unreadable runtime response")
)

Typed outcomes the caller acts on rather than just reports.

View Source
var ErrDeferralOverflow = errors.New("deferred delete queue is full")

ErrDeferralOverflow means the queue is at its cap: the caller must reload instead of deferring more deletes.

View Source
var ErrTooManyEntries = errors.New("runtime listing exceeds the entry limit")

ErrTooManyEntries means a listing is above the agent's limit, which is a refusal to read it, not a statement about what the worker holds.

View Source
var ErrUnknownOp = errors.New("unknown op kind")

ErrUnknownOp is what an op kind this agent does not execute wraps. The server refuses the whole batch and falls back to a reload.

View Source
var ErrUnsafeToken = errors.New("unsafe runtime token")

ErrUnsafeToken is what every negative-space rejection wraps. A token that reaches HAProxy unchecked can split the command line and run a second command, so the check is a refusal, never a sanitisation.

View Source
var ErrWorkerGone = errors.New("the deferred delete's HAProxy worker has changed")

ErrWorkerGone means a deferred attempt no longer addresses its original worker.

Functions

func Kinds

func Kinds() []string

Kinds lists the op kinds this agent executes, for /v1/state.agent_ops.

Types

type Client

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

Client is the agent's runtime plumbing.

func New

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

New wires both sockets without probing them: readiness is the server's job, and the agent must come up before HAProxy answers.

func (*Client) Execute

func (c *Client) Execute(programs []Program) ([]api.OpResult, error)

Execute runs the programs of one apply in the order the controller composed them and returns one result per program. It stops at the first failure: the caller's answer to a rejected op is to reload the desired set, which makes the commands after the failure irrelevant.

func (*Client) FrontendConnections

func (c *Client) FrontendConnections(ignore map[string]bool) (uint64, error)

FrontendConnections sums the cumulative accepted-connection counter of every frontend except the ignored ones. It is the drain's signal: the sum stops moving once nothing routes new connections to the worker any more.

func (*Client) Info

func (c *Client) Info() (api.HAProxyInfo, error)

Info reads the worker's process ID and microsecond-resolution start time.

func (*Client) Inventory

func (c *Client) Inventory(generation uint64) (api.Inventory, error)

Inventory reads what the running worker has loaded. The controller needs it to know whether a certificate or map it wants to touch at runtime exists at all; a path that is not in here is structural.

func (*Client) MapEntries

func (c *Client) MapEntries(path string) (map[string][]string, error)

MapEntries reads back one map file's runtime contents, keyed by map key. Duplicate keys keep every value, in insertion order, because that is what decides which one a lookup wins.

func (*Client) Raw

func (c *Client) Raw(command string) (string, error)

Raw runs one command on the worker socket with the experimental prefix.

func (*Client) Reload

func (c *Client) Reload() (string, error)

Reload asks the master to re-exec. The returned string is HAProxy's startup log, which is what a NACK carries back to the operator.

func (*Client) ServerNames

func (c *Client) ServerNames(backend string) (map[string]struct{}, error)

ServerNames reads the servers the running worker holds for a backend.

func (*Client) ShowProc

func (c *Client) ShowProc() (string, error)

ShowProc asks the master process for its worker table. It is the master socket's readiness gate and nothing else.

type Command

type Command struct {
	// Text is the command line. It never contains a newline, so a batch can
	// join commands with ';'.
	Text string
	// Payload is the block a "<<" command carries. A command with a payload is
	// always the last one on its line.
	Payload string
	// Expect is the substring HAProxy answers on success, matched
	// case-insensitively. Empty means success is any response that is not an
	// error.
	Expect string
	// Capture marks the command whose response carries the map version the rest
	// of the program substitutes into VersionPlaceholder.
	Capture bool
	// Repeat marks a command that has to run until HAProxy refuses it: `del map`
	// removes one duplicate per call on 3.4 and all of them on 3.0.
	Repeat bool
	// Optional marks the session prefix, whose response is version-dependent
	// and must never be mistaken for an op's verdict.
	Optional bool
}

Command is one HAProxy runtime command with the framing and the verdict rule that belong to it.

type CommandResult

type CommandResult struct {
	Output string
	Err    error
}

CommandResult is one command's verdict, with HAProxy's own words.

type Config

type Config struct {
	WorkerSocket string
	MasterSocket string
	Logger       *slog.Logger
}

Config names the worker and master sockets the agent talks to.

type Content

type Content func(path string) ([]byte, error)

Content supplies the on-disk bytes of a manifest path for the ops that push a whole file through the socket.

type Deferrals

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

Deferrals drains the delete tail of an apply off the apply path: `wait …-removable` blocks for as long as a client keeps a connection, and no apply may pay that.

func NewDeferrals

func NewDeferrals(client *Client, logger *slog.Logger, observer Observer) *Deferrals

NewDeferrals builds the queue. observer may be nil.

func (*Deferrals) Check

func (d *Deferrals) Check(servers []ServerRef, backends []string) error

Check validates a batch without making it visible to the drainer.

func (*Deferrals) Enqueue

func (d *Deferrals) Enqueue(worker api.HAProxyInfo, servers []ServerRef, backends []string) error

Enqueue commits deletes only after their inline traffic-stopping ops succeed.

func (*Deferrals) Pending

func (d *Deferrals) Pending() api.PendingDeletes

Pending reports what is still to happen, for /v1/state: the queue plus the delete the drain is inside, which is outstanding until it reports back.

func (*Deferrals) SetWorker

func (d *Deferrals) SetWorker(worker api.HAProxyInfo)

SetWorker retires queued work; in-flight commands stay on their old connection.

func (*Deferrals) Start

func (d *Deferrals) Start(ctx context.Context) error

Start drains the queue until the context ends. The ticker is what picks up requeued deletes: a server whose client will not let go is retried on the next tick, never in a spin.

func (*Deferrals) Wake

func (d *Deferrals) Wake()

Wake drains committed deletes without waiting for the next tick.

type Observer

type Observer interface {
	DeferredDeleteDone(kind string)
	DeferredDeleteDeferred(kind string)
	// DeferredDeleteAbandoned is a delete the agent gave up on: the object
	// stays until the next reload, which is a distinct fact from "retrying".
	DeferredDeleteAbandoned(kind string)
	DeferredDeleteSuperseded(kind string)
}

Observer receives deferred-delete outcomes so the server can export them.

type Program

type Program struct {
	Kind     string
	Commands []Command
	Abort    []Command
}

Program is the command sequence of one typed op, plus the cleanup that has to run when one of its commands fails. An open `set ssl cert` transaction wedges that certificate until a reload, so the abort is not optional.

func Compile

func Compile(op *api.Op, content Content) (Program, error)

Compile turns one typed op into its program. Every string that reaches HAProxy passes the negative-space check first.

type ServerRef

type ServerRef struct {
	Backend string `json:"backend"`
	Server  string `json:"server"`
}

ServerRef names one server of one backend.

func Split

func Split(ops []api.Op) (inline []api.Op, servers []ServerRef, backends []string)

Split separates the ops an apply runs inline from the deletes that block on `wait`. The controller composes the full A4 sequence; the agent runs the traffic-stopping half now and the removal half later.

func (ServerRef) String

func (r ServerRef) String() string

String renders the reference the way HAProxy addresses it.

Jump to

Keyboard shortcuts

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