mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
15924f2385
Closes #10807 Adds support for `dockerfile` ONLY when `Dockerfile` can't be found. If we're building from a Dockerfile via stdin/URL then always download it a `Dockerfile` and ignore the -f flag. Signed-off-by: Doug Davis <dug@us.ibm.com>
206 lines
4.9 KiB
Go
206 lines
4.9 KiB
Go
package builder
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/api"
|
|
"github.com/docker/docker/builder/parser"
|
|
"github.com/docker/docker/daemon"
|
|
"github.com/docker/docker/engine"
|
|
"github.com/docker/docker/graph"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/docker/docker/pkg/parsers"
|
|
"github.com/docker/docker/pkg/urlutil"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/docker/docker/runconfig"
|
|
"github.com/docker/docker/utils"
|
|
)
|
|
|
|
// whitelist of commands allowed for a commit/import
|
|
var validCommitCommands = map[string]bool{
|
|
"entrypoint": true,
|
|
"cmd": true,
|
|
"user": true,
|
|
"workdir": true,
|
|
"env": true,
|
|
"volume": true,
|
|
"expose": true,
|
|
"onbuild": true,
|
|
}
|
|
|
|
type BuilderJob struct {
|
|
Engine *engine.Engine
|
|
Daemon *daemon.Daemon
|
|
}
|
|
|
|
func (b *BuilderJob) Install() {
|
|
b.Engine.Register("build", b.CmdBuild)
|
|
b.Engine.Register("build_config", b.CmdBuildConfig)
|
|
}
|
|
|
|
func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
|
|
if len(job.Args) != 0 {
|
|
return job.Errorf("Usage: %s\n", job.Name)
|
|
}
|
|
var (
|
|
dockerfileName = job.Getenv("dockerfile")
|
|
remoteURL = job.Getenv("remote")
|
|
repoName = job.Getenv("t")
|
|
suppressOutput = job.GetenvBool("q")
|
|
noCache = job.GetenvBool("nocache")
|
|
rm = job.GetenvBool("rm")
|
|
forceRm = job.GetenvBool("forcerm")
|
|
pull = job.GetenvBool("pull")
|
|
authConfig = ®istry.AuthConfig{}
|
|
configFile = ®istry.ConfigFile{}
|
|
tag string
|
|
context io.ReadCloser
|
|
)
|
|
|
|
job.GetenvJson("authConfig", authConfig)
|
|
job.GetenvJson("configFile", configFile)
|
|
|
|
repoName, tag = parsers.ParseRepositoryTag(repoName)
|
|
if repoName != "" {
|
|
if err := registry.ValidateRepositoryName(repoName); err != nil {
|
|
return job.Error(err)
|
|
}
|
|
if len(tag) > 0 {
|
|
if err := graph.ValidateTagName(tag); err != nil {
|
|
return job.Error(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if remoteURL == "" {
|
|
context = ioutil.NopCloser(job.Stdin)
|
|
} else if urlutil.IsGitURL(remoteURL) {
|
|
if !urlutil.IsGitTransport(remoteURL) {
|
|
remoteURL = "https://" + remoteURL
|
|
}
|
|
root, err := ioutil.TempDir("", "docker-build-git")
|
|
if err != nil {
|
|
return job.Error(err)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
|
|
if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
|
|
return job.Errorf("Error trying to use git: %s (%s)", err, output)
|
|
}
|
|
|
|
c, err := archive.Tar(root, archive.Uncompressed)
|
|
if err != nil {
|
|
return job.Error(err)
|
|
}
|
|
context = c
|
|
} else if urlutil.IsURL(remoteURL) {
|
|
f, err := utils.Download(remoteURL)
|
|
if err != nil {
|
|
return job.Error(err)
|
|
}
|
|
defer f.Body.Close()
|
|
dockerFile, err := ioutil.ReadAll(f.Body)
|
|
if err != nil {
|
|
return job.Error(err)
|
|
}
|
|
|
|
// When we're downloading just a Dockerfile put it in
|
|
// the default name - don't allow the client to move/specify it
|
|
dockerfileName = api.DefaultDockerfileName
|
|
|
|
c, err := archive.Generate(dockerfileName, string(dockerFile))
|
|
if err != nil {
|
|
return job.Error(err)
|
|
}
|
|
context = c
|
|
}
|
|
defer context.Close()
|
|
|
|
sf := utils.NewStreamFormatter(job.GetenvBool("json"))
|
|
|
|
builder := &Builder{
|
|
Daemon: b.Daemon,
|
|
Engine: b.Engine,
|
|
OutStream: &utils.StdoutFormater{
|
|
Writer: job.Stdout,
|
|
StreamFormatter: sf,
|
|
},
|
|
ErrStream: &utils.StderrFormater{
|
|
Writer: job.Stdout,
|
|
StreamFormatter: sf,
|
|
},
|
|
Verbose: !suppressOutput,
|
|
UtilizeCache: !noCache,
|
|
Remove: rm,
|
|
ForceRemove: forceRm,
|
|
Pull: pull,
|
|
OutOld: job.Stdout,
|
|
StreamFormatter: sf,
|
|
AuthConfig: authConfig,
|
|
AuthConfigFile: configFile,
|
|
dockerfileName: dockerfileName,
|
|
}
|
|
|
|
id, err := builder.Run(context)
|
|
if err != nil {
|
|
return job.Error(err)
|
|
}
|
|
|
|
if repoName != "" {
|
|
b.Daemon.Repositories().Set(repoName, tag, id, true)
|
|
}
|
|
return engine.StatusOK
|
|
}
|
|
|
|
func (b *BuilderJob) CmdBuildConfig(job *engine.Job) engine.Status {
|
|
if len(job.Args) != 0 {
|
|
return job.Errorf("Usage: %s\n", job.Name)
|
|
}
|
|
|
|
var (
|
|
changes = job.GetenvList("changes")
|
|
newConfig runconfig.Config
|
|
)
|
|
|
|
if err := job.GetenvJson("config", &newConfig); err != nil {
|
|
return job.Error(err)
|
|
}
|
|
|
|
ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
|
|
if err != nil {
|
|
return job.Error(err)
|
|
}
|
|
|
|
// ensure that the commands are valid
|
|
for _, n := range ast.Children {
|
|
if !validCommitCommands[n.Value] {
|
|
return job.Errorf("%s is not a valid change command", n.Value)
|
|
}
|
|
}
|
|
|
|
builder := &Builder{
|
|
Daemon: b.Daemon,
|
|
Engine: b.Engine,
|
|
Config: &newConfig,
|
|
OutStream: ioutil.Discard,
|
|
ErrStream: ioutil.Discard,
|
|
disableCommit: true,
|
|
}
|
|
|
|
for i, n := range ast.Children {
|
|
if err := builder.dispatch(i, n); err != nil {
|
|
return job.Error(err)
|
|
}
|
|
}
|
|
|
|
if err := json.NewEncoder(job.Stdout).Encode(builder.Config); err != nil {
|
|
return job.Error(err)
|
|
}
|
|
return engine.StatusOK
|
|
}
|