storage/storage_image.go
func (s *storageImageDestination) TryReusingBlob(ctx context.Context, blobinfo types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) {
	// lock the entire method as it executes fairly quickly
	s.putBlobMutex.Lock()
	defer s.putBlobMutex.Unlock()
	if blobinfo.Digest == "" {
		return false, types.BlobInfo{}, errors.Errorf(`Can not check for a blob with unknown digest`)
	}
	if err := blobinfo.Digest.Validate(); err != nil {
		return false, types.BlobInfo{}, errors.Wrapf(err, `Can not check for a blob with invalid digest`)
	}

	// Check if we've already cached it in a file.
	if size, ok := s.fileSizes[blobinfo.Digest]; ok {
		return true, types.BlobInfo{
			Digest:    blobinfo.Digest,
			Size:      size,
			MediaType: blobinfo.MediaType,
		}, nil
	}

	// Check if we have a wasn't-compressed layer in storage that's based on that blob.
	layers, err := s.imageRef.transport.store.LayersByUncompressedDigest(blobinfo.Digest)
	if err != nil && errors.Cause(err) != storage.ErrLayerUnknown {
		return false, types.BlobInfo{}, errors.Wrapf(err, `Error looking for layers with digest %q`, blobinfo.Digest)
	}
	if len(layers) > 0 {
		// Save this for completeness.
		s.blobDiffIDs[blobinfo.Digest] = layers[0].UncompressedDigest
		return true, types.BlobInfo{
			Digest:    blobinfo.Digest,
			Size:      layers[0].UncompressedSize,
			MediaType: blobinfo.MediaType,
		}, nil
	}

	// Check if we have a was-compressed layer in storage that's based on that blob.
	layers, err = s.imageRef.transport.store.LayersByCompressedDigest(blobinfo.Digest)
	if err != nil && errors.Cause(err) != storage.ErrLayerUnknown {
		return false, types.BlobInfo{}, errors.Wrapf(err, `Error looking for compressed layers with digest %q`, blobinfo.Digest)
	}
	if len(layers) > 0 {
		// Record the uncompressed value so that we can use it to calculate layer IDs.
		s.blobDiffIDs[blobinfo.Digest] = layers[0].UncompressedDigest
		return true, types.BlobInfo{
			Digest:    blobinfo.Digest,
			Size:      layers[0].CompressedSize,
			MediaType: blobinfo.MediaType,
		}, nil
	}

	// Does the blob correspond to a known DiffID which we already have available?
	// Because we must return the size, which is unknown for unavailable compressed blobs, the returned BlobInfo refers to the
	// uncompressed layer, and that can happen only if canSubstitute.
	if canSubstitute {
		if uncompressedDigest := cache.UncompressedDigest(blobinfo.Digest); uncompressedDigest != "" && uncompressedDigest != blobinfo.Digest {
			layers, err := s.imageRef.transport.store.LayersByUncompressedDigest(uncompressedDigest)
			if err != nil && errors.Cause(err) != storage.ErrLayerUnknown {
				return false, types.BlobInfo{}, errors.Wrapf(err, `Error looking for layers with digest %q`, uncompressedDigest)
			}
			if len(layers) > 0 {
				s.blobDiffIDs[uncompressedDigest] = layers[0].UncompressedDigest
				return true, types.BlobInfo{
					Digest:    uncompressedDigest,
					Size:      layers[0].UncompressedSize,
					MediaType: blobinfo.MediaType,
				}, nil
			}
		}
	}

	// Nope, we don't have it.
	return false, types.BlobInfo{}, nil
}
func (s *storageImageDestination) TryReusingBlob(ctx context.Context, blobinfo types.BlobInfo, cache types.BlobInfoCache, canSubstitute bool) (bool, types.BlobInfo, error) {
	// lock the entire method as it executes fairly quickly
	s.putBlobMutex.Lock()
	defer s.putBlobMutex.Unlock()
	if blobinfo.Digest == "" {
		return false, types.BlobInfo{}, errors.Errorf(`Can not check for a blob with unknown digest`)
	}
	if err := blobinfo.Digest.Validate(); err != nil {
		return false, types.BlobInfo{}, errors.Wrapf(err, `Can not check for a blob with invalid digest`)
	}

	// Check if we've already cached it in a file.
	if size, ok := s.fileSizes[blobinfo.Digest]; ok {
		return true, types.BlobInfo{
			Digest:    blobinfo.Digest,
			Size:      size,
			MediaType: blobinfo.MediaType,
		}, nil
	}

	// Check if we have a wasn't-compressed layer in storage that's based on that blob.
	layers, err := s.imageRef.transport.store.LayersByUncompressedDigest(blobinfo.Digest)
	if err != nil && errors.Cause(err) != storage.ErrLayerUnknown {
		return false, types.BlobInfo{}, errors.Wrapf(err, `Error looking for layers with digest %q`, blobinfo.Digest)
	}
	if len(layers) > 0 {
		// Save this for completeness.
		s.blobDiffIDs[blobinfo.Digest] = layers[0].UncompressedDigest
		return true, types.BlobInfo{
			Digest:    blobinfo.Digest,
			Size:      layers[0].UncompressedSize,
			MediaType: blobinfo.MediaType,
		}, nil
	}

	// Check if we have a was-compressed layer in storage that's based on that blob.
	layers, err = s.imageRef.transport.store.LayersByCompressedDigest(blobinfo.Digest)
	if err != nil && errors.Cause(err) != storage.ErrLayerUnknown {
		return false, types.BlobInfo{}, errors.Wrapf(err, `Error looking for compressed layers with digest %q`, blobinfo.Digest)
	}
	if len(layers) > 0 {
		// Record the uncompressed value so that we can use it to calculate layer IDs.
		s.blobDiffIDs[blobinfo.Digest] = layers[0].UncompressedDigest
		return true, types.BlobInfo{
			Digest:    blobinfo.Digest,
			Size:      layers[0].CompressedSize,
			MediaType: blobinfo.MediaType,
		}, nil
	}

	// Does the blob correspond to a known DiffID which we already have available?
	// Because we must return the size, which is unknown for unavailable compressed blobs, the returned BlobInfo refers to the
	// uncompressed layer, and that can happen only if canSubstitute.
	if canSubstitute {
		if uncompressedDigest := cache.UncompressedDigest(blobinfo.Digest); uncompressedDigest != "" && uncompressedDigest != blobinfo.Digest {
			layers, err := s.imageRef.transport.store.LayersByUncompressedDigest(uncompressedDigest)
			if err != nil && errors.Cause(err) != storage.ErrLayerUnknown {
				return false, types.BlobInfo{}, errors.Wrapf(err, `Error looking for layers with digest %q`, uncompressedDigest)
			}
			if len(layers) > 0 {
				s.blobDiffIDs[uncompressedDigest] = layers[0].UncompressedDigest
				return true, types.BlobInfo{
					Digest:    uncompressedDigest,
					Size:      layers[0].UncompressedSize,
					MediaType: blobinfo.MediaType,
				}, nil
			}
		}
	}

	// Nope, we don't have it.
	return false, types.BlobInfo{}, nil
}
func ( s * storageImageDestination ) TryReusingBlob ( ctx context . Context , blobinfo types . BlobInfo , cache types . BlobInfoCache , canSubstitute bool ) ( bool , types . BlobInfo , error ) { // lock the entire method as it executes fairly quickly s . putBlobMutex . Lock ( ) defer s . putBlobMutex . Unlock ( ) if blobinfo . Digest == " " { return false , types . BlobInfo { } , errors . Errorf ( `Can not check for a blob with unknown digest` ) } if err := blobinfo . Digest . Validate ( ) ; err != nil { return false , types . BlobInfo { } , errors . Wrapf ( err , `Can not check for a blob with invalid digest` ) } // Check if we've already cached it in a file. if size , ok := s . fileSizes [ blobinfo . Digest ] ; ok { return true , types . BlobInfo { Digest : blobinfo . Digest , Size : size , MediaType : blobinfo . MediaType , } , nil } // Check if we have a wasn't-compressed layer in storage that's based on that blob. layers , err := s . imageRef . transport . store . LayersByUncompressedDigest ( blobinfo . Digest ) if err != nil && errors . Cause ( err ) != storage . ErrLayerUnknown { return false , types . BlobInfo { } , errors . Wrapf ( err , `Error looking for layers with digest %q` , blobinfo . Digest ) } if len ( layers ) > 0 { // Save this for completeness. s . blobDiffIDs [ blobinfo . Digest ] = layers [ 0 ] . UncompressedDigest return true , types . BlobInfo { Digest : blobinfo . Digest , Size : layers [ 0 ] . UncompressedSize , MediaType : blobinfo . MediaType , } , nil } // Check if we have a was-compressed layer in storage that's based on that blob. layers , err = s . imageRef . transport . store . LayersByCompressedDigest ( blobinfo . Digest ) if err != nil && errors . Cause ( err ) != storage . ErrLayerUnknown { return false , types . BlobInfo { } , errors . Wrapf ( err , `Error looking for compressed layers with digest %q` , blobinfo . Digest ) } if len ( layers ) > 0 { // Record the uncompressed value so that we can use it to calculate layer IDs. s . blobDiffIDs [ blobinfo . Digest ] = layers [ 0 ] . UncompressedDigest return true , types . BlobInfo { Digest : blobinfo . Digest , Size : layers [ 0 ] . CompressedSize , MediaType : blobinfo . MediaType , } , nil } // Does the blob correspond to a known DiffID which we already have available? // Because we must return the size, which is unknown for unavailable compressed blobs, the returned BlobInfo refers to the // uncompressed layer, and that can happen only if canSubstitute. if canSubstitute { if uncompressedDigest := cache . UncompressedDigest ( blobinfo . Digest ) ; uncompressedDigest != " " && uncompressedDigest != blobinfo . Digest { layers , err := s . imageRef . transport . store . LayersByUncompressedDigest ( uncompressedDigest ) if err != nil && errors . Cause ( err ) != storage . ErrLayerUnknown { return false , types . BlobInfo { } , errors . Wrapf ( err , `Error looking for layers with digest %q` , uncompressedDigest ) } if len ( layers ) > 0 { s . blobDiffIDs [ uncompressedDigest ] = layers [ 0 ] . UncompressedDigest return true , types . BlobInfo { Digest : uncompressedDigest , Size : layers [ 0 ] . UncompressedSize , MediaType : blobinfo . MediaType , } , nil } } } // Nope, we don't have it. return false , types . BlobInfo { } , nil }
// TryReusingBlob checks whether the transport already contains, or can efficiently reuse, a blob, and if so, applies it to the current destination
// (e.g. if the blob is a filesystem layer, this signifies that the changes it describes need to be applied again when composing a filesystem tree).
// info.Digest must not be empty.
// If canSubstitute, TryReusingBlob can use an equivalent equivalent of the desired blob; in that case the returned info may not match the input.
// If the blob has been succesfully reused, returns (true, info, nil); info must contain at least a digest and size.
// If the transport can not reuse the requested blob, TryReusingBlob returns (false, {}, nil); it returns a non-nil error only on an unexpected failure.
// May use and/or update cache.
TryReusingBlob checks whether the transport already contains or can efficiently reuse a blob and if so applies it to the current destination ( e . g . if the blob is a filesystem layer this signifies that the changes it describes need to be applied again when composing a filesystem tree ) . info . Digest must not be empty . If canSubstitute TryReusingBlob can use an equivalent equivalent of the desired blob ; in that case the returned info may not match the input . If the blob has been succesfully reused returns ( true info nil ) ; info must contain at least a digest and size . If the transport can not reuse the requested blob TryReusingBlob returns ( false {} nil ) ; it returns a non - nil error only on an unexpected failure . May use and / or update cache .