mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
492 lines
14 KiB
Go
492 lines
14 KiB
Go
/*
|
|
Copyright 2016 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package util
|
|
|
|
import (
|
|
"fmt"
|
|
"path"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
|
)
|
|
|
|
func TestEmptyVersion(t *testing.T) {
|
|
|
|
ver, err := KubernetesReleaseVersion("")
|
|
if err == nil {
|
|
t.Error("KubernetesReleaseVersion returned successfully, but error expected")
|
|
}
|
|
if ver != "" {
|
|
t.Error("KubernetesReleaseVersion returned value, expected only error")
|
|
}
|
|
}
|
|
|
|
func TestValidVersion(t *testing.T) {
|
|
validVersions := []string{
|
|
"v1.3.0",
|
|
"v1.4.0-alpha.0",
|
|
"v1.4.5",
|
|
"v1.4.0-beta.0",
|
|
"v2.0.0",
|
|
"v1.6.0-alpha.0.536+d60d9f3269288f",
|
|
"v1.5.0-alpha.0.1078+1044b6822497da-pull",
|
|
"v1.5.0-alpha.1.822+49b9e32fad9f32-pull-gke-gci",
|
|
"v1.6.1+coreos.0",
|
|
"1.7.1",
|
|
}
|
|
for _, s := range validVersions {
|
|
t.Run(s, func(t *testing.T) {
|
|
ver, err := kubernetesReleaseVersion(s, errorFetcher)
|
|
t.Log("Valid: ", s, ver, err)
|
|
if err != nil {
|
|
t.Errorf("KubernetesReleaseVersion unexpected error for version %q: %v", s, err)
|
|
}
|
|
if ver != s && ver != "v"+s {
|
|
t.Errorf("KubernetesReleaseVersion should return same valid version string. %q != %q", s, ver)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInvalidVersion(t *testing.T) {
|
|
invalidVersions := []string{
|
|
"v1.3",
|
|
"1.4",
|
|
"b1.4.0",
|
|
"c1.4.5+git",
|
|
"something1.2",
|
|
}
|
|
for _, s := range invalidVersions {
|
|
t.Run(s, func(t *testing.T) {
|
|
ver, err := kubernetesReleaseVersion(s, errorFetcher)
|
|
t.Log("Invalid: ", s, ver, err)
|
|
if err == nil {
|
|
t.Errorf("KubernetesReleaseVersion error expected for version %q, but returned successfully", s)
|
|
}
|
|
if ver != "" {
|
|
t.Errorf("KubernetesReleaseVersion should return empty string in case of error. Returned %q for version %q", ver, s)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidConvenientForUserVersion(t *testing.T) {
|
|
validVersions := []string{
|
|
"1.4.0",
|
|
"1.4.5+git",
|
|
"1.6.1_coreos.0",
|
|
}
|
|
for _, s := range validVersions {
|
|
t.Run(s, func(t *testing.T) {
|
|
ver, err := kubernetesReleaseVersion(s, errorFetcher)
|
|
t.Log("Valid: ", s, ver, err)
|
|
if err != nil {
|
|
t.Errorf("KubernetesReleaseVersion unexpected error for version %q: %v", s, err)
|
|
}
|
|
if ver != "v"+s {
|
|
t.Errorf("KubernetesReleaseVersion should return semantic version string. %q vs. %q", s, ver)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVersionFromNetwork(t *testing.T) {
|
|
type T struct {
|
|
Content string
|
|
Expected string
|
|
FetcherErrorExpected bool
|
|
ErrorExpected bool
|
|
}
|
|
|
|
currentVersion := normalizedBuildVersion(constants.CurrentKubernetesVersion.String())
|
|
|
|
cases := map[string]T{
|
|
"stable": {"stable-1", "v1.4.6", false, false}, // recursive pointer to stable-1
|
|
"stable-1": {"v1.4.6", "v1.4.6", false, false},
|
|
"stable-1.3": {"v1.3.10", "v1.3.10", false, false},
|
|
"latest": {"v1.6.0-alpha.0", "v1.6.0-alpha.0", false, false},
|
|
"latest-1.3": {"v1.3.11-beta.0", "v1.3.11-beta.0", false, false},
|
|
"latest-1.5": {"", currentVersion, true, false}, // fallback to currentVersion on fetcher error
|
|
"invalid-version": {"", "", false, true}, // invalid version cannot be parsed
|
|
}
|
|
|
|
for k, v := range cases {
|
|
t.Run(k, func(t *testing.T) {
|
|
|
|
fileFetcher := func(url string, timeout time.Duration) (string, error) {
|
|
key := strings.TrimSuffix(path.Base(url), ".txt")
|
|
res, found := cases[key]
|
|
if found {
|
|
if v.FetcherErrorExpected {
|
|
return "error", errors.New("expected error")
|
|
}
|
|
return res.Content, nil
|
|
}
|
|
return "Unknown test case key!", errors.New("unknown test case key")
|
|
}
|
|
|
|
ver, err := kubernetesReleaseVersion(k, fileFetcher)
|
|
t.Logf("Key: %q. Result: %q, Error: %v", k, ver, err)
|
|
switch {
|
|
case err != nil && !v.ErrorExpected:
|
|
t.Errorf("KubernetesReleaseVersion: unexpected error for %q. Error: %+v", k, err)
|
|
case err == nil && v.ErrorExpected:
|
|
t.Errorf("KubernetesReleaseVersion: error expected for key %q, but result is %q", k, ver)
|
|
case ver != v.Expected:
|
|
t.Errorf("KubernetesReleaseVersion: unexpected result for key %q. Expected: %q Actual: %q", k, v.Expected, ver)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVersionToTag(t *testing.T) {
|
|
type T struct {
|
|
input string
|
|
expected string
|
|
}
|
|
cases := []T{
|
|
// NOP
|
|
{"", ""},
|
|
// Official releases
|
|
{"v1.0.0", "v1.0.0"},
|
|
// CI or custom builds
|
|
{"v10.1.2-alpha.1.100+0123456789abcdef+SOMETHING", "v10.1.2-alpha.1.100_0123456789abcdef_SOMETHING"},
|
|
// random and invalid input: should return safe value
|
|
{"v1,0!0+üñµ", "v1_0_0____"},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(fmt.Sprintf("input:%s/expected:%s", tc.input, tc.expected), func(t *testing.T) {
|
|
tag := KubernetesVersionToImageTag(tc.input)
|
|
t.Logf("KubernetesVersionToImageTag: Input: %q. Result: %q. Expected: %q", tc.input, tag, tc.expected)
|
|
if tag != tc.expected {
|
|
t.Errorf("failed KubernetesVersionToImageTag: Input: %q. Result: %q. Expected: %q", tc.input, tag, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSplitVersion(t *testing.T) {
|
|
type T struct {
|
|
input string
|
|
bucket string
|
|
label string
|
|
valid bool
|
|
}
|
|
cases := []T{
|
|
// Release area
|
|
{"v1.7.0", "https://dl.k8s.io/release", "v1.7.0", true},
|
|
{"v1.8.0-alpha.2.1231+afabd012389d53a", "https://dl.k8s.io/release", "v1.8.0-alpha.2.1231+afabd012389d53a", true},
|
|
{"release/v1.7.0", "https://dl.k8s.io/release", "v1.7.0", true},
|
|
{"release/latest-1.7", "https://dl.k8s.io/release", "latest-1.7", true},
|
|
// CI builds area
|
|
{"ci/latest", "https://storage.googleapis.com/k8s-release-dev/ci", "latest", true},
|
|
{"ci/latest-1.7", "https://storage.googleapis.com/k8s-release-dev/ci", "latest-1.7", true},
|
|
// unknown label in default (release) area: splitVersion validate only areas.
|
|
{"unknown-1", "https://dl.k8s.io/release", "unknown-1", true},
|
|
// unknown area, not valid input.
|
|
{"unknown/latest-1", "", "", false},
|
|
// invalid input
|
|
{"", "", "", false},
|
|
{"ci/", "", "", false},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(fmt.Sprintf("input:%s/label:%s", tc.input, tc.label), func(t *testing.T) {
|
|
bucket, label, err := splitVersion(tc.input)
|
|
switch {
|
|
case err != nil && tc.valid:
|
|
t.Errorf("splitVersion: unexpected error for %q. Error: %v", tc.input, err)
|
|
case err == nil && !tc.valid:
|
|
t.Errorf("splitVersion: error expected for key %q, but result is %q, %q", tc.input, bucket, label)
|
|
case bucket != tc.bucket:
|
|
t.Errorf("splitVersion: unexpected bucket result for key %q. Expected: %q Actual: %q", tc.input, tc.bucket, bucket)
|
|
case label != tc.label:
|
|
t.Errorf("splitVersion: unexpected label result for key %q. Expected: %q Actual: %q", tc.input, tc.label, label)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestKubernetesIsCIVersion(t *testing.T) {
|
|
type T struct {
|
|
input string
|
|
expected bool
|
|
}
|
|
cases := []T{
|
|
{"", false},
|
|
// Official releases
|
|
{"v1.0.0", false},
|
|
{"release/v1.0.0", false},
|
|
// CI builds
|
|
{"ci/latest-1", true},
|
|
{"ci/v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
|
|
{"ci/", false},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(fmt.Sprintf("input:%s/expected:%t", tc.input, tc.expected), func(t *testing.T) {
|
|
result := KubernetesIsCIVersion(tc.input)
|
|
t.Logf("KubernetesIsCIVersion: Input: %q. Result: %v. Expected: %v", tc.input, result, tc.expected)
|
|
if result != tc.expected {
|
|
t.Errorf("failed KubernetesIsCIVersion: Input: %q. Result: %v. Expected: %v", tc.input, result, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Validate KubernetesReleaseVersion but with bucket prefixes
|
|
func TestCIBuildVersion(t *testing.T) {
|
|
type T struct {
|
|
input string
|
|
expected string
|
|
valid bool
|
|
}
|
|
cases := []T{
|
|
// Official releases
|
|
{"v1.7.0", "v1.7.0", true},
|
|
{"release/v1.8.0", "v1.8.0", true},
|
|
{"1.4.0-beta.0", "v1.4.0-beta.0", true},
|
|
{"release/0invalid", "", false},
|
|
// CI or custom builds
|
|
{"ci/v1.9.0-alpha.1.123+acbcbfd53bfa0a", "v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
|
|
{"ci/1.9.0-alpha.1.123+acbcbfd53bfa0a", "v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
|
|
{"ci/0invalid", "", false},
|
|
{"0invalid", "", false},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(fmt.Sprintf("input:%s/expected:%s", tc.input, tc.expected), func(t *testing.T) {
|
|
|
|
fileFetcher := func(url string, timeout time.Duration) (string, error) {
|
|
if tc.valid {
|
|
return tc.expected, nil
|
|
}
|
|
return "Unknown test case key!", errors.New("unknown test case key")
|
|
}
|
|
|
|
ver, err := kubernetesReleaseVersion(tc.input, fileFetcher)
|
|
t.Logf("Input: %q. Result: %q, Error: %v", tc.input, ver, err)
|
|
switch {
|
|
case err != nil && tc.valid:
|
|
t.Errorf("KubernetesReleaseVersion: unexpected error for input %q. Error: %v", tc.input, err)
|
|
case err == nil && !tc.valid:
|
|
t.Errorf("KubernetesReleaseVersion: error expected for input %q, but result is %q", tc.input, ver)
|
|
case ver != tc.expected:
|
|
t.Errorf("KubernetesReleaseVersion: unexpected result for input %q. Expected: %q Actual: %q", tc.input, tc.expected, ver)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNormalizedBuildVersionVersion(t *testing.T) {
|
|
type T struct {
|
|
input string
|
|
expected string
|
|
}
|
|
cases := []T{
|
|
{"v1.7.0", "v1.7.0"},
|
|
{"v1.8.0-alpha.2.1231+afabd012389d53a", "v1.8.0-alpha.2.1231+afabd012389d53a"},
|
|
{"1.7.0", "v1.7.0"},
|
|
{"unknown-1", ""},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(fmt.Sprintf("input:%s/expected:%s", tc.input, tc.expected), func(t *testing.T) {
|
|
output := normalizedBuildVersion(tc.input)
|
|
if output != tc.expected {
|
|
t.Errorf("normalizedBuildVersion: unexpected output %q for input %q. Expected: %q", output, tc.input, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestKubeadmVersion(t *testing.T) {
|
|
type T struct {
|
|
name string
|
|
input string
|
|
output string
|
|
outputError bool
|
|
parsingError bool
|
|
}
|
|
cases := []T{
|
|
{
|
|
name: "valid version with label and metadata",
|
|
input: "v1.8.0-alpha.2.1231+afabd012389d53a",
|
|
output: "v1.8.0-alpha.2",
|
|
},
|
|
{
|
|
name: "valid version with label and extra metadata",
|
|
input: "v1.8.0-alpha.2.1231+afabd012389d53a.extra",
|
|
output: "v1.8.0-alpha.2",
|
|
},
|
|
{
|
|
name: "valid patch version with label and extra metadata",
|
|
input: "v1.11.3-beta.0.38+135cc4c1f47994",
|
|
output: "v1.11.2",
|
|
},
|
|
{
|
|
name: "valid version with label extra",
|
|
input: "v1.8.0-alpha.2.1231",
|
|
output: "v1.8.0-alpha.2",
|
|
},
|
|
{
|
|
name: "valid patch version with label",
|
|
input: "v1.9.11-beta.0",
|
|
output: "v1.9.10",
|
|
},
|
|
{
|
|
name: "handle version with partial label",
|
|
input: "v1.8.0-alpha",
|
|
output: "v1.8.0-alpha.0",
|
|
},
|
|
{
|
|
name: "handle version missing 'v'",
|
|
input: "1.11.0",
|
|
output: "v1.11.0",
|
|
},
|
|
{
|
|
name: "valid version without label and metadata",
|
|
input: "v1.8.0",
|
|
output: "v1.8.0",
|
|
},
|
|
{
|
|
name: "valid patch version without label and metadata",
|
|
input: "v1.8.2",
|
|
output: "v1.8.2",
|
|
},
|
|
{
|
|
name: "invalid version",
|
|
input: "foo",
|
|
parsingError: true,
|
|
},
|
|
{
|
|
name: "invalid version with stray dash",
|
|
input: "v1.9.11-",
|
|
parsingError: true,
|
|
},
|
|
{
|
|
name: "invalid version without patch release",
|
|
input: "v1.9",
|
|
parsingError: true,
|
|
},
|
|
{
|
|
name: "invalid version with label and stray dot",
|
|
input: "v1.8.0-alpha.2.",
|
|
parsingError: true,
|
|
},
|
|
{
|
|
name: "invalid version with label and metadata",
|
|
input: "v1.8.0-alpha.2.1231+afabd012389d53a",
|
|
output: "v1.8.0-alpha.3",
|
|
outputError: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
output, err := kubeadmVersion(tc.input)
|
|
if (err != nil) != tc.parsingError {
|
|
t.Fatalf("expected error: %v, got: %v", tc.parsingError, err != nil)
|
|
}
|
|
if (output != tc.output) != tc.outputError {
|
|
t.Fatalf("expected output: %s, got: %s, for input: %s", tc.output, output, tc.input)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateStableVersion(t *testing.T) {
|
|
type T struct {
|
|
name string
|
|
remoteVersion string
|
|
clientVersion string
|
|
output string
|
|
expectedError bool
|
|
}
|
|
cases := []T{
|
|
{
|
|
name: "valid: remote version is newer; return stable label [1]",
|
|
remoteVersion: "v1.12.0",
|
|
clientVersion: "v1.11.0",
|
|
output: "stable-1.11",
|
|
},
|
|
{
|
|
name: "valid: remote version is newer; return stable label [2]",
|
|
remoteVersion: "v2.0.0",
|
|
clientVersion: "v1.11.0",
|
|
output: "stable-1.11",
|
|
},
|
|
{
|
|
name: "valid: remote version is newer; return stable label [3]",
|
|
remoteVersion: "v2.1.5",
|
|
clientVersion: "v1.11.5",
|
|
output: "stable-1.11",
|
|
},
|
|
{
|
|
name: "valid: return the remote version as it is part of the same release",
|
|
remoteVersion: "v1.11.5",
|
|
clientVersion: "v1.11.0",
|
|
output: "v1.11.5",
|
|
},
|
|
{
|
|
name: "valid: return the same version",
|
|
remoteVersion: "v1.11.0",
|
|
clientVersion: "v1.11.0",
|
|
output: "v1.11.0",
|
|
},
|
|
{
|
|
name: "invalid: client version is empty",
|
|
remoteVersion: "v1.12.1",
|
|
clientVersion: "",
|
|
expectedError: true,
|
|
},
|
|
{
|
|
name: "invalid: error parsing the remote version",
|
|
remoteVersion: "invalid-version",
|
|
clientVersion: "v1.12.0",
|
|
expectedError: true,
|
|
},
|
|
{
|
|
name: "invalid: error parsing the client version",
|
|
remoteVersion: "v1.12.0",
|
|
clientVersion: "invalid-version",
|
|
expectedError: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
output, err := validateStableVersion(tc.remoteVersion, tc.clientVersion)
|
|
if (err != nil) != tc.expectedError {
|
|
t.Fatalf("expected error: %v, got: %v", tc.expectedError, err != nil)
|
|
}
|
|
if output != tc.output {
|
|
t.Fatalf("expected output: %s, got: %s", tc.output, output)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func errorFetcher(url string, timeout time.Duration) (string, error) {
|
|
return "should not make internet calls", errors.Errorf("should not make internet calls, tried to request url: %s", url)
|
|
}
|