Skip to content

Commit

Permalink
Merge pull request #2000 from hirsim/enable-cache-image-for-windows
Browse files Browse the repository at this point in the history
Enable cache image for Windows
  • Loading branch information
r2d4 authored Oct 3, 2017
2 parents b71446b + 7f8f3bb commit 8e319bd
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 11 deletions.
68 changes: 57 additions & 11 deletions pkg/minikube/machine/cache_images.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package machine
import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
Expand All @@ -40,6 +41,8 @@ import (

const tempLoadDir = "/tmp"

var getWindowsVolumeName = getWindowsVolumeNameCmd

func CacheImagesForBootstrapper(version string, clusterBootstrapper string) error {
images := bootstrapper.GetCachedImageList(version, clusterBootstrapper)

Expand Down Expand Up @@ -97,17 +100,14 @@ func LoadImages(cmd bootstrapper.CommandRunner, images []string, cacheDir string

// # ParseReference cannot have a : in the directory path
func sanitizeCacheDir(image string) string {
if hasWindowsDriveLetter(image) {
if runtime.GOOS == "windows" && hasWindowsDriveLetter(image) {
// not sanitize Windows drive letter.
return image[:2] + strings.Replace(image[2:], ":", "_", -1)
}
return strings.Replace(image, ":", "_", -1)
}

func hasWindowsDriveLetter(s string) bool {
if runtime.GOOS != "windows" {
return false
}
if len(s) < 3 {
return false
}
Expand All @@ -122,6 +122,46 @@ func hasWindowsDriveLetter(s string) bool {
return false
}

// Replace a drive letter to a volume name.
func replaceWinDriveLetterToVolumeName(s string) (string, error) {
vname, err := getWindowsVolumeName(s[:1])
if err != nil {
return "", err
}
path := vname + s[3:]
if _, err := os.Stat(filepath.Dir(path)); err != nil {
return "", err
}

return path, nil
}

func getWindowsVolumeNameCmd(d string) (string, error) {
cmd := exec.Command("wmic", "volume", "where", "DriveLetter = '"+d+":'", "get", "DeviceID")

stdout, err := cmd.Output()
if err != nil {
return "", err
}

outs := strings.Split(strings.Replace(string(stdout), "\r", "", -1), "\n")

var vname string
for _, l := range outs {
s := strings.TrimSpace(l)
if strings.HasPrefix(s, `\\?\Volume{`) && strings.HasSuffix(s, `}\`) {
vname = s
break
}
}

if vname == "" {
return "", errors.New("failed to get a volume GUID")
}

return vname, nil
}

func LoadFromCacheBlocking(cmd bootstrapper.CommandRunner, src string) error {
glog.Infoln("Loading image from cache at ", src)
filename := filepath.Base(src)
Expand Down Expand Up @@ -162,6 +202,19 @@ func getSrcRef(image string) (types.ImageReference, error) {
}

func getDstRef(image, dst string) (types.ImageReference, error) {
if runtime.GOOS == "windows" && hasWindowsDriveLetter(dst) {
// ParseReference does not support a Windows drive letter.
// Therefore, will replace the drive letter to a volume name.
var err error
if dst, err = replaceWinDriveLetterToVolumeName(dst); err != nil {
return nil, errors.Wrap(err, "parsing docker archive dst ref: replace a Win drive letter to a volume name")
}
}

return _getDstRef(image, dst)
}

func _getDstRef(image, dst string) (types.ImageReference, error) {
dstRef, err := archive.ParseReference(dst + ":" + image)
if err != nil {
return nil, errors.Wrap(err, "parsing docker archive dst ref")
Expand All @@ -179,13 +232,6 @@ func CacheImage(image, dst string) error {
return errors.Wrapf(err, "making cache image directory: %s", dst)
}

// TODO: support Windows drive letter.
// L:164 ParseReference does not support Windows drive letter.
// If contains Windows drive letter, it disable cache image for now.
if hasWindowsDriveLetter(dst) {
return nil
}

srcRef, err := getSrcRef(image)
if err != nil {
return errors.Wrap(err, "creating docker image src ref")
Expand Down
70 changes: 70 additions & 0 deletions pkg/minikube/machine/cache_images_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ limitations under the License.
package machine

import (
"io/ioutil"
"os"
"runtime"
"strings"
"testing"

"k8s.io/minikube/pkg/minikube/constants"
Expand All @@ -29,3 +33,69 @@ func TestGetSrcRef(t *testing.T) {
}
}
}

func TestGetDstRef(t *testing.T) {
paths := []struct {
path, separator string
}{
{`/Users/foo/.minikube/cache/images`, `/`},
{`/home/foo/.minikube/cache/images`, `/`},
{`\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee}\Users\foo\.minikube\cache\images`, `\`},
{`\\?\Volume{aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee}\minikube\.minikube\cache\images`, `\`},
}

cases := []struct {
image, dst string
}{}
for _, tp := range paths {
for _, image := range constants.LocalkubeCachedImages {
dst := strings.Join([]string{tp.path, strings.Replace(image, ":", "_", -1)}, tp.separator)
cases = append(cases, struct{ image, dst string }{image, dst})
}
}

for _, tc := range cases {
if _, err := _getDstRef(tc.image, tc.dst); err != nil {
t.Errorf("Error getting dst ref for %s: %s", tc.dst, err)
}
}
}

func TestReplaceWinDriveLetterToVolumeName(t *testing.T) {
path, err := ioutil.TempDir("", "repwindl2vn")
if err != nil {
t.Fatalf("Error make tmp directory: %s", err)
}
defer os.RemoveAll(path)

if runtime.GOOS != "windows" {
// Replace to fake func.
getWindowsVolumeName = func(d string) (string, error) {
return `/`, nil
}
// Add dummy Windows drive letter.
path = `C:` + path
}

if _, err := replaceWinDriveLetterToVolumeName(path); err != nil {
t.Errorf("Error replace a Windows drive letter to a volume name: %s", err)
}
}

func TestHasWindowsDriveLetter(t *testing.T) {
cases := []struct {
path string
want bool
}{
{`C:\Users\Foo\.minikube`, true},
{`D:\minikube\.minikube`, true},
{`C\Foo\Bar\.minikube`, false},
{`/home/foo/.minikube`, false},
}

for _, tc := range cases {
if hasWindowsDriveLetter(tc.path) != tc.want {
t.Errorf("%s have a Windows drive letter: %t", tc.path, tc.want)
}
}
}

0 comments on commit 8e319bd

Please sign in to comment.