Skip to content

Commit

Permalink
chunked: define error for partial pulls not available
Browse files Browse the repository at this point in the history
define a new error type so that the caller can determine whether it is
safe to ignore the error and retrieve the resource fully.

Closes: containers#2115

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
  • Loading branch information
giuseppe committed Sep 30, 2024
1 parent c90a35b commit 1fe825a
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 13 deletions.
8 changes: 8 additions & 0 deletions pkg/chunked/internal/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package internal

import (
"errors"
)

// ErrPartialContentNotAvailable is returned when the requested content does not support partial content retrieval
var ErrPartialContentNotAvailable = errors.New("partial content not available")
34 changes: 21 additions & 13 deletions pkg/chunked/storage_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,40 +146,48 @@ func (c *chunkedDiffer) convertTarToZstdChunked(destDirectory string, payload *o
func GetDiffer(ctx context.Context, store storage.Store, blobDigest digest.Digest, blobSize int64, annotations map[string]string, iss ImageSourceSeekable) (graphdriver.Differ, error) {
pullOptions := store.PullOptions()

convertImages := parseBooleanPullOption(pullOptions, "convert_images", false)

if !parseBooleanPullOption(pullOptions, "enable_partial_images", true) {
return nil, errors.New("enable_partial_images not configured")
return nil, internal.ErrPartialContentNotAvailable
}

zstdChunkedTOCDigestString, hasZstdChunkedTOC := annotations[internal.ManifestChecksumKey]
estargzTOCDigestString, hasEstargzTOC := annotations[estargz.TOCJSONDigestAnnotation]

if hasZstdChunkedTOC && hasEstargzTOC {
return nil, errors.New("both zstd:chunked and eStargz TOC found")
if !convertImages {
return nil, fmt.Errorf("both zstd:chunked and eStargz TOC found: %w", internal.ErrPartialContentNotAvailable)
}
// If a conversion is required, ignore both TOCs and retrieve the entire layer.
hasZstdChunkedTOC, hasEstargzTOC = false, false
}

if hasZstdChunkedTOC {
zstdChunkedTOCDigest, err := digest.Parse(zstdChunkedTOCDigestString)
if err != nil {
return nil, fmt.Errorf("parsing zstd:chunked TOC digest %q: %w", zstdChunkedTOCDigestString, err)
if err == nil {
return makeZstdChunkedDiffer(store, blobSize, zstdChunkedTOCDigest, annotations, iss, pullOptions)
}
if !convertImages {
return nil, fmt.Errorf("parsing zstd:chunked TOC digest %q: %w", internal.ErrPartialContentNotAvailable)
}
return makeZstdChunkedDiffer(store, blobSize, zstdChunkedTOCDigest, annotations, iss, pullOptions)
}
if hasEstargzTOC {
estargzTOCDigest, err := digest.Parse(estargzTOCDigestString)
if err != nil {
return nil, fmt.Errorf("parsing estargz TOC digest %q: %w", estargzTOCDigestString, err)
if err == nil {
return makeEstargzChunkedDiffer(store, blobSize, estargzTOCDigest, iss, pullOptions)
}
if !convertImages {
return nil, fmt.Errorf("parsing eStargz TOC digest %q: %w", internal.ErrPartialContentNotAvailable)
}
return makeEstargzChunkedDiffer(store, blobSize, estargzTOCDigest, iss, pullOptions)
}

if !convertImages {
return nil, fmt.Errorf("convert_images not configured: %w", internal.ErrPartialContentNotAvailable)
}
return makeConvertFromRawDiffer(store, blobDigest, blobSize, iss, pullOptions)
}

func makeConvertFromRawDiffer(store storage.Store, blobDigest digest.Digest, blobSize int64, iss ImageSourceSeekable, pullOptions map[string]string) (*chunkedDiffer, error) {
if !parseBooleanPullOption(pullOptions, "convert_images", false) {
return nil, errors.New("convert_images not configured")
}

layersCache, err := getLayersCache(store)
if err != nil {
return nil, err
Expand Down
7 changes: 7 additions & 0 deletions pkg/chunked/toc/toc.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,10 @@ func GetTOCDigest(annotations map[string]string) (*digest.Digest, error) {
return nil, nil
}
}

// IsPartialContentNotAvailableError returns true if the error means that the requested content cannot
// be fetched partially. The caller can use this function to determine if the error can be ignored and
// the content should be fetched fully.
func IsPartialContentNotAvailableError(err error) bool {
return errors.Is(err, internal.ErrPartialContentNotAvailable)
}

0 comments on commit 1fe825a

Please sign in to comment.