Documentation
¶
Overview ¶
Package hostpreflight observes a target host and admits or refuses a local Apply before it mutates anything.
The package answers one question: can this device run what this kit declares it needs? It only reports what it measured — an unobservable fact is stated as unknown, never assumed to pass. Requirements come from the kit's CUE authority, never from constants here, so a kit cannot silently change what it demands of a device.
Index ¶
- Constants
- Variables
- func Executable(resolution Resolution) (bool, string)
- func ValidPolicy(value string) bool
- type Baseline
- type BaselineScope
- type Check
- type DiskFact
- type DockerFacts
- type Facts
- type FileChange
- type ListenerRequirement
- type MemoryFacts
- type Mode
- type ObserveRequest
- type Policy
- type PortFact
- type Record
- type Report
- type Requirements
- type Resolution
- type Status
- type StepRecord
Constants ¶
const SchemaVersion = "stackkit.host-preflight/v1"
SchemaVersion identifies the machine-readable preflight report.
Variables ¶
var ErrResolutionNotExecutable = errors.New("resolution cannot be carried out here")
ErrResolutionNotExecutable is returned for a resolution this host or this process may not carry out. It is a refusal, not a failure: nothing ran.
Functions ¶
func Executable ¶
func Executable(resolution Resolution) (bool, string)
Executable reports whether ApplyResolution could run this resolution now, and why not when it could not. It performs no probing and changes nothing, so a caller can list honest options before asking for consent.
func ValidPolicy ¶
ValidPolicy reports whether value names a supported policy.
Types ¶
type Baseline ¶
type Baseline struct {
SchemaVersion string `json:"schemaVersion"`
Revision string `json:"revision"`
PlanHash string `json:"planHash,omitempty"`
NodeRef string `json:"nodeRef,omitempty"`
ObservedAt time.Time `json:"observedAt"`
ExpiresAt time.Time `json:"expiresAt"`
BootID string `json:"bootId,omitempty"`
NetworkNamespace string `json:"networkNamespace,omitempty"`
Scopes []BaselineScope `json:"scopes"`
}
Baseline is scoped evidence, never host-wide absence or permission to adopt. Its own version lets consumers distinguish older reports without this evidence.
type BaselineScope ¶
type Check ¶
type Check struct {
ID string `json:"id"`
Status Status `json:"status"`
Summary string `json:"summary"`
FailureClass string `json:"failureClass,omitempty"`
Remediation []string `json:"remediation,omitempty"`
}
Check is one performed admission check.
type DiskFact ¶
type DiskFact struct {
Path string `json:"path"`
FreeGB float64 `json:"freeGb"`
Observed bool `json:"observed"`
}
DiskFact is free space at one path that Apply will write to.
type DockerFacts ¶
type DockerFacts struct {
BinaryPresent bool `json:"binaryPresent"`
DaemonReachable bool `json:"daemonReachable"`
PermissionDenied bool `json:"permissionDenied"`
ServerVersion string `json:"serverVersion,omitempty"`
ComposePluginVersion string `json:"composePluginVersion,omitempty"`
StorageDriver string `json:"storageDriver,omitempty"`
CgroupDriver string `json:"cgroupDriver,omitempty"`
CgroupVersion string `json:"cgroupVersion,omitempty"`
MemoryLimitSupported bool `json:"memoryLimitSupported"`
SwapLimitSupported bool `json:"swapLimitSupported"`
Rootless bool `json:"rootless"`
RootDir string `json:"rootDir,omitempty"`
Diagnostic string `json:"diagnostic,omitempty"`
}
DockerFacts is what one `docker info` and one `docker compose version` observation proves about the container runtime.
type Facts ¶
type Facts struct {
ObservedAt time.Time `json:"observedAt"`
OS string `json:"os"`
Distribution string `json:"distribution,omitempty"`
OSVersion string `json:"osVersion,omitempty"`
Architecture string `json:"architecture"`
KernelRelease string `json:"kernelRelease,omitempty"`
Virtualization string `json:"virtualization,omitempty"`
CPUCores int `json:"cpuCores"`
CPUBaseline *bool `json:"cpuBaselineX8664V2,omitempty"`
InitSystem hostconformance.InitSystemFacts `json:"initSystem"`
Memory MemoryFacts `json:"memory"`
CgroupVersion string `json:"cgroupVersion,omitempty"`
MemoryCgroup *bool `json:"memoryCgroupController,omitempty"`
NamespacesOK *bool `json:"namespacesAvailable,omitempty"`
ClockSynced *bool `json:"clockSynchronized,omitempty"`
Docker DockerFacts `json:"docker"`
Disks []DiskFact `json:"disks,omitempty"`
Ports []PortFact `json:"ports,omitempty"`
Baseline *Baseline `json:"baseline,omitempty"`
}
Facts is everything the probe measured about the host.
type FileChange ¶
type FileChange struct {
Path string `json:"path"`
Mode uint32 `json:"mode"`
Merge map[string]any `json:"merge,omitempty"`
Content string `json:"content,omitempty"`
Append string `json:"append,omitempty"`
// AppendUnlessPresent keeps an append idempotent: the line is added only
// when the file does not already mention this substring.
AppendUnlessPresent string `json:"appendUnlessPresent,omitempty"`
Backup bool `json:"backup"`
}
FileChange is one declared file edit. Merge, Content and Append are exclusive: a merge folds keys into an existing JSON document without disturbing the rest, content writes a self-contained drop-in, and append adds a line to a file that must otherwise stay as it is.
type ListenerRequirement ¶
type ListenerRequirement struct {
ID string `json:"id"`
NodeRef string `json:"nodeRef"`
Transport string `json:"transport"`
BindAddress string `json:"bindAddress"`
Port int `json:"port"`
}
ListenerRequirement is a projection of a verified compiler listener. Container target ports, URLs and observed sockets never become requirements.
func ListenersFromPlan ¶
func ListenersFromPlan(plan resolvedplan.ResolvedPlan, nodeRef string) ([]ListenerRequirement, error)
type MemoryFacts ¶
type MemoryFacts struct {
TotalGB float64 `json:"totalGb"`
AvailableGB float64 `json:"availableGb"`
SwapGB float64 `json:"swapGb"`
Observed bool `json:"observed"`
}
MemoryFacts reports host memory in gibibytes. Available and Swap are read separately from Total because a host with enough installed memory can still be unable to start a workload.
type ObserveRequest ¶
type ObserveRequest struct {
WorkspacePath string
PriorRuntimeComposePath string // Verified signed upgrade checkpoint only.
RequiredPorts []int // Legacy diagnostic callers only; Apply uses compiler listeners.
RequiredListeners []ListenerRequirement
PlanHash string
NodeRef string
}
ObserveRequest names the paths and ports this Apply will actually use, so the probe measures the host the rollout touches rather than a generic machine.
type Policy ¶
type Policy string
Policy decides how a report is turned into an admission decision.
const ( // PolicyWarn admits the host unless a check is blocking. It is the default: // an unverifiable fact must not stop a rollout that would otherwise work. PolicyWarn Policy = "warn" // PolicyStrict additionally refuses on warnings and unknown facts. PolicyStrict Policy = "strict" // PolicySkip performs no observation and admits the host. PolicySkip Policy = "skip" )
type PortFact ¶
type PortFact struct {
ListenerID string `json:"listenerId,omitempty"`
Transport string `json:"transport,omitempty"`
BindAddress string `json:"bindAddress,omitempty"`
Namespace string `json:"namespace,omitempty"`
Observed bool `json:"observed"`
Port int `json:"port"`
InUse bool `json:"inUse"`
OwnedByCurrentRuntime bool `json:"ownedByCurrentRuntime,omitempty"`
Detail string `json:"detail,omitempty"`
}
PortFact records whether a port Apply must publish is already bound.
func ObserveListeners ¶
func ObserveListeners(ctx context.Context, workspace string, listeners []ListenerRequirement) []PortFact
ObserveListeners measures only the exact host bindings being changed. A bind probe is immediately closed, not a reservation. Apply must still handle a foreign process winning the race afterwards without stopping that process.
type Record ¶
type Record struct {
ID string `json:"id"`
Title string `json:"title"`
AppliesTo string `json:"appliesTo"`
Status string `json:"status"`
StartedAt time.Time `json:"startedAt"`
FinishedAt time.Time `json:"finishedAt"`
Steps []StepRecord `json:"steps,omitempty"`
Before *Check `json:"before,omitempty"`
After *Check `json:"after,omitempty"`
Reason string `json:"reason,omitempty"`
}
Record is the account of one remediation: what was declared, what ran, and what the host looked like before and after.
The before/after pair is the point. A fix that reports success while the condition it targeted is unchanged is the same lie as a green checkmark over a failed rollout, so the check is re-measured rather than assumed.
func ApplyResolution ¶
func ApplyResolution(ctx context.Context, resolution Resolution) (Record, error)
ApplyResolution carries out one resolution and returns the account of it.
Files are written before commands so a service restart observes the configuration it is being restarted for. A failed step stops the rest: the remaining steps assume the earlier ones took effect, and running them anyway is how a partially changed host becomes an unexplainable one.
type Report ¶
type Report struct {
SchemaVersion string `json:"schemaVersion"`
Policy Policy `json:"policy"`
// Status is the worst measured check; Admitted is the policy decision made
// from it. They stay separate so a strict refusal still shows what it
// refused on, and a warn-policy pass still shows what was degraded.
Status Status `json:"status"`
Admitted bool `json:"admitted"`
KitSlug string `json:"kitSlug,omitempty"`
Requirements Requirements `json:"requirements"`
Facts Facts `json:"facts"`
Checks []Check `json:"checks"`
}
Report is the machine-readable preflight result.
func Evaluate ¶
func Evaluate(facts Facts, requirements Requirements, kitSlug string, policy Policy) Report
Evaluate turns measured facts into an admission decision under one policy.
A check that could not be measured reports unknown rather than pass, so a probe that silently failed can never admit a host it did not verify.
func EvaluateListenerAdmission ¶
func EvaluateListenerAdmission(ctx context.Context, request ObserveRequest, kitSlug string) Report
EvaluateListenerAdmission is the mandatory collision check when optional resource diagnostics are skipped. It cannot turn missing binding evidence into permission to mutate the host.
func (Report) BlockedChecks ¶
BlockedChecks returns the checks that caused a refusal, in report order.
type Requirements ¶
type Requirements struct {
MinCPUCores int `json:"minCpuCores"`
MinRAMGB int `json:"minRamGB"`
MinStorageGB int `json:"minStorageGB"`
RecommendedCPUCores int `json:"recommendedCpuCores"`
RecommendedRAMGB int `json:"recommendedRamGB"`
RecommendedStorageGB int `json:"recommendedStorageGB"`
HeadroomFactor float64 `json:"headroomFactor"`
}
Requirements is the kit-declared host floor, projected from the CUE KitDefinition. Zero values mean the kit declared nothing and the matching check reports unknown rather than inventing a threshold.
func RequirementsFromDefinition ¶
func RequirementsFromDefinition(definition map[string]any) Requirements
RequirementsFromDefinition projects the kit-declared host floor out of a decoded foundation.#KitDefinition. A kit that declares nothing yields a zero Requirements, and the resource checks then report unknown instead of inventing a threshold.
func RequirementsFromDefinitionForTier ¶
func RequirementsFromDefinitionForTier(definition map[string]any, tier string) Requirements
RequirementsFromDefinitionForTier uses computeTierGraphs[tier].hostRequirements when the kit declared that graph, otherwise the kit-level hostRequirements.
func (Requirements) Declared ¶
func (r Requirements) Declared() bool
Declared reports whether the kit supplied a usable floor.
type Resolution ¶
type Resolution struct {
ID string `json:"id"`
Title string `json:"title"`
AppliesTo string `json:"appliesTo"`
Mode Mode `json:"mode"`
Summary string `json:"summary"`
// Files are applied before Commands, so a service restart observes the
// configuration it is being restarted for.
Files []FileChange `json:"files,omitempty"`
Commands [][]string `json:"commands,omitempty"`
// Guidance is what to tell the operator: the whole content of a hint, and
// the caveats of an apply.
Guidance []string `json:"guidance,omitempty"`
RequiresRoot bool `json:"requiresRoot"`
RequiresReboot bool `json:"requiresReboot"`
// Reversible means this host can be put back the way it was: a backup is
// kept, or the change is a drop-in that can simply be removed.
Reversible bool `json:"reversible"`
// AutoInstallerEligible marks a resolution the installer may carry out in
// its unattended mode. It requires Reversible, no reboot and no credential:
// a fix nobody is watching must be one nobody regrets.
AutoInstallerEligible bool `json:"autoInstallerEligible"`
}
Resolution is one fix, bound to the check that justifies it.
func ResolutionByID ¶
func ResolutionByID(id string) (Resolution, bool)
ResolutionByID returns one catalog entry.
func Resolutions ¶
func Resolutions() []Resolution
Resolutions is the closed catalog, ordered by the check they answer.
func ResolutionsForReport ¶
func ResolutionsForReport(report Report) []Resolution
ResolutionsForReport returns the resolutions that answer what this report actually found. A passing check needs no fix, so only warnings, blocks and unknowns bring one forward.
type Status ¶
type Status string
Status is the closed outcome vocabulary for a single check and for a report.
const ( // StatusPass means the check was performed and satisfied. StatusPass Status = "pass" // StatusWarning means the check was performed and the host is usable but // degraded. Apply proceeds. StatusWarning Status = "warning" // StatusBlocked means the check was performed and Apply must not mutate // this host. StatusBlocked Status = "blocked" // StatusUnknown means the fact could not be observed. It never silently // passes: strict policy treats it as blocking. StatusUnknown Status = "unknown" // StatusSkipped means the check does not apply to this host. StatusSkipped Status = "skipped" )