diff --git a/internal/app/wwctl/upgrade/cobra.go b/internal/app/wwctl/upgrade/cobra.go index 22b57d6f..87448452 100644 --- a/internal/app/wwctl/upgrade/cobra.go +++ b/internal/app/wwctl/upgrade/cobra.go @@ -20,13 +20,15 @@ supported by the current version.`, RunE: UpgradeNodesConf, } - addDefaults bool - inputPath string - outputPath string + 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") } @@ -40,7 +42,7 @@ func UpgradeNodesConf(cmd *cobra.Command, args []string) error { if err != nil { return err } - upgraded := legacy.Upgrade(addDefaults) + upgraded := legacy.Upgrade(addDefaults, replaceOverlays) if err := util.CopyFile(outputPath, outputPath+"-old"); err != nil { return err } diff --git a/internal/pkg/upgrade/node.go b/internal/pkg/upgrade/node.go index 9f066ff2..f237da48 100644 --- a/internal/pkg/upgrade/node.go +++ b/internal/pkg/upgrade/node.go @@ -12,6 +12,45 @@ import ( "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 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:]...)...) +} + func logIgnore(name string, value interface{}, reason string) { wwlog.Warn("ignore: %s: %v (%s)", name, value, reason) } @@ -36,7 +75,7 @@ type NodesYaml struct { Nodes map[string]*Node } -func (this *NodesYaml) Upgrade(addDefaults bool) (upgraded *node.NodesYaml) { +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) @@ -44,10 +83,10 @@ func (this *NodesYaml) Upgrade(addDefaults bool) (upgraded *node.NodesYaml) { logIgnore("WW_INTERNAL", this.WWInternal, "obsolete") } for name, profile := range this.NodeProfiles { - upgraded.NodeProfiles[name] = profile.Upgrade(addDefaults) + upgraded.NodeProfiles[name] = profile.Upgrade(addDefaults, replaceOverlays) } for name, node := range this.Nodes { - upgraded.Nodes[name] = node.Upgrade(addDefaults) + 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) } @@ -60,28 +99,11 @@ func (this *NodesYaml) Upgrade(addDefaults bool) (upgraded *node.NodesYaml) { 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") + defaultProfile.SystemOverlay, wwinitSplitOverlays...) } if len(defaultProfile.RuntimeOverlay) == 0 { defaultProfile.RuntimeOverlay = append( - defaultProfile.RuntimeOverlay, - "hosts", - "ssh.authorized_keys", - "syncuser") + defaultProfile.RuntimeOverlay, genericSplitOverlays...) } if defaultProfile.Kernel.Args == "" { defaultProfile.Kernel.Args = "quiet crashkernel=no vga=791 net.naming-scheme=v238" @@ -103,7 +125,7 @@ type Node struct { Profile `yaml:"-,inline"` } -func (this *Node) Upgrade(addDefaults bool) (upgraded *node.Node) { +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) @@ -233,6 +255,20 @@ func (this *Node) Upgrade(addDefaults bool) (upgraded *node.Node) { 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 @@ -280,7 +316,7 @@ type Profile struct { TagsDel []string `yaml:"tagsdel,omitempty"` } -func (this *Profile) Upgrade(addDefaults bool) (upgraded *node.Profile) { +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) @@ -409,6 +445,20 @@ func (this *Profile) Upgrade(addDefaults bool) (upgraded *node.Profile) { 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 diff --git a/internal/pkg/upgrade/node_test.go b/internal/pkg/upgrade/node_test.go index 4e1d99dd..cfd98117 100644 --- a/internal/pkg/upgrade/node_test.go +++ b/internal/pkg/upgrade/node_test.go @@ -8,14 +8,16 @@ import ( ) var nodesYamlUpgradeTests = []struct { - name string - addDefaults bool - legacyYaml string - upgradedYaml string + name string + addDefaults bool + replaceOverlays bool + legacyYaml string + upgradedYaml string }{ { - name: "captured vers42 example", - addDefaults: false, + name: "captured vers42 example", + addDefaults: false, + replaceOverlays: false, legacyYaml: ` nodeprofiles: default: @@ -69,8 +71,9 @@ nodes: `, }, { - name: "captured vers43 example", - addDefaults: false, + name: "captured vers43 example", + addDefaults: false, + replaceOverlays: false, legacyYaml: ` WW_INTERNAL: 45 nodeprofiles: @@ -135,17 +138,19 @@ nodes: `, }, { - name: "remove WW_INTERNAL", - addDefaults: false, - legacyYaml: `WW_INTERNAL: 45`, + name: "remove WW_INTERNAL", + addDefaults: false, + replaceOverlays: false, + legacyYaml: `WW_INTERNAL: 45`, upgradedYaml: ` nodeprofiles: {} nodes: {} `, }, { - name: "disabled is obsolete", - addDefaults: false, + name: "disabled is obsolete", + addDefaults: false, + replaceOverlays: false, legacyYaml: ` nodes: n1: @@ -162,8 +167,9 @@ nodes: `, }, { - name: "inline IPMI settings", - addDefaults: false, + name: "inline IPMI settings", + addDefaults: false, + replaceOverlays: false, legacyYaml: ` nodes: n1: @@ -216,8 +222,9 @@ nodes: `, }, { - name: "inline Kernel settings", - addDefaults: false, + name: "inline Kernel settings", + addDefaults: false, + replaceOverlays: false, legacyYaml: ` nodeprofiles: default: @@ -246,8 +253,9 @@ nodes: `, }, { - name: "keys and tags", - addDefaults: false, + name: "keys and tags", + addDefaults: false, + replaceOverlays: false, legacyYaml: ` nodeprofiles: default: @@ -336,8 +344,9 @@ nodes: `, }, { - name: "primary network", - addDefaults: false, + name: "primary network", + addDefaults: false, + replaceOverlays: false, legacyYaml: ` nodes: n1: @@ -392,8 +401,9 @@ nodes: `, }, { - name: "overlays", - addDefaults: false, + name: "overlays", + addDefaults: false, + replaceOverlays: false, legacyYaml: ` nodes: n1: @@ -452,8 +462,9 @@ nodes: `, }, { - name: "disk example", - addDefaults: false, + name: "disk example", + addDefaults: false, + replaceOverlays: false, legacyYaml: ` nodes: n1: @@ -499,8 +510,9 @@ nodes: `, }, { - name: "add defaults", - addDefaults: true, + name: "add defaults", + addDefaults: true, + replaceOverlays: false, legacyYaml: ` nodes: n1: @@ -547,8 +559,9 @@ nodes: `, }, { - name: "add defaults conflicts", - addDefaults: true, + name: "add defaults conflicts", + addDefaults: true, + replaceOverlays: false, legacyYaml: ` nodeprofiles: default: @@ -606,6 +619,69 @@ nodes: 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 `, }, } @@ -615,7 +691,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(tt.addDefaults) + 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)))