Skip to content

Commit fd4b54a

Browse files
committed
Fix --kubernetes-version for upgrading cluster to correct version
This PR fix the issue of checking kubernetes-version and upgrading it. The fix now will do the following * Not specifying the --kubernetes-version flag means just use the currently deployed version. * If an update is available inform the user that they may use --kubernetes-version=<version>. * When --kubernetes-version is specifically set, upgrade the cluster. Also add integration testing to test upgrading and downgrading
1 parent 792dbf9 commit fd4b54a

File tree

3 files changed

+206
-7
lines changed

3 files changed

+206
-7
lines changed

cmd/minikube/cmd/start.go

+19-7
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func initMinikubeFlags() {
179179

180180
// initKubernetesFlags inits the commandline flags for kubernetes related options
181181
func initKubernetesFlags() {
182-
startCmd.Flags().String(kubernetesVersion, constants.DefaultKubernetesVersion, "The kubernetes version that the minikube VM will use (ex: v1.2.3)")
182+
startCmd.Flags().String(kubernetesVersion, "", "The kubernetes version that the minikube VM will use (ex: v1.2.3)")
183183
startCmd.Flags().Var(&extraOptions, "extra-config",
184184
`A set of key=value pairs that describe configuration that may be passed to different components.
185185
The key should be '.' separated, and the first part before the dot is the component to apply the configuration to.
@@ -1057,15 +1057,20 @@ Suggested workarounds:
10571057

10581058
// getKubernetesVersion ensures that the requested version is reasonable
10591059
func getKubernetesVersion(old *cfg.Config) (string, bool) {
1060-
rawVersion := viper.GetString(kubernetesVersion)
1060+
paramVersion := viper.GetString(kubernetesVersion)
10611061
isUpgrade := false
1062-
if rawVersion == "" {
1063-
rawVersion = constants.DefaultKubernetesVersion
1062+
1063+
if paramVersion == "" { // if the user did not specify any version then ...
1064+
if old != nil { // .. use the old version from config
1065+
paramVersion = old.KubernetesConfig.KubernetesVersion
1066+
} else { // .. otherwise use the default version
1067+
paramVersion = constants.DefaultKubernetesVersion
1068+
}
10641069
}
10651070

1066-
nvs, err := semver.Make(strings.TrimPrefix(rawVersion, version.VersionPrefix))
1071+
nvs, err := semver.Make(strings.TrimPrefix(paramVersion, version.VersionPrefix))
10671072
if err != nil {
1068-
exit.WithCodeT(exit.Data, `Unable to parse "{{.kubernetes_version}}": {{.error}}`, out.V{"kubernetes_version": rawVersion, "error": err})
1073+
exit.WithCodeT(exit.Data, `Unable to parse "{{.kubernetes_version}}": {{.error}}`, out.V{"kubernetes_version": paramVersion, "error": err})
10691074
}
10701075
nv := version.VersionPrefix + nvs.String()
10711076

@@ -1077,6 +1082,10 @@ func getKubernetesVersion(old *cfg.Config) (string, bool) {
10771082
if err != nil {
10781083
exit.WithCodeT(exit.Data, "Unable to parse oldest Kubernetes version from constants: {{.error}}", out.V{"error": err})
10791084
}
1085+
defaultVersion, err := semver.Make(strings.TrimPrefix(constants.DefaultKubernetesVersion, version.VersionPrefix))
1086+
if err != nil {
1087+
exit.WithCodeT(exit.Data, "Unable to parse default Kubernetes version from constants: {{.error}}", out.V{"error": err})
1088+
}
10801089

10811090
if nvs.LT(oldestVersion) {
10821091
out.WarningT("Specified Kubernetes version {{.specified}} is less than the oldest supported version: {{.oldest}}", out.V{"specified": nvs, "oldest": constants.OldestKubernetesVersion})
@@ -1105,8 +1114,11 @@ func getKubernetesVersion(old *cfg.Config) (string, bool) {
11051114
* Reuse the existing cluster with Kubernetes v{{.old}} or newer: Run "minikube start {{.profile}} --kubernetes-version={{.old}}"`, out.V{"new": nvs, "old": ovs, "profile": profileArg})
11061115

11071116
}
1117+
if defaultVersion.GT(nvs) {
1118+
out.T(out.ThumbsUp, "Kubernetes {{.new}} is now available. If you would like to upgrade, specify: --kubernetes-version={{.new}}", out.V{"new": defaultVersion})
1119+
}
1120+
11081121
if nvs.GT(ovs) {
1109-
out.T(out.ThumbsUp, "Upgrading from Kubernetes {{.old}} to {{.new}}", out.V{"old": ovs, "new": nvs})
11101122
isUpgrade = true
11111123
}
11121124
return nv, isUpgrade

cmd/minikube/cmd/start_test.go

+81
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,95 @@ limitations under the License.
1717
package cmd
1818

1919
import (
20+
"io/ioutil"
2021
"os"
22+
"strings"
2123
"testing"
2224

2325
"github.com/spf13/cobra"
2426
"github.com/spf13/viper"
27+
cfg "k8s.io/minikube/pkg/minikube/config"
2528
"k8s.io/minikube/pkg/minikube/constants"
2629
)
2730

31+
func TestGetKuberneterVersion(t *testing.T) {
32+
var tests = []struct {
33+
description string
34+
expectedVersion string
35+
paramVersion string
36+
upgrade bool
37+
stderrmsg string
38+
cfg *cfg.Config
39+
}{
40+
{
41+
description: "kubernetes-version not given, no config",
42+
expectedVersion: constants.DefaultKubernetesVersion,
43+
paramVersion: "",
44+
upgrade: false,
45+
},
46+
{
47+
description: "kubernetes-version not given, config available",
48+
expectedVersion: "v1.15.0",
49+
paramVersion: "",
50+
upgrade: false,
51+
cfg: &cfg.Config{KubernetesConfig: cfg.KubernetesConfig{KubernetesVersion: "v1.15.0"}},
52+
},
53+
{
54+
description: "kubernetes-version given, no config",
55+
expectedVersion: "v1.15.0",
56+
paramVersion: "v1.15.0",
57+
upgrade: false,
58+
},
59+
{
60+
description: "kubernetes-version given, config available",
61+
expectedVersion: "v1.16.0",
62+
paramVersion: "v1.16.0",
63+
upgrade: true,
64+
stderrmsg: "Kubernetes 1.16.2 is now available",
65+
cfg: &cfg.Config{KubernetesConfig: cfg.KubernetesConfig{KubernetesVersion: "v1.15.0"}},
66+
},
67+
}
68+
69+
for _, test := range tests {
70+
t.Run(test.description, func(t *testing.T) {
71+
oldStderr := os.Stderr
72+
reader, writer, err := os.Pipe()
73+
if err != nil {
74+
t.Fatalf("something is wrong when trying os.Pipe")
75+
}
76+
77+
os.Stderr = writer
78+
79+
viper.SetDefault(kubernetesVersion, test.paramVersion)
80+
version, upgrade := getKubernetesVersion(test.cfg)
81+
82+
writer.Close()
83+
// read the output printed on stdErr
84+
out, err := ioutil.ReadAll(reader)
85+
if err != nil {
86+
t.Fatalf("error reading from the piped stderr")
87+
}
88+
os.Stderr = oldStderr
89+
reader.Close()
90+
91+
// check whether the printed output is the same
92+
if !strings.Contains(string(out), test.stderrmsg) {
93+
t.Fatalf("test failed returned result does not contain the string %s", test.stderrmsg)
94+
}
95+
96+
// check whether we are getting the expected version
97+
if version != test.expectedVersion {
98+
t.Fatalf("test failed because the expected version %s is not returned", test.expectedVersion)
99+
}
100+
101+
// check whether the upgrade flag is correct
102+
if test.upgrade != upgrade {
103+
t.Fatalf("test failed expected upgrade is %t", test.upgrade)
104+
}
105+
})
106+
}
107+
}
108+
28109
func TestGenerateCfgFromFlagsHTTPProxyHandling(t *testing.T) {
29110
viper.SetDefault(memory, defaultMemorySize)
30111
viper.SetDefault(humanReadableDiskSize, defaultDiskSize)

test/integration/version_upgrade_test.go

+106
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,112 @@ import (
3535
pkgutil "k8s.io/minikube/pkg/util"
3636
)
3737

38+
// TestDifferentVersionUpgrade test installation of different version of Kubernetes.
39+
// Start with installing from v1.13.0 then to v1.15.0 and then try to downgrade it
40+
// to v1.13.0
41+
// Following are the test cases that the function is doing
42+
// 1. Start with specific version
43+
// ./out/minikube start -p vupgrade --kubernetes-version=v1.13.0
44+
// 2. Upgrade with newer version
45+
// ./out/minikube start -p vupgrade --kubernetes-version=v1.13.0
46+
// 3. Downgrade with older version (this will spit out error and test the error message)
47+
// ./out/minikube start -p vupgrade --kubernetes-version=v1.13.0
48+
func TestDifferentVersionUpgrade(t *testing.T) {
49+
MaybeParallel(t)
50+
WaitForStartSlot(t)
51+
profile := UniqueProfileName("testkubernetestversions")
52+
53+
ctx, cancel := context.WithTimeout(context.Background(), 55*time.Minute)
54+
defer CleanupWithLogs(t, profile, cancel)
55+
56+
// create a temporary file
57+
tf, err := ioutil.TempFile("", "minikube-release.*.exe")
58+
if err != nil {
59+
t.Fatalf("tempfile: %v", err)
60+
}
61+
defer os.Remove(tf.Name())
62+
tf.Close()
63+
64+
// download minikube
65+
url := pkgutil.GetBinaryDownloadURL("latest", runtime.GOOS)
66+
67+
// chmod the binary (when Linux)
68+
if runtime.GOOS != "windows" {
69+
if err := os.Chmod(tf.Name(), 0700); err != nil {
70+
t.Errorf("chmod: %v", err)
71+
}
72+
}
73+
74+
// download the file
75+
if err := retry.Expo(func() error { return getter.GetFile(tf.Name(), url) }, 3*time.Second, 3*time.Minute); err != nil {
76+
t.Fatalf("get failed: %v", err)
77+
}
78+
79+
t.Run("parallel", func(t *testing.T) {
80+
tests := []struct {
81+
name string
82+
message string
83+
kubernetesversion string
84+
expectstartupfailure bool
85+
}{
86+
{"fresh install kubernetes v1.13.0", "v1.13.0","v1.13.0", false},
87+
{"upgrade kubernetes to v 1.14.0", "v1.14.0","v1.14.0", false},
88+
{"downgrade kubernetes to v 1.12.0", "Non-destructive downgrades are not supported","v1.12.0", true},
89+
}
90+
for _, tc := range tests {
91+
tc := tc
92+
t.Run(tc.name, func(t *testing.T) {
93+
// instruct to install kubernetes v1.13.0
94+
args := append([]string{"start", "-p", profile, fmt.Sprintf("--kubernetes-version=%s", tc.kubernetesversion), "--alsologtostderr", "-v=1"}, StartArgs()...)
95+
96+
rr := &RunResult{}
97+
var startingFunc = func() error {
98+
rr, err = Run(t, exec.CommandContext(ctx, tf.Name(), args...))
99+
return err
100+
}
101+
102+
// if the test expect failure the function is different
103+
if (tc.expectstartupfailure) {
104+
startingFunc = func() error {
105+
rr, err = Run(t, exec.CommandContext(ctx, tf.Name(), args...))
106+
stderr := rr.Stderr.String()
107+
if (! strings.Contains(stderr, tc.message)) {
108+
t.Fatalf("test failed as no message %s detected", tc.message)
109+
return err
110+
}
111+
return nil
112+
}
113+
}
114+
115+
err := retry.Expo(startingFunc, 1*time.Second, 30*time.Minute, 3)
116+
117+
// as the text expect failure there is no need to continue the flow
118+
if (tc.expectstartupfailure) {
119+
return
120+
}
121+
122+
if err != nil {
123+
t.Fatalf("release start failed: %v", err)
124+
}
125+
126+
// grab the Stdout for checking...
127+
ss := rr.Stdout.String()
128+
129+
// fail if it does not contain the string
130+
if (! strings.Contains(ss, tc.message)) {
131+
t.Fatalf("test failed as no message %s detected", tc.message)
132+
}
133+
134+
// stop minikube
135+
rr, err = Run(t, exec.CommandContext(ctx, tf.Name(), "stop", "-p", profile))
136+
if err != nil {
137+
t.Fatalf("%s failed: %v", rr.Args, err)
138+
}
139+
})
140+
}
141+
})
142+
143+
}
38144
// TestVersionUpgrade downloads latest version of minikube and runs with
39145
// the odlest supported k8s version and then runs the current head minikube
40146
// and it tries to upgrade from the older supported k8s to news supported k8s

0 commit comments

Comments
 (0)