clean up logging, consolidate presigned handlers

This commit is contained in:
Evan Jarrett
2025-10-11 22:32:13 -05:00
parent a9e2a56568
commit ace980cff6
7 changed files with 136 additions and 347 deletions
+3 -3
View File
@@ -35,9 +35,9 @@ func main() {
mux := http.NewServeMux()
mux.HandleFunc("/health", service.HealthHandler)
mux.HandleFunc("/register", service.HandleRegister)
mux.HandleFunc("/get-presigned-url", service.HandleGetPresignedURL)
mux.HandleFunc("/head-presigned-url", service.HandleHeadPresignedURL)
mux.HandleFunc("/put-presigned-url", service.HandlePutPresignedURL)
mux.HandleFunc("/get-presigned-url", service.HandlePresignedURL(hold.OperationGet))
mux.HandleFunc("/head-presigned-url", service.HandlePresignedURL(hold.OperationHead))
mux.HandleFunc("/put-presigned-url", service.HandlePresignedURL(hold.OperationPut))
mux.HandleFunc("/move", service.HandleMove)
// Multipart upload endpoints
+3 -3
View File
@@ -297,7 +297,7 @@ func (w *ProxyBlobWriter) Cancel(ctx context.Context) error {
// Clear buffer to free memory
w.buffer = nil
fmt.Printf("[ProxyBlobWriter.Cancel] Upload cancelled: id=%s\n", w.id)
fmt.Printf("[ProxyBlobWriter.Cancel] Upload cancelled: id=%s\n", w.id)
return nil
}
```
@@ -318,7 +318,7 @@ The current `getUploadURL()` implementation in `cmd/hold/main.go` (lines 528-587
```go
url, err := req.Presign(15 * time.Minute)
if err != nil {
log.Printf("Failed to generate presigned upload URL: %v", err)
log.Printf("Failed to generate presigned upload URL: %v", err)
return s.getProxyUploadURL(digest, did), nil
}
@@ -442,7 +442,7 @@ func (s *HoldService) getHeadURL(ctx context.Context, digest string) (string, er
url, err := req.Presign(15 * time.Minute)
if err != nil {
log.Printf("[getHeadURL] Presign failed: %v", err)
log.Printf("[getHeadURL] Presign failed: %v", err)
// Fallback to proxy URL
return s.getProxyHeadURL(digest), nil
}
+54 -160
View File
@@ -12,157 +12,64 @@ import (
"atcr.io/pkg/atproto"
)
// HandleGetPresignedURL handles requests for download URLs
func (s *HoldService) HandleGetPresignedURL(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var req GetPresignedURLRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest)
return
}
log.Printf("📨 [HandleGetPresignedURL] Request received:")
log.Printf(" DID: %s", req.DID)
log.Printf(" Digest: %s", req.Digest)
log.Printf(" Remote: %s", r.RemoteAddr)
log.Printf(" s3Client nil? %v", s.s3Client == nil)
// Validate DID authorization for READ
if !s.isAuthorizedRead(req.DID) {
log.Printf("❌ [HandleGetPresignedURL] Authorization FAILED")
if req.DID == "" {
// Anonymous request to private hold
http.Error(w, "unauthorized: authentication required", http.StatusUnauthorized)
} else {
// Authenticated but not authorized
http.Error(w, "forbidden: access denied", http.StatusForbidden)
// HandlePresignedURL returns an HTTP handler for presigned URL requests (GET, HEAD, or PUT)
// This consolidates the three separate handlers into a single parameterized implementation
func (s *HoldService) HandlePresignedURL(operation PresignedURLOperation) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
return
}
// Generate presigned URL (15 minute expiry)
ctx := context.Background()
expiry := time.Now().Add(15 * time.Minute)
// For now, construct direct URL to blob
// In production, this would use driver-specific presigned URLs
url, err := s.getDownloadURL(ctx, req.Digest, req.DID)
if err != nil {
log.Printf("❌ [HandleGetPresignedURL] getDownloadURL failed: %v", err)
http.Error(w, fmt.Sprintf("failed to generate URL: %v", err), http.StatusInternalServerError)
return
}
log.Printf("✅ [HandleGetPresignedURL] Returning URL to client")
resp := GetPresignedURLResponse{
URL: url,
ExpiresAt: expiry,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
// HandleHeadPresignedURL handles requests for HEAD URLs
func (s *HoldService) HandleHeadPresignedURL(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var req HeadPresignedURLRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest)
return
}
log.Printf("📨 [HandleHeadPresignedURL] Request received:")
log.Printf(" DID: %s", req.DID)
log.Printf(" Digest: %s", req.Digest)
log.Printf(" Remote: %s", r.RemoteAddr)
// Validate DID authorization for READ
if !s.isAuthorizedRead(req.DID) {
log.Printf("❌ [HandleHeadPresignedURL] Authorization FAILED")
if req.DID == "" {
// Anonymous request to private hold
http.Error(w, "unauthorized: authentication required", http.StatusUnauthorized)
} else {
// Authenticated but not authorized
http.Error(w, "forbidden: access denied", http.StatusForbidden)
var req PresignedURLRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest)
return
}
return
}
// Generate presigned HEAD URL (15 minute expiry)
ctx := context.Background()
expiry := time.Now().Add(15 * time.Minute)
url, err := s.getHeadURL(ctx, req.Digest, req.DID)
if err != nil {
log.Printf("❌ [HandleHeadPresignedURL] getHeadURL failed: %v", err)
http.Error(w, fmt.Sprintf("failed to generate URL: %v", err), http.StatusInternalServerError)
return
}
log.Printf("✅ [HandleHeadPresignedURL] Returning URL to client")
resp := HeadPresignedURLResponse{
URL: url,
ExpiresAt: expiry,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
// HandlePutPresignedURL handles requests for upload URLs
func (s *HoldService) HandlePutPresignedURL(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var req PutPresignedURLRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest)
return
}
// Validate DID authorization for WRITE
if !s.isAuthorizedWrite(req.DID) {
if req.DID == "" {
// Anonymous write attempt
http.Error(w, "unauthorized: authentication required", http.StatusUnauthorized)
} else {
// Authenticated but not crew/owner
http.Error(w, "forbidden: write access denied", http.StatusForbidden)
// Validate DID authorization based on operation type
var authorized bool
switch operation {
case OperationGet, OperationHead:
authorized = s.isAuthorizedRead(req.DID)
case OperationPut:
authorized = s.isAuthorizedWrite(req.DID)
default:
http.Error(w, "unsupported operation", http.StatusBadRequest)
return
}
return
if !authorized {
log.Printf("[HandlePresignedURL:%s] Authorization FAILED", operation)
if req.DID == "" {
http.Error(w, "unauthorized: authentication required", http.StatusUnauthorized)
} else {
http.Error(w, "forbidden: access denied", http.StatusForbidden)
}
return
}
// Generate presigned URL (15 minute expiry)
ctx := context.Background()
expiry := time.Now().Add(15 * time.Minute)
url, err := s.getPresignedURL(ctx, operation, req.Digest, req.DID)
if err != nil {
log.Printf("[HandlePresignedURL:%s] getPresignedURL failed: %v", operation, err)
http.Error(w, fmt.Sprintf("failed to generate URL: %v", err), http.StatusInternalServerError)
return
}
log.Printf("[HandlePresignedURL:%s] Returning URL to client", operation)
resp := PresignedURLResponse{
URL: url,
ExpiresAt: expiry,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
// Generate presigned upload URL (15 minute expiry)
ctx := context.Background()
expiry := time.Now().Add(15 * time.Minute)
url, err := s.getUploadURL(ctx, req.Digest, req.Size, req.DID)
if err != nil {
http.Error(w, fmt.Sprintf("failed to generate URL: %v", err), http.StatusInternalServerError)
return
}
resp := PutPresignedURLResponse{
URL: url,
ExpiresAt: expiry,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
// HandleProxyGet proxies a blob download through the service
@@ -179,11 +86,6 @@ func (s *HoldService) HandleProxyGet(w http.ResponseWriter, r *http.Request) {
return
}
log.Printf("📥 [HandleProxyGet] Blob download request:")
log.Printf(" Method: %s", r.Method)
log.Printf(" Digest: %s", digest)
log.Printf(" Remote: %s", r.RemoteAddr)
// Get DID from query param or header
did := r.URL.Query().Get("did")
if did == "" {
@@ -193,7 +95,7 @@ func (s *HoldService) HandleProxyGet(w http.ResponseWriter, r *http.Request) {
// Authorize READ access
if !s.isAuthorizedRead(did) {
log.Printf("[HandleProxyGet] Authorization FAILED")
log.Printf("[HandleProxyGet] Authorization FAILED")
if did == "" {
http.Error(w, "unauthorized: authentication required", http.StatusUnauthorized)
} else {
@@ -201,7 +103,6 @@ func (s *HoldService) HandleProxyGet(w http.ResponseWriter, r *http.Request) {
}
return
}
log.Printf("✅ [HandleProxyGet] Authorization SUCCESS")
ctx := r.Context()
path := blobPath(digest)
@@ -290,14 +191,9 @@ func (s *HoldService) HandleProxyPut(w http.ResponseWriter, r *http.Request) {
did = r.Header.Get("X-ATCR-DID")
}
log.Printf("🔐 [HandleProxyPut] Authorization check:")
log.Printf(" Path: %s", digest)
log.Printf(" DID: %s", did)
log.Printf(" Owner DID: %s", s.config.Registration.OwnerDID)
// Authorize WRITE access
if !s.isAuthorizedWrite(did) {
log.Printf("[HandleProxyPut] Authorization FAILED")
log.Printf("[HandleProxyPut] Authorization FAILED")
if did == "" {
http.Error(w, "unauthorized: authentication required", http.StatusUnauthorized)
} else {
@@ -306,8 +202,6 @@ func (s *HoldService) HandleProxyPut(w http.ResponseWriter, r *http.Request) {
return
}
log.Printf("✅ [HandleProxyPut] Authorization SUCCESS")
// Stream blob to storage (no buffering)
ctx := r.Context()
path := blobPath(digest)
+6
View File
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log"
"sort"
"strings"
"time"
@@ -158,6 +159,11 @@ func (s *HoldService) completeMultipartUpload(ctx context.Context, digest, uploa
s3Key = s.s3PathPrefix + "/" + s3Key
}
// Sort parts by part number (S3 requires ascending order)
sort.Slice(parts, func(i, j int) bool {
return parts[i].PartNumber < parts[j].PartNumber
})
// Convert to S3 CompletedPart format
// IMPORTANT: S3 requires ETags to be quoted in the CompleteMultipartUpload XML
s3Parts := make([]*s3.CompletedPart, len(parts))
+46 -124
View File
@@ -39,79 +39,24 @@ func blobPath(digest string) string {
return fmt.Sprintf("/docker/registry/v2/blobs/%s/%s/%s/data", algorithm, hash[:2], hash)
}
// getDownloadURL generates a download URL for a blob
func (s *HoldService) getDownloadURL(ctx context.Context, digest string, did string) (string, error) {
// Check if blob exists
// getPresignedURL generates a presigned URL for GET, HEAD, or PUT operations
func (s *HoldService) getPresignedURL(ctx context.Context, operation PresignedURLOperation, digest string, did string) (string, error) {
path := blobPath(digest)
_, err := s.driver.Stat(ctx, path)
if err != nil {
return "", fmt.Errorf("blob not found: %w", err)
// Check blob exists for GET/HEAD operations (not for PUT since blob doesn't exist yet)
if operation == OperationGet || operation == OperationHead {
if _, err := s.driver.Stat(ctx, path); err != nil {
return "", fmt.Errorf("blob not found: %w", err)
}
}
// Check if presigned URLs are disabled for testing
// Check if presigned URLs are disabled
if s.config.Server.DisablePresignedURLs {
log.Printf("Presigned URLs disabled, using proxy URL")
return s.getProxyDownloadURL(digest, did), nil
return s.getProxyURL(digest, did), nil
}
// If S3 client available, generate presigned URL
if s.s3Client != nil {
// Build S3 key from blob path
// blobPath returns paths like: /docker/registry/v2/blobs/sha256/ab/abc123.../data
s3Key := strings.TrimPrefix(path, "/")
if s.s3PathPrefix != "" {
s3Key = s.s3PathPrefix + "/" + s3Key
}
// Generate presigned GET URL
// Note: Don't use ResponseContentType - not supported by all S3-compatible services
req, _ := s.s3Client.GetObjectRequest(&s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(s3Key),
})
log.Printf("🔍 [getDownloadURL] Before Presign:")
log.Printf(" Digest: %s", digest)
log.Printf(" S3 Key: %s", s3Key)
log.Printf(" Bucket: %s", s.bucket)
log.Printf(" Request Operation: %s", req.Operation.Name)
log.Printf(" Request HTTPMethod: %s", req.Operation.HTTPMethod)
url, err := req.Presign(15 * time.Minute)
if err != nil {
log.Printf("❌ [getDownloadURL] Presign FAILED: %v", err)
log.Printf(" Falling back to proxy URL")
return s.getProxyDownloadURL(digest, did), nil
}
log.Printf("✅ [getDownloadURL] Presigned URL generated successfully")
log.Printf(" URL: %s", url)
log.Printf(" URL Length: %d chars", len(url))
log.Printf(" Expires: 15min")
return url, nil
}
// Fallback: return proxy URL through this service
return s.getProxyDownloadURL(digest, did), nil
}
// getHeadURL generates a HEAD URL for a blob
func (s *HoldService) getHeadURL(ctx context.Context, digest string, did string) (string, error) {
// Check if blob exists
path := blobPath(digest)
_, err := s.driver.Stat(ctx, path)
if err != nil {
return "", fmt.Errorf("blob not found: %w", err)
}
// Check if presigned URLs are disabled for testing
if s.config.Server.DisablePresignedURLs {
log.Printf("Presigned URLs disabled, using proxy URL")
return s.getProxyDownloadURL(digest, did), nil
}
// If S3 client available, generate presigned HEAD URL
// Generate presigned URL if S3 client is available
if s.s3Client != nil {
// Build S3 key from blob path
s3Key := strings.TrimPrefix(path, "/")
@@ -119,75 +64,52 @@ func (s *HoldService) getHeadURL(ctx context.Context, digest string, did string)
s3Key = s.s3PathPrefix + "/" + s3Key
}
// Generate presigned HEAD URL
req, _ := s.s3Client.HeadObjectRequest(&s3.HeadObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(s3Key),
})
// Create appropriate S3 request based on operation
var req interface {
Presign(time.Duration) (string, error)
}
switch operation {
case OperationGet:
// Note: Don't use ResponseContentType - not supported by all S3-compatible services
req, _ = s.s3Client.GetObjectRequest(&s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(s3Key),
})
case OperationHead:
req, _ = s.s3Client.HeadObjectRequest(&s3.HeadObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(s3Key),
})
case OperationPut:
req, _ = s.s3Client.PutObjectRequest(&s3.PutObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(s3Key),
ContentType: aws.String("application/octet-stream"),
})
default:
return "", fmt.Errorf("unsupported operation: %s", operation)
}
// Generate presigned URL with 15 minute expiry
url, err := req.Presign(15 * time.Minute)
if err != nil {
log.Printf("[getHeadURL] Presign FAILED: %v", err)
log.Printf("[getPresignedURL] Presign FAILED for %s: %v", operation, err)
log.Printf(" Falling back to proxy URL")
return s.getProxyDownloadURL(digest, did), nil
return s.getProxyURL(digest, did), nil
}
log.Printf("✅ [getHeadURL] Presigned HEAD URL generated: digest=%s", digest)
return url, nil
}
// Fallback: return proxy URL through this service
return s.getProxyDownloadURL(digest, did), nil
return s.getProxyURL(digest, did), nil
}
// getProxyDownloadURL returns a proxy URL for blob download (fallback when presigned URLs unavailable)
func (s *HoldService) getProxyDownloadURL(digest, did string) string {
return fmt.Sprintf("%s/blobs/%s?did=%s", s.config.Server.PublicURL, digest, did)
}
// getUploadURL generates an upload URL for a blob
// Note: This is called from HandlePutPresignedURL which has the DID in the request
func (s *HoldService) getUploadURL(ctx context.Context, digest string, size int64, did string) (string, error) {
// Check if presigned URLs are disabled for testing
if s.config.Server.DisablePresignedURLs {
log.Printf("Presigned URLs disabled, using proxy URL")
return s.getProxyUploadURL(digest, did), nil
}
// If S3 client available, generate presigned URL
if s.s3Client != nil {
// Build S3 key from blob path
path := blobPath(digest)
s3Key := strings.TrimPrefix(path, "/")
if s.s3PathPrefix != "" {
s3Key = s.s3PathPrefix + "/" + s3Key
}
// Generate presigned PUT URL with Content-Type in signature
req, _ := s.s3Client.PutObjectRequest(&s3.PutObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(s3Key),
ContentType: aws.String("application/octet-stream"),
})
url, err := req.Presign(15 * time.Minute)
if err != nil {
log.Printf("WARN: Presigned URL generation failed for %s, falling back to proxy: %v", digest, err)
return s.getProxyUploadURL(digest, did), nil
}
log.Printf("🔑 Generated presigned upload URL for %s (expires in 15min)", digest)
log.Printf(" S3 Key: %s", s3Key)
log.Printf(" Bucket: %s", s.bucket)
log.Printf(" Size: %d bytes", size)
return url, nil
}
// Fallback: return proxy URL through this service
return s.getProxyUploadURL(digest, did), nil
}
// getProxyUploadURL returns a proxy URL for blob upload (fallback when presigned URLs unavailable)
func (s *HoldService) getProxyUploadURL(digest, did string) string {
// getProxyURL returns a proxy URL for blob operations (fallback when presigned URLs unavailable)
func (s *HoldService) getProxyURL(digest, did string) string {
// All operations use the same proxy endpoint
return fmt.Sprintf("%s/blobs/%s?did=%s", s.config.Server.PublicURL, digest, did)
}
+14 -29
View File
@@ -4,39 +4,24 @@ import (
"time"
)
// GetPresignedURLRequest represents a request for a presigned download URL
type GetPresignedURLRequest struct {
// PresignedURLOperation defines the type of presigned URL operation
type PresignedURLOperation string
const (
OperationGet PresignedURLOperation = "GET"
OperationHead PresignedURLOperation = "HEAD"
OperationPut PresignedURLOperation = "PUT"
)
// PresignedURLRequest represents a request for a presigned URL (GET, HEAD, or PUT)
type PresignedURLRequest struct {
DID string `json:"did"`
Digest string `json:"digest"`
Size int64 `json:"size,omitempty"` // Only required for PUT operations
}
// GetPresignedURLResponse contains the presigned URL
type GetPresignedURLResponse struct {
URL string `json:"url"`
ExpiresAt time.Time `json:"expires_at"`
}
// HeadPresignedURLRequest represents a request for a presigned HEAD URL
type HeadPresignedURLRequest struct {
DID string `json:"did"`
Digest string `json:"digest"`
}
// HeadPresignedURLResponse contains the presigned HEAD URL
type HeadPresignedURLResponse struct {
URL string `json:"url"`
ExpiresAt time.Time `json:"expires_at"`
}
// PutPresignedURLRequest represents a request for a presigned upload URL
type PutPresignedURLRequest struct {
DID string `json:"did"`
Digest string `json:"digest"`
Size int64 `json:"size"`
}
// PutPresignedURLResponse contains the presigned upload URL
type PutPresignedURLResponse struct {
// PresignedURLResponse contains the presigned URL
type PresignedURLResponse struct {
URL string `json:"url"`
ExpiresAt time.Time `json:"expires_at"`
}
+10 -28
View File
@@ -147,42 +147,32 @@ func (p *ProxyBlobStore) Put(ctx context.Context, mediaType string, content []by
// Get upload URL
url, err := p.getUploadURL(ctx, dgst, int64(len(content)))
if err != nil {
fmt.Printf("[proxy_blob_store/Put] Failed to get upload URL: digest=%s, error=%v\n", dgst, err)
fmt.Printf("[proxy_blob_store/Put] Failed to get upload URL: digest=%s, error=%v\n", dgst, err)
return distribution.Descriptor{}, err
}
fmt.Printf("📤 [proxy_blob_store/Put] Starting PUT request:\n")
fmt.Printf(" Digest: %s\n", dgst)
fmt.Printf(" Size: %d bytes\n", len(content))
fmt.Printf(" MediaType: %s\n", mediaType)
fmt.Printf(" URL: %s\n", url)
fmt.Printf(" Headers: Content-Type=application/octet-stream\n")
// Upload the blob
req, err := http.NewRequestWithContext(ctx, "PUT", url, bytes.NewReader(content))
if err != nil {
fmt.Printf("[proxy_blob_store/Put] Failed to create request: %v\n", err)
fmt.Printf("[proxy_blob_store/Put] Failed to create request: %v\n", err)
return distribution.Descriptor{}, err
}
req.Header.Set("Content-Type", "application/octet-stream")
resp, err := p.httpClient.Do(req)
if err != nil {
fmt.Printf("[proxy_blob_store/Put] HTTP request failed: %v\n", err)
fmt.Printf("[proxy_blob_store/Put] HTTP request failed: %v\n", err)
return distribution.Descriptor{}, err
}
defer resp.Body.Close()
fmt.Printf("📥 [proxy_blob_store/Put] Response:\n")
fmt.Printf(" Status: %d %s\n", resp.StatusCode, resp.Status)
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
bodyBytes, _ := io.ReadAll(resp.Body)
fmt.Printf(" Error Body: %s\n", string(bodyBytes))
return distribution.Descriptor{}, fmt.Errorf("upload failed with status %d: %s", resp.StatusCode, string(bodyBytes))
}
fmt.Printf("[proxy_blob_store/Put] Upload successful: digest=%s, size=%d\n", dgst, len(content))
fmt.Printf("[proxy_blob_store/Put] Upload successful: digest=%s, size=%d\n", dgst, len(content))
return distribution.Descriptor{
Digest: dgst,
@@ -224,10 +214,6 @@ func (p *ProxyBlobStore) ServeBlob(ctx context.Context, w http.ResponseWriter, r
// Create returns a blob writer for uploading using multipart upload
func (p *ProxyBlobStore) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) {
fmt.Printf("🔧 [proxy_blob_store/Create] Starting multipart upload\n")
fmt.Printf(" Storage endpoint: %s\n", p.storageEndpoint)
fmt.Printf(" Repository: %s\n", p.repository)
// Parse options
var opts distribution.CreateOptions
for _, option := range options {
@@ -624,8 +610,6 @@ func (w *ProxyBlobWriter) flushPart() error {
return fmt.Errorf("failed to get part presigned URL: %w", err)
}
fmt.Printf("📤 [flushPart] Uploading part %d: size=%d bytes\n", w.partNumber, w.buffer.Len())
// Upload part to S3
req, err := http.NewRequestWithContext(ctx, "PUT", url, bytes.NewReader(w.buffer.Bytes()))
if err != nil {
@@ -655,7 +639,7 @@ func (w *ProxyBlobWriter) flushPart() error {
ETag: etag,
})
fmt.Printf("[flushPart] Part %d uploaded successfully: ETag=%s\n", w.partNumber, etag)
fmt.Printf("[flushPart] Part %d uploaded successfully: ETag=%s\n", w.partNumber, etag)
// Reset buffer and increment part number
w.buffer.Reset()
@@ -706,8 +690,6 @@ func (w *ProxyBlobWriter) Commit(ctx context.Context, desc distribution.Descript
}
w.closed = true
fmt.Printf("📝 [Commit] Starting commit: digest=%s, size=%d\n", desc.Digest, w.size)
// Remove from global uploads map
globalUploadsMu.Lock()
delete(globalUploads, w.id)
@@ -715,7 +697,7 @@ func (w *ProxyBlobWriter) Commit(ctx context.Context, desc distribution.Descript
// Flush any remaining buffered data
if w.buffer.Len() > 0 {
fmt.Printf("📤 [Commit] Flushing final buffer: %d bytes\n", w.buffer.Len())
fmt.Printf("[Commit] Flushing final buffer: %d bytes\n", w.buffer.Len())
if err := w.flushPart(); err != nil {
// Try to abort multipart on error
tempDigest := fmt.Sprintf("uploads/temp-%s", w.id)
@@ -735,7 +717,7 @@ func (w *ProxyBlobWriter) Commit(ctx context.Context, desc distribution.Descript
tempPath := fmt.Sprintf("uploads/temp-%s", w.id)
finalPath := desc.Digest.String()
fmt.Printf("🚚 [Commit] Moving blob: %s → %s\n", tempPath, finalPath)
fmt.Printf("[Commit] Moving blob: %s → %s\n", tempPath, finalPath)
moveURL := fmt.Sprintf("%s/move?from=%s&to=%s&did=%s",
w.store.storageEndpoint, tempPath, finalPath, w.store.did)
@@ -755,7 +737,7 @@ func (w *ProxyBlobWriter) Commit(ctx context.Context, desc distribution.Descript
return distribution.Descriptor{}, fmt.Errorf("move blob failed: status %d, body: %s", resp.StatusCode, string(bodyBytes))
}
fmt.Printf("[Commit] Upload completed successfully: digest=%s, size=%d, parts=%d\n", desc.Digest, w.size, len(w.parts))
fmt.Printf("[Commit] Upload completed successfully: digest=%s, size=%d, parts=%d\n", desc.Digest, w.size, len(w.parts))
return distribution.Descriptor{
Digest: desc.Digest,
@@ -768,7 +750,7 @@ func (w *ProxyBlobWriter) Commit(ctx context.Context, desc distribution.Descript
func (w *ProxyBlobWriter) Cancel(ctx context.Context) error {
w.closed = true
fmt.Printf("[Cancel] Cancelling upload: id=%s\n", w.id)
fmt.Printf("[Cancel] Cancelling upload: id=%s\n", w.id)
// Remove from global uploads map
globalUploadsMu.Lock()
@@ -782,7 +764,7 @@ func (w *ProxyBlobWriter) Cancel(ctx context.Context) error {
// Continue anyway - we want to mark upload as cancelled
}
fmt.Printf("[Cancel] Upload cancelled: id=%s\n", w.id)
fmt.Printf("[Cancel] Upload cancelled: id=%s\n", w.id)
return nil
}