Move addDockerfileToBuildContext to the build package.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
Daniel Nephin 2017-04-16 16:06:16 -04:00
parent b98e03d3b0
commit a5044b4982
3 changed files with 79 additions and 148 deletions

View File

@ -6,11 +6,9 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"regexp" "regexp"
"runtime" "runtime"
"time"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
@ -24,7 +22,6 @@ import (
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/progress"
"github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/urlutil" "github.com/docker/docker/pkg/urlutil"
runconfigopts "github.com/docker/docker/runconfig/opts" runconfigopts "github.com/docker/docker/runconfig/opts"
units "github.com/docker/go-units" units "github.com/docker/go-units"
@ -237,7 +234,7 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
// replace Dockerfile if added dynamically // replace Dockerfile if added dynamically
if dockerfileCtx != nil { if dockerfileCtx != nil {
buildCtx, relDockerfile, err = addDockerfileToBuildContext(dockerfileCtx, buildCtx) buildCtx, relDockerfile, err = build.AddDockerfileToBuildContext(dockerfileCtx, buildCtx)
if err != nil { if err != nil {
return err return err
} }
@ -347,50 +344,6 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
return nil return nil
} }
func addDockerfileToBuildContext(dockerfileCtx io.ReadCloser, buildCtx io.ReadCloser) (io.ReadCloser, string, error) {
file, err := ioutil.ReadAll(dockerfileCtx)
dockerfileCtx.Close()
if err != nil {
return nil, "", err
}
now := time.Now()
hdrTmpl := &tar.Header{
Mode: 0600,
Uid: 0,
Gid: 0,
ModTime: now,
Typeflag: tar.TypeReg,
AccessTime: now,
ChangeTime: now,
}
randomName := ".dockerfile." + stringid.GenerateRandomID()[:20]
buildCtx = archive.ReplaceFileTarWrapper(buildCtx, map[string]archive.TarModifierFunc{
// Add the dockerfile with a random filename
randomName: func(_ string, h *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
return hdrTmpl, file, nil
},
// Update .dockerignore to include the random filename
".dockerignore": func(_ string, h *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
if h == nil {
h = hdrTmpl
}
b := &bytes.Buffer{}
if content != nil {
if _, err := b.ReadFrom(content); err != nil {
return nil, nil, err
}
} else {
b.WriteString(".dockerignore")
}
b.WriteString("\n" + randomName + "\n")
return h, b.Bytes(), nil
},
})
return buildCtx, randomName, nil
}
func isLocalDir(c string) bool { func isLocalDir(c string) bool {
_, err := os.Stat(c) _, err := os.Stat(c)
return err == nil return err == nil

View File

@ -11,6 +11,8 @@ import (
"runtime" "runtime"
"strings" "strings"
"archive/tar"
"bytes"
"github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/fileutils"
"github.com/docker/docker/pkg/gitutils" "github.com/docker/docker/pkg/gitutils"
@ -18,7 +20,9 @@ import (
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/progress" "github.com/docker/docker/pkg/progress"
"github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/pkg/stringid"
"github.com/pkg/errors" "github.com/pkg/errors"
"time"
) )
const ( const (
@ -293,3 +297,49 @@ func getDockerfileRelPath(absContextDir, givenDockerfile string) (string, error)
func isUNC(path string) bool { func isUNC(path string) bool {
return runtime.GOOS == "windows" && strings.HasPrefix(path, `\\`) return runtime.GOOS == "windows" && strings.HasPrefix(path, `\\`)
} }
// AddDockerfileToBuildContext from a ReadCloser, returns a new archive and
// the relative path to the dockerfile in the context.
func AddDockerfileToBuildContext(dockerfileCtx io.ReadCloser, buildCtx io.ReadCloser) (io.ReadCloser, string, error) {
file, err := ioutil.ReadAll(dockerfileCtx)
dockerfileCtx.Close()
if err != nil {
return nil, "", err
}
now := time.Now()
hdrTmpl := &tar.Header{
Mode: 0600,
Uid: 0,
Gid: 0,
ModTime: now,
Typeflag: tar.TypeReg,
AccessTime: now,
ChangeTime: now,
}
randomName := ".dockerfile." + stringid.GenerateRandomID()[:20]
buildCtx = archive.ReplaceFileTarWrapper(buildCtx, map[string]archive.TarModifierFunc{
// Add the dockerfile with a random filename
randomName: func(_ string, h *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
return hdrTmpl, file, nil
},
// Update .dockerignore to include the random filename
".dockerignore": func(_ string, h *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
if h == nil {
h = hdrTmpl
}
b := &bytes.Buffer{}
if content != nil {
if _, err := b.ReadFrom(content); err != nil {
return nil, nil, err
}
} else {
b.WriteString(".dockerignore")
}
b.WriteString("\n" + randomName + "\n")
return h, b.Bytes(), nil
},
})
return buildCtx, randomName, nil
}

View File

@ -12,7 +12,9 @@ import (
"testing" "testing"
"github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/testutil/assert" "github.com/docker/docker/pkg/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
const dockerfileContents = "FROM busybox" const dockerfileContents = "FROM busybox"
@ -36,10 +38,7 @@ func testValidateContextDirectory(t *testing.T, prepare func(t *testing.T) (stri
defer cleanup() defer cleanup()
err := ValidateContextDirectory(contextDir, excludes) err := ValidateContextDirectory(contextDir, excludes)
require.NoError(t, err)
if err != nil {
t.Fatalf("Error should be nil, got: %s", err)
}
} }
func TestGetContextFromLocalDirNoDockerfile(t *testing.T) { func TestGetContextFromLocalDirNoDockerfile(t *testing.T) {
@ -47,7 +46,7 @@ func TestGetContextFromLocalDirNoDockerfile(t *testing.T) {
defer cleanup() defer cleanup()
_, _, err := GetContextFromLocalDir(contextDir, "") _, _, err := GetContextFromLocalDir(contextDir, "")
assert.Error(t, err, "Dockerfile") testutil.ErrorContains(t, err, "Dockerfile")
} }
func TestGetContextFromLocalDirNotExistingDir(t *testing.T) { func TestGetContextFromLocalDirNotExistingDir(t *testing.T) {
@ -56,19 +55,8 @@ func TestGetContextFromLocalDirNotExistingDir(t *testing.T) {
fakePath := filepath.Join(contextDir, "fake") fakePath := filepath.Join(contextDir, "fake")
absContextDir, relDockerfile, err := GetContextFromLocalDir(fakePath, "") _, _, err := GetContextFromLocalDir(fakePath, "")
testutil.ErrorContains(t, err, "fake")
if err == nil {
t.Fatalf("Error should not be nil")
}
if absContextDir != "" {
t.Fatalf("Absolute directory path should be empty, got: %s", absContextDir)
}
if relDockerfile != "" {
t.Fatalf("Relative path to Dockerfile should be empty, got: %s", relDockerfile)
}
} }
func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) { func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) {
@ -78,7 +66,7 @@ func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) {
fakePath := filepath.Join(contextDir, "fake") fakePath := filepath.Join(contextDir, "fake")
_, _, err := GetContextFromLocalDir(contextDir, fakePath) _, _, err := GetContextFromLocalDir(contextDir, fakePath)
assert.Error(t, err, "fake") testutil.ErrorContains(t, err, "fake")
} }
func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) { func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
@ -91,18 +79,10 @@ func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) {
defer chdirCleanup() defer chdirCleanup()
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "") absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
require.NoError(t, err)
if err != nil { assert.Equal(t, contextDir, absContextDir)
t.Fatalf("Error when getting context from local dir: %s", err) assert.Equal(t, DefaultDockerfileName, relDockerfile)
}
if absContextDir != contextDir {
t.Fatalf("Absolute directory path should be equal to %s, got: %s", contextDir, absContextDir)
}
if relDockerfile != DefaultDockerfileName {
t.Fatalf("Relative path to dockerfile should be equal to %s, got: %s", DefaultDockerfileName, relDockerfile)
}
} }
func TestGetContextFromLocalDirWithDockerfile(t *testing.T) { func TestGetContextFromLocalDirWithDockerfile(t *testing.T) {
@ -112,18 +92,10 @@ func TestGetContextFromLocalDirWithDockerfile(t *testing.T) {
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777) createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "") absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "")
require.NoError(t, err)
if err != nil { assert.Equal(t, contextDir, absContextDir)
t.Fatalf("Error when getting context from local dir: %s", err) assert.Equal(t, DefaultDockerfileName, relDockerfile)
}
if absContextDir != contextDir {
t.Fatalf("Absolute directory path should be equal to %s, got: %s", contextDir, absContextDir)
}
if relDockerfile != DefaultDockerfileName {
t.Fatalf("Relative path to dockerfile should be equal to %s, got: %s", DefaultDockerfileName, relDockerfile)
}
} }
func TestGetContextFromLocalDirLocalFile(t *testing.T) { func TestGetContextFromLocalDirLocalFile(t *testing.T) {
@ -158,19 +130,10 @@ func TestGetContextFromLocalDirWithCustomDockerfile(t *testing.T) {
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777) createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, DefaultDockerfileName) absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, DefaultDockerfileName)
require.NoError(t, err)
if err != nil { assert.Equal(t, contextDir, absContextDir)
t.Fatalf("Error when getting context from local dir: %s", err) assert.Equal(t, DefaultDockerfileName, relDockerfile)
}
if absContextDir != contextDir {
t.Fatalf("Absolute directory path should be equal to %s, got: %s", contextDir, absContextDir)
}
if relDockerfile != DefaultDockerfileName {
t.Fatalf("Relative path to dockerfile should be equal to %s, got: %s", DefaultDockerfileName, relDockerfile)
}
} }
func TestGetContextFromReaderString(t *testing.T) { func TestGetContextFromReaderString(t *testing.T) {
@ -198,9 +161,7 @@ func TestGetContextFromReaderString(t *testing.T) {
t.Fatalf("Tar stream too long: %s", err) t.Fatalf("Tar stream too long: %s", err)
} }
if err = tarArchive.Close(); err != nil { require.NoError(t, tarArchive.Close())
t.Fatalf("Error when closing tar stream: %s", err)
}
if dockerfileContents != contents { if dockerfileContents != contents {
t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents) t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents)
@ -218,24 +179,15 @@ func TestGetContextFromReaderTar(t *testing.T) {
createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777) createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777)
tarStream, err := archive.Tar(contextDir, archive.Uncompressed) tarStream, err := archive.Tar(contextDir, archive.Uncompressed)
require.NoError(t, err)
if err != nil {
t.Fatalf("Error when creating tar: %s", err)
}
tarArchive, relDockerfile, err := GetContextFromReader(tarStream, DefaultDockerfileName) tarArchive, relDockerfile, err := GetContextFromReader(tarStream, DefaultDockerfileName)
require.NoError(t, err)
if err != nil {
t.Fatalf("Error when executing GetContextFromReader: %s", err)
}
tarReader := tar.NewReader(tarArchive) tarReader := tar.NewReader(tarArchive)
header, err := tarReader.Next() header, err := tarReader.Next()
require.NoError(t, err)
if err != nil {
t.Fatalf("Error when reading tar archive: %s", err)
}
if header.Name != DefaultDockerfileName { if header.Name != DefaultDockerfileName {
t.Fatalf("Dockerfile name should be: %s, got: %s", DefaultDockerfileName, header.Name) t.Fatalf("Dockerfile name should be: %s, got: %s", DefaultDockerfileName, header.Name)
@ -251,9 +203,7 @@ func TestGetContextFromReaderTar(t *testing.T) {
t.Fatalf("Tar stream too long: %s", err) t.Fatalf("Tar stream too long: %s", err)
} }
if err = tarArchive.Close(); err != nil { require.NoError(t, tarArchive.Close())
t.Fatalf("Error when closing tar stream: %s", err)
}
if dockerfileContents != contents { if dockerfileContents != contents {
t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents) t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents)
@ -293,11 +243,8 @@ func TestValidateContextDirectoryWithOneFileExcludes(t *testing.T) {
// When an error occurs, it terminates the test. // When an error occurs, it terminates the test.
func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) { func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) {
path, err := ioutil.TempDir(dir, prefix) path, err := ioutil.TempDir(dir, prefix)
assert.NilError(t, err) require.NoError(t, err)
return path, func() { require.NoError(t, os.RemoveAll(path)) }
return path, func() {
assert.NilError(t, os.RemoveAll(path))
}
} }
// createTestTempFile creates a temporary file within dir with specific contents and permissions. // createTestTempFile creates a temporary file within dir with specific contents and permissions.
@ -305,11 +252,7 @@ func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) {
func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.FileMode) string { func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.FileMode) string {
filePath := filepath.Join(dir, filename) filePath := filepath.Join(dir, filename)
err := ioutil.WriteFile(filePath, []byte(contents), perm) err := ioutil.WriteFile(filePath, []byte(contents), perm)
require.NoError(t, err)
if err != nil {
t.Fatalf("Error when creating %s file: %s", filename, err)
}
return filePath return filePath
} }
@ -319,22 +262,7 @@ func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.Fi
// When an error occurs, it terminates the test. // When an error occurs, it terminates the test.
func chdir(t *testing.T, dir string) func() { func chdir(t *testing.T, dir string) func() {
workingDirectory, err := os.Getwd() workingDirectory, err := os.Getwd()
require.NoError(t, err)
if err != nil { require.NoError(t, os.Chdir(dir))
t.Fatalf("Error when retrieving working directory: %s", err) return func() { require.NoError(t, os.Chdir(workingDirectory)) }
}
err = os.Chdir(dir)
if err != nil {
t.Fatalf("Error when changing directory to %s: %s", dir, err)
}
return func() {
err = os.Chdir(workingDirectory)
if err != nil {
t.Fatalf("Error when changing back to working directory (%s): %s", workingDirectory, err)
}
}
} }