Skip to content

Commit 8d99faf

Browse files
committed
exporter: workaround containerd client issue with grpc size limits
The grpc client for containerd won't properly break up the writes for a file when used in combination with `bytes.NewReader` and `content.WriteBlob`. To workaround the issue, this wraps the container image exporter for containerd with a bytes reader that will chunk the `Write` calls instead of relying on the client to do the chunking. See containerd/containerd#11440 for the upstream containerd issue. Signed-off-by: Jonathan A. Sternberg <[email protected]>
1 parent a20380c commit 8d99faf

File tree

1 file changed

+45
-7
lines changed

1 file changed

+45
-7
lines changed

exporter/containerimage/writer.go

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package containerimage
22

33
import (
4-
"bytes"
54
"context"
65
"encoding/json"
76
"fmt"
7+
"io"
88
"reflect"
99
"strconv"
1010
"strings"
@@ -342,7 +342,7 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session
342342
}
343343
idxDone := progress.OneOff(ctx, "exporting manifest list "+idxDigest.String())
344344

345-
if err := content.WriteBlob(ctx, ic.opt.ContentStore, idxDigest.String(), bytes.NewReader(idxBytes), idxDesc, content.WithLabels(labels)); err != nil {
345+
if err := content.WriteBlob(ctx, ic.opt.ContentStore, idxDigest.String(), newBytesReader(idxBytes), idxDesc, content.WithLabels(labels)); err != nil {
346346
return nil, idxDone(errors.Wrapf(err, "error writing manifest list blob %s", idxDigest))
347347
}
348348
idxDone(nil)
@@ -530,7 +530,7 @@ func (ic *ImageWriter) commitDistributionManifest(ctx context.Context, opts *Ima
530530
}
531531
mfstDone := progress.OneOff(ctx, "exporting manifest "+mfstDigest.String())
532532

533-
if err := content.WriteBlob(ctx, ic.opt.ContentStore, mfstDigest.String(), bytes.NewReader(mfstJSON), mfstDesc, content.WithLabels((labels))); err != nil {
533+
if err := content.WriteBlob(ctx, ic.opt.ContentStore, mfstDigest.String(), newBytesReader(mfstJSON), mfstDesc, content.WithLabels((labels))); err != nil {
534534
return nil, nil, mfstDone(errors.Wrapf(err, "error writing manifest blob %s", mfstDigest))
535535
}
536536
mfstDone(nil)
@@ -542,7 +542,7 @@ func (ic *ImageWriter) commitDistributionManifest(ctx context.Context, opts *Ima
542542
}
543543
configDone := progress.OneOff(ctx, "exporting config "+configDigest.String())
544544

545-
if err := content.WriteBlob(ctx, ic.opt.ContentStore, configDigest.String(), bytes.NewReader(config), configDesc); err != nil {
545+
if err := content.WriteBlob(ctx, ic.opt.ContentStore, configDigest.String(), newBytesReader(config), configDesc); err != nil {
546546
return nil, nil, configDone(errors.Wrap(err, "error writing config blob"))
547547
}
548548
configDone(nil)
@@ -584,7 +584,7 @@ func (ic *ImageWriter) commitAttestationsManifest(ctx context.Context, opts *Ima
584584
},
585585
}
586586

587-
if err := content.WriteBlob(ctx, ic.opt.ContentStore, digest.String(), bytes.NewReader(data), desc); err != nil {
587+
if err := content.WriteBlob(ctx, ic.opt.ContentStore, digest.String(), newBytesReader(data), desc); err != nil {
588588
return nil, errors.Wrapf(err, "error writing data blob %s", digest)
589589
}
590590
layers[i] = desc
@@ -641,10 +641,10 @@ func (ic *ImageWriter) commitAttestationsManifest(ctx context.Context, opts *Ima
641641
}
642642

643643
done := progress.OneOff(ctx, "exporting attestation manifest "+mfstDigest.String())
644-
if err := content.WriteBlob(ctx, ic.opt.ContentStore, mfstDigest.String(), bytes.NewReader(mfstJSON), mfstDesc, content.WithLabels((labels))); err != nil {
644+
if err := content.WriteBlob(ctx, ic.opt.ContentStore, mfstDigest.String(), newBytesReader(mfstJSON), mfstDesc, content.WithLabels((labels))); err != nil {
645645
return nil, done(errors.Wrapf(err, "error writing manifest blob %s", mfstDigest))
646646
}
647-
if err := content.WriteBlob(ctx, ic.opt.ContentStore, configDesc.Digest.String(), bytes.NewReader(config), configDesc); err != nil {
647+
if err := content.WriteBlob(ctx, ic.opt.ContentStore, configDesc.Digest.String(), newBytesReader(config), configDesc); err != nil {
648648
return nil, done(errors.Wrap(err, "error writing config blob"))
649649
}
650650
done(nil)
@@ -947,3 +947,41 @@ func getRefMetadata(ref cache.ImmutableRef, limit int) []refMetadata {
947947
}
948948
return metas
949949
}
950+
951+
// bytesReader is an alternative version of the bytes.Reader that implements
952+
// io.WriterTo by sending chunks instead of the entire buffer at once.
953+
//
954+
// This is needed to workaround https://github.com/containerd/containerd/issues/11440.
955+
// When that is fixed, this can be removed.
956+
type bytesReader struct {
957+
p []byte
958+
}
959+
960+
func newBytesReader(p []byte) *bytesReader {
961+
return &bytesReader{p}
962+
}
963+
964+
func (r *bytesReader) Read(p []byte) (n int, err error) {
965+
if len(r.p) == 0 {
966+
return 0, io.EOF
967+
}
968+
n = copy(p, r.p)
969+
r.p = r.p[n:]
970+
return n, nil
971+
}
972+
973+
func (r *bytesReader) WriteTo(w io.Writer) (n int64, err error) {
974+
const maxBufferSize = 8 << 20
975+
for len(r.p) > 0 && err == nil {
976+
sz := len(r.p)
977+
if sz > maxBufferSize {
978+
sz = maxBufferSize
979+
}
980+
981+
var n0 int
982+
n0, err = w.Write(r.p[:sz])
983+
n += int64(n0)
984+
r.p = r.p[n0:]
985+
}
986+
return n, err
987+
}

0 commit comments

Comments
 (0)