feat: add config builder and CLI command

This commit is contained in:
Brad Stein 2026-01-11 09:55:57 -03:00
parent 3e950a4aca
commit 62ec09cc02
3 changed files with 119 additions and 0 deletions

28
cmd/metis/config_cmd.go Normal file
View File

@ -0,0 +1,28 @@
package main
import (
"encoding/json"
"flag"
"log"
"os"
"metis/pkg/config"
)
func configCmd(args []string) {
fs := flag.NewFlagSet("config", flag.ExitOnError)
invPath := fs.String("inventory", "inventory.yaml", "inventory file")
node := fs.String("node", "", "target node")
fs.Parse(args)
if *node == "" {
log.Fatalf("--node is required")
}
inv := loadInventory(*invPath)
cfg, err := config.Build(inv, *node)
if err != nil {
log.Fatalf("config build: %v", err)
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
_ = enc.Encode(cfg)
}

View File

@ -22,6 +22,8 @@ func main() {
planCmd(os.Args[2:])
case "burn":
burnCmd(os.Args[2:])
case "config":
configCmd(os.Args[2:])
default:
usage()
os.Exit(1)

89
pkg/config/config.go Normal file
View File

@ -0,0 +1,89 @@
package config
import (
"fmt"
"metis/pkg/inventory"
)
// NodeConfig represents boot-time configuration to inject.
type NodeConfig struct {
Hostname string `json:"hostname"`
IP string `json:"ip"`
K3s K3sConfig `json:"k3s"`
SSHUser string `json:"ssh_user,omitempty"`
SSHKeys []string `json:"ssh_keys,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Taints []string `json:"taints,omitempty"`
Fstab []FstabEntry `json:"fstab,omitempty"`
}
// K3sConfig includes role and token/url.
type K3sConfig struct {
Role string `json:"role"`
URL string `json:"url,omitempty"`
Token string `json:"token,omitempty"`
Args []string `json:"args,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Taints []string `json:"taints,omitempty"`
}
// FstabEntry for Longhorn or other mounts.
type FstabEntry struct {
UUID string `json:"uuid"`
Mountpoint string `json:"mountpoint"`
FS string `json:"fs"`
Options string `json:"options"`
}
// Build creates a NodeConfig from inventory.
func Build(inv *inventory.Inventory, nodeName string) (*NodeConfig, error) {
n, cls, err := inv.FindNode(nodeName)
if err != nil {
return nil, err
}
labels := map[string]string{}
for k, v := range cls.DefaultLabels {
labels[k] = v
}
for k, v := range n.Labels {
labels[k] = v
}
taints := append([]string{}, cls.DefaultTaints...)
taints = append(taints, n.Taints...)
fstab := []FstabEntry{}
for _, d := range n.LonghornDisks {
fs := d.FS
if fs == "" {
fs = "ext4"
}
fstab = append(fstab, FstabEntry{
UUID: d.UUID,
Mountpoint: d.Mountpoint,
FS: fs,
Options: "defaults,nofail",
})
}
cfg := &NodeConfig{
Hostname: n.Hostname,
IP: n.IP,
SSHUser: n.SSHUser,
SSHKeys: n.SSHAuthorized,
Labels: labels,
Taints: taints,
Fstab: fstab,
K3s: K3sConfig{
Role: n.K3sRole,
URL: n.K3sURL,
Token: n.K3sToken,
Labels: labels,
Taints: taints,
},
}
if cfg.Hostname == "" || cfg.IP == "" {
return nil, fmt.Errorf("hostname/ip required for node %s", nodeName)
}
return cfg, nil
}