123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- /*
- 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"
- "net/http"
- "net/http/httptest"
- "path"
- "strings"
- "testing"
- )
- 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",
- }
- for _, s := range validVersions {
- t.Run(s, func(t *testing.T) {
- ver, err := KubernetesReleaseVersion(s)
- t.Log("Valid: ", s, ver, err)
- if err != nil {
- t.Errorf("KubernetesReleaseVersion unexpected error for version %q: %v", s, err)
- }
- if ver != 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)
- 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)
- 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
- Status int
- Expected string
- ErrorExpected bool
- }
- cases := map[string]T{
- "stable": {"stable-1", http.StatusOK, "v1.4.6", false}, // recursive pointer to stable-1
- "stable-1": {"v1.4.6", http.StatusOK, "v1.4.6", false},
- "stable-1.3": {"v1.3.10", http.StatusOK, "v1.3.10", false},
- "latest": {"v1.6.0-alpha.0", http.StatusOK, "v1.6.0-alpha.0", false},
- "latest-1.3": {"v1.3.11-beta.0", http.StatusOK, "v1.3.11-beta.0", false},
- "empty": {"", http.StatusOK, "", true},
- "garbage": {"<?xml version='1.0'?><Error><Code>NoSuchKey</Code><Message>The specified key does not exist.</Message></Error>", http.StatusOK, "", true},
- "unknown": {"The requested URL was not found on this server.", http.StatusNotFound, "", true},
- }
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- key := strings.TrimSuffix(path.Base(r.URL.Path), ".txt")
- res, found := cases[key]
- if found {
- http.Error(w, res.Content, res.Status)
- } else {
- http.Error(w, "Unknown test case key!", http.StatusNotFound)
- }
- }))
- defer server.Close()
- kubeReleaseBucketURL = server.URL
- for k, v := range cases {
- t.Run(k, func(t *testing.T) {
- ver, err := KubernetesReleaseVersion(k)
- 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, lookup actual builds at ci-cross/*.txt
- {"ci/latest", "https://dl.k8s.io/ci", "latest", true},
- {"ci-cross/latest", "https://dl.k8s.io/ci-cross", "latest", true},
- {"ci/latest-1.7", "https://dl.k8s.io/ci", "latest-1.7", true},
- {"ci-cross/latest-1.7", "https://dl.k8s.io/ci-cross", "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},
- }
- // kubeReleaseBucketURL can be overridden during network tests, thus ensure
- // it will contain value corresponding to expected outcome for this unit test
- kubeReleaseBucketURL = "https://dl.k8s.io"
- 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-cross/latest", true},
- {"ci/v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
- {"ci-cross/v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
- }
- 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-cross/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-cross/1.9.0-alpha.1.123+acbcbfd53bfa0a", "v1.9.0-alpha.1.123+acbcbfd53bfa0a", true},
- {"ci/0invalid", "", false},
- }
- for _, tc := range cases {
- t.Run(fmt.Sprintf("input:%s/expected:%s", tc.input, tc.expected), func(t *testing.T) {
- ver, err := KubernetesReleaseVersion(tc.input)
- 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 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)
- }
- })
- }
- }
|