Merge branch 'master' into less-catastrophic

pull/6121/head
tstromberg 2019-12-19 16:15:47 -08:00
commit f438bb7ec8
11 changed files with 376 additions and 60 deletions

View File

@ -304,10 +304,8 @@ func runStart(cmd *cobra.Command, args []string) {
validateFlags(cmd, driverName)
validateUser(driverName)
// No need to install a driver in download-only mode
if !viper.GetBool(downloadOnly) {
updateDriver(driverName)
}
// Download & update the driver, even in --download-only mode
updateDriver(driverName)
k8sVersion, isUpgrade := getKubernetesVersion(existing)
config, err := generateCfgFromFlags(cmd, k8sVersion, driverName)
@ -1061,7 +1059,7 @@ func validateNetwork(h *host.Host, r command.Runner) string {
}
func trySSH(h *host.Host, ip string) {
sshAddr := fmt.Sprintf("%s:22", ip)
sshAddr := net.JoinHostPort(ip, "22")
dial := func() (err error) {
d := net.Dialer{Timeout: 3 * time.Second}

View File

@ -17,6 +17,8 @@ limitations under the License.
package cmd
import (
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/minikube/pkg/minikube/cluster"
@ -45,7 +47,13 @@ var updateContextCmd = &cobra.Command{
if err != nil {
exit.WithError("Error host driver ip status", err)
}
updated, err := kubeconfig.UpdateIP(ip, machineName, constants.KubeconfigPath)
updated := false
kubeConfigPath := os.Getenv("KUBECONFIG")
if kubeConfigPath == "" {
updated, err = kubeconfig.UpdateIP(ip, machineName, constants.KubeconfigPath)
} else {
updated, err = kubeconfig.UpdateIP(ip, machineName, kubeConfigPath)
}
if err != nil {
exit.WithError("update config", err)
}

View File

@ -79,7 +79,7 @@ spec:
hostNetwork: true
containers:
- name: minikube-ingress-dns
image: "cryptexlabs/minikube-ingress-dns:0.2.0"
image: "cryptexlabs/minikube-ingress-dns:0.2.1"
imagePullPolicy: IfNotPresent
ports:
- containerPort: 53

View File

@ -17,6 +17,7 @@ limitations under the License.
package machine
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
@ -109,34 +110,41 @@ func LoadImages(cc *config.MachineConfig, runner command.Runner, images []string
for _, image := range images {
image := image
g.Go(func() error {
ref, err := name.ParseReference(image, name.WeakValidation)
if err != nil {
return errors.Wrap(err, "image name reference")
}
img, err := retrieveImage(ref)
if err != nil {
return errors.Wrap(err, "fetching image")
}
cf, err := img.ConfigName()
hash := cf.Hex
if err != nil {
glog.Infof("error retrieving image manifest for %s to check if it already exists: %v", image, err)
} else if cr.ImageExists(image, hash) {
glog.Infof("skipping re-loading image %q because sha %q already exists ", image, hash)
err := needsTransfer(image, cr)
if err == nil {
return nil
}
if err := transferAndLoadImage(runner, cc.KubernetesConfig, image, cacheDir); err != nil {
glog.Warningf("Failed to load %s: %v", image, err)
return errors.Wrapf(err, "loading image %s", image)
}
return nil
glog.Infof("%q needs transfer: %v", image, err)
return transferAndLoadImage(runner, cc.KubernetesConfig, image, cacheDir)
})
}
if err := g.Wait(); err != nil {
return errors.Wrap(err, "loading cached images")
}
glog.Infoln("Successfully loaded all cached images.")
glog.Infoln("Successfully loaded all cached images")
return nil
}
// needsTransfer returns an error if an image needs to be retransfered
func needsTransfer(image string, cr cruntime.Manager) error {
ref, err := name.ParseReference(image, name.WeakValidation)
if err != nil {
return errors.Wrap(err, "parse ref")
}
img, err := retrieveImage(ref)
if err != nil {
return errors.Wrap(err, "retrieve")
}
cf, err := img.ConfigName()
if err != nil {
return errors.Wrap(err, "image hash")
}
if !cr.ImageExists(image, cf.Hex) {
return fmt.Errorf("%q does not exist at hash %q in container runtime", image, cf.Hex)
}
return nil
}
@ -277,7 +285,7 @@ func transferAndLoadImage(cr command.Runner, k8s config.KubernetesConfig, imgNam
return errors.Wrapf(err, "%s load %s", r.Name(), dst)
}
glog.Infof("Successfully loaded image %s from cache", src)
glog.Infof("Transferred and loaded %s from cache", src)
return nil
}
@ -362,7 +370,7 @@ func CacheImage(image, dst string) error {
img, err := retrieveImage(ref)
if err != nil {
return errors.Wrap(err, "fetching image")
glog.Warningf("unable to retrieve image: %v", err)
}
glog.Infoln("OPENING: ", dstPath)
@ -370,27 +378,31 @@ func CacheImage(image, dst string) error {
if err != nil {
return err
}
defer func() { // clean up temp files
err := os.Remove(f.Name())
if err != nil {
glog.Infof("Failed to clean up the temp file %s : %v", f.Name(), err)
defer func() {
// If we left behind a temp file, remove it.
_, err := os.Stat(f.Name())
if err == nil {
os.Remove(f.Name())
if err != nil {
glog.Warningf("Failed to clean up the temp file %s: %v", f.Name(), err)
}
}
}()
tag, err := name.NewTag(image, name.WeakValidation)
if err != nil {
return err
return errors.Wrap(err, "newtag")
}
err = tarball.Write(tag, img, &tarball.WriteOptions{}, f)
if err != nil {
return err
return errors.Wrap(err, "write")
}
err = f.Close()
if err != nil {
return err
return errors.Wrap(err, "close")
}
err = os.Rename(f.Name(), dstPath)
if err != nil {
return err
return errors.Wrap(err, "rename")
}
glog.Infof("%s exists", dst)
return nil
@ -400,18 +412,20 @@ func retrieveImage(ref name.Reference) (v1.Image, error) {
glog.Infof("retrieving image: %+v", ref)
img, err := daemon.Image(ref)
if err == nil {
glog.Infof("found %s locally; caching", ref.Name())
return img, err
glog.Infof("found %s locally: %+v", ref.Name(), img)
return img, nil
}
glog.Infof("daemon image for %+v: %v", img, err)
// reference does not exist in the local daemon
if err != nil {
glog.Infof("daemon lookup for %+v: %v", ref, err)
}
img, err = remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
if err == nil {
return img, err
return img, nil
}
glog.Warningf("failed authn download for %+v (trying anon): %+v", ref, err)
glog.Warningf("authn lookup for %+v (trying anon): %+v", ref, err)
img, err = remote.Image(ref)
if err != nil {
glog.Warningf("failed anon download for %+v: %+v", ref, err)
}
return img, err
}

View File

@ -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
}

View File

@ -0,0 +1,130 @@
// +build darwin
/*
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)
}
})
}
}

View File

@ -80,3 +80,22 @@ func TestConcatStrings(t *testing.T) {
}
}
}
func TestCalculateSizeInMB(t *testing.T) {
testData := []struct {
size string
expectedNumber int
}{
{"1024kb", 1},
{"1024KB", 1},
{"1024mb", 1024},
{"1024b", 0},
}
for _, tt := range testData {
number := CalculateSizeInMB(tt.size)
if number != tt.expectedNumber {
t.Fatalf("Expected '%d'' but got '%d'", tt.expectedNumber, number)
}
}
}

View File

@ -10,6 +10,9 @@ description: >
* Announce release intent on #minikube
* Pause merge requests so that they are not accidentally left out of the ISO or release notes
* Two minikube repos checked out locally:
* Your personal fork
* Upstream
## Build a new ISO
@ -34,29 +37,31 @@ Edit the minikube `Makefile`, updating the version number values at the top:
* `VERSION_MAJOR`, `VERSION_MINOR`, `VERSION_BUILD` as necessary
* `ISO_VERSION` - defaults to MAJOR.MINOR.0 - update if point release requires a new ISO to be built.
Make sure the integration tests run against this PR, once the new ISO is built.
Make sure the integration tests run against this PR, once the new ISO is built.
## Ad-Hoc testing of other platforms
If there are supported platforms which do not have functioning Jenkins workers (Windows), you may use the following to build a sanity check:
```shell
env BUILD_IN_DOCKER=y make cross checksum
```
## Send out Makefile PR
Once submitted, HEAD will use the new ISO. Please pay attention to test failures, as this is our integration test across platforms. If there are known acceptable failures, please add a PR comment linking to the appropriate issue.
You can merge this change at any time before the release, but often the Makefile change is merged the next step: Release notes.
## Update Release Notes
Run the following script to update the release notes:
Run the following script from your local upstream repo copy to generate updated release notes:
```shell
hack/release_notes.sh
```
Merge the output into CHANGELOG.md. See [PR#3175](https://github.com/kubernetes/minikube/pull/3175) as an example. Then get the PR submitted.
Paste the output into CHANGELOG.md. See [PR#3175](https://github.com/kubernetes/minikube/pull/3175) as an example.
You'll need to massage the output in a few key ways:
- The changelog should only contain user facing change. This means removing PR's for:
- Documentation
- Low-risk refactors
- Test-only changes
- Sort the changes so that the ones users will want to know about the most appear first
- Remove bots from the contributor list
- Remove duplicated similar names from the contributor list
Merge the output into CHANGELOG.md. See [PR#3175](https://github.com/kubernetes/minikube/pull/3175) as an example.
## Tag the Release

View File

@ -0,0 +1,62 @@
// +build integration
/*
Copyright 2016 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 integration
import (
"context"
"fmt"
"os"
"os/exec"
"testing"
"time"
)
func TestOffline(t *testing.T) {
t.Run("group", func(t *testing.T) {
for _, runtime := range []string{"docker", "crio", "containerd"} {
t.Run(runtime, func(t *testing.T) {
MaybeParallel(t)
WaitForStartSlot(t)
if runtime != "docker" && NoneDriver() {
t.Skipf("skipping %s - incompatible with none driver", t.Name())
}
profile := UniqueProfileName(fmt.Sprintf("offline-%s", runtime))
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
defer CleanupWithLogs(t, profile, cancel)
startArgs := []string{"start", "-p", profile, "--alsologtostderr", "-v=1", "--wait=true", "--container-runtime", runtime}
startArgs = append(startArgs, StartArgs()...)
c := exec.CommandContext(ctx, Target(), startArgs...)
env := os.Environ()
// RFC1918 address that unlikely to host working a proxy server
env = append(env, "HTTP_PROXY=172.16.1.1:1")
env = append(env, "HTTP_PROXYS=172.16.1.1:1")
env = append(env, "DOCKER_HOST=172.16.1.1:1")
c.Env = env
rr, err := Run(t, c)
if err != nil {
// Fatal so that we may collect logs before stop/delete steps
t.Fatalf("%s failed: %v", rr.Args, err)
}
})
}
})
}

View File

@ -103,6 +103,7 @@ func TestFunctional(t *testing.T) {
{"SSHCmd", validateSSHCmd},
{"MySQL", validateMySQL},
{"FileSync", validateFileSync},
{"UpdateContextCmd", validateUpdateContextCmd},
}
for _, tc := range tests {
tc := tc
@ -645,6 +646,19 @@ func validateFileSync(ctx context.Context, t *testing.T, profile string) {
}
}
// validateUpdateContextCmd asserts basic "update-context" command functionality
func validateUpdateContextCmd(ctx context.Context, t *testing.T, profile string) {
rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "update-context", "--alsologtostderr", "-v=2"))
if err != nil {
t.Errorf("%s failed: %v", rr.Args, err)
}
want := []byte("IP was already correctly configured")
if !bytes.Contains(rr.Stdout.Bytes(), want) {
t.Errorf("update-context: got=%q, want=*%q*", rr.Stdout.Bytes(), want)
}
}
// startHTTPProxy runs a local http proxy and sets the env vars for it.
func startHTTPProxy(t *testing.T) (*http.Server, error) {
port, err := freeport.GetFreePort()