Skip to content

Commit

Permalink
Compress after rewriting the archive.
Browse files Browse the repository at this point in the history
Write a test showing compress failure.

Signed-off-by: Daniel Nephin <[email protected]>
  • Loading branch information
dnephin committed Jun 29, 2017
1 parent 74af31b commit f7f567f
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 13 deletions.
23 changes: 14 additions & 9 deletions cli/command/image/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,20 @@ func (o buildOptions) contextFromStdin() bool {
return o.context == "-"
}

// NewBuildCommand creates a new `docker build` command
func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
func newBuildOptions() buildOptions {
ulimits := make(map[string]*units.Ulimit)
options := buildOptions{
return buildOptions{
tags: opts.NewListOpts(validateTag),
buildArgs: opts.NewListOpts(opts.ValidateEnv),
ulimits: opts.NewUlimitOpt(&ulimits),
labels: opts.NewListOpts(opts.ValidateEnv),
extraHosts: opts.NewListOpts(opts.ValidateExtraHost),
}
}

// NewBuildCommand creates a new `docker build` command
func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
options := newBuildOptions()

cmd := &cobra.Command{
Use: "build [OPTIONS] PATH | URL | -",
Expand Down Expand Up @@ -237,13 +241,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
}

excludes = build.TrimBuildFilesFromExcludes(excludes, relDockerfile, options.dockerfileFromStdin())

compression := archive.Uncompressed
if options.compress {
compression = archive.Gzip
}
buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
Compression: compression,
ExcludePatterns: excludes,
})
if err != nil {
Expand Down Expand Up @@ -292,6 +290,13 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
}
}

if options.compress {
buildCtx, err = build.Compress(buildCtx)
if err != nil {
return err
}
}

// Setup an upload progress bar
progressOutput := streamformatter.NewProgressOutput(progBuff)
if !dockerCli.Out().IsTerminal() {
Expand Down
25 changes: 25 additions & 0 deletions cli/command/image/build/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/fileutils"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/pools"
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/pkg/stringid"
Expand Down Expand Up @@ -375,3 +376,27 @@ func AddDockerfileToBuildContext(dockerfileCtx io.ReadCloser, buildCtx io.ReadCl
})
return buildCtx, randomName, nil
}

// Compress the build context for sending to the API
func Compress(buildCtx io.ReadCloser) (io.ReadCloser, error) {
pipeReader, pipeWriter := io.Pipe()

go func() {
compressWriter, err := archive.CompressStream(pipeWriter, archive.Gzip)
if err != nil {
pipeWriter.CloseWithError(err)
}
defer buildCtx.Close()

if _, err := pools.Copy(compressWriter, buildCtx); err != nil {
pipeWriter.CloseWithError(
errors.Wrap(err, "failed to compress context"))
compressWriter.Close()
return
}
compressWriter.Close()
pipeWriter.Close()
}()

return pipeReader, nil
}
70 changes: 70 additions & 0 deletions cli/command/image/build_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package image

import (
"bytes"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"testing"

"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/archive"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/context"
)

func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
dest, err := ioutil.TempDir("", "test-build-compress-dest")
require.NoError(t, err)
defer os.RemoveAll(dest)

var dockerfileName string
fakeImageBuild := func(_ context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
buffer := new(bytes.Buffer)
tee := io.TeeReader(context, buffer)

assert.NoError(t, archive.Untar(tee, dest, nil))
dockerfileName = options.Dockerfile

header := buffer.Bytes()[:10]
assert.Equal(t, archive.Gzip, archive.DetectCompression(header))

body := new(bytes.Buffer)
return types.ImageBuildResponse{Body: ioutil.NopCloser(body)}, nil
}

cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeImageBuild}, ioutil.Discard)
dockerfile := bytes.NewBufferString(`
FROM alpine:3.6
COPY foo /
`)
cli.SetIn(command.NewInStream(ioutil.NopCloser(dockerfile)))

dir, err := ioutil.TempDir("", "test-build-compress")
require.NoError(t, err)
defer os.RemoveAll(dir)

ioutil.WriteFile(filepath.Join(dir, "foo"), []byte("some content"), 0644)

options := newBuildOptions()
options.compress = true
options.dockerfileName = "-"
options.context = dir

err = runBuild(cli, options)
require.NoError(t, err)

files, err := ioutil.ReadDir(dest)
require.NoError(t, err)
actual := []string{}
for _, fileInfo := range files {
actual = append(actual, fileInfo.Name())
}
sort.Strings(actual)
assert.Equal(t, []string{dockerfileName, ".dockerignore", "foo"}, actual)
}
8 changes: 8 additions & 0 deletions cli/command/image/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type fakeClient struct {
imageInspectFunc func(image string) (types.ImageInspect, []byte, error)
imageImportFunc func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error)
imageHistoryFunc func(image string) ([]image.HistoryResponseItem, error)
imageBuildFunc func(context.Context, io.Reader, types.ImageBuildOptions) (types.ImageBuildResponse, error)
}

func (cli *fakeClient) ImageTag(_ context.Context, image, ref string) error {
Expand Down Expand Up @@ -114,3 +115,10 @@ func (cli *fakeClient) ImageHistory(_ context.Context, img string) ([]image.Hist
}
return []image.HistoryResponseItem{{ID: img, Created: time.Now().Unix()}}, nil
}

func (cli *fakeClient) ImageBuild(ctx context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
if cli.imageBuildFunc != nil {
return cli.imageBuildFunc(ctx, context, options)
}
return types.ImageBuildResponse{Body: ioutil.NopCloser(strings.NewReader(""))}, nil
}
14 changes: 10 additions & 4 deletions cli/internal/test/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ type FakeCli struct {
// NewFakeCli returns a Cli backed by the fakeCli
func NewFakeCli(client client.APIClient, out io.Writer) *FakeCli {
return &FakeCli{
client: client,
out: command.NewOutStream(out),
err: ioutil.Discard,
in: command.NewInStream(ioutil.NopCloser(strings.NewReader(""))),
client: client,
out: command.NewOutStream(out),
err: ioutil.Discard,
in: command.NewInStream(ioutil.NopCloser(strings.NewReader(""))),
configfile: configfile.NewConfigFile("configfile"),
}
}

Expand Down Expand Up @@ -69,3 +70,8 @@ func (c *FakeCli) In() *command.InStream {
func (c *FakeCli) ConfigFile() *configfile.ConfigFile {
return c.configfile
}

// ServerInfo returns API server information for the server used by this client
func (c *FakeCli) ServerInfo() command.ServerInfo {
return c.server
}

0 comments on commit f7f567f

Please sign in to comment.