From 42356bfbb3e85b220fdae69ed1a51ed218bc4a87 Mon Sep 17 00:00:00 2001 From: TommyStarK Date: Tue, 20 Jun 2023 10:19:41 +0200 Subject: [PATCH] move common logic of highestSupportedVersion to util package Signed-off-by: TommyStarK --- pkg/kubelet/cm/dra/plugin/plugin.go | 43 +------ pkg/volume/csi/csi_plugin.go | 45 +------ pkg/volume/csi/csi_plugin_test.go | 111 +----------------- .../apimachinery/pkg/util/version/version.go | 42 +++++++ .../pkg/util/version/version_test.go | 106 +++++++++++++++++ 5 files changed, 158 insertions(+), 189 deletions(-) diff --git a/pkg/kubelet/cm/dra/plugin/plugin.go b/pkg/kubelet/cm/dra/plugin/plugin.go index 0f2a1ff4cb4..1e2384e0d4f 100644 --- a/pkg/kubelet/cm/dra/plugin/plugin.go +++ b/pkg/kubelet/cm/dra/plugin/plugin.go @@ -61,47 +61,6 @@ func (h *RegistrationHandler) RegisterPlugin(pluginName string, endpoint string, return nil } -// Return the highest supported version. -func highestSupportedVersion(versions []string) (*utilversion.Version, error) { - if len(versions) == 0 { - return nil, errors.New(log("DRA plugin reporting empty array for supported versions")) - } - - var highestSupportedVersion *utilversion.Version - - var theErr error - - for i := len(versions) - 1; i >= 0; i-- { - currentHighestVer, err := utilversion.ParseGeneric(versions[i]) - if err != nil { - theErr = err - - continue - } - - if currentHighestVer.Major() > 1 { - // DRA currently only has version 1.x - continue - } - - if highestSupportedVersion == nil || highestSupportedVersion.LessThan(currentHighestVer) { - highestSupportedVersion = currentHighestVer - } - } - - if highestSupportedVersion == nil { - return nil, fmt.Errorf( - "could not find a highest supported version from versions (%v) reported by this plugin: %+v", - versions, theErr) - } - - if highestSupportedVersion.Major() != 1 { - return nil, fmt.Errorf("highest supported version reported by plugin is %v, must be v1.x", highestSupportedVersion) - } - - return highestSupportedVersion, nil -} - func (h *RegistrationHandler) validateVersions( callerName string, pluginName string, @@ -118,7 +77,7 @@ func (h *RegistrationHandler) validateVersions( } // Validate version - newPluginHighestVersion, err := highestSupportedVersion(versions) + newPluginHighestVersion, err := utilversion.HighestSupportedVersion(versions) if err != nil { return nil, errors.New( log( diff --git a/pkg/volume/csi/csi_plugin.go b/pkg/volume/csi/csi_plugin.go index 4c26662797c..c5688e2de8a 100644 --- a/pkg/volume/csi/csi_plugin.go +++ b/pkg/volume/csi/csi_plugin.go @@ -157,7 +157,12 @@ func (h *RegistrationHandler) validateVersions(callerName, pluginName string, en } // Validate version - newDriverHighestVersion, err := highestSupportedVersion(versions) + // CSI currently only has version 0.x and 1.x (see https://github.com/container-storage-interface/spec/releases). + // Therefore any driver claiming version 2.x+ is ignored as an unsupported versions. + // Future 1.x versions of CSI are supposed to be backwards compatible so this version of Kubernetes will work with any 1.x driver + // (or 0.x), but it may not work with 2.x drivers (because 2.x does not have to be backwards compatible with 1.x). + // CSI v0.x is no longer supported as of Kubernetes v1.17 in accordance with deprecation policy set out in Kubernetes v1.13. + newDriverHighestVersion, err := utilversion.HighestSupportedVersion(versions) if err != nil { return nil, errors.New(log("%s for CSI driver %q failed. None of the versions specified %q are supported. err=%v", callerName, pluginName, versions, err)) } @@ -858,44 +863,6 @@ func unregisterDriver(driverName string) error { return nil } -// Return the highest supported version -func highestSupportedVersion(versions []string) (*utilversion.Version, error) { - if len(versions) == 0 { - return nil, errors.New(log("CSI driver reporting empty array for supported versions")) - } - - var highestSupportedVersion *utilversion.Version - var theErr error - for i := len(versions) - 1; i >= 0; i-- { - currentHighestVer, err := utilversion.ParseGeneric(versions[i]) - if err != nil { - theErr = err - continue - } - if currentHighestVer.Major() > 1 { - // CSI currently only has version 0.x and 1.x (see https://github.com/container-storage-interface/spec/releases). - // Therefore any driver claiming version 2.x+ is ignored as an unsupported versions. - // Future 1.x versions of CSI are supposed to be backwards compatible so this version of Kubernetes will work with any 1.x driver - // (or 0.x), but it may not work with 2.x drivers (because 2.x does not have to be backwards compatible with 1.x). - continue - } - if highestSupportedVersion == nil || highestSupportedVersion.LessThan(currentHighestVer) { - highestSupportedVersion = currentHighestVer - } - } - - if highestSupportedVersion == nil { - return nil, fmt.Errorf("could not find a highest supported version from versions (%v) reported by this driver: %v", versions, theErr) - } - - if highestSupportedVersion.Major() != 1 { - // CSI v0.x is no longer supported as of Kubernetes v1.17 in - // accordance with deprecation policy set out in Kubernetes v1.13 - return nil, fmt.Errorf("highest supported version reported by driver is %v, must be v1.x", highestSupportedVersion) - } - return highestSupportedVersion, nil -} - // waitForAPIServerForever waits forever to get a CSINode instance as a proxy // for a healthy APIServer func waitForAPIServerForever(client clientset.Interface, nodeName types.NodeName) error { diff --git a/pkg/volume/csi/csi_plugin_test.go b/pkg/volume/csi/csi_plugin_test.go index 4a76d734195..8df7a4ce9f6 100644 --- a/pkg/volume/csi/csi_plugin_test.go +++ b/pkg/volume/csi/csi_plugin_test.go @@ -28,6 +28,7 @@ import ( storage "k8s.io/api/storage/v1" meta "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + utilversion "k8s.io/apimachinery/pkg/util/version" "k8s.io/apimachinery/pkg/util/wait" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/informers" @@ -142,7 +143,7 @@ func newTestPluginWithVolumeHost(t *testing.T, client *fakeclient.Clientset, hos } func registerFakePlugin(pluginName, endpoint string, versions []string, t *testing.T) { - highestSupportedVersions, err := highestSupportedVersion(versions) + highestSupportedVersions, err := utilversion.HighestSupportedVersion(versions) if err != nil { t.Fatalf("unexpected error parsing versions (%v) for pluginName %q endpoint %q: %#v", versions, pluginName, endpoint, err) } @@ -1411,7 +1412,7 @@ func TestValidatePluginExistingDriver(t *testing.T) { for _, tc := range testCases { // Arrange & Act - highestSupportedVersions1, err := highestSupportedVersion(tc.versions1) + highestSupportedVersions1, err := utilversion.HighestSupportedVersion(tc.versions1) if err != nil { t.Fatalf("unexpected error parsing version for testcase: %#v: %v", tc, err) } @@ -1434,109 +1435,3 @@ func TestValidatePluginExistingDriver(t *testing.T) { } } } - -func TestHighestSupportedVersion(t *testing.T) { - testCases := []struct { - versions []string - expectedHighestSupportedVersion string - shouldFail bool - }{ - { - versions: []string{"v1.0.0"}, - expectedHighestSupportedVersion: "1.0.0", - shouldFail: false, - }, - { - versions: []string{"0.3.0"}, - shouldFail: true, - }, - { - versions: []string{"0.2.0"}, - shouldFail: true, - }, - { - versions: []string{"1.0.0"}, - expectedHighestSupportedVersion: "1.0.0", - shouldFail: false, - }, - { - versions: []string{"v0.3.0"}, - shouldFail: true, - }, - { - versions: []string{"0.2.0"}, - shouldFail: true, - }, - { - versions: []string{"0.2.0", "v0.3.0"}, - shouldFail: true, - }, - { - versions: []string{"0.2.0", "v1.0.0"}, - expectedHighestSupportedVersion: "1.0.0", - shouldFail: false, - }, - { - versions: []string{"0.2.0", "v1.2.3"}, - expectedHighestSupportedVersion: "1.2.3", - shouldFail: false, - }, - { - versions: []string{"v1.2.3", "v0.3.0"}, - expectedHighestSupportedVersion: "1.2.3", - shouldFail: false, - }, - { - versions: []string{"v1.2.3", "v0.3.0", "2.0.1"}, - expectedHighestSupportedVersion: "1.2.3", - shouldFail: false, - }, - { - versions: []string{"v1.2.3", "4.9.12", "v0.3.0", "2.0.1"}, - expectedHighestSupportedVersion: "1.2.3", - shouldFail: false, - }, - { - versions: []string{"4.9.12", "2.0.1"}, - expectedHighestSupportedVersion: "", - shouldFail: true, - }, - { - versions: []string{"v1.2.3", "boo", "v0.3.0", "2.0.1"}, - expectedHighestSupportedVersion: "1.2.3", - shouldFail: false, - }, - { - versions: []string{}, - expectedHighestSupportedVersion: "", - shouldFail: true, - }, - { - versions: []string{"var", "boo", "foo"}, - expectedHighestSupportedVersion: "", - shouldFail: true, - }, - } - - for _, tc := range testCases { - // Arrange & Act - actual, err := highestSupportedVersion(tc.versions) - - // Assert - if tc.shouldFail && err == nil { - t.Fatalf("expecting highestSupportedVersion to fail, but got nil error for testcase: %#v", tc) - } - if !tc.shouldFail && err != nil { - t.Fatalf("unexpected error during ValidatePlugin for testcase: %#v\r\n err:%v", tc, err) - } - if tc.expectedHighestSupportedVersion != "" { - result, err := actual.Compare(tc.expectedHighestSupportedVersion) - if err != nil { - t.Fatalf("comparison failed with %v for testcase %#v", err, tc) - } - if result != 0 { - t.Fatalf("expectedHighestSupportedVersion %v, but got %v for tc: %#v", tc.expectedHighestSupportedVersion, actual, tc) - } - } - } -} diff --git a/staging/src/k8s.io/apimachinery/pkg/util/version/version.go b/staging/src/k8s.io/apimachinery/pkg/util/version/version.go index 4c619569533..2292ba13765 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/version/version.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/version/version.go @@ -18,6 +18,7 @@ package version import ( "bytes" + "errors" "fmt" "regexp" "strconv" @@ -85,6 +86,47 @@ func parse(str string, semver bool) (*Version, error) { return v, nil } +// HighestSupportedVersion returns the highest supported version +// This function assumes that the highest supported version must be v1.x. +func HighestSupportedVersion(versions []string) (*Version, error) { + if len(versions) == 0 { + return nil, errors.New("empty array for supported versions") + } + + var ( + highestSupportedVersion *Version + theErr error + ) + + for i := len(versions) - 1; i >= 0; i-- { + currentHighestVer, err := ParseGeneric(versions[i]) + if err != nil { + theErr = err + continue + } + + if currentHighestVer.Major() > 1 { + continue + } + + if highestSupportedVersion == nil || highestSupportedVersion.LessThan(currentHighestVer) { + highestSupportedVersion = currentHighestVer + } + } + + if highestSupportedVersion == nil { + return nil, fmt.Errorf( + "could not find a highest supported version from versions (%v) reported: %+v", + versions, theErr) + } + + if highestSupportedVersion.Major() != 1 { + return nil, fmt.Errorf("highest supported version reported is %v, must be v1.x", highestSupportedVersion) + } + + return highestSupportedVersion, nil +} + // ParseGeneric parses a "generic" version string. The version string must consist of two // or more dot-separated numeric fields (the first of which can't have leading zeroes), // followed by arbitrary uninterpreted data (which need not be separated from the final diff --git a/staging/src/k8s.io/apimachinery/pkg/util/version/version_test.go b/staging/src/k8s.io/apimachinery/pkg/util/version/version_test.go index aa0675f7f12..4d36bf3c121 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/version/version_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/version/version_test.go @@ -346,3 +346,109 @@ func TestComponents(t *testing.T) { } } } + +func TestHighestSupportedVersion(t *testing.T) { + testCases := []struct { + versions []string + expectedHighestSupportedVersion string + shouldFail bool + }{ + { + versions: []string{"v1.0.0"}, + expectedHighestSupportedVersion: "1.0.0", + shouldFail: false, + }, + { + versions: []string{"0.3.0"}, + shouldFail: true, + }, + { + versions: []string{"0.2.0"}, + shouldFail: true, + }, + { + versions: []string{"1.0.0"}, + expectedHighestSupportedVersion: "1.0.0", + shouldFail: false, + }, + { + versions: []string{"v0.3.0"}, + shouldFail: true, + }, + { + versions: []string{"v0.2.0"}, + shouldFail: true, + }, + { + versions: []string{"0.2.0", "v0.3.0"}, + shouldFail: true, + }, + { + versions: []string{"0.2.0", "v1.0.0"}, + expectedHighestSupportedVersion: "1.0.0", + shouldFail: false, + }, + { + versions: []string{"0.2.0", "v1.2.3"}, + expectedHighestSupportedVersion: "1.2.3", + shouldFail: false, + }, + { + versions: []string{"v1.2.3", "v0.3.0"}, + expectedHighestSupportedVersion: "1.2.3", + shouldFail: false, + }, + { + versions: []string{"v1.2.3", "v0.3.0", "2.0.1"}, + expectedHighestSupportedVersion: "1.2.3", + shouldFail: false, + }, + { + versions: []string{"v1.2.3", "4.9.12", "v0.3.0", "2.0.1"}, + expectedHighestSupportedVersion: "1.2.3", + shouldFail: false, + }, + { + versions: []string{"4.9.12", "2.0.1"}, + expectedHighestSupportedVersion: "", + shouldFail: true, + }, + { + versions: []string{"v1.2.3", "boo", "v0.3.0", "2.0.1"}, + expectedHighestSupportedVersion: "1.2.3", + shouldFail: false, + }, + { + versions: []string{}, + expectedHighestSupportedVersion: "", + shouldFail: true, + }, + { + versions: []string{"var", "boo", "foo"}, + expectedHighestSupportedVersion: "", + shouldFail: true, + }, + } + + for _, tc := range testCases { + // Arrange & Act + actual, err := HighestSupportedVersion(tc.versions) + + // Assert + if tc.shouldFail && err == nil { + t.Fatalf("expecting highestSupportedVersion to fail, but got nil error for testcase: %#v", tc) + } + if !tc.shouldFail && err != nil { + t.Fatalf("unexpected error during ValidatePlugin for testcase: %#v\r\n err:%v", tc, err) + } + if tc.expectedHighestSupportedVersion != "" { + result, err := actual.Compare(tc.expectedHighestSupportedVersion) + if err != nil { + t.Fatalf("comparison failed with %v for testcase %#v", err, tc) + } + if result != 0 { + t.Fatalf("expectedHighestSupportedVersion %v, but got %v for tc: %#v", tc.expectedHighestSupportedVersion, actual, tc) + } + } + } +}