Implementation of the Manifest Service API refactor.
Add a generic Manifest interface to represent manifests in the registry and remove references to schema specific manifests. Add a ManifestBuilder to construct Manifest objects. Concrete manifest builders will exist for each manifest type and implementations will contain manifest specific data used to build a manifest. Remove Signatures() from Repository interface. Signatures are relevant only to schema1 manifests. Move access to the signature store inside the schema1 manifestStore. Add some API tests to verify signature roundtripping. schema1 ------- Change the way data is stored in schema1.Manifest to enable Payload() to be used to return complete Manifest JSON from the HTTP handler without knowledge of the schema1 protocol. tags ---- Move tag functionality to a seperate TagService and update ManifestService to use the new interfaces. Implement a driver based tagService to be backward compatible with the current tag service. Add a proxyTagService to enable the registry to get a digest for remote manifests from a tag. manifest store -------------- Remove revision store and move all signing functionality into the signed manifeststore. manifest registration --------------------- Add a mechanism to register manifest media types and to allow different manifest types to be Unmarshalled correctly. client ------ Add ManifestServiceOptions to client functions to allow tags to be passed into Put and Get for building correct registry URLs. Change functional arguments to be an interface type to allow passing data without mutating shared state. Signed-off-by: Richard Scothern <richard.scothern@gmail.com> Signed-off-by: Richard Scothern <richard.scothern@docker.com>
This commit is contained in:
parent
0fef25389d
commit
8efb9ca329
18 changed files with 1161 additions and 656 deletions
|
@ -42,7 +42,6 @@ func newRandomBlob(size int) (digest.Digest, []byte) {
|
|||
}
|
||||
|
||||
func addTestFetch(repo string, dgst digest.Digest, content []byte, m *testutil.RequestResponseMap) {
|
||||
|
||||
*m = append(*m, testutil.RequestResponseMapping{
|
||||
Request: testutil.Request{
|
||||
Method: "GET",
|
||||
|
@ -499,12 +498,7 @@ func newRandomSchemaV1Manifest(name, tag string, blobCount int) (*schema1.Signed
|
|||
panic(err)
|
||||
}
|
||||
|
||||
p, err := sm.Payload()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return sm, digest.FromBytes(p), p
|
||||
return sm, digest.FromBytes(sm.Canonical), sm.Canonical
|
||||
}
|
||||
|
||||
func addTestManifestWithEtag(repo, reference string, content []byte, m *testutil.RequestResponseMap, dgst string) {
|
||||
|
@ -525,6 +519,7 @@ func addTestManifestWithEtag(repo, reference string, content []byte, m *testutil
|
|||
Headers: http.Header(map[string][]string{
|
||||
"Content-Length": {"0"},
|
||||
"Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
|
||||
"Content-Type": {schema1.MediaTypeManifest},
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
|
@ -534,6 +529,7 @@ func addTestManifestWithEtag(repo, reference string, content []byte, m *testutil
|
|||
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": {schema1.MediaTypeManifest},
|
||||
}),
|
||||
}
|
||||
|
||||
|
@ -553,6 +549,7 @@ func addTestManifest(repo, reference string, content []byte, m *testutil.Request
|
|||
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": {schema1.MediaTypeManifest},
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
@ -566,6 +563,7 @@ func addTestManifest(repo, reference string, content []byte, m *testutil.Request
|
|||
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": {schema1.MediaTypeManifest},
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
@ -598,12 +596,17 @@ func checkEqualManifest(m1, m2 *schema1.SignedManifest) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func TestManifestFetch(t *testing.T) {
|
||||
func TestV1ManifestFetch(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repo := "test.example.com/repo"
|
||||
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||
var m testutil.RequestResponseMap
|
||||
addTestManifest(repo, dgst.String(), m1.Raw, &m)
|
||||
_, pl, err := m1.Payload()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
addTestManifest(repo, dgst.String(), pl, &m)
|
||||
addTestManifest(repo, "latest", pl, &m)
|
||||
|
||||
e, c := testServer(m)
|
||||
defer c()
|
||||
|
@ -617,7 +620,7 @@ func TestManifestFetch(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ok, err := ms.Exists(dgst)
|
||||
ok, err := ms.Exists(ctx, dgst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -625,11 +628,29 @@ func TestManifestFetch(t *testing.T) {
|
|||
t.Fatal("Manifest does not exist")
|
||||
}
|
||||
|
||||
manifest, err := ms.Get(dgst)
|
||||
manifest, err := ms.Get(ctx, dgst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := checkEqualManifest(manifest, m1); err != nil {
|
||||
v1manifest, ok := manifest.(*schema1.SignedManifest)
|
||||
if !ok {
|
||||
t.Fatalf("Unexpected manifest type from Get: %T", manifest)
|
||||
}
|
||||
|
||||
if err := checkEqualManifest(v1manifest, m1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
manifest, err = ms.Get(ctx, dgst, WithTag("latest"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
v1manifest, ok = manifest.(*schema1.SignedManifest)
|
||||
if !ok {
|
||||
t.Fatalf("Unexpected manifest type from Get: %T", manifest)
|
||||
}
|
||||
|
||||
if err = checkEqualManifest(v1manifest, m1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -643,17 +664,22 @@ func TestManifestFetchWithEtag(t *testing.T) {
|
|||
e, c := testServer(m)
|
||||
defer c()
|
||||
|
||||
r, err := NewRepository(context.Background(), repo, e, nil)
|
||||
ctx := context.Background()
|
||||
r, err := NewRepository(ctx, repo, e, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
ms, err := r.Manifests(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = ms.GetByTag("latest", AddEtagToTag("latest", d1.String()))
|
||||
clientManifestService, ok := ms.(*manifests)
|
||||
if !ok {
|
||||
panic("wrong type for client manifest service")
|
||||
}
|
||||
_, err = clientManifestService.Get(ctx, d1, WithTag("latest"), AddEtagToTag("latest", d1.String()))
|
||||
if err != distribution.ErrManifestNotModified {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -690,10 +716,10 @@ func TestManifestDelete(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := ms.Delete(dgst1); err != nil {
|
||||
if err := ms.Delete(ctx, dgst1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := ms.Delete(dgst2); err == nil {
|
||||
if err := ms.Delete(ctx, dgst2); err == nil {
|
||||
t.Fatal("Expected error deleting unknown manifest")
|
||||
}
|
||||
// TODO(dmcgowan): Check for specific unknown error
|
||||
|
@ -702,12 +728,17 @@ func TestManifestDelete(t *testing.T) {
|
|||
func TestManifestPut(t *testing.T) {
|
||||
repo := "test.example.com/repo/delete"
|
||||
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "other", 6)
|
||||
|
||||
_, payload, err := m1.Payload()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var m testutil.RequestResponseMap
|
||||
m = append(m, testutil.RequestResponseMapping{
|
||||
Request: testutil.Request{
|
||||
Method: "PUT",
|
||||
Route: "/v2/" + repo + "/manifests/other",
|
||||
Body: m1.Raw,
|
||||
Body: payload,
|
||||
},
|
||||
Response: testutil.Response{
|
||||
StatusCode: http.StatusAccepted,
|
||||
|
@ -731,7 +762,7 @@ func TestManifestPut(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := ms.Put(m1); err != nil {
|
||||
if _, err := ms.Put(ctx, m1, WithTag(m1.Tag)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -751,21 +782,22 @@ func TestManifestTags(t *testing.T) {
|
|||
}
|
||||
`))
|
||||
var m testutil.RequestResponseMap
|
||||
m = append(m, testutil.RequestResponseMapping{
|
||||
Request: testutil.Request{
|
||||
Method: "GET",
|
||||
Route: "/v2/" + repo + "/tags/list",
|
||||
},
|
||||
Response: testutil.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: tagsList,
|
||||
Headers: http.Header(map[string][]string{
|
||||
"Content-Length": {fmt.Sprint(len(tagsList))},
|
||||
"Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
m = append(m, testutil.RequestResponseMapping{
|
||||
Request: testutil.Request{
|
||||
Method: "GET",
|
||||
Route: "/v2/" + repo + "/tags/list",
|
||||
},
|
||||
Response: testutil.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: tagsList,
|
||||
Headers: http.Header(map[string][]string{
|
||||
"Content-Length": {fmt.Sprint(len(tagsList))},
|
||||
"Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
e, c := testServer(m)
|
||||
defer c()
|
||||
|
||||
|
@ -773,22 +805,29 @@ func TestManifestTags(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ms, err := r.Manifests(ctx)
|
||||
tagService := r.Tags(ctx)
|
||||
|
||||
tags, err := tagService.All(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tags, err := ms.Tags()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(tags) != 3 {
|
||||
t.Fatalf("Wrong number of tags returned: %d, expected 3", len(tags))
|
||||
}
|
||||
// TODO(dmcgowan): Check array
|
||||
|
||||
expected := map[string]struct{}{
|
||||
"tag1": {},
|
||||
"tag2": {},
|
||||
"funtag": {},
|
||||
}
|
||||
for _, t := range tags {
|
||||
delete(expected, t)
|
||||
}
|
||||
if len(expected) != 0 {
|
||||
t.Fatalf("unexpected tags returned: %v", expected)
|
||||
}
|
||||
// TODO(dmcgowan): Check for error cases
|
||||
}
|
||||
|
||||
|
@ -821,7 +860,7 @@ func TestManifestUnauthorized(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = ms.Get(dgst)
|
||||
_, err = ms.Get(ctx, dgst)
|
||||
if err == nil {
|
||||
t.Fatal("Expected error fetching manifest")
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue