diff --git a/builder/evaluator.go b/builder/evaluator.go index e027c8bceb..3ce5f0926f 100644 --- a/builder/evaluator.go +++ b/builder/evaluator.go @@ -96,6 +96,11 @@ type Builder struct { ForceRemove bool Pull bool + // set this to true if we want the builder to not commit between steps. + // This is useful when we only want to use the evaluator table to generate + // the final configs of the Dockerfile but dont want the layers + disableCommit bool + AuthConfig *registry.AuthConfig AuthConfigFile *registry.ConfigFile diff --git a/builder/internals.go b/builder/internals.go index c315d2f949..9668c5f3a3 100644 --- a/builder/internals.go +++ b/builder/internals.go @@ -60,6 +60,9 @@ func (b *Builder) readContext(context io.Reader) error { } func (b *Builder) commit(id string, autoCmd []string, comment string) error { + if b.disableCommit { + return nil + } if b.image == "" && !b.noBaseImage { return fmt.Errorf("Please provide a source image with `from` prior to commit") } diff --git a/builder/job.go b/builder/job.go index 53490b7e57..edc50c53c3 100644 --- a/builder/job.go +++ b/builder/job.go @@ -1,12 +1,16 @@ package builder import ( + "bytes" + "encoding/json" + "fmt" "io" "io/ioutil" "os" "os/exec" "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" @@ -14,6 +18,7 @@ import ( "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" ) @@ -24,6 +29,7 @@ type BuilderJob struct { 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 { @@ -138,3 +144,58 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status { } 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 ( + validCmd = map[string]struct{}{ + "entrypoint": {}, + "cmd": {}, + "user": {}, + "workdir": {}, + "env": {}, + "volume": {}, + "expose": {}, + "onbuild": {}, + } + + changes = job.Getenv("changes") + newConfig runconfig.Config + ) + + if err := job.GetenvJson("config", &newConfig); err != nil { + return job.Error(err) + } + + ast, err := parser.Parse(bytes.NewBufferString(changes)) + if err != nil { + return job.Error(err) + } + + builder := &Builder{ + Daemon: b.Daemon, + Engine: b.Engine, + Config: &newConfig, + OutStream: ioutil.Discard, + ErrStream: ioutil.Discard, + disableCommit: true, + } + + for i, n := range ast.Children { + cmd := n.Value + if _, ok := validCmd[cmd]; ok { + if err := builder.dispatch(i, n); err != nil { + return job.Error(err) + } + } else { + fmt.Fprintf(builder.ErrStream, "# Skipping serialization of instruction %s\n", strings.ToUpper(cmd)) + } + } + + if err := json.NewEncoder(job.Stdout).Encode(builder.Config); err != nil { + return job.Error(err) + } + return engine.StatusOK +}