Upload an empty part for zero byte multipart objects

What does this MR do and why?

A job artifact of exactly 0 bytes going to object storage through direct upload fails with a 500, and the Runner then retries it.

Multipart.Upload stops appending parts as soon as readAndUploadOnePart returns nil, which it does whenever a part reads no bytes. For an empty object that happens on the very first part, so the loop breaks before any append and CompleteMultipartUpload is still sent, carrying an empty part list.

S3 requires at least one part, answers 400, and Workhorse reports that to the Runner as a 500. The marshalled payload is the whole story:

  • with no parts appended: <CompleteMultipartUpload></CompleteMultipartUpload>
  • with one part: <CompleteMultipartUpload><Part><PartNumber>1</PartNumber>...</Part></CompleteMultipartUpload>

The fix uploads the first part even when it is empty, so the complete request always carries one part. Later parts keep their existing behaviour — an empty read still ends the loop — so non-empty objects are unaffected.

Any artifact type reaches this path; reports:codequality with an empty report file is simply where it was reported.

References

Differences

Before: a job producing a 0 byte artifact failed the upload. Workhorse logged CompleteMultipartUpload request ... returned: 400 Bad Request and answered the Runner with 500, which retried. A single byte in the file avoided it.

After: the empty object uploads one empty part, CompleteMultipartUpload carries a <Part> element, and the artifact is stored normally.

How to set up and validate locally

cd workhorse && go test ./internal/upload/destination/objectstore/ -run TestMultipartUpload

TestMultipartUploadEmptyObject fails on master — CompleteMultipartUpload goes out with no <Part> — and passes with this change.

End to end, with artifacts on S3-compatible object storage and artifacts_object_store_enabled = true, a job whose script is touch gl-code-quality-report.json and which uploads it as artifacts:reports:codequality now succeeds where it previously returned 500.

Edited by Jeston Singh

Merge request reports

Loading
Loading