mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #17617 from askb/17168_pull_error_fix
Fix for #17168 issue
This commit is contained in:
commit
e2417e3e92
3 changed files with 54 additions and 13 deletions
|
@ -3,6 +3,7 @@ package graph
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/cliconfig"
|
"github.com/docker/docker/cliconfig"
|
||||||
|
@ -87,12 +88,13 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
lastErr error
|
// use a slice to append the error strings and return a joined string to caller
|
||||||
|
errors []string
|
||||||
|
|
||||||
// discardNoSupportErrors is used to track whether an endpoint encountered an error of type registry.ErrNoSupport
|
// discardNoSupportErrors is used to track whether an endpoint encountered an error of type registry.ErrNoSupport
|
||||||
// By default it is false, which means that if a ErrNoSupport error is encountered, it will be saved in lastErr.
|
// By default it is false, which means that if a ErrNoSupport error is encountered, it will be saved in errors.
|
||||||
// As soon as another kind of error is encountered, discardNoSupportErrors is set to true, avoiding the saving of
|
// As soon as another kind of error is encountered, discardNoSupportErrors is set to true, avoiding the saving of
|
||||||
// any subsequent ErrNoSupport errors in lastErr.
|
// any subsequent ErrNoSupport errors in errors.
|
||||||
// It's needed for pull-by-digest on v1 endpoints: if there are only v1 endpoints configured, the error should be
|
// It's needed for pull-by-digest on v1 endpoints: if there are only v1 endpoints configured, the error should be
|
||||||
// returned and displayed, but if there was a v2 endpoint which supports pull-by-digest, then the last relevant
|
// returned and displayed, but if there was a v2 endpoint which supports pull-by-digest, then the last relevant
|
||||||
// error is the ones from v2 endpoints not v1.
|
// error is the ones from v2 endpoints not v1.
|
||||||
|
@ -103,7 +105,7 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf
|
||||||
|
|
||||||
puller, err := newPuller(s, endpoint, repoInfo, imagePullConfig, sf)
|
puller, err := newPuller(s, endpoint, repoInfo, imagePullConfig, sf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lastErr = err
|
errors = append(errors, err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if fallback, err := puller.Pull(tag); err != nil {
|
if fallback, err := puller.Pull(tag); err != nil {
|
||||||
|
@ -111,28 +113,35 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf
|
||||||
if _, ok := err.(registry.ErrNoSupport); !ok {
|
if _, ok := err.(registry.ErrNoSupport); !ok {
|
||||||
// Because we found an error that's not ErrNoSupport, discard all subsequent ErrNoSupport errors.
|
// Because we found an error that's not ErrNoSupport, discard all subsequent ErrNoSupport errors.
|
||||||
discardNoSupportErrors = true
|
discardNoSupportErrors = true
|
||||||
// save the current error
|
// append subsequent errors
|
||||||
lastErr = err
|
errors = append(errors, err.Error())
|
||||||
} else if !discardNoSupportErrors {
|
} else if !discardNoSupportErrors {
|
||||||
// Save the ErrNoSupport error, because it's either the first error or all encountered errors
|
// Save the ErrNoSupport error, because it's either the first error or all encountered errors
|
||||||
// were also ErrNoSupport errors.
|
// were also ErrNoSupport errors.
|
||||||
lastErr = err
|
// append subsequent errors
|
||||||
|
errors = append(errors, err.Error())
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
logrus.Debugf("Not continuing with error: %v", err)
|
errors = append(errors, err.Error())
|
||||||
return err
|
logrus.Debugf("Not continuing with error: %v", fmt.Errorf(strings.Join(errors, "\n")))
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return fmt.Errorf(strings.Join(errors, "\n"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.eventsService.Log("pull", logName, "")
|
s.eventsService.Log("pull", logName, "")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if lastErr == nil {
|
if len(errors) == 0 {
|
||||||
lastErr = fmt.Errorf("no endpoints found for %s", image)
|
return fmt.Errorf("no endpoints found for %s", image)
|
||||||
}
|
}
|
||||||
return lastErr
|
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return fmt.Errorf(strings.Join(errors, "\n"))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeStatus writes a status message to out. If layersDownloaded is true, the
|
// writeStatus writes a status message to out. If layersDownloaded is true, the
|
||||||
|
|
|
@ -1849,3 +1849,30 @@ func (s *DockerDaemonSuite) TestBridgeIPIsExcludedFromAllocatorPool(c *check.C)
|
||||||
cont++
|
cont++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test daemon for no space left on device error
|
||||||
|
func (s *DockerDaemonSuite) TestDaemonNoSpaceleftOnDeviceError(c *check.C) {
|
||||||
|
// create a 2MiB image and mount it as graph root
|
||||||
|
cmd := exec.Command("dd", "of=/tmp/testfs.img", "bs=1M", "seek=2", "count=0")
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
c.Fatalf("dd failed: %v", err)
|
||||||
|
}
|
||||||
|
cmd = exec.Command("mkfs.ext4", "-F", "/tmp/testfs.img")
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
c.Fatalf("mkfs.ext4 failed: %v", err)
|
||||||
|
}
|
||||||
|
cmd = exec.Command("mkdir", "-p", "/tmp/testfs-mount")
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
c.Fatalf("mkdir failed: %v", err)
|
||||||
|
}
|
||||||
|
cmd = exec.Command("mount", "-t", "ext4", "-no", "loop,rw", "/tmp/testfs.img", "/tmp/testfs-mount")
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
c.Fatalf("mount failed: %v", err)
|
||||||
|
}
|
||||||
|
err := s.d.Start("--graph", "/tmp/testfs-mount")
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
// pull a repository large enough to fill the mount point
|
||||||
|
out, err := s.d.Cmd("pull", "registry:2")
|
||||||
|
c.Assert(out, check.Not(check.Equals), 1, check.Commentf("no space left on device"))
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
@ -219,6 +220,10 @@ func ContinueOnError(err error) bool {
|
||||||
return shouldV2Fallback(v)
|
return shouldV2Fallback(v)
|
||||||
case *client.UnexpectedHTTPResponseError:
|
case *client.UnexpectedHTTPResponseError:
|
||||||
return true
|
return true
|
||||||
|
case error:
|
||||||
|
if val := strings.Contains(err.Error(), strings.ToLower(syscall.ENOSPC.Error())); val {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// let's be nice and fallback if the error is a completely
|
// let's be nice and fallback if the error is a completely
|
||||||
// unexpected one.
|
// unexpected one.
|
||||||
|
|
Loading…
Add table
Reference in a new issue