-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for experimental BuildKit #1111
Changes from all commits
656fe85
0f97642
b19294e
8cf213b
e0b3921
5314a8f
89e1024
82f0e1e
640cbb8
b2b3f9c
584d59d
ed75f62
15674d9
5919e8a
aef4209
8945270
6c60bb4
00792d1
5a103e1
b3a5c15
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,4 @@ TYPE ACTIVE | |
Images 0 | ||
Containers 0 | ||
Local Volumes 0 | ||
Build Cache | ||
Build Cache 0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,8 +17,8 @@ size: 0B | |
reclaimable: 0B | ||
|
||
type: Build Cache | ||
total: | ||
active: | ||
total: 0 | ||
active: 0 | ||
size: 0B | ||
reclaimable: 0B | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,6 +35,8 @@ import ( | |
"github.com/spf13/cobra" | ||
) | ||
|
||
var errStdinConflict = errors.New("invalid argument: can't use stdin for both build context and dockerfile") | ||
|
||
type buildOptions struct { | ||
context string | ||
dockerfileName string | ||
|
@@ -55,6 +57,7 @@ type buildOptions struct { | |
isolation string | ||
quiet bool | ||
noCache bool | ||
console opts.NullableBool | ||
rm bool | ||
forceRm bool | ||
pull bool | ||
|
@@ -149,6 +152,9 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command { | |
flags.SetAnnotation("stream", "experimental", nil) | ||
flags.SetAnnotation("stream", "version", []string{"1.31"}) | ||
|
||
flags.Var(&options.console, "console", "Show console output (with buildkit only) (true, false, auto)") | ||
flags.SetAnnotation("console", "experimental", nil) | ||
flags.SetAnnotation("console", "version", []string{"1.38"}) | ||
return cmd | ||
} | ||
|
||
|
@@ -170,6 +176,10 @@ func (out *lastProgressOutput) WriteProgress(prog progress.Progress) error { | |
|
||
// nolint: gocyclo | ||
func runBuild(dockerCli command.Cli, options buildOptions) error { | ||
if os.Getenv("DOCKER_BUILDKIT") != "" { | ||
return runBuildBuildKit(dockerCli, options) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason not to use CLI flag? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AkihiroSuda because in the future I'd like to remove it. |
||
} | ||
|
||
var ( | ||
buildCtx io.ReadCloser | ||
dockerfileCtx io.ReadCloser | ||
|
@@ -188,7 +198,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error { | |
|
||
if options.dockerfileFromStdin() { | ||
if options.contextFromStdin() { | ||
return errors.New("invalid argument: can't use stdin for both build context and dockerfile") | ||
return errStdinConflict | ||
} | ||
dockerfileCtx = dockerCli.In() | ||
} | ||
|
@@ -362,37 +372,11 @@ func runBuild(dockerCli command.Cli, options buildOptions) error { | |
|
||
configFile := dockerCli.ConfigFile() | ||
authConfigs, _ := configFile.GetAllCredentials() | ||
buildOptions := types.ImageBuildOptions{ | ||
Memory: options.memory.Value(), | ||
MemorySwap: options.memorySwap.Value(), | ||
Tags: options.tags.GetAll(), | ||
SuppressOutput: options.quiet, | ||
NoCache: options.noCache, | ||
Remove: options.rm, | ||
ForceRemove: options.forceRm, | ||
PullParent: options.pull, | ||
Isolation: container.Isolation(options.isolation), | ||
CPUSetCPUs: options.cpuSetCpus, | ||
CPUSetMems: options.cpuSetMems, | ||
CPUShares: options.cpuShares, | ||
CPUQuota: options.cpuQuota, | ||
CPUPeriod: options.cpuPeriod, | ||
CgroupParent: options.cgroupParent, | ||
Dockerfile: relDockerfile, | ||
ShmSize: options.shmSize.Value(), | ||
Ulimits: options.ulimits.GetList(), | ||
BuildArgs: configFile.ParseProxyConfig(dockerCli.Client().DaemonHost(), options.buildArgs.GetAll()), | ||
AuthConfigs: authConfigs, | ||
Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()), | ||
CacheFrom: options.cacheFrom, | ||
SecurityOpt: options.securityOpt, | ||
NetworkMode: options.networkMode, | ||
Squash: options.squash, | ||
ExtraHosts: options.extraHosts.GetAll(), | ||
Target: options.target, | ||
RemoteContext: remote, | ||
Platform: options.platform, | ||
} | ||
buildOptions := imageBuildOptions(dockerCli, options) | ||
buildOptions.Version = types.BuilderV1 | ||
buildOptions.Dockerfile = relDockerfile | ||
buildOptions.AuthConfigs = authConfigs | ||
buildOptions.RemoteContext = remote | ||
|
||
if s != nil { | ||
go func() { | ||
|
@@ -416,9 +400,9 @@ func runBuild(dockerCli command.Cli, options buildOptions) error { | |
defer response.Body.Close() | ||
|
||
imageID := "" | ||
aux := func(m jsonmessage.JSONMessage) { | ||
aux := func(msg jsonmessage.JSONMessage) { | ||
var result types.BuildResult | ||
if err := json.Unmarshal(*m.Aux, &result); err != nil { | ||
if err := json.Unmarshal(*msg.Aux, &result); err != nil { | ||
fmt.Fprintf(dockerCli.Err(), "Failed to parse aux message: %s", err) | ||
} else { | ||
imageID = result.ID | ||
|
@@ -602,3 +586,35 @@ func replaceDockerfileForContentTrust(ctx context.Context, inputTarStream io.Rea | |
|
||
return pipeReader | ||
} | ||
|
||
func imageBuildOptions(dockerCli command.Cli, options buildOptions) types.ImageBuildOptions { | ||
configFile := dockerCli.ConfigFile() | ||
return types.ImageBuildOptions{ | ||
Memory: options.memory.Value(), | ||
MemorySwap: options.memorySwap.Value(), | ||
Tags: options.tags.GetAll(), | ||
SuppressOutput: options.quiet, | ||
NoCache: options.noCache, | ||
Remove: options.rm, | ||
ForceRemove: options.forceRm, | ||
PullParent: options.pull, | ||
Isolation: container.Isolation(options.isolation), | ||
CPUSetCPUs: options.cpuSetCpus, | ||
CPUSetMems: options.cpuSetMems, | ||
CPUShares: options.cpuShares, | ||
CPUQuota: options.cpuQuota, | ||
CPUPeriod: options.cpuPeriod, | ||
CgroupParent: options.cgroupParent, | ||
ShmSize: options.shmSize.Value(), | ||
Ulimits: options.ulimits.GetList(), | ||
BuildArgs: configFile.ParseProxyConfig(dockerCli.Client().DaemonHost(), options.buildArgs.GetAll()), | ||
Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()), | ||
CacheFrom: options.cacheFrom, | ||
SecurityOpt: options.securityOpt, | ||
NetworkMode: options.networkMode, | ||
Squash: options.squash, | ||
ExtraHosts: options.extraHosts.GetAll(), | ||
Target: options.target, | ||
Platform: options.platform, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,59 +81,82 @@ func ValidateContextDirectory(srcPath string, excludes []string) error { | |
}) | ||
} | ||
|
||
// GetContextFromReader will read the contents of the given reader as either a | ||
// Dockerfile or tar archive. Returns a tar archive used as a context and a | ||
// path to the Dockerfile inside the tar. | ||
func GetContextFromReader(r io.ReadCloser, dockerfileName string) (out io.ReadCloser, relDockerfile string, err error) { | ||
buf := bufio.NewReader(r) | ||
// DetectArchiveReader detects whether the input stream is an archive or a | ||
// Dockerfile and returns a buffered version of input, safe to consume in lieu | ||
// of input. If an archive is detected, isArchive is set to true, and to false | ||
// otherwise, in which case it is safe to assume input represents the contents | ||
// of a Dockerfile. | ||
func DetectArchiveReader(input io.ReadCloser) (rc io.ReadCloser, isArchive bool, err error) { | ||
buf := bufio.NewReader(input) | ||
|
||
magic, err := buf.Peek(archiveHeaderSize) | ||
if err != nil && err != io.EOF { | ||
return nil, "", errors.Errorf("failed to peek context header from STDIN: %v", err) | ||
return nil, false, errors.Errorf("failed to peek context header from STDIN: %v", err) | ||
} | ||
|
||
if IsArchive(magic) { | ||
return ioutils.NewReadCloserWrapper(buf, func() error { return r.Close() }), dockerfileName, nil | ||
} | ||
return ioutils.NewReadCloserWrapper(buf, func() error { return input.Close() }), IsArchive(magic), nil | ||
} | ||
|
||
if dockerfileName == "-" { | ||
return nil, "", errors.New("build context is not an archive") | ||
// WriteTempDockerfile writes a Dockerfile stream to a temporary file with a | ||
// name specified by DefaultDockerfileName and returns the path to the | ||
// temporary directory containing the Dockerfile. | ||
func WriteTempDockerfile(rc io.ReadCloser) (dockerfileDir string, err error) { | ||
// err is a named return value, due to the defer call below. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was thinking naming it |
||
dockerfileDir, err = ioutil.TempDir("", "docker-build-tempdockerfile-") | ||
if err != nil { | ||
return "", errors.Errorf("unable to create temporary context directory: %v", err) | ||
} | ||
defer func() { | ||
if err != nil { | ||
os.RemoveAll(dockerfileDir) | ||
} | ||
}() | ||
|
||
// Input should be read as a Dockerfile. | ||
tmpDir, err := ioutil.TempDir("", "docker-build-context-") | ||
f, err := os.Create(filepath.Join(dockerfileDir, DefaultDockerfileName)) | ||
if err != nil { | ||
return nil, "", errors.Errorf("unable to create temporary context directory: %v", err) | ||
return "", err | ||
} | ||
defer f.Close() | ||
if _, err := io.Copy(f, rc); err != nil { | ||
return "", err | ||
} | ||
return dockerfileDir, rc.Close() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If The dir is also leaked on errors in in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ijc Addressed. |
||
} | ||
|
||
f, err := os.Create(filepath.Join(tmpDir, DefaultDockerfileName)) | ||
// GetContextFromReader will read the contents of the given reader as either a | ||
// Dockerfile or tar archive. Returns a tar archive used as a context and a | ||
// path to the Dockerfile inside the tar. | ||
func GetContextFromReader(rc io.ReadCloser, dockerfileName string) (out io.ReadCloser, relDockerfile string, err error) { | ||
rc, isArchive, err := DetectArchiveReader(rc) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
_, err = io.Copy(f, buf) | ||
if err != nil { | ||
f.Close() | ||
return nil, "", err | ||
|
||
if isArchive { | ||
return rc, dockerfileName, nil | ||
} | ||
|
||
if err := f.Close(); err != nil { | ||
return nil, "", err | ||
// Input should be read as a Dockerfile. | ||
|
||
if dockerfileName == "-" { | ||
return nil, "", errors.New("build context is not an archive") | ||
} | ||
if err := r.Close(); err != nil { | ||
|
||
dockerfileDir, err := WriteTempDockerfile(rc) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
|
||
tar, err := archive.Tar(tmpDir, archive.Uncompressed) | ||
tar, err := archive.Tar(dockerfileDir, archive.Uncompressed) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
|
||
return ioutils.NewReadCloserWrapper(tar, func() error { | ||
err := tar.Close() | ||
os.RemoveAll(tmpDir) | ||
os.RemoveAll(dockerfileDir) | ||
return err | ||
}), DefaultDockerfileName, nil | ||
|
||
} | ||
|
||
// IsArchive checks for the magic bytes of a tar or any supported compression | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be only visible when the daemon is experimental right ?