Documentation
¶
Overview ¶
Package installation evaluates product-declared installation requirements. It performs no discovery or mutation. Products own trusted observations, adapter authority, durable progress, serialization, effects and verification.
SPDX-License-Identifier: MPL-2.0
Index ¶
- Variables
- func Version() string
- type Action
- type Attempt
- type AttemptState
- type Binding
- type Observation
- type ObservedState
- type Outcome
- type OutcomeState
- type Plan
- func (p *Plan) Actions() []Action
- func (p *Plan) Binding() Binding
- func (p *Plan) ID() string
- func (p *Plan) MarshalJSON() ([]byte, error)
- func (p *Plan) Outcomes() []Outcome
- func (p *Plan) Recheck(current Profile, observations []Observation, supports []Support, ...) (*Plan, error)
- func (p *Plan) Status() PlanStatus
- type PlanStatus
- type Profile
- type Progress
- type Reason
- type Requirement
- type Support
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrInput = errors.New("invalid installation input") ErrStale = errors.New("installation binding or plan changed") )
Functions ¶
Types ¶
type Attempt ¶
type Attempt struct {
RequirementID string
State AttemptState
}
type AttemptState ¶
type AttemptState string
const ( Attempted AttemptState = "attempted" Verified AttemptState = "verified" )
type Binding ¶
type Binding struct {
ProfileID string
OperationID string
CandidateSHA256 string
PreconditionsSHA256 string
}
Binding identifies the exact operation, candidate and approved preconditions. Digests identify trusted non-secret artifact/configuration/target metadata. They do not establish publisher authenticity or grant execution authority.
type Observation ¶
type Observation struct {
RequirementID string
State ObservedState
}
type ObservedState ¶
type ObservedState string
const ( Satisfied ObservedState = "satisfied" Missing ObservedState = "missing" Unknown ObservedState = "unknown" Unsupported ObservedState = "unsupported" // NotApplied is stronger than absence: the adapter has fenced prior work and // definitively established that its effect did not occur and cannot arrive. NotApplied ObservedState = "not-applied" )
type Outcome ¶
type Outcome struct {
RequirementID string
Required bool
State OutcomeState
Reason Reason
AdapterID string
}
type OutcomeState ¶
type OutcomeState string
const ( Available OutcomeState = "available" Provisionable OutcomeState = "provisionable" OperatorRequired OutcomeState = "operator-required" Incompatible OutcomeState = "unsupported" )
type Plan ¶
type Plan struct {
// contains filtered or unexported fields
}
Plan is an immutable snapshot. Its ID binds the profile, candidate, operation, preconditions, requirement order and selected adapter/authority declarations. Observations/progress do not change its ID: those must advance during the same operation. The ID is a consistency precondition, never an authorization token.
func Assess ¶
func Assess(profile Profile, observations []Observation, supports []Support, progress Progress) (*Plan, error)
Assess validates complete finite input, classifies required outcomes and makes a deterministic plan. Every declared requirement needs exactly one observation. Missing, duplicate, unknown and malformed entries fail closed. No host tools, root/panel/CPU detection, resource acquisition or effects occur here.
func (*Plan) Actions ¶
Actions returns only proposals allowed by an otherwise satisfied preflight. A blocked/unsupported/uncertain plan offers no partial executable action set.
func (*Plan) MarshalJSON ¶
MarshalJSON exports only stable safe metadata, never profile secret values or raw inspection/provider errors. Caller-supplied identifiers must be non-secret.
func (*Plan) Recheck ¶
func (p *Plan) Recheck(current Profile, observations []Observation, supports []Support, progress Progress) (*Plan, error)
Recheck must receive current product binding and newly inspected evidence, adapter authority and durable progress before effects. It rejects a stale candidate/configuration/operation or changed adapter declarations. Callers use the returned fresh plan, never a cached earlier action list.
Example ¶
This example is a single serialized product operation against a disposable private marker. A real adapter must supply its own cross-process lock/fence, durable atomic progress storage, artifact trust and target-specific checks.
package main
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"github.com/AChWorks/achrix/installation"
)
func main() {
root, err := os.MkdirTemp("", "achrix-installation-example-")
if err != nil {
panic(err)
}
defer func() { _ = os.RemoveAll(root) }()
resource := filepath.Join(root, "private-marker")
journal := filepath.Join(root, "progress.json")
snapshot := func(s string) string { d := sha256.Sum256([]byte(s)); return hex.EncodeToString(d[:]) }
profile := installation.Profile{
Binding: installation.Binding{
ProfileID: "private-marker", OperationID: "install-42",
CandidateSHA256: snapshot("reviewed-example-artifact"),
PreconditionsSHA256: snapshot("empty-private-marker-target:" + root),
},
Requirements: []installation.Requirement{{ID: "private.marker", Required: true}},
}
support := []installation.Support{{RequirementID: "private.marker", AdapterID: "product-marker", Authorized: true}}
inspect := func() installation.Observation {
body, readErr := os.ReadFile(resource)
state := installation.Unknown
switch {
case errors.Is(readErr, os.ErrNotExist):
state = installation.Missing
case readErr == nil && string(body) == "install-42":
state = installation.Satisfied
case readErr == nil:
state = installation.Unsupported // Preserve a resource this operation does not own.
}
return installation.Observation{RequirementID: "private.marker", State: state}
}
plan, err := installation.Assess(profile, []installation.Observation{inspect()}, support, installation.Progress{})
if err != nil {
panic(err)
}
fmt.Println(plan.Status())
// The product durably records the attempt before its own effect. The private
// journal carries only operation metadata, never credentials or raw errors.
progress := installation.Progress{PlanID: plan.ID(), Attempts: []installation.Attempt{{RequirementID: "private.marker", State: installation.Attempted}}}
data, err := json.Marshal(progress)
if err != nil {
panic(err)
}
if err = os.WriteFile(journal, data, 0o600); err != nil {
panic(err)
}
// For this example, flush the private journal before attempting the effect.
file, err := os.OpenFile(journal, os.O_RDWR, 0)
if err != nil {
panic(err)
}
if err = file.Sync(); err != nil {
panic(err)
}
if err = file.Close(); err != nil {
panic(err)
}
// Persist creation of the journal entry before crossing the effect boundary.
directory, err := os.Open(root)
if err != nil {
panic(err)
}
if err = directory.Sync(); err != nil {
panic(err)
}
if err = directory.Close(); err != nil {
panic(err)
}
if len(plan.Actions()) != 1 {
panic("preflight did not allow a proposal")
}
file, err = os.OpenFile(resource, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o600)
if err != nil {
panic(err)
}
if _, err = file.WriteString("install-42"); err != nil {
panic(err)
}
if err = file.Sync(); err != nil {
panic(err)
}
if err = file.Close(); err != nil {
panic(err)
}
// Simulate losing the effect acknowledgement and returning to the operation
// with progress loaded from disk. Unknown evidence cannot cause a replay.
data, err = os.ReadFile(journal)
if err != nil {
panic(err)
}
progress = installation.Progress{}
if err = json.Unmarshal(data, &progress); err != nil {
panic(err)
}
uncertain := []installation.Observation{{RequirementID: "private.marker", State: installation.Unknown}}
resumed, err := plan.Recheck(profile, uncertain, support, progress)
if err != nil {
panic(err)
}
fmt.Println(resumed.Status())
if len(resumed.Actions()) != 0 {
panic("unknown acknowledgement permitted a replay")
}
// Exact product-owned verification resolves the existing effect. No second
// create is performed; a mere not-found read would still require a fence.
verified, err := plan.Recheck(profile, []installation.Observation{inspect()}, support, progress)
if err != nil {
panic(err)
}
fmt.Println(verified.Status())
fmt.Println("created once")
}
Output: changes-required reconciliation-required ready created once
func (*Plan) Status ¶
func (p *Plan) Status() PlanStatus
type PlanStatus ¶
type PlanStatus string
const ( Ready PlanStatus = "ready" ChangesRequired PlanStatus = "changes-required" OperatorActionRequired PlanStatus = "operator-action-required" ReconciliationRequired PlanStatus = "reconciliation-required" UnsupportedPlan PlanStatus = "unsupported" )
type Profile ¶
type Profile struct {
Binding Binding
Requirements []Requirement
}
type Progress ¶
Progress is product-owned durable evidence bound to the original plan. Persist Attempted before a mutation, then Verified only after actual postflight. A new empty Progress is valid only when no operation has previously started.
type Reason ¶
type Reason string
const ( ReasonVerified Reason = "verified" ReasonMissing Reason = "missing" ReasonUnverified Reason = "unverified" ReasonUnsupported Reason = "unsupported" ReasonAdapterRequired Reason = "adapter-required" ReasonAuthorityRequired Reason = "authority-required" ReasonReconcileRequired Reason = "reconciliation-required" ReasonVerificationLost Reason = "verification-lost" ReasonDefinitelyNotApplied Reason = "definitely-not-applied" )
type Requirement ¶
Requirement order is the product's intended action order. Context entries (Required=false) never create actions or block required outcomes.