Skip to content

Commit 892cfb8

Browse files
authored
Merge pull request #6385 from tstromberg/faster-docker
Batch file manipulation calls to reduce number of redundant commands
2 parents d8a5714 + 9aeae2d commit 892cfb8

File tree

12 files changed

+166
-59
lines changed

12 files changed

+166
-59
lines changed

pkg/minikube/bootstrapper/bsutil/binaries.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ limitations under the License.
1818
package bsutil
1919

2020
import (
21+
"os/exec"
2122
"path"
2223
"runtime"
2324

@@ -32,6 +33,12 @@ import (
3233

3334
// TransferBinaries transfers all required Kubernetes binaries
3435
func TransferBinaries(cfg config.KubernetesConfig, c command.Runner) error {
36+
dir := binRoot(cfg.KubernetesVersion)
37+
_, err := c.RunCmd(exec.Command("sudo", "mkdir", "-p", dir))
38+
if err != nil {
39+
return err
40+
}
41+
3542
var g errgroup.Group
3643
for _, name := range constants.KubernetesReleaseBinaries {
3744
name := name
@@ -41,7 +48,7 @@ func TransferBinaries(cfg config.KubernetesConfig, c command.Runner) error {
4148
return errors.Wrapf(err, "downloading %s", name)
4249
}
4350

44-
dst := path.Join(binRoot(cfg.KubernetesVersion), name)
51+
dst := path.Join(dir, name)
4552
if err := machine.CopyBinary(c, src, dst); err != nil {
4653
return errors.Wrapf(err, "copybinary %s -> %s", src, dst)
4754
}

pkg/minikube/bootstrapper/certs.go

+7-10
Original file line numberDiff line numberDiff line change
@@ -341,23 +341,20 @@ func configureCACerts(cr command.Runner, caCerts map[string]string) error {
341341
for _, caCertFile := range caCerts {
342342
dstFilename := path.Base(caCertFile)
343343
certStorePath := path.Join(SSLCertStoreDir, dstFilename)
344-
_, err := cr.RunCmd(exec.Command("sudo", "test", "-f", certStorePath))
345-
if err != nil {
346-
if _, err := cr.RunCmd(exec.Command("sudo", "ln", "-s", caCertFile, certStorePath)); err != nil {
347-
return errors.Wrapf(err, "create symlink for %s", caCertFile)
348-
}
344+
cmd := fmt.Sprintf("test -f %s || ln -s %s %s", caCertFile, certStorePath, caCertFile)
345+
if _, err := cr.RunCmd(exec.Command("sudo", "/bin/bash", "-c", cmd)); err != nil {
346+
return errors.Wrapf(err, "create symlink for %s", caCertFile)
349347
}
350348
if hasSSLBinary {
351349
subjectHash, err := getSubjectHash(cr, caCertFile)
352350
if err != nil {
353351
return errors.Wrapf(err, "calculate hash for cacert %s", caCertFile)
354352
}
355353
subjectHashLink := path.Join(SSLCertStoreDir, fmt.Sprintf("%s.0", subjectHash))
356-
_, err = cr.RunCmd(exec.Command("sudo", "test", "-f", subjectHashLink))
357-
if err != nil {
358-
if _, err := cr.RunCmd(exec.Command("sudo", "ln", "-s", certStorePath, subjectHashLink)); err != nil {
359-
return errors.Wrapf(err, "linking caCertFile %s", caCertFile)
360-
}
354+
355+
cmd := fmt.Sprintf("test -f %s || ln -s %s %s", subjectHashLink, certStorePath, subjectHashLink)
356+
if _, err := cr.RunCmd(exec.Command("sudo", "/bin/bash", "-c", cmd)); err != nil {
357+
return errors.Wrapf(err, "create symlink for %s", caCertFile)
361358
}
362359
}
363360
}

pkg/minikube/bootstrapper/certs_test.go

+4-15
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ limitations under the License.
1717
package bootstrapper
1818

1919
import (
20-
"fmt"
2120
"os"
22-
"path"
2321
"path/filepath"
2422
"testing"
2523

@@ -53,21 +51,12 @@ func TestSetupCerts(t *testing.T) {
5351
t.Fatalf("error generating certificate: %v", err)
5452
}
5553

56-
cmdMap := map[string]string{
57-
"sudo mkdir -p /var/lib/minikube/certs": "",
58-
}
59-
certFilenames := map[string]string{"ca.crt": "minikubeCA.pem", "mycert.pem": "mycert.pem"}
60-
for _, dst := range certFilenames {
61-
certFile := path.Join(CACertificatesDir, dst)
62-
certStorePath := path.Join(SSLCertStoreDir, dst)
63-
certNameHash := "abcdef"
64-
remoteCertHashLink := path.Join(SSLCertStoreDir, fmt.Sprintf("%s.0", certNameHash))
65-
cmdMap[fmt.Sprintf("sudo ln -s %s %s", certFile, certStorePath)] = "1"
66-
cmdMap[fmt.Sprintf("openssl x509 -hash -noout -in %s", certFile)] = certNameHash
67-
cmdMap[fmt.Sprintf("sudo ln -s %s %s", certStorePath, remoteCertHashLink)] = "1"
54+
expected := map[string]string{
55+
`sudo /bin/bash -c "test -f /usr/share/ca-certificates/mycert.pem || ln -s /etc/ssl/certs/mycert.pem /usr/share/ca-certificates/mycert.pem"`: "-",
56+
`sudo /bin/bash -c "test -f /usr/share/ca-certificates/minikubeCA.pem || ln -s /etc/ssl/certs/minikubeCA.pem /usr/share/ca-certificates/minikubeCA.pem"`: "-",
6857
}
6958
f := command.NewFakeCommandRunner()
70-
f.SetCommandToOutput(cmdMap)
59+
f.SetCommandToOutput(expected)
7160

7261
var filesToBeTransferred []string
7362
for _, cert := range certs {

pkg/minikube/bootstrapper/kubeadm/kubeadm.go

+11
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,17 @@ func (k *Bootstrapper) UpdateCluster(cfg config.MachineConfig) error {
471471
if err := bsutil.AddAddons(&files, assets.GenerateTemplateData(cfg.KubernetesConfig)); err != nil {
472472
return errors.Wrap(err, "adding addons")
473473
}
474+
475+
// Combine mkdir request into a single call to reduce load
476+
dirs := []string{}
477+
for _, f := range files {
478+
dirs = append(dirs, f.GetTargetDir())
479+
}
480+
args := append([]string{"mkdir", "-p"}, dirs...)
481+
if _, err := k.c.RunCmd(exec.Command("sudo", args...)); err != nil {
482+
return errors.Wrap(err, "mkdir")
483+
}
484+
474485
for _, f := range files {
475486
if err := k.c.Copy(f); err != nil {
476487
return errors.Wrapf(err, "copy")

pkg/minikube/cluster/cluster.go

+57
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"math"
2525
"net"
2626
"os/exec"
27+
"path"
2728
"path/filepath"
2829
"regexp"
2930
"strconv"
@@ -46,6 +47,7 @@ import (
4647
"github.com/shirou/gopsutil/mem"
4748
"github.com/spf13/viper"
4849

50+
"k8s.io/minikube/pkg/minikube/command"
4951
"k8s.io/minikube/pkg/minikube/config"
5052
cfg "k8s.io/minikube/pkg/minikube/config"
5153
"k8s.io/minikube/pkg/minikube/constants"
@@ -54,6 +56,8 @@ import (
5456
"k8s.io/minikube/pkg/minikube/localpath"
5557
"k8s.io/minikube/pkg/minikube/out"
5658
"k8s.io/minikube/pkg/minikube/registry"
59+
"k8s.io/minikube/pkg/minikube/sshutil"
60+
"k8s.io/minikube/pkg/minikube/vmpath"
5761
"k8s.io/minikube/pkg/util/lock"
5862
"k8s.io/minikube/pkg/util/retry"
5963
)
@@ -67,6 +71,17 @@ var (
6771
// The maximum the guest VM clock is allowed to be ahead and behind. This value is intentionally
6872
// large to allow for inaccurate methodology, but still small enough so that certificates are likely valid.
6973
maxClockDesyncSeconds = 2.1
74+
75+
// requiredDirectories are directories to create on the host during setup
76+
requiredDirectories = []string{
77+
vmpath.GuestAddonsDir,
78+
vmpath.GuestManifestsDir,
79+
vmpath.GuestEphemeralDir,
80+
vmpath.GuestPersistentDir,
81+
vmpath.GuestCertsDir,
82+
path.Join(vmpath.GuestPersistentDir, "images"),
83+
path.Join(vmpath.GuestPersistentDir, "binaries"),
84+
}
7085
)
7186

7287
// This init function is used to set the logtostderr variable to false so that INFO level log info does not clutter the CLI
@@ -497,6 +512,10 @@ func createHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error
497512
return nil, errors.Wrap(err, "create")
498513
}
499514

515+
if err := createRequiredDirectories(h); err != nil {
516+
return h, errors.Wrap(err, "required directories")
517+
}
518+
500519
if driver.BareMetal(config.VMDriver) {
501520
showLocalOsRelease()
502521
} else if !driver.BareMetal(config.VMDriver) && !driver.IsKIC(config.VMDriver) {
@@ -646,3 +665,41 @@ func IsMinikubeRunning(api libmachine.API) bool {
646665
}
647666
return true
648667
}
668+
669+
// createRequiredDirectories creates directories expected by minikube to exist
670+
func createRequiredDirectories(h *host.Host) error {
671+
if h.DriverName == driver.Mock {
672+
glog.Infof("skipping createRequiredDirectories")
673+
return nil
674+
}
675+
glog.Infof("creating required directories: %v", requiredDirectories)
676+
r, err := commandRunner(h)
677+
if err != nil {
678+
return errors.Wrap(err, "command runner")
679+
}
680+
681+
args := append([]string{"mkdir", "-p"}, requiredDirectories...)
682+
if _, err := r.RunCmd(exec.Command("sudo", args...)); err != nil {
683+
return errors.Wrapf(err, "sudo mkdir (%s)", h.DriverName)
684+
}
685+
return nil
686+
}
687+
688+
// commandRunner returns best available command runner for this host
689+
func commandRunner(h *host.Host) (command.Runner, error) {
690+
if h.DriverName == driver.Mock {
691+
glog.Errorf("commandRunner: returning unconfigured FakeCommandRunner, commands will fail!")
692+
return &command.FakeCommandRunner{}, nil
693+
}
694+
if driver.BareMetal(h.Driver.DriverName()) {
695+
return &command.ExecRunner{}, nil
696+
}
697+
if h.Driver.DriverName() == driver.Docker {
698+
return command.NewKICRunner(h.Name, "docker"), nil
699+
}
700+
client, err := sshutil.NewSSHClient(h.Driver)
701+
if err != nil {
702+
return nil, errors.Wrap(err, "getting ssh client for bootstrapper")
703+
}
704+
return command.NewSSHRunner(client), nil
705+
}

pkg/minikube/cluster/cluster_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ func TestGetHostDockerEnv(t *testing.T) {
374374
}
375375

376376
func TestGetHostDockerEnvIPv6(t *testing.T) {
377+
RegisterMockDriver(t)
378+
377379
tempDir := tests.MakeTempDir()
378380
defer os.RemoveAll(tempDir)
379381

pkg/minikube/command/exec_runner.go

-3
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,6 @@ func (*ExecRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) {
8080

8181
// Copy copies a file and its permissions
8282
func (*ExecRunner) Copy(f assets.CopyableFile) error {
83-
if err := os.MkdirAll(f.GetTargetDir(), os.ModePerm); err != nil {
84-
return errors.Wrapf(err, "error making dirs for %s", f.GetTargetDir())
85-
}
8683
targetPath := path.Join(f.GetTargetDir(), f.GetTargetName())
8784
if _, err := os.Stat(targetPath); err == nil {
8885
if err := os.Remove(targetPath); err != nil {

pkg/minikube/command/fake_runner.go

+28-9
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,21 @@ func (f *FakeCommandRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) {
5454

5555
start := time.Now()
5656

57-
out, ok := f.cmdMap.Load(strings.Join(rr.Args, " "))
57+
key := rr.Command()
58+
out, ok := f.cmdMap.Load(key)
59+
if !ok {
60+
cmds := f.commands()
61+
if len(cmds) == 0 {
62+
return rr, fmt.Errorf("asked to execute %s, but FakeCommandRunner has no commands stored", rr.Command())
63+
}
64+
65+
var txt strings.Builder
66+
for _, c := range f.commands() {
67+
txt.WriteString(fmt.Sprintf(" `%s`\n", c))
68+
}
69+
return rr, fmt.Errorf("unregistered command:\n `%s`\nexpected one of:\n%s", key, txt.String())
70+
}
71+
5872
var buf bytes.Buffer
5973
outStr := ""
6074
if out != nil {
@@ -69,14 +83,9 @@ func (f *FakeCommandRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) {
6983

7084
elapsed := time.Since(start)
7185

72-
if ok {
73-
// Reduce log spam
74-
if elapsed > (1 * time.Second) {
75-
glog.Infof("(FakeCommandRunner) Done: %v: (%s)", rr.Command(), elapsed)
76-
}
77-
} else {
78-
glog.Infof("(FakeCommandRunner) Non-zero exit: %v: (%s)\n%s", rr.Command(), elapsed, out)
79-
return rr, fmt.Errorf("unavailable command: %s", rr.Command())
86+
// Reduce log spam
87+
if elapsed > (1 * time.Second) {
88+
glog.Infof("(FakeCommandRunner) Done: %v: (%s)", rr.Command(), elapsed)
8089
}
8190
return rr, nil
8291
}
@@ -108,6 +117,7 @@ func (f *FakeCommandRunner) SetFileToContents(fileToContents map[string]string)
108117
// SetCommandToOutput stores the file to contents map for the FakeCommandRunner
109118
func (f *FakeCommandRunner) SetCommandToOutput(cmdToOutput map[string]string) {
110119
for k, v := range cmdToOutput {
120+
glog.Infof("fake command %q -> %q", k, v)
111121
f.cmdMap.Store(k, v)
112122
}
113123
}
@@ -121,6 +131,15 @@ func (f *FakeCommandRunner) GetFileToContents(filename string) (string, error) {
121131
return contents.(string), nil
122132
}
123133

134+
func (f *FakeCommandRunner) commands() []string {
135+
cmds := []string{}
136+
f.cmdMap.Range(func(k, v interface{}) bool {
137+
cmds = append(cmds, fmt.Sprintf("%s", k))
138+
return true
139+
})
140+
return cmds
141+
}
142+
124143
// DumpMaps prints out the list of stored commands and stored filenames.
125144
func (f *FakeCommandRunner) DumpMaps(w io.Writer) {
126145
fmt.Fprintln(w, "Commands:")

pkg/minikube/command/kic_runner.go

+13-10
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"os"
2525
"os/exec"
2626
"path"
27+
"strconv"
2728
"time"
2829

2930
"github.com/golang/glog"
@@ -118,7 +119,7 @@ func (k *kicRunner) RunCmd(cmd *exec.Cmd) (*RunResult, error) {
118119
if exitError, ok := err.(*exec.ExitError); ok {
119120
rr.ExitCode = exitError.ExitCode()
120121
}
121-
err = errors.Wrapf(err, "command failed: %s", oc.Args)
122+
err = fmt.Errorf("%s: %v\nstdout:\n%s\nstderr:\n%s", rr.Command(), err, rr.Stdout.String(), rr.Stderr.String())
122123
}
123124
return rr, err
124125

@@ -148,22 +149,24 @@ func (k *kicRunner) Copy(f assets.CopyableFile) error {
148149
return errors.Wrap(err, "close temporary file")
149150
}
150151
assetName = tmpFile.Name()
151-
152152
}
153+
153154
// based of format of "docker cp containerName:destination"
154155
destination := fmt.Sprintf("%s:%s/%s", k.nameOrID, f.GetTargetDir(), f.GetTargetName())
155-
// make sure dir exists inside the container
156-
if _, err := k.RunCmd(exec.Command("mkdir", "-p", f.GetTargetDir())); err != nil {
157-
return errors.Wrapf(err, "error making dir %s", f.GetTargetDir())
156+
157+
perms, err := strconv.ParseInt(f.GetPermissions(), 8, 0)
158+
if err != nil {
159+
return errors.Wrapf(err, "error converting permissions %s to integer", f.GetPermissions())
158160
}
159161

160-
if out, err := exec.Command(k.ociBin, "cp", assetName, destination).CombinedOutput(); err != nil {
161-
return errors.Wrapf(err, "error copying %s into node, output: %s", f.GetAssetName(), string(out))
162+
// Rely on cp -a to propagate permissions
163+
if err := os.Chmod(assetName, os.FileMode(perms)); err != nil {
164+
return errors.Wrapf(err, "chmod")
162165
}
163-
fp := path.Join(f.GetTargetDir(), f.GetTargetName())
164-
if _, err := k.RunCmd(exec.Command("sudo", "chmod", f.GetPermissions(), fp)); err != nil {
165-
return errors.Wrapf(err, "failed to chmod file permissions %s", fp)
166+
if out, err := exec.Command(k.ociBin, "cp", "-a", assetName, destination).CombinedOutput(); err != nil {
167+
return errors.Wrapf(err, "error copying %s into node, output: %s", f.GetAssetName(), string(out))
166168
}
169+
167170
return nil
168171
}
169172

pkg/minikube/command/ssh_runner.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ func (s *SSHRunner) Copy(f assets.CopyableFile) error {
196196
return nil
197197
})
198198

199-
scp := fmt.Sprintf("sudo mkdir -p %s && sudo scp -t %s", f.GetTargetDir(), f.GetTargetDir())
199+
scp := fmt.Sprintf("sudo test -d %s && sudo scp -t %s", f.GetTargetDir(), f.GetTargetDir())
200200
mtime, err := f.GetModTime()
201201
if err != nil {
202202
glog.Infof("error getting modtime for %s: %v", dst, err)

pkg/minikube/machine/cache_images.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"path"
2323
"path/filepath"
2424
"sync"
25+
"time"
2526

2627
"github.com/docker/docker/client"
2728
"github.com/docker/machine/libmachine/state"
@@ -63,7 +64,12 @@ func CacheImagesForBootstrapper(imageRepository string, version string, clusterB
6364
// LoadImages loads previously cached images into the container runtime
6465
func LoadImages(cc *config.MachineConfig, runner command.Runner, images []string, cacheDir string) error {
6566
glog.Infof("LoadImages start: %s", images)
66-
defer glog.Infof("LoadImages end")
67+
start := time.Now()
68+
69+
defer func() {
70+
glog.Infof("LoadImages completed in %s", time.Since(start))
71+
}()
72+
6773
var g errgroup.Group
6874
cr, err := cruntime.New(cruntime.Config{Type: cc.ContainerRuntime, Runner: runner})
6975
if err != nil {

0 commit comments

Comments
 (0)