diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e6acbd3..65e899c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Read environment variables from `/etc/default/warewulfd` #725 - Add support for VLANs to NetworkManager, wicked, ifcfg, debian.network_interfaces overlays. #1257 - Add support for static routes to NetworkManager, wicked, ifcfg, debian.network_interfaces overlays. #1257 +- Add `wwctl upgrade `. #230, #517 ### Changed diff --git a/cmd/update_configuration/update_configuration.go b/cmd/update_configuration/update_configuration.go deleted file mode 100644 index bc394445..00000000 --- a/cmd/update_configuration/update_configuration.go +++ /dev/null @@ -1,182 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" - "reflect" - - "github.com/warewulf/warewulf/cmd/update_configuration/vers42" - "github.com/warewulf/warewulf/cmd/update_configuration/vers43" - "github.com/warewulf/warewulf/internal/pkg/util" - "gopkg.in/yaml.v3" -) - -var nowrite bool -var quiet bool -var confFile string - -const actvers int = 43 - -type nodeVersionOnly struct { - WWInternal int `yaml:"WW_INTERNAL"` -} - -func saveConf(conf interface{}) { - out, err := yaml.Marshal(conf) - if err != nil { - myprintf("Error in marshal of conf: %s\n", err) - os.Exit(1) - } - if nowrite { - fmt.Print(string(out)) - } else { - err = util.CopyFile(confFile, confFile+".bak") - if err != nil { - myprintf("Could write file: %s\n", err) - os.Exit(1) - } - info, err := os.Stat(confFile) - if err != nil { - myprintf("Could not get file mode: %s\n", err) - os.Exit(1) - } - myprintf("writing configuration file %s as type %s\n", confFile, reflect.TypeOf(conf)) - err = os.WriteFile(confFile, out, info.Mode()) - if err != nil { - myprintf("Could not write file: %s\n", err) - os.Exit(1) - } - } -} - -func printB(x bool) string { - if x { - return "true" - } - return "false" -} - -func update42to43(conf42 vers42.NodeConf) vers43.NodeConf { - ret := vers43.NodeConf{ - Comment: conf42.Comment, - ClusterName: conf42.ClusterName, - ContainerName: conf42.ContainerName, - Init: conf42.Init, - Root: conf42.Root, - Discoverable: printB(conf42.Discoverable), - Profiles: conf42.Profiles, - Ipxe: conf42.Ipxe} - if conf42.RuntimeOverlay != "" { - ret.RuntimeOverlay = []string{conf42.RuntimeOverlay} - } - if conf42.SystemOverlay != "" { - ret.SystemOverlay = []string{conf42.SystemOverlay} - } - if conf42.KernelArgs != "" || conf42.KernelVersion != "" { - ret.Kernel = new(vers43.KernelConf) - ret.Kernel.Override = conf42.KernelVersion - ret.Kernel.Args = conf42.KernelArgs - - } - if conf42.IpmiUserName != "" || conf42.IpmiPassword != "" || conf42.IpmiIpaddr != "" || - conf42.IpmiNetmask != "" || conf42.IpmiPort != "" || conf42.IpmiGateway != "" || - conf42.IpmiInterface != "" { - ret.Ipmi = new(vers43.IpmiConf) - ret.Ipmi.UserName = conf42.IpmiUserName - ret.Ipmi.Password = conf42.IpmiPassword - ret.Ipmi.Ipaddr = conf42.IpmiIpaddr - ret.Ipmi.Netmask = conf42.IpmiNetmask - ret.Ipmi.Port = conf42.IpmiPort - ret.Ipmi.Gateway = conf42.IpmiGateway - ret.Ipmi.Interface = conf42.IpmiInterface - } - if len(conf42.Keys) != 0 { - ret.Keys = map[string]string{} - for k, v := range conf42.Keys { - ret.Keys[k] = v - } - } - ret.NetDevs = make(map[string]*vers43.NetDevs) - for devn, netdev := range conf42.NetDevs { - var device vers43.NetDevs = vers43.NetDevs{ - Type: netdev.Type, - Device: devn, - Primary: printB(netdev.Default), - Hwaddr: netdev.Hwaddr, - Ipaddr: netdev.Ipaddr, - IpCIDR: netdev.IpCIDR, - Prefix: netdev.Prefix, - Netmask: netdev.Netmask, - Gateway: netdev.Gateway} - ret.NetDevs[devn] = &device - } - return ret -} - -func myprintf(format string, a ...interface{}) { - if !quiet { - fmt.Printf(format, a...) - } -} - -func main() { - var endVers int - var startVers int - flag.StringVar(&confFile, "f", "", "Config file for update") - flag.IntVar(&endVers, "e", 0, "Final version of configuration file") - flag.IntVar(&startVers, "s", 0, "Start version of configuration file, 0 is for autodetection") - flag.BoolVar(&nowrite, "n", false, "Do not write, just print new conf to terminal") - flag.BoolVar(&quiet, "q", false, "Do not print what the program is doing") - flag.Parse() - if confFile == "" { - myprintf("No config file given\n!") - os.Exit(1) - } - myprintf("Opening node configuration file: %s\n", confFile) - data, err := os.ReadFile(confFile) - if err != nil { - myprintf("Could open file %v\n", err) - os.Exit(1) - } - var getConf nodeVersionOnly - myprintf("Unmarshaling the node configuration\n") - err = yaml.Unmarshal(data, &getConf) - if err != nil { - myprintf("Could not unmarshall: %v\n", err) - } - myprintf("Got version %v in %s\n", getConf.WWInternal, confFile) - if getConf.WWInternal == actvers { - myprintf("On actual version, bailing out\n") - os.Exit(0) - } - if startVers == 0 && getConf.WWInternal == 0 { - startVers = 42 - } - var conf42 vers42.NodeYaml - conf42.NodeProfiles = make(map[string]*vers42.NodeConf) - conf42.Nodes = make(map[string]*vers42.NodeConf) - - var conf43 vers43.NodeYaml - conf43.NodeProfiles = make(map[string]*vers43.NodeConf) - conf43.Nodes = make(map[string]*vers43.NodeConf) - conf43.WWInternal = 43 - - if startVers == 42 { - myprintf("Unmarshaling the node configuration vers 42\n") - err = yaml.Unmarshal(data, &conf42) - if err != nil { - myprintf("Could not unmarshall version 42: %v\n", err) - } - for pname, profile := range conf42.NodeProfiles { - p43 := update42to43(*profile) - conf43.NodeProfiles[pname] = &p43 - } - for nname, node := range conf42.Nodes { - n43 := update42to43(*node) - conf43.Nodes[nname] = &n43 - } - saveConf(conf43) - - } -} diff --git a/cmd/update_configuration/vers42/datastructure.go b/cmd/update_configuration/vers42/datastructure.go deleted file mode 100644 index 188419d8..00000000 --- a/cmd/update_configuration/vers42/datastructure.go +++ /dev/null @@ -1,46 +0,0 @@ -package vers42 - -/****** - * YAML data representations - ******/ - -// type nodeYaml struct { -type NodeYaml struct { // <-Needs to be exported - NodeProfiles map[string]*NodeConf - Nodes map[string]*NodeConf -} - -type NodeConf struct { - Comment string `yaml:"comment,omitempty"` - ClusterName string `yaml:"cluster name,omitempty"` - ContainerName string `yaml:"container name,omitempty"` - Ipxe string `yaml:"ipxe template,omitempty"` - KernelVersion string `yaml:"kernel version,omitempty"` - KernelArgs string `yaml:"kernel args,omitempty"` - IpmiUserName string `yaml:"ipmi username,omitempty"` - IpmiPassword string `yaml:"ipmi password,omitempty"` - IpmiIpaddr string `yaml:"ipmi ipaddr,omitempty"` - IpmiNetmask string `yaml:"ipmi netmask,omitempty"` - IpmiPort string `yaml:"ipmi port,omitempty"` - IpmiGateway string `yaml:"ipmi gateway,omitempty"` - IpmiInterface string `yaml:"ipmi interface,omitempty"` - RuntimeOverlay string `yaml:"runtime overlay,omitempty"` - SystemOverlay string `yaml:"system overlay,omitempty"` - Init string `yaml:"init,omitempty"` - Root string `yaml:"root,omitempty"` - Discoverable bool `yaml:"discoverable,omitempty"` - Profiles []string `yaml:"profiles,omitempty"` - NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"` - Keys map[string]string `yaml:"keys,omitempty"` -} - -type NetDevs struct { - Type string `yaml:"type,omitempty"` - Default bool `yaml:"default"` - Hwaddr string - Ipaddr string - IpCIDR string - Prefix string - Netmask string - Gateway string `yaml:"gateway,omitempty"` -} diff --git a/cmd/update_configuration/vers42/nodes.conf b/cmd/update_configuration/vers42/nodes.conf deleted file mode 100644 index 792e48a2..00000000 --- a/cmd/update_configuration/vers42/nodes.conf +++ /dev/null @@ -1,22 +0,0 @@ -nodeprofiles: - default: - comment: This profile is automatically included for each node - runtime overlay: "generic" - discoverable: false - leap: - comment: openSUSE leap - kernel version: "5.14.21" - ipmi netmask: "255.255.255.0" - keys: - foo: baar - network devices: - lan1: - gateway: 1.1.1.1 -nodes: - node01: - system overlay: "nodeoverlay" - discoverable: true - network devices: - eth0: - ipaddr: 1.2.3.4 - default: true diff --git a/cmd/update_configuration/vers43/datastructure.go b/cmd/update_configuration/vers43/datastructure.go deleted file mode 100644 index 24e8f185..00000000 --- a/cmd/update_configuration/vers43/datastructure.go +++ /dev/null @@ -1,65 +0,0 @@ -package vers43 - -/****** - * YAML data representations - ******/ - -// type nodeYaml struct { -type NodeYaml struct { // <- needs to be exported - WWInternal int `yaml:"WW_INTERNAL"` - NodeProfiles map[string]*NodeConf - Nodes map[string]*NodeConf -} - -/* -NodeConf is the datastructure which is stored on disk. -*/ -type NodeConf struct { - Comment string `yaml:"comment,omitempty"` - ClusterName string `yaml:"cluster name,omitempty"` - ContainerName string `yaml:"container name,omitempty"` - Ipxe string `yaml:"ipxe template,omitempty"` - RuntimeOverlay []string `yaml:"runtime overlay,omitempty"` - SystemOverlay []string `yaml:"system overlay,omitempty"` - Kernel *KernelConf `yaml:"kernel,omitempty"` - Ipmi *IpmiConf `yaml:"ipmi,omitempty"` - Init string `yaml:"init,omitempty"` - Root string `yaml:"root,omitempty"` - AssetKey string `yaml:"asset key,omitempty"` - Discoverable string `yaml:"discoverable,omitempty"` - Profiles []string `yaml:"profiles,omitempty"` - NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"` - Tags map[string]string `yaml:"tags,omitempty"` - Keys map[string]string `yaml:"keys,omitempty"` // Reverse compatibility -} - -type IpmiConf struct { - UserName string `yaml:"username,omitempty"` - Password string `yaml:"password,omitempty"` - Ipaddr string `yaml:"ipaddr,omitempty"` - Netmask string `yaml:"netmask,omitempty"` - Port string `yaml:"port,omitempty"` - Gateway string `yaml:"gateway,omitempty"` - Interface string `yaml:"interface,omitempty"` - Write bool `yaml:"write,omitempty"` -} -type KernelConf struct { - Version string `yaml:"version,omitempty"` - Override string `yaml:"override,omitempty"` - Args string `yaml:"args,omitempty"` -} - -type NetDevs struct { - Type string `yaml:"type,omitempty"` - OnBoot string `yaml:"onboot,omitempty"` - Device string `yaml:"device,omitempty"` - Hwaddr string `yaml:"hwaddr,omitempty"` - Ipaddr string `yaml:"ipaddr,omitempty"` - IpCIDR string `yaml:"ipcidr,omitempty"` - Ipaddr6 string `yaml:"ip6addr,omitempty"` - Prefix string `yaml:"prefix,omitempty"` - Netmask string `yaml:"netmask,omitempty"` - Gateway string `yaml:"gateway,omitempty"` - Primary string `yaml:"primary,omitempty"` - Tags map[string]string `yaml:"tags,omitempty"` -} diff --git a/cmd/update_configuration/vers43/nodes.conf b/cmd/update_configuration/vers43/nodes.conf deleted file mode 100644 index 22772050..00000000 --- a/cmd/update_configuration/vers43/nodes.conf +++ /dev/null @@ -1,32 +0,0 @@ -WW_INTERNAL: 45 -nodeprofiles: - default: - comment: This profile is automatically included for each node - runtime overlay: - - generic - discoverable: "false" - leap: - comment: openSUSE leap - kernel: - override: 5.14.21 - ipmi: - netmask: 255.255.255.0 - discoverable: "false" - network devices: - lan1: - device: lan1 - gateway: 1.1.1.1 - default: "false" - keys: - foo: baar -nodes: - node01: - system overlay: - - nodeoverlay - discoverable: "true" - network devices: - eth0: - device: eth0 - ipaddr: 1.2.3.4 - default: "true" - diff --git a/docs/man/man5/warewulf.conf.5 b/docs/man/man5/warewulf.conf.5 index 78716a41..bb22172d 100644 --- a/docs/man/man5/warewulf.conf.5 +++ b/docs/man/man5/warewulf.conf.5 @@ -16,12 +16,6 @@ warewulf.conf is defined using YAML document markup syntax. .LP The configuration parameters available include: -.TP -\fBWW_INTERNAL\fP -Specifies the version of the configuration file format. The current -version is 43. -.IP - .TP \fBipaddr\fP This is the control node's networking interface connecting to the @@ -279,7 +273,6 @@ A sample configuration file for a typical deployment, with all dependent services enabled. .EX -WW_INTERNAL: 45 ipaddr: 10.0.0.1 network: 10.0.0.0 netmask: 255.255.0.0 diff --git a/etc/nodes.conf b/etc/nodes.conf index 62d5b496..1b8c983a 100644 --- a/etc/nodes.conf +++ b/etc/nodes.conf @@ -1,4 +1,3 @@ -WW_INTERNAL: 46 nodeprofiles: default: comment: This profile is automatically included for each node diff --git a/etc/warewulf.conf b/etc/warewulf.conf index ae9203e4..b0589a91 100644 --- a/etc/warewulf.conf +++ b/etc/warewulf.conf @@ -1,4 +1,3 @@ -WW_INTERNAL: 45 ipaddr: 10.0.0.1 netmask: 255.255.252.0 warewulf: diff --git a/internal/app/wwclient/root.go b/internal/app/wwclient/root.go index 3e1fa799..8034deb2 100644 --- a/internal/app/wwclient/root.go +++ b/internal/app/wwclient/root.go @@ -102,7 +102,7 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { if conf.WWClient != nil && conf.WWClient.Port > 0 { localTCPAddr.Port = int(conf.WWClient.Port) wwlog.Info("Running from configured port %d", conf.WWClient.Port) - } else if conf.Warewulf.Secure { + } else if conf.Warewulf.Secure() { // Setup local port to something privileged (<1024) localTCPAddr.Port = 987 wwlog.Info("Running from trusted port") diff --git a/internal/app/wwctl/clean/main_test.go b/internal/app/wwctl/clean/main_test.go index 23d8c38b..6396e8bd 100644 --- a/internal/app/wwctl/clean/main_test.go +++ b/internal/app/wwctl/clean/main_test.go @@ -12,8 +12,7 @@ func Test_Clean(t *testing.T) { wwlog.SetLogLevel(wwlog.DEBUG) env := testenv.New(t) env.WriteFile(t, "etc/warewulf/nodes.conf", - `WW_INTERNAL: 45 -nodeprofiles: {} + `nodeprofiles: {} nodes: node1: {} `) diff --git a/internal/app/wwctl/container/exec/child/main.go b/internal/app/wwctl/container/exec/child/main.go index 9e9c064f..455ef9f9 100644 --- a/internal/app/wwctl/container/exec/child/main.go +++ b/internal/app/wwctl/container/exec/child/main.go @@ -134,14 +134,14 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { } for _, mntPnt := range mountPts { - if mntPnt.Copy { + if mntPnt.Copy() { continue } wwlog.Debug("bind mounting: %s -> %s", mntPnt.Source, path.Join(containerPath, mntPnt.Dest)) err = syscall.Mount(mntPnt.Source, path.Join(containerPath, mntPnt.Dest), "", syscall.MS_BIND, "") if err != nil { wwlog.Warn("Couldn't mount %s to %s: %s", mntPnt.Source, mntPnt.Dest, err) - } else if mntPnt.ReadOnly { + } else if mntPnt.ReadOnly() { err = syscall.Mount(mntPnt.Source, path.Join(containerPath, mntPnt.Dest), "", syscall.MS_REMOUNT|syscall.MS_RDONLY|syscall.MS_BIND, "") if err != nil { wwlog.Warn("failed to following mount readonly: %s", mntPnt.Source) @@ -191,7 +191,7 @@ the invalid mount points. Directories always have '/' as suffix func checkMountPoints(containerName string, binds []*warewulfconf.MountEntry) (overlayObjects []string) { overlayObjects = []string{} for _, b := range binds { - if b.Copy { + if b.Copy() { continue } _, err := os.Stat(b.Source) diff --git a/internal/app/wwctl/container/exec/main.go b/internal/app/wwctl/container/exec/main.go index 195775e4..a087338c 100644 --- a/internal/app/wwctl/container/exec/main.go +++ b/internal/app/wwctl/container/exec/main.go @@ -229,7 +229,7 @@ Check the objects we want to copy in, instead of mounting */ func getCopyFiles(binds []*warewulfconf.MountEntry) (copyObjects []*copyFile) { for _, bind := range binds { - if bind.Copy { + if bind.Copy() { copyObjects = append(copyObjects, ©File{ fileName: bind.Dest, src: bind.Source, diff --git a/internal/app/wwctl/container/list/main_test.go b/internal/app/wwctl/container/list/main_test.go index 1e841128..1baf769c 100644 --- a/internal/app/wwctl/container/list/main_test.go +++ b/internal/app/wwctl/container/list/main_test.go @@ -29,7 +29,7 @@ CONTAINER NAME NODES KERNEL VERSION CREATION TIME MODIFICATION TIME -------------- ----- -------------- ------------- ----------------- ---- test 1 kernel 01 Jan 70 00:00 UTC 01 Jan 70 00:00 UTC 0 B `, - inDb: `WW_INTERNAL: 43 + inDb: ` nodeprofiles: default: {} nodes: diff --git a/internal/app/wwctl/node/add/main_test.go b/internal/app/wwctl/node/add/main_test.go index 01a2811c..104cca51 100644 --- a/internal/app/wwctl/node/add/main_test.go +++ b/internal/app/wwctl/node/add/main_test.go @@ -23,8 +23,7 @@ func Test_Add(t *testing.T) { args: []string{"n01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: profiles: @@ -34,8 +33,7 @@ nodes: args: []string{"--profile=foo", "n01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: profiles: @@ -45,8 +43,7 @@ nodes: args: []string{"--discoverable=true", "n01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: discoverable: "true" @@ -57,8 +54,7 @@ nodes: args: []string{"--discoverable", "n01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: discoverable: "true" @@ -70,16 +66,14 @@ nodes: wantErr: true, stdout: "", chkout: false, - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: {} `}, {name: "single node add, discoverable false", args: []string{"--discoverable=false", "n01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: discoverable: "false" @@ -90,8 +84,7 @@ nodes: args: []string{"--kernelargs=foo", "n01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: kernel: @@ -103,8 +96,7 @@ nodes: args: []string{"n01", "n02"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: profiles: @@ -117,8 +109,7 @@ nodes: args: []string{"--ipaddr6=fdaa::1", "n01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: profiles: @@ -131,8 +122,7 @@ nodes: args: []string{"--ipaddr=10.0.0.1", "n01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: profiles: @@ -146,16 +136,14 @@ nodes: wantErr: true, stdout: "", chkout: false, - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: {} `}, {name: "three nodes with ipaddr", args: []string{"--ipaddr=10.10.0.1", "n[01-02,03]"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: profiles: @@ -180,8 +168,7 @@ nodes: args: []string{"--ipaddr=10.10.0.1", "--netname=foo", "n[01-03]"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: profiles: @@ -206,8 +193,7 @@ nodes: args: []string{"--ipaddr=10.10.0.1", "--netname=foo", "--ipmiaddr=10.20.0.1", "n[01-03]"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: ipmi: @@ -238,8 +224,7 @@ nodes: args: []string{"--fsname=/dev/vda1", "--fspath=/var", "n01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: profiles: @@ -252,16 +237,14 @@ nodes: args: []string{"--fsname=dev/vda1", "--fspath=/var", "n01"}, wantErr: true, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: {} `}, {name: "one node with filesystem and partition ", args: []string{"--fsname=var", "--fspath=/var", "--partname=var", "--diskname=/dev/vda", "--partnumber=1", "n01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: profiles: @@ -279,8 +262,7 @@ nodes: args: []string{"--fsname=var", "--fspath=/var", "--fsformat=btrfs", "--partname=var", "--diskname=/dev/vda", "--partnumber=1", "n01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: profiles: @@ -299,8 +281,7 @@ nodes: warewulfd.SetNoDaemon() for _, tt := range tests { env := testenv.New(t) - env.WriteFile(t, "etc/warewulf/nodes.conf", - `WW_INTERNAL: 45`) + env.WriteFile(t, "etc/warewulf/nodes.conf", ``) var err error t.Run(tt.name, func(t *testing.T) { baseCmd := GetCommand() diff --git a/internal/app/wwctl/node/add/root.go b/internal/app/wwctl/node/add/root.go index 4fb7e5e8..4626e325 100644 --- a/internal/app/wwctl/node/add/root.go +++ b/internal/app/wwctl/node/add/root.go @@ -12,7 +12,7 @@ import ( // Holds the variables which are needed in CobraRunE type variables struct { - nodeConf node.NodeConf + nodeConf node.Node nodeAdd node.NodeConfAdd } diff --git a/internal/app/wwctl/node/delete/main.go b/internal/app/wwctl/node/delete/main.go index 40ad2554..7da7c19f 100644 --- a/internal/app/wwctl/node/delete/main.go +++ b/internal/app/wwctl/node/delete/main.go @@ -18,7 +18,7 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { } if !SetYes { - var nodeList []node.NodeConf + var nodeList []node.Node // The checks run twice in the prompt case. // Avoiding putting in a blocking prompt in an API. nodeList, err = apiNode.NodeDeleteParameterCheck(&ndp, false) diff --git a/internal/app/wwctl/node/edit/main.go b/internal/app/wwctl/node/edit/main.go index 34660743..82a81f43 100644 --- a/internal/app/wwctl/node/edit/main.go +++ b/internal/app/wwctl/node/edit/main.go @@ -38,7 +38,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { Output: args, } nodeListMsg := apinode.FilteredNodes(&filterList) - nodeMap := make(map[string]*node.NodeConf) + nodeMap := make(map[string]*node.Node) // got proper yaml back _ = yaml.Unmarshal([]byte(nodeListMsg.NodeConfMapYaml), nodeMap) file, err := os.CreateTemp(os.TempDir(), "ww4NodeEdit*.yaml") @@ -50,7 +50,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { _ = file.Truncate(0) _, _ = file.Seek(0, 0) if !NoHeader { - yamlTemplate := node.UnmarshalConf(node.NodeConf{}, []string{"tagsdel"}) + yamlTemplate := node.UnmarshalConf(node.Node{}, []string{"tagsdel"}) _, _ = file.WriteString("#nodename:\n# " + strings.Join(yamlTemplate, "\n# ") + "\n") } _, _ = file.WriteString(nodeListMsg.NodeConfMapYaml) @@ -73,7 +73,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { wwlog.Debug("Hashes are before %s and after %s\n", sum1, sum2) if sum1 != sum2 { wwlog.Debug("Nodes were modified") - modifiedNodeMap := make(map[string]*node.NodeConf) + modifiedNodeMap := make(map[string]*node.Node) _, _ = file.Seek(0, 0) // ignore error as only may occurs under strange circumstances buffer, _ := io.ReadAll(file) diff --git a/internal/app/wwctl/node/imprt/main.go b/internal/app/wwctl/node/imprt/main.go index 2a7156c2..5bc1e81c 100644 --- a/internal/app/wwctl/node/imprt/main.go +++ b/internal/app/wwctl/node/imprt/main.go @@ -22,7 +22,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } defer file.Close() - importMap := make(map[string]*node.NodeConf) + importMap := make(map[string]*node.Node) buffer, err := io.ReadAll(file) if err != nil { return fmt.Errorf("could not read: %s", err) @@ -64,7 +64,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { continue } if importMap[line[0]] == nil { - importMap[line[0]] = new(node.NodeConf) + importMap[line[0]] = new(node.Node) } } } diff --git a/internal/app/wwctl/node/list/main_test.go b/internal/app/wwctl/node/list/main_test.go index f0a24793..b39c385e 100644 --- a/internal/app/wwctl/node/list/main_test.go +++ b/internal/app/wwctl/node/list/main_test.go @@ -32,8 +32,7 @@ NODE NAME PROFILES NETWORK --------- -------- ------- n01 [default] -- `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: default: {} nodes: n01: @@ -51,8 +50,7 @@ NODE NAME PROFILES NETWORK n01 [default] -- n02 [default] -- `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: default: {} nodes: n01: @@ -73,8 +71,7 @@ NODE NAME PROFILES NETWORK n01 [default] -- n02 [default] -- `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: default: {} nodes: n01: @@ -95,8 +92,7 @@ NODE NAME PROFILES NETWORK n01 [default] -- n03 [default] -- `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: default: {} nodes: n01: @@ -125,8 +121,7 @@ NODE NAME PROFILES NETWORK --------- -------- ------- n01 [default] -- `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: default: {} nodes: n01: @@ -146,8 +141,7 @@ NODE NAME PROFILES NETWORK --------- -------- ------- n01 [default] default `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: default: network devices: default: @@ -168,8 +162,7 @@ NODE FIELD PROFILE VALUE n01 Comment default profilecomment n01 Profiles -- default `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: default: comment: profilecomment nodes: @@ -188,8 +181,7 @@ NODE FIELD PROFILE VALUE n01 Comment SUPERSEDED nodecomment n01 Profiles -- default `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: default: comment: profilecomment nodes: @@ -208,8 +200,7 @@ NODE IPMI IPADDR IPMI PORT IPMI USERNAME IPMI INTERFACE ---- ----------- --------- ------------- -------------- n01 -- admin -- `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: default: ipmi: username: admin @@ -228,8 +219,7 @@ NODE IPMI IPADDR IPMI PORT IPMI USERNAME IPMI INTERFACE ---- ----------- --------- ------------- -------------- n01 -- user -- `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: default: ipmi: username: admin @@ -250,8 +240,7 @@ NODE NAME PROFILES NETWORK --------- -------- ------- n01 [p1 p2] -- `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: p1: {} p2: {} nodes: @@ -270,8 +259,7 @@ NODE FIELD PROFILE VALUE ---- ----- ------- ----- n01 Profiles -- p1,p2 `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: p1: {} p2: {} nodes: @@ -290,8 +278,7 @@ NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R) --------- --------------- --------- -------------- n01 -- -- /rop1,rop2 `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: p1: runtime overlay: - rop1 @@ -311,8 +298,7 @@ NODE NAME KERNEL OVERRIDE CONTAINER OVERLAYS (S/R) --------- --------------- --------- -------------- n01 -- -- sop1/nop1,~rop1,rop1,rop2 `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: p1: system overlay: - sop1 @@ -339,8 +325,7 @@ n01 Profiles -- p1 n01 RuntimeOverlay p1+ nop1,~rop1,rop1,rop2 n01 SystemOverlay p1 sop1 `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: p1: system overlay: - sop1 @@ -366,8 +351,7 @@ NODE FIELD PROFILE VALUE n01 Profiles -- p1 n01 RuntimeOverlay p1+ nop1,rop1,rop2 `, - inDb: `WW_INTERNAL: 45 -nodeprofiles: + inDb: `nodeprofiles: p1: runtime overlay: - rop1 @@ -389,8 +373,7 @@ NODE FIELD PROFILE VALUE ---- ----- ------- ----- n1 NetDevs[default].OnBoot -- true `, - inDb: `WW_INTERNAL: 45 -nodes: + inDb: `nodes: n1: network devices: default: @@ -405,8 +388,7 @@ nodes: NODE FIELD PROFILE VALUE ---- ----- ------- ----- `, - inDb: `WW_INTERNAL: 46 -nodes: + inDb: `nodes: wwnode1: network devices: default: {} @@ -414,7 +396,7 @@ nodes: }, } - conf_yml := `WW_INTERNAL: 0` + conf_yml := `` tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") assert.NoError(t, warewulfConfErr) defer os.Remove(tempWarewulfConf.Name()) @@ -470,7 +452,7 @@ func TestListMultipleFormats(t *testing.T) { kernel: {} ipmi: {} `, - inDb: `WW_INTERNAL: 43 + inDb: ` nodeprofiles: default: {} nodes: @@ -520,7 +502,7 @@ nodes: } ] `, - inDb: `WW_INTERNAL: 43 + inDb: ` nodeprofiles: default: {} nodes: @@ -604,7 +586,7 @@ nodes: } ] `, - inDb: `WW_INTERNAL: 43 + inDb: ` nodeprofiles: default: {} nodes: @@ -629,7 +611,7 @@ nodes: kernel: {} ipmi: {} `, - inDb: `WW_INTERNAL: 43 + inDb: ` nodeprofiles: default: {} nodes: diff --git a/internal/app/wwctl/node/sensors/root_test.go b/internal/app/wwctl/node/sensors/root_test.go index 3fff5f6a..450aab4c 100644 --- a/internal/app/wwctl/node/sensors/root_test.go +++ b/internal/app/wwctl/node/sensors/root_test.go @@ -15,7 +15,7 @@ func Test_Sensors(t *testing.T) { warewulfd.SetNoDaemon() env := testenv.New(t) defer env.RemoveAll(t) - env.WriteFile(t, "etc/warewulf/nodes.conf", `WW_INTERNAL: 43 + env.WriteFile(t, "etc/warewulf/nodes.conf", ` nodeprofiles: default: ipmi: diff --git a/internal/app/wwctl/node/set/main_test.go b/internal/app/wwctl/node/set/main_test.go index 1c8fbc14..ba0f7a2f 100644 --- a/internal/app/wwctl/node/set/main_test.go +++ b/internal/app/wwctl/node/set/main_test.go @@ -56,16 +56,14 @@ func Test_Single_Node_Change_Profile(t *testing.T) { args: []string{"--profile=foo", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit nodes: n01: profiles: - default`, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit nodes: @@ -82,15 +80,13 @@ func Test_Node_Unset(t *testing.T) { args: []string{"--comment=UNDEF", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 43 -nodeprofiles: {} + inDB: `nodeprofiles: {} nodes: n01: comment: foo profiles: - default`, - outDb: `WW_INTERNAL: 43 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: profiles: @@ -105,13 +101,11 @@ func Test_Set_Ipmi_Write_Explicit(t *testing.T) { args: []string{"--ipmiwrite", "true", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 43 -nodeprofiles: {} + inDB: `nodeprofiles: {} nodes: n01: {} `, - outDb: `WW_INTERNAL: 43 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: ipmi: @@ -126,13 +120,11 @@ func Test_Set_Ipmi_Write_Implicit(t *testing.T) { args: []string{"--ipmiwrite", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 43 -nodeprofiles: {} + inDB: `nodeprofiles: {} nodes: n01: {} `, - outDb: `WW_INTERNAL: 43 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: ipmi: @@ -147,15 +139,13 @@ func Test_Unset_Ipmi_Write(t *testing.T) { args: []string{"--ipmiwrite=UNDEF", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 43 -nodeprofiles: {} + inDB: `nodeprofiles: {} nodes: n01: ipmi: write: "true" `, - outDb: `WW_INTERNAL: 43 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: {} `, @@ -168,15 +158,13 @@ func Test_Unset_Ipmi_Write_False(t *testing.T) { args: []string{"--ipmiwrite=UNDEF", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 43 -nodeprofiles: {} + inDB: `nodeprofiles: {} nodes: n01: ipmi: write: "false" `, - outDb: `WW_INTERNAL: 43 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: {} `, @@ -189,8 +177,7 @@ func Test_Ipmi_Hidden_False(t *testing.T) { args: []string{"--ipmiwrite=false", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 43 -nodeprofiles: + inDB: `nodeprofiles: default: ipmi: write: "true" @@ -199,8 +186,7 @@ nodes: profiles: - default `, - outDb: `WW_INTERNAL: 43 -nodeprofiles: + outDb: `nodeprofiles: default: ipmi: write: "true" @@ -269,16 +255,14 @@ func Test_Multiple_Set_Tests(t *testing.T) { args: []string{"--profile=foo", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit nodes: n01: profiles: - default`, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit nodes: @@ -292,8 +276,7 @@ nodes: args: []string{"--profile=foo", "n0[1-2]"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit nodes: @@ -303,8 +286,7 @@ nodes: n02: profiles: - default`, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit nodes: @@ -321,16 +303,14 @@ nodes: args: []string{"--ipmitagadd", "foo=baar", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit nodes: n01: profiles: - default`, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit nodes: @@ -347,8 +327,7 @@ nodes: args: []string{"--tagdel", "tag1", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit nodes: @@ -358,8 +337,7 @@ nodes: tags: tag1: value1 tag2: value2`, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit nodes: @@ -375,8 +353,7 @@ nodes: args: []string{"--fsname=var", "--fspath=/var", "--fsformat=btrfs", "--partname=var", "--partnumber=1", "--diskname=/dev/vda", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit nodes: @@ -384,8 +361,7 @@ nodes: profiles: - default `, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit nodes: @@ -408,8 +384,7 @@ nodes: args: []string{"--fsdel=foo", "n01"}, wantErr: true, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit nodes: @@ -427,8 +402,7 @@ nodes: format: btrfs path: /var `, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit nodes: @@ -451,8 +425,7 @@ nodes: args: []string{"--fsdel=/dev/disk/by-partlabel/var", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit nodes: @@ -470,8 +443,7 @@ nodes: format: btrfs path: /var `, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit nodes: @@ -490,8 +462,7 @@ nodes: args: []string{"--partdel=var", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit nodes: @@ -509,8 +480,7 @@ nodes: format: btrfs path: /var `, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit nodes: @@ -528,8 +498,7 @@ nodes: args: []string{"--diskdel=/dev/vda", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit nodes: @@ -546,8 +515,7 @@ nodes: format: btrfs path: /var `, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit nodes: @@ -565,16 +533,14 @@ nodes: args: []string{"--mtu", "1234", "--netname=mynet", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit nodes: n01: profiles: - default`, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit nodes: @@ -591,8 +557,7 @@ nodes: args: []string{"--tagadd", "nodetag1=nodevalue1", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: p1: comment: testit 1 tags: @@ -606,8 +571,7 @@ nodes: profiles: - p1 - p2`, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: p1: comment: testit 1 tags: @@ -630,13 +594,11 @@ nodes: args: []string{"n01", "--comment", "This is a , comment"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 43 -nodes: + inDB: `nodes: n01: comment: old comment `, - outDb: `WW_INTERNAL: 43 -nodeprofiles: {} + outDb: `nodeprofiles: {} nodes: n01: comment: This is a , comment @@ -644,7 +606,7 @@ nodes: }, } - conf_yml := `WW_INTERNAL: 0` + conf_yml := `` tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") assert.NoError(t, warewulfConfErr) defer os.Remove(tempWarewulfConf.Name()) @@ -669,8 +631,7 @@ func Test_Node_Add(t *testing.T) { args: []string{"--tagadd=email=node", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 43 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit tags: @@ -679,8 +640,7 @@ nodes: n01: profiles: - default`, - outDb: `WW_INTERNAL: 43 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit tags: @@ -697,8 +657,7 @@ nodes: args: []string{"--tagadd=newtag=newval", "n01"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 43 -nodeprofiles: + inDB: `nodeprofiles: default: comment: testit tags: @@ -709,8 +668,7 @@ nodes: - default tags: email: node`, - outDb: `WW_INTERNAL: 43 -nodeprofiles: + outDb: `nodeprofiles: default: comment: testit tags: diff --git a/internal/app/wwctl/node/set/root.go b/internal/app/wwctl/node/set/root.go index 3e016e2a..6821614d 100644 --- a/internal/app/wwctl/node/set/root.go +++ b/internal/app/wwctl/node/set/root.go @@ -14,7 +14,7 @@ type variables struct { setNodeAll bool setYes bool setForce bool - nodeConf node.NodeConf + nodeConf node.Node nodeDel node.NodeConfDel nodeAdd node.NodeConfAdd } diff --git a/internal/app/wwctl/overlay/build/main.go b/internal/app/wwctl/overlay/build/main.go index 5f347bc6..819d08df 100644 --- a/internal/app/wwctl/overlay/build/main.go +++ b/internal/app/wwctl/overlay/build/main.go @@ -71,7 +71,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } - if BuildHost && controller.Warewulf.EnableHostOverlay { + if BuildHost && controller.Warewulf.EnableHostOverlay() { err := overlay.BuildHostOverlay() if err != nil { return fmt.Errorf("host overlay could not be built: %s", err) diff --git a/internal/app/wwctl/overlay/imprt/main.go b/internal/app/wwctl/overlay/imprt/main.go index 190747ce..acad75ec 100644 --- a/internal/app/wwctl/overlay/imprt/main.go +++ b/internal/app/wwctl/overlay/imprt/main.go @@ -72,7 +72,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { return fmt.Errorf("could not get node list: %s", err) } - var updateNodes []node.NodeConf + var updateNodes []node.Node for _, node := range nodes { if util.InSlice(node.SystemOverlay, overlayName) { diff --git a/internal/app/wwctl/overlay/imprt/main_test.go b/internal/app/wwctl/overlay/imprt/main_test.go index 8d8ef486..b78ea764 100644 --- a/internal/app/wwctl/overlay/imprt/main_test.go +++ b/internal/app/wwctl/overlay/imprt/main_test.go @@ -45,14 +45,11 @@ func Test_List(t *testing.T) { t.FailNow() } - inDb := `WW_INTERNAL: 45 -nodeprofiles: + inDb := `nodeprofiles: default: {} nodes: {} ` - conf_yml := ` -WW_INTERNAL: 0 - ` + conf_yml := `` conf := warewulfconf.New() err = conf.Parse([]byte(conf_yml)) diff --git a/internal/app/wwctl/overlay/show/main_test.go b/internal/app/wwctl/overlay/show/main_test.go index 451feb0a..e341b644 100644 --- a/internal/app/wwctl/overlay/show/main_test.go +++ b/internal/app/wwctl/overlay/show/main_test.go @@ -23,8 +23,7 @@ overlay name {{ .Overlay }} func Test_Overlay_List(t *testing.T) { env := testenv.New(t) - env.WriteFile(t, "etc/warewulf/warewulf.conf", `WW_INTERNAL: 43 -ipaddr: 192.168.0.1/24 + env.WriteFile(t, "etc/warewulf/warewulf.conf", `ipaddr: 192.168.0.1/24 netmask: 255.255.255.0 network: 192.168.0.0 warewulf: @@ -53,8 +52,7 @@ nfs: mount: false`) env.WriteFile(t, "etc/warewulf/nodes.conf", - `WW_INTERNAL: 45 -nodeprofiles: + `nodeprofiles: default: tags: email: admin@localhost @@ -141,8 +139,7 @@ func TestShowServerTemplate(t *testing.T) { env := testenv.New(t) env.WriteFile(t, "etc/warewulf/nodes.conf", - `WW_INTERNAL: 45 -nodeprofiles: + `nodeprofiles: default: tags: email: admin@localhost diff --git a/internal/app/wwctl/power/cycle/root_test.go b/internal/app/wwctl/power/cycle/root_test.go index 89ba118c..9e20db8f 100644 --- a/internal/app/wwctl/power/cycle/root_test.go +++ b/internal/app/wwctl/power/cycle/root_test.go @@ -15,7 +15,7 @@ func Test_PowerCycle(t *testing.T) { warewulfd.SetNoDaemon() env := testenv.New(t) defer env.RemoveAll(t) - env.WriteFile(t, "etc/warewulf/nodes.conf", `WW_INTERNAL: 43 + env.WriteFile(t, "etc/warewulf/nodes.conf", ` nodeprofiles: default: ipmi: diff --git a/internal/app/wwctl/power/off/root_test.go b/internal/app/wwctl/power/off/root_test.go index 4dd31141..5dc368c8 100644 --- a/internal/app/wwctl/power/off/root_test.go +++ b/internal/app/wwctl/power/off/root_test.go @@ -15,7 +15,7 @@ func Test_Power_Status(t *testing.T) { warewulfd.SetNoDaemon() env := testenv.New(t) defer env.RemoveAll(t) - env.WriteFile(t, "etc/warewulf/nodes.conf", `WW_INTERNAL: 43 + env.WriteFile(t, "etc/warewulf/nodes.conf", ` nodeprofiles: default: ipmi: diff --git a/internal/app/wwctl/power/on/root_test.go b/internal/app/wwctl/power/on/root_test.go index 82485f10..8ea62f84 100644 --- a/internal/app/wwctl/power/on/root_test.go +++ b/internal/app/wwctl/power/on/root_test.go @@ -15,7 +15,7 @@ func Test_Power_Status(t *testing.T) { warewulfd.SetNoDaemon() env := testenv.New(t) defer env.RemoveAll(t) - env.WriteFile(t, "etc/warewulf/nodes.conf", `WW_INTERNAL: 43 + env.WriteFile(t, "etc/warewulf/nodes.conf", ` nodeprofiles: default: ipmi: diff --git a/internal/app/wwctl/power/reset/root_test.go b/internal/app/wwctl/power/reset/root_test.go index 131f831a..8c9a0629 100644 --- a/internal/app/wwctl/power/reset/root_test.go +++ b/internal/app/wwctl/power/reset/root_test.go @@ -15,7 +15,7 @@ func Test_Power_Status(t *testing.T) { warewulfd.SetNoDaemon() env := testenv.New(t) defer env.RemoveAll(t) - env.WriteFile(t, "etc/warewulf/nodes.conf", `WW_INTERNAL: 43 + env.WriteFile(t, "etc/warewulf/nodes.conf", ` nodeprofiles: default: ipmi: diff --git a/internal/app/wwctl/power/soft/root_test.go b/internal/app/wwctl/power/soft/root_test.go index 14728692..565f2d07 100644 --- a/internal/app/wwctl/power/soft/root_test.go +++ b/internal/app/wwctl/power/soft/root_test.go @@ -15,7 +15,7 @@ func Test_Power_Status(t *testing.T) { warewulfd.SetNoDaemon() env := testenv.New(t) defer env.RemoveAll(t) - env.WriteFile(t, "etc/warewulf/nodes.conf", `WW_INTERNAL: 43 + env.WriteFile(t, "etc/warewulf/nodes.conf", ` nodeprofiles: default: ipmi: diff --git a/internal/app/wwctl/power/status/root_test.go b/internal/app/wwctl/power/status/root_test.go index e7160e3e..998dee14 100644 --- a/internal/app/wwctl/power/status/root_test.go +++ b/internal/app/wwctl/power/status/root_test.go @@ -15,7 +15,7 @@ func Test_Power_Status(t *testing.T) { warewulfd.SetNoDaemon() env := testenv.New(t) defer env.RemoveAll(t) - env.WriteFile(t, "etc/warewulf/nodes.conf", `WW_INTERNAL: 43 + env.WriteFile(t, "etc/warewulf/nodes.conf", ` nodeprofiles: default: ipmi: diff --git a/internal/app/wwctl/profile/add/main_test.go b/internal/app/wwctl/profile/add/main_test.go index d44f3c68..d0c3fb4a 100644 --- a/internal/app/wwctl/profile/add/main_test.go +++ b/internal/app/wwctl/profile/add/main_test.go @@ -20,22 +20,20 @@ func Test_Add(t *testing.T) { }{ { name: "single profile add", - args: []string{"--yes", "p01"}, + args: []string{"p01"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: p01: {} nodes: {} `, }, { name: "single profile add with netname and netdev", - args: []string{"--yes", "--netname", "primary", "--netdev", "eno3", "p02"}, + args: []string{"--netname", "primary", "--netdev", "eno3", "p02"}, wantErr: false, stdout: "", - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: p02: network devices: primary: @@ -48,8 +46,7 @@ nodes: {} warewulfd.SetNoDaemon() for _, tt := range tests { env := testenv.New(t) - env.WriteFile(t, "etc/warewulf/nodes.conf", - `WW_INTERNAL: 45`) + env.WriteFile(t, "etc/warewulf/nodes.conf", ``) var err error t.Run(tt.name, func(t *testing.T) { baseCmd := GetCommand() diff --git a/internal/app/wwctl/profile/add/root.go b/internal/app/wwctl/profile/add/root.go index f55eb7f8..cb9d30af 100644 --- a/internal/app/wwctl/profile/add/root.go +++ b/internal/app/wwctl/profile/add/root.go @@ -11,12 +11,8 @@ import ( ) type variables struct { - profileConf node.ProfileConf - nodeAdd node.NodeConfAdd - SetNetDevDel string - SetNodeAll bool - SetYes bool - SetForce bool + profileConf node.Profile + nodeAdd node.NodeConfAdd } // GetRootCommand returns the root cobra.Command for the application. @@ -59,6 +55,5 @@ func GetCommand() *cobra.Command { }); err != nil { log.Println(err) } - baseCmd.PersistentFlags().BoolVarP(&vars.SetYes, "yes", "y", false, "Set 'yes' to all questions asked") return baseCmd } diff --git a/internal/app/wwctl/profile/edit/main.go b/internal/app/wwctl/profile/edit/main.go index c3ec9437..f981ada4 100644 --- a/internal/app/wwctl/profile/edit/main.go +++ b/internal/app/wwctl/profile/edit/main.go @@ -39,7 +39,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { Output: args, } profileListMsg := apiprofile.FilteredProfiles(&filterList) - profileMap := make(map[string]*node.ProfileConf) + profileMap := make(map[string]*node.Profile) // got proper yaml back _ = yaml.Unmarshal([]byte(profileListMsg.NodeConfMapYaml), profileMap) file, err := os.CreateTemp(os.TempDir(), "ww4ProfileEdit*.yaml") @@ -51,7 +51,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { _ = file.Truncate(0) _, _ = file.Seek(0, 0) if !NoHeader { - yamlTemplate := node.UnmarshalConf(node.ProfileConf{}, []string{"tagsdel"}) + yamlTemplate := node.UnmarshalConf(node.Profile{}, []string{"tagsdel"}) _, _ = file.WriteString("#profilename:\n# " + strings.Join(yamlTemplate, "\n# ") + "\n") } _, _ = file.WriteString(profileListMsg.NodeConfMapYaml) @@ -74,7 +74,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { wwlog.Debug("Hashes are before %s and after %s\n", sum1, sum2) if sum1 != sum2 { wwlog.Debug("Profiles were modified") - modifiedProfileMap := make(map[string]*node.ProfileConf) + modifiedProfileMap := make(map[string]*node.Profile) _, _ = file.Seek(0, 0) // ignore error as only may occurs under strange circumstances buffer, _ := io.ReadAll(file) diff --git a/internal/app/wwctl/profile/list/main_test.go b/internal/app/wwctl/profile/list/main_test.go index 39362f5d..da7e6a0a 100644 --- a/internal/app/wwctl/profile/list/main_test.go +++ b/internal/app/wwctl/profile/list/main_test.go @@ -29,7 +29,7 @@ PROFILE NAME COMMENT/DESCRIPTION ------------ ------------------- default -- `, - inDb: `WW_INTERNAL: 45 + inDb: ` nodeprofiles: default: {} nodes: @@ -47,7 +47,7 @@ PROFILE NAME COMMENT/DESCRIPTION default -- test -- `, - inDb: `WW_INTERNAL: 45 + inDb: ` nodeprofiles: default: {} test: {} @@ -65,7 +65,7 @@ PROFILE NAME COMMENT/DESCRIPTION ------------ ------------------- test -- `, - inDb: `WW_INTERNAL: 45 + inDb: ` nodeprofiles: default: {} test: {} @@ -84,7 +84,7 @@ PROFILE NAME COMMENT/DESCRIPTION default -- test -- `, - inDb: `WW_INTERNAL: 45 + inDb: ` nodeprofiles: default: {} test: {} @@ -96,7 +96,7 @@ nodes: }, } - conf_yml := `WW_INTERNAL: 0` + conf_yml := `` tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") assert.NoError(t, warewulfConfErr) defer os.Remove(tempWarewulfConf.Name()) @@ -158,7 +158,7 @@ func TestListMultipleFormats(t *testing.T) { name: "single profile list yaml output", args: []string{"-y"}, output: `default: {}`, - inDb: `WW_INTERNAL: 43 + inDb: ` nodeprofiles: default: {} nodes: @@ -191,7 +191,7 @@ nodes: } } `, - inDb: `WW_INTERNAL: 43 + inDb: ` nodeprofiles: default: {} nodes: @@ -207,7 +207,7 @@ nodes: default: {} test: {} `, - inDb: `WW_INTERNAL: 43 + inDb: ` nodeprofiles: default: {} test: {} @@ -258,7 +258,7 @@ nodes: } } `, - inDb: `WW_INTERNAL: 43 + inDb: ` nodeprofiles: default: {} test: {} @@ -270,7 +270,7 @@ nodes: }, } - conf_yml := `WW_INTERNAL: 0` + conf_yml := `` tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") assert.NoError(t, warewulfConfErr) defer os.Remove(tempWarewulfConf.Name()) diff --git a/internal/app/wwctl/profile/set/main_test.go b/internal/app/wwctl/profile/set/main_test.go index 538d6f0b..ef677dd3 100644 --- a/internal/app/wwctl/profile/set/main_test.go +++ b/internal/app/wwctl/profile/set/main_test.go @@ -54,13 +54,11 @@ func Test_Set_Netdev(t *testing.T) { args: []string{"--netname=default", "--netdev=eth0", "default"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: {} nodes: {} `, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: network devices: default: @@ -74,13 +72,11 @@ func Test_Set_Netdev_and_Mask(t *testing.T) { args: []string{"--netname=default", "--netdev=eth0", "-M=255.255.255.0", "default"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: {} nodes: {} `, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: network devices: default: @@ -96,16 +92,14 @@ func Test_Set_Mask_Existing_NetDev(t *testing.T) { args: []string{"--netname=default", "-M=255.255.255.0", "default"}, wantErr: false, stdout: "", - inDB: `WW_INTERNAL: 45 -nodeprofiles: + inDB: `nodeprofiles: default: network devices: default: device: eth0 nodes: {} `, - outDb: `WW_INTERNAL: 45 -nodeprofiles: + outDb: `nodeprofiles: default: network devices: default: diff --git a/internal/app/wwctl/profile/set/root.go b/internal/app/wwctl/profile/set/root.go index 332fd0d5..b341bfd3 100644 --- a/internal/app/wwctl/profile/set/root.go +++ b/internal/app/wwctl/profile/set/root.go @@ -21,7 +21,7 @@ type variables struct { partName string diskName string fsName string - profileConf node.ProfileConf + profileConf node.Profile profileDel node.NodeConfDel profileAdd node.NodeConfAdd } diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index fb730d48..d225e7df 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -15,6 +15,7 @@ import ( "github.com/warewulf/warewulf/internal/app/wwctl/profile" "github.com/warewulf/warewulf/internal/app/wwctl/server" "github.com/warewulf/warewulf/internal/app/wwctl/ssh" + "github.com/warewulf/warewulf/internal/app/wwctl/upgrade" "github.com/warewulf/warewulf/internal/app/wwctl/version" warewulfconf "github.com/warewulf/warewulf/internal/pkg/config" "github.com/warewulf/warewulf/internal/pkg/help" @@ -60,6 +61,7 @@ func init() { rootCmd.AddCommand(ssh.GetCommand()) rootCmd.AddCommand(genconf.GetCommand()) rootCmd.AddCommand(clean.GetCommand()) + rootCmd.AddCommand(upgrade.Command) } // GetRootCommand returns the root cobra.Command for the application. @@ -89,7 +91,7 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) (err error) { } } if err != nil { - wwlog.Error("version: %s relase: %s WW_INTERNAL: %s", warewulfconf.Version, warewulfconf.Release, warewulfconf.Confversion) + wwlog.Error("version: %s relase: %s", warewulfconf.Version, warewulfconf.Release) return } err = conf.SetDynamicDefaults() diff --git a/internal/app/wwctl/upgrade/cobra.go b/internal/app/wwctl/upgrade/cobra.go new file mode 100644 index 00000000..88e1a611 --- /dev/null +++ b/internal/app/wwctl/upgrade/cobra.go @@ -0,0 +1,23 @@ +package upgrade + +import ( + "github.com/spf13/cobra" + + "github.com/warewulf/warewulf/internal/app/wwctl/upgrade/config" + "github.com/warewulf/warewulf/internal/app/wwctl/upgrade/nodes" +) + +var ( + Command = &cobra.Command{ + DisableFlagsInUseLine: true, + Use: "upgrade [OPTIONS]", + Short: "Upgrade configuration files", + Long: `Upgrade warewulf.conf or nodes.conf from a previous version of Warewulf 4 to a format +supported by the current version.`, + } +) + +func init() { + Command.AddCommand(config.Command) + Command.AddCommand(nodes.Command) +} diff --git a/internal/app/wwctl/upgrade/config/cobra.go b/internal/app/wwctl/upgrade/config/cobra.go new file mode 100644 index 00000000..a660a32f --- /dev/null +++ b/internal/app/wwctl/upgrade/config/cobra.go @@ -0,0 +1,56 @@ +package config + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/warewulf/warewulf/internal/pkg/config" + libupgrade "github.com/warewulf/warewulf/internal/pkg/upgrade" + "github.com/warewulf/warewulf/internal/pkg/util" +) + +var ( + Command = &cobra.Command{ + DisableFlagsInUseLine: true, + Use: "config [OPTIONS]", + Short: "Upgrade an existing nodes.conf", + Long: `Upgrades nodes.conf from a previous version of Warewulf 4 to a format +supported by the current version.`, + RunE: UpgradeNodesConf, + } + + inputPath string + outputPath string +) + +func init() { + Command.Flags().StringVarP(&inputPath, "input-path", "i", config.ConfigFile, "Path to a legacy nodes.conf") + Command.Flags().StringVarP(&outputPath, "output-path", "o", config.ConfigFile, "Path to write the upgraded nodes.conf to") +} + +func UpgradeNodesConf(cmd *cobra.Command, args []string) error { + data, err := os.ReadFile(inputPath) + if err != nil { + return err + } + legacy, err := libupgrade.ParseConfig(data) + if err != nil { + return err + } + upgraded := legacy.Upgrade() + if outputPath == "-" { + upgradedYaml, err := upgraded.Dump() + if err != nil { + return err + } + fmt.Print(string(upgradedYaml)) + return nil + } else { + if err := util.CopyFile(outputPath, outputPath+"-old"); err != nil { + return err + } + return upgraded.PersistToFile(outputPath) + } +} diff --git a/internal/app/wwctl/upgrade/nodes/cobra.go b/internal/app/wwctl/upgrade/nodes/cobra.go new file mode 100644 index 00000000..b652562b --- /dev/null +++ b/internal/app/wwctl/upgrade/nodes/cobra.go @@ -0,0 +1,66 @@ +package nodes + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "github.com/warewulf/warewulf/internal/pkg/node" + libupgrade "github.com/warewulf/warewulf/internal/pkg/upgrade" + "github.com/warewulf/warewulf/internal/pkg/util" +) + +var ( + Command = &cobra.Command{ + DisableFlagsInUseLine: true, + Use: "nodes [OPTIONS]", + Short: "Upgrade an existing nodes.conf", + Long: `Upgrades nodes.conf from a previous version of Warewulf 4 to a format +supported by the current version.`, + RunE: UpgradeNodesConf, + } + + addDefaults bool + replaceOverlays bool + inputPath string + outputPath string +) + +func init() { + Command.Flags().BoolVar(&addDefaults, "add-defaults", false, "Configure a default profile and set default node values") + Command.Flags().BoolVar(&replaceOverlays, "replace-overlays", false, "Replace 'wwinit' and 'generic' overlays with their split replacements") + Command.Flags().StringVarP(&inputPath, "input-path", "i", node.ConfigFile, "Path to a legacy nodes.conf") + Command.Flags().StringVarP(&outputPath, "output-path", "o", node.ConfigFile, "Path to write the upgraded nodes.conf to") + if err := Command.MarkFlagRequired("add-defaults"); err != nil { + panic(err) + } + if err := Command.MarkFlagRequired("replace-overlays"); err != nil { + panic(err) + } +} + +func UpgradeNodesConf(cmd *cobra.Command, args []string) error { + data, err := os.ReadFile(inputPath) + if err != nil { + return err + } + legacy, err := libupgrade.ParseNodes(data) + if err != nil { + return err + } + upgraded := legacy.Upgrade(addDefaults, replaceOverlays) + if outputPath == "-" { + upgradedYaml, err := upgraded.Dump() + if err != nil { + return err + } + fmt.Print(string(upgradedYaml)) + return nil + } else { + if err := util.CopyFile(outputPath, outputPath+"-old"); err != nil { + return err + } + return upgraded.PersistToFile(outputPath) + } +} diff --git a/internal/pkg/api/container/container.go b/internal/pkg/api/container/container.go index c9cc97e8..92618180 100644 --- a/internal/pkg/api/container/container.go +++ b/internal/pkg/api/container/container.go @@ -82,7 +82,7 @@ func ContainerBuild(cbp *wwapiv1.ContainerBuildParameter) (err error) { if len(containers) != 1 { return fmt.Errorf("can only set default for one container") } else { - var nodeDB node.NodeYaml + var nodeDB node.NodesYaml nodeDB, err = node.New() if err != nil { return fmt.Errorf("could not open node configuration: %s", err) @@ -238,7 +238,7 @@ func ContainerImport(cip *wwapiv1.ContainerImportParameter) (containerName strin } if cip.Default { - var nodeDB node.NodeYaml + var nodeDB node.NodesYaml nodeDB, err = node.New() if err != nil { err = fmt.Errorf("could not open node configuration: %s", err.Error()) diff --git a/internal/pkg/api/node/delete.go b/internal/pkg/api/node/delete.go index 7128c2bc..41e60917 100644 --- a/internal/pkg/api/node/delete.go +++ b/internal/pkg/api/node/delete.go @@ -15,7 +15,7 @@ import ( // NodeDelete adds nodes for management by Warewulf. func NodeDelete(ndp *wwapiv1.NodeDeleteParameter) (err error) { - var nodeList []node.NodeConf + var nodeList []node.Node nodeList, err = NodeDeleteParameterCheck(ndp, false) if err != nil { return @@ -55,7 +55,7 @@ func NodeDelete(ndp *wwapiv1.NodeDeleteParameter) (err error) { // NodeDeleteParameterCheck does error checking on NodeDeleteParameter. // Output to the console if console is true. // Returns the nodes to delete. -func NodeDeleteParameterCheck(ndp *wwapiv1.NodeDeleteParameter, console bool) (nodeList []node.NodeConf, err error) { +func NodeDeleteParameterCheck(ndp *wwapiv1.NodeDeleteParameter, console bool) (nodeList []node.Node, err error) { if ndp == nil { err = fmt.Errorf("NodeDeleteParameter is nil") diff --git a/internal/pkg/api/node/edit.go b/internal/pkg/api/node/edit.go index 79324c59..dc988bde 100644 --- a/internal/pkg/api/node/edit.go +++ b/internal/pkg/api/node/edit.go @@ -37,7 +37,7 @@ func NodeAddFromYaml(nodeList *wwapiv1.NodeYaml) (err error) { if err != nil { return fmt.Errorf("could not open NodeDB: %w", err) } - nodeMap := make(map[string]*node.NodeConf) + nodeMap := make(map[string]*node.Node) err = yaml.Unmarshal([]byte(nodeList.NodeConfMapYaml), nodeMap) if err != nil { return fmt.Errorf("could not unmarshal Yaml: %w", err) diff --git a/internal/pkg/api/node/set.go b/internal/pkg/api/node/set.go index e6f4c142..3dff91aa 100644 --- a/internal/pkg/api/node/set.go +++ b/internal/pkg/api/node/set.go @@ -17,7 +17,7 @@ func NodeSet(set *wwapiv1.ConfSetParameter) (err error) { if set == nil { return fmt.Errorf("NodeSetParameter is nil") } - var nodeDB node.NodeYaml + var nodeDB node.NodesYaml nodeDB, _, err = NodeSetParameterCheck(set) if err != nil { return err @@ -35,7 +35,7 @@ func NodeSet(set *wwapiv1.ConfSetParameter) (err error) { NodeSetParameterCheck does error checking and returns a modified NodeYml which than can be persisted */ -func NodeSetParameterCheck(set *wwapiv1.ConfSetParameter) (nodeDB node.NodeYaml, count uint, err error) { +func NodeSetParameterCheck(set *wwapiv1.ConfSetParameter) (nodeDB node.NodesYaml, count uint, err error) { nodeDB, err = node.New() if err != nil { wwlog.Error("Could not open configuration: %s", err) @@ -60,7 +60,7 @@ func NodeSetParameterCheck(set *wwapiv1.ConfSetParameter) (nodeDB node.NodeYaml, for _, nId := range set.ConfList { if util.InSlice(set.ConfList, nId) { wwlog.Debug("evaluating node: %s", nId) - var nodePtr *node.NodeConf + var nodePtr *node.Node nodePtr, err = nodeDB.GetNodeOnlyPtr(nId) if err != nil { wwlog.Warn("invalid node: %s", nId) diff --git a/internal/pkg/api/profile/delete.go b/internal/pkg/api/profile/delete.go index d0dc4cc4..b219da33 100644 --- a/internal/pkg/api/profile/delete.go +++ b/internal/pkg/api/profile/delete.go @@ -40,7 +40,7 @@ func ProfileDelete(ndp *wwapiv1.NodeDeleteParameter) (err error) { // ProfileDeleteParameterCheck does error checking on ProfileDeleteParameter. // Output to the console if console is true. // Returns the profiles to delete. -func ProfileDeleteParameterCheck(ndp *wwapiv1.NodeDeleteParameter, console bool) (nodeDB node.NodeYaml, profileList []string, err error) { +func ProfileDeleteParameterCheck(ndp *wwapiv1.NodeDeleteParameter, console bool) (nodeDB node.NodesYaml, profileList []string, err error) { if ndp == nil { err = fmt.Errorf("profileDeleteParameter is nil") diff --git a/internal/pkg/api/profile/edit.go b/internal/pkg/api/profile/edit.go index 059a3272..c8a384a0 100644 --- a/internal/pkg/api/profile/edit.go +++ b/internal/pkg/api/profile/edit.go @@ -40,7 +40,7 @@ func ProfileAddFromYaml(nodeList *wwapiv1.NodeAddParameter) (err error) { return fmt.Errorf("got wrong hash, not modifying profile database") } - profileMap := make(map[string]*node.ProfileConf) + profileMap := make(map[string]*node.Profile) err = yaml.Unmarshal([]byte(nodeList.NodeConfYaml), profileMap) if err != nil { return fmt.Errorf("couldn't unmarshall Yaml: %w", err) diff --git a/internal/pkg/api/profile/list.go b/internal/pkg/api/profile/list.go index 5d027943..be8073eb 100644 --- a/internal/pkg/api/profile/list.go +++ b/internal/pkg/api/profile/list.go @@ -38,7 +38,7 @@ func ProfileList(ShowOpt *wwapiv1.GetProfileList) (profileList wwapiv1.ProfileLi } } } else if ShowOpt.ShowYaml { - profileMap := make(map[string]node.ProfileConf) + profileMap := make(map[string]node.Profile) for _, profile := range profiles { profileMap[profile.Id()] = profile } @@ -46,7 +46,7 @@ func ProfileList(ShowOpt *wwapiv1.GetProfileList) (profileList wwapiv1.ProfileLi buf, _ := yaml.Marshal(profileMap) profileList.Output = append(profileList.Output, string(buf)) } else if ShowOpt.ShowJson { - profileMap := make(map[string]node.ProfileConf) + profileMap := make(map[string]node.Profile) for _, profile := range profiles { profileMap[profile.Id()] = profile } diff --git a/internal/pkg/api/profile/set.go b/internal/pkg/api/profile/set.go index 97c25c51..e20cd55c 100644 --- a/internal/pkg/api/profile/set.go +++ b/internal/pkg/api/profile/set.go @@ -35,7 +35,7 @@ func ProfileSet(set *wwapiv1.ConfSetParameter) (err error) { NodeSetParameterCheck does error checking and returns a modified NodeYml which than can be persisted */ -func ProfileSetParameterCheck(set *wwapiv1.ConfSetParameter) (nodeDB node.NodeYaml, count uint, err error) { +func ProfileSetParameterCheck(set *wwapiv1.ConfSetParameter) (nodeDB node.NodesYaml, count uint, err error) { nodeDB, err = node.New() if err != nil { wwlog.Error("Could not open configuration: %s", err) @@ -60,7 +60,7 @@ func ProfileSetParameterCheck(set *wwapiv1.ConfSetParameter) (nodeDB node.NodeYa for _, profileId := range set.ConfList { if util.InSlice(set.ConfList, profileId) { wwlog.Verbose("evaluating profile: %s", profileId) - var profilePtr *node.ProfileConf + var profilePtr *node.Profile profilePtr, err = nodeDB.GetProfilePtr(profileId) if err != nil { wwlog.Warn("invalid profile: %s", profileId) diff --git a/internal/pkg/config/buildconfig.go.in b/internal/pkg/config/buildconfig.go.in index 560afccd..d92a5128 100644 --- a/internal/pkg/config/buildconfig.go.in +++ b/internal/pkg/config/buildconfig.go.in @@ -2,6 +2,8 @@ package config import ( "path" + + "github.com/warewulf/warewulf/internal/pkg/util" ) var ConfigFile = "@SYSCONFDIR@/warewulf/warewulf.conf" @@ -24,28 +26,49 @@ type BuildConfig struct { const Version = "@VERSION@" const Release = "@RELEASE@" -// must be set .in file so that its available for tests -const Confversion = "45" - type TFTPConf struct { - Enabled bool `yaml:"enabled" default:"true"` - TftpRoot string `yaml:"tftproot" default:"@TFTPDIR@"` - SystemdName string `yaml:"systemd name" default:"tftp"` + EnabledP *bool `yaml:"enabled" default:"true"` + TftpRoot string `yaml:"tftproot,omitempty" default:"@TFTPDIR@"` + SystemdName string `yaml:"systemd name,omitempty" default:"tftp"` - IpxeBinaries map[string]string `yaml:"ipxe" default:"{\"00:09\": \"ipxe-snponly-x86_64.efi\",\"00:00\": \"undionly.kpxe\",\"00:0B\": \"arm64-efi/snponly.efi\",\"00:07\": \"ipxe-snponly-x86_64.efi\"}"` + IpxeBinaries map[string]string `yaml:"ipxe,omitempty" default:"{\"00:09\": \"ipxe-snponly-x86_64.efi\",\"00:00\": \"undionly.kpxe\",\"00:0B\": \"arm64-efi/snponly.efi\",\"00:07\": \"ipxe-snponly-x86_64.efi\"}"` +} + +func (this TFTPConf) Enabled() bool { + return util.BoolP(this.EnabledP) } // WarewulfConf adds additional Warewulf-specific configuration to // BaseConf. type WarewulfConf struct { - Port int `yaml:"port" default:"9873"` - Secure bool `yaml:"secure" default:"true"` - UpdateInterval int `yaml:"update interval" default:"60"` - AutobuildOverlays bool `yaml:"autobuild overlays" default:"true"` - EnableHostOverlay bool `yaml:"host overlay" default:"true"` - Syslog bool `yaml:"syslog" default:"false"` - DataStore string `yaml:"datastore" default:"@DATADIR@"` - GrubBoot bool `yaml:"grubboot" default:"false"` + Port int `yaml:"port,omitempty" default:"9873"` + SecureP *bool `yaml:"secure,omitempty" default:"true"` + UpdateInterval int `yaml:"update interval,omitempty" default:"60"` + AutobuildOverlaysP *bool `yaml:"autobuild overlays,omitempty" default:"true"` + EnableHostOverlayP *bool `yaml:"host overlay,omitempty" default:"true"` + SyslogP *bool `yaml:"syslog,omitempty" default:"false"` + DataStore string `yaml:"datastore,omitempty" default:"@DATADIR@"` + GrubBootP *bool `yaml:"grubboot,omitempty" default:"false"` +} + +func (this WarewulfConf) Secure() bool { + return util.BoolP(this.SecureP) +} + +func (this WarewulfConf) AutobuildOverlays() bool { + return util.BoolP(this.AutobuildOverlaysP) +} + +func (this WarewulfConf) EnableHostOverlay() bool { + return util.BoolP(this.EnableHostOverlayP) +} + +func (this WarewulfConf) Syslog() bool { + return util.BoolP(this.SyslogP) +} + +func (this WarewulfConf) GrubBoot() bool { + return util.BoolP(this.GrubBootP) } func (paths BuildConfig) OciBlobCachedir() string { diff --git a/internal/pkg/config/dhcp.go b/internal/pkg/config/dhcp.go index b1b8790e..56c70986 100644 --- a/internal/pkg/config/dhcp.go +++ b/internal/pkg/config/dhcp.go @@ -1,11 +1,19 @@ package config +import ( + "github.com/warewulf/warewulf/internal/pkg/util" +) + // DHCPConf represents the configuration for the DHCP service that // Warewulf will configure. type DHCPConf struct { - Enabled bool `yaml:"enabled" default:"true"` - Template string `yaml:"template" default:"default"` + EnabledP *bool `yaml:"enabled,omitempty" default:"true"` + Template string `yaml:"template,omitempty" default:"default"` RangeStart string `yaml:"range start,omitempty"` RangeEnd string `yaml:"range end,omitempty"` - SystemdName string `yaml:"systemd name" default:"dhcpd"` + SystemdName string `yaml:"systemd name,omitempty" default:"dhcpd"` +} + +func (this DHCPConf) Enabled() bool { + return util.BoolP(this.EnabledP) } diff --git a/internal/pkg/config/mounts.go b/internal/pkg/config/mounts.go index 305fbad4..f228a4e5 100644 --- a/internal/pkg/config/mounts.go +++ b/internal/pkg/config/mounts.go @@ -1,11 +1,23 @@ package config +import ( + "github.com/warewulf/warewulf/internal/pkg/util" +) + // A MountEntry represents a bind mount that is applied to a container // during exec and shell. type MountEntry struct { - Source string `yaml:"source"` - Dest string `yaml:"dest,omitempty"` - ReadOnly bool `yaml:"readonly,omitempty"` - Options string `yaml:"options,omitempty"` // ignored at the moment - Copy bool `yaml:"copy,omitempty"` // temporarily copy the file into the container + Source string `yaml:"source"` + Dest string `yaml:"dest,omitempty"` + ReadOnlyP *bool `yaml:"readonly,omitempty"` + Options string `yaml:"options,omitempty"` // ignored at the moment + CopyP *bool `yaml:"copy,omitempty"` // temporarily copy the file into the container +} + +func (this MountEntry) ReadOnly() bool { + return util.BoolP(this.ReadOnlyP) +} + +func (this MountEntry) Copy() bool { + return util.BoolP(this.CopyP) } diff --git a/internal/pkg/config/nfs.go b/internal/pkg/config/nfs.go index c7ad986e..ca6fc8b3 100644 --- a/internal/pkg/config/nfs.go +++ b/internal/pkg/config/nfs.go @@ -2,23 +2,33 @@ package config import ( "github.com/creasty/defaults" + + "github.com/warewulf/warewulf/internal/pkg/util" ) // NFSConf represents the NFS configuration that will be used by // Warewulf to generate exports on the server and mounts on compute // nodes. type NFSConf struct { - Enabled bool `yaml:"enabled" default:"true"` - ExportsExtended []*NFSExportConf `yaml:"export paths" default:"[]"` - SystemdName string `yaml:"systemd name" default:"nfsd"` + EnabledP *bool `yaml:"enabled,omitempty" default:"true"` + ExportsExtended []*NFSExportConf `yaml:"export paths,omitempty" default:"[]"` + SystemdName string `yaml:"systemd name,omitempty" default:"nfsd"` +} + +func (this NFSConf) Enabled() bool { + return util.BoolP(this.EnabledP) } // An NFSExportConf reprents a single NFS export / mount. type NFSExportConf struct { Path string `yaml:"path" default:"/dev/null"` - ExportOptions string `default:"rw,sync,no_subtree_check" yaml:"export options"` - MountOptions string `default:"defaults" yaml:"mount options"` - Mount bool `default:"true" yaml:"mount"` + ExportOptions string `yaml:"export options,omitempty" default:"rw,sync,no_subtree_check"` + MountOptions string `yaml:"mount options,omitempty" default:"defaults"` + MountP *bool `yaml:"mount,omitempty" default:"true"` +} + +func (this NFSExportConf) Mount() bool { + return util.BoolP(this.MountP) } // Implements the Unmarshal interface for NFSConf to set default diff --git a/internal/pkg/config/root.go b/internal/pkg/config/root.go index 94fae599..8655b901 100644 --- a/internal/pkg/config/root.go +++ b/internal/pkg/config/root.go @@ -7,6 +7,7 @@ package config import ( + "bytes" "fmt" "net" "net/netip" @@ -20,36 +21,35 @@ import ( "gopkg.in/yaml.v3" ) -var cachedConf RootConf +var cachedConf WarewulfYaml -// RootConf is the main Warewulf configuration structure. It stores +// WarewulfYaml is the main Warewulf configuration structure. It stores // some information about the Warewulf server locally, and has // [WarewulfConf], [DHCPConf], [TFTPConf], and [NFSConf] sub-sections. -type RootConf struct { - WWInternal int `yaml:"WW_INTERNAL"` +type WarewulfYaml struct { Comment string `yaml:"comment,omitempty"` - Ipaddr string `yaml:"ipaddr"` + Ipaddr string `yaml:"ipaddr,omitempty"` Ipaddr6 string `yaml:"ipaddr6,omitempty"` - Netmask string `yaml:"netmask"` + Netmask string `yaml:"netmask,omitempty"` Network string `yaml:"network,omitempty"` Ipv6net string `yaml:"ipv6net,omitempty"` Fqdn string `yaml:"fqdn,omitempty"` - Warewulf *WarewulfConf `yaml:"warewulf"` - DHCP *DHCPConf `yaml:"dhcp"` - TFTP *TFTPConf `yaml:"tftp"` - NFS *NFSConf `yaml:"nfs"` + Warewulf *WarewulfConf `yaml:"warewulf,omitempty"` + DHCP *DHCPConf `yaml:"dhcp,omitempty"` + TFTP *TFTPConf `yaml:"tftp,omitempty"` + NFS *NFSConf `yaml:"nfs,omitempty"` SSH *SSHConf `yaml:"ssh,omitempty"` - MountsContainer []*MountEntry `yaml:"container mounts" default:"[{\"source\": \"/etc/resolv.conf\", \"dest\": \"/etc/resolv.conf\"}]"` - Paths *BuildConfig `yaml:"paths"` + MountsContainer []*MountEntry `yaml:"container mounts,omitempty" default:"[{\"source\": \"/etc/resolv.conf\", \"dest\": \"/etc/resolv.conf\"}]"` + Paths *BuildConfig `yaml:"paths,omitempty"` WWClient *WWClientConf `yaml:"wwclient,omitempty"` warewulfconf string } -// New caches and returns a new [RootConf] initialized with empty +// New caches and returns a new [WarewulfYaml] initialized with empty // values, clearing replacing any previously cached value. -func New() *RootConf { - cachedConf = RootConf{} +func New() *WarewulfYaml { + cachedConf = WarewulfYaml{} cachedConf.warewulfconf = "" cachedConf.Warewulf = new(WarewulfConf) cachedConf.DHCP = new(DHCPConf) @@ -63,9 +63,9 @@ func New() *RootConf { return &cachedConf } -// Get returns a previously cached [RootConf] if it exists, or returns -// a new RootConf. -func Get() *RootConf { +// Get returns a previously cached [WarewulfYaml] if it exists, or returns +// a new WarewulfYaml. +func Get() *WarewulfYaml { // NOTE: This function can be called before any log level is set // so using wwlog.Verbose or wwlog.Debug won't work if reflect.ValueOf(cachedConf).IsZero() { @@ -74,9 +74,9 @@ func Get() *RootConf { return &cachedConf } -// Read populates [RootConf] with the values from a configuration +// Read populates [WarewulfYaml] with the values from a configuration // file. -func (conf *RootConf) Read(confFileName string) error { +func (conf *WarewulfYaml) Read(confFileName string) error { wwlog.Debug("Reading warewulf.conf from: %s", confFileName) conf.warewulfconf = confFileName if data, err := os.ReadFile(confFileName); err != nil { @@ -88,8 +88,8 @@ func (conf *RootConf) Read(confFileName string) error { } } -// Parse populates [RootConf] with the values from a yaml document. -func (conf *RootConf) Parse(data []byte) error { +// Parse populates [WarewulfYaml] with the values from a yaml document. +func (conf *WarewulfYaml) Parse(data []byte) error { // ipxe binaries are merged not overwritten, store defaults separate defIpxe := make(map[string]string) for k, v := range conf.TFTP.IpxeBinaries { @@ -114,13 +114,12 @@ func (conf *RootConf) Parse(data []byte) error { conf.Netmask = fmt.Sprintf("%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]) } } - return nil } -// SetDynamicDefaults populates [RootConf] with plausible defaults for +// SetDynamicDefaults populates [WarewulfYaml] with plausible defaults for // the runtime environment. -func (conf *RootConf) SetDynamicDefaults() (err error) { +func (conf *WarewulfYaml) SetDynamicDefaults() (err error) { if conf.Ipaddr == "" || conf.Netmask == "" || conf.Network == "" { var mask net.IPMask var network *net.IPNet @@ -198,12 +197,40 @@ func (conf *RootConf) SetDynamicDefaults() (err error) { return } -// InitializedFromFile returns true if [RootConf] memory was read from +// InitializedFromFile returns true if [WarewulfYaml] memory was read from // a file, or false otherwise. -func (conf *RootConf) InitializedFromFile() bool { +func (conf *WarewulfYaml) InitializedFromFile() bool { return conf.warewulfconf != "" } -func (conf *RootConf) GetWarewulfConf() string { +func (conf *WarewulfYaml) GetWarewulfConf() string { return conf.warewulfconf } + +func (config *WarewulfYaml) Dump() ([]byte, error) { + var buf bytes.Buffer + yamlEncoder := yaml.NewEncoder(&buf) + yamlEncoder.SetIndent(2) + err := yamlEncoder.Encode(config) + return buf.Bytes(), err +} + +func (config *WarewulfYaml) PersistToFile(configFile string) error { + out, dumpErr := config.Dump() + if dumpErr != nil { + wwlog.Error("%s", dumpErr) + return dumpErr + } + file, err := os.OpenFile(configFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) + if err != nil { + wwlog.Error("%s", err) + return err + } + defer file.Close() + _, err = file.WriteString(string(out)) + if err != nil { + return err + } + wwlog.Debug("persisted: %s", configFile) + return nil +} diff --git a/internal/pkg/config/root_test.go b/internal/pkg/config/root_test.go index 38104ffd..9b466b29 100644 --- a/internal/pkg/config/root_test.go +++ b/internal/pkg/config/root_test.go @@ -7,23 +7,23 @@ import ( "github.com/stretchr/testify/assert" ) -func TestDefaultRootConf(t *testing.T) { +func TestDefaultWarewulfYaml(t *testing.T) { conf := New() assert.Equal(t, 9873, conf.Warewulf.Port) - assert.True(t, conf.Warewulf.Secure) + assert.True(t, conf.Warewulf.Secure()) assert.Equal(t, 60, conf.Warewulf.UpdateInterval) - assert.True(t, conf.Warewulf.AutobuildOverlays) - assert.True(t, conf.Warewulf.EnableHostOverlay) - assert.False(t, conf.Warewulf.Syslog) + assert.True(t, conf.Warewulf.AutobuildOverlays()) + assert.True(t, conf.Warewulf.EnableHostOverlay()) + assert.False(t, conf.Warewulf.Syslog()) - assert.True(t, conf.DHCP.Enabled) + assert.True(t, conf.DHCP.Enabled()) assert.Equal(t, "default", conf.DHCP.Template) assert.Empty(t, conf.DHCP.RangeStart) assert.Empty(t, conf.DHCP.RangeEnd) assert.Equal(t, "dhcpd", conf.DHCP.SystemdName) - assert.True(t, conf.TFTP.Enabled) + assert.True(t, conf.TFTP.Enabled()) assert.NotEmpty(t, conf.TFTP.TftpRoot) assert.Equal(t, "tftp", conf.TFTP.SystemdName) assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:00"]) @@ -31,13 +31,13 @@ func TestDefaultRootConf(t *testing.T) { assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:09"]) assert.NotEmpty(t, conf.TFTP.IpxeBinaries["00:0B"]) - assert.True(t, conf.NFS.Enabled) + assert.True(t, conf.NFS.Enabled()) assert.Empty(t, conf.NFS.ExportsExtended) assert.Equal(t, "nfsd", conf.NFS.SystemdName) assert.Equal(t, "/etc/resolv.conf", conf.MountsContainer[0].Source) assert.Equal(t, "/etc/resolv.conf", conf.MountsContainer[0].Dest) - assert.False(t, conf.MountsContainer[0].ReadOnly) + assert.False(t, conf.MountsContainer[0].ReadOnly()) assert.Empty(t, conf.MountsContainer[0].Options) assert.NotEmpty(t, conf.Paths.Bindir) @@ -54,7 +54,7 @@ func TestDefaultRootConf(t *testing.T) { } func TestInitializedFromFile(t *testing.T) { - example_warewulf_conf := "WW_INTERNAL: 45" + example_warewulf_conf := "" tempWarewulfConf, warewulfConfErr := os.CreateTemp("", "warewulf.conf-") assert.NoError(t, warewulfConfErr) defer os.Remove(tempWarewulfConf.Name()) @@ -69,8 +69,8 @@ func TestInitializedFromFile(t *testing.T) { assert.Equal(t, conf.GetWarewulfConf(), tempWarewulfConf.Name()) } -func TestExampleRootConf(t *testing.T) { - example_warewulf_conf := `WW_INTERNAL: 45 +func TestExampleWarewulfYaml(t *testing.T) { + example_warewulf_conf := ` ipaddr: 192.168.200.1 netmask: 255.255.255.0 network: 192.168.200.0 @@ -121,34 +121,34 @@ container mounts: assert.Equal(t, "192.168.200.0", conf.Network) assert.Equal(t, 9873, conf.Warewulf.Port) - assert.False(t, conf.Warewulf.Secure) + assert.False(t, conf.Warewulf.Secure()) assert.Equal(t, 60, conf.Warewulf.UpdateInterval) - assert.True(t, conf.Warewulf.AutobuildOverlays) - assert.True(t, conf.Warewulf.EnableHostOverlay) - assert.False(t, conf.Warewulf.Syslog) + assert.True(t, conf.Warewulf.AutobuildOverlays()) + assert.True(t, conf.Warewulf.EnableHostOverlay()) + assert.False(t, conf.Warewulf.Syslog()) - assert.True(t, conf.DHCP.Enabled) + assert.True(t, conf.DHCP.Enabled()) assert.Equal(t, "192.168.200.50", conf.DHCP.RangeStart) assert.Equal(t, "192.168.200.99", conf.DHCP.RangeEnd) assert.Equal(t, "dhcpd", conf.DHCP.SystemdName) - assert.True(t, conf.TFTP.Enabled) + assert.True(t, conf.TFTP.Enabled()) assert.Equal(t, "tftp", conf.TFTP.SystemdName) - assert.True(t, conf.NFS.Enabled) + assert.True(t, conf.NFS.Enabled()) assert.Equal(t, "/home", conf.NFS.ExportsExtended[0].Path) assert.Equal(t, "rw,sync", conf.NFS.ExportsExtended[0].ExportOptions) assert.Equal(t, "defaults", conf.NFS.ExportsExtended[0].MountOptions) - assert.True(t, conf.NFS.ExportsExtended[0].Mount) + assert.True(t, conf.NFS.ExportsExtended[0].Mount()) assert.Equal(t, "/opt", conf.NFS.ExportsExtended[1].Path) assert.Equal(t, "ro,sync,no_root_squash", conf.NFS.ExportsExtended[1].ExportOptions) assert.Equal(t, "defaults", conf.NFS.ExportsExtended[1].MountOptions) - assert.False(t, conf.NFS.ExportsExtended[1].Mount) + assert.False(t, conf.NFS.ExportsExtended[1].Mount()) assert.Equal(t, "nfs-server", conf.NFS.SystemdName) assert.Equal(t, "/etc/resolv.conf", conf.MountsContainer[0].Source) assert.Equal(t, "/etc/resolv.conf", conf.MountsContainer[0].Dest) - assert.True(t, conf.MountsContainer[0].ReadOnly) + assert.True(t, conf.MountsContainer[0].ReadOnly()) } func TestCache(t *testing.T) { diff --git a/internal/pkg/config/ssh.go b/internal/pkg/config/ssh.go index a9abe4dc..5e08aac7 100644 --- a/internal/pkg/config/ssh.go +++ b/internal/pkg/config/ssh.go @@ -1,5 +1,5 @@ package config type SSHConf struct { - KeyTypes []string `yaml:"key types" default:"[\"rsa\",\"dsa\",\"ecdsa\",\"ed25519\"]"` + KeyTypes []string `yaml:"key types,omitempty" default:"[\"rsa\",\"dsa\",\"ecdsa\",\"ed25519\"]"` } diff --git a/internal/pkg/config/wwclient.go b/internal/pkg/config/wwclient.go index 13993be6..52d92d04 100644 --- a/internal/pkg/config/wwclient.go +++ b/internal/pkg/config/wwclient.go @@ -1,5 +1,5 @@ package config type WWClientConf struct { - Port uint16 `yaml:"port" default:"0"` + Port uint16 `yaml:"port,omitempty" default:"0"` } diff --git a/internal/pkg/configure/dhcp.go b/internal/pkg/configure/dhcp.go index 85fd2c03..95f6c816 100644 --- a/internal/pkg/configure/dhcp.go +++ b/internal/pkg/configure/dhcp.go @@ -17,7 +17,7 @@ func DHCP() (err error) { controller := warewulfconf.Get() - if !controller.DHCP.Enabled { + if !controller.DHCP.Enabled() { wwlog.Warn("This system is not configured as a Warewulf DHCP controller") return } @@ -29,7 +29,7 @@ func DHCP() (err error) { if controller.DHCP.RangeEnd == "" { return fmt.Errorf("configuration is not defined: `dhcpd range end`") } - if controller.Warewulf.EnableHostOverlay { + if controller.Warewulf.EnableHostOverlay() { err = overlay.BuildHostOverlay() if err != nil { wwlog.Warn("host overlay could not be built: %s", err) diff --git a/internal/pkg/configure/nfs.go b/internal/pkg/configure/nfs.go index d68916d3..62a9dd1a 100644 --- a/internal/pkg/configure/nfs.go +++ b/internal/pkg/configure/nfs.go @@ -17,8 +17,8 @@ func NFS() error { controller := warewulfconf.Get() - if controller.NFS.Enabled { - if controller.Warewulf.EnableHostOverlay { + if controller.NFS.Enabled() { + if controller.Warewulf.EnableHostOverlay() { err := overlay.BuildHostOverlay() if err != nil { wwlog.Warn("host overlay could not be built: %s", err) diff --git a/internal/pkg/configure/tftp.go b/internal/pkg/configure/tftp.go index c693cb5d..41c8d556 100644 --- a/internal/pkg/configure/tftp.go +++ b/internal/pkg/configure/tftp.go @@ -21,7 +21,7 @@ func TFTP() (err error) { return } - if controller.Warewulf.GrubBoot { + if controller.Warewulf.GrubBoot() { err := warewulfd.CopyShimGrub() if err != nil { wwlog.Warn("error when copying shim/grub binaries: %s", err) @@ -43,7 +43,7 @@ func TFTP() (err error) { } } } - if !controller.TFTP.Enabled { + if !controller.TFTP.Enabled() { wwlog.Warn("Warewulf does not auto start TFTP services due to disable by warewulf.conf") return nil } diff --git a/internal/pkg/container/mountpoints.go b/internal/pkg/container/mountpoints.go index 961513fb..07c7b57e 100644 --- a/internal/pkg/container/mountpoints.go +++ b/internal/pkg/container/mountpoints.go @@ -30,10 +30,10 @@ func InitMountPnts(binds []string) (mounts []*warewulfconf.MountEntry) { } } mntPnt := warewulfconf.MountEntry{ - Source: bind[0], - Dest: dest, - ReadOnly: readonly, - Copy: copy_, + Source: bind[0], + Dest: dest, + ReadOnlyP: &readonly, + CopyP: ©_, } mounts = append(mounts, &mntPnt) } diff --git a/internal/pkg/node/checkconf.go b/internal/pkg/node/checkconf.go index 623f078f..e80ecd01 100644 --- a/internal/pkg/node/checkconf.go +++ b/internal/pkg/node/checkconf.go @@ -14,13 +14,13 @@ import ( /* Checks if for NodeConf all values can be parsed according to their type. */ -func (nodeConf *NodeConf) Check() (err error) { +func (nodeConf *Node) Check() (err error) { nodeInfoType := reflect.TypeOf(nodeConf) nodeInfoVal := reflect.ValueOf(nodeConf) return check(nodeInfoType, nodeInfoVal) } -func (profileConf *ProfileConf) Check() (err error) { +func (profileConf *Profile) Check() (err error) { profileInfoType := reflect.TypeOf(profileConf) profileInfoVal := reflect.ValueOf(profileConf) return check(profileInfoType, profileInfoVal) @@ -49,8 +49,8 @@ func check(infoType reflect.Type, infoVal reflect.Value) (err error) { } } } - } else if infoType.Elem().Field(i).Type == reflect.TypeOf(map[string]*NetDevs(nil)) { - netMap := infoVal.Elem().Field(i).Interface().(map[string]*NetDevs) + } else if infoType.Elem().Field(i).Type == reflect.TypeOf(map[string]*NetDev(nil)) { + netMap := infoVal.Elem().Field(i).Interface().(map[string]*NetDev) for _, val := range netMap { netType := reflect.TypeOf(val) netVal := reflect.ValueOf(val) diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 88fec481..7e00ed7a 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -18,18 +18,21 @@ var ( ConfigFile string ) -/* -Creates a new nodeDb object from the on-disk configuration -*/ -func New() (NodeYaml, error) { +func init() { conf := warewulfconf.Get() if ConfigFile == "" { ConfigFile = path.Join(conf.Paths.Sysconfdir, "warewulf/nodes.conf") } +} + +/* +Creates a new nodeDb object from the on-disk configuration +*/ +func New() (NodesYaml, error) { wwlog.Verbose("Opening node configuration file: %s", ConfigFile) data, err := os.ReadFile(ConfigFile) if err != nil { - return NodeYaml{}, err + return NodesYaml{}, err } return Parse(data) } @@ -38,18 +41,18 @@ func New() (NodeYaml, error) { // document. Passes any errors return from yaml.Unmarshal. Returns an // error if any parsed value is not of a valid type for the given // parameter. -func Parse(data []byte) (nodeList NodeYaml, err error) { +func Parse(data []byte) (nodeList NodesYaml, err error) { wwlog.Debug("Unmarshaling the node configuration") err = yaml.Unmarshal(data, &nodeList) if err != nil { return nodeList, err } wwlog.Debug("Checking nodes for types") - if nodeList.nodes == nil { - nodeList.nodes = map[string]*NodeConf{} + if nodeList.Nodes == nil { + nodeList.Nodes = map[string]*Node{} } - if nodeList.nodeProfiles == nil { - nodeList.nodeProfiles = map[string]*ProfileConf{} + if nodeList.NodeProfiles == nil { + nodeList.NodeProfiles = map[string]*Profile{} } wwlog.Debug("returning node object") return nodeList, nil @@ -58,8 +61,8 @@ func Parse(data []byte) (nodeList NodeYaml, err error) { /* Get a node with its merged in nodes */ -func (config *NodeYaml) GetNode(id string) (node NodeConf, err error) { - if _, ok := config.nodes[id]; !ok { +func (config *NodesYaml) GetNode(id string) (node Node, err error) { + if _, ok := config.Nodes[id]; !ok { return node, ErrNotFound } node = EmptyNode() @@ -68,7 +71,7 @@ func (config *NodeYaml) GetNode(id string) (node NodeConf, err error) { var buf bytes.Buffer enc := gob.NewEncoder(&buf) dec := gob.NewDecoder(&buf) - err = enc.Encode(config.nodes[id]) + err = enc.Encode(config.Nodes[id]) if err != nil { return node, err } @@ -76,13 +79,13 @@ func (config *NodeYaml) GetNode(id string) (node NodeConf, err error) { if err != nil { return node, err } - for _, p := range cleanList(config.nodes[id].Profiles) { + for _, p := range cleanList(config.Nodes[id].Profiles) { includedProfile, err := config.GetProfile(p) if err != nil { wwlog.Warn("profile not found: %s", p) continue } - err = mergo.Merge(&node.ProfileConf, includedProfile, mergo.WithAppendSlice) + err = mergo.Merge(&node.Profile, includedProfile, mergo.WithAppendSlice) if err != nil { return node, err } @@ -112,9 +115,9 @@ func (config *NodeYaml) GetNode(id string) (node NodeConf, err error) { Return the node with the id string without the merged in nodes, return ErrNotFound otherwise */ -func (config *NodeYaml) GetNodeOnly(id string) (node NodeConf, err error) { +func (config *NodesYaml) GetNodeOnly(id string) (node Node, err error) { node = EmptyNode() - if found, ok := config.nodes[id]; ok { + if found, ok := config.Nodes[id]; ok { return *found, nil } return node, ErrNotFound @@ -124,9 +127,9 @@ func (config *NodeYaml) GetNodeOnly(id string) (node NodeConf, err error) { Return pointer to the node with the id string without the merged in nodes, return ErrMotFound otherwise */ -func (config *NodeYaml) GetNodeOnlyPtr(id string) (*NodeConf, error) { +func (config *NodesYaml) GetNodeOnlyPtr(id string) (*Node, error) { node := EmptyNode() - if found, ok := config.nodes[id]; ok { + if found, ok := config.Nodes[id]; ok { return found, nil } return &node, ErrNotFound @@ -135,8 +138,8 @@ func (config *NodeYaml) GetNodeOnlyPtr(id string) (*NodeConf, error) { /* Get the profile with id, return ErrNotFound otherwise */ -func (config *NodeYaml) GetProfile(id string) (profile ProfileConf, err error) { - if found, ok := config.nodeProfiles[id]; ok { +func (config *NodesYaml) GetProfile(id string) (profile Profile, err error) { + if found, ok := config.NodeProfiles[id]; ok { found.id = id return *found, nil } @@ -146,8 +149,8 @@ func (config *NodeYaml) GetProfile(id string) (profile ProfileConf, err error) { /* Get the profile with id, return ErrNotFound otherwise */ -func (config *NodeYaml) GetProfilePtr(id string) (profile *ProfileConf, err error) { - if found, ok := config.nodeProfiles[id]; ok { +func (config *NodesYaml) GetProfilePtr(id string) (profile *Profile, err error) { + if found, ok := config.NodeProfiles[id]; ok { found.id = id return found, nil } @@ -158,9 +161,9 @@ func (config *NodeYaml) GetProfilePtr(id string) (profile *ProfileConf, err erro Get the nodes from the loaded configuration. This function also merges the nodes with the given nodes. */ -func (config *NodeYaml) FindAllNodes(nodes ...string) (nodeList []NodeConf, err error) { +func (config *NodesYaml) FindAllNodes(nodes ...string) (nodeList []Node, err error) { if len(nodes) == 0 { - for n := range config.nodes { + for n := range config.Nodes { nodes = append(nodes, n) } } @@ -188,9 +191,9 @@ func (config *NodeYaml) FindAllNodes(nodes ...string) (nodeList []NodeConf, err /* Return all nodes as ProfileConf */ -func (config *NodeYaml) FindAllProfiles(nodes ...string) (profileList []ProfileConf, err error) { +func (config *NodesYaml) FindAllProfiles(nodes ...string) (profileList []Profile, err error) { if len(nodes) == 0 { - for n := range config.nodeProfiles { + for n := range config.NodeProfiles { nodes = append(nodes, n) } } @@ -219,9 +222,9 @@ func (config *NodeYaml) FindAllProfiles(nodes ...string) (profileList []ProfileC /* Return the names of all available nodes */ -func (config *NodeYaml) ListAllNodes() []string { - nodeList := make([]string, len(config.nodes)) - for name := range config.nodes { +func (config *NodesYaml) ListAllNodes() []string { + nodeList := make([]string, len(config.Nodes)) + for name := range config.Nodes { nodeList = append(nodeList, name) } return nodeList @@ -230,9 +233,9 @@ func (config *NodeYaml) ListAllNodes() []string { /* Return the names of all available nodes */ -func (config *NodeYaml) ListAllProfiles() []string { +func (config *NodesYaml) ListAllProfiles() []string { var nodeList []string - for name := range config.nodeProfiles { + for name := range config.NodeProfiles { nodeList = append(nodeList, name) } return nodeList @@ -246,7 +249,7 @@ without a hardware address is returned. If no unconfigured node is found, an error is returned. */ -func (config *NodeYaml) FindDiscoverableNode() (NodeConf, string, error) { +func (config *NodesYaml) FindDiscoverableNode() (Node, string, error) { nodes, _ := config.FindAllNodes() diff --git a/internal/pkg/node/constuctors_test.go b/internal/pkg/node/constructors_test.go similarity index 97% rename from internal/pkg/node/constuctors_test.go rename to internal/pkg/node/constructors_test.go index ec0171cf..cec97419 100644 --- a/internal/pkg/node/constuctors_test.go +++ b/internal/pkg/node/constructors_test.go @@ -8,7 +8,7 @@ import ( "gopkg.in/yaml.v3" ) -func newConstructorPrimaryNetworkTest(t *testing.T) NodeYaml { +func newConstructorPrimaryNetworkTest(t *testing.T) NodesYaml { var data = ` nodeprofiles: default: @@ -53,7 +53,7 @@ nodes: override: device: ib1 ` - var ret NodeYaml + var ret NodesYaml err := yaml.Unmarshal([]byte(data), &ret) assert.NoError(t, err) return ret @@ -138,7 +138,7 @@ func Test_FindDiscoverableNode(t *testing.T) { t.Run(tt.description, func(t *testing.T) { config := newConstructorPrimaryNetworkTest(t) for _, node := range tt.discoverable_nodes { - config.nodes[node].Discoverable = "true" + config.Nodes[node].Discoverable = "true" } discovered_node, discovered_interface, err := config.FindDiscoverableNode() if !tt.succeed { @@ -193,13 +193,13 @@ nodes: - profile2 ` assert := assert.New(t) - var ymlSrc NodeYaml + var ymlSrc NodesYaml err := yaml.Unmarshal([]byte(nodesconf), &ymlSrc) assert.NoError(err) wwlog.SetLogLevel(wwlog.DEBUG) nodes, err := ymlSrc.FindAllNodes() assert.NoError(err) - nodemap := make(map[string]*NodeConf) + nodemap := make(map[string]*Node) for i := range nodes { nodemap[nodes[i].Id()] = &nodes[i] } diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index bf44236b..cc48b5d3 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -3,9 +3,7 @@ package node import ( "net" - "github.com/warewulf/warewulf/internal/pkg/wwlog" "github.com/warewulf/warewulf/internal/pkg/wwtype" - "gopkg.in/yaml.v3" ) const undef string = "UNDEF" @@ -16,29 +14,28 @@ const undef string = "UNDEF" /* Structure of which goes to disk */ -type NodeYaml struct { - WWInternal int `yaml:"WW_INTERNAL,omitempty" json:"WW_INTERNAL,omitempty"` - nodeProfiles map[string]*ProfileConf - nodes map[string]*NodeConf +type NodesYaml struct { + NodeProfiles map[string]*Profile + Nodes map[string]*Node } /* -NodeConf is the datastructure describing a node and a profile which in disk format. +Node is the datastructure describing a node and a profile which in disk format. */ -type NodeConf struct { +type Node struct { id string valid bool // Is set true, if called by the constructor // exported values Discoverable wwtype.WWbool `yaml:"discoverable,omitempty" lopt:"discoverable" sopt:"e" comment:"Make discoverable in given network (true/false)"` AssetKey string `yaml:"asset key,omitempty" lopt:"asset" comment:"Set the node's Asset tag (key)"` Profiles []string `yaml:"profiles,omitempty" lopt:"profile" sopt:"P" comment:"Set the node's profile members (comma separated)"` - ProfileConf `yaml:"-,inline"` // include all values set in the profile, but inline them in yaml output if these are part of NodeConf + Profile `yaml:"-,inline"` // include all values set in the profile, but inline them in yaml output if these are part of Node } /* Holds the data which can be set for profiles and nodes. */ -type ProfileConf struct { +type Profile struct { id string // exported values Comment string `yaml:"comment,omitempty" lopt:"comment" comment:"Set arbitrary string comment"` @@ -51,7 +48,7 @@ type ProfileConf struct { Ipmi *IpmiConf `yaml:"ipmi,omitempty"` Init string `yaml:"init,omitempty" lopt:"init" sopt:"i" comment:"Define the init process to boot the container"` Root string `yaml:"root,omitempty" lopt:"root" comment:"Define the rootfs" ` - NetDevs map[string]*NetDevs `yaml:"network devices,omitempty"` + NetDevs map[string]*NetDev `yaml:"network devices,omitempty"` Tags map[string]string `yaml:"tags,omitempty"` PrimaryNetDev string `yaml:"primary network,omitempty" lopt:"primarynet" sopt:"p" comment:"Set the primary network interface"` Disks map[string]*Disk `yaml:"disks,omitempty"` @@ -78,9 +75,9 @@ type KernelConf struct { Args string `yaml:"args,omitempty" lopt:"kernelargs" sopt:"A" comment:"Set Kernel argument" json:"args,omitempty"` } -type NetDevs struct { +type NetDev struct { Type string `yaml:"type,omitempty" lopt:"type" sopt:"T" comment:"Set device type of given network"` - OnBoot *bool `yaml:"onboot,omitempty" lopt:"onboot" comment:"Enable/disable network device (true/false)"` + OnBoot wwtype.WWbool `yaml:"onboot,omitempty" lopt:"onboot" comment:"Enable/disable network device (true/false)"` Device string `yaml:"device,omitempty" lopt:"netdev" sopt:"N" comment:"Set the device for given network"` Hwaddr string `yaml:"hwaddr,omitempty" lopt:"hwaddr" sopt:"H" comment:"Set the device's HW address for given network" type:"MAC"` Ipaddr net.IP `yaml:"ipaddr,omitempty" comment:"IPv4 address in given network" sopt:"I" lopt:"ipaddr" type:"IP"` @@ -107,7 +104,7 @@ Partitions map */ type Partition struct { Number string `yaml:"number,omitempty" lopt:"partnumber" comment:"set the partition number, if not set next free slot is used" type:"uint"` - SizeMiB string `yaml:"size_mib,omitempty" lopt:"partsize " comment:"set the size of the partition, if not set maximal possible size is used" type:"uint"` + SizeMiB string `yaml:"size_mib,omitempty" lopt:"partsize" comment:"set the size of the partition, if not set maximal possible size is used" type:"uint"` StartMiB string `yaml:"start_mib,omitempty" comment:"the start of the partition" type:"uint"` TypeGuid string `yaml:"type_guid,omitempty" comment:"Linux filesystem data will be used if empty"` Guid string `yaml:"guid,omitempty" comment:"the GPT unique partition GUID"` @@ -128,48 +125,3 @@ type FileSystem struct { Options []string `yaml:"options,omitempty" comment:"any additional options to be passed to the format-specific mkfs utility"` MountOptions string `yaml:"mount_options,omitempty" comment:"any special options to be passed to the mount command"` } - -/* -interface so that nodes and profiles which aren't exported will -be marshaled -*/ -type ExportedYml struct { - WWInternal int `yaml:"WW_INTERNAL"` - NodeProfiles map[string]*ProfileConf `yaml:"nodeprofiles"` - Nodes map[string]*NodeConf `yaml:"nodes"` -} - -/* -Marshall Exported stuff, not NodeYaml directly -*/ -func (yml *NodeYaml) MarshalYAML() (interface{}, error) { - wwlog.Debug("marshall yml") - var exp ExportedYml - exp.WWInternal = yml.WWInternal - exp.Nodes = yml.nodes - exp.NodeProfiles = yml.nodeProfiles - node := yaml.Node{} - err := node.Encode(exp) - if err != nil { - return node, err - } - return node, err -} - -/* -Unmarshal to intermediate format -*/ -func (yml *NodeYaml) UnmarshalYAML( - unmarshal func(interface{}) (err error), -) (err error) { - wwlog.Debug("UnmarshalYAML called") - var exp ExportedYml - err = unmarshal(&exp) - if err != nil { - return - } - yml.WWInternal = exp.WWInternal - yml.nodes = exp.Nodes - yml.nodeProfiles = exp.NodeProfiles - return nil -} diff --git a/internal/pkg/node/flags.go b/internal/pkg/node/flags.go index e87c750d..62dad5e1 100644 --- a/internal/pkg/node/flags.go +++ b/internal/pkg/node/flags.go @@ -31,11 +31,11 @@ type NodeConfAdd struct { Create cmd line flags from the NodeConf fields. Returns a []func() where every function must be called, as the command line parser returns e.g. netip.IP objects which must be parsed back to strings. */ -func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command) { +func (nodeConf *Node) CreateFlags(baseCmd *cobra.Command) { recursiveCreateFlags(nodeConf, baseCmd) } -func (profileConf *ProfileConf) CreateFlags(baseCmd *cobra.Command) { +func (profileConf *Profile) CreateFlags(baseCmd *cobra.Command) { recursiveCreateFlags(profileConf, baseCmd) } diff --git a/internal/pkg/node/hash.go b/internal/pkg/node/hash.go index 1c57dce9..757a1879 100644 --- a/internal/pkg/node/hash.go +++ b/internal/pkg/node/hash.go @@ -11,12 +11,12 @@ import ( /* Calculate the hash of NodeYaml in an orderder fashion */ -func (config *NodeYaml) Hash() [32]byte { +func (config *NodesYaml) Hash() [32]byte { // flatten out profiles and nodes - for _, val := range config.nodeProfiles { + for _, val := range config.NodeProfiles { val.Flatten() } - for _, val := range config.nodes { + for _, val := range config.Nodes { val.Flatten() } data, err := yaml.Marshal(config) @@ -29,7 +29,7 @@ func (config *NodeYaml) Hash() [32]byte { /* Return the hash as string */ -func (config *NodeYaml) StringHash() string { +func (config *NodesYaml) StringHash() string { buffer := config.Hash() return hex.EncodeToString(buffer[:]) } diff --git a/internal/pkg/node/hash_test.go b/internal/pkg/node/hash_test.go index 482a7ef8..8fc66e79 100644 --- a/internal/pkg/node/hash_test.go +++ b/internal/pkg/node/hash_test.go @@ -10,7 +10,6 @@ import ( func TestHash(t *testing.T) { nodeConfYml1 := ` -WW_INTERNAL: 45 nodeprofiles: default: comment: This profile is automatically included for each node @@ -33,7 +32,6 @@ nodes: ipaddr: 10.0.10.2 ` nodeConfYml2 := ` -WW_INTERNAL: 45 nodeprofiles: default: comment: This profile is automatically included for each node @@ -56,7 +54,6 @@ nodes: ipaddr: 10.0.10.1 ` nodeConfYml3 := ` -WW_INTERNAL: 45 nodeprofiles: default: comment: This profile is automatically included for each node @@ -78,7 +75,7 @@ nodes: default: ipaddr: 10.0.10.3 ` - var nodeConf1, nodeConf2, nodeConf3 NodeYaml + var nodeConf1, nodeConf2, nodeConf3 NodesYaml err := yaml.Unmarshal([]byte(nodeConfYml1), &nodeConf1) assert.NoError(t, err) err = yaml.Unmarshal([]byte(nodeConfYml2), &nodeConf2) @@ -87,7 +84,7 @@ nodes: assert.NoError(t, err) t.Run("Same NodeYaml with same conf", func(t *testing.T) { - var testConf NodeYaml + var testConf NodesYaml err = yaml.Unmarshal([]byte(nodeConfYml1), &testConf) assert.NoError(t, err) if testConf.Hash() != nodeConf1.Hash() { diff --git a/internal/pkg/node/ignition.go b/internal/pkg/node/ignition.go index fbffab87..c3ac4e96 100644 --- a/internal/pkg/node/ignition.go +++ b/internal/pkg/node/ignition.go @@ -13,7 +13,7 @@ import ( /* Create a ignition struct class for ignition */ -func (node *NodeConf) GetStorage() (stor types_3_4.Storage, err error, rep string) { +func (node *Node) GetStorage() (stor types_3_4.Storage, err error, rep string) { var fileSystems []types_3_4.Filesystem for fsdevice, fs := range node.FileSystems { var mountOptions []types_3_4.MountOption @@ -141,7 +141,7 @@ type SimpleIgnitionConfig struct { /* Get a simple config which can be marshalled to json */ -func (node *NodeConf) GetConfig() (conf SimpleIgnitionConfig, rep string, err error) { +func (node *Node) GetConfig() (conf SimpleIgnitionConfig, rep string, err error) { conf.Storage, err, rep = node.GetStorage() conf.Ignition.Version = "3.1.0" return diff --git a/internal/pkg/node/list.go b/internal/pkg/node/list.go index 17d9ba02..0cabf65e 100644 --- a/internal/pkg/node/list.go +++ b/internal/pkg/node/list.go @@ -41,10 +41,10 @@ type fieldMap map[string]*NodeFields /* Get all the info out of NodeConf. If emptyFields is set true, all fields are shown not only the ones with effective values */ -func (nodeYml *NodeYaml) GetFields(node NodeConf) (output []NodeFields) { +func (nodeYml *NodesYaml) GetFields(node Node) (output []NodeFields) { nodeMap := make(fieldMap) for _, p := range node.Profiles { - if profile, ok := nodeYml.nodeProfiles[p]; ok { + if profile, ok := nodeYml.NodeProfiles[p]; ok { nodeMap.importFields(profile, "", p) } } @@ -64,7 +64,7 @@ func (nodeYml *NodeYaml) GetFields(node NodeConf) (output []NodeFields) { /* Get all the info out of ProfileConf. If emptyFields is set true, all fields are shown not only the ones with effective values */ -func (nodeYml *NodeYaml) GetFieldsProfile(profile ProfileConf) (output []NodeFields) { +func (nodeYml *NodesYaml) GetFieldsProfile(profile Profile) (output []NodeFields) { profileMap := make(fieldMap) profileMap.importFields(&profile, "", "") for _, elem := range profileMap { diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index ea449145..4e1ca218 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -10,13 +10,13 @@ import ( "github.com/warewulf/warewulf/internal/pkg/util" ) -type nodeList []NodeConf +type nodeList []Node func (a nodeList) Len() int { return len(a) } func (a nodeList) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a nodeList) Less(i, j int) bool { return a[i].id < a[j].id } -type profileList []ProfileConf +type profileList []Profile func (a profileList) Len() int { return len(a) } func (a profileList) Swap(i, j int) { a[i], a[j] = a[j], a[i] } @@ -32,9 +32,9 @@ func (a profileList) Less(i, j int) bool { return a[i].id < a[j].id } Filter a given slice of NodeConf against a given regular expression */ -func FilterNodeListByName(set []NodeConf, searchList []string) []NodeConf { - var ret []NodeConf - unique := make(map[string]NodeConf) +func FilterNodeListByName(set []Node, searchList []string) []Node { + var ret []Node + unique := make(map[string]Node) if len(searchList) > 0 { for _, search := range searchList { @@ -59,9 +59,9 @@ func FilterNodeListByName(set []NodeConf, searchList []string) []NodeConf { Filter a given slice of ProfileConf against a given regular expression */ -func FilterProfileListByName(set []ProfileConf, searchList []string) []ProfileConf { - var ret []ProfileConf - unique := make(map[string]ProfileConf) +func FilterProfileListByName(set []Profile, searchList []string) []Profile { + var ret []Profile + unique := make(map[string]Profile) if len(searchList) > 0 { for _, search := range searchList { @@ -85,23 +85,23 @@ func FilterProfileListByName(set []ProfileConf, searchList []string) []ProfileCo /* Creates an NodeConf with the given id. Doesn't add it to the database */ -func NewNode(id string) (nodeconf NodeConf) { +func NewNode(id string) (nodeconf Node) { nodeconf = EmptyNode() nodeconf.id = id return nodeconf } -func NewProfile(id string) (profileconf ProfileConf) { +func NewProfile(id string) (profileconf Profile) { profileconf = EmptyProfile() profileconf.id = id return profileconf } -func EmptyNode() (nodeconf NodeConf) { +func EmptyNode() (nodeconf Node) { nodeconf.Ipmi = new(IpmiConf) nodeconf.Ipmi.Tags = map[string]string{} nodeconf.Kernel = new(KernelConf) - nodeconf.NetDevs = make(map[string]*NetDevs) + nodeconf.NetDevs = make(map[string]*NetDev) nodeconf.Tags = map[string]string{} return nodeconf } @@ -109,11 +109,11 @@ func EmptyNode() (nodeconf NodeConf) { /* Creates a ProfileConf but doesn't add it to the database. */ -func EmptyProfile() (profileconf ProfileConf) { +func EmptyProfile() (profileconf Profile) { profileconf.Ipmi = new(IpmiConf) profileconf.Ipmi.Tags = map[string]string{} profileconf.Kernel = new(KernelConf) - profileconf.NetDevs = make(map[string]*NetDevs) + profileconf.NetDevs = make(map[string]*NetDev) profileconf.Tags = map[string]string{} return profileconf } @@ -123,7 +123,7 @@ Flattens out a NodeConf, which means if there are no explicit values in *IpmiCon or *KernelConf, these pointer will set to nil. This will remove something like ipmi: {} from nodes.conf */ -func (info *NodeConf) Flatten() { +func (info *Node) Flatten() { recursiveFlatten(info) } @@ -132,7 +132,7 @@ Flattens out a ProfileConf, which means if there are no explicit values in *Ipmi or *KernelConf, these pointer will set to nil. This will remove something like ipmi: {} from nodes.conf */ -func (info *ProfileConf) Flatten() { +func (info *Profile) Flatten() { recursiveFlatten(info) } @@ -296,28 +296,28 @@ Getters for unexported fields /* Returns the id of the node */ -func (node *NodeConf) Id() string { +func (node *Node) Id() string { return node.id } /* Returns the id of the profile */ -func (node *ProfileConf) Id() string { +func (node *Profile) Id() string { return node.id } /* Returns if the node is a valid in the database */ -func (node *NodeConf) Valid() bool { +func (node *Node) Valid() bool { return node.valid } /* Check if the netdev is the primary one */ -func (dev *NetDevs) Primary() bool { +func (dev *NetDev) Primary() bool { return dev.primary } @@ -353,7 +353,7 @@ func cleanList(list []string) (ret []string) { Return the ipv4 address and mask in CIDR format. Aimed for the use in templates. */ -func (netdev *NetDevs) IpCIDR() string { +func (netdev *NetDev) IpCIDR() string { if netdev.Ipaddr.IsUnspecified() || netdev.Netmask.IsUnspecified() { return "" } diff --git a/internal/pkg/node/methods_test.go b/internal/pkg/node/methods_test.go index a629ecb4..860e8257 100644 --- a/internal/pkg/node/methods_test.go +++ b/internal/pkg/node/methods_test.go @@ -6,8 +6,8 @@ import ( ) func Test_Empty(t *testing.T) { - var netdev NetDevs - var netdevPtr *NetDevs + var netdev NetDev + var netdevPtr *NetDev t.Run("test for empty", func(t *testing.T) { if ObjectIsEmpty(netdev) != true { diff --git a/internal/pkg/node/modifiers.go b/internal/pkg/node/modifiers.go index 6abf88a1..1bd5e03b 100644 --- a/internal/pkg/node/modifiers.go +++ b/internal/pkg/node/modifiers.go @@ -14,13 +14,13 @@ import ( /* Add a node with the given ID and return a pointer to it */ -func (config *NodeYaml) AddNode(nodeID string) (*NodeConf, error) { +func (config *NodesYaml) AddNode(nodeID string) (*Node, error) { newNode := NewNode(nodeID) wwlog.Verbose("Adding new node: %s", nodeID) - if _, ok := config.nodes[nodeID]; ok { + if _, ok := config.Nodes[nodeID]; ok { return nil, errors.New("nodename already exists: " + nodeID) } else { - config.nodes[nodeID] = &newNode + config.Nodes[nodeID] = &newNode } return &newNode, nil } @@ -28,13 +28,13 @@ func (config *NodeYaml) AddNode(nodeID string) (*NodeConf, error) { /* delete node with the given id */ -func (config *NodeYaml) DelNode(nodeID string) error { - if _, ok := config.nodes[nodeID]; !ok { +func (config *NodesYaml) DelNode(nodeID string) error { + if _, ok := config.Nodes[nodeID]; !ok { return errors.New("nodename does not exist: " + nodeID) } wwlog.Verbose("Deleting node: %s", nodeID) - delete(config.nodes, nodeID) + delete(config.Nodes, nodeID) return nil } @@ -42,8 +42,8 @@ func (config *NodeYaml) DelNode(nodeID string) error { /* set node for the node with id the values of vals */ -func (config *NodeYaml) SetNode(nodeID string, vals NodeConf) error { - node, ok := config.nodes[nodeID] +func (config *NodesYaml) SetNode(nodeID string, vals Node) error { + node, ok := config.Nodes[nodeID] if !ok { return ErrNotFound } @@ -61,8 +61,8 @@ func (config *NodeYaml) SetNode(nodeID string, vals NodeConf) error { /* set profile for the node with id the values of vals */ -func (config *NodeYaml) SetProfile(profileId string, vals ProfileConf) error { - profile, ok := config.nodeProfiles[profileId] +func (config *NodesYaml) SetProfile(profileId string, vals Profile) error { + profile, ok := config.NodeProfiles[profileId] if !ok { return ErrNotFound } @@ -80,13 +80,13 @@ func (config *NodeYaml) SetProfile(profileId string, vals ProfileConf) error { /* Add a node with the given ID and return a pointer to it */ -func (config *NodeYaml) AddProfile(profileId string) (*ProfileConf, error) { +func (config *NodesYaml) AddProfile(profileId string) (*Profile, error) { profile := EmptyProfile() wwlog.Verbose("adding new profile: %s", profileId) - if _, ok := config.nodeProfiles[profileId]; ok { + if _, ok := config.NodeProfiles[profileId]; ok { return nil, errors.New("profile already exists: " + profileId) } else { - config.nodeProfiles[profileId] = &profile + config.NodeProfiles[profileId] = &profile } return &profile, nil } @@ -94,13 +94,13 @@ func (config *NodeYaml) AddProfile(profileId string) (*ProfileConf, error) { /* delete node with the given id */ -func (config *NodeYaml) DelProfile(nodeID string) error { - if _, ok := config.nodes[nodeID]; !ok { +func (config *NodesYaml) DelProfile(nodeID string) error { + if _, ok := config.Nodes[nodeID]; !ok { return errors.New("profile does not exist: " + nodeID) } wwlog.Verbose("deleting profile: %s", nodeID) - delete(config.nodes, nodeID) + delete(config.Nodes, nodeID) return nil } @@ -108,13 +108,20 @@ func (config *NodeYaml) DelProfile(nodeID string) error { /* Write the the NodeYaml to disk. */ -func (config *NodeYaml) Persist() error { +func (config *NodesYaml) Persist() error { + return config.PersistToFile(ConfigFile) +} + +func (config *NodesYaml) PersistToFile(configFile string) error { + if configFile == "" { + configFile = ConfigFile + } out, dumpErr := config.Dump() if dumpErr != nil { wwlog.Error("%s", dumpErr) return dumpErr } - file, err := os.OpenFile(ConfigFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) + file, err := os.OpenFile(configFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) if err != nil { wwlog.Error("%s", err) return err @@ -124,7 +131,7 @@ func (config *NodeYaml) Persist() error { if err != nil { return err } - wwlog.Debug("persisted: %s", ConfigFile) + wwlog.Debug("persisted: %s", configFile) return nil } @@ -132,12 +139,12 @@ func (config *NodeYaml) Persist() error { Dump returns a YAML document representing the nodeDb instance. Passes through any errors generated by yaml.Marshal. */ -func (config *NodeYaml) Dump() ([]byte, error) { +func (config *NodesYaml) Dump() ([]byte, error) { // flatten out profiles and nodes - for _, val := range config.nodeProfiles { + for _, val := range config.NodeProfiles { val.Flatten() } - for _, val := range config.nodes { + for _, val := range config.Nodes { val.Flatten() } var buf bytes.Buffer diff --git a/internal/pkg/node/util.go b/internal/pkg/node/util.go index c3924da1..478e55a4 100644 --- a/internal/pkg/node/util.go +++ b/internal/pkg/node/util.go @@ -10,9 +10,9 @@ import ( /* Gets a node by its hardware(mac) address */ -func (config *NodeYaml) FindByHwaddr(hwa string) (NodeConf, error) { +func (config *NodesYaml) FindByHwaddr(hwa string) (Node, error) { if _, err := net.ParseMAC(hwa); err != nil { - return NodeConf{}, errors.New("invalid hardware address: " + hwa) + return Node{}, errors.New("invalid hardware address: " + hwa) } nodeList, _ := config.FindAllNodes() for _, node := range nodeList { @@ -23,20 +23,20 @@ func (config *NodeYaml) FindByHwaddr(hwa string) (NodeConf, error) { } } - return NodeConf{}, ErrNotFound + return Node{}, ErrNotFound } /* Find a node by its ip address */ -func (config *NodeYaml) FindByIpaddr(ipaddr string) (NodeConf, error) { +func (config *NodesYaml) FindByIpaddr(ipaddr string) (Node, error) { addr := net.ParseIP(ipaddr) if addr == nil { - return NodeConf{}, errors.New("invalid IP:" + ipaddr) + return Node{}, errors.New("invalid IP:" + ipaddr) } nodeList, err := config.FindAllNodes() if err != nil { - return NodeConf{}, err + return Node{}, err } for _, node := range nodeList { for _, dev := range node.NetDevs { @@ -46,7 +46,7 @@ func (config *NodeYaml) FindByIpaddr(ipaddr string) (NodeConf, error) { } } - return NodeConf{}, ErrNotFound + return Node{}, ErrNotFound } /* diff --git a/internal/pkg/node/util_test.go b/internal/pkg/node/util_test.go index 54f2dbbc..ecbc4fef 100644 --- a/internal/pkg/node/util_test.go +++ b/internal/pkg/node/util_test.go @@ -8,7 +8,7 @@ import ( "gopkg.in/yaml.v3" ) -func NewUtilTestNode() (NodeYaml, error) { +func NewUtilTestNode() (NodesYaml, error) { var data = ` nodeprofiles: default: @@ -41,7 +41,7 @@ nodes: default: false ipaddr: fd1a:2b3c:4d5e:06f0:1234:5678:90ab:cdef ` - var ret NodeYaml + var ret NodesYaml err := yaml.Unmarshal([]byte(data), &ret) if err != nil { return ret, err @@ -62,7 +62,7 @@ func Test_nodeYaml_FindByHwaddr(t *testing.T) { tests := []struct { name string //fields fields - config NodeYaml + config NodesYaml args args want string wantErr bool @@ -100,7 +100,7 @@ func Test_nodeYaml_FindByIpaddr(t *testing.T) { } tests := []struct { name string - config NodeYaml + config NodesYaml args args want string wantErr bool diff --git a/internal/pkg/overlay/datastructure.go b/internal/pkg/overlay/datastructure.go index 8a996023..240fcb2b 100644 --- a/internal/pkg/overlay/datastructure.go +++ b/internal/pkg/overlay/datastructure.go @@ -35,17 +35,17 @@ type TemplateStruct struct { Warewulf warewulfconf.WarewulfConf Tftp warewulfconf.TFTPConf Paths warewulfconf.BuildConfig - AllNodes []node.NodeConf - node.NodeConf + AllNodes []node.Node + node.Node // backward compatiblity Container string - ThisNode *node.NodeConf + ThisNode *node.Node } /* Initialize an TemplateStruct with the given node.NodeInfo */ -func InitStruct(nodeData node.NodeConf) (TemplateStruct, error) { +func InitStruct(nodeData node.Node) (TemplateStruct, error) { var tstruct TemplateStruct hostname, _ := os.Hostname() tstruct.BuildHost = hostname @@ -78,7 +78,7 @@ func InitStruct(nodeData node.NodeConf) (TemplateStruct, error) { dt := time.Now() tstruct.BuildTime = dt.Format("01-02-2006 15:04:05 MST") tstruct.BuildTimeUnix = strconv.FormatInt(dt.Unix(), 10) - tstruct.NodeConf.Tags = map[string]string{} + tstruct.Node.Tags = map[string]string{} var buf bytes.Buffer enc := gob.NewEncoder(&buf) dec := gob.NewDecoder(&buf) diff --git a/internal/pkg/overlay/funcmap.go b/internal/pkg/overlay/funcmap.go index 751a97d0..a9758d76 100644 --- a/internal/pkg/overlay/funcmap.go +++ b/internal/pkg/overlay/funcmap.go @@ -106,7 +106,7 @@ func templateContainerFileInclude(containername string, filepath string) string // don't return an error as we use this function for template evaluation, // so error will turn up there as the return string -func createIgnitionJson(node *node.NodeConf) string { +func createIgnitionJson(node *node.Node) string { conf, rep, err := node.GetConfig() if len(conf.Storage.Disks) == 0 && len(conf.Storage.Filesystems) == 0 { wwlog.Debug("no disks or filesystems present, don't create a json object") diff --git a/internal/pkg/overlay/funcmap_test.go b/internal/pkg/overlay/funcmap_test.go index b68e3bd0..641bc50e 100644 --- a/internal/pkg/overlay/funcmap_test.go +++ b/internal/pkg/overlay/funcmap_test.go @@ -9,8 +9,7 @@ import ( ) func Test_createIgnitionJson(t *testing.T) { - node_config := `WW_INTERNAL: 45 -nodes: + node_config := `nodes: n1: disks: /dev/vda: diff --git a/internal/pkg/overlay/overlay.go b/internal/pkg/overlay/overlay.go index d14747aa..8542b346 100644 --- a/internal/pkg/overlay/overlay.go +++ b/internal/pkg/overlay/overlay.go @@ -28,7 +28,7 @@ var ( /* Build all overlays for a node */ -func BuildAllOverlays(nodes []node.NodeConf) error { +func BuildAllOverlays(nodes []node.Node) error { for _, n := range nodes { sysOverlays := n.SystemOverlay @@ -50,7 +50,7 @@ func BuildAllOverlays(nodes []node.NodeConf) error { // TODO: Add an Overlay Delete for both sourcedir and image -func BuildSpecificOverlays(nodes []node.NodeConf, overlayNames []string) error { +func BuildSpecificOverlays(nodes []node.Node, overlayNames []string) error { for _, n := range nodes { wwlog.Info("Building overlay for %s: %v", n, overlayNames) for _, overlayName := range overlayNames { @@ -123,7 +123,7 @@ func OverlayInit(overlayName string) error { /* Build the given overlays for a node and create a Image for them */ -func BuildOverlay(nodeConf node.NodeConf, context string, overlayNames []string) error { +func BuildOverlay(nodeConf node.Node, context string, overlayNames []string) error { if len(overlayNames) == 0 && context == "" { return nil } @@ -172,7 +172,7 @@ func BuildOverlay(nodeConf node.NodeConf, context string, overlayNames []string) Build the given overlays for a node in the given directory. If the given does not exists it will be created. */ -func BuildOverlayIndir(nodeData node.NodeConf, overlayNames []string, outputDir string) error { +func BuildOverlayIndir(nodeData node.Node, overlayNames []string, outputDir string) error { if len(overlayNames) == 0 { return nil } diff --git a/internal/pkg/overlay/overlay_test.go b/internal/pkg/overlay/overlay_test.go index d1a02a28..56dab2f4 100644 --- a/internal/pkg/overlay/overlay_test.go +++ b/internal/pkg/overlay/overlay_test.go @@ -265,7 +265,7 @@ func Test_BuildAllOverlays(t *testing.T) { defer os.RemoveAll(provisionDir) conf.Paths.WWProvisiondir = provisionDir - var nodes []node.NodeConf + var nodes []node.Node for i, nodeName := range tt.nodes { nodeInfo := node.NewNode(nodeName) if tt.systemOverlays != nil { @@ -363,7 +363,7 @@ func Test_BuildSpecificOverlays(t *testing.T) { defer os.RemoveAll(provisionDir) conf.Paths.WWProvisiondir = provisionDir - var nodes []node.NodeConf + var nodes []node.Node for _, nodeName := range tt.nodes { nodeInfo := node.NewNode(nodeName) nodes = append(nodes, nodeInfo) diff --git a/internal/pkg/testenv/testenv.go b/internal/pkg/testenv/testenv.go index 94d88a83..24ab8e50 100644 --- a/internal/pkg/testenv/testenv.go +++ b/internal/pkg/testenv/testenv.go @@ -18,9 +18,8 @@ import ( "github.com/warewulf/warewulf/internal/pkg/node" ) -const initWarewulfConf = `WW_INTERNAL: 0` -const initNodesConf = `WW_INTERNAL: 45 -nodeprofiles: +const initWarewulfConf = `` +const initNodesConf = `nodeprofiles: default: {} nodes: node1: {} diff --git a/internal/pkg/testenv/testenv_test.go b/internal/pkg/testenv/testenv_test.go index 46623c96..25ea7929 100644 --- a/internal/pkg/testenv/testenv_test.go +++ b/internal/pkg/testenv/testenv_test.go @@ -19,8 +19,7 @@ func Test_Basic(t *testing.T) { func Test_two_nodes(t *testing.T) { env := New(t) - env.WriteFile(t, "etc/warewulf/nodes.conf", `WW_INTERNAL: 45 -nodeprofiles: + env.WriteFile(t, "etc/warewulf/nodes.conf", `nodeprofiles: default: {} nodes: node1: {} diff --git a/internal/pkg/upgrade/config.go b/internal/pkg/upgrade/config.go new file mode 100644 index 00000000..ac597911 --- /dev/null +++ b/internal/pkg/upgrade/config.go @@ -0,0 +1,243 @@ +package upgrade + +import ( + "gopkg.in/yaml.v3" + + "github.com/warewulf/warewulf/internal/pkg/config" +) + +func ParseConfig(data []byte) (warewulfYaml *WarewulfYaml, err error) { + warewulfYaml = new(WarewulfYaml) + if err = yaml.Unmarshal(data, warewulfYaml); err != nil { + return warewulfYaml, err + } + return warewulfYaml, nil +} + +type WarewulfYaml struct { + WWInternal string `yaml:"WW_INTERNAL"` + Comment string `yaml:"comment"` + Ipaddr string `yaml:"ipaddr"` + Ipaddr6 string `yaml:"ipaddr6"` + Netmask string `yaml:"netmask"` + Network string `yaml:"network"` + Ipv6net string `yaml:"ipv6net"` + Fqdn string `yaml:"fqdn"` + Warewulf *WarewulfConf `yaml:"warewulf"` + DHCP *DHCPConf `yaml:"dhcp"` + TFTP *TFTPConf `yaml:"tftp"` + NFS *NFSConf `yaml:"nfs"` + SSH *SSHConf `yaml:"ssh"` + MountsContainer []*MountEntry `yaml:"container mounts"` + Paths *BuildConfig `yaml:"paths"` + WWClient *WWClientConf `yaml:"wwclient"` +} + +func (this *WarewulfYaml) Upgrade() (upgraded *config.WarewulfYaml) { + upgraded = new(config.WarewulfYaml) + if this.WWInternal != "" { + logIgnore("WW_INTERNAL", this.WWInternal, "obsolete") + } + upgraded.Comment = this.Comment + upgraded.Ipaddr = this.Ipaddr + upgraded.Ipaddr6 = this.Ipaddr6 + upgraded.Netmask = this.Netmask + upgraded.Network = this.Network + upgraded.Ipv6net = this.Ipv6net + upgraded.Fqdn = this.Fqdn + if this.Warewulf != nil { + upgraded.Warewulf = this.Warewulf.Upgrade() + } + if this.DHCP != nil { + upgraded.DHCP = this.DHCP.Upgrade() + } + if this.TFTP != nil { + upgraded.TFTP = this.TFTP.Upgrade() + } + if this.NFS != nil { + upgraded.NFS = this.NFS.Upgrade() + } + if this.SSH != nil { + upgraded.SSH = this.SSH.Upgrade() + } + upgraded.MountsContainer = make([]*config.MountEntry, 0) + for _, mount := range this.MountsContainer { + upgraded.MountsContainer = append(upgraded.MountsContainer, mount.Upgrade()) + } + if this.Paths != nil { + upgraded.Paths = this.Paths.Upgrade() + } + if this.WWClient != nil { + upgraded.WWClient = this.WWClient.Upgrade() + } + return upgraded +} + +type WarewulfConf struct { + Port int `yaml:"port"` + Secure *bool `yaml:"secure"` + UpdateInterval int `yaml:"update interval"` + AutobuildOverlays *bool `yaml:"autobuild overlays"` + EnableHostOverlay *bool `yaml:"host overlay"` + Syslog *bool `yaml:"syslog"` + DataStore string `yaml:"datastore"` + GrubBoot *bool `yaml:"grubboot"` +} + +func (this *WarewulfConf) Upgrade() (upgraded *config.WarewulfConf) { + upgraded = new(config.WarewulfConf) + upgraded.Port = this.Port + upgraded.SecureP = this.Secure + upgraded.UpdateInterval = this.UpdateInterval + upgraded.AutobuildOverlaysP = this.AutobuildOverlays + upgraded.EnableHostOverlayP = this.EnableHostOverlay + upgraded.SyslogP = this.Syslog + upgraded.DataStore = this.DataStore + upgraded.GrubBootP = this.GrubBoot + return upgraded +} + +type DHCPConf struct { + Enabled *bool `yaml:"enabled"` + Template string `yaml:"template"` + RangeStart string `yaml:"range start"` + RangeEnd string `yaml:"range end"` + SystemdName string `yaml:"systemd name"` +} + +func (this *DHCPConf) Upgrade() (upgraded *config.DHCPConf) { + upgraded = new(config.DHCPConf) + upgraded.EnabledP = this.Enabled + upgraded.Template = this.Template + upgraded.RangeStart = this.RangeStart + upgraded.RangeEnd = this.RangeEnd + upgraded.SystemdName = this.SystemdName + return upgraded +} + +type TFTPConf struct { + Enabled *bool `yaml:"enabled"` + TftpRoot string `yaml:"tftproot"` + SystemdName string `yaml:"systemd name"` + IpxeBinaries map[string]string `yaml:"ipxe"` +} + +func (this *TFTPConf) Upgrade() (upgraded *config.TFTPConf) { + upgraded = new(config.TFTPConf) + upgraded.EnabledP = this.Enabled + upgraded.TftpRoot = this.TftpRoot + upgraded.SystemdName = this.SystemdName + upgraded.IpxeBinaries = make(map[string]string) + for name, binary := range this.IpxeBinaries { + upgraded.IpxeBinaries[name] = binary + } + return upgraded +} + +type NFSConf struct { + Enabled *bool `yaml:"enabled"` + Exports []string `yaml:"exports"` + ExportsExtended []*NFSExportConf `yaml:"export paths"` + SystemdName string `yaml:"systemd name"` +} + +func (this *NFSConf) Upgrade() (upgraded *config.NFSConf) { + upgraded = new(config.NFSConf) + upgraded.EnabledP = this.Enabled + upgraded.ExportsExtended = make([]*config.NFSExportConf, 0) + for _, export := range this.Exports { + extendedExport := new(config.NFSExportConf) + extendedExport.Path = export + upgraded.ExportsExtended = append(upgraded.ExportsExtended, extendedExport) + } + for _, export := range this.ExportsExtended { + upgraded.ExportsExtended = append(upgraded.ExportsExtended, export.Upgrade()) + } + upgraded.SystemdName = this.SystemdName + return upgraded +} + +type NFSExportConf struct { + Path string `yaml:"path"` + ExportOptions string `yaml:"export options"` + MountOptions string `yaml:"mount options"` + Mount *bool `yaml:"mount"` +} + +func (this *NFSExportConf) Upgrade() (upgraded *config.NFSExportConf) { + upgraded = new(config.NFSExportConf) + upgraded.Path = this.Path + upgraded.ExportOptions = this.ExportOptions + upgraded.MountOptions = this.MountOptions + upgraded.MountP = this.Mount + return upgraded +} + +type SSHConf struct { + KeyTypes []string `yaml:"key types"` +} + +func (this *SSHConf) Upgrade() (upgraded *config.SSHConf) { + upgraded = new(config.SSHConf) + upgraded.KeyTypes = append([]string{}, this.KeyTypes...) + return upgraded +} + +type MountEntry struct { + Source string `yaml:"source"` + Dest string `yaml:"dest"` + ReadOnly *bool `yaml:"readonly"` + Options string `yaml:"options"` + Copy *bool `yaml:"copy"` +} + +func (this *MountEntry) Upgrade() (upgraded *config.MountEntry) { + upgraded = new(config.MountEntry) + upgraded.Source = this.Source + upgraded.Dest = this.Dest + upgraded.ReadOnlyP = this.ReadOnly + upgraded.Options = this.Options + upgraded.CopyP = this.Copy + return upgraded +} + +type BuildConfig struct { + Bindir string + Sysconfdir string + Localstatedir string + Cachedir string + Ipxesource string + Srvdir string + Firewallddir string + Systemddir string + WWOverlaydir string + WWChrootdir string + WWProvisiondir string + WWClientdir string +} + +func (this *BuildConfig) Upgrade() (upgraded *config.BuildConfig) { + upgraded = new(config.BuildConfig) + upgraded.Bindir = this.Bindir + upgraded.Sysconfdir = this.Sysconfdir + upgraded.Localstatedir = this.Localstatedir + upgraded.Cachedir = this.Cachedir + upgraded.Ipxesource = this.Ipxesource + upgraded.Srvdir = this.Srvdir + upgraded.Firewallddir = this.Firewallddir + upgraded.WWOverlaydir = this.WWOverlaydir + upgraded.WWChrootdir = this.WWChrootdir + upgraded.WWProvisiondir = this.WWProvisiondir + upgraded.WWClientdir = this.WWClientdir + return upgraded +} + +type WWClientConf struct { + Port uint16 `yaml:"port"` +} + +func (this *WWClientConf) Upgrade() (upgraded *config.WWClientConf) { + upgraded = new(config.WWClientConf) + upgraded.Port = this.Port + return upgraded +} diff --git a/internal/pkg/upgrade/config_test.go b/internal/pkg/upgrade/config_test.go new file mode 100644 index 00000000..cd29511d --- /dev/null +++ b/internal/pkg/upgrade/config_test.go @@ -0,0 +1,422 @@ +package upgrade + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +var configUpgradeTests = []struct { + name string + legacyYaml string + upgradedYaml string +}{ + { + name: "empty", + legacyYaml: ``, + upgradedYaml: `{}`, + }, + { + name: "v4.0.0", + legacyYaml: ` +ipaddr: 192.168.1.1 +netmask: 255.255.255.0 +warewulf: + port: 9873 + secure: true + update interval: 60 +dhcp: + enabled: true + range start: 192.168.1.150 + range end: 192.168.1.200 + template: default + systemd name: dhcpd +tftp: + enabled: true + tftproot: /var/lib/tftpboot + systemd name: tftp +nfs: + systemd name: nfs-server + exports: + - /home + - /var/warewulf +`, + upgradedYaml: ` +ipaddr: 192.168.1.1 +netmask: 255.255.255.0 +warewulf: + port: 9873 + secure: true + update interval: 60 +dhcp: + enabled: true + template: default + range start: 192.168.1.150 + range end: 192.168.1.200 + systemd name: dhcpd +tftp: + enabled: true + tftproot: /var/lib/tftpboot + systemd name: tftp +nfs: + export paths: + - path: /home + - path: /var/warewulf + systemd name: nfs-server +`, + }, + { + name: "v4.1.0", + legacyYaml: ` +ipaddr: 192.168.1.1 +netmask: 255.255.255.0 +warewulf: + port: 9873 + secure: true + update interval: 60 +dhcp: + enabled: true + range start: 192.168.1.150 + range end: 192.168.1.200 + template: default + systemd name: dhcpd +tftp: + enabled: true + tftproot: /var/lib/tftpboot + systemd name: tftp +nfs: + systemd name: nfs-server + exports: + - /home + - /var/warewulf +`, + upgradedYaml: ` +ipaddr: 192.168.1.1 +netmask: 255.255.255.0 +warewulf: + port: 9873 + secure: true + update interval: 60 +dhcp: + enabled: true + template: default + range start: 192.168.1.150 + range end: 192.168.1.200 + systemd name: dhcpd +tftp: + enabled: true + tftproot: /var/lib/tftpboot + systemd name: tftp +nfs: + export paths: + - path: /home + - path: /var/warewulf + systemd name: nfs-server +`, + }, + { + name: "v4.2.0", + legacyYaml: ` +ipaddr: 192.168.200.1 +netmask: 255.255.255.0 +warewulf: + port: 9873 + secure: true + autobuild overlays: true + update interval: 60 + syslog: false +dhcp: + enabled: true + range start: 192.168.200.50 + range end: 192.168.200.99 + template: default + systemd name: dhcpd +tftp: + enabled: true + tftproot: /var/lib/tftpboot + systemd name: tftp +nfs: + systemd name: nfs-server + exports: + - /home + - /var/warewulf +`, + upgradedYaml: ` +ipaddr: 192.168.200.1 +netmask: 255.255.255.0 +warewulf: + port: 9873 + secure: true + update interval: 60 + autobuild overlays: true + syslog: false +dhcp: + enabled: true + template: default + range start: 192.168.200.50 + range end: 192.168.200.99 + systemd name: dhcpd +tftp: + enabled: true + tftproot: /var/lib/tftpboot + systemd name: tftp +nfs: + export paths: + - path: /home + - path: /var/warewulf + systemd name: nfs-server +`, + }, + { + name: "v4.3.0", + legacyYaml: ` +WW_INTERNAL: 43 +ipaddr: 192.168.200.1 +netmask: 255.255.255.0 +network: 192.168.200.0 +warewulf: + port: 9873 + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + syslog: false + datastore: "" +dhcp: + enabled: true + template: default + range start: 192.168.200.50 + range end: 192.168.200.99 + systemd name: dhcpd +tftp: + enabled: true + tftproot: "" + systemd name: tftp +nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + mount options: defaults + mount: true + - path: /opt + export options: ro,sync,no_root_squash + mount options: defaults + mount: false + systemd name: nfs-server +`, + upgradedYaml: ` +ipaddr: 192.168.200.1 +netmask: 255.255.255.0 +network: 192.168.200.0 +warewulf: + port: 9873 + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + syslog: false +dhcp: + enabled: true + template: default + range start: 192.168.200.50 + range end: 192.168.200.99 + systemd name: dhcpd +tftp: + enabled: true + systemd name: tftp +nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + mount options: defaults + mount: true + - path: /opt + export options: ro,sync,no_root_squash + mount options: defaults + mount: false + systemd name: nfs-server +`, + }, + { + name: "v4.4.1", + legacyYaml: ` +WW_INTERNAL: 43 +ipaddr: 192.168.200.1 +netmask: 255.255.255.0 +network: 192.168.200.0 +warewulf: + port: 9873 + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + syslog: false +dhcp: + enabled: true + range start: 192.168.200.50 + range end: 192.168.200.99 + systemd name: dhcpd +tftp: + enabled: true + systemd name: tftp +nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + mount options: defaults + mount: true + - path: /opt + export options: ro,sync,no_root_squash + mount options: defaults + mount: false + systemd name: nfs-server +`, + upgradedYaml: ` +ipaddr: 192.168.200.1 +netmask: 255.255.255.0 +network: 192.168.200.0 +warewulf: + port: 9873 + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + syslog: false +dhcp: + enabled: true + range start: 192.168.200.50 + range end: 192.168.200.99 + systemd name: dhcpd +tftp: + enabled: true + systemd name: tftp +nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + mount options: defaults + mount: true + - path: /opt + export options: ro,sync,no_root_squash + mount options: defaults + mount: false + systemd name: nfs-server +`, + }, + { + name: "v4.5.8", + legacyYaml: ` +WW_INTERNAL: 45 +ipaddr: 10.0.0.1 +netmask: 255.255.252.0 +network: 10.0.0.0 +warewulf: + port: 9873 + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + syslog: false +dhcp: + enabled: true + range start: 10.0.1.1 + range end: 10.0.1.255 + systemd name: dhcpd +tftp: + enabled: true + systemd name: tftp + ipxe: + 00:09: ipxe-snponly-x86_64.efi + 00:00: undionly.kpxe + 00:0B: arm64-efi/snponly.efi + 00:07: ipxe-snponly-x86_64.efi +nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + mount options: defaults + mount: true + - path: /opt + export options: ro,sync,no_root_squash + mount options: defaults + mount: false + systemd name: nfs-server +container mounts: + - source: /etc/resolv.conf + dest: /etc/resolv.conf + readonly: true +ssh: + key types: + - rsa + - dsa + - ecdsa + - ed25519 +`, + upgradedYaml: ` +ipaddr: 10.0.0.1 +netmask: 255.255.252.0 +network: 10.0.0.0 +warewulf: + port: 9873 + secure: false + update interval: 60 + autobuild overlays: true + host overlay: true + syslog: false +dhcp: + enabled: true + range start: 10.0.1.1 + range end: 10.0.1.255 + systemd name: dhcpd +tftp: + enabled: true + systemd name: tftp + ipxe: + 00:0B: arm64-efi/snponly.efi + "00:00": undionly.kpxe + "00:07": ipxe-snponly-x86_64.efi + "00:09": ipxe-snponly-x86_64.efi +nfs: + enabled: true + export paths: + - path: /home + export options: rw,sync + mount options: defaults + mount: true + - path: /opt + export options: ro,sync,no_root_squash + mount options: defaults + mount: false + systemd name: nfs-server +ssh: + key types: + - rsa + - dsa + - ecdsa + - ed25519 +container mounts: + - source: /etc/resolv.conf + dest: /etc/resolv.conf + readonly: true +`, + }, +} + +func Test_UpgradeConfig(t *testing.T) { + for _, tt := range configUpgradeTests { + t.Run(tt.name, func(t *testing.T) { + legacy, err := ParseConfig([]byte(tt.legacyYaml)) + assert.NoError(t, err) + upgraded := legacy.Upgrade() + upgradedYaml, err := upgraded.Dump() + assert.NoError(t, err) + assert.Equal(t, strings.TrimSpace(tt.upgradedYaml), strings.TrimSpace(string(upgradedYaml))) + }) + } +} diff --git a/internal/pkg/upgrade/logging.go b/internal/pkg/upgrade/logging.go new file mode 100644 index 00000000..68b1e01d --- /dev/null +++ b/internal/pkg/upgrade/logging.go @@ -0,0 +1,15 @@ +package upgrade + +import ( + "github.com/warewulf/warewulf/internal/pkg/wwlog" +) + +func logIgnore(name string, value interface{}, reason string) { + wwlog.Warn("ignore: %s: %v (%s)", name, value, reason) +} + +func warnError(err error) { + if err != nil { + wwlog.Warn("%s", err) + } +} diff --git a/internal/pkg/upgrade/node.go b/internal/pkg/upgrade/node.go new file mode 100644 index 00000000..d02e1a5f --- /dev/null +++ b/internal/pkg/upgrade/node.go @@ -0,0 +1,640 @@ +package upgrade + +import ( + "net" + "strconv" + "strings" + + "gopkg.in/yaml.v3" + + "github.com/warewulf/warewulf/internal/pkg/node" + "github.com/warewulf/warewulf/internal/pkg/util" + "github.com/warewulf/warewulf/internal/pkg/wwlog" +) + +var wwinitSplitOverlays = []string{ + "wwinit", + "wwclient", + "fstab", + "hostname", + "ssh.host_keys", + "issue", + "resolv", + "udev.netname", + "systemd.netname", + "ifcfg", + "NetworkManager", + "debian.interfaces", + "wicked", + "ignition", +} + +var genericSplitOverlays = []string{ + "hosts", + "ssh.authorized_keys", + "syncuser", +} + +func ParseNodes(data []byte) (nodesYaml *NodesYaml, err error) { + nodesYaml = new(NodesYaml) + if err = yaml.Unmarshal(data, nodesYaml); err != nil { + return nodesYaml, err + } + return nodesYaml, nil +} + +type NodesYaml struct { + WWInternal string `yaml:"WW_INTERNAL"` + NodeProfiles map[string]*Profile + Nodes map[string]*Node +} + +func (this *NodesYaml) Upgrade(addDefaults bool, replaceOverlays bool) (upgraded *node.NodesYaml) { + upgraded = new(node.NodesYaml) + upgraded.NodeProfiles = make(map[string]*node.Profile) + upgraded.Nodes = make(map[string]*node.Node) + if this.WWInternal != "" { + logIgnore("WW_INTERNAL", this.WWInternal, "obsolete") + } + for name, profile := range this.NodeProfiles { + upgraded.NodeProfiles[name] = profile.Upgrade(addDefaults, replaceOverlays) + } + for name, node := range this.Nodes { + upgraded.Nodes[name] = node.Upgrade(addDefaults, replaceOverlays) + if addDefaults && !util.InSlice(upgraded.Nodes[name].Profiles, "default") { + wwlog.Warn("node %s does not include the default profile: verify default settings manually", name) + } + } + if addDefaults { + if _, ok := upgraded.NodeProfiles["default"]; !ok { + upgraded.NodeProfiles["default"] = new(node.Profile) + upgraded.NodeProfiles["default"].Kernel = new(node.KernelConf) + } + defaultProfile := upgraded.NodeProfiles["default"] + if len(defaultProfile.SystemOverlay) == 0 { + defaultProfile.SystemOverlay = append( + defaultProfile.SystemOverlay, wwinitSplitOverlays...) + } + if len(defaultProfile.RuntimeOverlay) == 0 { + defaultProfile.RuntimeOverlay = append( + defaultProfile.RuntimeOverlay, genericSplitOverlays...) + } + if defaultProfile.Kernel.Args == "" { + defaultProfile.Kernel.Args = "quiet crashkernel=no vga=791 net.naming-scheme=v238" + } + if defaultProfile.Init == "" { + defaultProfile.Init = "/sbin/init" + } + if defaultProfile.Root == "" { + defaultProfile.Root = "initramfs" + } + if defaultProfile.Ipxe == "" { + defaultProfile.Ipxe = "default" + } + } + return upgraded +} + +type Node struct { + Profile `yaml:"-,inline"` +} + +func (this *Node) Upgrade(addDefaults bool, replaceOverlays bool) (upgraded *node.Node) { + upgraded = new(node.Node) + upgraded.Tags = make(map[string]string) + upgraded.Disks = make(map[string]*node.Disk) + upgraded.FileSystems = make(map[string]*node.FileSystem) + upgraded.Ipmi = new(node.IpmiConf) + upgraded.Kernel = new(node.KernelConf) + upgraded.NetDevs = make(map[string]*node.NetDev) + upgraded.AssetKey = this.AssetKey + upgraded.ClusterName = this.ClusterName + upgraded.Comment = this.Comment + upgraded.ContainerName = this.ContainerName + if this.Disabled != "" { + logIgnore("Disabled", this.Disabled, "obsolete") + } + if this.Discoverable != "" { + warnError(upgraded.Discoverable.Set(this.Discoverable)) + } + if this.Disks != nil { + for name, disk := range this.Disks { + upgraded.Disks[name] = disk.Upgrade() + } + } + if this.FileSystems != nil { + for name, fileSystem := range this.FileSystems { + upgraded.FileSystems[name] = fileSystem.Upgrade() + } + } + upgraded.Init = this.Init + if this.Ipmi != nil { + upgraded.Ipmi = this.Ipmi.Upgrade() + } else { + upgraded.Ipmi = new(node.IpmiConf) + } + if upgraded.Ipmi.EscapeChar == "" { + upgraded.Ipmi.EscapeChar = this.IpmiEscapeChar + } + if upgraded.Ipmi.Gateway.Equal(net.IP{}) { + upgraded.Ipmi.Gateway = net.ParseIP(this.IpmiGateway) + } + if upgraded.Ipmi.Interface == "" { + upgraded.Ipmi.Interface = this.IpmiInterface + } + if upgraded.Ipmi.Ipaddr.Equal(net.IP{}) { + upgraded.Ipmi.Ipaddr = net.ParseIP(this.IpmiIpaddr) + } + if upgraded.Ipmi.Netmask.Equal(net.IP{}) { + upgraded.Ipmi.Netmask = net.ParseIP(this.IpmiNetmask) + } + if upgraded.Ipmi.Password == "" { + upgraded.Ipmi.Password = this.IpmiPassword + } + if upgraded.Ipmi.Port == "" { + upgraded.Ipmi.Port = this.IpmiPort + } + if upgraded.Ipmi.UserName == "" { + upgraded.Ipmi.UserName = this.IpmiUserName + } + if upgraded.Ipmi.Write == "" && this.IpmiWrite != "" { + warnError(upgraded.Ipmi.Write.Set(this.IpmiWrite)) + } + upgraded.Ipxe = this.Ipxe + if this.Kernel != nil { + upgraded.Kernel = this.Kernel.Upgrade() + } else { + upgraded.Kernel = new(node.KernelConf) + } + if upgraded.Kernel.Args == "" { + upgraded.Kernel.Args = this.KernelArgs + } + if upgraded.Kernel.Override == "" { + upgraded.Kernel.Override = this.KernelOverride + } + if upgraded.Kernel.Version == "" { + upgraded.Kernel.Version = this.KernelVersion + } + if this.Keys != nil { + for key, value := range this.Keys { + upgraded.Tags[key] = value + } + } + if this.NetDevs != nil { + for name, netDev := range this.NetDevs { + upgraded.NetDevs[name] = netDev.Upgrade(addDefaults) + } + } + if this.PrimaryNetDev != "" { + upgraded.PrimaryNetDev = this.PrimaryNetDev + } else { + for name, netDev := range this.NetDevs { + if b, _ := strconv.ParseBool(netDev.Primary); b { + upgraded.PrimaryNetDev = name + break + } else if b, _ := strconv.ParseBool(netDev.Default); b { + upgraded.PrimaryNetDev = name + break + } + } + } + upgraded.Profiles = append(upgraded.Profiles, this.Profiles...) + if addDefaults { + if len(upgraded.Profiles) == 0 { + upgraded.Profiles = append(upgraded.Profiles, "default") + } + } + upgraded.Root = this.Root + if this.RuntimeOverlay != nil { + switch overlay := this.RuntimeOverlay.(type) { + case string: + upgraded.RuntimeOverlay = append(upgraded.RuntimeOverlay, strings.Split(overlay, ",")...) + case []interface{}: + for _, each := range overlay { + upgraded.RuntimeOverlay = append(upgraded.RuntimeOverlay, each.(string)) + } + default: + wwlog.Error("unparsable RuntimeOverlay: %v", overlay) + } + } + if this.SystemOverlay != nil { + switch overlay := this.SystemOverlay.(type) { + case string: + upgraded.SystemOverlay = append(upgraded.SystemOverlay, strings.Split(overlay, ",")...) + case []interface{}: + for _, each := range overlay { + upgraded.SystemOverlay = append(upgraded.SystemOverlay, each.(string)) + } + default: + wwlog.Error("unparsable SystemOverlay: %v", overlay) + } + } + if replaceOverlays { + if indexOf(upgraded.SystemOverlay, "wwinit") != -1 { + upgraded.SystemOverlay = replaceSliceElement( + upgraded.SystemOverlay, + indexOf(upgraded.SystemOverlay, "wwinit"), + wwinitSplitOverlays) + } + if indexOf(upgraded.RuntimeOverlay, "generic") != -1 { + upgraded.RuntimeOverlay = replaceSliceElement( + upgraded.RuntimeOverlay, + indexOf(upgraded.RuntimeOverlay, "generic"), + genericSplitOverlays) + } + } + if this.Tags != nil { + for key, value := range this.Tags { + upgraded.Tags[key] = value + } + } + for _, tag := range this.TagsDel { + delete(upgraded.Tags, tag) + } + return +} + +type Profile struct { + AssetKey string `yaml:"asset key,omitempty"` + ClusterName string `yaml:"cluster name,omitempty"` + Comment string `yaml:"comment,omitempty"` + ContainerName string `yaml:"container name,omitempty"` + Disabled string `yaml:"disabled,omitempty"` + Discoverable string `yaml:"discoverable,omitempty"` + Disks map[string]*Disk `yaml:"disks,omitempty"` + FileSystems map[string]*FileSystem `yaml:"filesystems,omitempty"` + Init string `yaml:"init,omitempty"` + Ipmi *IpmiConf `yaml:"ipmi,omitempty"` + IpmiEscapeChar string `yaml:"ipmi escapechar,omitempty"` + IpmiGateway string `yaml:"ipmi gateway,omitempty"` + IpmiInterface string `yaml:"ipmi interface,omitempty"` + IpmiIpaddr string `yaml:"ipmi ipaddr,omitempty"` + IpmiNetmask string `yaml:"ipmi netmask,omitempty"` + IpmiPassword string `yaml:"ipmi password,omitempty"` + IpmiPort string `yaml:"ipmi port,omitempty"` + IpmiUserName string `yaml:"ipmi username,omitempty"` + IpmiWrite string `yaml:"ipmi write,omitempty"` + Ipxe string `yaml:"ipxe template,omitempty"` + Kernel *KernelConf `yaml:"kernel,omitempty"` + KernelArgs string `yaml:"kernel args,omitempty"` + KernelOverride string `yaml:"kernel override,omitempty"` + KernelVersion string `yaml:"kernel version,omitempty"` + Keys map[string]string `yaml:"keys,omitempty"` + NetDevs map[string]*NetDev `yaml:"network devices,omitempty"` + PrimaryNetDev string `yaml:"primary network,omitempty"` + Profiles []string `yaml:"profiles,omitempty"` + Root string `yaml:"root,omitempty"` + RuntimeOverlay interface{} `yaml:"runtime overlay,omitempty"` + SystemOverlay interface{} `yaml:"system overlay,omitempty"` + Tags map[string]string `yaml:"tags,omitempty"` + TagsDel []string `yaml:"tagsdel,omitempty"` +} + +func (this *Profile) Upgrade(addDefaults bool, replaceOverlays bool) (upgraded *node.Profile) { + upgraded = new(node.Profile) + upgraded.Tags = make(map[string]string) + upgraded.Disks = make(map[string]*node.Disk) + upgraded.FileSystems = make(map[string]*node.FileSystem) + upgraded.Kernel = new(node.KernelConf) + upgraded.NetDevs = make(map[string]*node.NetDev) + if this.AssetKey != "" { + logIgnore("AssetKey", this.AssetKey, "invalid for profiles") + } + upgraded.ClusterName = this.ClusterName + upgraded.Comment = this.Comment + upgraded.ContainerName = this.ContainerName + if this.Disabled != "" { + logIgnore("Disabled", this.Disabled, "obsolete") + } + if this.Discoverable != "" { + logIgnore("Discoverable", this.Discoverable, "invalid for profiles") + } + if this.Disks != nil { + for name, disk := range this.Disks { + upgraded.Disks[name] = disk.Upgrade() + } + } + if this.FileSystems != nil { + for name, fileSystem := range this.FileSystems { + upgraded.FileSystems[name] = fileSystem.Upgrade() + } + } + upgraded.Init = this.Init + upgraded.Ipmi = new(node.IpmiConf) + if this.Ipmi != nil { + upgraded.Ipmi = this.Ipmi.Upgrade() + } else { + upgraded.Ipmi = new(node.IpmiConf) + } + if upgraded.Ipmi.EscapeChar == "" { + upgraded.Ipmi.EscapeChar = this.IpmiEscapeChar + } + if upgraded.Ipmi.Gateway.Equal(net.IP{}) { + upgraded.Ipmi.Gateway = net.ParseIP(this.IpmiGateway) + } + if upgraded.Ipmi.Interface == "" { + upgraded.Ipmi.Interface = this.IpmiInterface + } + if upgraded.Ipmi.Ipaddr.Equal(net.IP{}) { + upgraded.Ipmi.Ipaddr = net.ParseIP(this.IpmiIpaddr) + } + if upgraded.Ipmi.Netmask.Equal(net.IP{}) { + upgraded.Ipmi.Netmask = net.ParseIP(this.IpmiNetmask) + } + if upgraded.Ipmi.Password == "" { + upgraded.Ipmi.Password = this.IpmiPassword + } + if upgraded.Ipmi.Port == "" { + upgraded.Ipmi.Port = this.IpmiPort + } + if upgraded.Ipmi.UserName == "" { + upgraded.Ipmi.UserName = this.IpmiUserName + } + if upgraded.Ipmi.Write == "" && this.IpmiWrite != "" { + warnError(upgraded.Ipmi.Write.Set(this.IpmiWrite)) + } + upgraded.Ipxe = this.Ipxe + if this.Kernel != nil { + upgraded.Kernel = this.Kernel.Upgrade() + } else { + upgraded.Kernel = new(node.KernelConf) + } + if upgraded.Kernel.Args == "" { + upgraded.Kernel.Args = this.KernelArgs + } + if upgraded.Kernel.Override == "" { + upgraded.Kernel.Override = this.KernelOverride + } + if upgraded.Kernel.Version == "" { + upgraded.Kernel.Version = this.KernelVersion + } + if this.Keys != nil { + for key, value := range this.Keys { + upgraded.Tags[key] = value + } + } + if this.NetDevs != nil { + for name, netDev := range this.NetDevs { + upgraded.NetDevs[name] = netDev.Upgrade(addDefaults) + } + } + if this.PrimaryNetDev != "" { + upgraded.PrimaryNetDev = this.PrimaryNetDev + } else { + for name, netDev := range this.NetDevs { + if b, _ := strconv.ParseBool(netDev.Primary); b { + upgraded.PrimaryNetDev = name + break + } else if b, _ := strconv.ParseBool(netDev.Default); b { + upgraded.PrimaryNetDev = name + break + } + } + } + if this.Profiles != nil { + logIgnore("Profiles", this.Profiles, "invalid for profiles") + } + upgraded.Root = this.Root + if this.RuntimeOverlay != nil { + switch overlay := this.RuntimeOverlay.(type) { + case string: + upgraded.RuntimeOverlay = append(upgraded.RuntimeOverlay, strings.Split(overlay, ",")...) + case []interface{}: + for _, each := range overlay { + upgraded.RuntimeOverlay = append(upgraded.RuntimeOverlay, each.(string)) + } + default: + wwlog.Error("unparsable RuntimeOverlay: %v", overlay) + } + } + if this.SystemOverlay != nil { + switch overlay := this.SystemOverlay.(type) { + case string: + upgraded.SystemOverlay = append(upgraded.SystemOverlay, strings.Split(overlay, ",")...) + case []interface{}: + for _, each := range overlay { + upgraded.SystemOverlay = append(upgraded.SystemOverlay, each.(string)) + } + default: + wwlog.Error("unparsable SystemOverlay: %v", overlay) + } + } + if replaceOverlays { + if indexOf(upgraded.SystemOverlay, "wwinit") != -1 { + upgraded.SystemOverlay = replaceSliceElement( + upgraded.SystemOverlay, + indexOf(upgraded.SystemOverlay, "wwinit"), + wwinitSplitOverlays) + } + if indexOf(upgraded.RuntimeOverlay, "generic") != -1 { + upgraded.RuntimeOverlay = replaceSliceElement( + upgraded.RuntimeOverlay, + indexOf(upgraded.RuntimeOverlay, "generic"), + genericSplitOverlays) + } + } + if this.Tags != nil { + for key, value := range this.Tags { + upgraded.Tags[key] = value + } + } + for _, tag := range this.TagsDel { + delete(upgraded.Tags, tag) + } + return +} + +type IpmiConf struct { + EscapeChar string `yaml:"escapechar,omitempty"` + Gateway string `yaml:"gateway,omitempty"` + Interface string `yaml:"interface,omitempty"` + Ipaddr string `yaml:"ipaddr,omitempty"` + Netmask string `yaml:"netmask,omitempty"` + Password string `yaml:"password,omitempty"` + Port string `yaml:"port,omitempty"` + Tags map[string]string `yaml:"tags,omitempty"` + TagsDel []string `yaml:"tagsdel,omitempty"` + UserName string `yaml:"username,omitempty"` + Write string `yaml:"write,omitempty"` +} + +func (this *IpmiConf) Upgrade() (upgraded *node.IpmiConf) { + upgraded = new(node.IpmiConf) + upgraded.Tags = make(map[string]string) + upgraded.EscapeChar = this.EscapeChar + upgraded.Gateway = net.ParseIP(this.Gateway) + upgraded.Interface = this.Interface + upgraded.Ipaddr = net.ParseIP(this.Ipaddr) + upgraded.Netmask = net.ParseIP(this.Netmask) + upgraded.Password = this.Password + upgraded.Port = this.Port + if this.Tags != nil { + for key, value := range this.Tags { + upgraded.Tags[key] = value + } + } + for _, tag := range this.TagsDel { + delete(upgraded.Tags, tag) + } + upgraded.UserName = this.UserName + if this.Write != "" { + warnError(upgraded.Write.Set(this.Write)) + } + return +} + +type KernelConf struct { + Args string `yaml:"args,omitempty"` + Override string `yaml:"override,omitempty"` + Version string `yaml:"version,omitempty"` +} + +func (this *KernelConf) Upgrade() (upgraded *node.KernelConf) { + upgraded = new(node.KernelConf) + upgraded.Args = this.Args + upgraded.Override = this.Override + upgraded.Version = this.Version + return +} + +type NetDev struct { + Default string `yaml:"default"` + Device string `yaml:"device,omitempty"` + Gateway string `yaml:"gateway,omitempty"` + Hwaddr string `yaml:"hwaddr,omitempty"` + IpCIDR string `yaml:"ipcidr,omitempty"` + Ipaddr string `yaml:"ipaddr,omitempty"` + Ipaddr6 string `yaml:"ip6addr,omitempty"` + MTU string `yaml:"mtu,omitempty"` + Netmask string `yaml:"netmask,omitempty"` + OnBoot string `yaml:"onboot,omitempty"` + Prefix string `yaml:"prefix,omitempty"` + Primary string `yaml:"primary,omitempty"` + Tags map[string]string `yaml:"tags,omitempty"` + TagsDel []string `yaml:"tagsdel,omitempty"` + Type string `yaml:"type,omitempty"` +} + +func (this *NetDev) Upgrade(addDefaults bool) (upgraded *node.NetDev) { + upgraded = new(node.NetDev) + upgraded.Tags = make(map[string]string) + upgraded.Device = this.Device + upgraded.Gateway = net.ParseIP(this.Gateway) + upgraded.Hwaddr = this.Hwaddr + upgraded.Ipaddr = net.ParseIP(this.Ipaddr) + upgraded.Ipaddr6 = net.ParseIP(this.Ipaddr6) + upgraded.MTU = this.MTU + upgraded.Netmask = net.ParseIP(this.Netmask) + if this.IpCIDR != "" { + cidrIP, cidrIPNet, err := net.ParseCIDR(this.IpCIDR) + if err != nil { + wwlog.Error("%v is not a valid CIDR address: %w", this.IpCIDR, err) + } else { + if upgraded.Ipaddr == nil { + upgraded.Ipaddr = cidrIP + } + if upgraded.Netmask == nil { + upgraded.Netmask = net.IP(cidrIPNet.Mask) + } + } + } + if this.OnBoot != "" { + warnError(upgraded.OnBoot.Set(this.OnBoot)) + } + upgraded.Prefix = net.ParseIP(this.Prefix) + if this.Tags != nil { + for key, value := range this.Tags { + upgraded.Tags[key] = value + } + } + for _, tag := range this.TagsDel { + delete(upgraded.Tags, tag) + } + upgraded.Type = this.Type + if addDefaults { + if upgraded.Type == "" { + upgraded.Type = "ethernet" + } + if upgraded.Netmask == nil { + upgraded.Netmask = net.ParseIP("255.255.255.0") + } + } + return +} + +type Disk struct { + Partitions map[string]*Partition `yaml:"partitions,omitempty"` + WipeTable string `yaml:"wipe_table,omitempty"` +} + +func (this *Disk) Upgrade() (upgraded *node.Disk) { + upgraded = new(node.Disk) + upgraded.Partitions = make(map[string]*node.Partition) + if this.Partitions != nil { + for name, partition := range this.Partitions { + upgraded.Partitions[name] = partition.Upgrade() + } + } + upgraded.WipeTable, _ = strconv.ParseBool(this.WipeTable) + return +} + +type Partition struct { + Guid string `yaml:"guid,omitempty"` + Number string `yaml:"number,omitempty"` + Resize string `yaml:"resize,omitempty"` + ShouldExist string `yaml:"should_exist,omitempty"` + SizeMiB string `yaml:"size_mib,omitempty"` + StartMiB string `yaml:"start_mib,omitempty"` + TypeGuid string `yaml:"type_guid,omitempty"` + WipePartitionEntry string `yaml:"wipe_partition_entry,omitempty"` +} + +func (this *Partition) Upgrade() (upgraded *node.Partition) { + upgraded = new(node.Partition) + upgraded.Guid = this.Guid + upgraded.Number = this.Number + upgraded.Resize, _ = strconv.ParseBool(this.Resize) + upgraded.ShouldExist, _ = strconv.ParseBool(this.ShouldExist) + upgraded.SizeMiB = this.SizeMiB + upgraded.StartMiB = this.StartMiB + upgraded.TypeGuid = this.TypeGuid + upgraded.WipePartitionEntry, _ = strconv.ParseBool(this.WipePartitionEntry) + return +} + +type FileSystem struct { + Format string `yaml:"format,omitempty"` + Label string `yaml:"label,omitempty"` + MountOptions interface{} `yaml:"mount_options,omitempty"` + Options []string `yaml:"options,omitempty"` + Path string `yaml:"path,omitempty"` + Uuid string `yaml:"uuid,omitempty"` + WipeFileSystem string `yaml:"wipe_filesystem,omitempty"` +} + +func (this *FileSystem) Upgrade() (upgraded *node.FileSystem) { + upgraded = new(node.FileSystem) + upgraded.Options = make([]string, 0) + upgraded.Format = this.Format + upgraded.Label = this.Label + if this.MountOptions != nil { + switch mountOptions := this.MountOptions.(type) { + case string: + upgraded.MountOptions = mountOptions + case []interface{}: + mountOptionsStrings := make([]string, 0) + for _, option := range mountOptions { + mountOptionsStrings = append(mountOptionsStrings, option.(string)) + } + upgraded.MountOptions = strings.Join(mountOptionsStrings, " ") + default: + wwlog.Error("unparsable MountOptions: %v", mountOptions) + } + } + upgraded.Options = append(upgraded.Options, this.Options...) + upgraded.Path = this.Path + upgraded.Uuid = this.Uuid + upgraded.WipeFileSystem, _ = strconv.ParseBool(this.WipeFileSystem) + return +} diff --git a/internal/pkg/upgrade/node_test.go b/internal/pkg/upgrade/node_test.go new file mode 100644 index 00000000..1f28f563 --- /dev/null +++ b/internal/pkg/upgrade/node_test.go @@ -0,0 +1,700 @@ +package upgrade + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +var nodesYamlUpgradeTests = []struct { + name string + addDefaults bool + replaceOverlays bool + legacyYaml string + upgradedYaml string +}{ + { + name: "captured vers42 example", + addDefaults: false, + replaceOverlays: false, + legacyYaml: ` +nodeprofiles: + default: + comment: This profile is automatically included for each node + runtime overlay: "generic" + discoverable: false + leap: + comment: openSUSE leap + kernel version: 5.14.21 + ipmi netmask: "255.255.255.0" + keys: + foo: baar + network devices: + lan1: + gateway: 1.1.1.1 +nodes: + node01: + system overlay: "nodeoverlay" + discoverable: true + network devices: + eth0: + ipaddr: 1.2.3.4 + default: true +`, + upgradedYaml: ` +nodeprofiles: + default: + comment: This profile is automatically included for each node + runtime overlay: + - generic + leap: + comment: openSUSE leap + kernel: + version: 5.14.21 + ipmi: + netmask: 255.255.255.0 + network devices: + lan1: + gateway: 1.1.1.1 + tags: + foo: baar +nodes: + node01: + discoverable: "true" + system overlay: + - nodeoverlay + network devices: + eth0: + ipaddr: 1.2.3.4 + primary network: eth0 +`, + }, + { + name: "captured vers43 example", + addDefaults: false, + replaceOverlays: false, + legacyYaml: ` +WW_INTERNAL: 45 +nodeprofiles: + default: + comment: This profile is automatically included for each node + runtime overlay: + - generic + discoverable: "false" + leap: + comment: openSUSE leap + kernel: + override: 5.14.21 + ipmi: + netmask: 255.255.255.0 + discoverable: "false" + network devices: + lan1: + device: lan1 + gateway: 1.1.1.1 + default: "false" + keys: + foo: baar +nodes: + node01: + system overlay: + - nodeoverlay + discoverable: "true" + network devices: + eth0: + device: eth0 + ipaddr: 1.2.3.4 + default: "true" +`, + upgradedYaml: ` +nodeprofiles: + default: + comment: This profile is automatically included for each node + runtime overlay: + - generic + leap: + comment: openSUSE leap + kernel: + override: 5.14.21 + ipmi: + netmask: 255.255.255.0 + network devices: + lan1: + device: lan1 + gateway: 1.1.1.1 + tags: + foo: baar +nodes: + node01: + discoverable: "true" + system overlay: + - nodeoverlay + network devices: + eth0: + device: eth0 + ipaddr: 1.2.3.4 + primary network: eth0 +`, + }, + { + name: "remove WW_INTERNAL", + addDefaults: false, + replaceOverlays: false, + legacyYaml: `WW_INTERNAL: 45`, + upgradedYaml: ` +nodeprofiles: {} +nodes: {} +`, + }, + { + name: "disabled is obsolete", + addDefaults: false, + replaceOverlays: false, + legacyYaml: ` +nodes: + n1: + disabled: true +nodeprofiles: + default: + disabled: true +`, + upgradedYaml: ` +nodeprofiles: + default: {} +nodes: + n1: {} +`, + }, + { + name: "inline IPMI settings", + addDefaults: false, + replaceOverlays: false, + legacyYaml: ` +nodes: + n1: + ipmi escapechar: "~" + ipmi gateway: 192.168.0.1 + ipmi interface: lanplus + ipmi ipaddr: 192.168.0.100 + ipmi netmask: 255.255.255.0 + ipmi password: password + ipmi port: 623 + ipmi username: admin + ipmi write: true +nodeprofiles: + default: + ipmi escapechar: "~" + ipmi gateway: 192.168.0.1 + ipmi interface: lanplus + ipmi ipaddr: 192.168.0.100 + ipmi netmask: 255.255.255.0 + ipmi password: password + ipmi port: 623 + ipmi username: admin + ipmi write: true +`, + upgradedYaml: ` +nodeprofiles: + default: + ipmi: + username: admin + password: password + ipaddr: 192.168.0.100 + gateway: 192.168.0.1 + netmask: 255.255.255.0 + port: "623" + interface: lanplus + escapechar: "~" + write: "true" +nodes: + n1: + ipmi: + username: admin + password: password + ipaddr: 192.168.0.100 + gateway: 192.168.0.1 + netmask: 255.255.255.0 + port: "623" + interface: lanplus + escapechar: "~" + write: "true" +`, + }, + { + name: "inline Kernel settings", + addDefaults: false, + replaceOverlays: false, + legacyYaml: ` +nodeprofiles: + default: + kernel args: quiet + kernel override: rockylinux-9 + kernel version: 2.6 +nodes: + n1: + kernel args: quiet + kernel override: rockylinux-9 + kernel version: 2.6 +`, + upgradedYaml: ` +nodeprofiles: + default: + kernel: + version: "2.6" + override: rockylinux-9 + args: quiet +nodes: + n1: + kernel: + version: "2.6" + override: rockylinux-9 + args: quiet +`, + }, + { + name: "keys and tags", + addDefaults: false, + replaceOverlays: false, + legacyYaml: ` +nodeprofiles: + default: + keys: + key1: val1 + key2: val2 + tags: + key2: valB + key3: valC + key4: valD + tagsdel: + - key4 + network devices: + default: + tags: + key2: valB + key3: valC + key4: valD + tagsdel: + - key4 + ipmi: + tags: + key2: valB + key3: valC + key4: valD + tagsdel: + - key4 +nodes: + n1: + keys: + key1: val1 + key2: val2 + tags: + key2: valB + key3: valC + key4: valD + tagsdel: + - key4 + network devices: + default: + tags: + key2: valB + key3: valC + key4: valD + tagsdel: + - key4 + ipmi: + tags: + key2: valB + key3: valC + key4: valD + tagsdel: + - key4 +`, + upgradedYaml: ` +nodeprofiles: + default: + ipmi: + tags: + key2: valB + key3: valC + network devices: + default: + tags: + key2: valB + key3: valC + tags: + key1: val1 + key2: valB + key3: valC +nodes: + n1: + ipmi: + tags: + key2: valB + key3: valC + network devices: + default: + tags: + key2: valB + key3: valC + tags: + key1: val1 + key2: valB + key3: valC +`, + }, + { + name: "primary network", + addDefaults: false, + replaceOverlays: false, + legacyYaml: ` +nodes: + n1: + network devices: + eth0: {} + eth1: + default: true + n2: + network devices: + eth0: + primary: true + eth1: {} + n3: + network devices: + eth0: + primary: true + eth1: {} + primary network: eth1 +nodeprofiles: + p1: + network devices: + eth0: {} + eth1: + default: true + p2: + network devices: + eth0: + primary: true + eth1: {} + p3: + network devices: + eth0: + primary: true + eth1: {} + primary network: eth1 +`, + upgradedYaml: ` +nodeprofiles: + p1: + primary network: eth1 + p2: + primary network: eth0 + p3: + primary network: eth1 +nodes: + n1: + primary network: eth1 + n2: + primary network: eth0 + n3: + primary network: eth1 +`, + }, + { + name: "overlays", + addDefaults: false, + replaceOverlays: false, + legacyYaml: ` +nodes: + n1: + runtime overlay: + - r1 + - r2 + system overlay: + - s1 + - s2 + n2: + runtime overlay: r1,r2 + system overlay: s1,s2 +nodeprofiles: + p1: + runtime overlay: + - r1 + - r2 + system overlay: + - s1 + - s2 + p2: + runtime overlay: r1,r2 + system overlay: s1,s2 +`, + upgradedYaml: ` +nodeprofiles: + p1: + runtime overlay: + - r1 + - r2 + system overlay: + - s1 + - s2 + p2: + runtime overlay: + - r1 + - r2 + system overlay: + - s1 + - s2 +nodes: + n1: + runtime overlay: + - r1 + - r2 + system overlay: + - s1 + - s2 + n2: + runtime overlay: + - r1 + - r2 + system overlay: + - s1 + - s2 +`, + }, + { + name: "disk example", + addDefaults: false, + replaceOverlays: false, + legacyYaml: ` +nodes: + n1: + disks: + /dev/vda: + wipe_table: true + partitions: + scratch: + number: "1" + should_exist: true + swap: + number: "2" + size_mib: "1024" + filesystems: + /dev/disk/by-partlabel/scratch: + format: btrfs + path: /scratch + /dev/disk/by-partlabel/swap: + format: swap + path: swap +`, + upgradedYaml: ` +nodeprofiles: {} +nodes: + n1: + disks: + /dev/vda: + wipe_table: true + partitions: + scratch: + number: "1" + should_exist: true + swap: + number: "2" + size_mib: "1024" + filesystems: + /dev/disk/by-partlabel/scratch: + format: btrfs + path: /scratch + /dev/disk/by-partlabel/swap: + format: swap + path: swap +`, + }, + { + name: "add defaults", + addDefaults: true, + replaceOverlays: false, + legacyYaml: ` +nodes: + n1: + network devices: + default: + ipaddr: 192.168.0.100 +`, + upgradedYaml: ` +nodeprofiles: + default: + ipxe template: default + runtime overlay: + - hosts + - ssh.authorized_keys + - syncuser + system overlay: + - wwinit + - wwclient + - fstab + - hostname + - ssh.host_keys + - issue + - resolv + - udev.netname + - systemd.netname + - ifcfg + - NetworkManager + - debian.interfaces + - wicked + - ignition + kernel: + args: quiet crashkernel=no vga=791 net.naming-scheme=v238 + init: /sbin/init + root: initramfs +nodes: + n1: + profiles: + - default + network devices: + default: + type: ethernet + ipaddr: 192.168.0.100 + netmask: 255.255.255.0 +`, + }, + { + name: "add defaults conflicts", + addDefaults: true, + replaceOverlays: false, + legacyYaml: ` +nodeprofiles: + default: + system overlay: + - wwinit + - wwclient + - fstab + - hostname + - ssh.host_keys + - issue + - resolv + - udev.netname + - systemd.netname + - NetworkManager + custom: {} +nodes: + n1: + profiles: + - custom + network devices: + default: + ipaddr: 10.0.0.100 + netmask: 255.255.0.0 +`, + upgradedYaml: ` +nodeprofiles: + custom: {} + default: + ipxe template: default + runtime overlay: + - hosts + - ssh.authorized_keys + - syncuser + system overlay: + - wwinit + - wwclient + - fstab + - hostname + - ssh.host_keys + - issue + - resolv + - udev.netname + - systemd.netname + - NetworkManager + kernel: + args: quiet crashkernel=no vga=791 net.naming-scheme=v238 + init: /sbin/init + root: initramfs +nodes: + n1: + profiles: + - custom + network devices: + default: + type: ethernet + ipaddr: 10.0.0.100 + netmask: 255.255.0.0 +`, + }, + { + name: "add defaults conflicts", + addDefaults: false, + replaceOverlays: true, + legacyYaml: ` +nodeprofiles: + default: + runtime overlay: + - generic + system overlay: + - wwinit +nodes: + n1: + runtime overlay: + - generic + system overlay: + - wwinit +`, + upgradedYaml: ` +nodeprofiles: + default: + runtime overlay: + - hosts + - ssh.authorized_keys + - syncuser + system overlay: + - wwinit + - wwclient + - fstab + - hostname + - ssh.host_keys + - issue + - resolv + - udev.netname + - systemd.netname + - ifcfg + - NetworkManager + - debian.interfaces + - wicked + - ignition +nodes: + n1: + runtime overlay: + - hosts + - ssh.authorized_keys + - syncuser + system overlay: + - wwinit + - wwclient + - fstab + - hostname + - ssh.host_keys + - issue + - resolv + - udev.netname + - systemd.netname + - ifcfg + - NetworkManager + - debian.interfaces + - wicked + - ignition +`, + }, +} + +func Test_UpgradeNodesYaml(t *testing.T) { + for _, tt := range nodesYamlUpgradeTests { + t.Run(tt.name, func(t *testing.T) { + legacy, err := ParseNodes([]byte(tt.legacyYaml)) + assert.NoError(t, err) + upgraded := legacy.Upgrade(tt.addDefaults, tt.replaceOverlays) + upgradedYaml, err := upgraded.Dump() + assert.NoError(t, err) + assert.Equal(t, strings.TrimSpace(tt.upgradedYaml), strings.TrimSpace(string(upgradedYaml))) + }) + } +} diff --git a/internal/pkg/upgrade/slices.go b/internal/pkg/upgrade/slices.go new file mode 100644 index 00000000..780350d3 --- /dev/null +++ b/internal/pkg/upgrade/slices.go @@ -0,0 +1,17 @@ +package upgrade + +func indexOf[T comparable](slice []T, item T) int { + for i, v := range slice { + if v == item { + return i + } + } + return -1 +} + +func replaceSliceElement[T any](original []T, index int, replacement []T) []T { + if index < 0 || index >= len(original) { + return original + } + return append(original[:index], append(replacement, original[index+1:]...)...) +} diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go index 67b95378..e2099844 100644 --- a/internal/pkg/util/util.go +++ b/internal/pkg/util/util.go @@ -18,6 +18,10 @@ import ( "github.com/warewulf/warewulf/internal/pkg/wwlog" ) +func BoolP(p *bool) bool { + return p != nil && *p +} + func FirstError(errs ...error) (err error) { for _, e := range errs { if err == nil { diff --git a/internal/pkg/warewulfd/daemon.go b/internal/pkg/warewulfd/daemon.go index 2ab8f6ba..1a338f71 100644 --- a/internal/pkg/warewulfd/daemon.go +++ b/internal/pkg/warewulfd/daemon.go @@ -57,7 +57,7 @@ func DaemonInitLogging() error { conf := warewulfconf.Get() - if conf.Warewulf.Syslog { + if conf.Warewulf.Syslog() { wwlog.Debug("Changing log output to syslog") diff --git a/internal/pkg/warewulfd/nodedb.go b/internal/pkg/warewulfd/nodedb.go index 32903c4a..e9f23986 100644 --- a/internal/pkg/warewulfd/nodedb.go +++ b/internal/pkg/warewulfd/nodedb.go @@ -12,7 +12,7 @@ import ( type nodeDB struct { lock sync.RWMutex NodeInfo map[string]string - yml node.NodeYaml + yml node.NodesYaml } var ( @@ -53,7 +53,7 @@ func loadNodeDB() (err error) { return nil } -func GetNodeOrSetDiscoverable(hwaddr string) (node.NodeConf, error) { +func GetNodeOrSetDiscoverable(hwaddr string) (node.Node, error) { db.lock.RLock() defer db.lock.RUnlock() // NOTE: since discoverable nodes will write an updated DB to file and then diff --git a/internal/pkg/warewulfd/provision.go b/internal/pkg/warewulfd/provision.go index 9426e067..5a165a55 100644 --- a/internal/pkg/warewulfd/provision.go +++ b/internal/pkg/warewulfd/provision.go @@ -35,7 +35,7 @@ type templateVars struct { KernelArgs string KernelOverride string Tags map[string]string - NetDevs map[string]*node.NetDevs + NetDevs map[string]*node.NetDev } func ProvisionSend(w http.ResponseWriter, req *http.Request) { @@ -50,7 +50,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { wwlog.Info("request from hwaddr:%s ipaddr:%s | stage:%s", rinfo.hwaddr, req.RemoteAddr, rinfo.stage) - if (rinfo.stage == "runtime" || len(rinfo.overlay) > 0) && conf.Warewulf.Secure { + if (rinfo.stage == "runtime" || len(rinfo.overlay) > 0) && conf.Warewulf.Secure() { if rinfo.remoteport >= 1024 { wwlog.Denied("Non-privileged port: %s", req.RemoteAddr) w.WriteHeader(http.StatusUnauthorized) @@ -149,7 +149,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) { remoteNode, context, request_overlays, - conf.Warewulf.AutobuildOverlays) + conf.Warewulf.AutobuildOverlays()) if err != nil { if errors.Is(err, overlay.ErrDoesNotExist) { diff --git a/internal/pkg/warewulfd/provision_test.go b/internal/pkg/warewulfd/provision_test.go index ba1bc977..cc35f909 100644 --- a/internal/pkg/warewulfd/provision_test.go +++ b/internal/pkg/warewulfd/provision_test.go @@ -37,8 +37,7 @@ var provisionSendTests = []struct { func Test_ProvisionSend(t *testing.T) { env := testenv.New(t) - env.WriteFile(t, "etc/warewulf/nodes.conf", `WW_INTERNAL: 45 -nodeprofiles: + env.WriteFile(t, "etc/warewulf/nodes.conf", `nodeprofiles: default: container name: suse nodes: @@ -102,7 +101,8 @@ nodes: dbErr := LoadNodeDB() assert.NoError(t, dbErr) - conf.Warewulf.Secure = false + secureFalse := false + conf.Warewulf.SecureP = &secureFalse assert.NoError(t, os.MkdirAll(path.Join(conf.Paths.OverlayProvisiondir(), "n1"), 0700)) assert.NoError(t, os.WriteFile(path.Join(conf.Paths.OverlayProvisiondir(), "n1", "__SYSTEM__.img"), []byte("system overlay"), 0600)) assert.NoError(t, os.WriteFile(path.Join(conf.Paths.OverlayProvisiondir(), "n1", "__RUNTIME__.img"), []byte("runtime overlay"), 0600)) diff --git a/internal/pkg/warewulfd/util.go b/internal/pkg/warewulfd/util.go index 0ba04921..7938d343 100644 --- a/internal/pkg/warewulfd/util.go +++ b/internal/pkg/warewulfd/util.go @@ -44,7 +44,7 @@ func sendFile( return nil } -func getOverlayFile(n node.NodeConf, context string, stage_overlays []string, autobuild bool) (stage_file string, err error) { +func getOverlayFile(n node.Node, context string, stage_overlays []string, autobuild bool) (stage_file string, err error) { stage_file = overlay.OverlayImage(n.Id(), context, stage_overlays) err = nil @@ -60,9 +60,9 @@ func getOverlayFile(n node.NodeConf, context string, stage_overlays []string, au if build { if len(stage_overlays) > 0 { - err = overlay.BuildSpecificOverlays([]node.NodeConf{n}, stage_overlays) + err = overlay.BuildSpecificOverlays([]node.Node{n}, stage_overlays) } else { - err = overlay.BuildAllOverlays([]node.NodeConf{n}) + err = overlay.BuildAllOverlays([]node.Node{n}) } if err != nil { wwlog.Error("Failed to build overlay: %s, %s, %s\n%s", diff --git a/overlays/NetworkManager/internal/nodes.conf b/overlays/NetworkManager/internal/nodes.conf index fefbdbc8..33a7b4d2 100644 --- a/overlays/NetworkManager/internal/nodes.conf +++ b/overlays/NetworkManager/internal/nodes.conf @@ -1,4 +1,3 @@ -WW_INTERNAL: 45 nodes: node1: network devices: diff --git a/overlays/NetworkManager/internal/nodes.conf-vlan b/overlays/NetworkManager/internal/nodes.conf-vlan index 3302207f..71338a9a 100644 --- a/overlays/NetworkManager/internal/nodes.conf-vlan +++ b/overlays/NetworkManager/internal/nodes.conf-vlan @@ -3,8 +3,10 @@ nodes: primary network: untagged network devices: untagged: + onboot: true device: eth0 tagged: + onboot: true type: vlan device: eth0.902 tags: diff --git a/overlays/NetworkManager/internal/nodes_empty_mac.conf b/overlays/NetworkManager/internal/nodes_empty_mac.conf index 7f95f690..b1aa5217 100644 --- a/overlays/NetworkManager/internal/nodes_empty_mac.conf +++ b/overlays/NetworkManager/internal/nodes_empty_mac.conf @@ -1,4 +1,3 @@ -WW_INTERNAL: 45 nodes: node1: network devices: diff --git a/overlays/NetworkManager/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww b/overlays/NetworkManager/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww index 2640b892..1c0e5c7c 100644 --- a/overlays/NetworkManager/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww +++ b/overlays/NetworkManager/rootfs/etc/NetworkManager/system-connections/ww4-managed.ww @@ -14,7 +14,7 @@ master={{ $master }} type=ethernet {{- else }} type={{if $netdev.Type}}{{ $netdev.Type }}{{ else }}ethernet{{end}} -{{- if or $netdev.OnBoot (eq $netdev.OnBoot nil) }} +{{- if $netdev.OnBoot }} autoconnect=true {{- end }} {{- end }} diff --git a/overlays/debian.interfaces/internal/debian_interfaces_test.go b/overlays/debian.interfaces/internal/debian_interfaces_test.go index ad0a3ade..2b2e5f97 100644 --- a/overlays/debian.interfaces/internal/debian_interfaces_test.go +++ b/overlays/debian.interfaces/internal/debian_interfaces_test.go @@ -53,7 +53,6 @@ const debian_interfaces string = `backupFile: true writeFile: true Filename: default # This file is autogenerated by warewulf -auto wwnet0 allow-hotplug wwnet0 iface wwnet0 inet static address 192.168.3.21 @@ -64,7 +63,6 @@ backupFile: true writeFile: true Filename: secondary # This file is autogenerated by warewulf -auto wwnet1 allow-hotplug wwnet1 iface wwnet1 inet static address 192.168.3.22 diff --git a/overlays/debian.interfaces/rootfs/etc/network/interfaces.d/default.ww b/overlays/debian.interfaces/rootfs/etc/network/interfaces.d/default.ww index 178ec263..e1fe1cdb 100644 --- a/overlays/debian.interfaces/rootfs/etc/network/interfaces.d/default.ww +++ b/overlays/debian.interfaces/rootfs/etc/network/interfaces.d/default.ww @@ -1,7 +1,7 @@ {{- range $devname, $netdev := .ThisNode.NetDevs }} {{- file $devname }} # This file is autogenerated by warewulf -{{- if or $netdev.OnBoot (eq $netdev.OnBoot nil) }} +{{- if $netdev.OnBoot }} auto {{ $netdev.Device }} {{- end }} allow-hotplug {{ $netdev.Device }} diff --git a/overlays/fstab/internal/warewulf.conf b/overlays/fstab/internal/warewulf.conf index 56d81ac5..7c5f020c 100644 --- a/overlays/fstab/internal/warewulf.conf +++ b/overlays/fstab/internal/warewulf.conf @@ -1,4 +1,3 @@ -WW_INTERNAL: 43 ipaddr: 192.168.0.1/24 netmask: 255.255.255.0 network: 192.168.0.0 diff --git a/overlays/host/internal/warewulf.conf b/overlays/host/internal/warewulf.conf index 56d81ac5..7c5f020c 100644 --- a/overlays/host/internal/warewulf.conf +++ b/overlays/host/internal/warewulf.conf @@ -1,4 +1,3 @@ -WW_INTERNAL: 43 ipaddr: 192.168.0.1/24 netmask: 255.255.255.0 network: 192.168.0.0 diff --git a/overlays/host/internal/warewulf.conf-static b/overlays/host/internal/warewulf.conf-static index 4e3207c5..cd4b744d 100644 --- a/overlays/host/internal/warewulf.conf-static +++ b/overlays/host/internal/warewulf.conf-static @@ -1,4 +1,3 @@ -WW_INTERNAL: 43 ipaddr: 192.168.0.1/24 netmask: 255.255.255.0 network: 192.168.0.0 diff --git a/overlays/hosts/internal/warewulf.conf b/overlays/hosts/internal/warewulf.conf index 56d81ac5..7c5f020c 100644 --- a/overlays/hosts/internal/warewulf.conf +++ b/overlays/hosts/internal/warewulf.conf @@ -1,4 +1,3 @@ -WW_INTERNAL: 43 ipaddr: 192.168.0.1/24 netmask: 255.255.255.0 network: 192.168.0.0 diff --git a/overlays/ifcfg/internal/ifcfg_test.go b/overlays/ifcfg/internal/ifcfg_test.go index 3725f4b6..c78b73f3 100644 --- a/overlays/ifcfg/internal/ifcfg_test.go +++ b/overlays/ifcfg/internal/ifcfg_test.go @@ -83,7 +83,6 @@ IPADDR=192.168.3.21 NETMASK=255.255.255.0 GATEWAY=192.168.3.1 HWADDR=e6:92:39:49:7b:03 -ONBOOT=true IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes @@ -102,7 +101,6 @@ IPADDR=192.168.3.22 NETMASK=255.255.255.0 GATEWAY=192.168.3.1 HWADDR=9a:77:29:73:14:f1 -ONBOOT=true IPV6INIT=yes IPV6_AUTOCONF=yes IPV6_DEFROUTE=yes diff --git a/overlays/ifcfg/internal/nodes.conf-vlan b/overlays/ifcfg/internal/nodes.conf-vlan index 1f418c08..1345481e 100644 --- a/overlays/ifcfg/internal/nodes.conf-vlan +++ b/overlays/ifcfg/internal/nodes.conf-vlan @@ -3,8 +3,10 @@ nodes: primary network: untagged network devices: untagged: + onboot: true device: eth0 tagged: + onboot: true type: vlan device: eth0.902 tags: diff --git a/overlays/ifcfg/rootfs/etc/sysconfig/network-scripts/ifcfg.ww b/overlays/ifcfg/rootfs/etc/sysconfig/network-scripts/ifcfg.ww index ffe040d5..f89b7c36 100644 --- a/overlays/ifcfg/rootfs/etc/sysconfig/network-scripts/ifcfg.ww +++ b/overlays/ifcfg/rootfs/etc/sysconfig/network-scripts/ifcfg.ww @@ -27,7 +27,7 @@ GATEWAY={{ $netdev.Gateway }} {{- if $netdev.Hwaddr }} HWADDR={{ $netdev.Hwaddr }} {{- end }} -{{ if or $netdev.OnBoot (eq $netdev.OnBoot nil) -}} +{{- if $netdev.OnBoot }} ONBOOT=true {{- end }} IPV6INIT=yes diff --git a/overlays/wicked/internal/nodes.conf-vlan b/overlays/wicked/internal/nodes.conf-vlan index 3302207f..71338a9a 100644 --- a/overlays/wicked/internal/nodes.conf-vlan +++ b/overlays/wicked/internal/nodes.conf-vlan @@ -3,8 +3,10 @@ nodes: primary network: untagged network devices: untagged: + onboot: true device: eth0 tagged: + onboot: true type: vlan device: eth0.902 tags: diff --git a/overlays/wicked/internal/wicked_test.go b/overlays/wicked/internal/wicked_test.go index 7b835d07..ffa7c32b 100644 --- a/overlays/wicked/internal/wicked_test.go +++ b/overlays/wicked/internal/wicked_test.go @@ -65,7 +65,7 @@ This file is autogenerated by warewulf wwnet0 ethernet - boot + manual @@ -99,7 +99,7 @@ This file is autogenerated by warewulf wwnet1 ethernet - boot + manual diff --git a/overlays/wicked/rootfs/etc/wicked/ifconfig/ifcfg.xml.ww b/overlays/wicked/rootfs/etc/wicked/ifconfig/ifcfg.xml.ww index 19012c0d..4fd01a6a 100644 --- a/overlays/wicked/rootfs/etc/wicked/ifconfig/ifcfg.xml.ww +++ b/overlays/wicked/rootfs/etc/wicked/ifconfig/ifcfg.xml.ww @@ -18,7 +18,7 @@ This file is autogenerated by warewulf {{ $netdev.MTU }} {{- end }} - {{ if or $netdev.OnBoot (eq $netdev.OnBoot nil) }}boot{{ else }}manual{{end}} + {{ if $netdev.OnBoot }}boot{{ else }}manual{{ end }} diff --git a/overlays/wwinit/internal/warewulf.conf b/overlays/wwinit/internal/warewulf.conf index 56d81ac5..7c5f020c 100644 --- a/overlays/wwinit/internal/warewulf.conf +++ b/overlays/wwinit/internal/warewulf.conf @@ -1,4 +1,3 @@ -WW_INTERNAL: 43 ipaddr: 192.168.0.1/24 netmask: 255.255.255.0 network: 192.168.0.0 diff --git a/overlays/wwinit/internal/wwinit_test.go b/overlays/wwinit/internal/wwinit_test.go index 0ef11229..cb51a407 100644 --- a/overlays/wwinit/internal/wwinit_test.go +++ b/overlays/wwinit/internal/wwinit_test.go @@ -57,7 +57,6 @@ func Test_wwinitOverlay(t *testing.T) { const wwinit_warewulf_conf string = `backupFile: true writeFile: true Filename: etc/warewulf/warewulf.conf -WW_INTERNAL: 43 ipaddr: 192.168.0.1/24 netmask: 255.255.255.0 network: 192.168.0.0 diff --git a/userdocs/contents/configuration.rst b/userdocs/contents/configuration.rst index b46443f6..13309244 100644 --- a/userdocs/contents/configuration.rst +++ b/userdocs/contents/configuration.rst @@ -14,7 +14,6 @@ Warewulf (4.5.2): .. code-block:: yaml - WW_INTERNAL: 45 ipaddr: 10.0.0.1 netmask: 255.255.252.0 network: 10.0.0.0 @@ -202,6 +201,26 @@ command. This also goes for ``warewulf.conf`` as well - any changes made also require ``warewulfd`` to be restarted. The restart should be done using the following command: ``systemctl restart warewulfd`` +Upgrades +======== + +New versions of Warewulf might introduce changes to ``warewulf.conf`` and ``nodes.conf``. +The ``wwctl upgrade`` command can help ease the transition between versions. + +.. note:: + + ``wwctl upgrade`` will back up any files before it changes them (to ``-old``) + but it is good practice to back up your configuration manually. + +.. code-block:: console + + # wwctl upgrade config + # wwctl upgrade nodes --add-defaults --replace-overlays + +Both upgrade commands support specifying ``--output-path=-`` +to print the upgraded configuration file to standard out +for inspection before replacing the configuration files. + Directories =========== diff --git a/userdocs/contributing/development-environment-vagrant.rst b/userdocs/contributing/development-environment-vagrant.rst index 8d8944b7..cbfab328 100644 --- a/userdocs/contributing/development-environment-vagrant.rst +++ b/userdocs/contributing/development-environment-vagrant.rst @@ -190,7 +190,6 @@ Vagrantfile head.vm.provision "shell", inline: <<-SHELL cat << 'CONF' | sudo tee /etc/warewulf/warewulf.conf - WW_INTERNAL: 45 ipaddr: 192.168.200.254 netmask: 255.255.255.0 network: 192.168.200.0 diff --git a/userdocs/quickstart/debian12.rst b/userdocs/quickstart/debian12.rst index eaf4a9b8..f2692fa4 100644 --- a/userdocs/quickstart/debian12.rst +++ b/userdocs/quickstart/debian12.rst @@ -65,7 +65,6 @@ address of your cluster's private network interface: .. code-block:: yaml - WW_INTERNAL: 45 ipaddr: 192.168.200.1 netmask: 255.255.255.0 network: 192.168.200.0 diff --git a/userdocs/quickstart/el.rst b/userdocs/quickstart/el.rst index 3edc50e7..e41de7b8 100644 --- a/userdocs/quickstart/el.rst +++ b/userdocs/quickstart/el.rst @@ -58,7 +58,6 @@ address of your cluster's private network interface. .. code-block:: yaml - WW_INTERNAL: 45 ipaddr: 10.0.0.1 netmask: 255.255.252.0 network: 10.0.0.0 diff --git a/userdocs/quickstart/suse15.rst b/userdocs/quickstart/suse15.rst index a6039c7f..355f36c7 100644 --- a/userdocs/quickstart/suse15.rst +++ b/userdocs/quickstart/suse15.rst @@ -46,7 +46,6 @@ address of your cluster's private network interface: .. code-block:: yaml - WW_INTERNAL: 45 ipaddr: 192.168.200.1 netmask: 255.255.255.0 network: 192.168.200.0