Merge pull request #2402 from smarterclayton/order_manifest
Allow clients to request specific manifest media typesmaster
						commit
						7484e51bf6
					
				
							
								
								
									
										15
									
								
								registry.go
								
								
								
								
							
							
						
						
									
										15
									
								
								registry.go
								
								
								
								
							|  | @ -73,6 +73,21 @@ func (o WithTagOption) Apply(m ManifestService) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // WithManifestMediaTypes lists the media types the client wishes
 | ||||||
|  | // the server to provide.
 | ||||||
|  | func WithManifestMediaTypes(mediaTypes []string) ManifestServiceOption { | ||||||
|  | 	return WithManifestMediaTypesOption{mediaTypes} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // WithManifestMediaTypesOption holds a list of accepted media types
 | ||||||
|  | type WithManifestMediaTypesOption struct{ MediaTypes []string } | ||||||
|  | 
 | ||||||
|  | // Apply conforms to the ManifestServiceOption interface
 | ||||||
|  | func (o WithManifestMediaTypesOption) Apply(m ManifestService) error { | ||||||
|  | 	// no implementation
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Repository is a named collection of manifests and layers.
 | // Repository is a named collection of manifests and layers.
 | ||||||
| type Repository interface { | type Repository interface { | ||||||
| 	// Named returns the name of the repository.
 | 	// Named returns the name of the repository.
 | ||||||
|  |  | ||||||
|  | @ -418,18 +418,22 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis | ||||||
| 		ref         reference.Named | 		ref         reference.Named | ||||||
| 		err         error | 		err         error | ||||||
| 		contentDgst *digest.Digest | 		contentDgst *digest.Digest | ||||||
|  | 		mediaTypes  []string | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	for _, option := range options { | 	for _, option := range options { | ||||||
| 		if opt, ok := option.(distribution.WithTagOption); ok { | 		switch opt := option.(type) { | ||||||
|  | 		case distribution.WithTagOption: | ||||||
| 			digestOrTag = opt.Tag | 			digestOrTag = opt.Tag | ||||||
| 			ref, err = reference.WithTag(ms.name, opt.Tag) | 			ref, err = reference.WithTag(ms.name, opt.Tag) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, err | 				return nil, err | ||||||
| 			} | 			} | ||||||
| 		} else if opt, ok := option.(contentDigestOption); ok { | 		case contentDigestOption: | ||||||
| 			contentDgst = opt.digest | 			contentDgst = opt.digest | ||||||
| 		} else { | 		case distribution.WithManifestMediaTypesOption: | ||||||
|  | 			mediaTypes = opt.MediaTypes | ||||||
|  | 		default: | ||||||
| 			err := option.Apply(ms) | 			err := option.Apply(ms) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, err | 				return nil, err | ||||||
|  | @ -445,6 +449,10 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if len(mediaTypes) == 0 { | ||||||
|  | 		mediaTypes = distribution.ManifestMediaTypes() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	u, err := ms.ub.BuildManifestURL(ref) | 	u, err := ms.ub.BuildManifestURL(ref) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|  | @ -455,7 +463,7 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, t := range distribution.ManifestMediaTypes() { | 	for _, t := range mediaTypes { | ||||||
| 		req.Header.Add("Accept", t) | 		req.Header.Add("Accept", t) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,8 @@ import ( | ||||||
| 	"log" | 	"log" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/http/httptest" | 	"net/http/httptest" | ||||||
|  | 	"reflect" | ||||||
|  | 	"sort" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  | @ -815,6 +817,65 @@ func TestManifestFetchWithEtag(t *testing.T) { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestManifestFetchWithAccept(t *testing.T) { | ||||||
|  | 	ctx := context.Background() | ||||||
|  | 	repo, _ := reference.WithName("test.example.com/repo") | ||||||
|  | 	_, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6) | ||||||
|  | 	headers := make(chan []string, 1) | ||||||
|  | 	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||||||
|  | 		headers <- req.Header["Accept"] | ||||||
|  | 	})) | ||||||
|  | 	defer close(headers) | ||||||
|  | 	defer s.Close() | ||||||
|  | 
 | ||||||
|  | 	r, err := NewRepository(repo, s.URL, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	ms, err := r.Manifests(ctx) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	testCases := []struct { | ||||||
|  | 		// the media types we send
 | ||||||
|  | 		mediaTypes []string | ||||||
|  | 		// the expected Accept headers the server should receive
 | ||||||
|  | 		expect []string | ||||||
|  | 		// whether to sort the request and response values for comparison
 | ||||||
|  | 		sort bool | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			mediaTypes: []string{}, | ||||||
|  | 			expect:     distribution.ManifestMediaTypes(), | ||||||
|  | 			sort:       true, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			mediaTypes: []string{"test1", "test2"}, | ||||||
|  | 			expect:     []string{"test1", "test2"}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			mediaTypes: []string{"test1"}, | ||||||
|  | 			expect:     []string{"test1"}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			mediaTypes: []string{""}, | ||||||
|  | 			expect:     []string{""}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	for _, testCase := range testCases { | ||||||
|  | 		ms.Get(ctx, dgst, distribution.WithManifestMediaTypes(testCase.mediaTypes)) | ||||||
|  | 		actual := <-headers | ||||||
|  | 		if testCase.sort { | ||||||
|  | 			sort.Strings(actual) | ||||||
|  | 			sort.Strings(testCase.expect) | ||||||
|  | 		} | ||||||
|  | 		if !reflect.DeepEqual(actual, testCase.expect) { | ||||||
|  | 			t.Fatalf("unexpected Accept header values: %v", actual) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestManifestDelete(t *testing.T) { | func TestManifestDelete(t *testing.T) { | ||||||
| 	repo, _ := reference.WithName("test.example.com/repo/delete") | 	repo, _ := reference.WithName("test.example.com/repo/delete") | ||||||
| 	_, dgst1, _ := newRandomSchemaV1Manifest(repo, "latest", 6) | 	_, dgst1, _ := newRandomSchemaV1Manifest(repo, "latest", 6) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue