fix(registry) stream blob uploads and lower default chunk to 95MB
- commit
- fce0810071fe09068787a1302b91b09ccef61fc8
- author
- bnema · · 2026-02-06 20:42 UTC
- parent
- 28e7d52
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
9 files · +67 -63
M internal/adapters/in/http/registry/handler.go +8 -14
| @@ -23,9 +23,9 @@ const ( | |||
| 23 | 23 | // MaxManifestSize limits manifest uploads to 10MB. | |
| 24 | 24 | MaxManifestSize = 10 * 1024 * 1024 | |
| 25 | 25 | // DefaultMaxBlobChunkSize is the default maximum size for a single blob upload chunk. | |
| 26 | - | // Docker/Podman typically send a full layer as one request body, so this default | |
| 27 | - | // must handle common base image layer sizes. | |
| 28 | - | DefaultMaxBlobChunkSize = 512 * 1024 * 1024 | |
| 26 | + | // Kept at 95MB to stay within Cloudflare's 100MB per-request limit. | |
| 27 | + | // Users who need larger chunks can configure it explicitly via max_blob_chunk_size. | |
| 28 | + | DefaultMaxBlobChunkSize = 95 * 1024 * 1024 | |
| 29 | 29 | ) | |
| 30 | 30 | ||
| 31 | 31 | // Handler implements the HTTP handler for Docker Registry API v2. | |
| @@ -407,11 +407,13 @@ func (h *Handler) handleBlobUpload(w http.ResponseWriter, r *http.Request) { | |||
| 407 | 407 | } | |
| 408 | 408 | } | |
| 409 | 409 | ||
| 410 | - | // Limit blob chunk size to prevent memory exhaustion | |
| 410 | + | // Limit blob chunk size to prevent excessive uploads. | |
| 411 | + | // MaxBytesReader wraps the body so the downstream io.Copy stops at the limit. | |
| 411 | 412 | r.Body = http.MaxBytesReader(w, r.Body, h.maxBlobChunkSize) | |
| 412 | 413 | ||
| 413 | - | // Read the chunk from the request body | |
| 414 | - | chunk, err := io.ReadAll(r.Body) | |
| 414 | + | // Stream the body directly to storage — memory usage is bounded to a small | |
| 415 | + | // copy buffer (~32KB) regardless of chunk size. | |
| 416 | + | length, err := h.registrySvc.AppendBlobChunk(ctx, name, uuid, r.Body) | |
| 415 | 417 | if err != nil { | |
| 416 | 418 | var maxBytesErr *http.MaxBytesError | |
| 417 | 419 | if errors.As(err, &maxBytesErr) { | |
| @@ -419,14 +421,6 @@ func (h *Handler) handleBlobUpload(w http.ResponseWriter, r *http.Request) { | |||
| 419 | 421 | h.sendRegistryError(w, http.StatusRequestEntityTooLarge, "SIZE_INVALID", "blob chunk exceeds maximum size") | |
| 420 | 422 | return | |
| 421 | 423 | } | |
| 422 | - | log.Error().Err(err).Msg("failed to read blob chunk") | |
| 423 | - | h.sendRegistryError(w, http.StatusBadRequest, "BLOB_UPLOAD_INVALID", "invalid blob chunk") | |
| 424 | - | return | |
| 425 | - | } | |
| 426 | - | ||
| 427 | - | // Append the chunk to the upload | |
| 428 | - | length, err := h.registrySvc.AppendBlobChunk(ctx, name, uuid, chunk) | |
| 429 | - | if err != nil { | |
| 430 | 424 | log.Error().Err(err).Msg("failed to append blob chunk") | |
| 431 | 425 | h.sendRegistryError(w, http.StatusInternalServerError, "BLOB_UPLOAD_UNKNOWN", "failed to append blob chunk") | |
| 432 | 426 | return | |
| @@ -23,9 +23,9 @@ const ( | |||||
| 23 | // MaxManifestSize limits manifest uploads to 10MB. | 23 | // MaxManifestSize limits manifest uploads to 10MB. | ||
| 24 | MaxManifestSize = 10 * 1024 * 1024 | 24 | MaxManifestSize = 10 * 1024 * 1024 | ||
| 25 | // DefaultMaxBlobChunkSize is the default maximum size for a single blob upload chunk. | 25 | // DefaultMaxBlobChunkSize is the default maximum size for a single blob upload chunk. | ||
| 26 | - | // Docker/Podman typically send a full layer as one request body, so this default | 26 | + | // Kept at 95MB to stay within Cloudflare's 100MB per-request limit. |
| 27 | - | // must handle common base image layer sizes. | 27 | + | // Users who need larger chunks can configure it explicitly via max_blob_chunk_size. |
| 28 | - | DefaultMaxBlobChunkSize = 512 * 1024 * 1024 | 28 | + | DefaultMaxBlobChunkSize = 95 * 1024 * 1024 |
| 29 | ) | 29 | ) | ||
| 30 | 30 | ||||
| 31 | // Handler implements the HTTP handler for Docker Registry API v2. | 31 | // Handler implements the HTTP handler for Docker Registry API v2. | ||
| @@ -407,11 +407,13 @@ func (h *Handler) handleBlobUpload(w http.ResponseWriter, r *http.Request) { | |||||
| 407 | } | 407 | } | ||
| 408 | } | 408 | } | ||
| 409 | 409 | ||||
| 410 | - | // Limit blob chunk size to prevent memory exhaustion | 410 | + | // Limit blob chunk size to prevent excessive uploads. |
| 411 | + | // MaxBytesReader wraps the body so the downstream io.Copy stops at the limit. | |||
| 411 | r.Body = http.MaxBytesReader(w, r.Body, h.maxBlobChunkSize) | 412 | r.Body = http.MaxBytesReader(w, r.Body, h.maxBlobChunkSize) | ||
| 412 | 413 | ||||
| 413 | - | // Read the chunk from the request body | 414 | + | // Stream the body directly to storage — memory usage is bounded to a small |
| 414 | - | chunk, err := io.ReadAll(r.Body) | 415 | + | // copy buffer (~32KB) regardless of chunk size. |
| 416 | + | length, err := h.registrySvc.AppendBlobChunk(ctx, name, uuid, r.Body) | |||
| 415 | if err != nil { | 417 | if err != nil { | ||
| 416 | var maxBytesErr *http.MaxBytesError | 418 | var maxBytesErr *http.MaxBytesError | ||
| 417 | if errors.As(err, &maxBytesErr) { | 419 | if errors.As(err, &maxBytesErr) { | ||
| @@ -419,14 +421,6 @@ func (h *Handler) handleBlobUpload(w http.ResponseWriter, r *http.Request) { | |||||
| 419 | h.sendRegistryError(w, http.StatusRequestEntityTooLarge, "SIZE_INVALID", "blob chunk exceeds maximum size") | 421 | h.sendRegistryError(w, http.StatusRequestEntityTooLarge, "SIZE_INVALID", "blob chunk exceeds maximum size") | ||
| 420 | return | 422 | return | ||
| 421 | } | 423 | } | ||
| 422 | - | log.Error().Err(err).Msg("failed to read blob chunk") | |||
| 423 | - | h.sendRegistryError(w, http.StatusBadRequest, "BLOB_UPLOAD_INVALID", "invalid blob chunk") | |||
| 424 | - | return | |||
| 425 | - | } | |||
| 426 | - | ||||
| 427 | - | // Append the chunk to the upload | |||
| 428 | - | length, err := h.registrySvc.AppendBlobChunk(ctx, name, uuid, chunk) | |||
| 429 | - | if err != nil { | |||
| 430 | log.Error().Err(err).Msg("failed to append blob chunk") | 424 | log.Error().Err(err).Msg("failed to append blob chunk") | ||
| 431 | h.sendRegistryError(w, http.StatusInternalServerError, "BLOB_UPLOAD_UNKNOWN", "failed to append blob chunk") | 425 | h.sendRegistryError(w, http.StatusInternalServerError, "BLOB_UPLOAD_UNKNOWN", "failed to append blob chunk") | ||
| 432 | return | 426 | return | ||
M internal/adapters/in/http/registry/handler_test.go +17 -6
| @@ -2,7 +2,9 @@ package registry | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "bytes" | |
| 5 | + | "context" | |
| 5 | 6 | "encoding/json" | |
| 7 | + | "io" | |
| 6 | 8 | "net/http" | |
| 7 | 9 | "net/http/httptest" | |
| 8 | 10 | "os" | |
| @@ -291,7 +293,7 @@ func TestHandler_BlobUpload_PATCH(t *testing.T) { | |||
| 291 | 293 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | |
| 292 | 294 | ||
| 293 | 295 | chunkData := []byte("chunk content") | |
| 294 | - | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", chunkData).Return(int64(len(chunkData)), nil) | |
| 296 | + | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", mock.Anything).Return(int64(len(chunkData)), nil) | |
| 295 | 297 | ||
| 296 | 298 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader(chunkData)) | |
| 297 | 299 | rec := httptest.NewRecorder() | |
| @@ -308,7 +310,7 @@ func TestHandler_BlobUpload_PUT_Finalize(t *testing.T) { | |||
| 308 | 310 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | |
| 309 | 311 | ||
| 310 | 312 | chunkData := []byte("final chunk") | |
| 311 | - | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", chunkData).Return(int64(len(chunkData)), nil) | |
| 313 | + | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", mock.Anything).Return(int64(len(chunkData)), nil) | |
| 312 | 314 | registrySvc.EXPECT().FinishUpload(mock.Anything, "550e8400-e29b-41d4-a716-446655440000", "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4").Return(nil) | |
| 313 | 315 | ||
| 314 | 316 | req := httptest.NewRequest("PUT", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000?digest=sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", bytes.NewReader(chunkData)) | |
| @@ -430,7 +432,7 @@ func TestHandler_BlobUpload_PATCH_ReturnsDockerUploadUUID(t *testing.T) { | |||
| 430 | 432 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | |
| 431 | 433 | ||
| 432 | 434 | chunkData := []byte("chunk content") | |
| 433 | - | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", chunkData).Return(int64(len(chunkData)), nil) | |
| 435 | + | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", mock.Anything).Return(int64(len(chunkData)), nil) | |
| 434 | 436 | ||
| 435 | 437 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader(chunkData)) | |
| 436 | 438 | rec := httptest.NewRecorder() | |
| @@ -441,14 +443,23 @@ func TestHandler_BlobUpload_PATCH_ReturnsDockerUploadUUID(t *testing.T) { | |||
| 441 | 443 | assert.Equal(t, "550e8400-e29b-41d4-a716-446655440000", rec.Header().Get("Docker-Upload-UUID")) | |
| 442 | 444 | } | |
| 443 | 445 | ||
| 444 | - | func TestDefaultMaxBlobChunkSize_Is512MB(t *testing.T) { | |
| 445 | - | assert.Equal(t, int64(512*1024*1024), int64(DefaultMaxBlobChunkSize)) | |
| 446 | + | func TestDefaultMaxBlobChunkSize_Is95MB(t *testing.T) { | |
| 447 | + | assert.Equal(t, int64(95*1024*1024), int64(DefaultMaxBlobChunkSize)) | |
| 446 | 448 | } | |
| 447 | 449 | ||
| 448 | 450 | func TestHandler_BlobUpload_PATCH_RespectsConfiguredMaxBlobChunkSize(t *testing.T) { | |
| 449 | 451 | registrySvc := inmocks.NewMockRegistryService(t) | |
| 450 | 452 | handler := NewHandler(registrySvc, testLogger(), 5) | |
| 451 | 453 | ||
| 454 | + | // The handler wraps r.Body in MaxBytesReader and passes it to AppendBlobChunk. | |
| 455 | + | // The mock must read the body to trigger MaxBytesError, which the handler detects. | |
| 456 | + | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", mock.Anything). | |
| 457 | + | Run(func(_ context.Context, _ string, _ string, data io.Reader) { | |
| 458 | + | // Drain the reader to trigger MaxBytesError | |
| 459 | + | _, _ = io.ReadAll(data) | |
| 460 | + | }). | |
| 461 | + | Return(int64(0), &http.MaxBytesError{Limit: 5}) | |
| 462 | + | ||
| 452 | 463 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader([]byte("chunk content"))) | |
| 453 | 464 | rec := httptest.NewRecorder() | |
| 454 | 465 | ||
| @@ -462,7 +473,7 @@ func TestHandler_BlobUpload_PATCH_ReturnsRangeHeader(t *testing.T) { | |||
| 462 | 473 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | |
| 463 | 474 | ||
| 464 | 475 | chunkData := []byte("chunk content") | |
| 465 | - | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", chunkData).Return(int64(len(chunkData)), nil) | |
| 476 | + | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", mock.Anything).Return(int64(len(chunkData)), nil) | |
| 466 | 477 | ||
| 467 | 478 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader(chunkData)) | |
| 468 | 479 | rec := httptest.NewRecorder() | |
| @@ -2,7 +2,9 @@ package registry | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "bytes" | 4 | "bytes" | ||
| 5 | + | "context" | |||
| 5 | "encoding/json" | 6 | "encoding/json" | ||
| 7 | + | "io" | |||
| 6 | "net/http" | 8 | "net/http" | ||
| 7 | "net/http/httptest" | 9 | "net/http/httptest" | ||
| 8 | "os" | 10 | "os" | ||
| @@ -291,7 +293,7 @@ func TestHandler_BlobUpload_PATCH(t *testing.T) { | |||||
| 291 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | 293 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | ||
| 292 | 294 | ||||
| 293 | chunkData := []byte("chunk content") | 295 | chunkData := []byte("chunk content") | ||
| 294 | - | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", chunkData).Return(int64(len(chunkData)), nil) | 296 | + | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", mock.Anything).Return(int64(len(chunkData)), nil) |
| 295 | 297 | ||||
| 296 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader(chunkData)) | 298 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader(chunkData)) | ||
| 297 | rec := httptest.NewRecorder() | 299 | rec := httptest.NewRecorder() | ||
| @@ -308,7 +310,7 @@ func TestHandler_BlobUpload_PUT_Finalize(t *testing.T) { | |||||
| 308 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | 310 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | ||
| 309 | 311 | ||||
| 310 | chunkData := []byte("final chunk") | 312 | chunkData := []byte("final chunk") | ||
| 311 | - | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", chunkData).Return(int64(len(chunkData)), nil) | 313 | + | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", mock.Anything).Return(int64(len(chunkData)), nil) |
| 312 | registrySvc.EXPECT().FinishUpload(mock.Anything, "550e8400-e29b-41d4-a716-446655440000", "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4").Return(nil) | 314 | registrySvc.EXPECT().FinishUpload(mock.Anything, "550e8400-e29b-41d4-a716-446655440000", "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4").Return(nil) | ||
| 313 | 315 | ||||
| 314 | req := httptest.NewRequest("PUT", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000?digest=sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", bytes.NewReader(chunkData)) | 316 | req := httptest.NewRequest("PUT", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000?digest=sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4", bytes.NewReader(chunkData)) | ||
| @@ -430,7 +432,7 @@ func TestHandler_BlobUpload_PATCH_ReturnsDockerUploadUUID(t *testing.T) { | |||||
| 430 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | 432 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | ||
| 431 | 433 | ||||
| 432 | chunkData := []byte("chunk content") | 434 | chunkData := []byte("chunk content") | ||
| 433 | - | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", chunkData).Return(int64(len(chunkData)), nil) | 435 | + | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", mock.Anything).Return(int64(len(chunkData)), nil) |
| 434 | 436 | ||||
| 435 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader(chunkData)) | 437 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader(chunkData)) | ||
| 436 | rec := httptest.NewRecorder() | 438 | rec := httptest.NewRecorder() | ||
| @@ -441,14 +443,23 @@ func TestHandler_BlobUpload_PATCH_ReturnsDockerUploadUUID(t *testing.T) { | |||||
| 441 | assert.Equal(t, "550e8400-e29b-41d4-a716-446655440000", rec.Header().Get("Docker-Upload-UUID")) | 443 | assert.Equal(t, "550e8400-e29b-41d4-a716-446655440000", rec.Header().Get("Docker-Upload-UUID")) | ||
| 442 | } | 444 | } | ||
| 443 | 445 | ||||
| 444 | - | func TestDefaultMaxBlobChunkSize_Is512MB(t *testing.T) { | 446 | + | func TestDefaultMaxBlobChunkSize_Is95MB(t *testing.T) { |
| 445 | - | assert.Equal(t, int64(512*1024*1024), int64(DefaultMaxBlobChunkSize)) | 447 | + | assert.Equal(t, int64(95*1024*1024), int64(DefaultMaxBlobChunkSize)) |
| 446 | } | 448 | } | ||
| 447 | 449 | ||||
| 448 | func TestHandler_BlobUpload_PATCH_RespectsConfiguredMaxBlobChunkSize(t *testing.T) { | 450 | func TestHandler_BlobUpload_PATCH_RespectsConfiguredMaxBlobChunkSize(t *testing.T) { | ||
| 449 | registrySvc := inmocks.NewMockRegistryService(t) | 451 | registrySvc := inmocks.NewMockRegistryService(t) | ||
| 450 | handler := NewHandler(registrySvc, testLogger(), 5) | 452 | handler := NewHandler(registrySvc, testLogger(), 5) | ||
| 451 | 453 | ||||
| 454 | + | // The handler wraps r.Body in MaxBytesReader and passes it to AppendBlobChunk. | |||
| 455 | + | // The mock must read the body to trigger MaxBytesError, which the handler detects. | |||
| 456 | + | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", mock.Anything). | |||
| 457 | + | Run(func(_ context.Context, _ string, _ string, data io.Reader) { | |||
| 458 | + | // Drain the reader to trigger MaxBytesError | |||
| 459 | + | _, _ = io.ReadAll(data) | |||
| 460 | + | }). | |||
| 461 | + | Return(int64(0), &http.MaxBytesError{Limit: 5}) | |||
| 462 | + | ||||
| 452 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader([]byte("chunk content"))) | 463 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader([]byte("chunk content"))) | ||
| 453 | rec := httptest.NewRecorder() | 464 | rec := httptest.NewRecorder() | ||
| 454 | 465 | ||||
| @@ -462,7 +473,7 @@ func TestHandler_BlobUpload_PATCH_ReturnsRangeHeader(t *testing.T) { | |||||
| 462 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | 473 | handler := NewHandler(registrySvc, testLogger(), DefaultMaxBlobChunkSize) | ||
| 463 | 474 | ||||
| 464 | chunkData := []byte("chunk content") | 475 | chunkData := []byte("chunk content") | ||
| 465 | - | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", chunkData).Return(int64(len(chunkData)), nil) | 476 | + | registrySvc.EXPECT().AppendBlobChunk(mock.Anything, "myapp", "550e8400-e29b-41d4-a716-446655440000", mock.Anything).Return(int64(len(chunkData)), nil) |
| 466 | 477 | ||||
| 467 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader(chunkData)) | 478 | req := httptest.NewRequest("PATCH", "/v2/myapp/blobs/uploads/550e8400-e29b-41d4-a716-446655440000", bytes.NewReader(chunkData)) | ||
| 468 | rec := httptest.NewRecorder() | 479 | rec := httptest.NewRecorder() | ||
M internal/adapters/out/filesystem/blob.go +3 -3
| @@ -197,7 +197,7 @@ func (s *BlobStorage) StartBlobUpload(name string) (string, error) { | |||
| 197 | 197 | } | |
| 198 | 198 | ||
| 199 | 199 | // AppendBlobChunk appends data to an in-progress upload. | |
| 200 | - | func (s *BlobStorage) AppendBlobChunk(name, uuid string, chunk []byte) (int64, error) { | |
| 200 | + | func (s *BlobStorage) AppendBlobChunk(name, uuid string, data io.Reader) (int64, error) { | |
| 201 | 201 | uploadPath, err := s.getUploadPath(uuid) | |
| 202 | 202 | if err != nil { | |
| 203 | 203 | return 0, fmt.Errorf("invalid upload path: %w", err) | |
| @@ -212,7 +212,7 @@ func (s *BlobStorage) AppendBlobChunk(name, uuid string, chunk []byte) (int64, e | |||
| 212 | 212 | } | |
| 213 | 213 | defer file.Close() | |
| 214 | 214 | ||
| 215 | - | written, err := file.Write(chunk) | |
| 215 | + | written, err := io.Copy(file, data) | |
| 216 | 216 | if err != nil { | |
| 217 | 217 | return 0, fmt.Errorf("failed to write chunk to upload file: %w", err) | |
| 218 | 218 | } | |
| @@ -227,7 +227,7 @@ func (s *BlobStorage) AppendBlobChunk(name, uuid string, chunk []byte) (int64, e | |||
| 227 | 227 | Str(zerowrap.FieldAdapter, "filesystem"). | |
| 228 | 228 | Str("uuid", uuid). | |
| 229 | 229 | Str("name", name). | |
| 230 | - | Int("chunk_size", written). | |
| 230 | + | Int64("chunk_size", written). | |
| 231 | 231 | Int64("total_size", fi.Size()). | |
| 232 | 232 | Msg("appended chunk to blob upload") | |
| 233 | 233 | ||
| @@ -197,7 +197,7 @@ func (s *BlobStorage) StartBlobUpload(name string) (string, error) { | |||||
| 197 | } | 197 | } | ||
| 198 | 198 | ||||
| 199 | // AppendBlobChunk appends data to an in-progress upload. | 199 | // AppendBlobChunk appends data to an in-progress upload. | ||
| 200 | - | func (s *BlobStorage) AppendBlobChunk(name, uuid string, chunk []byte) (int64, error) { | 200 | + | func (s *BlobStorage) AppendBlobChunk(name, uuid string, data io.Reader) (int64, error) { |
| 201 | uploadPath, err := s.getUploadPath(uuid) | 201 | uploadPath, err := s.getUploadPath(uuid) | ||
| 202 | if err != nil { | 202 | if err != nil { | ||
| 203 | return 0, fmt.Errorf("invalid upload path: %w", err) | 203 | return 0, fmt.Errorf("invalid upload path: %w", err) | ||
| @@ -212,7 +212,7 @@ func (s *BlobStorage) AppendBlobChunk(name, uuid string, chunk []byte) (int64, e | |||||
| 212 | } | 212 | } | ||
| 213 | defer file.Close() | 213 | defer file.Close() | ||
| 214 | 214 | ||||
| 215 | - | written, err := file.Write(chunk) | 215 | + | written, err := io.Copy(file, data) |
| 216 | if err != nil { | 216 | if err != nil { | ||
| 217 | return 0, fmt.Errorf("failed to write chunk to upload file: %w", err) | 217 | return 0, fmt.Errorf("failed to write chunk to upload file: %w", err) | ||
| 218 | } | 218 | } | ||
| @@ -227,7 +227,7 @@ func (s *BlobStorage) AppendBlobChunk(name, uuid string, chunk []byte) (int64, e | |||||
| 227 | Str(zerowrap.FieldAdapter, "filesystem"). | 227 | Str(zerowrap.FieldAdapter, "filesystem"). | ||
| 228 | Str("uuid", uuid). | 228 | Str("uuid", uuid). | ||
| 229 | Str("name", name). | 229 | Str("name", name). | ||
| 230 | - | Int("chunk_size", written). | 230 | + | Int64("chunk_size", written). |
| 231 | Int64("total_size", fi.Size()). | 231 | Int64("total_size", fi.Size()). | ||
| 232 | Msg("appended chunk to blob upload") | 232 | Msg("appended chunk to blob upload") | ||
| 233 | 233 | ||||
M internal/adapters/out/filesystem/blob_test.go +5 -5
| @@ -242,13 +242,13 @@ func TestBlobStorage_AppendBlobChunk(t *testing.T) { | |||
| 242 | 242 | ||
| 243 | 243 | // Append first chunk | |
| 244 | 244 | chunk1 := []byte("first chunk") | |
| 245 | - | size1, err := storage.AppendBlobChunk("myapp", uuid, chunk1) | |
| 245 | + | size1, err := storage.AppendBlobChunk("myapp", uuid, bytes.NewReader(chunk1)) | |
| 246 | 246 | require.NoError(t, err) | |
| 247 | 247 | assert.Equal(t, int64(len(chunk1)), size1) | |
| 248 | 248 | ||
| 249 | 249 | // Append second chunk | |
| 250 | 250 | chunk2 := []byte(" second chunk") | |
| 251 | - | size2, err := storage.AppendBlobChunk("myapp", uuid, chunk2) | |
| 251 | + | size2, err := storage.AppendBlobChunk("myapp", uuid, bytes.NewReader(chunk2)) | |
| 252 | 252 | require.NoError(t, err) | |
| 253 | 253 | assert.Equal(t, int64(len(chunk1)+len(chunk2)), size2) | |
| 254 | 254 | } | |
| @@ -260,7 +260,7 @@ func TestBlobStorage_AppendBlobChunk_NotFound(t *testing.T) { | |||
| 260 | 260 | storage, err := NewBlobStorage(tmpDir, log) | |
| 261 | 261 | require.NoError(t, err) | |
| 262 | 262 | ||
| 263 | - | _, err = storage.AppendBlobChunk("myapp", "00000000-0000-4000-a000-000000000000", []byte("data")) | |
| 263 | + | _, err = storage.AppendBlobChunk("myapp", "00000000-0000-4000-a000-000000000000", bytes.NewReader([]byte("data"))) | |
| 264 | 264 | ||
| 265 | 265 | assert.Error(t, err) | |
| 266 | 266 | assert.Contains(t, err.Error(), "upload not found") | |
| @@ -312,7 +312,7 @@ func TestBlobStorage_FinishBlobUpload(t *testing.T) { | |||
| 312 | 312 | ||
| 313 | 313 | // Append data - content that hashes to testDigest1 | |
| 314 | 314 | blobData := []byte("complete blob content") | |
| 315 | - | _, err = storage.AppendBlobChunk("myapp", uuid, blobData) | |
| 315 | + | _, err = storage.AppendBlobChunk("myapp", uuid, bytes.NewReader(blobData)) | |
| 316 | 316 | require.NoError(t, err) | |
| 317 | 317 | ||
| 318 | 318 | // Finish upload - use testDigest1 (validation will pass, but hash won't match) | |
| @@ -439,7 +439,7 @@ func TestBlobStorage_CompleteUploadFlow(t *testing.T) { | |||
| 439 | 439 | ||
| 440 | 440 | var totalSize int64 | |
| 441 | 441 | for _, chunk := range chunks { | |
| 442 | - | size, err := storage.AppendBlobChunk(repoName, uuid, chunk) | |
| 442 | + | size, err := storage.AppendBlobChunk(repoName, uuid, bytes.NewReader(chunk)) | |
| 443 | 443 | require.NoError(t, err) | |
| 444 | 444 | totalSize = size | |
| 445 | 445 | } | |
| @@ -242,13 +242,13 @@ func TestBlobStorage_AppendBlobChunk(t *testing.T) { | |||||
| 242 | 242 | ||||
| 243 | // Append first chunk | 243 | // Append first chunk | ||
| 244 | chunk1 := []byte("first chunk") | 244 | chunk1 := []byte("first chunk") | ||
| 245 | - | size1, err := storage.AppendBlobChunk("myapp", uuid, chunk1) | 245 | + | size1, err := storage.AppendBlobChunk("myapp", uuid, bytes.NewReader(chunk1)) |
| 246 | require.NoError(t, err) | 246 | require.NoError(t, err) | ||
| 247 | assert.Equal(t, int64(len(chunk1)), size1) | 247 | assert.Equal(t, int64(len(chunk1)), size1) | ||
| 248 | 248 | ||||
| 249 | // Append second chunk | 249 | // Append second chunk | ||
| 250 | chunk2 := []byte(" second chunk") | 250 | chunk2 := []byte(" second chunk") | ||
| 251 | - | size2, err := storage.AppendBlobChunk("myapp", uuid, chunk2) | 251 | + | size2, err := storage.AppendBlobChunk("myapp", uuid, bytes.NewReader(chunk2)) |
| 252 | require.NoError(t, err) | 252 | require.NoError(t, err) | ||
| 253 | assert.Equal(t, int64(len(chunk1)+len(chunk2)), size2) | 253 | assert.Equal(t, int64(len(chunk1)+len(chunk2)), size2) | ||
| 254 | } | 254 | } | ||
| @@ -260,7 +260,7 @@ func TestBlobStorage_AppendBlobChunk_NotFound(t *testing.T) { | |||||
| 260 | storage, err := NewBlobStorage(tmpDir, log) | 260 | storage, err := NewBlobStorage(tmpDir, log) | ||
| 261 | require.NoError(t, err) | 261 | require.NoError(t, err) | ||
| 262 | 262 | ||||
| 263 | - | _, err = storage.AppendBlobChunk("myapp", "00000000-0000-4000-a000-000000000000", []byte("data")) | 263 | + | _, err = storage.AppendBlobChunk("myapp", "00000000-0000-4000-a000-000000000000", bytes.NewReader([]byte("data"))) |
| 264 | 264 | ||||
| 265 | assert.Error(t, err) | 265 | assert.Error(t, err) | ||
| 266 | assert.Contains(t, err.Error(), "upload not found") | 266 | assert.Contains(t, err.Error(), "upload not found") | ||
| @@ -312,7 +312,7 @@ func TestBlobStorage_FinishBlobUpload(t *testing.T) { | |||||
| 312 | 312 | ||||
| 313 | // Append data - content that hashes to testDigest1 | 313 | // Append data - content that hashes to testDigest1 | ||
| 314 | blobData := []byte("complete blob content") | 314 | blobData := []byte("complete blob content") | ||
| 315 | - | _, err = storage.AppendBlobChunk("myapp", uuid, blobData) | 315 | + | _, err = storage.AppendBlobChunk("myapp", uuid, bytes.NewReader(blobData)) |
| 316 | require.NoError(t, err) | 316 | require.NoError(t, err) | ||
| 317 | 317 | ||||
| 318 | // Finish upload - use testDigest1 (validation will pass, but hash won't match) | 318 | // Finish upload - use testDigest1 (validation will pass, but hash won't match) | ||
| @@ -439,7 +439,7 @@ func TestBlobStorage_CompleteUploadFlow(t *testing.T) { | |||||
| 439 | 439 | ||||
| 440 | var totalSize int64 | 440 | var totalSize int64 | ||
| 441 | for _, chunk := range chunks { | 441 | for _, chunk := range chunks { | ||
| 442 | - | size, err := storage.AppendBlobChunk(repoName, uuid, chunk) | 442 | + | size, err := storage.AppendBlobChunk(repoName, uuid, bytes.NewReader(chunk)) |
| 443 | require.NoError(t, err) | 443 | require.NoError(t, err) | ||
| 444 | totalSize = size | 444 | totalSize = size | ||
| 445 | } | 445 | } | ||
M internal/boundaries/in/mocks/mock_registry_service.go +15 -15
| @@ -40,8 +40,8 @@ func (_m *MockRegistryService) EXPECT() *MockRegistryService_Expecter { | |||
| 40 | 40 | } | |
| 41 | 41 | ||
| 42 | 42 | // AppendBlobChunk provides a mock function for the type MockRegistryService | |
| 43 | - | func (_mock *MockRegistryService) AppendBlobChunk(ctx context.Context, name string, uuid string, chunk []byte) (int64, error) { | |
| 44 | - | ret := _mock.Called(ctx, name, uuid, chunk) | |
| 43 | + | func (_mock *MockRegistryService) AppendBlobChunk(ctx context.Context, name string, uuid string, data io.Reader) (int64, error) { | |
| 44 | + | ret := _mock.Called(ctx, name, uuid, data) | |
| 45 | 45 | ||
| 46 | 46 | if len(ret) == 0 { | |
| 47 | 47 | panic("no return value specified for AppendBlobChunk") | |
| @@ -49,16 +49,16 @@ func (_mock *MockRegistryService) AppendBlobChunk(ctx context.Context, name stri | |||
| 49 | 49 | ||
| 50 | 50 | var r0 int64 | |
| 51 | 51 | var r1 error | |
| 52 | - | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, []byte) (int64, error)); ok { | |
| 53 | - | return returnFunc(ctx, name, uuid, chunk) | |
| 52 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, io.Reader) (int64, error)); ok { | |
| 53 | + | return returnFunc(ctx, name, uuid, data) | |
| 54 | 54 | } | |
| 55 | - | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, []byte) int64); ok { | |
| 56 | - | r0 = returnFunc(ctx, name, uuid, chunk) | |
| 55 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, io.Reader) int64); ok { | |
| 56 | + | r0 = returnFunc(ctx, name, uuid, data) | |
| 57 | 57 | } else { | |
| 58 | 58 | r0 = ret.Get(0).(int64) | |
| 59 | 59 | } | |
| 60 | - | if returnFunc, ok := ret.Get(1).(func(context.Context, string, string, []byte) error); ok { | |
| 61 | - | r1 = returnFunc(ctx, name, uuid, chunk) | |
| 60 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, string, string, io.Reader) error); ok { | |
| 61 | + | r1 = returnFunc(ctx, name, uuid, data) | |
| 62 | 62 | } else { | |
| 63 | 63 | r1 = ret.Error(1) | |
| 64 | 64 | } | |
| @@ -74,12 +74,12 @@ type MockRegistryService_AppendBlobChunk_Call struct { | |||
| 74 | 74 | // - ctx context.Context | |
| 75 | 75 | // - name string | |
| 76 | 76 | // - uuid string | |
| 77 | - | // - chunk []byte | |
| 78 | - | func (_e *MockRegistryService_Expecter) AppendBlobChunk(ctx interface{}, name interface{}, uuid interface{}, chunk interface{}) *MockRegistryService_AppendBlobChunk_Call { | |
| 79 | - | return &MockRegistryService_AppendBlobChunk_Call{Call: _e.mock.On("AppendBlobChunk", ctx, name, uuid, chunk)} | |
| 77 | + | // - data io.Reader | |
| 78 | + | func (_e *MockRegistryService_Expecter) AppendBlobChunk(ctx interface{}, name interface{}, uuid interface{}, data interface{}) *MockRegistryService_AppendBlobChunk_Call { | |
| 79 | + | return &MockRegistryService_AppendBlobChunk_Call{Call: _e.mock.On("AppendBlobChunk", ctx, name, uuid, data)} | |
| 80 | 80 | } | |
| 81 | 81 | ||
| 82 | - | func (_c *MockRegistryService_AppendBlobChunk_Call) Run(run func(ctx context.Context, name string, uuid string, chunk []byte)) *MockRegistryService_AppendBlobChunk_Call { | |
| 82 | + | func (_c *MockRegistryService_AppendBlobChunk_Call) Run(run func(ctx context.Context, name string, uuid string, data io.Reader)) *MockRegistryService_AppendBlobChunk_Call { | |
| 83 | 83 | _c.Call.Run(func(args mock.Arguments) { | |
| 84 | 84 | var arg0 context.Context | |
| 85 | 85 | if args[0] != nil { | |
| @@ -93,9 +93,9 @@ func (_c *MockRegistryService_AppendBlobChunk_Call) Run(run func(ctx context.Con | |||
| 93 | 93 | if args[2] != nil { | |
| 94 | 94 | arg2 = args[2].(string) | |
| 95 | 95 | } | |
| 96 | - | var arg3 []byte | |
| 96 | + | var arg3 io.Reader | |
| 97 | 97 | if args[3] != nil { | |
| 98 | - | arg3 = args[3].([]byte) | |
| 98 | + | arg3 = args[3].(io.Reader) | |
| 99 | 99 | } | |
| 100 | 100 | run( | |
| 101 | 101 | arg0, | |
| @@ -112,7 +112,7 @@ func (_c *MockRegistryService_AppendBlobChunk_Call) Return(n int64, err error) * | |||
| 112 | 112 | return _c | |
| 113 | 113 | } | |
| 114 | 114 | ||
| 115 | - | func (_c *MockRegistryService_AppendBlobChunk_Call) RunAndReturn(run func(ctx context.Context, name string, uuid string, chunk []byte) (int64, error)) *MockRegistryService_AppendBlobChunk_Call { | |
| 115 | + | func (_c *MockRegistryService_AppendBlobChunk_Call) RunAndReturn(run func(ctx context.Context, name string, uuid string, data io.Reader) (int64, error)) *MockRegistryService_AppendBlobChunk_Call { | |
| 116 | 116 | _c.Call.Return(run) | |
| 117 | 117 | return _c | |
| 118 | 118 | } | |
| @@ -40,8 +40,8 @@ func (_m *MockRegistryService) EXPECT() *MockRegistryService_Expecter { | |||||
| 40 | } | 40 | } | ||
| 41 | 41 | ||||
| 42 | // AppendBlobChunk provides a mock function for the type MockRegistryService | 42 | // AppendBlobChunk provides a mock function for the type MockRegistryService | ||
| 43 | - | func (_mock *MockRegistryService) AppendBlobChunk(ctx context.Context, name string, uuid string, chunk []byte) (int64, error) { | 43 | + | func (_mock *MockRegistryService) AppendBlobChunk(ctx context.Context, name string, uuid string, data io.Reader) (int64, error) { |
| 44 | - | ret := _mock.Called(ctx, name, uuid, chunk) | 44 | + | ret := _mock.Called(ctx, name, uuid, data) |
| 45 | 45 | ||||
| 46 | if len(ret) == 0 { | 46 | if len(ret) == 0 { | ||
| 47 | panic("no return value specified for AppendBlobChunk") | 47 | panic("no return value specified for AppendBlobChunk") | ||
| @@ -49,16 +49,16 @@ func (_mock *MockRegistryService) AppendBlobChunk(ctx context.Context, name stri | |||||
| 49 | 49 | ||||
| 50 | var r0 int64 | 50 | var r0 int64 | ||
| 51 | var r1 error | 51 | var r1 error | ||
| 52 | - | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, []byte) (int64, error)); ok { | 52 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, io.Reader) (int64, error)); ok { |
| 53 | - | return returnFunc(ctx, name, uuid, chunk) | 53 | + | return returnFunc(ctx, name, uuid, data) |
| 54 | } | 54 | } | ||
| 55 | - | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, []byte) int64); ok { | 55 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, io.Reader) int64); ok { |
| 56 | - | r0 = returnFunc(ctx, name, uuid, chunk) | 56 | + | r0 = returnFunc(ctx, name, uuid, data) |
| 57 | } else { | 57 | } else { | ||
| 58 | r0 = ret.Get(0).(int64) | 58 | r0 = ret.Get(0).(int64) | ||
| 59 | } | 59 | } | ||
| 60 | - | if returnFunc, ok := ret.Get(1).(func(context.Context, string, string, []byte) error); ok { | 60 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, string, string, io.Reader) error); ok { |
| 61 | - | r1 = returnFunc(ctx, name, uuid, chunk) | 61 | + | r1 = returnFunc(ctx, name, uuid, data) |
| 62 | } else { | 62 | } else { | ||
| 63 | r1 = ret.Error(1) | 63 | r1 = ret.Error(1) | ||
| 64 | } | 64 | } | ||
| @@ -74,12 +74,12 @@ type MockRegistryService_AppendBlobChunk_Call struct { | |||||
| 74 | // - ctx context.Context | 74 | // - ctx context.Context | ||
| 75 | // - name string | 75 | // - name string | ||
| 76 | // - uuid string | 76 | // - uuid string | ||
| 77 | - | // - chunk []byte | 77 | + | // - data io.Reader |
| 78 | - | func (_e *MockRegistryService_Expecter) AppendBlobChunk(ctx interface{}, name interface{}, uuid interface{}, chunk interface{}) *MockRegistryService_AppendBlobChunk_Call { | 78 | + | func (_e *MockRegistryService_Expecter) AppendBlobChunk(ctx interface{}, name interface{}, uuid interface{}, data interface{}) *MockRegistryService_AppendBlobChunk_Call { |
| 79 | - | return &MockRegistryService_AppendBlobChunk_Call{Call: _e.mock.On("AppendBlobChunk", ctx, name, uuid, chunk)} | 79 | + | return &MockRegistryService_AppendBlobChunk_Call{Call: _e.mock.On("AppendBlobChunk", ctx, name, uuid, data)} |
| 80 | } | 80 | } | ||
| 81 | 81 | ||||
| 82 | - | func (_c *MockRegistryService_AppendBlobChunk_Call) Run(run func(ctx context.Context, name string, uuid string, chunk []byte)) *MockRegistryService_AppendBlobChunk_Call { | 82 | + | func (_c *MockRegistryService_AppendBlobChunk_Call) Run(run func(ctx context.Context, name string, uuid string, data io.Reader)) *MockRegistryService_AppendBlobChunk_Call { |
| 83 | _c.Call.Run(func(args mock.Arguments) { | 83 | _c.Call.Run(func(args mock.Arguments) { | ||
| 84 | var arg0 context.Context | 84 | var arg0 context.Context | ||
| 85 | if args[0] != nil { | 85 | if args[0] != nil { | ||
| @@ -93,9 +93,9 @@ func (_c *MockRegistryService_AppendBlobChunk_Call) Run(run func(ctx context.Con | |||||
| 93 | if args[2] != nil { | 93 | if args[2] != nil { | ||
| 94 | arg2 = args[2].(string) | 94 | arg2 = args[2].(string) | ||
| 95 | } | 95 | } | ||
| 96 | - | var arg3 []byte | 96 | + | var arg3 io.Reader |
| 97 | if args[3] != nil { | 97 | if args[3] != nil { | ||
| 98 | - | arg3 = args[3].([]byte) | 98 | + | arg3 = args[3].(io.Reader) |
| 99 | } | 99 | } | ||
| 100 | run( | 100 | run( | ||
| 101 | arg0, | 101 | arg0, | ||
| @@ -112,7 +112,7 @@ func (_c *MockRegistryService_AppendBlobChunk_Call) Return(n int64, err error) * | |||||
| 112 | return _c | 112 | return _c | ||
| 113 | } | 113 | } | ||
| 114 | 114 | ||||
| 115 | - | func (_c *MockRegistryService_AppendBlobChunk_Call) RunAndReturn(run func(ctx context.Context, name string, uuid string, chunk []byte) (int64, error)) *MockRegistryService_AppendBlobChunk_Call { | 115 | + | func (_c *MockRegistryService_AppendBlobChunk_Call) RunAndReturn(run func(ctx context.Context, name string, uuid string, data io.Reader) (int64, error)) *MockRegistryService_AppendBlobChunk_Call { |
| 116 | _c.Call.Return(run) | 116 | _c.Call.Return(run) | ||
| 117 | return _c | 117 | return _c | ||
| 118 | } | 118 | } | ||
M internal/boundaries/in/registry.go +1 -1
| @@ -22,7 +22,7 @@ type RegistryService interface { | |||
| 22 | 22 | ||
| 23 | 23 | // Upload operations | |
| 24 | 24 | StartUpload(ctx context.Context, name string) (string, error) | |
| 25 | - | AppendBlobChunk(ctx context.Context, name, uuid string, chunk []byte) (int64, error) | |
| 25 | + | AppendBlobChunk(ctx context.Context, name, uuid string, data io.Reader) (int64, error) | |
| 26 | 26 | FinishUpload(ctx context.Context, uuid, digest string) error | |
| 27 | 27 | CancelUpload(ctx context.Context, uuid string) error | |
| 28 | 28 | ||
| @@ -22,7 +22,7 @@ type RegistryService interface { | |||||
| 22 | 22 | ||||
| 23 | // Upload operations | 23 | // Upload operations | ||
| 24 | StartUpload(ctx context.Context, name string) (string, error) | 24 | StartUpload(ctx context.Context, name string) (string, error) | ||
| 25 | - | AppendBlobChunk(ctx context.Context, name, uuid string, chunk []byte) (int64, error) | 25 | + | AppendBlobChunk(ctx context.Context, name, uuid string, data io.Reader) (int64, error) |
| 26 | FinishUpload(ctx context.Context, uuid, digest string) error | 26 | FinishUpload(ctx context.Context, uuid, digest string) error | ||
| 27 | CancelUpload(ctx context.Context, uuid string) error | 27 | CancelUpload(ctx context.Context, uuid string) error | ||
| 28 | 28 | ||||
M internal/boundaries/out/mocks/mock_blob_storage.go +15 -15
| @@ -38,8 +38,8 @@ func (_m *MockBlobStorage) EXPECT() *MockBlobStorage_Expecter { | |||
| 38 | 38 | } | |
| 39 | 39 | ||
| 40 | 40 | // AppendBlobChunk provides a mock function for the type MockBlobStorage | |
| 41 | - | func (_mock *MockBlobStorage) AppendBlobChunk(name string, uuid string, chunk []byte) (int64, error) { | |
| 42 | - | ret := _mock.Called(name, uuid, chunk) | |
| 41 | + | func (_mock *MockBlobStorage) AppendBlobChunk(name string, uuid string, data io.Reader) (int64, error) { | |
| 42 | + | ret := _mock.Called(name, uuid, data) | |
| 43 | 43 | ||
| 44 | 44 | if len(ret) == 0 { | |
| 45 | 45 | panic("no return value specified for AppendBlobChunk") | |
| @@ -47,16 +47,16 @@ func (_mock *MockBlobStorage) AppendBlobChunk(name string, uuid string, chunk [] | |||
| 47 | 47 | ||
| 48 | 48 | var r0 int64 | |
| 49 | 49 | var r1 error | |
| 50 | - | if returnFunc, ok := ret.Get(0).(func(string, string, []byte) (int64, error)); ok { | |
| 51 | - | return returnFunc(name, uuid, chunk) | |
| 50 | + | if returnFunc, ok := ret.Get(0).(func(string, string, io.Reader) (int64, error)); ok { | |
| 51 | + | return returnFunc(name, uuid, data) | |
| 52 | 52 | } | |
| 53 | - | if returnFunc, ok := ret.Get(0).(func(string, string, []byte) int64); ok { | |
| 54 | - | r0 = returnFunc(name, uuid, chunk) | |
| 53 | + | if returnFunc, ok := ret.Get(0).(func(string, string, io.Reader) int64); ok { | |
| 54 | + | r0 = returnFunc(name, uuid, data) | |
| 55 | 55 | } else { | |
| 56 | 56 | r0 = ret.Get(0).(int64) | |
| 57 | 57 | } | |
| 58 | - | if returnFunc, ok := ret.Get(1).(func(string, string, []byte) error); ok { | |
| 59 | - | r1 = returnFunc(name, uuid, chunk) | |
| 58 | + | if returnFunc, ok := ret.Get(1).(func(string, string, io.Reader) error); ok { | |
| 59 | + | r1 = returnFunc(name, uuid, data) | |
| 60 | 60 | } else { | |
| 61 | 61 | r1 = ret.Error(1) | |
| 62 | 62 | } | |
| @@ -71,12 +71,12 @@ type MockBlobStorage_AppendBlobChunk_Call struct { | |||
| 71 | 71 | // AppendBlobChunk is a helper method to define mock.On call | |
| 72 | 72 | // - name string | |
| 73 | 73 | // - uuid string | |
| 74 | - | // - chunk []byte | |
| 75 | - | func (_e *MockBlobStorage_Expecter) AppendBlobChunk(name interface{}, uuid interface{}, chunk interface{}) *MockBlobStorage_AppendBlobChunk_Call { | |
| 76 | - | return &MockBlobStorage_AppendBlobChunk_Call{Call: _e.mock.On("AppendBlobChunk", name, uuid, chunk)} | |
| 74 | + | // - data io.Reader | |
| 75 | + | func (_e *MockBlobStorage_Expecter) AppendBlobChunk(name interface{}, uuid interface{}, data interface{}) *MockBlobStorage_AppendBlobChunk_Call { | |
| 76 | + | return &MockBlobStorage_AppendBlobChunk_Call{Call: _e.mock.On("AppendBlobChunk", name, uuid, data)} | |
| 77 | 77 | } | |
| 78 | 78 | ||
| 79 | - | func (_c *MockBlobStorage_AppendBlobChunk_Call) Run(run func(name string, uuid string, chunk []byte)) *MockBlobStorage_AppendBlobChunk_Call { | |
| 79 | + | func (_c *MockBlobStorage_AppendBlobChunk_Call) Run(run func(name string, uuid string, data io.Reader)) *MockBlobStorage_AppendBlobChunk_Call { | |
| 80 | 80 | _c.Call.Run(func(args mock.Arguments) { | |
| 81 | 81 | var arg0 string | |
| 82 | 82 | if args[0] != nil { | |
| @@ -86,9 +86,9 @@ func (_c *MockBlobStorage_AppendBlobChunk_Call) Run(run func(name string, uuid s | |||
| 86 | 86 | if args[1] != nil { | |
| 87 | 87 | arg1 = args[1].(string) | |
| 88 | 88 | } | |
| 89 | - | var arg2 []byte | |
| 89 | + | var arg2 io.Reader | |
| 90 | 90 | if args[2] != nil { | |
| 91 | - | arg2 = args[2].([]byte) | |
| 91 | + | arg2 = args[2].(io.Reader) | |
| 92 | 92 | } | |
| 93 | 93 | run( | |
| 94 | 94 | arg0, | |
| @@ -104,7 +104,7 @@ func (_c *MockBlobStorage_AppendBlobChunk_Call) Return(n int64, err error) *Mock | |||
| 104 | 104 | return _c | |
| 105 | 105 | } | |
| 106 | 106 | ||
| 107 | - | func (_c *MockBlobStorage_AppendBlobChunk_Call) RunAndReturn(run func(name string, uuid string, chunk []byte) (int64, error)) *MockBlobStorage_AppendBlobChunk_Call { | |
| 107 | + | func (_c *MockBlobStorage_AppendBlobChunk_Call) RunAndReturn(run func(name string, uuid string, data io.Reader) (int64, error)) *MockBlobStorage_AppendBlobChunk_Call { | |
| 108 | 108 | _c.Call.Return(run) | |
| 109 | 109 | return _c | |
| 110 | 110 | } | |
| @@ -38,8 +38,8 @@ func (_m *MockBlobStorage) EXPECT() *MockBlobStorage_Expecter { | |||||
| 38 | } | 38 | } | ||
| 39 | 39 | ||||
| 40 | // AppendBlobChunk provides a mock function for the type MockBlobStorage | 40 | // AppendBlobChunk provides a mock function for the type MockBlobStorage | ||
| 41 | - | func (_mock *MockBlobStorage) AppendBlobChunk(name string, uuid string, chunk []byte) (int64, error) { | 41 | + | func (_mock *MockBlobStorage) AppendBlobChunk(name string, uuid string, data io.Reader) (int64, error) { |
| 42 | - | ret := _mock.Called(name, uuid, chunk) | 42 | + | ret := _mock.Called(name, uuid, data) |
| 43 | 43 | ||||
| 44 | if len(ret) == 0 { | 44 | if len(ret) == 0 { | ||
| 45 | panic("no return value specified for AppendBlobChunk") | 45 | panic("no return value specified for AppendBlobChunk") | ||
| @@ -47,16 +47,16 @@ func (_mock *MockBlobStorage) AppendBlobChunk(name string, uuid string, chunk [] | |||||
| 47 | 47 | ||||
| 48 | var r0 int64 | 48 | var r0 int64 | ||
| 49 | var r1 error | 49 | var r1 error | ||
| 50 | - | if returnFunc, ok := ret.Get(0).(func(string, string, []byte) (int64, error)); ok { | 50 | + | if returnFunc, ok := ret.Get(0).(func(string, string, io.Reader) (int64, error)); ok { |
| 51 | - | return returnFunc(name, uuid, chunk) | 51 | + | return returnFunc(name, uuid, data) |
| 52 | } | 52 | } | ||
| 53 | - | if returnFunc, ok := ret.Get(0).(func(string, string, []byte) int64); ok { | 53 | + | if returnFunc, ok := ret.Get(0).(func(string, string, io.Reader) int64); ok { |
| 54 | - | r0 = returnFunc(name, uuid, chunk) | 54 | + | r0 = returnFunc(name, uuid, data) |
| 55 | } else { | 55 | } else { | ||
| 56 | r0 = ret.Get(0).(int64) | 56 | r0 = ret.Get(0).(int64) | ||
| 57 | } | 57 | } | ||
| 58 | - | if returnFunc, ok := ret.Get(1).(func(string, string, []byte) error); ok { | 58 | + | if returnFunc, ok := ret.Get(1).(func(string, string, io.Reader) error); ok { |
| 59 | - | r1 = returnFunc(name, uuid, chunk) | 59 | + | r1 = returnFunc(name, uuid, data) |
| 60 | } else { | 60 | } else { | ||
| 61 | r1 = ret.Error(1) | 61 | r1 = ret.Error(1) | ||
| 62 | } | 62 | } | ||
| @@ -71,12 +71,12 @@ type MockBlobStorage_AppendBlobChunk_Call struct { | |||||
| 71 | // AppendBlobChunk is a helper method to define mock.On call | 71 | // AppendBlobChunk is a helper method to define mock.On call | ||
| 72 | // - name string | 72 | // - name string | ||
| 73 | // - uuid string | 73 | // - uuid string | ||
| 74 | - | // - chunk []byte | 74 | + | // - data io.Reader |
| 75 | - | func (_e *MockBlobStorage_Expecter) AppendBlobChunk(name interface{}, uuid interface{}, chunk interface{}) *MockBlobStorage_AppendBlobChunk_Call { | 75 | + | func (_e *MockBlobStorage_Expecter) AppendBlobChunk(name interface{}, uuid interface{}, data interface{}) *MockBlobStorage_AppendBlobChunk_Call { |
| 76 | - | return &MockBlobStorage_AppendBlobChunk_Call{Call: _e.mock.On("AppendBlobChunk", name, uuid, chunk)} | 76 | + | return &MockBlobStorage_AppendBlobChunk_Call{Call: _e.mock.On("AppendBlobChunk", name, uuid, data)} |
| 77 | } | 77 | } | ||
| 78 | 78 | ||||
| 79 | - | func (_c *MockBlobStorage_AppendBlobChunk_Call) Run(run func(name string, uuid string, chunk []byte)) *MockBlobStorage_AppendBlobChunk_Call { | 79 | + | func (_c *MockBlobStorage_AppendBlobChunk_Call) Run(run func(name string, uuid string, data io.Reader)) *MockBlobStorage_AppendBlobChunk_Call { |
| 80 | _c.Call.Run(func(args mock.Arguments) { | 80 | _c.Call.Run(func(args mock.Arguments) { | ||
| 81 | var arg0 string | 81 | var arg0 string | ||
| 82 | if args[0] != nil { | 82 | if args[0] != nil { | ||
| @@ -86,9 +86,9 @@ func (_c *MockBlobStorage_AppendBlobChunk_Call) Run(run func(name string, uuid s | |||||
| 86 | if args[1] != nil { | 86 | if args[1] != nil { | ||
| 87 | arg1 = args[1].(string) | 87 | arg1 = args[1].(string) | ||
| 88 | } | 88 | } | ||
| 89 | - | var arg2 []byte | 89 | + | var arg2 io.Reader |
| 90 | if args[2] != nil { | 90 | if args[2] != nil { | ||
| 91 | - | arg2 = args[2].([]byte) | 91 | + | arg2 = args[2].(io.Reader) |
| 92 | } | 92 | } | ||
| 93 | run( | 93 | run( | ||
| 94 | arg0, | 94 | arg0, | ||
| @@ -104,7 +104,7 @@ func (_c *MockBlobStorage_AppendBlobChunk_Call) Return(n int64, err error) *Mock | |||||
| 104 | return _c | 104 | return _c | ||
| 105 | } | 105 | } | ||
| 106 | 106 | ||||
| 107 | - | func (_c *MockBlobStorage_AppendBlobChunk_Call) RunAndReturn(run func(name string, uuid string, chunk []byte) (int64, error)) *MockBlobStorage_AppendBlobChunk_Call { | 107 | + | func (_c *MockBlobStorage_AppendBlobChunk_Call) RunAndReturn(run func(name string, uuid string, data io.Reader) (int64, error)) *MockBlobStorage_AppendBlobChunk_Call { |
| 108 | _c.Call.Return(run) | 108 | _c.Call.Return(run) | ||
| 109 | return _c | 109 | return _c | ||
| 110 | } | 110 | } | ||
M internal/boundaries/out/storage.go +1 -1
| @@ -25,7 +25,7 @@ type BlobStorage interface { | |||
| 25 | 25 | StartBlobUpload(name string) (string, error) | |
| 26 | 26 | ||
| 27 | 27 | // AppendBlobChunk appends data to an in-progress upload. | |
| 28 | - | AppendBlobChunk(name, uuid string, chunk []byte) (int64, error) | |
| 28 | + | AppendBlobChunk(name, uuid string, data io.Reader) (int64, error) | |
| 29 | 29 | ||
| 30 | 30 | // GetBlobUpload returns a writer for the upload. | |
| 31 | 31 | GetBlobUpload(uuid string) (io.WriteCloser, error) | |
| @@ -25,7 +25,7 @@ type BlobStorage interface { | |||||
| 25 | StartBlobUpload(name string) (string, error) | 25 | StartBlobUpload(name string) (string, error) | ||
| 26 | 26 | ||||
| 27 | // AppendBlobChunk appends data to an in-progress upload. | 27 | // AppendBlobChunk appends data to an in-progress upload. | ||
| 28 | - | AppendBlobChunk(name, uuid string, chunk []byte) (int64, error) | 28 | + | AppendBlobChunk(name, uuid string, data io.Reader) (int64, error) |
| 29 | 29 | ||||
| 30 | // GetBlobUpload returns a writer for the upload. | 30 | // GetBlobUpload returns a writer for the upload. | ||
| 31 | GetBlobUpload(uuid string) (io.WriteCloser, error) | 31 | GetBlobUpload(uuid string) (io.WriteCloser, error) | ||
M internal/usecase/registry/service.go +2 -3
| @@ -184,17 +184,16 @@ func (s *Service) StartUpload(ctx context.Context, name string) (string, error) | |||
| 184 | 184 | } | |
| 185 | 185 | ||
| 186 | 186 | // AppendBlobChunk appends data to an in-progress blob upload. | |
| 187 | - | func (s *Service) AppendBlobChunk(ctx context.Context, name, uuid string, chunk []byte) (int64, error) { | |
| 187 | + | func (s *Service) AppendBlobChunk(ctx context.Context, name, uuid string, data io.Reader) (int64, error) { | |
| 188 | 188 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | |
| 189 | 189 | zerowrap.FieldLayer: "usecase", | |
| 190 | 190 | zerowrap.FieldUseCase: "AppendBlobChunk", | |
| 191 | 191 | "name": name, | |
| 192 | 192 | "uuid": uuid, | |
| 193 | - | zerowrap.FieldSize: len(chunk), | |
| 194 | 193 | }) | |
| 195 | 194 | log := zerowrap.FromCtx(ctx) | |
| 196 | 195 | ||
| 197 | - | length, err := s.blobStorage.AppendBlobChunk(name, uuid, chunk) | |
| 196 | + | length, err := s.blobStorage.AppendBlobChunk(name, uuid, data) | |
| 198 | 197 | if err != nil { | |
| 199 | 198 | return 0, log.WrapErr(err, "failed to append blob chunk") | |
| 200 | 199 | } | |
| @@ -184,17 +184,16 @@ func (s *Service) StartUpload(ctx context.Context, name string) (string, error) | |||||
| 184 | } | 184 | } | ||
| 185 | 185 | ||||
| 186 | // AppendBlobChunk appends data to an in-progress blob upload. | 186 | // AppendBlobChunk appends data to an in-progress blob upload. | ||
| 187 | - | func (s *Service) AppendBlobChunk(ctx context.Context, name, uuid string, chunk []byte) (int64, error) { | 187 | + | func (s *Service) AppendBlobChunk(ctx context.Context, name, uuid string, data io.Reader) (int64, error) { |
| 188 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | 188 | ctx = zerowrap.CtxWithFields(ctx, map[string]any{ | ||
| 189 | zerowrap.FieldLayer: "usecase", | 189 | zerowrap.FieldLayer: "usecase", | ||
| 190 | zerowrap.FieldUseCase: "AppendBlobChunk", | 190 | zerowrap.FieldUseCase: "AppendBlobChunk", | ||
| 191 | "name": name, | 191 | "name": name, | ||
| 192 | "uuid": uuid, | 192 | "uuid": uuid, | ||
| 193 | - | zerowrap.FieldSize: len(chunk), | |||
| 194 | }) | 193 | }) | ||
| 195 | log := zerowrap.FromCtx(ctx) | 194 | log := zerowrap.FromCtx(ctx) | ||
| 196 | 195 | ||||
| 197 | - | length, err := s.blobStorage.AppendBlobChunk(name, uuid, chunk) | 196 | + | length, err := s.blobStorage.AppendBlobChunk(name, uuid, data) |
| 198 | if err != nil { | 197 | if err != nil { | ||
| 199 | return 0, log.WrapErr(err, "failed to append blob chunk") | 198 | return 0, log.WrapErr(err, "failed to append blob chunk") | ||
| 200 | } | 199 | } | ||