From 835506f5e8c7c29c8305d0d4a9371c9c3e3685ae Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Tue, 5 Nov 2024 10:25:58 -0700 Subject: [PATCH] Support inserting defaults during config upgrade Signed-off-by: Jonathon Anderson --- internal/pkg/upgrade/node.go | 103 ++++++++++++++++----- internal/pkg/upgrade/node_test.go | 145 +++++++++++++++++++++++++++--- 2 files changed, 214 insertions(+), 34 deletions(-) diff --git a/internal/pkg/upgrade/node.go b/internal/pkg/upgrade/node.go index e6d57cf7..363b6a22 100644 --- a/internal/pkg/upgrade/node.go +++ b/internal/pkg/upgrade/node.go @@ -8,6 +8,7 @@ import ( "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" ) @@ -29,21 +30,64 @@ type NodesYaml struct { Nodes map[string]*Node } -func (this *NodesYaml) Upgrade() (upgraded *node.NodesYaml) { +func (this *NodesYaml) Upgrade(addDefaults 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") } - if this.NodeProfiles != nil { - for name, profile := range this.NodeProfiles { - upgraded.NodeProfiles[name] = profile.Upgrade() + for name, profile := range this.NodeProfiles { + upgraded.NodeProfiles[name] = profile.Upgrade(addDefaults) + } + for name, node := range this.Nodes { + upgraded.Nodes[name] = node.Upgrade(addDefaults) + 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 this.Nodes != nil { - for name, node := range this.Nodes { - upgraded.Nodes[name] = node.Upgrade() + 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, + "wwinit", + "wwclient", + "fstab", + "hostname", + "ssh.host_keys", + "issue", + "resolv", + "udev.netname", + "systemd.netname", + "ifcfg", + "NetworkManager", + "debian.interfaces", + "wicked", + "ignition") + } + if len(defaultProfile.RuntimeOverlay) == 0 { + defaultProfile.RuntimeOverlay = append( + defaultProfile.RuntimeOverlay, + "hosts", + "ssh.authorized_keys", + "syncuser") + } + 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 @@ -53,7 +97,7 @@ type Node struct { Profile `yaml:"-,inline"` } -func (this *Node) Upgrade() (upgraded *node.Node) { +func (this *Node) Upgrade(addDefaults bool) (upgraded *node.Node) { upgraded = new(node.Node) upgraded.Tags = make(map[string]string) upgraded.Disks = make(map[string]*node.Disk) @@ -136,7 +180,7 @@ func (this *Node) Upgrade() (upgraded *node.Node) { } if this.NetDevs != nil { for name, netDev := range this.NetDevs { - upgraded.NetDevs[name] = netDev.Upgrade() + upgraded.NetDevs[name] = netDev.Upgrade(addDefaults) } } if this.PrimaryNetDev != "" { @@ -153,6 +197,11 @@ func (this *Node) Upgrade() (upgraded *node.Node) { } } 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) { @@ -225,7 +274,7 @@ type Profile struct { TagsDel []string `yaml:"tagsdel,omitempty"` } -func (this *Profile) Upgrade() (upgraded *node.Profile) { +func (this *Profile) Upgrade(addDefaults bool) (upgraded *node.Profile) { upgraded = new(node.Profile) upgraded.Tags = make(map[string]string) upgraded.Disks = make(map[string]*node.Disk) @@ -310,7 +359,7 @@ func (this *Profile) Upgrade() (upgraded *node.Profile) { } if this.NetDevs != nil { for name, netDev := range this.NetDevs { - upgraded.NetDevs[name] = netDev.Upgrade() + upgraded.NetDevs[name] = netDev.Upgrade(addDefaults) } } if this.PrimaryNetDev != "" { @@ -434,7 +483,7 @@ type NetDev struct { Type string `yaml:"type,omitempty"` } -func (this *NetDev) Upgrade() (upgraded *node.NetDev) { +func (this *NetDev) Upgrade(addDefaults bool) (upgraded *node.NetDev) { upgraded = new(node.NetDev) upgraded.Tags = make(map[string]string) upgraded.Device = this.Device @@ -468,6 +517,14 @@ func (this *NetDev) Upgrade() (upgraded *node.NetDev) { 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 } @@ -527,17 +584,19 @@ func (this *FileSystem) Upgrade() (upgraded *node.FileSystem) { upgraded.Options = make([]string, 0) upgraded.Format = this.Format upgraded.Label = this.Label - 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)) + 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.MountOptions = strings.Join(mountOptionsStrings, " ") - default: - wwlog.Error("unparsable MountOptions: %v", mountOptions) } upgraded.Options = append(upgraded.Options, this.Options...) upgraded.Path = this.Path diff --git a/internal/pkg/upgrade/node_test.go b/internal/pkg/upgrade/node_test.go index 06ae79ee..ae4bec8a 100644 --- a/internal/pkg/upgrade/node_test.go +++ b/internal/pkg/upgrade/node_test.go @@ -9,11 +9,13 @@ import ( var nodesYamlUpgradeTests = []struct { name string + addDefaults bool legacyYaml string upgradedYaml string }{ { - name: "captured vers42 example", + name: "captured vers42 example", + addDefaults: false, legacyYaml: ` nodeprofiles: default: @@ -67,7 +69,8 @@ nodes: `, }, { - name: "captured vers43 example", + name: "captured vers43 example", + addDefaults: false, legacyYaml: ` WW_INTERNAL: 45 nodeprofiles: @@ -132,15 +135,17 @@ nodes: `, }, { - name: "remove WW_INTERNAL", - legacyYaml: `WW_INTERNAL: 45`, + name: "remove WW_INTERNAL", + addDefaults: false, + legacyYaml: `WW_INTERNAL: 45`, upgradedYaml: ` nodeprofiles: {} nodes: {} `, }, { - name: "disabled is obsolete", + name: "disabled is obsolete", + addDefaults: false, legacyYaml: ` nodes: n1: @@ -157,7 +162,8 @@ nodes: `, }, { - name: "inline IPMI settings", + name: "inline IPMI settings", + addDefaults: false, legacyYaml: ` nodes: n1: @@ -210,7 +216,8 @@ nodes: `, }, { - name: "inline Kernel settings", + name: "inline Kernel settings", + addDefaults: false, legacyYaml: ` nodeprofiles: default: @@ -239,7 +246,8 @@ nodes: `, }, { - name: "keys and tags", + name: "keys and tags", + addDefaults: false, legacyYaml: ` nodeprofiles: default: @@ -328,7 +336,8 @@ nodes: `, }, { - name: "primary network", + name: "primary network", + addDefaults: false, legacyYaml: ` nodes: n1: @@ -383,7 +392,8 @@ nodes: `, }, { - name: "overlays", + name: "overlays", + addDefaults: false, legacyYaml: ` nodes: n1: @@ -442,7 +452,8 @@ nodes: `, }, { - name: "disk example", + name: "disk example", + addDefaults: false, legacyYaml: ` nodes: n1: @@ -485,6 +496,116 @@ nodes: /dev/disk/by-partlabel/swap: format: swap path: swap +`, + }, + { + name: "add defaults", + addDefaults: true, + 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, + 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 `, }, } @@ -494,7 +615,7 @@ func Test_UpgradeNodesYaml(t *testing.T) { t.Run(tt.name, func(t *testing.T) { legacy, err := Parse([]byte(tt.legacyYaml)) assert.NoError(t, err) - upgraded := legacy.Upgrade() + upgraded := legacy.Upgrade(tt.addDefaults) upgradedYaml, err := upgraded.Dump() assert.Equal(t, strings.TrimSpace(tt.upgradedYaml), strings.TrimSpace(string(upgradedYaml))) })