Skip to content

Commit b74a2a1

Browse files
authored
feat: add type for failure during deleting dangling referrer index (#482)
Resolves #479 Signed-off-by: Billy Zha <jinzha1@microsoft.com>
1 parent e8225cb commit b74a2a1

File tree

3 files changed

+98
-9
lines changed

3 files changed

+98
-9
lines changed

registry/remote/example_test.go

+61-8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"bytes"
1919
"context"
2020
"encoding/json"
21+
"errors"
2122
"fmt"
2223
"io"
2324
"net/http"
@@ -38,14 +39,18 @@ import (
3839
)
3940

4041
const (
41-
exampleRepositoryName = "example"
42-
exampleTag = "latest"
43-
exampleConfig = "Example config content"
44-
exampleLayer = "Example layer content"
45-
exampleUploadUUid = "0bc84d80-837c-41d9-824e-1907463c53b3"
46-
ManifestDigest = "sha256:0b696106ecd0654e031f19e0a8cbd1aee4ad457d7c9cea881f07b12a930cd307"
47-
ReferenceManifestDigest = "sha256:6983f495f7ee70d43e571657ae8b39ca3d3ca1b0e77270fd4fbddfb19832a1cf"
48-
_ = ExampleUnplayable
42+
exampleRepositoryName = "example"
43+
exampleTag = "latest"
44+
exampleConfig = "Example config content"
45+
exampleLayer = "Example layer content"
46+
exampleUploadUUid = "0bc84d80-837c-41d9-824e-1907463c53b3"
47+
ManifestDigest = "sha256:0b696106ecd0654e031f19e0a8cbd1aee4ad457d7c9cea881f07b12a930cd307"
48+
ReferenceManifestDigest = "sha256:6983f495f7ee70d43e571657ae8b39ca3d3ca1b0e77270fd4fbddfb19832a1cf"
49+
referrersAPIUnavailableRepositoryName = "no-referrers-api"
50+
referrerDigest = "sha256:21c623eb8ccd273f2702efd74a0abb455dd06a99987f413c2114fb00961ebfe7"
51+
referrersTag = "sha256-c824a9aa7d2e3471306648c6d4baa1abbcb97ff0276181ab4722ca27127cdba0"
52+
referrerIndexDigest = "sha256:7baac5147dd58d56fdbaad5a888fa919235a3a90cb71aaa8b56ee5d19f4cd838"
53+
_ = ExampleUnplayable
4954
)
5055

5156
var (
@@ -99,6 +104,15 @@ var (
99104
ArtifactType: "example/manifest",
100105
Digest: digest.FromBytes(exampleManifestWithBlobs),
101106
Size: int64(len(exampleManifestWithBlobs))}
107+
subjectDescriptor = content.NewDescriptorFromBytes(ocispec.MediaTypeImageManifest, []byte(`{"layers":[]}`))
108+
referrerManifestContent, _ = json.Marshal(ocispec.Manifest{
109+
MediaType: ocispec.MediaTypeImageManifest,
110+
Subject: &subjectDescriptor,
111+
})
112+
referrerDescriptor = content.NewDescriptorFromBytes(ocispec.MediaTypeImageManifest, referrerManifestContent)
113+
referrerIndex, _ = json.Marshal(ocispec.Index{
114+
Manifests: []ocispec.Descriptor{},
115+
})
102116
)
103117

104118
var host string
@@ -205,6 +219,20 @@ func TestMain(m *testing.M) {
205219
if m == "GET" {
206220
w.Write([]byte(resultBlob))
207221
}
222+
case p == fmt.Sprintf("/v2/%s/referrers/%s", referrersAPIUnavailableRepositoryName, "sha256:0000000000000000000000000000000000000000000000000000000000000000"):
223+
w.WriteHeader(http.StatusNotFound)
224+
case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrerDigest) && m == http.MethodPut:
225+
w.WriteHeader(http.StatusCreated)
226+
case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrersTag) && m == http.MethodGet:
227+
w.Write(referrerIndex)
228+
w.Header().Set("Content-Type", ocispec.MediaTypeImageIndex)
229+
w.Header().Set("Content-Length", strconv.Itoa(len(referrerIndex)))
230+
w.Header().Set("Docker-Content-Digest", digest.Digest(string(referrerIndex)).String())
231+
w.WriteHeader(http.StatusCreated)
232+
case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrersTag) && m == http.MethodPut:
233+
w.WriteHeader(http.StatusCreated)
234+
case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrerIndexDigest) && m == http.MethodDelete:
235+
w.WriteHeader(http.StatusMethodNotAllowed)
208236
}
209237

210238
}))
@@ -785,3 +813,28 @@ func Example_tagReference() {
785813
// Output:
786814
// Tagged sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7 as latest
787815
}
816+
817+
// Example_pushAndIgnoreReferrersIndexError gives example snippets on how to
818+
// ignore referrer index deletion error during push a referrer manifest.
819+
func Example_pushAndIgnoreReferrersIndexError() {
820+
repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, referrersAPIUnavailableRepositoryName))
821+
if err != nil {
822+
panic(err)
823+
}
824+
ctx := context.Background()
825+
826+
// push a referrer manifest and ignore cleaning up error
827+
err = repo.Push(ctx, referrerDescriptor, bytes.NewReader(referrerManifestContent))
828+
if err != nil {
829+
var re *remote.ReferrersError
830+
if !errors.As(err, &re) || !re.IsReferrersIndexDelete() {
831+
panic(err)
832+
}
833+
fmt.Println("ignoring error occurred during cleaning obsolete referrers index")
834+
}
835+
fmt.Println("Push finished")
836+
837+
// Output:
838+
// ignoring error occurred during cleaning obsolete referrers index
839+
// Push finished
840+
}

registry/remote/referrers.go

+32
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,38 @@ var (
6868
errNoReferrerUpdate = errors.New("no referrer update")
6969
)
7070

71+
const (
72+
// opDeleteReferrersIndex represents the operation for deleting a
73+
// referrers index.
74+
opDeleteReferrersIndex = "DeleteReferrersIndex"
75+
)
76+
77+
// ReferrersError records an error and the operation and the subject descriptor.
78+
type ReferrersError struct {
79+
// Op represents the failing operation.
80+
Op string
81+
// Subject is the descriptor of referenced artifact.
82+
Subject ocispec.Descriptor
83+
// Err is the entity of referrers error.
84+
Err error
85+
}
86+
87+
// Error returns error msg of IgnorableError.
88+
func (e *ReferrersError) Error() string {
89+
return e.Err.Error()
90+
}
91+
92+
// Unwrap returns the inner error of IgnorableError.
93+
func (e *ReferrersError) Unwrap() error {
94+
return errors.Unwrap(e.Err)
95+
}
96+
97+
// IsIndexDelete tells if e is kind of error related to referrers
98+
// index deletion.
99+
func (e *ReferrersError) IsReferrersIndexDelete() bool {
100+
return e.Op == opDeleteReferrersIndex
101+
}
102+
71103
// buildReferrersTag builds the referrers tag for the given manifest descriptor.
72104
// Format: <algorithm>-<digest>
73105
// Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#unavailable-referrers-api

registry/remote/repository.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -1271,7 +1271,11 @@ func (s *manifestStore) updateReferrersIndex(ctx context.Context, subject ocispe
12711271
// 4. delete the dangling original referrers index
12721272
if !skipDelete {
12731273
if err := s.repo.delete(ctx, oldIndexDesc, true); err != nil {
1274-
return fmt.Errorf("failed to delete dangling referrers index %s for referrers tag %s: %w", oldIndexDesc.Digest.String(), referrersTag, err)
1274+
return &ReferrersError{
1275+
Op: opDeleteReferrersIndex,
1276+
Err: fmt.Errorf("failed to delete dangling referrers index %s for referrers tag %s: %w", oldIndexDesc.Digest.String(), referrersTag, err),
1277+
Subject: subject,
1278+
}
12751279
}
12761280
}
12771281
return nil

0 commit comments

Comments
 (0)