Skip to content

Commit f9395dd

Browse files
Merge pull request #11067 from vrothberg/fix-10154-2
remote build: fix streaming and error handling
2 parents 1bf7a9e + 4df6e31 commit f9395dd

File tree

5 files changed

+38
-36
lines changed

5 files changed

+38
-36
lines changed

pkg/api/handlers/compat/images_build.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -393,16 +393,16 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
393393
defer auth.RemoveAuthfile(authfile)
394394

395395
// Channels all mux'ed in select{} below to follow API build protocol
396-
stdout := channel.NewWriter(make(chan []byte, 1))
396+
stdout := channel.NewWriter(make(chan []byte))
397397
defer stdout.Close()
398398

399-
auxout := channel.NewWriter(make(chan []byte, 1))
399+
auxout := channel.NewWriter(make(chan []byte))
400400
defer auxout.Close()
401401

402-
stderr := channel.NewWriter(make(chan []byte, 1))
402+
stderr := channel.NewWriter(make(chan []byte))
403403
defer stderr.Close()
404404

405-
reporter := channel.NewWriter(make(chan []byte, 1))
405+
reporter := channel.NewWriter(make(chan []byte))
406406
defer reporter.Close()
407407

408408
runtime := r.Context().Value("runtime").(*libpod.Runtime)
@@ -529,7 +529,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
529529

530530
enc := json.NewEncoder(body)
531531
enc.SetEscapeHTML(true)
532-
loop:
532+
533533
for {
534534
m := struct {
535535
Stream string `json:"stream,omitempty"`
@@ -543,13 +543,13 @@ loop:
543543
stderr.Write([]byte(err.Error()))
544544
}
545545
flush()
546-
case e := <-auxout.Chan():
546+
case e := <-reporter.Chan():
547547
m.Stream = string(e)
548548
if err := enc.Encode(m); err != nil {
549549
stderr.Write([]byte(err.Error()))
550550
}
551551
flush()
552-
case e := <-reporter.Chan():
552+
case e := <-auxout.Chan():
553553
m.Stream = string(e)
554554
if err := enc.Encode(m); err != nil {
555555
stderr.Write([]byte(err.Error()))
@@ -561,8 +561,8 @@ loop:
561561
logrus.Warnf("Failed to json encode error %v", err)
562562
}
563563
flush()
564+
return
564565
case <-runCtx.Done():
565-
flush()
566566
if success {
567567
if !utils.IsLibpodRequest(r) {
568568
m.Stream = fmt.Sprintf("Successfully built %12.12s\n", imageID)
@@ -579,7 +579,8 @@ loop:
579579
}
580580
}
581581
}
582-
break loop
582+
flush()
583+
return
583584
case <-r.Context().Done():
584585
cancel()
585586
logrus.Infof("Client disconnect reported for build %q / %q.", registry, query.Dockerfile)

pkg/bindings/connection.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string,
327327
uri := fmt.Sprintf("http://d/v%d.%d.%d/libpod"+endpoint, params...)
328328
logrus.Debugf("DoRequest Method: %s URI: %v", httpMethod, uri)
329329

330-
req, err := http.NewRequest(httpMethod, uri, httpBody)
330+
req, err := http.NewRequestWithContext(context.WithValue(context.Background(), clientKey, c), httpMethod, uri, httpBody)
331331
if err != nil {
332332
return nil, err
333333
}
@@ -337,7 +337,6 @@ func (c *Connection) DoRequest(httpBody io.Reader, httpMethod, endpoint string,
337337
for key, val := range header {
338338
req.Header.Set(key, val)
339339
}
340-
req = req.WithContext(context.WithValue(context.Background(), clientKey, c))
341340
// Give the Do three chances in the case of a comm/service hiccup
342341
for i := 0; i < 3; i++ {
343342
response, err = c.Client.Do(req) // nolint

pkg/bindings/images/build.go

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -391,42 +391,50 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
391391
dec := json.NewDecoder(body)
392392

393393
var id string
394-
var mErr error
395394
for {
396395
var s struct {
397396
Stream string `json:"stream,omitempty"`
398397
Error string `json:"error,omitempty"`
399398
}
400-
if err := dec.Decode(&s); err != nil {
401-
if errors.Is(err, io.EOF) {
402-
if mErr == nil && id == "" {
403-
mErr = errors.New("stream dropped, unexpected failure")
404-
}
405-
break
406-
}
407-
s.Error = err.Error() + "\n"
408-
}
409399

410400
select {
401+
// FIXME(vrothberg): it seems we always hit the EOF case below,
402+
// even when the server quit but it seems desirable to
403+
// distinguish a proper build from a transient EOF.
411404
case <-response.Request.Context().Done():
412-
return &entities.BuildReport{ID: id}, mErr
405+
return &entities.BuildReport{ID: id}, nil
413406
default:
414407
// non-blocking select
415408
}
416409

410+
if err := dec.Decode(&s); err != nil {
411+
if errors.Is(err, io.ErrUnexpectedEOF) {
412+
return nil, errors.Wrap(err, "server probably quit")
413+
}
414+
// EOF means the stream is over in which case we need
415+
// to have read the id.
416+
if errors.Is(err, io.EOF) && id != "" {
417+
break
418+
}
419+
return &entities.BuildReport{ID: id}, errors.Wrap(err, "decoding stream")
420+
}
421+
417422
switch {
418423
case s.Stream != "":
419-
stdout.Write([]byte(s.Stream))
420-
if iidRegex.Match([]byte(s.Stream)) {
424+
raw := []byte(s.Stream)
425+
stdout.Write(raw)
426+
if iidRegex.Match(raw) {
421427
id = strings.TrimSuffix(s.Stream, "\n")
422428
}
423429
case s.Error != "":
424-
mErr = errors.New(s.Error)
430+
// If there's an error, return directly. The stream
431+
// will be closed on return.
432+
return &entities.BuildReport{ID: id}, errors.New(s.Error)
425433
default:
426434
return &entities.BuildReport{ID: id}, errors.New("failed to parse build results stream, unexpected input")
427435
}
428436
}
429-
return &entities.BuildReport{ID: id}, mErr
437+
return &entities.BuildReport{ID: id}, nil
430438
}
431439

432440
func nTar(excludes []string, sources ...string) (io.ReadCloser, error) {

pkg/domain/infra/runtime_abi.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func NewImageEngine(facts *entities.PodmanConfig) (entities.ImageEngine, error)
3333
r, err := NewLibpodImageRuntime(facts.FlagSet, facts)
3434
return r, err
3535
case entities.TunnelMode:
36+
// TODO: look at me!
3637
ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity)
3738
return &tunnel.ImageEngine{ClientCtx: ctx}, err
3839
}

test/system/070-build.bats

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -749,16 +749,9 @@ RUN echo $random_string
749749
EOF
750750

751751
run_podman 125 build -t build_test --pull-never $tmpdir
752-
# FIXME: this is just ridiculous. Even after #10030 and #10034, Ubuntu
753-
# remote *STILL* flakes this test! It fails with the correct exit status,
754-
# but the error output is 'Error: stream dropped, unexpected failure'
755-
# Let's just stop checking on podman-remote. As long as it exits 125,
756-
# we're happy.
757-
if ! is_remote; then
758-
is "$output" \
759-
".*Error: error creating build container: quay.io/libpod/nosuchimage:nosuchtag: image not known" \
760-
"--pull-never fails with expected error message"
761-
fi
752+
is "$output" \
753+
".*Error: error creating build container: quay.io/libpod/nosuchimage:nosuchtag: image not known" \
754+
"--pull-never fails with expected error message"
762755
}
763756

764757
@test "podman build --logfile test" {

0 commit comments

Comments
 (0)