Documentation
¶
Overview ¶
Package api is the wire contract between the controller and the HAPTIC agent (the container that owns an HAProxy pod's file tree and runtime socket). Both ends compile this one package: the controller composes an Apply, the agent executes it verbatim and reports. Additive changes only within a major version; a breaking change is a new major with an overlap.
Index ¶
- Constants
- func SafePayloadValue(s string) bool
- func SafeToken(s string) bool
- type ApplyError
- type ApplyResult
- type ApplyTiming
- type Conflict
- type File
- type FileAt
- type FilePatch
- type HAProxyInfo
- type Inventory
- type KeywordArg
- type Manifest
- type Missing
- type Op
- type OpResult
- type PendingDeletes
- type PlanMoved
- type PlanStored
- type ReloadInfo
- type RollbackInfo
- type State
- type StateRead
- type Token
Constants ¶
const ( PathState = "/v1/state" PathApply = "/v1/apply" PathPlan = "/v1/plan" PathHealthz = "/healthz" PathReadyz = "/readyz" // PathDrain is served on the drain unix socket only: it holds the preStop // hook until the worker sees no new connection for the quiet period. PathDrain = "/drain" )
Paths of the calls plus health.
const ( PartManifest = "manifest" PartPlan = "plan" )
Multipart part names of an Apply request. Every other part is a file whose filename is its manifest path.
const ( FileKindConfig = "config" FileKindMap = "map" FileKindCert = "cert" FileKindCA = "ca" FileKindCRTList = "crtlist" FileKindGeneral = "general" )
File kinds, as the render declares them (renderplan.File.Kind).
const ( ModeAuto = "auto" // run ops when the baseline matches, else reload ModeReload = "reload" // write and reload, ops ignored ModeRevertLKG = "revert_lkg" // restore the last-known-good set and reload )
Apply modes.
const ( FeatureFilePatch = "file_patch" // accepts File.Patch FeatureWorkerFence = "worker_fence" FeatureRuntimeBatches = "runtime_batches" )
Features an agent advertises in its state beyond the op kinds it executes.
const ( OpBackendAdd = "backend_add" // Backend, Profile, Mode, GUID OpBackendPublish = "backend_publish" // Backend OpBackendUnpublish = "backend_unpublish" // Backend OpBackendDel = "backend_del" // Backend OpBackendWaitRemovable = "wait_be_removable" // Backend, TimeoutMs OpServerAdd = "server_add" // Backend, Server, Address, Port, Keywords OpServerEnable = "server_enable" // Backend, Server, Health OpServerDisable = "server_disable" // Backend, Server OpServerSetAddr = "server_set_addr" // Backend, Server, Address, Port OpServerSetWeight = "server_set_weight" // Backend, Server, Weight OpServerSetState = "server_set_state" // Backend, Server, State (ready|maint|drain) OpServerWaitRemovable = "wait_srv_removable" // Backend, Server, TimeoutMs OpShutdownSessions = "shutdown_sessions" // Backend, Server OpServerDel = "server_del" // Backend, Server OpMapAdd = "map_add" // Path, Key, Value (payload form) OpMapSet = "map_set" // Path, Key, Value (line form; value must be line-safe) OpMapDel = "map_del" // Path, Key (every value of the key) OpMapReplace = "map_replace" // Path (versioned atomic replace from the file part) OpCertSet = "cert_set" // Path (set + commit from the file part) OpCertNew = "cert_new" // Path (new + set + commit) OpCASet = "ca_set" // Path OpCANew = "ca_new" // Path OpCRTListAdd = "crtlist_add" // Path (list), Cert (the crt-list line token: bare under crt-base), Options, SNIFilters (payload form) OpCRTListDel = "crtlist_del" // Path (list), Cert (as in crtlist_add) )
Op kinds the agent executes. Unknown kinds are refused and the apply falls back to a reload (fail closed).
const ( ResultRuntime = "runtime" ResultFileOnly = "file_only" ResultReload = "reload" ResultScheduled = "scheduled" ResultNoop = "noop" ResultRejected = "rejected" )
Apply outcome modes.
const ( MaxApplyBodyBytes = 64 << 20 // one apply request MaxFiles = 4096 MaxPlanBlobBytes = 8 << 20 MaxPathBytes = 255 MaxOpsPerApply = 1000 // operations per execution batch, including in-place ops in the first MaxOpBatches = 8 MaxCommandLineBytes = 12 << 10 // ';'-joined CLI line; tune.bufsize is 16 KiB all-or-nothing MaxPayloadBytes = 12 << 10 // one payload command; HAProxy 3.0 caps payloads at tune.bufsize MaxWaitBudgetMs = 30000 // total wait …-removable per apply MaxReloadMs = 60000 MaxReloadIntervalMs = 60000 // --reload-interval-min, refused above it MaxPendingServerDeletes = 1000 // per pod, beyond → forced reload MaxPendingBackendDeletes = 100 ConnectRetries = 3 ConnectRetryBackoffMs = 100 MaxSections = 65536 MaxMapDelRepeat = 64 // 3.4 deletes one duplicate per `del map` call MaxDeferredAttempts = 5 // per queued delete before the agent gives up on it MaxInventoryEntries = 4096 MaxMapEntries = 1 << 18 // one map's entries; the design target is one per route )
Limits, asserted at both ends. Every loop the agent runs is bounded by one of these or by a counted collection.
const ExactIdentityVersion = 1
ExactIdentityVersion is the role-proof protocol every Manifest must declare.
const Version = 1
Version is the API major version an agent reports and a client compares.
Variables ¶
This section is empty.
Functions ¶
func SafePayloadValue ¶
SafePayloadValue reports whether s can travel inside a payload block, where only the line framing is significant.
func SafeToken ¶
SafeToken reports whether s can travel as one word of a runtime command — a name, a path, a map key, a keyword or a keyword argument. Both ends compile it: the controller never composes an op whose tokens fail it, and the agent refuses to execute one, so the two cannot disagree on what is sendable. Whitespace is rejected because HAProxy splits the line on it, which would silently turn one word into two.
Types ¶
type ApplyError ¶
ApplyError names the stage that failed and HAProxy's own words.
type ApplyResult ¶
type ApplyResult struct {
PlanID string `json:"plan_id"`
OK bool `json:"ok"`
Mode string `json:"mode"`
AppliedPlanID string `json:"applied_plan_id"`
AppliedPlanProof string `json:"applied_plan_proof,omitempty"`
RunningPlanID string `json:"running_plan_id"`
RunningPlanProof string `json:"running_plan_proof,omitempty"`
WorkerOpsPlanID string `json:"worker_ops_plan_id"`
WorkerOpsPlanProof string `json:"worker_ops_plan_proof,omitempty"`
AppliedToken Token `json:"applied_token"`
LKGPlanID string `json:"lkg_plan_id"`
LKGPlanProof string `json:"lkg_plan_proof,omitempty"`
OpResults []OpResult `json:"op_results,omitempty"`
Reload *ReloadInfo `json:"reload,omitempty"`
Rollback *RollbackInfo `json:"rollback,omitempty"`
Error *ApplyError `json:"error,omitempty"`
HAProxy HAProxyInfo `json:"haproxy"`
Inventory *Inventory `json:"runtime_inventory,omitempty"` // present when its generation advanced
At string `json:"at"` // RFC 3339
Timing ApplyTiming `json:"timing"`
}
ApplyResult is the response of POST /v1/apply: the ACK or NACK.
type ApplyTiming ¶
type ApplyTiming struct {
// StageMs is receiving and verifying the file parts.
StageMs int64 `json:"stage_ms"`
// AdmitMs is the checks between staging and the write: held files, the
// known-bad cache, baseline promotion and the role proofs.
AdmitMs int64 `json:"admit_ms"`
// WriteMs is backing up and installing the staged parts.
WriteMs int64 `json:"write_ms"`
// OpsMs is the runtime commands, or the reload when one ran instead.
OpsMs int64 `json:"ops_ms"`
// FinishMs is recording the outcome once the ops ran.
FinishMs int64 `json:"finish_ms"`
// TotalMs is from the manifest's arrival to the result.
TotalMs int64 `json:"total_ms"`
}
ApplyTiming is where an apply spent its time on the agent, for the deploy latency budget: the controller reports the slowest pod's split with every deployment.
type Conflict ¶
type Conflict struct {
AppliedPlanID string `json:"applied_plan_id"`
AppliedPlanProof string `json:"applied_plan_proof,omitempty"`
AppliedToken Token `json:"applied_token"`
RunningPlanID string `json:"running_plan_id"`
RunningPlanProof string `json:"running_plan_proof,omitempty"`
WorkerOpsPlanID string `json:"worker_ops_plan_id"`
WorkerOpsPlanProof string `json:"worker_ops_plan_proof,omitempty"`
LKGPlanID string `json:"lkg_plan_id"`
LKGPlanProof string `json:"lkg_plan_proof,omitempty"`
Reason string `json:"reason"` // prev_mismatch|stale_epoch|unknown_baseline|worker_ops_mismatch
}
Conflict is the body of a 409 baseline mismatch: the agent's actual baseline, so the caller can re-diff.
type File ¶
type File struct {
Path string `json:"path"` // relative to the agent's base dir, no "..", no leading "/"
Digest string `json:"digest"`
Proof string `json:"proof,omitempty"`
Size int64 `json:"size"`
Kind string `json:"kind"`
ReloadOnChange bool `json:"reload_on_change"`
Patch *FilePatch `json:"patch,omitempty"`
}
File is one desired file. Proof is an opaque controller-local equality witness; an absent or unknown proof requires the multipart bytes.
type FileAt ¶
type FileAt struct {
Digest string `json:"digest"`
Proof string `json:"proof,omitempty"`
Size int64 `json:"size"`
}
FileAt is a file the agent holds.
type FilePatch ¶
type FilePatch struct {
BaseDigest string `json:"base_digest"`
BaseSize int64 `json:"base_size"`
Offset int64 `json:"offset"`
Length int64 `json:"length"`
}
FilePatch delivers a file as a splice into the one the agent holds: the part named by the file's path carries Size-BaseSize+Length bytes that replace [Offset, Offset+Length) of the held file, which must be at BaseDigest and BaseSize. The result is verified against Digest and Size exactly as a whole part is. An agent that does not hold the base answers the path as missing, and the controller sends the whole file. Only an agent whose state lists FeatureFilePatch receives one.
type HAProxyInfo ¶
type HAProxyInfo struct {
Version string `json:"version"`
FullVersion string `json:"full_version"`
WorkerPID int `json:"worker_pid"`
WorkerStartTimeUnixMicros int64 `json:"worker_start_time_unix_micros"`
}
HAProxyInfo is what the agent learned from the worker (`show info`).
func (HAProxyInfo) HasWorkerIdentity ¶
func (i HAProxyInfo) HasWorkerIdentity() bool
func (HAProxyInfo) SameWorker ¶
func (i HAProxyInfo) SameWorker(other HAProxyInfo) bool
SameWorker distinguishes process IDs reused after a container restart.
type Inventory ¶
type Inventory struct {
Generation uint64 `json:"generation"`
Maps []string `json:"maps"`
Certs []string `json:"certs"`
CAFiles []string `json:"ca_files"`
CRLFiles []string `json:"crl_files"`
CRTLists []string `json:"crt_lists"`
}
Inventory is what the running worker has loaded, refreshed after reloads and on a worker identity change.
type KeywordArg ¶
KeywordArg is one HAProxy keyword with its arguments, as data.
type Manifest ¶
type Manifest struct {
IdentityVersion int `json:"identity_version"`
PlanID string `json:"plan_id"`
// PlanProof names the refused agent role only in ModeRevertLKG.
PlanProof string `json:"plan_proof,omitempty"`
PlanSchemaVersion int `json:"plan_schema_version"`
Token Token `json:"token"`
// ExpectedPrevPlanID and ExpectedPrevToken are the baseline the ops were
// composed against; a mismatch is a 409, never a write.
ExpectedPrevPlanID string `json:"expected_prev_plan_id"`
ExpectedPrevPlanProof string `json:"expected_prev_plan_proof,omitempty"`
ExpectedPrevToken Token `json:"expected_prev_token"`
// Automatic applies and executable InPlaceOps require this exact worker baseline.
ExpectedWorkerOpsPlanID string `json:"expected_worker_ops_plan_id,omitempty"`
ExpectedWorkerOpsPlanProof string `json:"expected_worker_ops_plan_proof,omitempty"`
// WorkerOpsPlanID names the partial plan after InPlaceOps, not the desired render.
WorkerOpsPlanID string `json:"worker_ops_plan_id,omitempty"`
WorkerOpsPlanProof string `json:"worker_ops_plan_proof,omitempty"`
// ValidatedPlanID is the newest plan the controller's haproxy -c passed;
// the agent promotes its rollback baseline when it equals the applied plan.
ValidatedPlanID string `json:"validated_plan_id,omitempty"`
ValidatedPlanProof string `json:"validated_plan_proof,omitempty"`
Files []File `json:"files"`
Ops []Op `json:"ops,omitempty"`
OpBatches [][]Op `json:"op_batches,omitempty"`
InPlaceOps []Op `json:"in_place_ops,omitempty"`
Mode string `json:"mode"`
}
Manifest is the JSON part of an Apply: the complete desired file set, the ops composed for this pod, and the fencing state.
func (*Manifest) RuntimeOps ¶
RuntimeOps returns all runtime operations in execution order.
func (*Manifest) ValidateOpBatches ¶
ValidateOpBatches enforces the per-batch and complete-transaction budgets.
type Missing ¶
type Missing struct {
Missing []string `json:"missing"`
}
Missing is the body of a 409 when file parts are missing: resend these.
type Op ¶
type Op struct {
Kind string `json:"kind"`
Backend string `json:"backend,omitempty"`
Server string `json:"server,omitempty"`
Profile string `json:"profile,omitempty"`
Mode string `json:"mode,omitempty"`
GUID string `json:"guid,omitempty"`
Address string `json:"address,omitempty"`
Port int `json:"port,omitempty"`
Weight *int `json:"weight,omitempty"`
State string `json:"state,omitempty"`
Health bool `json:"health,omitempty"`
Keywords []KeywordArg `json:"keywords,omitempty"`
TimeoutMs int `json:"timeout_ms,omitempty"`
Path string `json:"path,omitempty"`
Key string `json:"key,omitempty"`
Value string `json:"value,omitempty"`
Cert string `json:"cert,omitempty"`
Options []KeywordArg `json:"options,omitempty"`
SNIFilters []string `json:"sni_filters,omitempty"`
}
Op is one typed runtime command. Fields not used by a kind are empty. Every string is validated by the agent against its keyword grammar; tokens never carry ';', a newline, "<<" or a backslash.
type OpResult ¶
type OpResult struct {
Kind string `json:"kind"`
OK bool `json:"ok"`
Output string `json:"output,omitempty"`
}
OpResult is one executed op's verdict.
type PendingDeletes ¶
type PendingDeletes struct {
Servers []string `json:"servers"` // backend/server
Backends []string `json:"backends"` // unpublished, awaiting del
}
PendingDeletes are deferred runtime deletes still to complete.
type PlanMoved ¶
type PlanMoved struct {
AppliedPlanID string `json:"applied_plan_id"`
AppliedPlanProof string `json:"applied_plan_proof,omitempty"`
}
PlanMoved is the 409 to a PUT of a plan blob for a plan the pod no longer has applied; the blob was dropped.
type PlanStored ¶
PlanStored answers a PUT of the plan blob: the plan the blob now describes, which is the pod's applied plan at that moment.
type ReloadInfo ¶
type ReloadInfo struct {
Performed bool `json:"performed"`
OK bool `json:"ok"`
WorkerPID int `json:"worker_pid,omitempty"`
TookMs int64 `json:"took_ms,omitempty"`
Output string `json:"output,omitempty"`
ScheduledAt string `json:"scheduled_at,omitempty"`
}
ReloadInfo describes the reload an apply performed or scheduled.
type RollbackInfo ¶
RollbackInfo describes an abort's restore.
type State ¶
type State struct {
APIVersion int `json:"api_version"`
AgentVersion string `json:"agent_version"`
PlanSchemaVersion int `json:"plan_schema_version"`
AgentOps []string `json:"agent_ops"` // op kinds this agent executes
Features []string `json:"features,omitempty"` // Feature* this agent accepts
HAProxy HAProxyInfo `json:"haproxy"`
Generation uint64 `json:"generation"`
AppliedPlanID string `json:"applied_plan_id"`
AppliedPlanProof string `json:"applied_plan_proof,omitempty"`
RunningPlanID string `json:"running_plan_id"`
RunningPlanProof string `json:"running_plan_proof,omitempty"`
WorkerOpsPlanID string `json:"worker_ops_plan_id"`
WorkerOpsPlanProof string `json:"worker_ops_plan_proof,omitempty"`
AppliedToken Token `json:"applied_token"`
LKGPlanID string `json:"lkg_plan_id"`
LKGPlanProof string `json:"lkg_plan_proof,omitempty"`
AppliedPlan []byte `json:"applied_plan,omitempty"` // opaque, what the controller sent
// AppliedPlanStored reports that the agent holds the blob for the applied
// plan; a state read with plan=0 leaves the blob itself out.
AppliedPlanStored bool `json:"applied_plan_stored"`
Files map[string]FileAt `json:"files"`
Inventory Inventory `json:"runtime_inventory"`
ReloadPendingAt string `json:"reload_pending_at,omitempty"` // RFC 3339
PendingDeletes PendingDeletes `json:"pending_deletes"`
LastApply *ApplyResult `json:"last_apply,omitempty"`
// InvariantViolation is the name of the invariant that failed most recently,
// empty if none has. The agent never takes itself down over a violation, so
// this is the only place a silent one surfaces.
InvariantViolation string `json:"invariant_violation,omitempty"`
}
State is the response of GET /v1/state.
func (*State) HoldsAppliedPlan ¶
HoldsAppliedPlan reports whether the agent stores the blob for its applied plan, whether or not the read carried the blob itself.