Skip to content

Commit 460a517

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 eea2348 commit 460a517

File tree

3 files changed

+181
-7
lines changed

3 files changed

+181
-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.
@@ -1079,15 +1079,20 @@ func tryRegistry(r command.Runner) {
10791079

10801080
// getKubernetesVersion ensures that the requested version is reasonable
10811081
func getKubernetesVersion(old *cfg.Config) (string, bool) {
1082-
rawVersion := viper.GetString(kubernetesVersion)
1082+
paramVersion := viper.GetString(kubernetesVersion)
10831083
isUpgrade := false
1084-
if rawVersion == "" {
1085-
rawVersion = constants.DefaultKubernetesVersion
1084+
1085+
if paramVersion == "" { // if the user did not specify any version then ...
1086+
if old != nil { // .. use the old version from config
1087+
paramVersion = old.KubernetesConfig.KubernetesVersion
1088+
} else { // .. otherwise use the default version
1089+
paramVersion = constants.DefaultKubernetesVersion
1090+
}
10861091
}
10871092

1088-
nvs, err := semver.Make(strings.TrimPrefix(rawVersion, version.VersionPrefix))
1093+
nvs, err := semver.Make(strings.TrimPrefix(paramVersion, version.VersionPrefix))
10891094
if err != nil {
1090-
exit.WithCodeT(exit.Data, `Unable to parse "{{.kubernetes_version}}": {{.error}}`, out.V{"kubernetes_version": rawVersion, "error": err})
1095+
exit.WithCodeT(exit.Data, `Unable to parse "{{.kubernetes_version}}": {{.error}}`, out.V{"kubernetes_version": paramVersion, "error": err})
10911096
}
10921097
nv := version.VersionPrefix + nvs.String()
10931098

@@ -1099,6 +1104,10 @@ func getKubernetesVersion(old *cfg.Config) (string, bool) {
10991104
if err != nil {
11001105
exit.WithCodeT(exit.Data, "Unable to parse oldest Kubernetes version from constants: {{.error}}", out.V{"error": err})
11011106
}
1107+
defaultVersion, err := semver.Make(strings.TrimPrefix(constants.DefaultKubernetesVersion, version.VersionPrefix))
1108+
if err != nil {
1109+
exit.WithCodeT(exit.Data, "Unable to parse default Kubernetes version from constants: {{.error}}", out.V{"error": err})
1110+
}
11021111

11031112
if nvs.LT(oldestVersion) {
11041113
out.WarningT("Specified Kubernetes version {{.specified}} is less than the oldest supported version: {{.oldest}}", out.V{"specified": nvs, "oldest": constants.OldestKubernetesVersion})
@@ -1127,8 +1136,11 @@ func getKubernetesVersion(old *cfg.Config) (string, bool) {
11271136
* 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})
11281137

11291138
}
1139+
if defaultVersion.GT(nvs) {
1140+
out.T(out.ThumbsUp, "Kubernetes {{.new}} is now available. If you would like to upgrade, specify: --kubernetes-version={{.new}}", out.V{"new": defaultVersion})
1141+
}
1142+
11301143
if nvs.GT(ovs) {
1131-
out.T(out.ThumbsUp, "Upgrading from Kubernetes {{.old}} to {{.new}}", out.V{"old": ovs, "new": nvs})
11321144
isUpgrade = true
11331145
}
11341146
return nv, isUpgrade

cmd/minikube/cmd/start_test.go

+55
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,64 @@ import (
2222

2323
"github.com/spf13/cobra"
2424
"github.com/spf13/viper"
25+
cfg "k8s.io/minikube/pkg/minikube/config"
2526
"k8s.io/minikube/pkg/minikube/constants"
2627
)
2728

29+
func TestGetKuberneterVersion(t *testing.T) {
30+
var tests = []struct {
31+
description string
32+
expectedVersion string
33+
paramVersion string
34+
upgrade bool
35+
cfg *cfg.Config
36+
}{
37+
{
38+
description: "kubernetes-version not given, no config",
39+
expectedVersion: constants.DefaultKubernetesVersion,
40+
paramVersion: "",
41+
upgrade: false,
42+
},
43+
{
44+
description: "kubernetes-version not given, config available",
45+
expectedVersion: "v1.15.0",
46+
paramVersion: "",
47+
upgrade: false,
48+
cfg: &cfg.Config{KubernetesConfig: cfg.KubernetesConfig{KubernetesVersion: "v1.15.0"}},
49+
},
50+
{
51+
description: "kubernetes-version given, no config",
52+
expectedVersion: "v1.15.0",
53+
paramVersion: "v1.15.0",
54+
upgrade: false,
55+
},
56+
{
57+
description: "kubernetes-version given, config available",
58+
expectedVersion: "v1.16.0",
59+
paramVersion: "v1.16.0",
60+
upgrade: true,
61+
cfg: &cfg.Config{KubernetesConfig: cfg.KubernetesConfig{KubernetesVersion: "v1.15.0"}},
62+
},
63+
}
64+
65+
for _, test := range tests {
66+
t.Run(test.description, func(t *testing.T) {
67+
viper.SetDefault(kubernetesVersion, test.paramVersion)
68+
version, upgrade := getKubernetesVersion(test.cfg)
69+
70+
// check whether we are getting the expected version
71+
if version != test.expectedVersion {
72+
t.Fatalf("test failed because the expected version %s is not returned", test.expectedVersion)
73+
}
74+
75+
// check whether the upgrade flag is correct
76+
if test.upgrade != upgrade {
77+
t.Fatalf("test failed expected upgrade is %t", test.upgrade)
78+
}
79+
})
80+
}
81+
}
82+
2883
func TestGenerateCfgFromFlagsHTTPProxyHandling(t *testing.T) {
2984
viper.SetDefault(memory, defaultMemorySize)
3085
viper.SetDefault(humanReadableDiskSize, defaultDiskSize)

test/integration/version_upgrade_test.go

+107
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,113 @@ 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+
}
144+
38145
// TestVersionUpgrade downloads latest version of minikube and runs with
39146
// the odlest supported k8s version and then runs the current head minikube
40147
// and it tries to upgrade from the older supported k8s to news supported k8s

0 commit comments

Comments
 (0)