Skip to content

Commit 0c780c8

Browse files
committed
Use long running session in builder
Signed-off-by: Tonis Tiigi <[email protected]>
1 parent ae8d049 commit 0c780c8

1 file changed

Lines changed: 72 additions & 1 deletion

File tree

cli/command/image/build.go

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,30 @@ import (
44
"archive/tar"
55
"bufio"
66
"bytes"
7+
"crypto/rand"
8+
"crypto/sha256"
9+
"encoding/hex"
710
"encoding/json"
811
"fmt"
912
"io"
1013
"io/ioutil"
1114
"os"
15+
"path/filepath"
1216
"regexp"
1317
"runtime"
1418

19+
"github.com/Sirupsen/logrus"
1520
"github.com/docker/cli/cli"
1621
"github.com/docker/cli/cli/command"
1722
"github.com/docker/cli/cli/command/image/build"
23+
cliconfig "github.com/docker/cli/cli/config"
1824
"github.com/docker/cli/opts"
1925
"github.com/docker/distribution/reference"
2026
"github.com/docker/docker/api"
2127
"github.com/docker/docker/api/types"
2228
"github.com/docker/docker/api/types/container"
29+
"github.com/docker/docker/api/types/versions"
30+
"github.com/docker/docker/client/session"
2331
"github.com/docker/docker/pkg/archive"
2432
"github.com/docker/docker/pkg/jsonmessage"
2533
"github.com/docker/docker/pkg/progress"
@@ -152,6 +160,10 @@ func (out *lastProgressOutput) WriteProgress(prog progress.Progress) error {
152160
return out.output.WriteProgress(prog)
153161
}
154162

163+
func isSessionSupported(dockerCli *command.DockerCli) bool {
164+
return dockerCli.ServerInfo().HasExperimental && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.30")
165+
}
166+
155167
// nolint: gocyclo
156168
func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
157169
var (
@@ -163,6 +175,7 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
163175
relDockerfile string
164176
progBuff io.Writer
165177
buildBuff io.Writer
178+
remote string
166179
)
167180

168181
if options.dockerfileFromStdin() {
@@ -250,7 +263,8 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
250263
}
251264
}
252265

253-
ctx := context.Background()
266+
ctx, cancel := context.WithCancel(context.Background())
267+
defer cancel()
254268

255269
var resolvedTags []*resolvedTag
256270
if command.IsTrusted() {
@@ -268,6 +282,21 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
268282
progressOutput = &lastProgressOutput{output: progressOutput}
269283
}
270284

285+
if dockerfileCtx != nil && buildCtx == nil {
286+
buildCtx = dockerfileCtx
287+
}
288+
var s *session.Session
289+
if isSessionSupported(dockerCli) {
290+
sharedKey, err := getBuildSharedKey(contextDir)
291+
if err != nil {
292+
return errors.Wrap(err, "failed to get build shared key")
293+
}
294+
s, err = session.NewSession(filepath.Base(contextDir), sharedKey)
295+
if err != nil {
296+
return errors.Wrap(err, "failed to create session")
297+
}
298+
}
299+
271300
var body io.Reader = progress.NewProgressReader(buildCtx, progressOutput, 0, "", "Sending build context to Docker daemon")
272301

273302
authConfigs, _ := dockerCli.GetAllCredentials()
@@ -299,6 +328,18 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
299328
Squash: options.squash,
300329
ExtraHosts: options.extraHosts.GetAll(),
301330
Target: options.target,
331+
RemoteContext: remote,
332+
}
333+
334+
if s != nil {
335+
go func() {
336+
logrus.Debugf("running session: %v", s.UUID())
337+
if err := s.Run(ctx, dockerCli.Client().DialSession); err != nil {
338+
logrus.Error(err)
339+
cancel()
340+
}
341+
}()
342+
buildOptions.SessionID = s.UUID()
302343
}
303344

304345
response, err := dockerCli.Client().ImageBuild(ctx, body, buildOptions)
@@ -373,6 +414,36 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
373414
return nil
374415
}
375416

417+
func getBuildSharedKey(dir string) (string, error) {
418+
// build session is hash of build dir with node based randomness
419+
sessionFile := filepath.Join(cliconfig.Dir(), ".buildsharedkey")
420+
if _, err := os.Lstat(sessionFile); err != nil {
421+
if os.IsNotExist(err) {
422+
b := make([]byte, 32)
423+
if _, err := rand.Read(b); err != nil {
424+
return "", err
425+
}
426+
if err := os.MkdirAll(cliconfig.Dir(), 0600); err != nil {
427+
return "", err
428+
}
429+
if err := ioutil.WriteFile(sessionFile, []byte(hex.EncodeToString(b)), 0600); err != nil {
430+
return "", err
431+
}
432+
} else {
433+
return "", err
434+
}
435+
}
436+
437+
dt, err := ioutil.ReadFile(sessionFile)
438+
if err != nil {
439+
return "", errors.Wrapf(err, "failed to read %s", sessionFile)
440+
441+
}
442+
443+
s := sha256.Sum256([]byte(fmt.Sprintf("%s:%s", dt, dir)))
444+
return hex.EncodeToString(s[:]), nil // add randomness to force recheck
445+
}
446+
376447
func isLocalDir(c string) bool {
377448
_, err := os.Stat(c)
378449
return err == nil

0 commit comments

Comments
 (0)