Skip to content

Commit

Permalink
Fix --kubernetes-version for upgrading cluster to correct version
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nanikjava committed Nov 12, 2019
1 parent e611b38 commit 30499dc
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 7 deletions.
26 changes: 19 additions & 7 deletions cmd/minikube/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func initMinikubeFlags() {

// initKubernetesFlags inits the commandline flags for kubernetes related options
func initKubernetesFlags() {
startCmd.Flags().String(kubernetesVersion, constants.DefaultKubernetesVersion, "The kubernetes version that the minikube VM will use (ex: v1.2.3)")
startCmd.Flags().String(kubernetesVersion, "", "The kubernetes version that the minikube VM will use (ex: v1.2.3)")
startCmd.Flags().Var(&extraOptions, "extra-config",
`A set of key=value pairs that describe configuration that may be passed to different components.
The key should be '.' separated, and the first part before the dot is the component to apply the configuration to.
Expand Down Expand Up @@ -1110,15 +1110,20 @@ func tryRegistry(r command.Runner) {

// getKubernetesVersion ensures that the requested version is reasonable
func getKubernetesVersion(old *cfg.MachineConfig) (string, bool) {
rawVersion := viper.GetString(kubernetesVersion)
paramVersion := viper.GetString(kubernetesVersion)
isUpgrade := false
if rawVersion == "" {
rawVersion = constants.DefaultKubernetesVersion

if paramVersion == "" { // if the user did not specify any version then ...
if old != nil { // .. use the old version from config
paramVersion = old.KubernetesConfig.KubernetesVersion
} else { // .. otherwise use the default version
paramVersion = constants.DefaultKubernetesVersion
}
}

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

Expand All @@ -1130,6 +1135,10 @@ func getKubernetesVersion(old *cfg.MachineConfig) (string, bool) {
if err != nil {
exit.WithCodeT(exit.Data, "Unable to parse oldest Kubernetes version from constants: {{.error}}", out.V{"error": err})
}
defaultVersion, err := semver.Make(strings.TrimPrefix(constants.DefaultKubernetesVersion, version.VersionPrefix))
if err != nil {
exit.WithCodeT(exit.Data, "Unable to parse default Kubernetes version from constants: {{.error}}", out.V{"error": err})
}

if nvs.LT(oldestVersion) {
out.WarningT("Specified Kubernetes version {{.specified}} is less than the oldest supported version: {{.oldest}}", out.V{"specified": nvs, "oldest": constants.OldestKubernetesVersion})
Expand Down Expand Up @@ -1158,8 +1167,11 @@ func getKubernetesVersion(old *cfg.MachineConfig) (string, bool) {
* 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})

}
if defaultVersion.GT(nvs) {
out.T(out.ThumbsUp, "Kubernetes {{.new}} is now available. If you would like to upgrade, specify: --kubernetes-version={{.new}}", out.V{"new": defaultVersion})
}

if nvs.GT(ovs) {
out.T(out.ThumbsUp, "Upgrading from Kubernetes {{.old}} to {{.new}}", out.V{"old": ovs, "new": nvs})
isUpgrade = true
}
return nv, isUpgrade
Expand Down
55 changes: 55 additions & 0 deletions cmd/minikube/cmd/start_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,64 @@ import (

"github.com/spf13/cobra"
"github.com/spf13/viper"
cfg "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/constants"
)

func TestGetKuberneterVersion(t *testing.T) {
var tests = []struct {
description string
expectedVersion string
paramVersion string
upgrade bool
cfg *cfg.MachineConfig
}{
{
description: "kubernetes-version not given, no config",
expectedVersion: constants.DefaultKubernetesVersion,
paramVersion: "",
upgrade: false,
},
{
description: "kubernetes-version not given, config available",
expectedVersion: "v1.15.0",
paramVersion: "",
upgrade: false,
cfg: &cfg.MachineConfig{KubernetesConfig: cfg.KubernetesConfig{KubernetesVersion: "v1.15.0"}},
},
{
description: "kubernetes-version given, no config",
expectedVersion: "v1.15.0",
paramVersion: "v1.15.0",
upgrade: false,
},
{
description: "kubernetes-version given, config available",
expectedVersion: "v1.16.0",
paramVersion: "v1.16.0",
upgrade: true,
cfg: &cfg.MachineConfig{KubernetesConfig: cfg.KubernetesConfig{KubernetesVersion: "v1.15.0"}},
},
}

for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
viper.SetDefault(kubernetesVersion, test.paramVersion)
version, upgrade := getKubernetesVersion(test.cfg)

// check whether we are getting the expected version
if version != test.expectedVersion {
t.Fatalf("test failed because the expected version %s is not returned", test.expectedVersion)
}

// check whether the upgrade flag is correct
if test.upgrade != upgrade {
t.Fatalf("test failed expected upgrade is %t", test.upgrade)
}
})
}
}

func TestGenerateCfgFromFlagsHTTPProxyHandling(t *testing.T) {
viper.SetDefault(memory, defaultMemorySize)
viper.SetDefault(humanReadableDiskSize, defaultDiskSize)
Expand Down
107 changes: 107 additions & 0 deletions test/integration/version_upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,113 @@ import (
pkgutil "k8s.io/minikube/pkg/util"
)

// TestDifferentVersionUpgrade test installation of different version of Kubernetes.
// Start with installing from v1.13.0 then to v1.15.0 and then try to downgrade it
// to v1.13.0
// Following are the test cases that the function is doing
// 1. Start with specific version
// ./out/minikube start -p vupgrade --kubernetes-version=v1.13.0
// 2. Upgrade with newer version
// ./out/minikube start -p vupgrade --kubernetes-version=v1.13.0
// 3. Downgrade with older version (this will spit out error and test the error message)
// ./out/minikube start -p vupgrade --kubernetes-version=v1.13.0
func TestDifferentVersionUpgrade(t *testing.T) {
MaybeParallel(t)
WaitForStartSlot(t)
profile := UniqueProfileName("testkubernetestversions")

ctx, cancel := context.WithTimeout(context.Background(), 55*time.Minute)
defer CleanupWithLogs(t, profile, cancel)

// create a temporary file
tf, err := ioutil.TempFile("", "minikube-release.*.exe")
if err != nil {
t.Fatalf("tempfile: %v", err)
}
defer os.Remove(tf.Name())
tf.Close()

// download minikube
url := pkgutil.GetBinaryDownloadURL("latest", runtime.GOOS)

// chmod the binary (when Linux)
if runtime.GOOS != "windows" {
if err := os.Chmod(tf.Name(), 0700); err != nil {
t.Errorf("chmod: %v", err)
}
}

// download the file
if err := retry.Expo(func() error { return getter.GetFile(tf.Name(), url) }, 3*time.Second, 3*time.Minute); err != nil {
t.Fatalf("get failed: %v", err)
}

t.Run("parallel", func(t *testing.T) {
tests := []struct {
name string
message string
kubernetesversion string
expectstartupfailure bool
}{
{"fresh install kubernetes v1.13.0", "v1.13.0", "v1.13.0", false},
{"upgrade kubernetes to v 1.14.0", "v1.14.0", "v1.14.0", false},
{"downgrade kubernetes to v 1.12.0", "Non-destructive downgrades are not supported", "v1.12.0", true},
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
// instruct to install kubernetes v1.13.0
args := append([]string{"start", "-p", profile, fmt.Sprintf("--kubernetes-version=%s", tc.kubernetesversion), "--alsologtostderr", "-v=1"}, StartArgs()...)

rr := &RunResult{}
var startingFunc = func() error {
rr, err = Run(t, exec.CommandContext(ctx, tf.Name(), args...))
return err
}

// if the test expect failure the function is different
if tc.expectstartupfailure {
startingFunc = func() error {
rr, err = Run(t, exec.CommandContext(ctx, tf.Name(), args...))
stderr := rr.Stderr.String()
if !strings.Contains(stderr, tc.message) {
t.Fatalf("test failed as no message %s detected", tc.message)
return err
}
return nil
}
}

err := retry.Expo(startingFunc, 1*time.Second, 30*time.Minute, 3)

// as the text expect failure there is no need to continue the flow
if tc.expectstartupfailure {
return
}

if err != nil {
t.Fatalf("release start failed: %v", err)
}

// grab the Stdout for checking...
ss := rr.Stdout.String()

// fail if it does not contain the string
if !strings.Contains(ss, tc.message) {
t.Fatalf("test failed as no message %s detected", tc.message)
}

// stop minikube
rr, err = Run(t, exec.CommandContext(ctx, tf.Name(), "stop", "-p", profile))
if err != nil {
t.Fatalf("%s failed: %v", rr.Args, err)
}
})
}
})

}

// TestVersionUpgrade downloads latest version of minikube and runs with
// the odlest supported k8s version and then runs the current head minikube
// and it tries to upgrade from the older supported k8s to news supported k8s
Expand Down

0 comments on commit 30499dc

Please sign in to comment.