| ... | ... |
@@ -3,6 +3,7 @@ package graph |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"io" |
| 6 |
+ "strings" |
|
| 6 | 7 |
|
| 7 | 8 |
"github.com/Sirupsen/logrus" |
| 8 | 9 |
"github.com/docker/docker/cliconfig" |
| ... | ... |
@@ -87,12 +88,13 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf |
| 87 | 87 |
} |
| 88 | 88 |
|
| 89 | 89 |
var ( |
| 90 |
- lastErr error |
|
| 90 |
+ // use a slice to append the error strings and return a joined string to caller |
|
| 91 |
+ errors []string |
|
| 91 | 92 |
|
| 92 | 93 |
// discardNoSupportErrors is used to track whether an endpoint encountered an error of type registry.ErrNoSupport |
| 93 |
- // By default it is false, which means that if a ErrNoSupport error is encountered, it will be saved in lastErr. |
|
| 94 |
+ // By default it is false, which means that if a ErrNoSupport error is encountered, it will be saved in errors. |
|
| 94 | 95 |
// As soon as another kind of error is encountered, discardNoSupportErrors is set to true, avoiding the saving of |
| 95 |
- // any subsequent ErrNoSupport errors in lastErr. |
|
| 96 |
+ // any subsequent ErrNoSupport errors in errors. |
|
| 96 | 97 |
// It's needed for pull-by-digest on v1 endpoints: if there are only v1 endpoints configured, the error should be |
| 97 | 98 |
// returned and displayed, but if there was a v2 endpoint which supports pull-by-digest, then the last relevant |
| 98 | 99 |
// error is the ones from v2 endpoints not v1. |
| ... | ... |
@@ -103,7 +105,7 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf |
| 103 | 103 |
|
| 104 | 104 |
puller, err := newPuller(s, endpoint, repoInfo, imagePullConfig, sf) |
| 105 | 105 |
if err != nil {
|
| 106 |
- lastErr = err |
|
| 106 |
+ errors = append(errors, err.Error()) |
|
| 107 | 107 |
continue |
| 108 | 108 |
} |
| 109 | 109 |
if fallback, err := puller.Pull(tag); err != nil {
|
| ... | ... |
@@ -111,28 +113,35 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf |
| 111 | 111 |
if _, ok := err.(registry.ErrNoSupport); !ok {
|
| 112 | 112 |
// Because we found an error that's not ErrNoSupport, discard all subsequent ErrNoSupport errors. |
| 113 | 113 |
discardNoSupportErrors = true |
| 114 |
- // save the current error |
|
| 115 |
- lastErr = err |
|
| 114 |
+ // append subsequent errors |
|
| 115 |
+ errors = append(errors, err.Error()) |
|
| 116 | 116 |
} else if !discardNoSupportErrors {
|
| 117 | 117 |
// Save the ErrNoSupport error, because it's either the first error or all encountered errors |
| 118 | 118 |
// were also ErrNoSupport errors. |
| 119 |
- lastErr = err |
|
| 119 |
+ // append subsequent errors |
|
| 120 |
+ errors = append(errors, err.Error()) |
|
| 120 | 121 |
} |
| 121 | 122 |
continue |
| 122 | 123 |
} |
| 123 |
- logrus.Debugf("Not continuing with error: %v", err)
|
|
| 124 |
- return err |
|
| 125 |
- |
|
| 124 |
+ errors = append(errors, err.Error()) |
|
| 125 |
+ logrus.Debugf("Not continuing with error: %v", fmt.Errorf(strings.Join(errors, "\n")))
|
|
| 126 |
+ if len(errors) > 0 {
|
|
| 127 |
+ return fmt.Errorf(strings.Join(errors, "\n")) |
|
| 128 |
+ } |
|
| 126 | 129 |
} |
| 127 | 130 |
|
| 128 | 131 |
s.eventsService.Log("pull", logName, "")
|
| 129 | 132 |
return nil |
| 130 | 133 |
} |
| 131 | 134 |
|
| 132 |
- if lastErr == nil {
|
|
| 133 |
- lastErr = fmt.Errorf("no endpoints found for %s", image)
|
|
| 135 |
+ if len(errors) == 0 {
|
|
| 136 |
+ return fmt.Errorf("no endpoints found for %s", image)
|
|
| 137 |
+ } |
|
| 138 |
+ |
|
| 139 |
+ if len(errors) > 0 {
|
|
| 140 |
+ return fmt.Errorf(strings.Join(errors, "\n")) |
|
| 134 | 141 |
} |
| 135 |
- return lastErr |
|
| 142 |
+ return nil |
|
| 136 | 143 |
} |
| 137 | 144 |
|
| 138 | 145 |
// writeStatus writes a status message to out. If layersDownloaded is true, the |
| ... | ... |
@@ -1849,3 +1849,30 @@ func (s *DockerDaemonSuite) TestBridgeIPIsExcludedFromAllocatorPool(c *check.C) |
| 1849 | 1849 |
cont++ |
| 1850 | 1850 |
} |
| 1851 | 1851 |
} |
| 1852 |
+ |
|
| 1853 |
+// Test daemon for no space left on device error |
|
| 1854 |
+func (s *DockerDaemonSuite) TestDaemonNoSpaceleftOnDeviceError(c *check.C) {
|
|
| 1855 |
+ // create a 2MiB image and mount it as graph root |
|
| 1856 |
+ cmd := exec.Command("dd", "of=/tmp/testfs.img", "bs=1M", "seek=2", "count=0")
|
|
| 1857 |
+ if err := cmd.Run(); err != nil {
|
|
| 1858 |
+ c.Fatalf("dd failed: %v", err)
|
|
| 1859 |
+ } |
|
| 1860 |
+ cmd = exec.Command("mkfs.ext4", "-F", "/tmp/testfs.img")
|
|
| 1861 |
+ if err := cmd.Run(); err != nil {
|
|
| 1862 |
+ c.Fatalf("mkfs.ext4 failed: %v", err)
|
|
| 1863 |
+ } |
|
| 1864 |
+ cmd = exec.Command("mkdir", "-p", "/tmp/testfs-mount")
|
|
| 1865 |
+ if err := cmd.Run(); err != nil {
|
|
| 1866 |
+ c.Fatalf("mkdir failed: %v", err)
|
|
| 1867 |
+ } |
|
| 1868 |
+ cmd = exec.Command("mount", "-t", "ext4", "-no", "loop,rw", "/tmp/testfs.img", "/tmp/testfs-mount")
|
|
| 1869 |
+ if err := cmd.Run(); err != nil {
|
|
| 1870 |
+ c.Fatalf("mount failed: %v", err)
|
|
| 1871 |
+ } |
|
| 1872 |
+ err := s.d.Start("--graph", "/tmp/testfs-mount")
|
|
| 1873 |
+ c.Assert(err, check.IsNil) |
|
| 1874 |
+ |
|
| 1875 |
+ // pull a repository large enough to fill the mount point |
|
| 1876 |
+ out, err := s.d.Cmd("pull", "registry:2")
|
|
| 1877 |
+ c.Assert(out, check.Not(check.Equals), 1, check.Commentf("no space left on device"))
|
|
| 1878 |
+} |
| ... | ... |
@@ -13,6 +13,7 @@ import ( |
| 13 | 13 |
"path/filepath" |
| 14 | 14 |
"runtime" |
| 15 | 15 |
"strings" |
| 16 |
+ "syscall" |
|
| 16 | 17 |
"time" |
| 17 | 18 |
|
| 18 | 19 |
"github.com/Sirupsen/logrus" |
| ... | ... |
@@ -219,6 +220,10 @@ func ContinueOnError(err error) bool {
|
| 219 | 219 |
return shouldV2Fallback(v) |
| 220 | 220 |
case *client.UnexpectedHTTPResponseError: |
| 221 | 221 |
return true |
| 222 |
+ case error: |
|
| 223 |
+ if val := strings.Contains(err.Error(), strings.ToLower(syscall.ENOSPC.Error())); val {
|
|
| 224 |
+ return false |
|
| 225 |
+ } |
|
| 222 | 226 |
} |
| 223 | 227 |
// let's be nice and fallback if the error is a completely |
| 224 | 228 |
// unexpected one. |