diff --git a/pkg/minikube/registry/drvs/hyperkit/hyperkit.go b/pkg/minikube/registry/drvs/hyperkit/hyperkit.go index 96933137da9a..f6be3f789a4f 100644 --- a/pkg/minikube/registry/drvs/hyperkit/hyperkit.go +++ b/pkg/minikube/registry/drvs/hyperkit/hyperkit.go @@ -22,6 +22,9 @@ import ( "fmt" "os/exec" "strings" + "time" + + "github.com/pkg/errors" "github.com/docker/machine/libmachine/drivers" "github.com/pborman/uuid" @@ -37,6 +40,11 @@ const ( docURL = "https://minikube.sigs.k8s.io/docs/reference/drivers/hyperkit/" ) +var ( + // minimumVersion is used by hyperkit versionCheck(whether user's hyperkit is older than this) + minimumVersion = "0.20190201" +) + func init() { if err := registry.Register(registry.DriverDef{ Name: driver.HyperKit, @@ -85,5 +93,63 @@ func status() registry.State { return registry.State{Installed: true, Error: fmt.Errorf("%s failed:\n%s", strings.Join(cmd.Args, " "), out), Fix: "Run 'brew install hyperkit'", Doc: docURL} } + // Split version from v0.YYYYMMDD-HH-xxxxxxx or 0.YYYYMMDD to YYYYMMDD + currentVersion := convertVersionToDate(string(out)) + specificVersion := splitHyperKitVersion(minimumVersion) + // If current hyperkit is not newer than minimumVersion, suggest upgrade information + isNew, err := isNewerVersion(currentVersion, specificVersion) + if err != nil { + return registry.State{Installed: true, Healthy: true, Error: fmt.Errorf("hyperkit version check failed:\n%v", err), Doc: docURL} + } + if !isNew { + return registry.State{Installed: true, Healthy: true, Error: fmt.Errorf("the installed hyperkit version (0.%s) is older than the minimum recommended version (%s)", currentVersion, minimumVersion), Fix: "Run 'brew upgrade hyperkit'", Doc: docURL} + } + return registry.State{Installed: true, Healthy: true} } + +// isNewerVersion checks whether current hyperkit is newer than specific version +func isNewerVersion(currentVersion string, specificVersion string) (bool, error) { + // Convert hyperkit version to time.Date to compare whether hyperkit version is not old. + layout := "20060102" + currentVersionDate, err := time.Parse(layout, currentVersion) + if err != nil { + return false, errors.Wrap(err, "parse date") + } + specificVersionDate, err := time.Parse(layout, specificVersion) + if err != nil { + return false, errors.Wrap(err, "parse date") + } + // If currentVersionDate is equal to specificVersionDate, no need to upgrade hyperkit + if currentVersionDate.Equal(specificVersionDate) { + return true, nil + } + // If currentVersionDate is after specificVersionDate, return true + return currentVersionDate.After(specificVersionDate), nil +} + +// convertVersionToDate returns current hyperkit version +// hyperkit returns version info with two patterns(v0.YYYYMMDD-HH-xxxxxxx or 0.YYYYMMDD) +// convertVersionToDate splits version to YYYYMMDD format +func convertVersionToDate(hyperKitVersionOutput string) string { + // `hyperkit -v` returns version info with multi line. + // Cut off like "hyperkit: v0.20190201-11-gc0dd46" or "hyperkit: 0.20190201" + versionLine := strings.Split(hyperKitVersionOutput, "\n")[0] + // Cut off like "v0.20190201-11-gc0dd46" or "0.20190201" + version := strings.Split(versionLine, ": ")[1] + // Format to "YYYYMMDD" + return splitHyperKitVersion(version) +} + +// splitHyperKitVersion splits version from v0.YYYYMMDD-HH-xxxxxxx or 0.YYYYMMDD to YYYYMMDD +func splitHyperKitVersion(version string) string { + if strings.Contains(version, ".") { + // Cut off like "20190201-11-gc0dd46" or "20190201" + version = strings.Split(version, ".")[1] + } + if len(version) >= 8 { + // Format to "20190201" + version = version[:8] + } + return version +} diff --git a/pkg/minikube/registry/drvs/hyperkit/hyperkit_test.go b/pkg/minikube/registry/drvs/hyperkit/hyperkit_test.go new file mode 100644 index 000000000000..51cc3415ff46 --- /dev/null +++ b/pkg/minikube/registry/drvs/hyperkit/hyperkit_test.go @@ -0,0 +1,128 @@ +/* +Copyright 2019 The Kubernetes Authors All rights reserved. + +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 hyperkit + +import ( + "testing" +) + +var ( + versionOutputType1 = "hyperkit: v0.20190201-11-gc0dd46\n\nHomepage: https://github.com/docker/hyperkit\nLicense: BSD\n\n" + versionOutputType2 = "hyperkit: 0.20190201\n\nHomepage: https://github.com/docker/hyperkit\nLicense: BSD\n\n" +) + +func TestSplitHyperKitVersion(t *testing.T) { + var tc = []struct { + desc, version, expect string + }{ + { + desc: "split type1 output to YYYYMMDD format", + version: "v.20190201-gc0dd46", + expect: "20190201", + }, + { + desc: "split type2 output to YYYYMMDD format", + version: "0.20190201", + expect: "20190201", + }, + { + desc: "non split YYYYMMDD output to YYYYMMDD format", + version: "20190201", + expect: "20190201", + }, + { + desc: "split semver output to YYYYMMDD format", + version: "v1.0.0", + expect: "0", + }, + } + for _, test := range tc { + t.Run(test.desc, func(t *testing.T) { + version := splitHyperKitVersion(test.version) + if version != test.expect { + t.Fatalf("Error %v expected but result %v", test.expect, version) + } + }) + } +} + +func TestConvertVersionToDate(t *testing.T) { + var tc = []struct { + desc, versionOutput, expect string + }{ + { + desc: "split type1 output to YYYYMMDD format", + versionOutput: versionOutputType1, + expect: "20190201", + }, + { + desc: "split type2 output to YYYYMMDD format", + versionOutput: versionOutputType2, + expect: "20190201", + }, + { + desc: "split semver output to YYYYMMDD format", + versionOutput: "hyperkit: v1.0.0\n\nHomepage: https://github.com/docker/hyperkit\nLicense: BSD\n\n", + expect: "0", + }, + } + for _, test := range tc { + t.Run(test.desc, func(t *testing.T) { + version := convertVersionToDate(test.versionOutput) + if version != test.expect { + t.Fatalf("Error %v expected but result %v", test.expect, version) + } + }) + } +} + +func TestIsNewerVersion(t *testing.T) { + var tc = []struct { + desc, currentVersion, specificVersion string + isNew bool + }{ + { + desc: "version check newer", + currentVersion: "29991231", + specificVersion: "20190802", + isNew: true, + }, + { + desc: "version check equal", + currentVersion: "20190802", + specificVersion: "20190802", + isNew: true, + }, + { + desc: "version check older", + currentVersion: "20190201", + specificVersion: "20190802", + isNew: false, + }, + } + for _, test := range tc { + t.Run(test.desc, func(t *testing.T) { + isNew, err := isNewerVersion(test.currentVersion, test.specificVersion) + if err != nil { + t.Fatalf("Got unexpected error %v", err) + } + if isNew != test.isNew { + t.Fatalf("Error %v expected but result %v", test.isNew, isNew) + } + }) + } +}