Support HEAD requests without Docker-Content-Digest header
A statically hosted registry that responds correctly to GET with a manifest will load the right digest (by looking at the manifest body and calculating the digest). If the registry returns a HEAD without `Docker-Content-Digest`, then the client Tags().Get() call will return an empty digest. This commit changes the client to fallback to loading the tag via GET if the `Docker-Content-Digest` header is not set. Signed-off-by: Clayton Coleman <ccoleman@redhat.com>master
							parent
							
								
									d9e0121fef
								
							
						
					
					
						commit
						a2015272c1
					
				|  | @ -321,7 +321,8 @@ func (t *tags) Get(ctx context.Context, tag string) (distribution.Descriptor, er | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
| 
 | 
 | ||||||
| 	switch { | 	switch { | ||||||
| 	case resp.StatusCode >= 200 && resp.StatusCode < 400: | 	case resp.StatusCode >= 200 && resp.StatusCode < 400 && len(resp.Header.Get("Docker-Content-Digest")) > 0: | ||||||
|  | 		// if the response is a success AND a Docker-Content-Digest can be retrieved from the headers
 | ||||||
| 		return descriptorFromResponse(resp) | 		return descriptorFromResponse(resp) | ||||||
| 	default: | 	default: | ||||||
| 		// if the response is an error - there will be no body to decode.
 | 		// if the response is an error - there will be no body to decode.
 | ||||||
|  |  | ||||||
|  | @ -647,7 +647,38 @@ func addTestManifest(repo reference.Named, reference string, mediatype string, c | ||||||
| 			}), | 			}), | ||||||
| 		}, | 		}, | ||||||
| 	}) | 	}) | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|  | func addTestManifestWithoutDigestHeader(repo reference.Named, reference string, mediatype string, content []byte, m *testutil.RequestResponseMap) { | ||||||
|  | 	*m = append(*m, testutil.RequestResponseMapping{ | ||||||
|  | 		Request: testutil.Request{ | ||||||
|  | 			Method: "GET", | ||||||
|  | 			Route:  "/v2/" + repo.Name() + "/manifests/" + reference, | ||||||
|  | 		}, | ||||||
|  | 		Response: testutil.Response{ | ||||||
|  | 			StatusCode: http.StatusOK, | ||||||
|  | 			Body:       content, | ||||||
|  | 			Headers: http.Header(map[string][]string{ | ||||||
|  | 				"Content-Length": {fmt.Sprint(len(content))}, | ||||||
|  | 				"Last-Modified":  {time.Now().Add(-1 * time.Second).Format(time.ANSIC)}, | ||||||
|  | 				"Content-Type":   {mediatype}, | ||||||
|  | 			}), | ||||||
|  | 		}, | ||||||
|  | 	}) | ||||||
|  | 	*m = append(*m, testutil.RequestResponseMapping{ | ||||||
|  | 		Request: testutil.Request{ | ||||||
|  | 			Method: "HEAD", | ||||||
|  | 			Route:  "/v2/" + repo.Name() + "/manifests/" + reference, | ||||||
|  | 		}, | ||||||
|  | 		Response: testutil.Response{ | ||||||
|  | 			StatusCode: http.StatusOK, | ||||||
|  | 			Headers: http.Header(map[string][]string{ | ||||||
|  | 				"Content-Length": {fmt.Sprint(len(content))}, | ||||||
|  | 				"Last-Modified":  {time.Now().Add(-1 * time.Second).Format(time.ANSIC)}, | ||||||
|  | 				"Content-Type":   {mediatype}, | ||||||
|  | 			}), | ||||||
|  | 		}, | ||||||
|  | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func checkEqualManifest(m1, m2 *schema1.SignedManifest) error { | func checkEqualManifest(m1, m2 *schema1.SignedManifest) error { | ||||||
|  | @ -994,6 +1025,36 @@ func TestObtainsErrorForMissingTag(t *testing.T) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestObtainsManifestForTagWithoutHeaders(t *testing.T) { | ||||||
|  | 	repo, _ := reference.WithName("test.example.com/repo") | ||||||
|  | 
 | ||||||
|  | 	var m testutil.RequestResponseMap | ||||||
|  | 	m1, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6) | ||||||
|  | 	_, pl, err := m1.Payload() | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	addTestManifestWithoutDigestHeader(repo, "1.0.0", schema1.MediaTypeSignedManifest, pl, &m) | ||||||
|  | 
 | ||||||
|  | 	e, c := testServer(m) | ||||||
|  | 	defer c() | ||||||
|  | 
 | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 	r, err := NewRepository(ctx, repo, e, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	tagService := r.Tags(ctx) | ||||||
|  | 
 | ||||||
|  | 	desc, err := tagService.Get(ctx, "1.0.0") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("Expected no error") | ||||||
|  | 	} | ||||||
|  | 	if desc.Digest != dgst { | ||||||
|  | 		t.Fatalf("Unexpected digest") | ||||||
|  | 	} | ||||||
|  | } | ||||||
| func TestManifestTagsPaginated(t *testing.T) { | func TestManifestTagsPaginated(t *testing.T) { | ||||||
| 	s := httptest.NewServer(http.NotFoundHandler()) | 	s := httptest.NewServer(http.NotFoundHandler()) | ||||||
| 	defer s.Close() | 	defer s.Close() | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue