1 package handlers
2
3 import (
4 "encoding/json"
5 "net/http"
6 "net/http/httptest"
7 "net/url"
8 "reflect"
9 "testing"
10
11 "github.com/docker/distribution/configuration"
12 "github.com/docker/distribution/context"
13 "github.com/docker/distribution/registry/api/errcode"
14 v2 "github.com/docker/distribution/registry/api/v2"
15 "github.com/docker/distribution/registry/auth"
16 _ "github.com/docker/distribution/registry/auth/silly"
17 "github.com/docker/distribution/registry/storage"
18 memorycache "github.com/docker/distribution/registry/storage/cache/memory"
19 "github.com/docker/distribution/registry/storage/driver/testdriver"
20 )
21
22
23
24
25
26 func TestAppDispatcher(t *testing.T) {
27 driver := testdriver.New()
28 ctx := context.Background()
29 registry, err := storage.NewRegistry(ctx, driver, storage.BlobDescriptorCacheProvider(memorycache.NewInMemoryBlobDescriptorCacheProvider()), storage.EnableDelete, storage.EnableRedirect)
30 if err != nil {
31 t.Fatalf("error creating registry: %v", err)
32 }
33 app := &App{
34 Config: &configuration.Configuration{},
35 Context: ctx,
36 router: v2.Router(),
37 driver: driver,
38 registry: registry,
39 }
40 server := httptest.NewServer(app)
41 defer server.Close()
42 router := v2.Router()
43
44 serverURL, err := url.Parse(server.URL)
45 if err != nil {
46 t.Fatalf("error parsing server url: %v", err)
47 }
48
49 varCheckingDispatcher := func(expectedVars map[string]string) dispatchFunc {
50 return func(ctx *Context, r *http.Request) http.Handler {
51
52 if ctx.Repository.Named().Name() != getName(ctx) {
53 t.Fatalf("unexpected name: %q != %q", ctx.Repository.Named().Name(), "foo/bar")
54 }
55
56
57 for expectedK, expectedV := range expectedVars {
58 if ctx.Value(expectedK) != expectedV {
59 t.Fatalf("unexpected %s in context vars: %q != %q", expectedK, ctx.Value(expectedK), expectedV)
60 }
61 }
62
63
64 for k, v := range ctx.Value("vars").(map[string]string) {
65 _, ok := expectedVars[k]
66
67 if !ok {
68
69 t.Fatalf("unexpected key %q in vars with value %q", k, v)
70 }
71 }
72
73 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
74 w.WriteHeader(http.StatusOK)
75 })
76 }
77 }
78
79
80 unflatten := func(vars []string) map[string]string {
81 m := make(map[string]string)
82 for i := 0; i < len(vars)-1; i = i + 2 {
83 m[vars[i]] = vars[i+1]
84 }
85
86 return m
87 }
88
89 for _, testcase := range []struct {
90 endpoint string
91 vars []string
92 }{
93 {
94 endpoint: v2.RouteNameManifest,
95 vars: []string{
96 "name", "foo/bar",
97 "reference", "sometag",
98 },
99 },
100 {
101 endpoint: v2.RouteNameTags,
102 vars: []string{
103 "name", "foo/bar",
104 },
105 },
106 {
107 endpoint: v2.RouteNameBlobUpload,
108 vars: []string{
109 "name", "foo/bar",
110 },
111 },
112 {
113 endpoint: v2.RouteNameBlobUploadChunk,
114 vars: []string{
115 "name", "foo/bar",
116 "uuid", "theuuid",
117 },
118 },
119 } {
120 app.register(testcase.endpoint, varCheckingDispatcher(unflatten(testcase.vars)))
121 route := router.GetRoute(testcase.endpoint).Host(serverURL.Host)
122 u, err := route.URL(testcase.vars...)
123
124 if err != nil {
125 t.Fatal(err)
126 }
127
128 resp, err := http.Get(u.String())
129
130 if err != nil {
131 t.Fatal(err)
132 }
133
134 if resp.StatusCode != http.StatusOK {
135 t.Fatalf("unexpected status code: %v != %v", resp.StatusCode, http.StatusOK)
136 }
137 }
138 }
139
140
141
142 func TestNewApp(t *testing.T) {
143 ctx := context.Background()
144 config := configuration.Configuration{
145 Storage: configuration.Storage{
146 "testdriver": nil,
147 "maintenance": configuration.Parameters{"uploadpurging": map[interface{}]interface{}{
148 "enabled": false,
149 }},
150 },
151 Auth: configuration.Auth{
152
153
154 "silly": {
155 "realm": "realm-test",
156 "service": "service-test",
157 },
158 },
159 }
160
161
162
163
164 app := NewApp(ctx, &config)
165
166 server := httptest.NewServer(app)
167 defer server.Close()
168 builder, err := v2.NewURLBuilderFromString(server.URL, false)
169 if err != nil {
170 t.Fatalf("error creating urlbuilder: %v", err)
171 }
172
173 baseURL, err := builder.BuildBaseURL()
174 if err != nil {
175 t.Fatalf("error creating baseURL: %v", err)
176 }
177
178
179
180
181 req, err := http.Get(baseURL)
182 if err != nil {
183 t.Fatalf("unexpected error during GET: %v", err)
184 }
185 defer req.Body.Close()
186
187 if req.StatusCode != http.StatusUnauthorized {
188 t.Fatalf("unexpected status code during request: %v", err)
189 }
190
191 if req.Header.Get("Content-Type") != "application/json; charset=utf-8" {
192 t.Fatalf("unexpected content-type: %v != %v", req.Header.Get("Content-Type"), "application/json; charset=utf-8")
193 }
194
195 expectedAuthHeader := "Bearer realm=\"realm-test\",service=\"service-test\""
196 if e, a := expectedAuthHeader, req.Header.Get("WWW-Authenticate"); e != a {
197 t.Fatalf("unexpected WWW-Authenticate header: %q != %q", e, a)
198 }
199
200 var errs errcode.Errors
201 dec := json.NewDecoder(req.Body)
202 if err := dec.Decode(&errs); err != nil {
203 t.Fatalf("error decoding error response: %v", err)
204 }
205
206 err2, ok := errs[0].(errcode.ErrorCoder)
207 if !ok {
208 t.Fatalf("not an ErrorCoder: %#v", errs[0])
209 }
210 if err2.ErrorCode() != errcode.ErrorCodeUnauthorized {
211 t.Fatalf("unexpected error code: %v != %v", err2.ErrorCode(), errcode.ErrorCodeUnauthorized)
212 }
213 }
214
215
216 func TestAppendAccessRecords(t *testing.T) {
217 repo := "testRepo"
218
219 expectedResource := auth.Resource{
220 Type: "repository",
221 Name: repo,
222 }
223
224 expectedPullRecord := auth.Access{
225 Resource: expectedResource,
226 Action: "pull",
227 }
228 expectedPushRecord := auth.Access{
229 Resource: expectedResource,
230 Action: "push",
231 }
232 expectedDeleteRecord := auth.Access{
233 Resource: expectedResource,
234 Action: "delete",
235 }
236
237 records := []auth.Access{}
238 result := appendAccessRecords(records, "GET", repo)
239 expectedResult := []auth.Access{expectedPullRecord}
240 if ok := reflect.DeepEqual(result, expectedResult); !ok {
241 t.Fatalf("Actual access record differs from expected")
242 }
243
244 records = []auth.Access{}
245 result = appendAccessRecords(records, "HEAD", repo)
246 expectedResult = []auth.Access{expectedPullRecord}
247 if ok := reflect.DeepEqual(result, expectedResult); !ok {
248 t.Fatalf("Actual access record differs from expected")
249 }
250
251 records = []auth.Access{}
252 result = appendAccessRecords(records, "POST", repo)
253 expectedResult = []auth.Access{expectedPullRecord, expectedPushRecord}
254 if ok := reflect.DeepEqual(result, expectedResult); !ok {
255 t.Fatalf("Actual access record differs from expected")
256 }
257
258 records = []auth.Access{}
259 result = appendAccessRecords(records, "PUT", repo)
260 expectedResult = []auth.Access{expectedPullRecord, expectedPushRecord}
261 if ok := reflect.DeepEqual(result, expectedResult); !ok {
262 t.Fatalf("Actual access record differs from expected")
263 }
264
265 records = []auth.Access{}
266 result = appendAccessRecords(records, "PATCH", repo)
267 expectedResult = []auth.Access{expectedPullRecord, expectedPushRecord}
268 if ok := reflect.DeepEqual(result, expectedResult); !ok {
269 t.Fatalf("Actual access record differs from expected")
270 }
271
272 records = []auth.Access{}
273 result = appendAccessRecords(records, "DELETE", repo)
274 expectedResult = []auth.Access{expectedDeleteRecord}
275 if ok := reflect.DeepEqual(result, expectedResult); !ok {
276 t.Fatalf("Actual access record differs from expected")
277 }
278
279 }
280
View as plain text