fix(storage): Fix takeover response handling. (#13239) · googleapis/google-cloud-go@26d75bc
@@ -731,6 +731,79 @@ var methods = map[string][]retryFunc{
731731return fmt.Errorf("Reader.Read: %w", err)
732732 }
733733734+gotMd5 := md5.Sum(content)
735+expectedMd5 := md5.Sum(toWrite)
736+if d := cmp.Diff(gotMd5, expectedMd5); d != "" {
737+return fmt.Errorf("content mismatch, got %v bytes (md5: %v), want %v bytes (md5: %v)",
738+len(content), gotMd5, len(toWrite), expectedMd5)
739+ }
740+return nil
741+ },
742+// Appendable upload using a takeover.
743+func(ctx context.Context, c *Client, fs *resources, preconditions bool) error {
744+bucketName := fmt.Sprintf("%s-appendable", bucketIDs.New())
745+b := c.Bucket(bucketName)
746+if err := b.Create(ctx, projectID, nil); err != nil {
747+return err
748+ }
749+defer b.Delete(ctx)
750+751+obj := b.Object(objectIDs.New())
752+if preconditions {
753+obj = obj.If(Conditions{DoesNotExist: true})
754+ }
755+756+// Force multiple messages per chunk, and multiple chunks in the object.
757+chunkSize := 2 * maxPerMessageWriteSize
758+toWrite := generateRandomBytes(chunkSize * 3)
759+760+objW := obj.NewWriter(ctx)
761+objW.Append = true
762+objW.ChunkSize = chunkSize
763+if _, err := objW.Write(toWrite[0:maxPerMessageWriteSize]); err != nil {
764+return fmt.Errorf("Writer.Write: %w", err)
765+ }
766+// Close this writer, which will create the appendable unfinalized object
767+// (there was not enough in Write to trigger a send).
768+if err := objW.Close(); err != nil {
769+return fmt.Errorf("Creation Writer.Close: %v", err)
770+ }
771+772+generation := int64(0)
773+if preconditions {
774+generation = objW.Attrs().Generation
775+ }
776+objT := b.Object(obj.ObjectName()).Generation(generation)
777+w, l, err := objT.NewWriterFromAppendableObject(ctx, &AppendableWriterOpts{ChunkSize: chunkSize})
778+if err != nil {
779+return fmt.Errorf("NewWriterFromAppendableObject: %v", err)
780+ }
781+if l != int64(maxPerMessageWriteSize) {
782+return fmt.Errorf("NewWriterFromAppendableObject unexpected len: got %v, want %v", l, maxPerMessageWriteSize)
783+ }
784+785+if _, err := w.Write(toWrite[maxPerMessageWriteSize:]); err != nil {
786+return fmt.Errorf("Writer.Write: %v", err)
787+ }
788+if err := w.Close(); err != nil {
789+return fmt.Errorf("Writer.Close: %v", err)
790+ }
791+792+if w.Attrs() == nil {
793+return fmt.Errorf("Writer.Attrs: expected attrs for written object, got nil")
794+ }
795+796+// Don't reuse obj, in case preconditions were set on the write request.
797+r, err := b.Object(obj.ObjectName()).NewReader(ctx)
798+defer r.Close()
799+if err != nil {
800+return fmt.Errorf("obj.NewReader: %v", err)
801+ }
802+content, err := io.ReadAll(r)
803+if err != nil {
804+return fmt.Errorf("Reader.Read: %v", err)
805+ }
806+734807gotMd5 := md5.Sum(content)
735808expectedMd5 := md5.Sum(toWrite)
736809if d := cmp.Diff(gotMd5, expectedMd5); d != "" {