Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add type for failure during deleting dangling referrer index #482

Merged
merged 19 commits into from
Apr 27, 2023
69 changes: 61 additions & 8 deletions registry/remote/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
Expand All @@ -38,14 +39,18 @@ import (
)

const (
exampleRepositoryName = "example"
exampleTag = "latest"
exampleConfig = "Example config content"
exampleLayer = "Example layer content"
exampleUploadUUid = "0bc84d80-837c-41d9-824e-1907463c53b3"
ManifestDigest = "sha256:0b696106ecd0654e031f19e0a8cbd1aee4ad457d7c9cea881f07b12a930cd307"
ReferenceManifestDigest = "sha256:6983f495f7ee70d43e571657ae8b39ca3d3ca1b0e77270fd4fbddfb19832a1cf"
_ = ExampleUnplayable
exampleRepositoryName = "example"
exampleTag = "latest"
exampleConfig = "Example config content"
exampleLayer = "Example layer content"
exampleUploadUUid = "0bc84d80-837c-41d9-824e-1907463c53b3"
ManifestDigest = "sha256:0b696106ecd0654e031f19e0a8cbd1aee4ad457d7c9cea881f07b12a930cd307"
ReferenceManifestDigest = "sha256:6983f495f7ee70d43e571657ae8b39ca3d3ca1b0e77270fd4fbddfb19832a1cf"
referrersAPIUnavailableRepositoryName = "no-referrers-api"
referrerDigest = "sha256:21c623eb8ccd273f2702efd74a0abb455dd06a99987f413c2114fb00961ebfe7"
referrersTag = "sha256-c824a9aa7d2e3471306648c6d4baa1abbcb97ff0276181ab4722ca27127cdba0"
referrerIndexDigest = "sha256:7baac5147dd58d56fdbaad5a888fa919235a3a90cb71aaa8b56ee5d19f4cd838"
_ = ExampleUnplayable
)

var (
Expand Down Expand Up @@ -99,6 +104,15 @@ var (
ArtifactType: "example/manifest",
Digest: digest.FromBytes(exampleManifestWithBlobs),
Size: int64(len(exampleManifestWithBlobs))}
subjectDescriptor = content.NewDescriptorFromBytes(ocispec.MediaTypeImageManifest, []byte(`{"layers":[]}`))
referrerManifestContent, _ = json.Marshal(ocispec.Manifest{
MediaType: ocispec.MediaTypeImageManifest,
Subject: &subjectDescriptor,
})
referrerDescriptor = content.NewDescriptorFromBytes(ocispec.MediaTypeImageManifest, referrerManifestContent)
referrerIndex, _ = json.Marshal(ocispec.Index{
Manifests: []ocispec.Descriptor{},
})
)

var host string
Expand Down Expand Up @@ -205,6 +219,20 @@ func TestMain(m *testing.M) {
if m == "GET" {
w.Write([]byte(resultBlob))
}
case p == fmt.Sprintf("/v2/%s/referrers/%s", referrersAPIUnavailableRepositoryName, "sha256:0000000000000000000000000000000000000000000000000000000000000000"):
w.WriteHeader(http.StatusNotFound)
case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrerDigest) && m == http.MethodPut:
w.WriteHeader(http.StatusCreated)
case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrersTag) && m == http.MethodGet:
w.Write(referrerIndex)
w.Header().Set("Content-Type", ocispec.MediaTypeImageIndex)
w.Header().Set("Content-Length", strconv.Itoa(len(referrerIndex)))
w.Header().Set("Docker-Content-Digest", digest.Digest(string(referrerIndex)).String())
w.WriteHeader(http.StatusCreated)
case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrersTag) && m == http.MethodPut:
w.WriteHeader(http.StatusCreated)
case p == fmt.Sprintf("/v2/%s/manifests/%s", referrersAPIUnavailableRepositoryName, referrerIndexDigest) && m == http.MethodDelete:
w.WriteHeader(http.StatusMethodNotAllowed)
}

}))
Expand Down Expand Up @@ -785,3 +813,28 @@ func Example_tagReference() {
// Output:
// Tagged sha256:b53dc03a49f383ba230d8ac2b78a9c4aec132e4a9f36cc96524df98163202cc7 as latest
}

// Example_pushAndIgnoreReferrersIndexError gives example snippets on how to
// ignore referrer index deletion error during push a referrer manifest.
func Example_pushAndIgnoreReferrersIndexError() {
repo, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, referrersAPIUnavailableRepositoryName))
if err != nil {
panic(err)
}
ctx := context.Background()

// push a referrer manifest and ignore cleaning up error
err = repo.Push(ctx, referrerDescriptor, bytes.NewReader(referrerManifestContent))
if err != nil {
var re *remote.ReferrersError
if !errors.As(err, &re) || !re.IsReferrersIndexDelete() {
panic(err)
}
fmt.Println("ignoring error occurred during cleaning obsolete referrers index")
}
fmt.Println("Push finished")

// Output:
// ignoring error occurred during cleaning obsolete referrers index
// Push finished
}
32 changes: 32 additions & 0 deletions registry/remote/referrers.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,38 @@ var (
errNoReferrerUpdate = errors.New("no referrer update")
)

const (
// opDeleteReferrersIndex represents the operation for deleting a
// referrers index.
opDeleteReferrersIndex = "DeleteReferrersIndex"
)

// ReferrersError records an error and the operation and the subject descriptor.
type ReferrersError struct {
// Op represents the failing operation.
Op string
// Subject is the descriptor of referenced artifact.
Subject ocispec.Descriptor
// Err is the entity of referrers error.
Err error
}

// Error returns error msg of IgnorableError.
func (e *ReferrersError) Error() string {
return e.Err.Error()
}

// Unwrap returns the inner error of IgnorableError.
func (e *ReferrersError) Unwrap() error {
return errors.Unwrap(e.Err)
}

// IsIndexDelete tells if e is kind of error related to referrers
// index deletion.
func (e *ReferrersError) IsReferrersIndexDelete() bool {
return e.Op == opDeleteReferrersIndex
}

// buildReferrersTag builds the referrers tag for the given manifest descriptor.
// Format: <algorithm>-<digest>
// Reference: https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#unavailable-referrers-api
Expand Down
6 changes: 5 additions & 1 deletion registry/remote/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -1271,7 +1271,11 @@ func (s *manifestStore) updateReferrersIndex(ctx context.Context, subject ocispe
// 4. delete the dangling original referrers index
if !skipDelete {
if err := s.repo.delete(ctx, oldIndexDesc, true); err != nil {
return fmt.Errorf("failed to delete dangling referrers index %s for referrers tag %s: %w", oldIndexDesc.Digest.String(), referrersTag, err)
return &ReferrersError{
Op: opDeleteReferrersIndex,
Err: fmt.Errorf("failed to delete dangling referrers index %s for referrers tag %s: %w", oldIndexDesc.Digest.String(), referrersTag, err),
Subject: subject,
}
}
}
return nil
Expand Down