Skip to content

Commit 908b771

Browse files
committed
Add code to return message field of returned registry errors
Docker registries return errors in a know format so this change now checks for these errors and returns the message field. If the error is not in the expected format fall back to the original behaviour. #3076 Signed-off-by: Jack Baines <[email protected]>
1 parent 5840ecc commit 908b771

5 files changed

Lines changed: 526 additions & 1 deletion

File tree

remotes/docker/fetcher.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@ package docker
1818

1919
import (
2020
"context"
21+
"encoding/json"
2122
"fmt"
2223
"io"
2324
"io/ioutil"
2425
"net/http"
2526
"path"
2627
"strings"
2728

29+
"github.com/docker/distribution/registry/api/errcode"
30+
2831
"github.com/containerd/containerd/errdefs"
2932
"github.com/containerd/containerd/images"
3033
"github.com/containerd/containerd/log"
@@ -102,10 +105,18 @@ func (r dockerFetcher) open(ctx context.Context, u, mediatype string, offset int
102105
// can discard the bytes, hiding the seek behavior from the
103106
// implementation.
104107

105-
resp.Body.Close()
108+
defer resp.Body.Close()
106109
if resp.StatusCode == http.StatusNotFound {
107110
return nil, errors.Wrapf(errdefs.ErrNotFound, "content at %v not found", u)
108111
}
112+
body, err := ioutil.ReadAll(resp.Body)
113+
if err == nil {
114+
dockerErr := errcode.Errors{}
115+
err := json.Unmarshal(body, &dockerErr)
116+
if err == nil && dockerErr.Len() > 0 {
117+
return nil, errors.Errorf("unexpected status code %v: %s - Server message: %s", u, resp.Status, dockerErr.Error())
118+
}
119+
}
109120
return nil, errors.Errorf("unexpected status code %v: %v", u, resp.Status)
110121
}
111122
if offset > 0 {

remotes/docker/fetcher_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@ package docker
1818

1919
import (
2020
"context"
21+
"encoding/json"
2122
"fmt"
23+
"io"
2224
"io/ioutil"
2325
"math/rand"
2426
"net/http"
2527
"net/http/httptest"
2628
"testing"
29+
30+
"github.com/docker/distribution/registry/api/errcode"
31+
"github.com/pkg/errors"
32+
"gotest.tools/assert"
2733
)
2834

2935
func TestFetcherOpen(t *testing.T) {
@@ -92,3 +98,66 @@ func TestFetcherOpen(t *testing.T) {
9298
t.Fatal("expected error opening with invalid server response")
9399
}
94100
}
101+
102+
// New set of test to test new error cases
103+
func Test_dockerFetcher_open(t *testing.T) {
104+
tests := []struct {
105+
name string
106+
mockedStatus int
107+
mockedErr error
108+
want io.ReadCloser
109+
wantErr bool
110+
wantServerMessageError bool
111+
wantPlainError bool
112+
}{
113+
{
114+
name: "should return status and error.message if it exists if the registry request fails",
115+
mockedStatus: 500,
116+
mockedErr: errcode.Errors{errcode.Error{
117+
Code: errcode.ErrorCodeUnknown,
118+
Message: "Test Error",
119+
}},
120+
want: nil,
121+
wantErr: true,
122+
wantServerMessageError: true,
123+
},
124+
{
125+
name: "should return just status if the registry request fails and does not return a docker error",
126+
mockedStatus: 500,
127+
mockedErr: fmt.Errorf("Non-docker error"),
128+
want: nil,
129+
wantErr: true,
130+
wantPlainError: true,
131+
},
132+
}
133+
for _, tt := range tests {
134+
t.Run(tt.name, func(t *testing.T) {
135+
136+
s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
137+
rw.WriteHeader(tt.mockedStatus)
138+
bytes, _ := json.Marshal(tt.mockedErr)
139+
rw.Write(bytes)
140+
}))
141+
defer s.Close()
142+
143+
r := dockerFetcher{&dockerBase{
144+
client: s.Client(),
145+
}}
146+
147+
got, err := r.open(context.TODO(), s.URL, "", 0)
148+
assert.Equal(t, tt.wantErr, (err != nil))
149+
assert.Equal(t, tt.want, got)
150+
if tt.wantErr {
151+
var expectedError error
152+
if tt.wantServerMessageError {
153+
expectedError = errors.Errorf("unexpected status code %v: %v %s - Server message: %s", s.URL, tt.mockedStatus, http.StatusText(tt.mockedStatus), tt.mockedErr.Error())
154+
} else if tt.wantPlainError {
155+
expectedError = errors.Errorf("unexpected status code %v: %v %s", s.URL, tt.mockedStatus, http.StatusText(tt.mockedStatus))
156+
}
157+
assert.Equal(t, expectedError.Error(), err.Error())
158+
159+
}
160+
161+
})
162+
}
163+
}

vendor/github.com/docker/distribution/registry/api/errcode/errors.go

Lines changed: 267 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)