130 lines
3.3 KiB
Go
130 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"metis/pkg/inventory"
|
|
"metis/pkg/plan"
|
|
)
|
|
|
|
var (
|
|
fatalf = log.Fatalf
|
|
exit = os.Exit
|
|
)
|
|
|
|
func main() {
|
|
if len(os.Args) < 2 {
|
|
usage()
|
|
exit(1)
|
|
}
|
|
switch os.Args[1] {
|
|
case "plan":
|
|
planCmd(os.Args[2:])
|
|
case "burn":
|
|
burnCmd(os.Args[2:])
|
|
case "image":
|
|
imageCmd(os.Args[2:])
|
|
case "serve":
|
|
serveCmd(os.Args[2:])
|
|
case "inject":
|
|
injectCmd(os.Args[2:])
|
|
case "config":
|
|
configCmd(os.Args[2:])
|
|
case "facts":
|
|
factsCmd(os.Args[2:])
|
|
case "remote-devices":
|
|
remoteDevicesCmd(os.Args[2:])
|
|
case "remote-build":
|
|
remoteBuildCmd(os.Args[2:])
|
|
case "remote-flash":
|
|
remoteFlashCmd(os.Args[2:])
|
|
default:
|
|
usage()
|
|
exit(1)
|
|
}
|
|
}
|
|
|
|
func usage() {
|
|
fmt.Fprintf(os.Stderr, "Usage: metis <plan|burn|image|serve|inject|config|facts|remote-devices|remote-build|remote-flash> [options]\n")
|
|
}
|
|
|
|
func loadInventory(path string) *inventory.Inventory {
|
|
inv, err := inventory.Load(path)
|
|
if err != nil {
|
|
fatalf("load inventory: %v", err)
|
|
}
|
|
return inv
|
|
}
|
|
|
|
func planCmd(args []string) {
|
|
fs := flag.NewFlagSet("plan", flag.ExitOnError)
|
|
invPath := fs.String("inventory", "inventory.yaml", "inventory file")
|
|
node := fs.String("node", "", "target node")
|
|
device := fs.String("device", "/dev/sdX", "target block device")
|
|
cache := fs.String("cache", filepath.Join(os.TempDir(), "metis-cache"), "image cache dir")
|
|
boot := fs.String("boot", "", "mounted boot path for injection (optional)")
|
|
root := fs.String("root", "", "mounted root path for injection (optional)")
|
|
fs.Parse(args)
|
|
if *node == "" {
|
|
fatalf("--node is required")
|
|
}
|
|
inv := loadInventory(*invPath)
|
|
if *boot != "" {
|
|
os.Setenv("METIS_BOOT_PATH", *boot)
|
|
}
|
|
if *root != "" {
|
|
os.Setenv("METIS_ROOT_PATH", *root)
|
|
}
|
|
p, err := plan.Build(inv, *node, *device, *cache)
|
|
if err != nil {
|
|
fatalf("build plan: %v", err)
|
|
}
|
|
enc := json.NewEncoder(os.Stdout)
|
|
enc.SetIndent("", " ")
|
|
_ = enc.Encode(p)
|
|
}
|
|
|
|
func burnCmd(args []string) {
|
|
fs := flag.NewFlagSet("burn", flag.ExitOnError)
|
|
invPath := fs.String("inventory", "inventory.yaml", "inventory file")
|
|
node := fs.String("node", "", "target node")
|
|
device := fs.String("device", "", "target block device (e.g. /dev/sdX)")
|
|
cache := fs.String("cache", filepath.Join(os.TempDir(), "metis-cache"), "image cache dir")
|
|
boot := fs.String("boot", "", "mounted boot path for injection (optional)")
|
|
root := fs.String("root", "", "mounted root path for injection (optional)")
|
|
autoMount := fs.Bool("auto-mount", false, "auto-mount boot/root for injection (linux, requires privileges)")
|
|
confirm := fs.Bool("yes", false, "actually write to device")
|
|
fs.Parse(args)
|
|
if *node == "" || *device == "" {
|
|
fatalf("--node and --device are required")
|
|
}
|
|
inv := loadInventory(*invPath)
|
|
if *boot != "" {
|
|
os.Setenv("METIS_BOOT_PATH", *boot)
|
|
}
|
|
if *root != "" {
|
|
os.Setenv("METIS_ROOT_PATH", *root)
|
|
}
|
|
if *autoMount {
|
|
os.Setenv("METIS_AUTO_MOUNT", "1")
|
|
}
|
|
p, err := plan.Execute(inv, *node, *device, *cache, *confirm)
|
|
if err != nil {
|
|
fatalf("burn: %v", err)
|
|
}
|
|
fmt.Printf("Plan for %s to %s:\n", p.Node, p.Device)
|
|
for _, a := range p.Actions {
|
|
fmt.Printf("- [%s] %s\n", a.Type, a.Detail)
|
|
}
|
|
if !*confirm {
|
|
fmt.Printf("\nDry run. Re-run with --yes to execute.\n")
|
|
return
|
|
}
|
|
fmt.Println("\nBurn complete. Safely eject and insert the SD into the node.")
|
|
}
|