@specific.dev/spectest 0.131.6 → 0.131.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,352 @@
1
+ --- a/vendor/github.com/johannesboyne/gofakes3/gofakes3.go
2
+ +++ b/vendor/github.com/johannesboyne/gofakes3/gofakes3.go
3
+ @@ -933,6 +933,14 @@
4
+ return err
5
+ }
6
+
7
+ + if meta["X-Amz-Checksum-Algorithm"] == "SHA256" {
8
+ + if kind := meta["X-Amz-Checksum-Type"]; kind != "" && kind != "COMPOSITE" {
9
+ + return ErrInvalidArgument
10
+ + }
11
+ + meta["X-Amz-Checksum-Type"] = "COMPOSITE"
12
+ + w.Header().Set("x-amz-checksum-algorithm", "SHA256")
13
+ + w.Header().Set("x-amz-checksum-type", "COMPOSITE")
14
+ + }
15
+ uploadID, err := g.uploader.CreateMultipartUpload(bucket, object, meta)
16
+ if err != nil {
17
+ return err
18
+ @@ -983,12 +991,20 @@
19
+ }
20
+ }
21
+
22
+ + var checksum *sha256PartReader
23
+ + if expected := r.Header.Get("x-amz-checksum-sha256"); expected != "" {
24
+ + checksum = newSHA256PartReader(rdr, expected)
25
+ + rdr = checksum
26
+ + }
27
+ etag, err := g.uploader.UploadPart(bucket, object, uploadID, int(partNumber), r.ContentLength, rdr)
28
+ if err != nil {
29
+ return err
30
+ }
31
+
32
+ w.Header().Add("ETag", etag)
33
+ + if checksum != nil {
34
+ + w.Header().Set("x-amz-checksum-sha256", checksum.sum())
35
+ + }
36
+ return nil
37
+ }
38
+
39
+ @@ -1009,6 +1025,13 @@
40
+ return err
41
+ }
42
+
43
+ + var err error
44
+ + in.Conditions, err = parsePutConditions(r.Header)
45
+ + if err != nil {
46
+ + return err
47
+ + }
48
+ + in.ChecksumSHA256 = r.Header.Get("x-amz-checksum-sha256")
49
+ + in.ChecksumType = r.Header.Get("x-amz-checksum-type")
50
+ versionID, etag, err := g.uploader.CompleteMultipartUpload(bucket, object, uploadID, &in)
51
+ if err != nil {
52
+ return err
53
+ @@ -1031,10 +1054,12 @@
54
+ }
55
+
56
+ return g.xmlEncoder(w).Encode(&CompleteMultipartUploadResult{
57
+ - ETag: etag,
58
+ - Bucket: bucket,
59
+ - Key: object,
60
+ - Location: location,
61
+ + ETag: etag,
62
+ + Bucket: bucket,
63
+ + Key: object,
64
+ + Location: location,
65
+ + ChecksumSHA256: in.ChecksumSHA256,
66
+ + ChecksumType: in.ChecksumType,
67
+ })
68
+ }
69
+
70
+ --- a/vendor/github.com/johannesboyne/gofakes3/uploader.go
71
+ +++ b/vendor/github.com/johannesboyne/gofakes3/uploader.go
72
+ @@ -3,6 +3,8 @@
73
+ import (
74
+ "bytes"
75
+ "crypto/md5"
76
+ + "crypto/sha256"
77
+ + "encoding/base64"
78
+ "encoding/hex"
79
+ "fmt"
80
+ "io"
81
+ @@ -428,13 +430,26 @@
82
+ return "", "", ErrInvalidPart
83
+ }
84
+
85
+ - if !input.partsAreSorted() {
86
+ + if len(input.Parts) == 0 || !input.partsAreSorted() {
87
+ return "", "", ErrInvalidPartOrder
88
+ }
89
+
90
+ var size int64
91
+
92
+ - for _, inPart := range input.Parts {
93
+ + composite := sha256.New()
94
+ + withSHA256 := mpu.Meta["X-Amz-Checksum-Algorithm"] == "SHA256"
95
+ + if withSHA256 && input.ChecksumType != "" && input.ChecksumType != "COMPOSITE" {
96
+ + return "", "", ErrBadDigest
97
+ + }
98
+ + previous := 0
99
+ + for i, inPart := range input.Parts {
100
+ + if inPart.PartNumber <= previous {
101
+ + return "", "", ErrInvalidPartOrder
102
+ + }
103
+ + previous = inPart.PartNumber
104
+ + if withSHA256 && inPart.PartNumber != i+1 {
105
+ + return "", "", ErrInvalidPartOrder
106
+ + }
107
+ if inPart.PartNumber >= mpuPartsLen || mpu.parts[inPart.PartNumber] == nil {
108
+ return "", "", ErrorMessagef(ErrInvalidPart, "unexpected part number %d in complete request", inPart.PartNumber)
109
+ }
110
+ @@ -444,6 +459,13 @@
111
+ return "", "", ErrorMessagef(ErrInvalidPart, "unexpected part etag for number %d in complete request", inPart.PartNumber)
112
+ }
113
+
114
+ + if withSHA256 {
115
+ + digest := sha256.Sum256(upPart.Body)
116
+ + if inPart.ChecksumSHA256 != base64.StdEncoding.EncodeToString(digest[:]) {
117
+ + return "", "", ErrBadDigest
118
+ + }
119
+ + composite.Write(digest[:])
120
+ + }
121
+ size += int64(len(upPart.Body))
122
+ }
123
+
124
+ @@ -461,7 +483,20 @@
125
+
126
+ etag = fmt.Sprintf(`"%s-%d"`, hex.EncodeToString(hash.Sum(nil)), len(input.Parts))
127
+
128
+ - result, err := u.storage.PutObject(bucket, object, mpu.Meta, bytes.NewReader(body), int64(len(body)), nil)
129
+ + if withSHA256 {
130
+ + checksum := fmt.Sprintf("%s-%d", base64.StdEncoding.EncodeToString(composite.Sum(nil)), len(input.Parts))
131
+ + if input.ChecksumSHA256 != "" && input.ChecksumSHA256 != checksum {
132
+ + return "", "", ErrBadDigest
133
+ + }
134
+ + input.ChecksumSHA256 = checksum
135
+ + input.ChecksumType = "COMPOSITE"
136
+ + mpu.Meta["X-Amz-Checksum-Sha256"] = checksum
137
+ + mpu.Meta["X-Amz-Checksum-Type"] = "COMPOSITE"
138
+ + } else {
139
+ + input.ChecksumSHA256 = ""
140
+ + input.ChecksumType = ""
141
+ + }
142
+ + result, err := u.storage.PutObject(bucket, object, mpu.Meta, bytes.NewReader(body), int64(len(body)), input.Conditions)
143
+ if err != nil {
144
+ return "", "", err
145
+ }
146
+ --- a/vendor/github.com/johannesboyne/gofakes3/messages.go
147
+ +++ b/vendor/github.com/johannesboyne/gofakes3/messages.go
148
+ @@ -48,12 +48,16 @@
149
+ }
150
+
151
+ type CompletedPart struct {
152
+ - PartNumber int `xml:"PartNumber"`
153
+ - ETag string `xml:"ETag"`
154
+ + ChecksumSHA256 string `xml:"ChecksumSHA256,omitempty"`
155
+ + PartNumber int `xml:"PartNumber"`
156
+ + ETag string `xml:"ETag"`
157
+ }
158
+
159
+ type CompleteMultipartUploadRequest struct {
160
+ - Parts []CompletedPart `xml:"Part"`
161
+ + Conditions *PutConditions `xml:"-"`
162
+ + ChecksumSHA256 string `xml:"-"`
163
+ + ChecksumType string `xml:"-"`
164
+ + Parts []CompletedPart `xml:"Part"`
165
+ }
166
+
167
+ func (c CompleteMultipartUploadRequest) partsAreSorted() bool {
168
+ @@ -70,10 +74,12 @@
169
+ }
170
+
171
+ type CompleteMultipartUploadResult struct {
172
+ - Location string `xml:"Location"`
173
+ - Bucket string `xml:"Bucket"`
174
+ - Key string `xml:"Key"`
175
+ - ETag string `xml:"ETag"`
176
+ + ChecksumSHA256 string `xml:"ChecksumSHA256,omitempty"`
177
+ + ChecksumType string `xml:"ChecksumType,omitempty"`
178
+ + Location string `xml:"Location"`
179
+ + Bucket string `xml:"Bucket"`
180
+ + Key string `xml:"Key"`
181
+ + ETag string `xml:"ETag"`
182
+ }
183
+
184
+ type Content struct {
185
+ --- a/vendor/github.com/johannesboyne/gofakes3/checksum.go
186
+ +++ b/vendor/github.com/johannesboyne/gofakes3/checksum.go
187
+ @@ -0,0 +1,32 @@
188
+ +package gofakes3
189
+ +
190
+ +import (
191
+ + "crypto/sha256"
192
+ + "encoding/base64"
193
+ + "hash"
194
+ + "io"
195
+ +)
196
+ +
197
+ +// Validate before UploadPart commits bytes to the existing multipart store.
198
+ +type sha256PartReader struct {
199
+ + input io.Reader
200
+ + digest hash.Hash
201
+ + expected string
202
+ +}
203
+ +
204
+ +func newSHA256PartReader(input io.Reader, expected string) *sha256PartReader {
205
+ + return &sha256PartReader{input: input, digest: sha256.New(), expected: expected}
206
+ +}
207
+ +
208
+ +func (r *sha256PartReader) sum() string {
209
+ + return base64.StdEncoding.EncodeToString(r.digest.Sum(nil))
210
+ +}
211
+ +
212
+ +func (r *sha256PartReader) Read(p []byte) (int, error) {
213
+ + n, err := r.input.Read(p)
214
+ + r.digest.Write(p[:n])
215
+ + if err == io.EOF && r.sum() != r.expected {
216
+ + return n, ErrBadDigest
217
+ + }
218
+ + return n, err
219
+ +}
220
+ --- a/vendor/github.com/johannesboyne/gofakes3/backend.go
221
+ +++ b/vendor/github.com/johannesboyne/gofakes3/backend.go
222
+ @@ -3,6 +3,7 @@
223
+ import (
224
+ "encoding/hex"
225
+ "errors"
226
+ + "strings"
227
+
228
+ "io"
229
+ "time"
230
+ @@ -434,6 +435,11 @@
231
+ // carry over metadata if it exists
232
+ if existingObj != nil {
233
+ for k, v := range existingObj.Metadata {
234
+ + // Integrity metadata belongs to the bytes being written, never a
235
+ + // previous version of this key. CopyObject supplies its own metadata.
236
+ + if strings.HasPrefix(k, "X-Amz-Checksum-") {
237
+ + continue
238
+ + }
239
+ // new metadata overwrites old but keep the rest
240
+ // TODO: check how metadata can be deleted?!
241
+ if _, ok := meta[k]; !ok {
242
+ --- a/vendor/github.com/johannesboyne/gofakes3/gofakes3.go
243
+ +++ b/vendor/github.com/johannesboyne/gofakes3/gofakes3.go
244
+ @@ -960,6 +960,10 @@
245
+ return ErrInvalidPart
246
+ }
247
+
248
+ + if r.Header.Get("x-amz-copy-source") != "" {
249
+ + return g.copyMultipartUploadPart(bucket, object, uploadID, int(partNumber), w, r)
250
+ + }
251
+ +
252
+ size, err := strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64)
253
+ if err != nil || size <= 0 {
254
+ return ErrMissingContentLength
255
+ --- /dev/null
256
+ +++ b/vendor/github.com/johannesboyne/gofakes3/upload_part_copy.go
257
+ @@ -0,0 +1,95 @@
258
+ +package gofakes3
259
+ +
260
+ +import (
261
+ + "crypto/sha256"
262
+ + "encoding/base64"
263
+ + "encoding/xml"
264
+ + "io"
265
+ + "net/http"
266
+ + "net/url"
267
+ + "strings"
268
+ +)
269
+ +
270
+ +// Copy from one backend snapshot: a separate HEAD followed by GET could race
271
+ +// an overwrite and copy bytes that no longer match the caller's condition.
272
+ +func (g *GoFakeS3) copyMultipartUploadPart(bucket, object string, uploadID UploadID, number int, w http.ResponseWriter, r *http.Request) error {
273
+ + source, query, _ := strings.Cut(r.Header.Get("x-amz-copy-source"), "?")
274
+ + source, err := url.PathUnescape(source)
275
+ + if err != nil {
276
+ + return ErrInvalidArgument
277
+ + }
278
+ + parts := strings.SplitN(strings.TrimPrefix(source, "/"), "/", 2)
279
+ + if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
280
+ + return ErrInvalidArgument
281
+ + }
282
+ + params, err := url.ParseQuery(query)
283
+ + if err != nil {
284
+ + return ErrInvalidArgument
285
+ + }
286
+ + for key := range params {
287
+ + if key != "versionId" {
288
+ + return ErrInvalidArgument
289
+ + }
290
+ + }
291
+ + // Do not silently ignore conditions this compatibility subset cannot apply.
292
+ + for _, key := range []string{"x-amz-copy-source-if-modified-since", "x-amz-copy-source-if-unmodified-since"} {
293
+ + if r.Header.Get(key) != "" {
294
+ + return ErrNotImplemented
295
+ + }
296
+ + }
297
+ + var src *Object
298
+ + if version := params.Get("versionId"); version != "" {
299
+ + if g.versioned == nil {
300
+ + return ErrNotImplemented
301
+ + }
302
+ + src, err = g.versioned.GetObjectVersion(parts[0], parts[1], VersionID(version), nil)
303
+ + } else {
304
+ + src, err = g.storage.GetObject(parts[0], parts[1], nil)
305
+ + }
306
+ + if err != nil {
307
+ + return err
308
+ + }
309
+ + if src == nil || src.Contents == nil {
310
+ + return ErrInternal
311
+ + }
312
+ + defer src.Contents.Close()
313
+ + if src.IsDeleteMarker {
314
+ + return ErrInvalidArgument
315
+ + }
316
+ + tag := FormatETag(src.Hash)
317
+ + if expected := r.Header.Get("x-amz-copy-source-if-match"); expected != "" && expected != tag {
318
+ + return ErrPreconditionFailed
319
+ + }
320
+ + if expected := r.Header.Get("x-amz-copy-source-if-none-match"); expected != "" && expected == tag {
321
+ + return ErrPreconditionFailed
322
+ + }
323
+ + start, size := int64(0), src.Size
324
+ + if value := r.Header.Get("x-amz-copy-source-range"); value != "" {
325
+ + span, err := parseRangeHeader(value)
326
+ + if err != nil || span == nil || span.FromEnd || span.End == RangeNoEnd || span.End >= src.Size {
327
+ + return ErrInvalidRange
328
+ + }
329
+ + start, size = span.Start, span.End-span.Start+1
330
+ + }
331
+ + if size <= 0 {
332
+ + return ErrInvalidRange
333
+ + }
334
+ + if _, err := io.CopyN(io.Discard, src.Contents, start); err != nil {
335
+ + return err
336
+ + }
337
+ + digest := sha256.New()
338
+ + body := io.TeeReader(io.LimitReader(src.Contents, size), digest)
339
+ + etag, err := g.uploader.UploadPart(bucket, object, uploadID, number, size, body)
340
+ + if err != nil {
341
+ + return err
342
+ + }
343
+ + if src.VersionID != "" {
344
+ + w.Header().Set("x-amz-copy-source-version-id", string(src.VersionID))
345
+ + }
346
+ + return g.xmlEncoder(w).Encode(struct {
347
+ + XMLName xml.Name `xml:"CopyPartResult"`
348
+ + ETag string `xml:"ETag"`
349
+ + LastModified ContentTime `xml:"LastModified"`
350
+ + ChecksumSHA256 string `xml:"ChecksumSHA256"`
351
+ + }{ETag: etag, LastModified: NewContentTime(g.timeSource.Now()), ChecksumSHA256: base64.StdEncoding.EncodeToString(digest.Sum(nil))})
352
+ +}
@@ -0,0 +1,12 @@
1
+ #!/bin/sh
2
+ # Exercise the exact vendored compatibility patches used by the runtime image.
3
+ set -eu
4
+ source_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
5
+ test_dir=$(mktemp -d)
6
+ trap 'rm -rf "$test_dir"' EXIT HUP INT TERM
7
+ cp "$source_dir"/*.go "$source_dir/go.mod" "$source_dir/go.sum" "$source_dir"/*.patch "$test_dir/"
8
+ cd "$test_dir"
9
+ go mod vendor
10
+ patch -p1 < metadata.patch
11
+ patch -p1 < multipart.patch
12
+ go test -mod=vendor -race ./...
@@ -52,11 +52,11 @@ const S3_DOCKERFILE = `
52
52
  FROM golang:1.27.2-alpine@sha256:f92b6ef800e499660581efdabdf25d9d817a9d124eaf900924f0504e7e27e12d AS build
53
53
  RUN apk add --no-cache patch
54
54
  WORKDIR /src
55
- ${["go.mod", "go.sum", "main.go", "metadata.patch"].map((file) => {
55
+ ${["go.mod", "go.sum", "main.go", "metadata.patch", "multipart.patch"].map((file) => {
56
56
  const data = readFileSync(new URL(`./s3-runtime/${file}`, import.meta.url)).toString("base64");
57
57
  return `RUN printf '%s' '${data}' | base64 -d > ${file}`;
58
58
  }).join("\n")}
59
- RUN go mod vendor && patch -p1 < metadata.patch
59
+ RUN go mod vendor && patch -p1 < metadata.patch && patch -p1 < multipart.patch
60
60
  RUN CGO_ENABLED=0 go build -mod=vendor -trimpath -ldflags='-s -w' -o /s3-server .
61
61
  FROM scratch
62
62
  COPY --from=build /s3-server /s3-server