57 lines
1.8 KiB
Go
57 lines
1.8 KiB
Go
package planner
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
const planLifetime = 15 * time.Minute
|
|
|
|
// Plan is an offline intent preview, not an executable authorization. Hash binds
|
|
// its contents but is not a signature. No secret may be added to this structure.
|
|
type Plan struct {
|
|
Intent Intent `json:"intent"`
|
|
ProjectName string `json:"projectName"`
|
|
DataPath string `json:"dataPath"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
ExpiresAt time.Time `json:"expiresAt"`
|
|
Hash string `json:"hash"`
|
|
}
|
|
|
|
func Build(i Intent, now time.Time) (Plan, error) {
|
|
if err := i.Validate(); err != nil {
|
|
return Plan{}, err
|
|
}
|
|
now = now.UTC().Truncate(time.Second)
|
|
p := Plan{Intent: i, ProjectName: "sd-" + i.InstanceID, DataPath: "/var/lib/server-deploy/instances/" + i.InstanceID, CreatedAt: now, ExpiresAt: now.Add(planLifetime)}
|
|
encoded, err := json.Marshal(p)
|
|
if err != nil {
|
|
return Plan{}, errors.New("cannot encode plan")
|
|
}
|
|
sum := sha256.Sum256(encoded)
|
|
p.Hash = "sha256:" + hex.EncodeToString(sum[:])
|
|
return p, nil
|
|
}
|
|
|
|
// Verify checks offline consistency only. Current must be independently inspected
|
|
// under a host lock before any future write operation uses this comparison.
|
|
func (p Plan) Verify(current Intent, now time.Time) error {
|
|
if now.Before(p.CreatedAt) || !now.Before(p.ExpiresAt) {
|
|
return errors.New("plan is not within its validity window")
|
|
}
|
|
if p.Intent != current {
|
|
return errors.New("plan does not match current intent or observed state")
|
|
}
|
|
expected, err := Build(current, p.CreatedAt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if p.ProjectName != expected.ProjectName || p.DataPath != expected.DataPath || !p.CreatedAt.Equal(expected.CreatedAt) || !p.ExpiresAt.Equal(expected.ExpiresAt) || p.Hash != expected.Hash {
|
|
return errors.New("plan integrity check failed")
|
|
}
|
|
return nil
|
|
}
|