Browse code

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 authored on 2015/01/10 06:07:00
Showing 3 changed files
... ...
@@ -96,6 +96,11 @@ type Builder struct {
96 96
 	ForceRemove bool
97 97
 	Pull        bool
98 98
 
99
+	// set this to true if we want the builder to not commit between steps.
100
+	// This is useful when we only want to use the evaluator table to generate
101
+	// the final configs of the Dockerfile but dont want the layers
102
+	disableCommit bool
103
+
99 104
 	AuthConfig     *registry.AuthConfig
100 105
 	AuthConfigFile *registry.ConfigFile
101 106
 
... ...
@@ -60,6 +60,9 @@ func (b *Builder) readContext(context io.Reader) error {
60 60
 }
61 61
 
62 62
 func (b *Builder) commit(id string, autoCmd []string, comment string) error {
63
+	if b.disableCommit {
64
+		return nil
65
+	}
63 66
 	if b.image == "" && !b.noBaseImage {
64 67
 		return fmt.Errorf("Please provide a source image with `from` prior to commit")
65 68
 	}
... ...
@@ -1,12 +1,16 @@
1 1
 package builder
2 2
 
3 3
 import (
4
+	"bytes"
5
+	"encoding/json"
6
+	"fmt"
4 7
 	"io"
5 8
 	"io/ioutil"
6 9
 	"os"
7 10
 	"os/exec"
8 11
 
9 12
 	"github.com/docker/docker/api"
13
+	"github.com/docker/docker/builder/parser"
10 14
 	"github.com/docker/docker/daemon"
11 15
 	"github.com/docker/docker/engine"
12 16
 	"github.com/docker/docker/graph"
... ...
@@ -14,6 +18,7 @@ import (
14 14
 	"github.com/docker/docker/pkg/parsers"
15 15
 	"github.com/docker/docker/pkg/urlutil"
16 16
 	"github.com/docker/docker/registry"
17
+	"github.com/docker/docker/runconfig"
17 18
 	"github.com/docker/docker/utils"
18 19
 )
19 20
 
... ...
@@ -24,6 +29,7 @@ type BuilderJob struct {
24 24
 
25 25
 func (b *BuilderJob) Install() {
26 26
 	b.Engine.Register("build", b.CmdBuild)
27
+	b.Engine.Register("build_config", b.CmdBuildConfig)
27 28
 }
28 29
 
29 30
 func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
... ...
@@ -138,3 +144,58 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
138 138
 	}
139 139
 	return engine.StatusOK
140 140
 }
141
+
142
+func (b *BuilderJob) CmdBuildConfig(job *engine.Job) engine.Status {
143
+	if len(job.Args) != 0 {
144
+		return job.Errorf("Usage: %s\n", job.Name)
145
+	}
146
+	var (
147
+		validCmd = map[string]struct{}{
148
+			"entrypoint": {},
149
+			"cmd":        {},
150
+			"user":       {},
151
+			"workdir":    {},
152
+			"env":        {},
153
+			"volume":     {},
154
+			"expose":     {},
155
+			"onbuild":    {},
156
+		}
157
+
158
+		changes   = job.Getenv("changes")
159
+		newConfig runconfig.Config
160
+	)
161
+
162
+	if err := job.GetenvJson("config", &newConfig); err != nil {
163
+		return job.Error(err)
164
+	}
165
+
166
+	ast, err := parser.Parse(bytes.NewBufferString(changes))
167
+	if err != nil {
168
+		return job.Error(err)
169
+	}
170
+
171
+	builder := &Builder{
172
+		Daemon:        b.Daemon,
173
+		Engine:        b.Engine,
174
+		Config:        &newConfig,
175
+		OutStream:     ioutil.Discard,
176
+		ErrStream:     ioutil.Discard,
177
+		disableCommit: true,
178
+	}
179
+
180
+	for i, n := range ast.Children {
181
+		cmd := n.Value
182
+		if _, ok := validCmd[cmd]; ok {
183
+			if err := builder.dispatch(i, n); err != nil {
184
+				return job.Error(err)
185
+			}
186
+		} else {
187
+			fmt.Fprintf(builder.ErrStream, "# Skipping serialization of instruction %s\n", strings.ToUpper(cmd))
188
+		}
189
+	}
190
+
191
+	if err := json.NewEncoder(job.Stdout).Encode(builder.Config); err != nil {
192
+		return job.Error(err)
193
+	}
194
+	return engine.StatusOK
195
+}