1
0
mirror of https://github.com/moby/buildkit.git synced 2025-04-18 18:04:03 +03:00
buildkit/cache/blobs_linux.go
Marat Radchenko 0a5a80cfec Remove pre-Go 1.17 build tags
Signed-off-by: Marat Radchenko <marat@slonopotamus.org>
2024-11-21 10:58:27 +03:00

130 lines
4.5 KiB
Go

//go:build linux
package cache
import (
"bufio"
"context"
"io"
"github.com/containerd/containerd/content"
labelspkg "github.com/containerd/containerd/labels"
"github.com/containerd/containerd/mount"
cerrdefs "github.com/containerd/errdefs"
"github.com/moby/buildkit/util/bklog"
"github.com/moby/buildkit/util/compression"
"github.com/moby/buildkit/util/overlay"
digest "github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
var emptyDesc = ocispecs.Descriptor{}
// computeOverlayBlob provides overlayfs-specialized method to compute
// diff between lower and upper snapshot. If the passed mounts cannot
// be computed (e.g. because the mounts aren't overlayfs), it returns
// an error.
func (sr *immutableRef) tryComputeOverlayBlob(ctx context.Context, lower, upper []mount.Mount, mediaType string, ref string, compressorFunc compression.Compressor) (_ ocispecs.Descriptor, ok bool, err error) {
// Get upperdir location if mounts are overlayfs that can be processed by this differ.
upperdir, err := overlay.GetUpperdir(lower, upper)
if err != nil {
// This is not an overlayfs snapshot. This is not an error so don't return error here
// and let the caller fallback to another differ.
return emptyDesc, false, nil
}
cw, err := sr.cm.ContentStore.Writer(ctx,
content.WithRef(ref),
content.WithDescriptor(ocispecs.Descriptor{
MediaType: mediaType, // most contentstore implementations just ignore this
}))
if err != nil {
return emptyDesc, false, errors.Wrap(err, "failed to open writer")
}
defer func() {
if cw != nil {
ctx = context.WithoutCancel(ctx)
// after commit success cw will be set to nil, if cw isn't nil, error
// happened before commit, we should abort this ingest, and because the
// error may incured by ctx cancel, use a new context here. And since
// cm.Close will unlock this ref in the content store, we invoke abort
// to remove the ingest root in advance.
if aerr := sr.cm.ContentStore.Abort(ctx, ref); aerr != nil {
bklog.G(ctx).WithError(aerr).Warnf("failed to abort writer %q", ref)
}
if cerr := cw.Close(); cerr != nil {
bklog.G(ctx).WithError(cerr).Warnf("failed to close writer %q", ref)
}
}
}()
if err = cw.Truncate(0); err != nil {
return emptyDesc, false, errors.Wrap(err, "failed to truncate writer")
}
bufW := bufio.NewWriterSize(cw, 128*1024)
var labels map[string]string
if compressorFunc != nil {
dgstr := digest.SHA256.Digester()
compressed, err := compressorFunc(bufW, mediaType)
if err != nil {
return emptyDesc, false, errors.Wrap(err, "failed to get compressed stream")
}
// Close ensure compressorFunc does some finalization works.
defer compressed.Close()
if err := overlay.WriteUpperdir(ctx, io.MultiWriter(compressed, dgstr.Hash()), upperdir, lower); err != nil {
return emptyDesc, false, errors.Wrap(err, "failed to write compressed diff")
}
if err := compressed.Close(); err != nil {
return emptyDesc, false, errors.Wrap(err, "failed to close compressed diff writer")
}
labels = map[string]string{
labelspkg.LabelUncompressed: dgstr.Digest().String(),
}
} else {
if err = overlay.WriteUpperdir(ctx, bufW, upperdir, lower); err != nil {
return emptyDesc, false, errors.Wrap(err, "failed to write diff")
}
}
if err := bufW.Flush(); err != nil {
return emptyDesc, false, errors.Wrap(err, "failed to flush diff")
}
var commitopts []content.Opt
if labels != nil {
commitopts = append(commitopts, content.WithLabels(labels))
}
dgst := cw.Digest()
if err := cw.Commit(ctx, 0, dgst, commitopts...); err != nil {
if !cerrdefs.IsAlreadyExists(err) {
return emptyDesc, false, errors.Wrap(err, "failed to commit")
}
}
if err := cw.Close(); err != nil {
return emptyDesc, false, err
}
cw = nil
cinfo, err := sr.cm.ContentStore.Info(ctx, dgst)
if err != nil {
return emptyDesc, false, errors.Wrap(err, "failed to get info from content store")
}
if cinfo.Labels == nil {
cinfo.Labels = make(map[string]string)
}
// Set uncompressed label if digest already existed without label
if _, ok := cinfo.Labels[labelspkg.LabelUncompressed]; !ok {
cinfo.Labels[labelspkg.LabelUncompressed] = labels[labelspkg.LabelUncompressed]
if _, err := sr.cm.ContentStore.Update(ctx, cinfo, "labels."+labelspkg.LabelUncompressed); err != nil {
return emptyDesc, false, errors.Wrap(err, "error setting uncompressed label")
}
}
return ocispecs.Descriptor{
MediaType: mediaType,
Size: cinfo.Size,
Digest: cinfo.Digest,
}, true, nil
}