瀏覽代碼

build_config job: parse dockerfile ast into config

Instead of building the actual image, `build_config` will serialize a subset of
dockerfile ast into *runconfig.Config

Docker-DCO-1.1-Signed-off-by: Daniel, Dao Quang Minh <dqminh89@gmail.com> (github: dqminh)

Docker-DCO-1.1-Signed-off-by: Dan Walsh <dwalsh@redhat.com> (github: rhatdan)
Dan Walsh 10 年之前
父節點
當前提交
7f091eca70
共有 3 個文件被更改,包括 69 次插入0 次删除
  1. 5 0
      builder/evaluator.go
  2. 3 0
      builder/internals.go
  3. 61 0
      builder/job.go

+ 5 - 0
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
 

+ 3 - 0
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")
 	}

+ 61 - 0
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
+}