Skip to content

Commit

Permalink
Only copy new or modified files into VM on restart
Browse files Browse the repository at this point in the history
When minikube restarts, we can save time by only copying over files that
have changes or don't exist in the VM. The code in this PR first checks
if the file already exists in the VM, and skips copying it over again if
it does.
  • Loading branch information
Priya Wadhwa committed Nov 7, 2019
1 parent 79fcf71 commit e958862
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 1 deletion.
17 changes: 17 additions & 0 deletions pkg/minikube/assets/vm_assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"io"
"os"
"path"
"time"

"github.com/golang/glog"
"github.com/pkg/errors"
Expand All @@ -36,6 +37,7 @@ type CopyableFile interface {
GetTargetDir() string
GetTargetName() string
GetPermissions() string
GetModTime() time.Time
}

// BaseAsset is the base asset class
Expand All @@ -44,6 +46,7 @@ type BaseAsset struct {
TargetDir string
TargetName string
Permissions string
ModTime time.Time
}

// GetAssetName returns asset name
Expand All @@ -66,6 +69,11 @@ func (b *BaseAsset) GetPermissions() string {
return b.Permissions
}

// GetModTime returns mod time
func (b *BaseAsset) GetModTime() time.Time {
return b.ModTime
}

// FileAsset is an asset using a file
type FileAsset struct {
BaseAsset
Expand Down Expand Up @@ -104,6 +112,15 @@ func (f *FileAsset) GetLength() (flen int) {
return int(fi.Size())
}

// GetModTime returns modification timeof the file
func (f *FileAsset) GetModTime() time.Time {
fi, err := os.Stat(f.AssetName)
if err != nil {
return time.Time{}
}
return fi.ModTime()
}

func (f *FileAsset) Read(p []byte) (int, error) {
if f.reader == nil {
return 0, errors.New("Error attempting FileAsset.Read, FileAsset.reader uninitialized")
Expand Down
51 changes: 50 additions & 1 deletion pkg/minikube/command/ssh_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"io"
"os/exec"
"path"
"strconv"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -143,6 +145,11 @@ func (s *SSHRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) {

// Copy copies a file to the remote over SSH.
func (s *SSHRunner) Copy(f assets.CopyableFile) error {
dst := path.Join(path.Join(f.GetTargetDir(), f.GetTargetName()))
if s.fileExistsRemotely(f, dst) {
glog.Infof("Skipping copying %s as it already exists", f.GetAssetName())
}

sess, err := s.c.NewSession()
if err != nil {
return errors.Wrap(err, "NewSession")
Expand All @@ -156,7 +163,6 @@ func (s *SSHRunner) Copy(f assets.CopyableFile) error {
// StdinPipe is closed. But let's use errgroup to make it explicit.
var g errgroup.Group
var copied int64
dst := path.Join(path.Join(f.GetTargetDir(), f.GetTargetName()))
glog.Infof("Transferring %d bytes to %s", f.GetLength(), dst)

g.Go(func() error {
Expand Down Expand Up @@ -189,6 +195,49 @@ func (s *SSHRunner) Copy(f assets.CopyableFile) error {
return g.Wait()
}

func (s *SSHRunner) fileExistsRemotely(f assets.CopyableFile, dst string) bool {
sess, err := s.c.NewSession()
if err != nil {
return false
}

// check if sizes of the two files are the same
srcSize := f.GetLength()
size := fmt.Sprintf("ls -l %s | cut -d \" \" -f5", dst)
out, err := sess.CombinedOutput(size)
if err != nil {
return false
}
dstSize, err := strconv.Atoi(strings.Trim(string(out), "\n"))
if err != nil {
return false
}
if srcSize != dstSize {
return false
}

sess, err = s.c.NewSession()
if err != nil {
return false
}
// ensure src file hasn't been modified since dst was copied over
srcModTime := f.GetModTime()
stat := "stat -c %Y" + fmt.Sprintf(" %s", dst)
out, err = sess.CombinedOutput(stat)
if err != nil {
return false
}
unix, err := strconv.Atoi(strings.Trim(string(out), "\n"))
if err != nil {
return false
}
dstModTime := time.Unix(int64(unix), 0)
if err != nil {
return false
}
return srcModTime.Before(dstModTime)
}

// teePrefix copies bytes from a reader to writer, logging each new line.
func teePrefix(prefix string, r io.Reader, w io.Writer, logger func(format string, args ...interface{})) error {
scanner := bufio.NewScanner(r)
Expand Down

0 comments on commit e958862

Please sign in to comment.