If a command during build fails, `docker build` now returns with
the exit code of that command.
This makes it necessary to change the build api endpoint to
return a json object stream.
... | ... |
@@ -960,9 +960,17 @@ func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Requ |
960 | 960 |
return err |
961 | 961 |
} |
962 | 962 |
|
963 |
- b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput, !noCache, rm) |
|
963 |
+ if version > 1.6 { |
|
964 |
+ w.Header().Set("Content-Type", "application/json") |
|
965 |
+ } |
|
966 |
+ sf := utils.NewStreamFormatter(version > 1.6) |
|
967 |
+ b := NewBuildFile(srv, utils.NewWriteFlusher(w), !suppressOutput, !noCache, rm, sf) |
|
964 | 968 |
id, err := b.Build(context) |
965 | 969 |
if err != nil { |
970 |
+ if sf.Used() { |
|
971 |
+ w.Write(sf.FormatError(err)) |
|
972 |
+ return nil |
|
973 |
+ } |
|
966 | 974 |
return fmt.Errorf("Error build: %s", err) |
967 | 975 |
} |
968 | 976 |
if repoName != "" { |
... | ... |
@@ -37,6 +37,7 @@ type buildFile struct { |
37 | 37 |
tmpImages map[string]struct{} |
38 | 38 |
|
39 | 39 |
out io.Writer |
40 |
+ sf *utils.StreamFormatter |
|
40 | 41 |
} |
41 | 42 |
|
42 | 43 |
func (b *buildFile) clearTmp(containers map[string]struct{}) { |
... | ... |
@@ -52,7 +53,7 @@ func (b *buildFile) CmdFrom(name string) error { |
52 | 52 |
if err != nil { |
53 | 53 |
if b.runtime.graph.IsNotExist(err) { |
54 | 54 |
remote, tag := utils.ParseRepositoryTag(name) |
55 |
- if err := b.srv.ImagePull(remote, tag, b.out, utils.NewStreamFormatter(false), nil, nil, true); err != nil { |
|
55 |
+ if err := b.srv.ImagePull(remote, tag, b.out, b.sf, nil, nil, true); err != nil { |
|
56 | 56 |
return err |
57 | 57 |
} |
58 | 58 |
image, err = b.runtime.repositories.LookupImage(name) |
... | ... |
@@ -100,7 +101,11 @@ func (b *buildFile) CmdRun(args string) error { |
100 | 100 |
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { |
101 | 101 |
return err |
102 | 102 |
} else if cache != nil { |
103 |
- fmt.Fprintf(b.out, " ---> Using cache\n") |
|
103 |
+ if b.sf.Used() { |
|
104 |
+ b.out.Write(b.sf.FormatStatus("", " ---> Using cache")) |
|
105 |
+ } else { |
|
106 |
+ fmt.Fprintf(b.out, " ---> Using cache\n") |
|
107 |
+ } |
|
104 | 108 |
utils.Debugf("[BUILDER] Use cached version") |
105 | 109 |
b.image = cache.ID |
106 | 110 |
return nil |
... | ... |
@@ -376,8 +381,11 @@ func (b *buildFile) run() (string, error) { |
376 | 376 |
return "", err |
377 | 377 |
} |
378 | 378 |
b.tmpContainers[c.ID] = struct{}{} |
379 |
- fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(c.ID)) |
|
380 |
- |
|
379 |
+ if b.sf.Used() { |
|
380 |
+ b.out.Write(b.sf.FormatStatus("", " ---> Running in %s", utils.TruncateID(c.ID))) |
|
381 |
+ } else { |
|
382 |
+ fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(c.ID)) |
|
383 |
+ } |
|
381 | 384 |
// override the entry point that may have been picked up from the base image |
382 | 385 |
c.Path = b.config.Cmd[0] |
383 | 386 |
c.Args = b.config.Cmd[1:] |
... | ... |
@@ -403,7 +411,11 @@ func (b *buildFile) run() (string, error) { |
403 | 403 |
|
404 | 404 |
// Wait for it to finish |
405 | 405 |
if ret := c.Wait(); ret != 0 { |
406 |
- return "", fmt.Errorf("The command %v returned a non-zero code: %d", b.config.Cmd, ret) |
|
406 |
+ err := &utils.JSONError{ |
|
407 |
+ Message: fmt.Sprintf("The command %v returned a non-zero code: %d", b.config.Cmd, ret), |
|
408 |
+ Code: ret, |
|
409 |
+ } |
|
410 |
+ return "", err |
|
407 | 411 |
} |
408 | 412 |
|
409 | 413 |
return c.ID, nil |
... | ... |
@@ -424,7 +436,11 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { |
424 | 424 |
if cache, err := b.srv.ImageGetCached(b.image, b.config); err != nil { |
425 | 425 |
return err |
426 | 426 |
} else if cache != nil { |
427 |
- fmt.Fprintf(b.out, " ---> Using cache\n") |
|
427 |
+ if b.sf.Used() { |
|
428 |
+ b.out.Write(b.sf.FormatStatus("", " ---> Using cache")) |
|
429 |
+ } else { |
|
430 |
+ fmt.Fprintf(b.out, " ---> Using cache\n") |
|
431 |
+ } |
|
428 | 432 |
utils.Debugf("[BUILDER] Use cached version") |
429 | 433 |
b.image = cache.ID |
430 | 434 |
return nil |
... | ... |
@@ -441,7 +457,11 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { |
441 | 441 |
fmt.Fprintf(b.out, " ---> [Warning] %s\n", warning) |
442 | 442 |
} |
443 | 443 |
b.tmpContainers[container.ID] = struct{}{} |
444 |
- fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(container.ID)) |
|
444 |
+ if b.sf.Used() { |
|
445 |
+ b.out.Write(b.sf.FormatStatus("", " ---> Running in %s", utils.TruncateID(container.ID))) |
|
446 |
+ } else { |
|
447 |
+ fmt.Fprintf(b.out, " ---> Running in %s\n", utils.TruncateID(container.ID)) |
|
448 |
+ } |
|
445 | 449 |
id = container.ID |
446 | 450 |
if err := container.EnsureMounted(); err != nil { |
447 | 451 |
return err |
... | ... |
@@ -507,22 +527,22 @@ func (b *buildFile) Build(context io.Reader) (string, error) { |
507 | 507 |
|
508 | 508 |
method, exists := reflect.TypeOf(b).MethodByName("Cmd" + strings.ToUpper(instruction[:1]) + strings.ToLower(instruction[1:])) |
509 | 509 |
if !exists { |
510 |
- fmt.Fprintf(b.out, "# Skipping unknown instruction %s\n", strings.ToUpper(instruction)) |
|
510 |
+ b.out.Write(b.sf.FormatStatus("", "# Skipping unknown instruction %s", strings.ToUpper(instruction))) |
|
511 | 511 |
continue |
512 | 512 |
} |
513 | 513 |
|
514 | 514 |
stepN += 1 |
515 |
- fmt.Fprintf(b.out, "Step %d : %s %s\n", stepN, strings.ToUpper(instruction), arguments) |
|
515 |
+ b.out.Write(b.sf.FormatStatus("", "Step %d : %s %s", stepN, strings.ToUpper(instruction), arguments)) |
|
516 | 516 |
|
517 | 517 |
ret := method.Func.Call([]reflect.Value{reflect.ValueOf(b), reflect.ValueOf(arguments)})[0].Interface() |
518 | 518 |
if ret != nil { |
519 | 519 |
return "", ret.(error) |
520 | 520 |
} |
521 | 521 |
|
522 |
- fmt.Fprintf(b.out, " ---> %v\n", utils.TruncateID(b.image)) |
|
522 |
+ b.out.Write(b.sf.FormatStatus("", " ---> %s", utils.TruncateID(b.image))) |
|
523 | 523 |
} |
524 | 524 |
if b.image != "" { |
525 |
- fmt.Fprintf(b.out, "Successfully built %s\n", utils.TruncateID(b.image)) |
|
525 |
+ b.out.Write(b.sf.FormatStatus("", "Successfully built %s", utils.TruncateID(b.image))) |
|
526 | 526 |
if b.rm { |
527 | 527 |
b.clearTmp(b.tmpContainers) |
528 | 528 |
} |
... | ... |
@@ -531,7 +551,7 @@ func (b *buildFile) Build(context io.Reader) (string, error) { |
531 | 531 |
return "", fmt.Errorf("An error occurred during the build\n") |
532 | 532 |
} |
533 | 533 |
|
534 |
-func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache, rm bool) BuildFile { |
|
534 |
+func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache, rm bool, sf *utils.StreamFormatter) BuildFile { |
|
535 | 535 |
return &buildFile{ |
536 | 536 |
runtime: srv.runtime, |
537 | 537 |
srv: srv, |
... | ... |
@@ -542,5 +562,6 @@ func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache, rm bool) Bu |
542 | 542 |
verbose: verbose, |
543 | 543 |
utilizeCache: utilizeCache, |
544 | 544 |
rm: rm, |
545 |
+ sf: sf, |
|
545 | 546 |
} |
546 | 547 |
} |
... | ... |
@@ -220,42 +220,16 @@ func (cli *DockerCli) CmdBuild(args ...string) error { |
220 | 220 |
if *rm { |
221 | 221 |
v.Set("rm", "1") |
222 | 222 |
} |
223 |
- req, err := http.NewRequest("POST", fmt.Sprintf("/v%g/build?%s", APIVERSION, v.Encode()), body) |
|
224 |
- if err != nil { |
|
225 |
- return err |
|
226 |
- } |
|
223 |
+ |
|
224 |
+ headers := http.Header(make(map[string][]string)) |
|
227 | 225 |
if context != nil { |
228 |
- req.Header.Set("Content-Type", "application/tar") |
|
229 |
- } |
|
230 |
- dial, err := net.Dial(cli.proto, cli.addr) |
|
231 |
- if err != nil { |
|
232 |
- return err |
|
233 |
- } |
|
234 |
- clientconn := httputil.NewClientConn(dial, nil) |
|
235 |
- resp, err := clientconn.Do(req) |
|
236 |
- defer clientconn.Close() |
|
237 |
- if err != nil { |
|
238 |
- return err |
|
226 |
+ headers.Set("Content-Type", "application/tar") |
|
239 | 227 |
} |
240 |
- defer resp.Body.Close() |
|
241 |
- // Check for errors |
|
242 |
- if resp.StatusCode < 200 || resp.StatusCode >= 400 { |
|
243 |
- body, err := ioutil.ReadAll(resp.Body) |
|
244 |
- if err != nil { |
|
245 |
- return err |
|
246 |
- } |
|
247 |
- if len(body) == 0 { |
|
248 |
- return fmt.Errorf("Error: %s", http.StatusText(resp.StatusCode)) |
|
249 |
- } |
|
250 |
- return fmt.Errorf("Error: %s", body) |
|
251 |
- } |
|
252 |
- |
|
253 |
- // Output the result |
|
254 |
- if _, err := io.Copy(cli.out, resp.Body); err != nil { |
|
255 |
- return err |
|
228 |
+ err = cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), body, cli.out, headers) |
|
229 |
+ if jerr, ok := err.(*utils.JSONError); ok { |
|
230 |
+ return &utils.StatusError{Status: jerr.Message, StatusCode: jerr.Code} |
|
256 | 231 |
} |
257 |
- |
|
258 |
- return nil |
|
232 |
+ return err |
|
259 | 233 |
} |
260 | 234 |
|
261 | 235 |
// 'docker login': login / register a user to registry service. |
... | ... |
@@ -699,7 +673,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error { |
699 | 699 |
} |
700 | 700 |
fmt.Fprintf(cli.out, "]") |
701 | 701 |
if status != 0 { |
702 |
- return &utils.StatusError{Status: status} |
|
702 |
+ return &utils.StatusError{StatusCode: status} |
|
703 | 703 |
} |
704 | 704 |
return nil |
705 | 705 |
} |
... | ... |
@@ -1584,7 +1558,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error { |
1584 | 1584 |
return err |
1585 | 1585 |
} |
1586 | 1586 |
if status != 0 { |
1587 |
- return &utils.StatusError{Status: status} |
|
1587 |
+ return &utils.StatusError{StatusCode: status} |
|
1588 | 1588 |
} |
1589 | 1589 |
|
1590 | 1590 |
return nil |
... | ... |
@@ -2167,7 +2141,7 @@ func (cli *DockerCli) CmdRun(args ...string) error { |
2167 | 2167 |
} |
2168 | 2168 |
} |
2169 | 2169 |
if status != 0 { |
2170 |
- return &utils.StatusError{Status: status} |
|
2170 |
+ return &utils.StatusError{StatusCode: status} |
|
2171 | 2171 |
} |
2172 | 2172 |
return nil |
2173 | 2173 |
} |
... | ... |
@@ -100,7 +100,10 @@ func main() { |
100 | 100 |
protoAddrParts := strings.SplitN(flHosts[0], "://", 2) |
101 | 101 |
if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil { |
102 | 102 |
if sterr, ok := err.(*utils.StatusError); ok { |
103 |
- os.Exit(sterr.Status) |
|
103 |
+ if sterr.Status != "" { |
|
104 |
+ log.Println(sterr.Status) |
|
105 |
+ } |
|
106 |
+ os.Exit(sterr.StatusCode) |
|
104 | 107 |
} |
105 | 108 |
log.Fatal(err) |
106 | 109 |
} |
... | ... |
@@ -138,6 +138,11 @@ What's new |
138 | 138 |
This URI no longer exists. The ``images -viz`` output is now generated in |
139 | 139 |
the client, using the ``/images/json`` data. |
140 | 140 |
|
141 |
+.. http:post:: /build |
|
142 |
+ |
|
143 |
+ **New!** This endpoint now returns build status as json stream. In case |
|
144 |
+ of a build error, it returns the exit status of the failed command. |
|
145 |
+ |
|
141 | 146 |
v1.6 |
142 | 147 |
**** |
143 | 148 |
|
... | ... |
@@ -990,9 +990,11 @@ Build an image from Dockerfile via stdin |
990 | 990 |
.. sourcecode:: http |
991 | 991 |
|
992 | 992 |
HTTP/1.1 200 OK |
993 |
+ Content-Type: application/json |
|
993 | 994 |
|
994 |
- {{ STREAM }} |
|
995 |
- |
|
995 |
+ {"status":"Step 1..."} |
|
996 |
+ {"status":"..."} |
|
997 |
+ {"error":"Error...", "errorDetail":{"code": 123, "message": "Error..."}} |
|
996 | 998 |
|
997 | 999 |
The stream must be a tar archive compressed with one of the |
998 | 1000 |
following algorithms: identity (no compression), gzip, bzip2, |
... | ... |
@@ -5,6 +5,7 @@ import ( |
5 | 5 |
"github.com/dotcloud/docker" |
6 | 6 |
"github.com/dotcloud/docker/archive" |
7 | 7 |
"github.com/dotcloud/docker/engine" |
8 |
+ "github.com/dotcloud/docker/utils" |
|
8 | 9 |
"io/ioutil" |
9 | 10 |
"net" |
10 | 11 |
"net/http" |
... | ... |
@@ -226,11 +227,14 @@ func mkTestingFileServer(files [][2]string) (*httptest.Server, error) { |
226 | 226 |
|
227 | 227 |
func TestBuild(t *testing.T) { |
228 | 228 |
for _, ctx := range testContexts { |
229 |
- buildImage(ctx, t, nil, true) |
|
229 |
+ _, err := buildImage(ctx, t, nil, true) |
|
230 |
+ if err != nil { |
|
231 |
+ t.Fatal(err) |
|
232 |
+ } |
|
230 | 233 |
} |
231 | 234 |
} |
232 | 235 |
|
233 |
-func buildImage(context testContextTemplate, t *testing.T, eng *engine.Engine, useCache bool) *docker.Image { |
|
236 |
+func buildImage(context testContextTemplate, t *testing.T, eng *engine.Engine, useCache bool) (*docker.Image, error) { |
|
234 | 237 |
if eng == nil { |
235 | 238 |
eng = NewTestEngine(t) |
236 | 239 |
runtime := mkRuntimeFromEngine(eng, t) |
... | ... |
@@ -262,25 +266,24 @@ func buildImage(context testContextTemplate, t *testing.T, eng *engine.Engine, u |
262 | 262 |
} |
263 | 263 |
dockerfile := constructDockerfile(context.dockerfile, ip, port) |
264 | 264 |
|
265 |
- buildfile := docker.NewBuildFile(srv, ioutil.Discard, false, useCache, false) |
|
265 |
+ buildfile := docker.NewBuildFile(srv, ioutil.Discard, false, useCache, false, utils.NewStreamFormatter(false)) |
|
266 | 266 |
id, err := buildfile.Build(mkTestContext(dockerfile, context.files, t)) |
267 | 267 |
if err != nil { |
268 |
- t.Fatal(err) |
|
268 |
+ return nil, err |
|
269 | 269 |
} |
270 | 270 |
|
271 |
- img, err := srv.ImageInspect(id) |
|
272 |
- if err != nil { |
|
273 |
- t.Fatal(err) |
|
274 |
- } |
|
275 |
- return img |
|
271 |
+ return srv.ImageInspect(id) |
|
276 | 272 |
} |
277 | 273 |
|
278 | 274 |
func TestVolume(t *testing.T) { |
279 |
- img := buildImage(testContextTemplate{` |
|
275 |
+ img, err := buildImage(testContextTemplate{` |
|
280 | 276 |
from {IMAGE} |
281 | 277 |
volume /test |
282 | 278 |
cmd Hello world |
283 | 279 |
`, nil, nil}, t, nil, true) |
280 |
+ if err != nil { |
|
281 |
+ t.Fatal(err) |
|
282 |
+ } |
|
284 | 283 |
|
285 | 284 |
if len(img.Config.Volumes) == 0 { |
286 | 285 |
t.Fail() |
... | ... |
@@ -293,10 +296,13 @@ func TestVolume(t *testing.T) { |
293 | 293 |
} |
294 | 294 |
|
295 | 295 |
func TestBuildMaintainer(t *testing.T) { |
296 |
- img := buildImage(testContextTemplate{` |
|
296 |
+ img, err := buildImage(testContextTemplate{` |
|
297 | 297 |
from {IMAGE} |
298 | 298 |
maintainer dockerio |
299 | 299 |
`, nil, nil}, t, nil, true) |
300 |
+ if err != nil { |
|
301 |
+ t.Fatal(err) |
|
302 |
+ } |
|
300 | 303 |
|
301 | 304 |
if img.Author != "dockerio" { |
302 | 305 |
t.Fail() |
... | ... |
@@ -304,10 +310,13 @@ func TestBuildMaintainer(t *testing.T) { |
304 | 304 |
} |
305 | 305 |
|
306 | 306 |
func TestBuildUser(t *testing.T) { |
307 |
- img := buildImage(testContextTemplate{` |
|
307 |
+ img, err := buildImage(testContextTemplate{` |
|
308 | 308 |
from {IMAGE} |
309 | 309 |
user dockerio |
310 | 310 |
`, nil, nil}, t, nil, true) |
311 |
+ if err != nil { |
|
312 |
+ t.Fatal(err) |
|
313 |
+ } |
|
311 | 314 |
|
312 | 315 |
if img.Config.User != "dockerio" { |
313 | 316 |
t.Fail() |
... | ... |
@@ -315,11 +324,15 @@ func TestBuildUser(t *testing.T) { |
315 | 315 |
} |
316 | 316 |
|
317 | 317 |
func TestBuildEnv(t *testing.T) { |
318 |
- img := buildImage(testContextTemplate{` |
|
318 |
+ img, err := buildImage(testContextTemplate{` |
|
319 | 319 |
from {IMAGE} |
320 | 320 |
env port 4243 |
321 | 321 |
`, |
322 | 322 |
nil, nil}, t, nil, true) |
323 |
+ if err != nil { |
|
324 |
+ t.Fatal(err) |
|
325 |
+ } |
|
326 |
+ |
|
323 | 327 |
hasEnv := false |
324 | 328 |
for _, envVar := range img.Config.Env { |
325 | 329 |
if envVar == "port=4243" { |
... | ... |
@@ -333,11 +346,14 @@ func TestBuildEnv(t *testing.T) { |
333 | 333 |
} |
334 | 334 |
|
335 | 335 |
func TestBuildCmd(t *testing.T) { |
336 |
- img := buildImage(testContextTemplate{` |
|
336 |
+ img, err := buildImage(testContextTemplate{` |
|
337 | 337 |
from {IMAGE} |
338 | 338 |
cmd ["/bin/echo", "Hello World"] |
339 | 339 |
`, |
340 | 340 |
nil, nil}, t, nil, true) |
341 |
+ if err != nil { |
|
342 |
+ t.Fatal(err) |
|
343 |
+ } |
|
341 | 344 |
|
342 | 345 |
if img.Config.Cmd[0] != "/bin/echo" { |
343 | 346 |
t.Log(img.Config.Cmd[0]) |
... | ... |
@@ -350,11 +366,14 @@ func TestBuildCmd(t *testing.T) { |
350 | 350 |
} |
351 | 351 |
|
352 | 352 |
func TestBuildExpose(t *testing.T) { |
353 |
- img := buildImage(testContextTemplate{` |
|
353 |
+ img, err := buildImage(testContextTemplate{` |
|
354 | 354 |
from {IMAGE} |
355 | 355 |
expose 4243 |
356 | 356 |
`, |
357 | 357 |
nil, nil}, t, nil, true) |
358 |
+ if err != nil { |
|
359 |
+ t.Fatal(err) |
|
360 |
+ } |
|
358 | 361 |
|
359 | 362 |
if img.Config.PortSpecs[0] != "4243" { |
360 | 363 |
t.Fail() |
... | ... |
@@ -362,11 +381,14 @@ func TestBuildExpose(t *testing.T) { |
362 | 362 |
} |
363 | 363 |
|
364 | 364 |
func TestBuildEntrypoint(t *testing.T) { |
365 |
- img := buildImage(testContextTemplate{` |
|
365 |
+ img, err := buildImage(testContextTemplate{` |
|
366 | 366 |
from {IMAGE} |
367 | 367 |
entrypoint ["/bin/echo"] |
368 | 368 |
`, |
369 | 369 |
nil, nil}, t, nil, true) |
370 |
+ if err != nil { |
|
371 |
+ t.Fatal(err) |
|
372 |
+ } |
|
370 | 373 |
|
371 | 374 |
if img.Config.Entrypoint[0] != "/bin/echo" { |
372 | 375 |
} |
... | ... |
@@ -378,19 +400,25 @@ func TestBuildEntrypointRunCleanup(t *testing.T) { |
378 | 378 |
eng := NewTestEngine(t) |
379 | 379 |
defer nuke(mkRuntimeFromEngine(eng, t)) |
380 | 380 |
|
381 |
- img := buildImage(testContextTemplate{` |
|
381 |
+ img, err := buildImage(testContextTemplate{` |
|
382 | 382 |
from {IMAGE} |
383 | 383 |
run echo "hello" |
384 | 384 |
`, |
385 | 385 |
nil, nil}, t, eng, true) |
386 |
+ if err != nil { |
|
387 |
+ t.Fatal(err) |
|
388 |
+ } |
|
386 | 389 |
|
387 |
- img = buildImage(testContextTemplate{` |
|
390 |
+ img, err = buildImage(testContextTemplate{` |
|
388 | 391 |
from {IMAGE} |
389 | 392 |
run echo "hello" |
390 | 393 |
add foo /foo |
391 | 394 |
entrypoint ["/bin/echo"] |
392 | 395 |
`, |
393 | 396 |
[][2]string{{"foo", "HEYO"}}, nil}, t, eng, true) |
397 |
+ if err != nil { |
|
398 |
+ t.Fatal(err) |
|
399 |
+ } |
|
394 | 400 |
|
395 | 401 |
if len(img.Config.Cmd) != 0 { |
396 | 402 |
t.Fail() |
... | ... |
@@ -407,11 +435,18 @@ func TestBuildImageWithCache(t *testing.T) { |
407 | 407 |
`, |
408 | 408 |
nil, nil} |
409 | 409 |
|
410 |
- img := buildImage(template, t, eng, true) |
|
410 |
+ img, err := buildImage(template, t, eng, true) |
|
411 |
+ if err != nil { |
|
412 |
+ t.Fatal(err) |
|
413 |
+ } |
|
414 |
+ |
|
411 | 415 |
imageId := img.ID |
412 | 416 |
|
413 | 417 |
img = nil |
414 |
- img = buildImage(template, t, eng, true) |
|
418 |
+ img, err = buildImage(template, t, eng, true) |
|
419 |
+ if err != nil { |
|
420 |
+ t.Fatal(err) |
|
421 |
+ } |
|
415 | 422 |
|
416 | 423 |
if imageId != img.ID { |
417 | 424 |
t.Logf("Image ids should match: %s != %s", imageId, img.ID) |
... | ... |
@@ -429,11 +464,17 @@ func TestBuildImageWithoutCache(t *testing.T) { |
429 | 429 |
`, |
430 | 430 |
nil, nil} |
431 | 431 |
|
432 |
- img := buildImage(template, t, eng, true) |
|
432 |
+ img, err := buildImage(template, t, eng, true) |
|
433 |
+ if err != nil { |
|
434 |
+ t.Fatal(err) |
|
435 |
+ } |
|
433 | 436 |
imageId := img.ID |
434 | 437 |
|
435 | 438 |
img = nil |
436 |
- img = buildImage(template, t, eng, false) |
|
439 |
+ img, err = buildImage(template, t, eng, false) |
|
440 |
+ if err != nil { |
|
441 |
+ t.Fatal(err) |
|
442 |
+ } |
|
437 | 443 |
|
438 | 444 |
if imageId == img.ID { |
439 | 445 |
t.Logf("Image ids should not match: %s == %s", imageId, img.ID) |
... | ... |
@@ -475,7 +516,7 @@ func TestForbiddenContextPath(t *testing.T) { |
475 | 475 |
} |
476 | 476 |
dockerfile := constructDockerfile(context.dockerfile, ip, port) |
477 | 477 |
|
478 |
- buildfile := docker.NewBuildFile(srv, ioutil.Discard, false, true, false) |
|
478 |
+ buildfile := docker.NewBuildFile(srv, ioutil.Discard, false, true, false, utils.NewStreamFormatter(false)) |
|
479 | 479 |
_, err = buildfile.Build(mkTestContext(dockerfile, context.files, t)) |
480 | 480 |
|
481 | 481 |
if err == nil { |
... | ... |
@@ -521,7 +562,7 @@ func TestBuildADDFileNotFound(t *testing.T) { |
521 | 521 |
} |
522 | 522 |
dockerfile := constructDockerfile(context.dockerfile, ip, port) |
523 | 523 |
|
524 |
- buildfile := docker.NewBuildFile(mkServerFromEngine(eng, t), ioutil.Discard, false, true, false) |
|
524 |
+ buildfile := docker.NewBuildFile(mkServerFromEngine(eng, t), ioutil.Discard, false, true, false, utils.NewStreamFormatter(false)) |
|
525 | 525 |
_, err = buildfile.Build(mkTestContext(dockerfile, context.files, t)) |
526 | 526 |
|
527 | 527 |
if err == nil { |
... | ... |
@@ -539,18 +580,26 @@ func TestBuildInheritance(t *testing.T) { |
539 | 539 |
eng := NewTestEngine(t) |
540 | 540 |
defer nuke(mkRuntimeFromEngine(eng, t)) |
541 | 541 |
|
542 |
- img := buildImage(testContextTemplate{` |
|
542 |
+ img, err := buildImage(testContextTemplate{` |
|
543 | 543 |
from {IMAGE} |
544 | 544 |
expose 4243 |
545 | 545 |
`, |
546 | 546 |
nil, nil}, t, eng, true) |
547 | 547 |
|
548 |
- img2 := buildImage(testContextTemplate{fmt.Sprintf(` |
|
548 |
+ if err != nil { |
|
549 |
+ t.Fatal(err) |
|
550 |
+ } |
|
551 |
+ |
|
552 |
+ img2, _ := buildImage(testContextTemplate{fmt.Sprintf(` |
|
549 | 553 |
from %s |
550 | 554 |
entrypoint ["/bin/echo"] |
551 | 555 |
`, img.ID), |
552 | 556 |
nil, nil}, t, eng, true) |
553 | 557 |
|
558 |
+ if err != nil { |
|
559 |
+ t.Fatal(err) |
|
560 |
+ } |
|
561 |
+ |
|
554 | 562 |
// from child |
555 | 563 |
if img2.Config.Entrypoint[0] != "/bin/echo" { |
556 | 564 |
t.Fail() |
... | ... |
@@ -561,3 +610,23 @@ func TestBuildInheritance(t *testing.T) { |
561 | 561 |
t.Fail() |
562 | 562 |
} |
563 | 563 |
} |
564 |
+ |
|
565 |
+func TestBuildFails(t *testing.T) { |
|
566 |
+ _, err := buildImage(testContextTemplate{` |
|
567 |
+ from {IMAGE} |
|
568 |
+ run sh -c "exit 23" |
|
569 |
+ `, |
|
570 |
+ nil, nil}, t, nil, true) |
|
571 |
+ |
|
572 |
+ if err == nil { |
|
573 |
+ t.Fatal("Error should not be nil") |
|
574 |
+ } |
|
575 |
+ |
|
576 |
+ sterr, ok := err.(*utils.JSONError) |
|
577 |
+ if !ok { |
|
578 |
+ t.Fatalf("Error should be utils.JSONError") |
|
579 |
+ } |
|
580 |
+ if sterr.Code != 23 { |
|
581 |
+ t.Fatalf("StatusCode %d unexpected, should be 23", sterr.Code) |
|
582 |
+ } |
|
583 |
+} |
... | ... |
@@ -905,9 +905,12 @@ run [ "$(ls -d /var/run/sshd)" = "/var/run/sshd" ] |
905 | 905 |
nil, |
906 | 906 |
nil, |
907 | 907 |
} |
908 |
- image := buildImage(testBuilder, t, eng, true) |
|
908 |
+ image, err := buildImage(testBuilder, t, eng, true) |
|
909 |
+ if err != nil { |
|
910 |
+ t.Fatal(err) |
|
911 |
+ } |
|
909 | 912 |
|
910 |
- err := mkServerFromEngine(eng, t).ContainerTag(image.ID, "test", "latest", false) |
|
913 |
+ err = mkServerFromEngine(eng, t).ContainerTag(image.ID, "test", "latest", false) |
|
911 | 914 |
if err != nil { |
912 | 915 |
t.Fatal(err) |
913 | 916 |
} |
... | ... |
@@ -1184,11 +1184,12 @@ func (graph *DependencyGraph) GenerateTraversalMap() ([][]string, error) { |
1184 | 1184 |
|
1185 | 1185 |
// An StatusError reports an unsuccessful exit by a command. |
1186 | 1186 |
type StatusError struct { |
1187 |
- Status int |
|
1187 |
+ Status string |
|
1188 |
+ StatusCode int |
|
1188 | 1189 |
} |
1189 | 1190 |
|
1190 | 1191 |
func (e *StatusError) Error() string { |
1191 |
- return fmt.Sprintf("Status: %d", e.Status) |
|
1192 |
+ return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode) |
|
1192 | 1193 |
} |
1193 | 1194 |
|
1194 | 1195 |
func quote(word string, buf *bytes.Buffer) { |