metis/pkg/plan/plan.go

60 lines
1.8 KiB
Go
Raw Normal View History

package plan
import (
"fmt"
"path/filepath"
"time"
"metis/pkg/inventory"
)
// Action describes a step in the burn process.
type Action struct {
Type string `json:"type"`
Detail string `json:"detail"`
Command string `json:"command,omitempty"`
}
// Plan describes the overall burn for a node.
type Plan struct {
Node string `json:"node"`
Device string `json:"device"`
Image string `json:"image"`
Class string `json:"class"`
Actions []Action `json:"actions"`
}
// Build constructs a plan without executing it.
func Build(inv *inventory.Inventory, nodeName, device, cacheDir string) (*Plan, error) {
node, class, err := inv.FindNode(nodeName)
if err != nil {
return nil, err
}
if device == "" {
device = "/dev/sdX" // placeholder
}
cacheImage := filepath.Join(cacheDir, filepath.Base(class.Image))
actions := []Action{
{Type: "fetch", Detail: fmt.Sprintf("Download %s to %s", class.Image, cacheImage)},
}
if class.Checksum != "" {
actions = append(actions, Action{Type: "verify", Detail: fmt.Sprintf("Verify checksum %s", class.Checksum)})
}
actions = append(actions, Action{Type: "write", Detail: fmt.Sprintf("Write image to %s", device), Command: fmt.Sprintf("dd if=%s of=%s bs=4M status=progress conv=fsync", cacheImage, device)})
actions = append(actions, Action{Type: "inject", Detail: "Inject hostname/network/k3s config into boot or rootfs"})
actions = append(actions, Action{Type: "finalize", Detail: fmt.Sprintf("Ready to insert SD for %s", node.Hostname)})
return &Plan{
Node: nodeName,
Device: device,
Image: class.Image,
Class: class.Name,
Actions: actions,
}, nil
}
// NextRunStale returns true if the last success was older than the given duration.
func NextRunStale(lastSuccess time.Time, maxAge time.Duration) bool {
return time.Since(lastSuccess) > maxAge
}