Browse code

builder: fix references to jobs in daemon, make builder a first class package referring to evaluator

Docker-DCO-1.1-Signed-off-by: Erik Hollensbe <github@hollensbe.org> (github: erikh)

Erik Hollensbe authored on 2014/08/12 00:44:31
Showing 9 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,34 @@
0
+package builder
1
+
2
+import (
3
+	"github.com/docker/docker/builder/evaluator"
4
+	"github.com/docker/docker/nat"
5
+	"github.com/docker/docker/runconfig"
6
+)
7
+
8
+// Create a new builder.
9
+func NewBuilder(opts *evaluator.BuildOpts) *evaluator.BuildFile {
10
+	return &evaluator.BuildFile{
11
+		Dockerfile:    nil,
12
+		Env:           evaluator.EnvMap{},
13
+		Config:        initRunConfig(),
14
+		Options:       opts,
15
+		TmpContainers: evaluator.UniqueMap{},
16
+		TmpImages:     evaluator.UniqueMap{},
17
+	}
18
+}
19
+
20
+func initRunConfig() *runconfig.Config {
21
+	return &runconfig.Config{
22
+		PortSpecs: []string{},
23
+		// FIXME(erikh) this should be a type that lives in runconfig
24
+		ExposedPorts: map[nat.Port]struct{}{},
25
+		Env:          []string{},
26
+		Cmd:          []string{},
27
+
28
+		// FIXME(erikh) this should also be a type in runconfig
29
+		Volumes:    map[string]struct{}{},
30
+		Entrypoint: []string{"/bin/sh", "-c"},
31
+		OnBuild:    []string{},
32
+	}
33
+}
... ...
@@ -18,7 +18,7 @@ import (
18 18
 )
19 19
 
20 20
 // dispatch with no layer / parsing. This is effectively not a command.
21
-func nullDispatch(b *buildFile, args []string) error {
21
+func nullDispatch(b *BuildFile, args []string) error {
22 22
 	return nil
23 23
 }
24 24
 
... ...
@@ -27,7 +27,7 @@ func nullDispatch(b *buildFile, args []string) error {
27 27
 // Sets the environment variable foo to bar, also makes interpolation
28 28
 // in the dockerfile available from the next statement on via ${foo}.
29 29
 //
30
-func env(b *buildFile, args []string) error {
30
+func env(b *BuildFile, args []string) error {
31 31
 	if len(args) != 2 {
32 32
 		return fmt.Errorf("ENV accepts two arguments")
33 33
 	}
... ...
@@ -35,22 +35,22 @@ func env(b *buildFile, args []string) error {
35 35
 	// the duplication here is intended to ease the replaceEnv() call's env
36 36
 	// handling. This routine gets much shorter with the denormalization here.
37 37
 	key := args[0]
38
-	b.env[key] = args[1]
39
-	b.config.Env = append(b.config.Env, strings.Join([]string{key, b.env[key]}, "="))
38
+	b.Env[key] = args[1]
39
+	b.Config.Env = append(b.Config.Env, strings.Join([]string{key, b.Env[key]}, "="))
40 40
 
41
-	return b.commit("", b.config.Cmd, fmt.Sprintf("ENV %s=%s", key, b.env[key]))
41
+	return b.commit("", b.Config.Cmd, fmt.Sprintf("ENV %s=%s", key, b.Env[key]))
42 42
 }
43 43
 
44 44
 // MAINTAINER some text <maybe@an.email.address>
45 45
 //
46 46
 // Sets the maintainer metadata.
47
-func maintainer(b *buildFile, args []string) error {
47
+func maintainer(b *BuildFile, args []string) error {
48 48
 	if len(args) != 1 {
49 49
 		return fmt.Errorf("MAINTAINER requires only one argument")
50 50
 	}
51 51
 
52 52
 	b.maintainer = args[0]
53
-	return b.commit("", b.config.Cmd, fmt.Sprintf("MAINTAINER %s", b.maintainer))
53
+	return b.commit("", b.Config.Cmd, fmt.Sprintf("MAINTAINER %s", b.maintainer))
54 54
 }
55 55
 
56 56
 // ADD foo /path
... ...
@@ -58,7 +58,7 @@ func maintainer(b *buildFile, args []string) error {
58 58
 // Add the file 'foo' to '/path'. Tarball and Remote URL (git, http) handling
59 59
 // exist here. If you do not wish to have this automatic handling, use COPY.
60 60
 //
61
-func add(b *buildFile, args []string) error {
61
+func add(b *BuildFile, args []string) error {
62 62
 	if len(args) != 2 {
63 63
 		return fmt.Errorf("ADD requires two arguments")
64 64
 	}
... ...
@@ -70,7 +70,7 @@ func add(b *buildFile, args []string) error {
70 70
 //
71 71
 // Same as 'ADD' but without the tar and remote url handling.
72 72
 //
73
-func dispatchCopy(b *buildFile, args []string) error {
73
+func dispatchCopy(b *BuildFile, args []string) error {
74 74
 	if len(args) != 2 {
75 75
 		return fmt.Errorf("COPY requires two arguments")
76 76
 	}
... ...
@@ -82,16 +82,16 @@ func dispatchCopy(b *buildFile, args []string) error {
82 82
 //
83 83
 // This sets the image the dockerfile will build on top of.
84 84
 //
85
-func from(b *buildFile, args []string) error {
85
+func from(b *BuildFile, args []string) error {
86 86
 	if len(args) != 1 {
87 87
 		return fmt.Errorf("FROM requires one argument")
88 88
 	}
89 89
 
90 90
 	name := args[0]
91 91
 
92
-	image, err := b.options.Daemon.Repositories().LookupImage(name)
92
+	image, err := b.Options.Daemon.Repositories().LookupImage(name)
93 93
 	if err != nil {
94
-		if b.options.Daemon.Graph().IsNotExist(err) {
94
+		if b.Options.Daemon.Graph().IsNotExist(err) {
95 95
 			image, err = b.pullImage(name)
96 96
 		}
97 97
 
... ...
@@ -114,7 +114,7 @@ func from(b *buildFile, args []string) error {
114 114
 // special cases. search for 'OnBuild' in internals.go for additional special
115 115
 // cases.
116 116
 //
117
-func onbuild(b *buildFile, args []string) error {
117
+func onbuild(b *BuildFile, args []string) error {
118 118
 	triggerInstruction := strings.ToUpper(strings.TrimSpace(args[0]))
119 119
 	switch triggerInstruction {
120 120
 	case "ONBUILD":
... ...
@@ -125,15 +125,15 @@ func onbuild(b *buildFile, args []string) error {
125 125
 
126 126
 	trigger := strings.Join(args, " ")
127 127
 
128
-	b.config.OnBuild = append(b.config.OnBuild, trigger)
129
-	return b.commit("", b.config.Cmd, fmt.Sprintf("ONBUILD %s", trigger))
128
+	b.Config.OnBuild = append(b.Config.OnBuild, trigger)
129
+	return b.commit("", b.Config.Cmd, fmt.Sprintf("ONBUILD %s", trigger))
130 130
 }
131 131
 
132 132
 // WORKDIR /tmp
133 133
 //
134 134
 // Set the working directory for future RUN/CMD/etc statements.
135 135
 //
136
-func workdir(b *buildFile, args []string) error {
136
+func workdir(b *BuildFile, args []string) error {
137 137
 	if len(args) != 1 {
138 138
 		return fmt.Errorf("WORKDIR requires exactly one argument")
139 139
 	}
... ...
@@ -141,15 +141,15 @@ func workdir(b *buildFile, args []string) error {
141 141
 	workdir := args[0]
142 142
 
143 143
 	if workdir[0] == '/' {
144
-		b.config.WorkingDir = workdir
144
+		b.Config.WorkingDir = workdir
145 145
 	} else {
146
-		if b.config.WorkingDir == "" {
147
-			b.config.WorkingDir = "/"
146
+		if b.Config.WorkingDir == "" {
147
+			b.Config.WorkingDir = "/"
148 148
 		}
149
-		b.config.WorkingDir = filepath.Join(b.config.WorkingDir, workdir)
149
+		b.Config.WorkingDir = filepath.Join(b.Config.WorkingDir, workdir)
150 150
 	}
151 151
 
152
-	return b.commit("", b.config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
152
+	return b.commit("", b.Config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
153 153
 }
154 154
 
155 155
 // RUN some command yo
... ...
@@ -161,7 +161,7 @@ func workdir(b *buildFile, args []string) error {
161 161
 // RUN echo hi          # sh -c echo hi
162 162
 // RUN [ "echo", "hi" ] # echo hi
163 163
 //
164
-func run(b *buildFile, args []string) error {
164
+func run(b *BuildFile, args []string) error {
165 165
 	if len(args) == 1 { // literal string command, not an exec array
166 166
 		args = append([]string{"/bin/sh", "-c"}, args[0])
167 167
 	}
... ...
@@ -175,14 +175,14 @@ func run(b *buildFile, args []string) error {
175 175
 		return err
176 176
 	}
177 177
 
178
-	cmd := b.config.Cmd
178
+	cmd := b.Config.Cmd
179 179
 	// set Cmd manually, this is special case only for Dockerfiles
180
-	b.config.Cmd = config.Cmd
181
-	runconfig.Merge(b.config, config)
180
+	b.Config.Cmd = config.Cmd
181
+	runconfig.Merge(b.Config, config)
182 182
 
183
-	defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
183
+	defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
184 184
 
185
-	utils.Debugf("Command to be executed: %v", b.config.Cmd)
185
+	utils.Debugf("Command to be executed: %v", b.Config.Cmd)
186 186
 
187 187
 	hit, err := b.probeCache()
188 188
 	if err != nil {
... ...
@@ -217,13 +217,13 @@ func run(b *buildFile, args []string) error {
217 217
 // Set the default command to run in the container (which may be empty).
218 218
 // Argument handling is the same as RUN.
219 219
 //
220
-func cmd(b *buildFile, args []string) error {
220
+func cmd(b *BuildFile, args []string) error {
221 221
 	if len(args) < 2 {
222 222
 		args = append([]string{"/bin/sh", "-c"}, args...)
223 223
 	}
224 224
 
225
-	b.config.Cmd = args
226
-	if err := b.commit("", b.config.Cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
225
+	b.Config.Cmd = args
226
+	if err := b.commit("", b.Config.Cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
227 227
 		return err
228 228
 	}
229 229
 
... ...
@@ -236,17 +236,17 @@ func cmd(b *buildFile, args []string) error {
236 236
 // Set the entrypoint (which defaults to sh -c) to /usr/sbin/nginx. Will
237 237
 // accept the CMD as the arguments to /usr/sbin/nginx.
238 238
 //
239
-// Handles command processing similar to CMD and RUN, only b.config.Entrypoint
239
+// Handles command processing similar to CMD and RUN, only b.Config.Entrypoint
240 240
 // is initialized at NewBuilder time instead of through argument parsing.
241 241
 //
242
-func entrypoint(b *buildFile, args []string) error {
243
-	b.config.Entrypoint = args
242
+func entrypoint(b *BuildFile, args []string) error {
243
+	b.Config.Entrypoint = args
244 244
 
245 245
 	// if there is no cmd in current Dockerfile - cleanup cmd
246 246
 	if !b.cmdSet {
247
-		b.config.Cmd = nil
247
+		b.Config.Cmd = nil
248 248
 	}
249
-	if err := b.commit("", b.config.Cmd, fmt.Sprintf("ENTRYPOINT %v", entrypoint)); err != nil {
249
+	if err := b.commit("", b.Config.Cmd, fmt.Sprintf("ENTRYPOINT %v", entrypoint)); err != nil {
250 250
 		return err
251 251
 	}
252 252
 	return nil
... ...
@@ -255,28 +255,28 @@ func entrypoint(b *buildFile, args []string) error {
255 255
 // EXPOSE 6667/tcp 7000/tcp
256 256
 //
257 257
 // Expose ports for links and port mappings. This all ends up in
258
-// b.config.ExposedPorts for runconfig.
258
+// b.Config.ExposedPorts for runconfig.
259 259
 //
260
-func expose(b *buildFile, args []string) error {
260
+func expose(b *BuildFile, args []string) error {
261 261
 	portsTab := args
262 262
 
263
-	if b.config.ExposedPorts == nil {
264
-		b.config.ExposedPorts = make(nat.PortSet)
263
+	if b.Config.ExposedPorts == nil {
264
+		b.Config.ExposedPorts = make(nat.PortSet)
265 265
 	}
266 266
 
267
-	ports, _, err := nat.ParsePortSpecs(append(portsTab, b.config.PortSpecs...))
267
+	ports, _, err := nat.ParsePortSpecs(append(portsTab, b.Config.PortSpecs...))
268 268
 	if err != nil {
269 269
 		return err
270 270
 	}
271 271
 
272 272
 	for port := range ports {
273
-		if _, exists := b.config.ExposedPorts[port]; !exists {
274
-			b.config.ExposedPorts[port] = struct{}{}
273
+		if _, exists := b.Config.ExposedPorts[port]; !exists {
274
+			b.Config.ExposedPorts[port] = struct{}{}
275 275
 		}
276 276
 	}
277
-	b.config.PortSpecs = nil
277
+	b.Config.PortSpecs = nil
278 278
 
279
-	return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
279
+	return b.commit("", b.Config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
280 280
 }
281 281
 
282 282
 // USER foo
... ...
@@ -284,13 +284,13 @@ func expose(b *buildFile, args []string) error {
284 284
 // Set the user to 'foo' for future commands and when running the
285 285
 // ENTRYPOINT/CMD at container run time.
286 286
 //
287
-func user(b *buildFile, args []string) error {
287
+func user(b *BuildFile, args []string) error {
288 288
 	if len(args) != 1 {
289 289
 		return fmt.Errorf("USER requires exactly one argument")
290 290
 	}
291 291
 
292
-	b.config.User = args[0]
293
-	return b.commit("", b.config.Cmd, fmt.Sprintf("USER %v", args))
292
+	b.Config.User = args[0]
293
+	return b.commit("", b.Config.Cmd, fmt.Sprintf("USER %v", args))
294 294
 }
295 295
 
296 296
 // VOLUME /foo
... ...
@@ -298,26 +298,26 @@ func user(b *buildFile, args []string) error {
298 298
 // Expose the volume /foo for use. Will also accept the JSON form, but either
299 299
 // way requires exactly one argument.
300 300
 //
301
-func volume(b *buildFile, args []string) error {
301
+func volume(b *BuildFile, args []string) error {
302 302
 	if len(args) != 1 {
303 303
 		return fmt.Errorf("Volume cannot be empty")
304 304
 	}
305 305
 
306 306
 	volume := args
307 307
 
308
-	if b.config.Volumes == nil {
309
-		b.config.Volumes = map[string]struct{}{}
308
+	if b.Config.Volumes == nil {
309
+		b.Config.Volumes = map[string]struct{}{}
310 310
 	}
311 311
 	for _, v := range volume {
312
-		b.config.Volumes[v] = struct{}{}
312
+		b.Config.Volumes[v] = struct{}{}
313 313
 	}
314
-	if err := b.commit("", b.config.Cmd, fmt.Sprintf("VOLUME %s", args)); err != nil {
314
+	if err := b.commit("", b.Config.Cmd, fmt.Sprintf("VOLUME %s", args)); err != nil {
315 315
 		return err
316 316
 	}
317 317
 	return nil
318 318
 }
319 319
 
320 320
 // INSERT is no longer accepted, but we still parse it.
321
-func insert(b *buildFile, args []string) error {
321
+func insert(b *BuildFile, args []string) error {
322 322
 	return fmt.Errorf("INSERT has been deprecated. Please use ADD instead")
323 323
 }
... ...
@@ -32,21 +32,23 @@ import (
32 32
 	"github.com/docker/docker/builder/parser"
33 33
 	"github.com/docker/docker/daemon"
34 34
 	"github.com/docker/docker/engine"
35
-	"github.com/docker/docker/nat"
36 35
 	"github.com/docker/docker/pkg/tarsum"
37 36
 	"github.com/docker/docker/registry"
38 37
 	"github.com/docker/docker/runconfig"
39 38
 	"github.com/docker/docker/utils"
40 39
 )
41 40
 
41
+type EnvMap map[string]string
42
+type UniqueMap map[string]struct{}
43
+
42 44
 var (
43 45
 	ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty")
44 46
 )
45 47
 
46
-var evaluateTable map[string]func(*buildFile, []string) error
48
+var evaluateTable map[string]func(*BuildFile, []string) error
47 49
 
48 50
 func init() {
49
-	evaluateTable = map[string]func(*buildFile, []string) error{
51
+	evaluateTable = map[string]func(*BuildFile, []string) error{
50 52
 		"env":            env,
51 53
 		"maintainer":     maintainer,
52 54
 		"add":            add,
... ...
@@ -65,25 +67,24 @@ func init() {
65 65
 	}
66 66
 }
67 67
 
68
-type envMap map[string]string
69
-type uniqueMap map[string]struct{}
70
-
71 68
 // internal struct, used to maintain configuration of the Dockerfile's
72 69
 // processing as it evaluates the parsing result.
73
-type buildFile struct {
74
-	dockerfile  *parser.Node      // the syntax tree of the dockerfile
75
-	env         envMap            // map of environment variables
76
-	image       string            // image name for commit processing
77
-	config      *runconfig.Config // runconfig for cmd, run, entrypoint etc.
78
-	options     *BuildOpts        // see below
79
-	maintainer  string            // maintainer name. could probably be removed.
80
-	cmdSet      bool              // indicates is CMD was set in current Dockerfile
81
-	context     *tarsum.TarSum    // the context is a tarball that is uploaded by the client
82
-	contextPath string            // the path of the temporary directory the local context is unpacked to (server side)
70
+type BuildFile struct {
71
+	Dockerfile *parser.Node      // the syntax tree of the dockerfile
72
+	Env        EnvMap            // map of environment variables
73
+	Config     *runconfig.Config // runconfig for cmd, run, entrypoint etc.
74
+	Options    *BuildOpts        // see below
83 75
 
84 76
 	// both of these are controlled by the Remove and ForceRemove options in BuildOpts
85
-	tmpContainers uniqueMap // a map of containers used for removes
86
-	tmpImages     uniqueMap // a map of images used for removes
77
+	TmpContainers UniqueMap // a map of containers used for removes
78
+	TmpImages     UniqueMap // a map of images used for removes
79
+
80
+	image       string         // image name for commit processing
81
+	maintainer  string         // maintainer name. could probably be removed.
82
+	cmdSet      bool           // indicates is CMD was set in current Dockerfile
83
+	context     *tarsum.TarSum // the context is a tarball that is uploaded by the client
84
+	contextPath string         // the path of the temporary directory the local context is unpacked to (server side)
85
+
87 86
 }
88 87
 
89 88
 type BuildOpts struct {
... ...
@@ -110,18 +111,6 @@ type BuildOpts struct {
110 110
 	StreamFormatter *utils.StreamFormatter
111 111
 }
112 112
 
113
-// Create a new builder.
114
-func NewBuilder(opts *BuildOpts) (*buildFile, error) {
115
-	return &buildFile{
116
-		dockerfile:    nil,
117
-		env:           envMap{},
118
-		config:        initRunConfig(),
119
-		options:       opts,
120
-		tmpContainers: make(uniqueMap),
121
-		tmpImages:     make(uniqueMap),
122
-	}, nil
123
-}
124
-
125 113
 // Run the builder with the context. This is the lynchpin of this package. This
126 114
 // will (barring errors):
127 115
 //
... ...
@@ -134,7 +123,7 @@ func NewBuilder(opts *BuildOpts) (*buildFile, error) {
134 134
 //   processing.
135 135
 // * Print a happy message and return the image ID.
136 136
 //
137
-func (b *buildFile) Run(context io.Reader) (string, error) {
137
+func (b *BuildFile) Run(context io.Reader) (string, error) {
138 138
 	if err := b.readContext(context); err != nil {
139 139
 		return "", err
140 140
 	}
... ...
@@ -155,16 +144,16 @@ func (b *buildFile) Run(context io.Reader) (string, error) {
155 155
 		return "", err
156 156
 	}
157 157
 
158
-	b.dockerfile = ast
158
+	b.Dockerfile = ast
159 159
 
160
-	for i, n := range b.dockerfile.Children {
160
+	for i, n := range b.Dockerfile.Children {
161 161
 		if err := b.dispatch(i, n); err != nil {
162
-			if b.options.ForceRemove {
163
-				b.clearTmp(b.tmpContainers)
162
+			if b.Options.ForceRemove {
163
+				b.clearTmp(b.TmpContainers)
164 164
 			}
165 165
 			return "", err
166
-		} else if b.options.Remove {
167
-			b.clearTmp(b.tmpContainers)
166
+		} else if b.Options.Remove {
167
+			b.clearTmp(b.TmpContainers)
168 168
 		}
169 169
 	}
170 170
 
... ...
@@ -172,32 +161,17 @@ func (b *buildFile) Run(context io.Reader) (string, error) {
172 172
 		return "", fmt.Errorf("No image was generated. Is your Dockerfile empty?\n")
173 173
 	}
174 174
 
175
-	fmt.Fprintf(b.options.OutStream, "Successfully built %s\n", utils.TruncateID(b.image))
175
+	fmt.Fprintf(b.Options.OutStream, "Successfully built %s\n", utils.TruncateID(b.image))
176 176
 	return b.image, nil
177 177
 }
178 178
 
179
-func initRunConfig() *runconfig.Config {
180
-	return &runconfig.Config{
181
-		PortSpecs: []string{},
182
-		// FIXME(erikh) this should be a type that lives in runconfig
183
-		ExposedPorts: map[nat.Port]struct{}{},
184
-		Env:          []string{},
185
-		Cmd:          []string{},
186
-
187
-		// FIXME(erikh) this should also be a type in runconfig
188
-		Volumes:    map[string]struct{}{},
189
-		Entrypoint: []string{"/bin/sh", "-c"},
190
-		OnBuild:    []string{},
191
-	}
192
-}
193
-
194 179
 // This method is the entrypoint to all statement handling routines.
195 180
 //
196 181
 // Almost all nodes will have this structure:
197 182
 // Child[Node, Node, Node] where Child is from parser.Node.Children and each
198 183
 // node comes from parser.Node.Next. This forms a "line" with a statement and
199 184
 // arguments and we process them in this normalized form by hitting
200
-// evaluateTable with the leaf nodes of the command and the buildFile object.
185
+// evaluateTable with the leaf nodes of the command and the BuildFile object.
201 186
 //
202 187
 // ONBUILD is a special case; in this case the parser will emit:
203 188
 // Child[Node, Child[Node, Node...]] where the first node is the literal
... ...
@@ -205,12 +179,12 @@ func initRunConfig() *runconfig.Config {
205 205
 // such as `RUN` in ONBUILD RUN foo. There is special case logic in here to
206 206
 // deal with that, at least until it becomes more of a general concern with new
207 207
 // features.
208
-func (b *buildFile) dispatch(stepN int, ast *parser.Node) error {
208
+func (b *BuildFile) dispatch(stepN int, ast *parser.Node) error {
209 209
 	cmd := ast.Value
210 210
 	strs := []string{}
211 211
 
212 212
 	if cmd == "onbuild" {
213
-		fmt.Fprintf(b.options.OutStream, "%#v\n", ast.Next.Children[0].Value)
213
+		fmt.Fprintf(b.Options.OutStream, "%#v\n", ast.Next.Children[0].Value)
214 214
 		ast = ast.Next.Children[0]
215 215
 		strs = append(strs, ast.Value)
216 216
 	}
... ...
@@ -220,7 +194,7 @@ func (b *buildFile) dispatch(stepN int, ast *parser.Node) error {
220 220
 		strs = append(strs, replaceEnv(b, ast.Value))
221 221
 	}
222 222
 
223
-	fmt.Fprintf(b.options.OutStream, "Step %d : %s %s\n", stepN, strings.ToUpper(cmd), strings.Join(strs, " "))
223
+	fmt.Fprintf(b.Options.OutStream, "Step %d : %s %s\n", stepN, strings.ToUpper(cmd), strings.Join(strs, " "))
224 224
 
225 225
 	// XXX yes, we skip any cmds that are not valid; the parser should have
226 226
 	// picked these out already.
... ...
@@ -30,7 +30,7 @@ import (
30 30
 	"github.com/docker/docker/utils"
31 31
 )
32 32
 
33
-func (b *buildFile) readContext(context io.Reader) error {
33
+func (b *BuildFile) readContext(context io.Reader) error {
34 34
 	tmpdirPath, err := ioutil.TempDir("", "docker-build")
35 35
 	if err != nil {
36 36
 		return err
... ...
@@ -50,15 +50,15 @@ func (b *buildFile) readContext(context io.Reader) error {
50 50
 	return nil
51 51
 }
52 52
 
53
-func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
53
+func (b *BuildFile) commit(id string, autoCmd []string, comment string) error {
54 54
 	if b.image == "" {
55 55
 		return fmt.Errorf("Please provide a source image with `from` prior to commit")
56 56
 	}
57
-	b.config.Image = b.image
57
+	b.Config.Image = b.image
58 58
 	if id == "" {
59
-		cmd := b.config.Cmd
60
-		b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
61
-		defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
59
+		cmd := b.Config.Cmd
60
+		b.Config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
61
+		defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
62 62
 
63 63
 		hit, err := b.probeCache()
64 64
 		if err != nil {
... ...
@@ -68,15 +68,15 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
68 68
 			return nil
69 69
 		}
70 70
 
71
-		container, warnings, err := b.options.Daemon.Create(b.config, "")
71
+		container, warnings, err := b.Options.Daemon.Create(b.Config, "")
72 72
 		if err != nil {
73 73
 			return err
74 74
 		}
75 75
 		for _, warning := range warnings {
76
-			fmt.Fprintf(b.options.OutStream, " ---> [Warning] %s\n", warning)
76
+			fmt.Fprintf(b.Options.OutStream, " ---> [Warning] %s\n", warning)
77 77
 		}
78
-		b.tmpContainers[container.ID] = struct{}{}
79
-		fmt.Fprintf(b.options.OutStream, " ---> Running in %s\n", utils.TruncateID(container.ID))
78
+		b.TmpContainers[container.ID] = struct{}{}
79
+		fmt.Fprintf(b.Options.OutStream, " ---> Running in %s\n", utils.TruncateID(container.ID))
80 80
 		id = container.ID
81 81
 
82 82
 		if err := container.Mount(); err != nil {
... ...
@@ -84,25 +84,25 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
84 84
 		}
85 85
 		defer container.Unmount()
86 86
 	}
87
-	container := b.options.Daemon.Get(id)
87
+	container := b.Options.Daemon.Get(id)
88 88
 	if container == nil {
89 89
 		return fmt.Errorf("An error occured while creating the container")
90 90
 	}
91 91
 
92 92
 	// Note: Actually copy the struct
93
-	autoConfig := *b.config
93
+	autoConfig := *b.Config
94 94
 	autoConfig.Cmd = autoCmd
95 95
 	// Commit the container
96
-	image, err := b.options.Daemon.Commit(container, "", "", "", b.maintainer, true, &autoConfig)
96
+	image, err := b.Options.Daemon.Commit(container, "", "", "", b.maintainer, true, &autoConfig)
97 97
 	if err != nil {
98 98
 		return err
99 99
 	}
100
-	b.tmpImages[image.ID] = struct{}{}
100
+	b.TmpImages[image.ID] = struct{}{}
101 101
 	b.image = image.ID
102 102
 	return nil
103 103
 }
104 104
 
105
-func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDecompression bool, cmdName string) error {
105
+func (b *BuildFile) runContextCommand(args []string, allowRemote bool, allowDecompression bool, cmdName string) error {
106 106
 	if b.context == nil {
107 107
 		return fmt.Errorf("No context given. Impossible to use %s", cmdName)
108 108
 	}
... ...
@@ -114,10 +114,10 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
114 114
 	orig := args[0]
115 115
 	dest := args[1]
116 116
 
117
-	cmd := b.config.Cmd
118
-	b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, orig, dest)}
119
-	defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
120
-	b.config.Image = b.image
117
+	cmd := b.Config.Cmd
118
+	b.Config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, orig, dest)}
119
+	defer func(cmd []string) { b.Config.Cmd = cmd }(cmd)
120
+	b.Config.Image = b.image
121 121
 
122 122
 	var (
123 123
 		origPath   = orig
... ...
@@ -201,7 +201,7 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
201 201
 	}
202 202
 
203 203
 	// Hash path and check the cache
204
-	if b.options.UtilizeCache {
204
+	if b.Options.UtilizeCache {
205 205
 		var (
206 206
 			hash string
207 207
 			sums = b.context.GetSums()
... ...
@@ -233,7 +233,7 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
233 233
 				hash = "file:" + h
234 234
 			}
235 235
 		}
236
-		b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, hash, dest)}
236
+		b.Config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, hash, dest)}
237 237
 		hit, err := b.probeCache()
238 238
 		if err != nil {
239 239
 			return err
... ...
@@ -245,11 +245,11 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
245 245
 	}
246 246
 
247 247
 	// Create the container
248
-	container, _, err := b.options.Daemon.Create(b.config, "")
248
+	container, _, err := b.Options.Daemon.Create(b.Config, "")
249 249
 	if err != nil {
250 250
 		return err
251 251
 	}
252
-	b.tmpContainers[container.ID] = struct{}{}
252
+	b.TmpContainers[container.ID] = struct{}{}
253 253
 
254 254
 	if err := container.Mount(); err != nil {
255 255
 		return err
... ...
@@ -269,27 +269,27 @@ func (b *buildFile) runContextCommand(args []string, allowRemote bool, allowDeco
269 269
 	return nil
270 270
 }
271 271
 
272
-func (b *buildFile) pullImage(name string) (*imagepkg.Image, error) {
272
+func (b *BuildFile) pullImage(name string) (*imagepkg.Image, error) {
273 273
 	remote, tag := parsers.ParseRepositoryTag(name)
274
-	pullRegistryAuth := b.options.AuthConfig
275
-	if len(b.options.AuthConfigFile.Configs) > 0 {
274
+	pullRegistryAuth := b.Options.AuthConfig
275
+	if len(b.Options.AuthConfigFile.Configs) > 0 {
276 276
 		// The request came with a full auth config file, we prefer to use that
277 277
 		endpoint, _, err := registry.ResolveRepositoryName(remote)
278 278
 		if err != nil {
279 279
 			return nil, err
280 280
 		}
281
-		resolvedAuth := b.options.AuthConfigFile.ResolveAuthConfig(endpoint)
281
+		resolvedAuth := b.Options.AuthConfigFile.ResolveAuthConfig(endpoint)
282 282
 		pullRegistryAuth = &resolvedAuth
283 283
 	}
284
-	job := b.options.Engine.Job("pull", remote, tag)
285
-	job.SetenvBool("json", b.options.StreamFormatter.Json())
284
+	job := b.Options.Engine.Job("pull", remote, tag)
285
+	job.SetenvBool("json", b.Options.StreamFormatter.Json())
286 286
 	job.SetenvBool("parallel", true)
287 287
 	job.SetenvJson("authConfig", pullRegistryAuth)
288
-	job.Stdout.Add(b.options.OutOld)
288
+	job.Stdout.Add(b.Options.OutOld)
289 289
 	if err := job.Run(); err != nil {
290 290
 		return nil, err
291 291
 	}
292
-	image, err := b.options.Daemon.Repositories().LookupImage(name)
292
+	image, err := b.Options.Daemon.Repositories().LookupImage(name)
293 293
 	if err != nil {
294 294
 		return nil, err
295 295
 	}
... ...
@@ -297,23 +297,23 @@ func (b *buildFile) pullImage(name string) (*imagepkg.Image, error) {
297 297
 	return image, nil
298 298
 }
299 299
 
300
-func (b *buildFile) processImageFrom(img *imagepkg.Image) error {
300
+func (b *BuildFile) processImageFrom(img *imagepkg.Image) error {
301 301
 	b.image = img.ID
302
-	b.config = &runconfig.Config{}
302
+	b.Config = &runconfig.Config{}
303 303
 	if img.Config != nil {
304
-		b.config = img.Config
304
+		b.Config = img.Config
305 305
 	}
306
-	if b.config.Env == nil || len(b.config.Env) == 0 {
307
-		b.config.Env = append(b.config.Env, "PATH="+daemon.DefaultPathEnv)
306
+	if b.Config.Env == nil || len(b.Config.Env) == 0 {
307
+		b.Config.Env = append(b.Config.Env, "PATH="+daemon.DefaultPathEnv)
308 308
 	}
309 309
 	// Process ONBUILD triggers if they exist
310
-	if nTriggers := len(b.config.OnBuild); nTriggers != 0 {
311
-		fmt.Fprintf(b.options.ErrStream, "# Executing %d build triggers\n", nTriggers)
310
+	if nTriggers := len(b.Config.OnBuild); nTriggers != 0 {
311
+		fmt.Fprintf(b.Options.ErrStream, "# Executing %d build triggers\n", nTriggers)
312 312
 	}
313 313
 
314 314
 	// Copy the ONBUILD triggers, and remove them from the config, since the config will be commited.
315
-	onBuildTriggers := b.config.OnBuild
316
-	b.config.OnBuild = []string{}
315
+	onBuildTriggers := b.Config.OnBuild
316
+	b.Config.OnBuild = []string{}
317 317
 
318 318
 	// FIXME rewrite this so that builder/parser is used; right now steps in
319 319
 	// onbuild are muted because we have no good way to represent the step
... ...
@@ -343,17 +343,17 @@ func (b *buildFile) processImageFrom(img *imagepkg.Image) error {
343 343
 	return nil
344 344
 }
345 345
 
346
-// probeCache checks to see if image-caching is enabled (`b.options.UtilizeCache`)
347
-// and if so attempts to look up the current `b.image` and `b.config` pair
348
-// in the current server `b.options.Daemon`. If an image is found, probeCache returns
346
+// probeCache checks to see if image-caching is enabled (`b.Options.UtilizeCache`)
347
+// and if so attempts to look up the current `b.image` and `b.Config` pair
348
+// in the current server `b.Options.Daemon`. If an image is found, probeCache returns
349 349
 // `(true, nil)`. If no image is found, it returns `(false, nil)`. If there
350 350
 // is any error, it returns `(false, err)`.
351
-func (b *buildFile) probeCache() (bool, error) {
352
-	if b.options.UtilizeCache {
353
-		if cache, err := b.options.Daemon.ImageGetCached(b.image, b.config); err != nil {
351
+func (b *BuildFile) probeCache() (bool, error) {
352
+	if b.Options.UtilizeCache {
353
+		if cache, err := b.Options.Daemon.ImageGetCached(b.image, b.Config); err != nil {
354 354
 			return false, err
355 355
 		} else if cache != nil {
356
-			fmt.Fprintf(b.options.OutStream, " ---> Using cache\n")
356
+			fmt.Fprintf(b.Options.OutStream, " ---> Using cache\n")
357 357
 			utils.Debugf("[BUILDER] Use cached version")
358 358
 			b.image = cache.ID
359 359
 			return true, nil
... ...
@@ -364,37 +364,37 @@ func (b *buildFile) probeCache() (bool, error) {
364 364
 	return false, nil
365 365
 }
366 366
 
367
-func (b *buildFile) create() (*daemon.Container, error) {
367
+func (b *BuildFile) create() (*daemon.Container, error) {
368 368
 	if b.image == "" {
369 369
 		return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
370 370
 	}
371
-	b.config.Image = b.image
371
+	b.Config.Image = b.image
372 372
 
373 373
 	// Create the container
374
-	c, _, err := b.options.Daemon.Create(b.config, "")
374
+	c, _, err := b.Options.Daemon.Create(b.Config, "")
375 375
 	if err != nil {
376 376
 		return nil, err
377 377
 	}
378
-	b.tmpContainers[c.ID] = struct{}{}
379
-	fmt.Fprintf(b.options.OutStream, " ---> Running in %s\n", utils.TruncateID(c.ID))
378
+	b.TmpContainers[c.ID] = struct{}{}
379
+	fmt.Fprintf(b.Options.OutStream, " ---> Running in %s\n", utils.TruncateID(c.ID))
380 380
 
381 381
 	// override the entry point that may have been picked up from the base image
382
-	c.Path = b.config.Cmd[0]
383
-	c.Args = b.config.Cmd[1:]
382
+	c.Path = b.Config.Cmd[0]
383
+	c.Args = b.Config.Cmd[1:]
384 384
 
385 385
 	return c, nil
386 386
 }
387 387
 
388
-func (b *buildFile) run(c *daemon.Container) error {
388
+func (b *BuildFile) run(c *daemon.Container) error {
389 389
 	var errCh chan error
390
-	if b.options.Verbose {
390
+	if b.Options.Verbose {
391 391
 		errCh = utils.Go(func() error {
392 392
 			// FIXME: call the 'attach' job so that daemon.Attach can be made private
393 393
 			//
394 394
 			// FIXME (LK4D4): Also, maybe makes sense to call "logs" job, it is like attach
395 395
 			// but without hijacking for stdin. Also, with attach there can be race
396 396
 			// condition because of some output already was printed before it.
397
-			return <-b.options.Daemon.Attach(c, nil, nil, b.options.OutStream, b.options.ErrStream)
397
+			return <-b.Options.Daemon.Attach(c, nil, nil, b.Options.OutStream, b.Options.ErrStream)
398 398
 		})
399 399
 	}
400 400
 
... ...
@@ -412,7 +412,7 @@ func (b *buildFile) run(c *daemon.Container) error {
412 412
 	// Wait for it to finish
413 413
 	if ret, _ := c.State.WaitStop(-1 * time.Second); ret != 0 {
414 414
 		err := &utils.JSONError{
415
-			Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.config.Cmd, ret),
415
+			Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.Config.Cmd, ret),
416 416
 			Code:    ret,
417 417
 		}
418 418
 		return err
... ...
@@ -421,7 +421,7 @@ func (b *buildFile) run(c *daemon.Container) error {
421 421
 	return nil
422 422
 }
423 423
 
424
-func (b *buildFile) checkPathForAddition(orig string) error {
424
+func (b *BuildFile) checkPathForAddition(orig string) error {
425 425
 	origPath := path.Join(b.contextPath, orig)
426 426
 	if p, err := filepath.EvalSymlinks(origPath); err != nil {
427 427
 		if os.IsNotExist(err) {
... ...
@@ -444,7 +444,7 @@ func (b *buildFile) checkPathForAddition(orig string) error {
444 444
 	return nil
445 445
 }
446 446
 
447
-func (b *buildFile) addContext(container *daemon.Container, orig, dest string, decompress bool) error {
447
+func (b *BuildFile) addContext(container *daemon.Container, orig, dest string, decompress bool) error {
448 448
 	var (
449 449
 		err        error
450 450
 		destExists = true
... ...
@@ -549,14 +549,14 @@ func fixPermissions(destination string, uid, gid int) error {
549 549
 	})
550 550
 }
551 551
 
552
-func (b *buildFile) clearTmp(containers map[string]struct{}) {
552
+func (b *BuildFile) clearTmp(containers map[string]struct{}) {
553 553
 	for c := range containers {
554
-		tmp := b.options.Daemon.Get(c)
555
-		if err := b.options.Daemon.Destroy(tmp); err != nil {
556
-			fmt.Fprintf(b.options.OutStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error())
554
+		tmp := b.Options.Daemon.Get(c)
555
+		if err := b.Options.Daemon.Destroy(tmp); err != nil {
556
+			fmt.Fprintf(b.Options.OutStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error())
557 557
 		} else {
558 558
 			delete(containers, c)
559
-			fmt.Fprintf(b.options.OutStream, "Removing intermediate container %s\n", utils.TruncateID(c))
559
+			fmt.Fprintf(b.Options.OutStream, "Removing intermediate container %s\n", utils.TruncateID(c))
560 560
 		}
561 561
 	}
562 562
 }
... ...
@@ -10,12 +10,12 @@ var (
10 10
 )
11 11
 
12 12
 // handle environment replacement. Used in dispatcher.
13
-func replaceEnv(b *buildFile, str string) string {
13
+func replaceEnv(b *BuildFile, str string) string {
14 14
 	for _, match := range TOKEN_ENV_INTERPOLATION.FindAllString(str, -1) {
15 15
 		match = match[strings.Index(match, "$"):]
16 16
 		matchKey := strings.Trim(match, "${}")
17 17
 
18
-		for envKey, envValue := range b.env {
18
+		for envKey, envValue := range b.Env {
19 19
 			if matchKey == envKey {
20 20
 				str = strings.Replace(str, match, envValue, -1)
21 21
 			}
22 22
new file mode 100644
... ...
@@ -0,0 +1,119 @@
0
+package builder
1
+
2
+import (
3
+	"io"
4
+	"io/ioutil"
5
+	"os"
6
+	"os/exec"
7
+	"strings"
8
+
9
+	"github.com/docker/docker/archive"
10
+	"github.com/docker/docker/builder/evaluator"
11
+	"github.com/docker/docker/daemon"
12
+	"github.com/docker/docker/engine"
13
+	"github.com/docker/docker/pkg/parsers"
14
+	"github.com/docker/docker/registry"
15
+	"github.com/docker/docker/utils"
16
+)
17
+
18
+type BuilderJob struct {
19
+	Engine *engine.Engine
20
+	Daemon *daemon.Daemon
21
+}
22
+
23
+func (b *BuilderJob) Install() {
24
+	b.Engine.Register("build", b.CmdBuild)
25
+}
26
+
27
+func (b *BuilderJob) CmdBuild(job *engine.Job) engine.Status {
28
+	if len(job.Args) != 0 {
29
+		return job.Errorf("Usage: %s\n", job.Name)
30
+	}
31
+	var (
32
+		remoteURL      = job.Getenv("remote")
33
+		repoName       = job.Getenv("t")
34
+		suppressOutput = job.GetenvBool("q")
35
+		noCache        = job.GetenvBool("nocache")
36
+		rm             = job.GetenvBool("rm")
37
+		forceRm        = job.GetenvBool("forcerm")
38
+		authConfig     = &registry.AuthConfig{}
39
+		configFile     = &registry.ConfigFile{}
40
+		tag            string
41
+		context        io.ReadCloser
42
+	)
43
+	job.GetenvJson("authConfig", authConfig)
44
+	job.GetenvJson("configFile", configFile)
45
+	repoName, tag = parsers.ParseRepositoryTag(repoName)
46
+
47
+	if remoteURL == "" {
48
+		context = ioutil.NopCloser(job.Stdin)
49
+	} else if utils.IsGIT(remoteURL) {
50
+		if !strings.HasPrefix(remoteURL, "git://") {
51
+			remoteURL = "https://" + remoteURL
52
+		}
53
+		root, err := ioutil.TempDir("", "docker-build-git")
54
+		if err != nil {
55
+			return job.Error(err)
56
+		}
57
+		defer os.RemoveAll(root)
58
+
59
+		if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
60
+			return job.Errorf("Error trying to use git: %s (%s)", err, output)
61
+		}
62
+
63
+		c, err := archive.Tar(root, archive.Uncompressed)
64
+		if err != nil {
65
+			return job.Error(err)
66
+		}
67
+		context = c
68
+	} else if utils.IsURL(remoteURL) {
69
+		f, err := utils.Download(remoteURL)
70
+		if err != nil {
71
+			return job.Error(err)
72
+		}
73
+		defer f.Body.Close()
74
+		dockerFile, err := ioutil.ReadAll(f.Body)
75
+		if err != nil {
76
+			return job.Error(err)
77
+		}
78
+		c, err := archive.Generate("Dockerfile", string(dockerFile))
79
+		if err != nil {
80
+			return job.Error(err)
81
+		}
82
+		context = c
83
+	}
84
+	defer context.Close()
85
+
86
+	sf := utils.NewStreamFormatter(job.GetenvBool("json"))
87
+
88
+	opts := &evaluator.BuildOpts{
89
+		Daemon: b.Daemon,
90
+		Engine: b.Engine,
91
+		OutStream: &utils.StdoutFormater{
92
+			Writer:          job.Stdout,
93
+			StreamFormatter: sf,
94
+		},
95
+		ErrStream: &utils.StderrFormater{
96
+			Writer:          job.Stdout,
97
+			StreamFormatter: sf,
98
+		},
99
+		Verbose:         !suppressOutput,
100
+		UtilizeCache:    !noCache,
101
+		Remove:          rm,
102
+		ForceRemove:     forceRm,
103
+		OutOld:          job.Stdout,
104
+		StreamFormatter: sf,
105
+		AuthConfig:      authConfig,
106
+		AuthConfigFile:  configFile,
107
+	}
108
+
109
+	id, err := NewBuilder(opts).Run(context)
110
+	if err != nil {
111
+		return job.Error(err)
112
+	}
113
+
114
+	if repoName != "" {
115
+		b.Daemon.Repositories().Set(repoName, tag, id, false)
116
+	}
117
+	return engine.StatusOK
118
+}
0 119
deleted file mode 100644
... ...
@@ -1,1006 +0,0 @@
1
-package daemon
2
-
3
-import (
4
-	"crypto/sha256"
5
-	"encoding/hex"
6
-	"encoding/json"
7
-	"errors"
8
-	"fmt"
9
-	"io"
10
-	"io/ioutil"
11
-	"net/url"
12
-	"os"
13
-	"os/exec"
14
-	"path"
15
-	"path/filepath"
16
-	"reflect"
17
-	"regexp"
18
-	"sort"
19
-	"strings"
20
-	"syscall"
21
-	"time"
22
-
23
-	"github.com/docker/docker/archive"
24
-	"github.com/docker/docker/engine"
25
-	"github.com/docker/docker/nat"
26
-	"github.com/docker/docker/pkg/log"
27
-	"github.com/docker/docker/pkg/parsers"
28
-	"github.com/docker/docker/pkg/symlink"
29
-	"github.com/docker/docker/pkg/system"
30
-	"github.com/docker/docker/pkg/tarsum"
31
-	"github.com/docker/docker/registry"
32
-	"github.com/docker/docker/runconfig"
33
-	"github.com/docker/docker/utils"
34
-)
35
-
36
-func (daemon *Daemon) CmdBuild(job *engine.Job) engine.Status {
37
-	if len(job.Args) != 0 {
38
-		return job.Errorf("Usage: %s\n", job.Name)
39
-	}
40
-	var (
41
-		remoteURL      = job.Getenv("remote")
42
-		repoName       = job.Getenv("t")
43
-		suppressOutput = job.GetenvBool("q")
44
-		noCache        = job.GetenvBool("nocache")
45
-		rm             = job.GetenvBool("rm")
46
-		forceRm        = job.GetenvBool("forcerm")
47
-		authConfig     = &registry.AuthConfig{}
48
-		configFile     = &registry.ConfigFile{}
49
-		tag            string
50
-		context        io.ReadCloser
51
-	)
52
-	job.GetenvJson("authConfig", authConfig)
53
-	job.GetenvJson("configFile", configFile)
54
-	repoName, tag = parsers.ParseRepositoryTag(repoName)
55
-
56
-	if remoteURL == "" {
57
-		context = ioutil.NopCloser(job.Stdin)
58
-	} else if utils.IsGIT(remoteURL) {
59
-		if !strings.HasPrefix(remoteURL, "git://") {
60
-			remoteURL = "https://" + remoteURL
61
-		}
62
-		root, err := ioutil.TempDir("", "docker-build-git")
63
-		if err != nil {
64
-			return job.Error(err)
65
-		}
66
-		defer os.RemoveAll(root)
67
-
68
-		if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
69
-			return job.Errorf("Error trying to use git: %s (%s)", err, output)
70
-		}
71
-
72
-		c, err := archive.Tar(root, archive.Uncompressed)
73
-		if err != nil {
74
-			return job.Error(err)
75
-		}
76
-		context = c
77
-	} else if utils.IsURL(remoteURL) {
78
-		f, err := utils.Download(remoteURL)
79
-		if err != nil {
80
-			return job.Error(err)
81
-		}
82
-		defer f.Body.Close()
83
-		dockerFile, err := ioutil.ReadAll(f.Body)
84
-		if err != nil {
85
-			return job.Error(err)
86
-		}
87
-		c, err := archive.Generate("Dockerfile", string(dockerFile))
88
-		if err != nil {
89
-			return job.Error(err)
90
-		}
91
-		context = c
92
-	}
93
-	defer context.Close()
94
-
95
-	sf := utils.NewStreamFormatter(job.GetenvBool("json"))
96
-	b := NewBuildFile(daemon, daemon.eng,
97
-		&utils.StdoutFormater{
98
-			Writer:          job.Stdout,
99
-			StreamFormatter: sf,
100
-		},
101
-		&utils.StderrFormater{
102
-			Writer:          job.Stdout,
103
-			StreamFormatter: sf,
104
-		},
105
-		!suppressOutput, !noCache, rm, forceRm, job.Stdout, sf, authConfig, configFile)
106
-	id, err := b.Build(context)
107
-	if err != nil {
108
-		return job.Error(err)
109
-	}
110
-	if repoName != "" {
111
-		daemon.Repositories().Set(repoName, tag, id, false)
112
-	}
113
-	return engine.StatusOK
114
-}
115
-
116
-var (
117
-	ErrDockerfileEmpty = errors.New("Dockerfile cannot be empty")
118
-)
119
-
120
-type BuildFile interface {
121
-	Build(io.Reader) (string, error)
122
-	CmdFrom(string) error
123
-	CmdRun(string) error
124
-}
125
-
126
-type buildFile struct {
127
-	daemon *Daemon
128
-	eng    *engine.Engine
129
-
130
-	image      string
131
-	maintainer string
132
-	config     *runconfig.Config
133
-
134
-	contextPath string
135
-	context     *tarsum.TarSum
136
-
137
-	verbose      bool
138
-	utilizeCache bool
139
-	rm           bool
140
-	forceRm      bool
141
-
142
-	authConfig *registry.AuthConfig
143
-	configFile *registry.ConfigFile
144
-
145
-	tmpContainers map[string]struct{}
146
-	tmpImages     map[string]struct{}
147
-
148
-	outStream io.Writer
149
-	errStream io.Writer
150
-
151
-	// Deprecated, original writer used for ImagePull. To be removed.
152
-	outOld io.Writer
153
-	sf     *utils.StreamFormatter
154
-
155
-	// cmdSet indicates is CMD was set in current Dockerfile
156
-	cmdSet bool
157
-}
158
-
159
-func (b *buildFile) clearTmp(containers map[string]struct{}) {
160
-	for c := range containers {
161
-		tmp := b.daemon.Get(c)
162
-		if err := b.daemon.Destroy(tmp); err != nil {
163
-			fmt.Fprintf(b.outStream, "Error removing intermediate container %s: %s\n", utils.TruncateID(c), err.Error())
164
-		} else {
165
-			delete(containers, c)
166
-			fmt.Fprintf(b.outStream, "Removing intermediate container %s\n", utils.TruncateID(c))
167
-		}
168
-	}
169
-}
170
-
171
-func (b *buildFile) CmdFrom(name string) error {
172
-	image, err := b.daemon.Repositories().LookupImage(name)
173
-	if err != nil {
174
-		if b.daemon.Graph().IsNotExist(err) {
175
-			remote, tag := parsers.ParseRepositoryTag(name)
176
-			pullRegistryAuth := b.authConfig
177
-			if len(b.configFile.Configs) > 0 {
178
-				// The request came with a full auth config file, we prefer to use that
179
-				endpoint, _, err := registry.ResolveRepositoryName(remote)
180
-				if err != nil {
181
-					return err
182
-				}
183
-				resolvedAuth := b.configFile.ResolveAuthConfig(endpoint)
184
-				pullRegistryAuth = &resolvedAuth
185
-			}
186
-			job := b.eng.Job("pull", remote, tag)
187
-			job.SetenvBool("json", b.sf.Json())
188
-			job.SetenvBool("parallel", true)
189
-			job.SetenvJson("authConfig", pullRegistryAuth)
190
-			job.Stdout.Add(b.outOld)
191
-			if err := job.Run(); err != nil {
192
-				return err
193
-			}
194
-			image, err = b.daemon.Repositories().LookupImage(name)
195
-			if err != nil {
196
-				return err
197
-			}
198
-		} else {
199
-			return err
200
-		}
201
-	}
202
-	b.image = image.ID
203
-	b.config = &runconfig.Config{}
204
-	if image.Config != nil {
205
-		b.config = image.Config
206
-	}
207
-	if b.config.Env == nil || len(b.config.Env) == 0 {
208
-		b.config.Env = append(b.config.Env, "PATH="+DefaultPathEnv)
209
-	}
210
-	// Process ONBUILD triggers if they exist
211
-	if nTriggers := len(b.config.OnBuild); nTriggers != 0 {
212
-		fmt.Fprintf(b.errStream, "# Executing %d build triggers\n", nTriggers)
213
-	}
214
-
215
-	// Copy the ONBUILD triggers, and remove them from the config, since the config will be commited.
216
-	onBuildTriggers := b.config.OnBuild
217
-	b.config.OnBuild = []string{}
218
-
219
-	for n, step := range onBuildTriggers {
220
-		splitStep := strings.Split(step, " ")
221
-		stepInstruction := strings.ToUpper(strings.Trim(splitStep[0], " "))
222
-		switch stepInstruction {
223
-		case "ONBUILD":
224
-			return fmt.Errorf("Source image contains forbidden chained `ONBUILD ONBUILD` trigger: %s", step)
225
-		case "MAINTAINER", "FROM":
226
-			return fmt.Errorf("Source image contains forbidden %s trigger: %s", stepInstruction, step)
227
-		}
228
-		if err := b.BuildStep(fmt.Sprintf("onbuild-%d", n), step); err != nil {
229
-			return err
230
-		}
231
-	}
232
-	return nil
233
-}
234
-
235
-// The ONBUILD command declares a build instruction to be executed in any future build
236
-// using the current image as a base.
237
-func (b *buildFile) CmdOnbuild(trigger string) error {
238
-	splitTrigger := strings.Split(trigger, " ")
239
-	triggerInstruction := strings.ToUpper(strings.Trim(splitTrigger[0], " "))
240
-	switch triggerInstruction {
241
-	case "ONBUILD":
242
-		return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
243
-	case "MAINTAINER", "FROM":
244
-		return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", triggerInstruction)
245
-	}
246
-	b.config.OnBuild = append(b.config.OnBuild, trigger)
247
-	return b.commit("", b.config.Cmd, fmt.Sprintf("ONBUILD %s", trigger))
248
-}
249
-
250
-func (b *buildFile) CmdMaintainer(name string) error {
251
-	b.maintainer = name
252
-	return b.commit("", b.config.Cmd, fmt.Sprintf("MAINTAINER %s", name))
253
-}
254
-
255
-// probeCache checks to see if image-caching is enabled (`b.utilizeCache`)
256
-// and if so attempts to look up the current `b.image` and `b.config` pair
257
-// in the current server `b.daemon`. If an image is found, probeCache returns
258
-// `(true, nil)`. If no image is found, it returns `(false, nil)`. If there
259
-// is any error, it returns `(false, err)`.
260
-func (b *buildFile) probeCache() (bool, error) {
261
-	if b.utilizeCache {
262
-		if cache, err := b.daemon.ImageGetCached(b.image, b.config); err != nil {
263
-			return false, err
264
-		} else if cache != nil {
265
-			fmt.Fprintf(b.outStream, " ---> Using cache\n")
266
-			log.Debugf("[BUILDER] Use cached version")
267
-			b.image = cache.ID
268
-			return true, nil
269
-		} else {
270
-			log.Debugf("[BUILDER] Cache miss")
271
-		}
272
-	}
273
-	return false, nil
274
-}
275
-
276
-func (b *buildFile) CmdRun(args string) error {
277
-	if b.image == "" {
278
-		return fmt.Errorf("Please provide a source image with `from` prior to run")
279
-	}
280
-	config, _, _, err := runconfig.Parse(append([]string{b.image}, b.buildCmdFromJson(args)...), nil)
281
-	if err != nil {
282
-		return err
283
-	}
284
-
285
-	cmd := b.config.Cmd
286
-	// set Cmd manually, this is special case only for Dockerfiles
287
-	b.config.Cmd = config.Cmd
288
-	runconfig.Merge(b.config, config)
289
-
290
-	defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
291
-
292
-	log.Debugf("Command to be executed: %v", b.config.Cmd)
293
-
294
-	hit, err := b.probeCache()
295
-	if err != nil {
296
-		return err
297
-	}
298
-	if hit {
299
-		return nil
300
-	}
301
-
302
-	c, err := b.create()
303
-	if err != nil {
304
-		return err
305
-	}
306
-	// Ensure that we keep the container mounted until the commit
307
-	// to avoid unmounting and then mounting directly again
308
-	c.Mount()
309
-	defer c.Unmount()
310
-
311
-	err = b.run(c)
312
-	if err != nil {
313
-		return err
314
-	}
315
-	if err := b.commit(c.ID, cmd, "run"); err != nil {
316
-		return err
317
-	}
318
-
319
-	return nil
320
-}
321
-
322
-func (b *buildFile) FindEnvKey(key string) int {
323
-	for k, envVar := range b.config.Env {
324
-		envParts := strings.SplitN(envVar, "=", 2)
325
-		if key == envParts[0] {
326
-			return k
327
-		}
328
-	}
329
-	return -1
330
-}
331
-
332
-func (b *buildFile) ReplaceEnvMatches(value string) (string, error) {
333
-	exp, err := regexp.Compile("(\\\\\\\\+|[^\\\\]|\\b|\\A)\\$({?)([[:alnum:]_]+)(}?)")
334
-	if err != nil {
335
-		return value, err
336
-	}
337
-	matches := exp.FindAllString(value, -1)
338
-	for _, match := range matches {
339
-		match = match[strings.Index(match, "$"):]
340
-		matchKey := strings.Trim(match, "${}")
341
-
342
-		for _, envVar := range b.config.Env {
343
-			envParts := strings.SplitN(envVar, "=", 2)
344
-			envKey := envParts[0]
345
-			envValue := envParts[1]
346
-
347
-			if envKey == matchKey {
348
-				value = strings.Replace(value, match, envValue, -1)
349
-				break
350
-			}
351
-		}
352
-	}
353
-	return value, nil
354
-}
355
-
356
-func (b *buildFile) CmdEnv(args string) error {
357
-	tmp := strings.SplitN(args, " ", 2)
358
-	if len(tmp) != 2 {
359
-		return fmt.Errorf("Invalid ENV format")
360
-	}
361
-	key := strings.Trim(tmp[0], " \t")
362
-	value := strings.Trim(tmp[1], " \t")
363
-
364
-	envKey := b.FindEnvKey(key)
365
-	replacedValue, err := b.ReplaceEnvMatches(value)
366
-	if err != nil {
367
-		return err
368
-	}
369
-	replacedVar := fmt.Sprintf("%s=%s", key, replacedValue)
370
-
371
-	if envKey >= 0 {
372
-		b.config.Env[envKey] = replacedVar
373
-	} else {
374
-		b.config.Env = append(b.config.Env, replacedVar)
375
-	}
376
-	return b.commit("", b.config.Cmd, fmt.Sprintf("ENV %s", replacedVar))
377
-}
378
-
379
-func (b *buildFile) buildCmdFromJson(args string) []string {
380
-	var cmd []string
381
-	if err := json.Unmarshal([]byte(args), &cmd); err != nil {
382
-		log.Debugf("Error unmarshalling: %s, setting to /bin/sh -c", err)
383
-		cmd = []string{"/bin/sh", "-c", args}
384
-	}
385
-	return cmd
386
-}
387
-
388
-func (b *buildFile) CmdCmd(args string) error {
389
-	cmd := b.buildCmdFromJson(args)
390
-	b.config.Cmd = cmd
391
-	if err := b.commit("", b.config.Cmd, fmt.Sprintf("CMD %v", cmd)); err != nil {
392
-		return err
393
-	}
394
-	b.cmdSet = true
395
-	return nil
396
-}
397
-
398
-func (b *buildFile) CmdEntrypoint(args string) error {
399
-	entrypoint := b.buildCmdFromJson(args)
400
-	b.config.Entrypoint = entrypoint
401
-	// if there is no cmd in current Dockerfile - cleanup cmd
402
-	if !b.cmdSet {
403
-		b.config.Cmd = nil
404
-	}
405
-	if err := b.commit("", b.config.Cmd, fmt.Sprintf("ENTRYPOINT %v", entrypoint)); err != nil {
406
-		return err
407
-	}
408
-	return nil
409
-}
410
-
411
-func (b *buildFile) CmdExpose(args string) error {
412
-	portsTab := strings.Split(args, " ")
413
-
414
-	if b.config.ExposedPorts == nil {
415
-		b.config.ExposedPorts = make(nat.PortSet)
416
-	}
417
-	ports, _, err := nat.ParsePortSpecs(append(portsTab, b.config.PortSpecs...))
418
-	if err != nil {
419
-		return err
420
-	}
421
-	for port := range ports {
422
-		if _, exists := b.config.ExposedPorts[port]; !exists {
423
-			b.config.ExposedPorts[port] = struct{}{}
424
-		}
425
-	}
426
-	b.config.PortSpecs = nil
427
-
428
-	return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
429
-}
430
-
431
-func (b *buildFile) CmdUser(args string) error {
432
-	b.config.User = args
433
-	return b.commit("", b.config.Cmd, fmt.Sprintf("USER %v", args))
434
-}
435
-
436
-func (b *buildFile) CmdInsert(args string) error {
437
-	return fmt.Errorf("INSERT has been deprecated. Please use ADD instead")
438
-}
439
-
440
-func (b *buildFile) CmdCopy(args string) error {
441
-	return b.runContextCommand(args, false, false, "COPY")
442
-}
443
-
444
-func (b *buildFile) CmdWorkdir(workdir string) error {
445
-	if workdir[0] == '/' {
446
-		b.config.WorkingDir = workdir
447
-	} else {
448
-		if b.config.WorkingDir == "" {
449
-			b.config.WorkingDir = "/"
450
-		}
451
-		b.config.WorkingDir = filepath.Join(b.config.WorkingDir, workdir)
452
-	}
453
-	return b.commit("", b.config.Cmd, fmt.Sprintf("WORKDIR %v", workdir))
454
-}
455
-
456
-func (b *buildFile) CmdVolume(args string) error {
457
-	if args == "" {
458
-		return fmt.Errorf("Volume cannot be empty")
459
-	}
460
-
461
-	var volume []string
462
-	if err := json.Unmarshal([]byte(args), &volume); err != nil {
463
-		volume = []string{args}
464
-	}
465
-	if b.config.Volumes == nil {
466
-		b.config.Volumes = map[string]struct{}{}
467
-	}
468
-	for _, v := range volume {
469
-		b.config.Volumes[v] = struct{}{}
470
-	}
471
-	if err := b.commit("", b.config.Cmd, fmt.Sprintf("VOLUME %s", args)); err != nil {
472
-		return err
473
-	}
474
-	return nil
475
-}
476
-
477
-func (b *buildFile) checkPathForAddition(orig string) error {
478
-	origPath := path.Join(b.contextPath, orig)
479
-	origPath, err := filepath.EvalSymlinks(origPath)
480
-	if err != nil {
481
-		if os.IsNotExist(err) {
482
-			return fmt.Errorf("%s: no such file or directory", orig)
483
-		}
484
-		return err
485
-	}
486
-	if !strings.HasPrefix(origPath, b.contextPath) {
487
-		return fmt.Errorf("Forbidden path outside the build context: %s (%s)", orig, origPath)
488
-	}
489
-	if _, err := os.Stat(origPath); err != nil {
490
-		if os.IsNotExist(err) {
491
-			return fmt.Errorf("%s: no such file or directory", orig)
492
-		}
493
-		return err
494
-	}
495
-	return nil
496
-}
497
-
498
-func (b *buildFile) addContext(container *Container, orig, dest string, decompress bool) error {
499
-	var (
500
-		err        error
501
-		destExists = true
502
-		origPath   = path.Join(b.contextPath, orig)
503
-		destPath   = path.Join(container.RootfsPath(), dest)
504
-	)
505
-
506
-	if destPath != container.RootfsPath() {
507
-		destPath, err = symlink.FollowSymlinkInScope(destPath, container.RootfsPath())
508
-		if err != nil {
509
-			return err
510
-		}
511
-	}
512
-
513
-	// Preserve the trailing '/'
514
-	if strings.HasSuffix(dest, "/") || dest == "." {
515
-		destPath = destPath + "/"
516
-	}
517
-
518
-	destStat, err := os.Stat(destPath)
519
-	if err != nil {
520
-		if !os.IsNotExist(err) {
521
-			return err
522
-		}
523
-		destExists = false
524
-	}
525
-
526
-	fi, err := os.Stat(origPath)
527
-	if err != nil {
528
-		if os.IsNotExist(err) {
529
-			return fmt.Errorf("%s: no such file or directory", orig)
530
-		}
531
-		return err
532
-	}
533
-
534
-	if fi.IsDir() {
535
-		return copyAsDirectory(origPath, destPath, destExists)
536
-	}
537
-
538
-	// If we are adding a remote file (or we've been told not to decompress), do not try to untar it
539
-	if decompress {
540
-		// First try to unpack the source as an archive
541
-		// to support the untar feature we need to clean up the path a little bit
542
-		// because tar is very forgiving.  First we need to strip off the archive's
543
-		// filename from the path but this is only added if it does not end in / .
544
-		tarDest := destPath
545
-		if strings.HasSuffix(tarDest, "/") {
546
-			tarDest = filepath.Dir(destPath)
547
-		}
548
-
549
-		// try to successfully untar the orig
550
-		if err := archive.UntarPath(origPath, tarDest); err == nil {
551
-			return nil
552
-		} else if err != io.EOF {
553
-			log.Debugf("Couldn't untar %s to %s: %s", origPath, tarDest, err)
554
-		}
555
-	}
556
-
557
-	if err := os.MkdirAll(path.Dir(destPath), 0755); err != nil {
558
-		return err
559
-	}
560
-	if err := archive.CopyWithTar(origPath, destPath); err != nil {
561
-		return err
562
-	}
563
-
564
-	resPath := destPath
565
-	if destExists && destStat.IsDir() {
566
-		resPath = path.Join(destPath, path.Base(origPath))
567
-	}
568
-
569
-	return fixPermissions(resPath, 0, 0)
570
-}
571
-
572
-func (b *buildFile) runContextCommand(args string, allowRemote bool, allowDecompression bool, cmdName string) error {
573
-	if b.context == nil {
574
-		return fmt.Errorf("No context given. Impossible to use %s", cmdName)
575
-	}
576
-	tmp := strings.SplitN(args, " ", 2)
577
-	if len(tmp) != 2 {
578
-		return fmt.Errorf("Invalid %s format", cmdName)
579
-	}
580
-
581
-	orig, err := b.ReplaceEnvMatches(strings.Trim(tmp[0], " \t"))
582
-	if err != nil {
583
-		return err
584
-	}
585
-
586
-	dest, err := b.ReplaceEnvMatches(strings.Trim(tmp[1], " \t"))
587
-	if err != nil {
588
-		return err
589
-	}
590
-
591
-	cmd := b.config.Cmd
592
-	b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, orig, dest)}
593
-	defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
594
-	b.config.Image = b.image
595
-
596
-	var (
597
-		origPath   = orig
598
-		destPath   = dest
599
-		remoteHash string
600
-		isRemote   bool
601
-		decompress = true
602
-	)
603
-
604
-	isRemote = utils.IsURL(orig)
605
-	if isRemote && !allowRemote {
606
-		return fmt.Errorf("Source can't be an URL for %s", cmdName)
607
-	} else if utils.IsURL(orig) {
608
-		// Initiate the download
609
-		resp, err := utils.Download(orig)
610
-		if err != nil {
611
-			return err
612
-		}
613
-
614
-		// Create a tmp dir
615
-		tmpDirName, err := ioutil.TempDir(b.contextPath, "docker-remote")
616
-		if err != nil {
617
-			return err
618
-		}
619
-
620
-		// Create a tmp file within our tmp dir
621
-		tmpFileName := path.Join(tmpDirName, "tmp")
622
-		tmpFile, err := os.OpenFile(tmpFileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
623
-		if err != nil {
624
-			return err
625
-		}
626
-		defer os.RemoveAll(tmpDirName)
627
-
628
-		// Download and dump result to tmp file
629
-		if _, err := io.Copy(tmpFile, resp.Body); err != nil {
630
-			tmpFile.Close()
631
-			return err
632
-		}
633
-		tmpFile.Close()
634
-
635
-		// Remove the mtime of the newly created tmp file
636
-		if err := system.UtimesNano(tmpFileName, make([]syscall.Timespec, 2)); err != nil {
637
-			return err
638
-		}
639
-
640
-		origPath = path.Join(filepath.Base(tmpDirName), filepath.Base(tmpFileName))
641
-
642
-		// Process the checksum
643
-		r, err := archive.Tar(tmpFileName, archive.Uncompressed)
644
-		if err != nil {
645
-			return err
646
-		}
647
-		tarSum := &tarsum.TarSum{Reader: r, DisableCompression: true}
648
-		if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
649
-			return err
650
-		}
651
-		remoteHash = tarSum.Sum(nil)
652
-		r.Close()
653
-
654
-		// If the destination is a directory, figure out the filename.
655
-		if strings.HasSuffix(dest, "/") {
656
-			u, err := url.Parse(orig)
657
-			if err != nil {
658
-				return err
659
-			}
660
-			path := u.Path
661
-			if strings.HasSuffix(path, "/") {
662
-				path = path[:len(path)-1]
663
-			}
664
-			parts := strings.Split(path, "/")
665
-			filename := parts[len(parts)-1]
666
-			if filename == "" {
667
-				return fmt.Errorf("cannot determine filename from url: %s", u)
668
-			}
669
-			destPath = dest + filename
670
-		}
671
-	}
672
-
673
-	if err := b.checkPathForAddition(origPath); err != nil {
674
-		return err
675
-	}
676
-
677
-	// Hash path and check the cache
678
-	if b.utilizeCache {
679
-		var (
680
-			hash string
681
-			sums = b.context.GetSums()
682
-		)
683
-
684
-		if remoteHash != "" {
685
-			hash = remoteHash
686
-		} else if fi, err := os.Stat(path.Join(b.contextPath, origPath)); err != nil {
687
-			return err
688
-		} else if fi.IsDir() {
689
-			var subfiles []string
690
-			for file, sum := range sums {
691
-				absFile := path.Join(b.contextPath, file)
692
-				absOrigPath := path.Join(b.contextPath, origPath)
693
-				if strings.HasPrefix(absFile, absOrigPath) {
694
-					subfiles = append(subfiles, sum)
695
-				}
696
-			}
697
-			sort.Strings(subfiles)
698
-			hasher := sha256.New()
699
-			hasher.Write([]byte(strings.Join(subfiles, ",")))
700
-			hash = "dir:" + hex.EncodeToString(hasher.Sum(nil))
701
-		} else {
702
-			if origPath[0] == '/' && len(origPath) > 1 {
703
-				origPath = origPath[1:]
704
-			}
705
-			origPath = strings.TrimPrefix(origPath, "./")
706
-			if h, ok := sums[origPath]; ok {
707
-				hash = "file:" + h
708
-			}
709
-		}
710
-		b.config.Cmd = []string{"/bin/sh", "-c", fmt.Sprintf("#(nop) %s %s in %s", cmdName, hash, dest)}
711
-		hit, err := b.probeCache()
712
-		if err != nil {
713
-			return err
714
-		}
715
-		// If we do not have a hash, never use the cache
716
-		if hit && hash != "" {
717
-			return nil
718
-		}
719
-	}
720
-
721
-	// Create the container
722
-	container, _, err := b.daemon.Create(b.config, "")
723
-	if err != nil {
724
-		return err
725
-	}
726
-	b.tmpContainers[container.ID] = struct{}{}
727
-
728
-	if err := container.Mount(); err != nil {
729
-		return err
730
-	}
731
-	defer container.Unmount()
732
-
733
-	if !allowDecompression || isRemote {
734
-		decompress = false
735
-	}
736
-	if err := b.addContext(container, origPath, destPath, decompress); err != nil {
737
-		return err
738
-	}
739
-
740
-	if err := b.commit(container.ID, cmd, fmt.Sprintf("%s %s in %s", cmdName, orig, dest)); err != nil {
741
-		return err
742
-	}
743
-	return nil
744
-}
745
-
746
-func (b *buildFile) CmdAdd(args string) error {
747
-	return b.runContextCommand(args, true, true, "ADD")
748
-}
749
-
750
-func (b *buildFile) create() (*Container, error) {
751
-	if b.image == "" {
752
-		return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
753
-	}
754
-	b.config.Image = b.image
755
-
756
-	// Create the container
757
-	c, _, err := b.daemon.Create(b.config, "")
758
-	if err != nil {
759
-		return nil, err
760
-	}
761
-	b.tmpContainers[c.ID] = struct{}{}
762
-	fmt.Fprintf(b.outStream, " ---> Running in %s\n", utils.TruncateID(c.ID))
763
-
764
-	// override the entry point that may have been picked up from the base image
765
-	c.Path = b.config.Cmd[0]
766
-	c.Args = b.config.Cmd[1:]
767
-
768
-	return c, nil
769
-}
770
-
771
-func (b *buildFile) run(c *Container) error {
772
-	var errCh chan error
773
-	if b.verbose {
774
-		errCh = utils.Go(func() error {
775
-			// FIXME: call the 'attach' job so that daemon.Attach can be made private
776
-			//
777
-			// FIXME (LK4D4): Also, maybe makes sense to call "logs" job, it is like attach
778
-			// but without hijacking for stdin. Also, with attach there can be race
779
-			// condition because of some output already was printed before it.
780
-			return <-b.daemon.Attach(c, nil, nil, b.outStream, b.errStream)
781
-		})
782
-	}
783
-
784
-	//start the container
785
-	if err := c.Start(); err != nil {
786
-		return err
787
-	}
788
-
789
-	if errCh != nil {
790
-		if err := <-errCh; err != nil {
791
-			return err
792
-		}
793
-	}
794
-
795
-	// Wait for it to finish
796
-	if ret, _ := c.State.WaitStop(-1 * time.Second); ret != 0 {
797
-		err := &utils.JSONError{
798
-			Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.config.Cmd, ret),
799
-			Code:    ret,
800
-		}
801
-		return err
802
-	}
803
-
804
-	return nil
805
-}
806
-
807
-// Commit the container <id> with the autorun command <autoCmd>
808
-func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
809
-	if b.image == "" {
810
-		return fmt.Errorf("Please provide a source image with `from` prior to commit")
811
-	}
812
-	b.config.Image = b.image
813
-	if id == "" {
814
-		cmd := b.config.Cmd
815
-		b.config.Cmd = []string{"/bin/sh", "-c", "#(nop) " + comment}
816
-		defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
817
-
818
-		hit, err := b.probeCache()
819
-		if err != nil {
820
-			return err
821
-		}
822
-		if hit {
823
-			return nil
824
-		}
825
-
826
-		container, warnings, err := b.daemon.Create(b.config, "")
827
-		if err != nil {
828
-			return err
829
-		}
830
-		for _, warning := range warnings {
831
-			fmt.Fprintf(b.outStream, " ---> [Warning] %s\n", warning)
832
-		}
833
-		b.tmpContainers[container.ID] = struct{}{}
834
-		fmt.Fprintf(b.outStream, " ---> Running in %s\n", utils.TruncateID(container.ID))
835
-		id = container.ID
836
-
837
-		if err := container.Mount(); err != nil {
838
-			return err
839
-		}
840
-		defer container.Unmount()
841
-	}
842
-	container := b.daemon.Get(id)
843
-	if container == nil {
844
-		return fmt.Errorf("An error occured while creating the container")
845
-	}
846
-
847
-	// Note: Actually copy the struct
848
-	autoConfig := *b.config
849
-	autoConfig.Cmd = autoCmd
850
-	// Commit the container
851
-	image, err := b.daemon.Commit(container, "", "", "", b.maintainer, true, &autoConfig)
852
-	if err != nil {
853
-		return err
854
-	}
855
-	b.tmpImages[image.ID] = struct{}{}
856
-	b.image = image.ID
857
-	return nil
858
-}
859
-
860
-// Long lines can be split with a backslash
861
-var lineContinuation = regexp.MustCompile(`\\\s*\n`)
862
-
863
-func (b *buildFile) Build(context io.Reader) (string, error) {
864
-	tmpdirPath, err := ioutil.TempDir("", "docker-build")
865
-	if err != nil {
866
-		return "", err
867
-	}
868
-
869
-	decompressedStream, err := archive.DecompressStream(context)
870
-	if err != nil {
871
-		return "", err
872
-	}
873
-
874
-	b.context = &tarsum.TarSum{Reader: decompressedStream, DisableCompression: true}
875
-	if err := archive.Untar(b.context, tmpdirPath, nil); err != nil {
876
-		return "", err
877
-	}
878
-	defer os.RemoveAll(tmpdirPath)
879
-
880
-	b.contextPath = tmpdirPath
881
-	filename := path.Join(tmpdirPath, "Dockerfile")
882
-	if _, err := os.Stat(filename); os.IsNotExist(err) {
883
-		return "", fmt.Errorf("Can't build a directory with no Dockerfile")
884
-	}
885
-	fileBytes, err := ioutil.ReadFile(filename)
886
-	if err != nil {
887
-		return "", err
888
-	}
889
-	if len(fileBytes) == 0 {
890
-		return "", ErrDockerfileEmpty
891
-	}
892
-	var (
893
-		dockerfile = lineContinuation.ReplaceAllString(stripComments(fileBytes), "")
894
-		stepN      = 0
895
-	)
896
-	for _, line := range strings.Split(dockerfile, "\n") {
897
-		line = strings.Trim(strings.Replace(line, "\t", " ", -1), " \t\r\n")
898
-		if len(line) == 0 {
899
-			continue
900
-		}
901
-		if err := b.BuildStep(fmt.Sprintf("%d", stepN), line); err != nil {
902
-			if b.forceRm {
903
-				b.clearTmp(b.tmpContainers)
904
-			}
905
-			return "", err
906
-		} else if b.rm {
907
-			b.clearTmp(b.tmpContainers)
908
-		}
909
-		stepN++
910
-	}
911
-	if b.image != "" {
912
-		fmt.Fprintf(b.outStream, "Successfully built %s\n", utils.TruncateID(b.image))
913
-		return b.image, nil
914
-	}
915
-	return "", fmt.Errorf("No image was generated. This may be because the Dockerfile does not, like, do anything.\n")
916
-}
917
-
918
-// BuildStep parses a single build step from `instruction` and executes it in the current context.
919
-func (b *buildFile) BuildStep(name, expression string) error {
920
-	fmt.Fprintf(b.outStream, "Step %s : %s\n", name, expression)
921
-	tmp := strings.SplitN(expression, " ", 2)
922
-	if len(tmp) != 2 {
923
-		return fmt.Errorf("Invalid Dockerfile format")
924
-	}
925
-	instruction := strings.ToLower(strings.Trim(tmp[0], " "))
926
-	arguments := strings.Trim(tmp[1], " ")
927
-
928
-	method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:]))
929
-	if !exists {
930
-		fmt.Fprintf(b.errStream, "# Skipping unknown instruction %s\n", strings.ToUpper(instruction))
931
-		return nil
932
-	}
933
-
934
-	ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface()
935
-	if ret != nil {
936
-		return ret.(error)
937
-	}
938
-
939
-	fmt.Fprintf(b.outStream, " ---> %s\n", utils.TruncateID(b.image))
940
-	return nil
941
-}
942
-
943
-func stripComments(raw []byte) string {
944
-	var (
945
-		out   []string
946
-		lines = strings.Split(string(raw), "\n")
947
-	)
948
-	for _, l := range lines {
949
-		if len(l) == 0 || l[0] == '#' {
950
-			continue
951
-		}
952
-		out = append(out, l)
953
-	}
954
-	return strings.Join(out, "\n")
955
-}
956
-
957
-func copyAsDirectory(source, destination string, destinationExists bool) error {
958
-	if err := archive.CopyWithTar(source, destination); err != nil {
959
-		return err
960
-	}
961
-
962
-	if destinationExists {
963
-		files, err := ioutil.ReadDir(source)
964
-		if err != nil {
965
-			return err
966
-		}
967
-
968
-		for _, file := range files {
969
-			if err := fixPermissions(filepath.Join(destination, file.Name()), 0, 0); err != nil {
970
-				return err
971
-			}
972
-		}
973
-		return nil
974
-	}
975
-
976
-	return fixPermissions(destination, 0, 0)
977
-}
978
-
979
-func fixPermissions(destination string, uid, gid int) error {
980
-	return filepath.Walk(destination, func(path string, info os.FileInfo, err error) error {
981
-		if err := os.Lchown(path, uid, gid); err != nil && !os.IsNotExist(err) {
982
-			return err
983
-		}
984
-		return nil
985
-	})
986
-}
987
-
988
-func NewBuildFile(d *Daemon, eng *engine.Engine, outStream, errStream io.Writer, verbose, utilizeCache, rm bool, forceRm bool, outOld io.Writer, sf *utils.StreamFormatter, auth *registry.AuthConfig, authConfigFile *registry.ConfigFile) BuildFile {
989
-	return &buildFile{
990
-		daemon:        d,
991
-		eng:           eng,
992
-		config:        &runconfig.Config{},
993
-		outStream:     outStream,
994
-		errStream:     errStream,
995
-		tmpContainers: make(map[string]struct{}),
996
-		tmpImages:     make(map[string]struct{}),
997
-		verbose:       verbose,
998
-		utilizeCache:  utilizeCache,
999
-		rm:            rm,
1000
-		forceRm:       forceRm,
1001
-		sf:            sf,
1002
-		authConfig:    auth,
1003
-		configFile:    authConfigFile,
1004
-		outOld:        outOld,
1005
-	}
1006
-}
... ...
@@ -101,7 +101,6 @@ func (daemon *Daemon) Install(eng *engine.Engine) error {
101 101
 	// FIXME: remove ImageDelete's dependency on Daemon, then move to graph/
102 102
 	for name, method := range map[string]engine.Handler{
103 103
 		"attach":            daemon.ContainerAttach,
104
-		"build":             daemon.CmdBuild,
105 104
 		"commit":            daemon.ContainerCommit,
106 105
 		"container_changes": daemon.ContainerChanges,
107 106
 		"container_copy":    daemon.ContainerCopy,
... ...
@@ -5,6 +5,7 @@ package main
5 5
 import (
6 6
 	"log"
7 7
 
8
+	"github.com/docker/docker/builder"
8 9
 	"github.com/docker/docker/builtins"
9 10
 	"github.com/docker/docker/daemon"
10 11
 	_ "github.com/docker/docker/daemon/execdriver/lxc"
... ...
@@ -48,6 +49,10 @@ func mainDaemon() {
48 48
 		if err := d.Install(eng); err != nil {
49 49
 			log.Fatal(err)
50 50
 		}
51
+
52
+		b := &builder.BuilderJob{eng, d}
53
+		b.Install()
54
+
51 55
 		// after the daemon is done setting up we can tell the api to start
52 56
 		// accepting connections
53 57
 		if err := eng.Job("acceptconnections").Run(); err != nil {