Merge pull request #1319 from RichardScothern/update-tags
Remove tags referencing deleted manifests.master
						commit
						3a1220de01
					
				|  | @ -1623,6 +1623,7 @@ func testManifestDelete(t *testing.T, env *testEnv, args manifestArgs) { | |||
| 	dgst := args.dgst | ||||
| 	manifest := args.manifest | ||||
| 	manifestDigestURL, err := env.builder.BuildManifestURL(imageName, dgst.String()) | ||||
| 
 | ||||
| 	// ---------------
 | ||||
| 	// Delete by digest
 | ||||
| 	resp, err := httpDelete(manifestDigestURL) | ||||
|  | @ -1678,6 +1679,77 @@ func testManifestDelete(t *testing.T, env *testEnv, args manifestArgs) { | |||
| 	checkErr(t, err, "delting unknown manifest by digest") | ||||
| 	checkResponse(t, "fetching deleted manifest", resp, http.StatusNotFound) | ||||
| 
 | ||||
| 	// --------------------
 | ||||
| 	// Uupload manifest by tag
 | ||||
| 	tag := signedManifest.Tag | ||||
| 	manifestTagURL, err := env.builder.BuildManifestURL(imageName, tag) | ||||
| 	resp = putManifest(t, "putting signed manifest by tag", manifestTagURL, signedManifest) | ||||
| 	checkResponse(t, "putting signed manifest by tag", resp, http.StatusCreated) | ||||
| 	checkHeaders(t, resp, http.Header{ | ||||
| 		"Location":              []string{manifestDigestURL}, | ||||
| 		"Docker-Content-Digest": []string{dgst.String()}, | ||||
| 	}) | ||||
| 
 | ||||
| 	tagsURL, err := env.builder.BuildTagsURL(imageName) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error building tags url: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// Ensure that the tag is listed.
 | ||||
| 	resp, err = http.Get(tagsURL) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error getting unknown tags: %v", err) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	dec := json.NewDecoder(resp.Body) | ||||
| 	var tagsResponse tagsAPIResponse | ||||
| 	if err := dec.Decode(&tagsResponse); err != nil { | ||||
| 		t.Fatalf("unexpected error decoding error response: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if tagsResponse.Name != imageName { | ||||
| 		t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(tagsResponse.Tags) != 1 { | ||||
| 		t.Fatalf("expected some tags in response: %v", tagsResponse.Tags) | ||||
| 	} | ||||
| 
 | ||||
| 	if tagsResponse.Tags[0] != tag { | ||||
| 		t.Fatalf("tag not as expected: %q != %q", tagsResponse.Tags[0], tag) | ||||
| 	} | ||||
| 
 | ||||
| 	// ---------------
 | ||||
| 	// Delete by digest
 | ||||
| 	resp, err = httpDelete(manifestDigestURL) | ||||
| 	checkErr(t, err, "deleting manifest by digest") | ||||
| 
 | ||||
| 	checkResponse(t, "deleting manifest with tag", resp, http.StatusAccepted) | ||||
| 	checkHeaders(t, resp, http.Header{ | ||||
| 		"Content-Length": []string{"0"}, | ||||
| 	}) | ||||
| 
 | ||||
| 	// Ensure that the tag is not listed.
 | ||||
| 	resp, err = http.Get(tagsURL) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error getting unknown tags: %v", err) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	dec = json.NewDecoder(resp.Body) | ||||
| 	if err := dec.Decode(&tagsResponse); err != nil { | ||||
| 		t.Fatalf("unexpected error decoding error response: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if tagsResponse.Name != imageName { | ||||
| 		t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(tagsResponse.Tags) != 0 { | ||||
| 		t.Fatalf("expected 0 tags in response: %v", tagsResponse.Tags) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| type testEnv struct { | ||||
|  |  | |||
|  | @ -320,5 +320,19 @@ func (imh *imageManifestHandler) DeleteImageManifest(w http.ResponseWriter, r *h | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	tagService := imh.Repository.Tags(imh) | ||||
| 	referencedTags, err := tagService.Lookup(imh, distribution.Descriptor{Digest: imh.Digest}) | ||||
| 	if err != nil { | ||||
| 		imh.Errors = append(imh.Errors, err) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tag := range referencedTags { | ||||
| 		if err := tagService.Untag(imh, tag); err != nil { | ||||
| 			imh.Errors = append(imh.Errors, err) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	w.WriteHeader(http.StatusAccepted) | ||||
| } | ||||
|  |  | |||
|  | @ -116,15 +116,19 @@ func (ts *tagStore) Get(ctx context.Context, tag string) (distribution.Descripto | |||
| 	return distribution.Descriptor{Digest: revision}, nil | ||||
| } | ||||
| 
 | ||||
| // delete removes the tag from repository, including the history of all
 | ||||
| // revisions that have the specified tag.
 | ||||
| // Untag removes the tag association
 | ||||
| func (ts *tagStore) Untag(ctx context.Context, tag string) error { | ||||
| 	tagPath, err := pathFor(manifestTagPathSpec{ | ||||
| 		name: ts.repository.Name(), | ||||
| 		tag:  tag, | ||||
| 	}) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 	switch err.(type) { | ||||
| 	case storagedriver.PathNotFoundError: | ||||
| 		return distribution.ErrTagUnknown{Tag: tag} | ||||
| 	case nil: | ||||
| 		break | ||||
| 	default: | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
|  | @ -153,7 +157,35 @@ func (ts *tagStore) linkedBlobStore(ctx context.Context, tag string) *linkedBlob | |||
| 
 | ||||
| // Lookup recovers a list of tags which refer to this digest.  When a manifest is deleted by
 | ||||
| // digest, tag entries which point to it need to be recovered to avoid dangling tags.
 | ||||
| func (ts *tagStore) Lookup(ctx context.Context, digest distribution.Descriptor) ([]string, error) { | ||||
| 	// An efficient implementation of this will require changes to the S3 driver.
 | ||||
| 	return make([]string, 0), nil | ||||
| func (ts *tagStore) Lookup(ctx context.Context, desc distribution.Descriptor) ([]string, error) { | ||||
| 	allTags, err := ts.All(ctx) | ||||
| 	switch err.(type) { | ||||
| 	case distribution.ErrRepositoryUnknown: | ||||
| 		// This tag store has been initialized but not yet populated
 | ||||
| 		break | ||||
| 	case nil: | ||||
| 		break | ||||
| 	default: | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	var tags []string | ||||
| 	for _, tag := range allTags { | ||||
| 		tagLinkPathSpec := manifestTagCurrentPathSpec{ | ||||
| 			name: ts.repository.Name(), | ||||
| 			tag:  tag, | ||||
| 		} | ||||
| 
 | ||||
| 		tagLinkPath, err := pathFor(tagLinkPathSpec) | ||||
| 		tagDigest, err := ts.blobStore.readlink(ctx, tagLinkPath) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		if tagDigest == desc.Digest { | ||||
| 			tags = append(tags, tag) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return tags, nil | ||||
| } | ||||
|  |  | |||
|  | @ -102,7 +102,7 @@ func TestTagStoreUnTag(t *testing.T) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestTagAll(t *testing.T) { | ||||
| func TestTagStoreAll(t *testing.T) { | ||||
| 	env := testTagStore(t) | ||||
| 	tagStore := env.ts | ||||
| 	ctx := env.ctx | ||||
|  | @ -148,3 +148,59 @@ func TestTagAll(t *testing.T) { | |||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestTagLookup(t *testing.T) { | ||||
| 	env := testTagStore(t) | ||||
| 	tagStore := env.ts | ||||
| 	ctx := env.ctx | ||||
| 
 | ||||
| 	descA := distribution.Descriptor{Digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"} | ||||
| 	desc0 := distribution.Descriptor{Digest: "sha256:0000000000000000000000000000000000000000000000000000000000000000"} | ||||
| 
 | ||||
| 	tags, err := tagStore.Lookup(ctx, descA) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if len(tags) != 0 { | ||||
| 		t.Fatalf("Lookup returned > 0 tags from empty store") | ||||
| 	} | ||||
| 
 | ||||
| 	err = tagStore.Tag(ctx, "a", descA) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = tagStore.Tag(ctx, "b", descA) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = tagStore.Tag(ctx, "0", desc0) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = tagStore.Tag(ctx, "1", desc0) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	tags, err = tagStore.Lookup(ctx, descA) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(tags) != 2 { | ||||
| 		t.Errorf("Lookup of descA returned %d tags, expected 2", len(tags)) | ||||
| 	} | ||||
| 
 | ||||
| 	tags, err = tagStore.Lookup(ctx, desc0) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(tags) != 2 { | ||||
| 		t.Errorf("Lookup of descB returned %d tags, expected 2", len(tags)) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue