Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add latest-x pattern for major machine image versions #525

Merged
merged 2 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkg/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ const (
PatternTwoMinorBeforeLatest = "twoMinorBeforeLatest"
PatternThreeMinorBeforeLatest = "threeMinorBeforeLatest"
PatternFourMinorBeforeLatest = "fourMinorBeforeLatest"
PatternOneMajorBeforeLatest = "oneMajorBeforeLatest"
PatternTwoMajorBeforeLatest = "twoMajorBeforeLatest"
PatternThreeMajorBeforeLatest = "threeMajorBeforeLatest"
PatternFourMajorBeforeLatest = "fourMajorBeforeLatest"

// TM Dashboard
DashboardExecutionGroupParameter = "runID"
Expand Down
7 changes: 5 additions & 2 deletions pkg/shootflavors/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ func SetupWorker(cloudprofile gardencorev1beta1.CloudProfile, workers []gardenco
res := make([]gardencorev1beta1.Worker, len(workers))
for i, w := range workers {
worker := w.DeepCopy()
if worker.Machine.Image != nil && (worker.Machine.Image.Version == nil || *worker.Machine.Image.Version == common.PatternLatest) {
version, err := util.GetLatestMachineImageVersion(cloudprofile, worker.Machine.Image.Name, *worker.Machine.Architecture)
if worker.Machine.Image != nil {
if worker.Machine.Image.Version == nil {
*worker.Machine.Image.Version = common.PatternLatest
}
version, err := util.GetMachineImageVersion(cloudprofile, *worker.Machine.Image.Version, worker.Machine.Image.Name, *worker.Machine.Architecture)
if err != nil {
return nil, err
}
Expand Down
119 changes: 88 additions & 31 deletions pkg/util/machine_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,49 @@ package util

import (
"fmt"
"sort"
"time"

"github.com/Masterminds/semver/v3"
gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
"github.com/pkg/errors"
"k8s.io/utils/strings/slices"

"github.com/gardener/test-infra/pkg/common"
)

// GetLatestK8sVersion returns the latest avilable kubernetes version from the cloudprofile
func GetLatestMachineImageVersion(cloudprofile gardencorev1beta1.CloudProfile, imageName, arch string) (gardencorev1beta1.MachineImageVersion, error) {
// GetMachineImageVersion returns a version identified by a pattern or defaults to the version string handed over
func GetMachineImageVersion(cloudprofile gardencorev1beta1.CloudProfile, version, imageName, arch string) (gardencorev1beta1.MachineImageVersion, error) {

var (
machineImageVersion gardencorev1beta1.MachineImageVersion
err error
)

switch version {
default:
machineImageVersion = gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: version,
},
}
case common.PatternLatest:
machineImageVersion, err = GetLatestMachineImageVersion(cloudprofile, imageName, arch)
case common.PatternOneMajorBeforeLatest:
machineImageVersion, err = GetXMajorsBeforeLatestMachineImageVersion(cloudprofile, imageName, arch, 1)
case common.PatternTwoMajorBeforeLatest:
machineImageVersion, err = GetXMajorsBeforeLatestMachineImageVersion(cloudprofile, imageName, arch, 2)
case common.PatternThreeMajorBeforeLatest:
machineImageVersion, err = GetXMajorsBeforeLatestMachineImageVersion(cloudprofile, imageName, arch, 3)
case common.PatternFourMajorBeforeLatest:
machineImageVersion, err = GetXMajorsBeforeLatestMachineImageVersion(cloudprofile, imageName, arch, 4)
}

return machineImageVersion, err
}

// GetXMajorsBeforeLatestMachineImageVersion extracts the latest-x major version from a list of relevant versions found in a cloudprofile
func GetXMajorsBeforeLatestMachineImageVersion(cloudprofile gardencorev1beta1.CloudProfile, imageName, arch string, x uint64) (gardencorev1beta1.MachineImageVersion, error) {
machineImage, err := GetMachineImage(cloudprofile, imageName)
if err != nil {
return gardencorev1beta1.MachineImageVersion{}, err
Expand All @@ -21,9 +54,60 @@ func GetLatestMachineImageVersion(cloudprofile gardencorev1beta1.CloudProfile, i
return gardencorev1beta1.MachineImageVersion{}, fmt.Errorf("no machine image versions found for cloudprofle %s", cloudprofile.GetName())
}

machineVersions = FilterArchSpecificMachineImage(machineVersions, arch)
machineVersions = FilterExpiredMachineImageVersions(FilterArchSpecificMachineImage(machineVersions, arch))

return getXMajorsBeforeLatestMachineImageVersion(machineVersions, x)
}

// getXMajorsBeforeLatestMachineImageVersion returns the latest-x version of a list of machine image versions
func getXMajorsBeforeLatestMachineImageVersion(rawVersions []gardencorev1beta1.MachineImageVersion, x uint64) (gardencorev1beta1.MachineImageVersion, error) {
if len(rawVersions) == 0 {
return gardencorev1beta1.MachineImageVersion{}, errors.New("no machine image versions found")
}

parsedVersions := make([]*semver.Version, 0)
for _, raw := range rawVersions {
v, err := semver.NewVersion(raw.Version)
if err != nil {
return gardencorev1beta1.MachineImageVersion{}, err
}
if v.Metadata() != "" {
continue
}
parsedVersions = append(parsedVersions, v)
}
sort.Sort(sort.Reverse(semver.Collection(parsedVersions)))

xMajorBeforeLatest := x
cmpVersion := parsedVersions[0]
for _, version := range parsedVersions {
if xMajorBeforeLatest == 0 {
return gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: version.Original(),
},
}, nil
}
if version.Major() < cmpVersion.Major() {
xMajorBeforeLatest--
if xMajorBeforeLatest == 0 {
return gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: version.Original(),
},
}, nil
}
cmpVersion = version
}
}
return gardencorev1beta1.MachineImageVersion{}, errors.New(fmt.Sprintf("no machine image version matching the pattern latest-%d found", x))

return getLatestMachineImageVersion(FilterExpiredMachineImageVersions(machineVersions))
}

// GetLatestMachineImageVersion returns the latest available machine image version from the cloudprofile
func GetLatestMachineImageVersion(cloudprofile gardencorev1beta1.CloudProfile, imageName, arch string) (gardencorev1beta1.MachineImageVersion, error) {

return GetXMajorsBeforeLatestMachineImageVersion(cloudprofile, imageName, arch, 0)
}

// FilterArchSpecificMachineImage removes all version which doesn't support given architecture.
Expand All @@ -48,33 +132,6 @@ func FilterExpiredMachineImageVersions(versions []gardencorev1beta1.MachineImage
return filtered
}

// getLatestMachineImageVersion returns the latest image from a list of expirable versions
func getLatestMachineImageVersion(rawVersions []gardencorev1beta1.MachineImageVersion) (gardencorev1beta1.MachineImageVersion, error) {
if len(rawVersions) == 0 {
return gardencorev1beta1.MachineImageVersion{}, errors.New("no machine image versions found")
}

var (
latestExpVersion gardencorev1beta1.MachineImageVersion
latestVersion *semver.Version
)

for _, rawVersion := range rawVersions {
v, err := semver.NewVersion(rawVersion.Version)
if err != nil {
return gardencorev1beta1.MachineImageVersion{}, err
}
if v.Metadata() != "" {
continue
}
if latestVersion == nil || v.GreaterThan(latestVersion) {
latestVersion = v
latestExpVersion = rawVersion
}
}
return latestExpVersion, nil
}

func GetMachineImage(cloudprofile gardencorev1beta1.CloudProfile, imageName string) (gardencorev1beta1.MachineImage, error) {
for _, image := range cloudprofile.Spec.MachineImages {
if image.Name == imageName {
Expand Down
126 changes: 112 additions & 14 deletions pkg/util/machine_image_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,81 @@
package util

import (
"time"

gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/gardener/test-infra/pkg/common"
)

const (
imageName = "gardenlinux"
arch_amd64 = "amd64"
arch_arm64 = "arm64"
)

var _ = Describe("get machine images from a cloudprofile", func() {
var (
cloudprofile gardencorev1beta1.CloudProfile
expiredTime metav1.Time
futureTime metav1.Time
)

BeforeEach(func() {
expiredTime = metav1.NewTime(time.Now())
futureTime = metav1.NewTime(time.Now().Add(time.Hour * 24))
cloudprofile = gardencorev1beta1.CloudProfile{
Spec: gardencorev1beta1.CloudProfileSpec{
MachineImages: []gardencorev1beta1.MachineImage{
{
Name: imageName,
Versions: []gardencorev1beta1.MachineImageVersion{
{ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "3.4.5",
ExpirationDate: &futureTime,
}, Architectures: []string{arch_amd64, arch_arm64}},
{ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "2.3.3",
ExpirationDate: &futureTime,
}, Architectures: []string{arch_amd64, arch_arm64}},
{ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "2.3.4",
dguendisch marked this conversation as resolved.
Show resolved Hide resolved
ExpirationDate: &futureTime,
}, Architectures: []string{arch_amd64, arch_arm64}},
{ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "4.5.6",
ExpirationDate: &expiredTime,
}, Architectures: []string{arch_amd64, arch_arm64}},
},
},
},
},
}
})

It("should return the latest, not-expired machine image version from a cloudprofile", func() {
version, err := GetMachineImageVersion(cloudprofile, common.PatternLatest, imageName, arch_amd64)
Expect(err).ToNot(HaveOccurred())
Expect(version.Version).To(Equal("3.4.5"))
})

It("should return the latest-1, not-expired machine image version from a cloudprofile", func() {
version, err := GetMachineImageVersion(cloudprofile, common.PatternOneMajorBeforeLatest, imageName, arch_amd64)
Expect(err).ToNot(HaveOccurred())
Expect(version.Version).To(Equal("2.3.4"))
})
It("should return the version string parsed from the flavor", func() {
versionInput := "1.2.3"
version, err := GetMachineImageVersion(cloudprofile, versionInput, imageName, arch_amd64)
Expect(err).ToNot(HaveOccurred())
Expect(version.Version).To(Equal(versionInput))
})

})

var _ = Describe("machine image version test", func() {

var (
Expand All @@ -16,65 +86,93 @@ var _ = Describe("machine image version test", func() {
rawVersions = []gardencorev1beta1.MachineImageVersion{
gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "1.2.3",
Version: "1.3.4",
},
},
gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "3.2.4",
},
},
gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "1.2.4",
Version: "2.3.4",
},
},
gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "1.2.4-pre-release",
Version: "3.2.3",
},
},
gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "3.2.4-pre-release",
},
},
}
})

It("should return the latest of two machine image versions", func() {
version, err := getLatestMachineImageVersion(rawVersions)
It("should return the latest of several machine image versions", func() {
version, err := getXMajorsBeforeLatestMachineImageVersion(rawVersions, 0)
Expect(err).ToNot(HaveOccurred())
Expect(version.Version).To(Equal("1.2.4"))
Expect(version.Version).To(Equal("3.2.4"))
})

It("should return the latest-1 of several machine image versions", func() {
version, err := getXMajorsBeforeLatestMachineImageVersion(rawVersions, 1)
Expect(err).ToNot(HaveOccurred())
Expect(version.Version).To(Equal("2.3.4"))
})

It("should return the latest-2 of several machine image versions", func() {
version, err := getXMajorsBeforeLatestMachineImageVersion(rawVersions, 2)
Expect(err).ToNot(HaveOccurred())
Expect(version.Version).To(Equal("1.3.4"))
})

It("should return an error if no matching version is found for latest-x", func() {
_, err := getXMajorsBeforeLatestMachineImageVersion(rawVersions, 3)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("no machine image version matching the pattern latest-3 found"))
})

It("should consider full version higher than pre-release and build", func() {
rawVersions = append(rawVersions,
gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "1.2.4+build",
Version: "3.2.4+build",
},
},
gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "1.2.4-pre+build",
Version: "3.2.4-pre+build",
},
},
)

version, err := getLatestMachineImageVersion(rawVersions)
version, err := getXMajorsBeforeLatestMachineImageVersion(rawVersions, 0)
Expect(err).ToNot(HaveOccurred())
Expect(version.Version).To(Equal("1.2.4"))
Expect(version.Version).To(Equal("3.2.4"))

})

It("should skip build versions", func() {
rawVersions = append(rawVersions,
gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "1.2.5+build",
Version: "3.2.5+build",
},
},
gardencorev1beta1.MachineImageVersion{
ExpirableVersion: gardencorev1beta1.ExpirableVersion{
Version: "1.2.5-pre+build",
Version: "3.2.5-pre+build",
},
},
)

version, err := getLatestMachineImageVersion(rawVersions)
version, err := getXMajorsBeforeLatestMachineImageVersion(rawVersions, 0)
Expect(err).ToNot(HaveOccurred())
Expect(version.Version).To(Equal("1.2.4"))
Expect(version.Version).To(Equal("3.2.4"))

})
})