in internal/storage/fake/bucket.go [799:871]
func (b *bucket) ComposeObjects(
ctx context.Context,
req *gcs.ComposeObjectsRequest) (o *gcs.Object, err error) {
b.mu.Lock()
defer b.mu.Unlock()
// GCS doesn't like too few or too many sources.
if len(req.Sources) < 1 {
err = errors.New("you must provide at least one source component")
return
}
if len(req.Sources) > gcs.MaxSourcesPerComposeRequest {
err = errors.New("you have provided too many source components")
return
}
// Find readers for all of the source objects, also computing the sum of
// their component counts.
var srcReaders []io.Reader
var dstComponentCount int64
for _, src := range req.Sources {
var r io.Reader
var srcIndex int
r, srcIndex, err = b.newReaderLocked(&gcs.ReadObjectRequest{
Name: src.Name,
Generation: src.Generation,
})
if err != nil {
return
}
srcReaders = append(srcReaders, r)
dstComponentCount += b.objects[srcIndex].metadata.ComponentCount
}
// GCS doesn't like the component count to go too high.
if dstComponentCount > gcs.MaxComponentCount {
err = errors.New("result would have too many components")
return
}
// Create the new object.
createReq := &gcs.CreateObjectRequest{
Name: req.DstName,
GenerationPrecondition: req.DstGenerationPrecondition,
MetaGenerationPrecondition: req.DstMetaGenerationPrecondition,
Contents: io.MultiReader(srcReaders...),
ContentType: req.ContentType,
Metadata: req.Metadata,
}
_, err = b.createObjectLocked(createReq)
if err != nil {
return
}
dstIndex := b.objects.find(req.DstName)
metadata := &b.objects[dstIndex].metadata
// Touchup: fix the component count.
metadata.ComponentCount = dstComponentCount
// Touchup: emulate the real GCS behavior of not exporting an MD5 hash for
// composite objects.
metadata.MD5 = nil
o = copyObject(metadata)
return
}