fix autodetected kernel sorting

Signed-off-by: xu yang <xyang@ciq.com>
This commit is contained in:
xu yang
2024-09-18 12:49:34 +00:00
committed by Jonathon Anderson
parent cd39a766d8
commit 31c980f731
5 changed files with 172 additions and 10 deletions

View File

@@ -52,6 +52,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Return an error during `wwctl container import` if archive filename includes a colon. #1371 - Return an error during `wwctl container import` if archive filename includes a colon. #1371
- Correctly extract smbios asset key during Grub boot. #1291 - Correctly extract smbios asset key during Grub boot. #1291
- Refactor of `wwinit/init` to more properly address rootfs options. #1098 - Refactor of `wwinit/init` to more properly address rootfs options. #1098
- Fix autodetected kernel sorting issue. #1332
## v4.5.7, 2024-09-11 ## v4.5.7, 2024-09-11

1
go.mod
View File

@@ -14,6 +14,7 @@ require (
github.com/golang/glog v1.2.0 github.com/golang/glog v1.2.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
github.com/hashicorp/go-version v1.7.0
github.com/manifoldco/promptui v0.9.0 github.com/manifoldco/promptui v0.9.0
github.com/olekukonko/tablewriter v0.0.5 github.com/olekukonko/tablewriter v0.0.5
github.com/opencontainers/image-spec v1.1.0 github.com/opencontainers/image-spec v1.1.0

2
go.sum
View File

@@ -223,6 +223,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=

View File

@@ -8,10 +8,12 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"sort"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/hashicorp/go-version"
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config" warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
"github.com/warewulf/warewulf/internal/pkg/util" "github.com/warewulf/warewulf/internal/pkg/util"
"github.com/warewulf/warewulf/internal/pkg/wwlog" "github.com/warewulf/warewulf/internal/pkg/wwlog"
@@ -21,7 +23,7 @@ var (
kernelSearchPaths = []string{ kernelSearchPaths = []string{
// This is a printf format where the %s will be the kernel version // This is a printf format where the %s will be the kernel version
"/boot/Image-%s", // this is the aarch64 for SUSE, vmlinux which is also present won't boot "/boot/Image-%s", // this is the aarch64 for SUSE, vmlinux which is also present won't boot
"/boot/vmlinuz-linux%.s", "/boot/vmlinuz-linux%s",
"/boot/vmlinuz-%s", "/boot/vmlinuz-%s",
"/boot/vmlinuz-%s.gz", "/boot/vmlinuz-%s.gz",
"/lib/modules/%s/vmlinuz", "/lib/modules/%s/vmlinuz",
@@ -32,6 +34,8 @@ var (
"lib/firmware/*", "lib/firmware/*",
"lib/modprobe.d", "lib/modprobe.d",
"lib/modules-load.d"} "lib/modules-load.d"}
// kenrel naming convention <base kernel version>-<ABI number>.<upload number>-<flavour>
kernelVersionRegex = `(\d+\.\d+\.\d+)-((\d+\.*){1,})`
) )
func KernelImageTopDir() string { func KernelImageTopDir() string {
@@ -241,7 +245,48 @@ func DeleteKernel(name string) error {
Searches for kernel under a given path. First return result is the Searches for kernel under a given path. First return result is the
full path, second the version and an error if the kernel couldn't be found. full path, second the version and an error if the kernel couldn't be found.
*/ */
func FindKernel(root string) (kPath string, version string, err error) { type kernel struct {
version string
path string
}
func filter(val string, filters []func(string) (string, error)) (string, error) {
for _, ft := range filters {
newVal, err := ft(val)
if err != nil {
return val, err
}
val = newVal
}
return val, nil
}
func nonDebugKernel(val string) (string, error) {
if strings.HasSuffix(val, "+debug") {
return val, fmt.Errorf("%s is debug kernel, skipped", val)
}
return val, nil
}
func nonSemaVer(val string) (string, error) {
// need to extract version info
verRegx := regexp.MustCompile(kernelVersionRegex)
verRe := verRegx.FindAllStringSubmatch(val, -1)
// only if at the least the following pattern is matched <xx.xx.xx>-<xx[.xx.xx]>
if len(verRe) > 0 && len(verRe[0]) > 2 {
// verRe[0][1] -> <xx.xx.xx>
// verRe[0][2] -> <xx[.xx.xx]>
verStr := strings.TrimSuffix(fmt.Sprintf("%s-%s", verRe[0][1], verRe[0][2]), ".")
_, err := version.NewVersion(verStr)
if err != nil {
return val, fmt.Errorf("semantic incompatible version detected, version string: %s, err: %s", verStr, err)
}
return verStr, nil
}
return val, fmt.Errorf("unable to extract version info from %s", val)
}
func FindKernel(root string) (string, string, error) {
wwlog.Debug("root: %s", root) wwlog.Debug("root: %s", root)
for _, searchPath := range kernelSearchPaths { for _, searchPath := range kernelSearchPaths {
testPattern := fmt.Sprintf(path.Join(root, searchPath), `*`) testPattern := fmt.Sprintf(path.Join(root, searchPath), `*`)
@@ -250,18 +295,38 @@ func FindKernel(root string) (kPath string, version string, err error) {
if len(potentialKernel) == 0 { if len(potentialKernel) == 0 {
continue continue
} }
verMap := make(map[*version.Version]*kernel, len(potentialKernel))
for _, foundKernel := range potentialKernel { for _, foundKernel := range potentialKernel {
wwlog.Debug("Parsing out kernel version for %s", foundKernel) wwlog.Debug("Parsing out kernel version for %s", foundKernel)
re := regexp.MustCompile(fmt.Sprintf(path.Join(root, searchPath), `([\w\d-\.+]*)`)) re := regexp.MustCompile(fmt.Sprintf(path.Join(root, searchPath), `([\w\d-\.+]*)`))
version := re.FindAllStringSubmatch(foundKernel, -1) kernelVer := re.FindAllStringSubmatch(foundKernel, -1)
if version == nil { if kernelVer == nil {
return foundKernel, "", fmt.Errorf("could not parse kernel version") break
} }
wwlog.Verbose("found kernel version %s", strings.TrimSuffix(version[0][1], ".gz")) // kernelVerStr is like 5.14.0-427.18.1.el9_4.x86_64
return foundKernel, strings.TrimSuffix(version[0][1], ".gz"), nil kernelVerStr := strings.TrimSuffix(kernelVer[0][1], ".gz")
newVal, err := filter(kernelVerStr, []func(string) (string, error){nonDebugKernel, nonSemaVer})
if err != nil {
wwlog.Verbose("While filtering kernel version for %s, having error: %s", kernelVerStr, err)
continue
}
ver, _ := version.NewVersion(newVal)
verMap[ver] = &kernel{
version: kernelVerStr,
path: foundKernel,
}
} }
if len(verMap) > 0 {
var keys []*version.Version
for k := range verMap {
keys = append(keys, k)
}
sort.Sort(sort.Reverse(version.Collection(keys)))
return verMap[keys[0]].path, verMap[keys[0]].version, nil
}
} }
return "", "", fmt.Errorf("could not find kernel version") return "", "", fmt.Errorf("could not find kernel version")

View File

@@ -1,8 +1,10 @@
package kernel package kernel
import ( import (
"fmt"
"os" "os"
"path" "path"
"path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -17,9 +19,10 @@ var kernelBuildTests = []struct {
kernelFileName string kernelFileName string
succeed bool succeed bool
}{ }{
{"4.3.2.1", "kernel1", "vmlinuz-1.2.3.4.gz", false}, // kernel naming convention is <base kernel version>-<ABI number>.<upload number>-<flavour>
{"1.2.3.4", "kernel1", "vmlinuz-1.2.3.4.gz", true}, {"4.3.2-1", "kernel1", "vmlinuz-1.2.3-4.gz", false},
{"1.2+3+4", "kernel1", "vmlinuz-1.2+3+4.gz", true}, {"1.2.3-4", "kernel1", "vmlinuz-1.2.3-4.gz", true},
{"1.2.3-4.3.1-generic", "kernel1", "vmlinuz-1.2.3-4.3.1-generic.gz", true},
} }
func Test_BuildKernel(t *testing.T) { func Test_BuildKernel(t *testing.T) {
@@ -73,3 +76,93 @@ func Test_BuildKernel(t *testing.T) {
os.RemoveAll(kernelDir) os.RemoveAll(kernelDir)
} }
} }
var kernelFindTests = []struct {
name string
prefix string // kernel name prefix
kernelNames []string
expVer string
expPath string
}{
{
name: "vmlinuz under boot directory ok case",
prefix: "/boot/vmlinuz-%s",
kernelNames: []string{"5.14.0-427.18.1.el9_4.x86_64", "5.14.0-427.24.1.el9_4.x86_64", "4.14.0-427.18.1.el8_4.x86_64"},
expVer: "5.14.0-427.24.1.el9_4.x86_64",
expPath: "/boot/vmlinuz-5.14.0-427.24.1.el9_4.x86_64",
},
{
name: "vmlinuz under boot directory ok case 2",
prefix: "/boot/vmlinuz-%s",
kernelNames: []string{"5.15.0-119-generic", "5.14.0-427.24.1.el9_4.x86_64", "6.15.0-119-generic"},
expVer: "6.15.0-119-generic",
expPath: "/boot/vmlinuz-6.15.0-119-generic",
},
{
name: "vmlinuz under boot directory ok case 3",
prefix: "/boot/vmlinuz-%s",
kernelNames: []string{"5.15.0-0-vanilla", "5.14.0-427.24.1.el9_4.x86_64"},
expVer: "5.15.0-0-vanilla",
expPath: "/boot/vmlinuz-5.15.0-0-vanilla",
},
{
// <base kernel version>-<ABI number>.<upload number>-<flavour>
name: "vmlinuz under boot directory ok case (becuase the first version naming is incorrect)",
prefix: "/boot/vmlinuz-%s",
kernelNames: []string{"5.15.0-generic", "5.14.0-427.24.1.el9_4.x86_64", "5.13.0-427.24.1.el9_4.x86_64"},
expVer: "5.14.0-427.24.1.el9_4.x86_64",
expPath: "/boot/vmlinuz-5.14.0-427.24.1.el9_4.x86_64",
},
{
name: "vmlinuz under lib modules ok case",
prefix: "/lib/modules/%s/vmlinuz",
kernelNames: []string{"5.14.0-427.18.1.el9_4.x86_64", "5.14.0-427.24.1.el9_4.x86_64"},
expVer: "5.14.0-427.24.1.el9_4.x86_64",
expPath: "/lib/modules/5.14.0-427.24.1.el9_4.x86_64/vmlinuz",
},
{
name: "vmlinuz.gz under boot directory ok case",
prefix: "/boot/vmlinuz-%s.gz",
kernelNames: []string{"5.14.0-427.18.1.el9_4.x86_64", "5.14.0-427.24.1.el9_4.x86_64"},
expVer: "5.14.0-427.24.1.el9_4.x86_64",
expPath: "/boot/vmlinuz-5.14.0-427.24.1.el9_4.x86_64.gz",
},
{
name: "mixed rescue / debug kernel testing",
prefix: "/boot/vmlinuz-%s",
kernelNames: []string{"0-rescue-eb46964329b146e39518c625feab3ea0", "5.14.0-362.24.1.el9_3.aarch64", "5.14.0-427.31.1.el9_4.aarch64+debug", "5.14.0-284.30.1.el9_2.aarch64", "5.14.0-427.31.1.el9_4.aarch64"},
expVer: "5.14.0-427.31.1.el9_4.aarch64",
expPath: "/boot/vmlinuz-5.14.0-427.31.1.el9_4.aarch64",
},
}
func Test_FindKernel(t *testing.T) {
wwlog.SetLogLevel(wwlog.DEBUG)
for _, tt := range kernelFindTests {
srvDir, err := os.MkdirTemp(os.TempDir(), "ww-test-srv-*")
assert.NoError(t, err)
conf := warewulfconf.Get()
conf.Paths.WWProvisiondir = srvDir
kernelDir, err := os.MkdirTemp(os.TempDir(), "ww-test-kernel-*")
assert.NoError(t, err)
{
for _, version := range tt.kernelNames {
kernel := fmt.Sprintf(tt.prefix, version)
parent := filepath.Dir(kernel)
err = os.MkdirAll(path.Join(kernelDir, parent), 0755)
assert.NoError(t, err)
_, err := os.Create(path.Join(kernelDir, kernel))
assert.NoError(t, err)
}
}
t.Run(tt.name, func(t *testing.T) {
kpath, kver, err := FindKernel(kernelDir)
assert.NoError(t, err)
assert.Equal(t, tt.expVer, kver)
assert.Equal(t, filepath.Join(kernelDir, tt.expPath), kpath)
})
os.RemoveAll(srvDir)
os.RemoveAll(kernelDir)
}
}