github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/distribution/xfer/upload_test.go (about)

     1  package xfer // import "github.com/docker/docker/distribution/xfer"
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"sync/atomic"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/docker/distribution"
    11  	"github.com/docker/docker/layer"
    12  	"github.com/docker/docker/pkg/progress"
    13  )
    14  
    15  const maxUploadConcurrency = 3
    16  
    17  type mockUploadDescriptor struct {
    18  	currentUploads  *int32
    19  	diffID          layer.DiffID
    20  	simulateRetries int
    21  }
    22  
    23  // Key returns the key used to deduplicate downloads.
    24  func (u *mockUploadDescriptor) Key() string {
    25  	return u.diffID.String()
    26  }
    27  
    28  // ID returns the ID for display purposes.
    29  func (u *mockUploadDescriptor) ID() string {
    30  	return u.diffID.String()
    31  }
    32  
    33  // DiffID should return the DiffID for this layer.
    34  func (u *mockUploadDescriptor) DiffID() layer.DiffID {
    35  	return u.diffID
    36  }
    37  
    38  // SetRemoteDescriptor is not used in the mock.
    39  func (u *mockUploadDescriptor) SetRemoteDescriptor(remoteDescriptor distribution.Descriptor) {
    40  }
    41  
    42  // Upload is called to perform the upload.
    43  func (u *mockUploadDescriptor) Upload(ctx context.Context, progressOutput progress.Output) (distribution.Descriptor, error) {
    44  	if u.currentUploads != nil {
    45  		defer atomic.AddInt32(u.currentUploads, -1)
    46  
    47  		if atomic.AddInt32(u.currentUploads, 1) > maxUploadConcurrency {
    48  			return distribution.Descriptor{}, errors.New("concurrency limit exceeded")
    49  		}
    50  	}
    51  
    52  	// Sleep a bit to simulate a time-consuming upload.
    53  	for i := int64(0); i <= 10; i++ {
    54  		select {
    55  		case <-ctx.Done():
    56  			return distribution.Descriptor{}, ctx.Err()
    57  		case <-time.After(10 * time.Millisecond):
    58  			progressOutput.WriteProgress(progress.Progress{ID: u.ID(), Current: i, Total: 10})
    59  		}
    60  	}
    61  
    62  	if u.simulateRetries != 0 {
    63  		u.simulateRetries--
    64  		return distribution.Descriptor{}, errors.New("simulating retry")
    65  	}
    66  
    67  	return distribution.Descriptor{}, nil
    68  }
    69  
    70  func uploadDescriptors(currentUploads *int32) []UploadDescriptor {
    71  	return []UploadDescriptor{
    72  		&mockUploadDescriptor{currentUploads: currentUploads, diffID: "sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf"},
    73  		&mockUploadDescriptor{currentUploads: currentUploads, diffID: "sha256:1515325234325236634634608943609283523908626098235490238423902343"},
    74  		&mockUploadDescriptor{currentUploads: currentUploads, diffID: "sha256:6929356290463485374960346430698374523437683470934634534953453453"},
    75  		&mockUploadDescriptor{currentUploads: currentUploads, diffID: "sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf"},
    76  		&mockUploadDescriptor{currentUploads: currentUploads, diffID: "sha256:8159352387436803946235346346368745389534789534897538734598734987", simulateRetries: 1},
    77  		&mockUploadDescriptor{currentUploads: currentUploads, diffID: "sha256:4637863963478346897346987346987346789346789364879364897364987346"},
    78  	}
    79  }
    80  
    81  func TestSuccessfulUpload(t *testing.T) {
    82  	lum := NewLayerUploadManager(maxUploadConcurrency, func(m *LayerUploadManager) { m.waitDuration = time.Millisecond })
    83  
    84  	progressChan := make(chan progress.Progress)
    85  	progressDone := make(chan struct{})
    86  	receivedProgress := make(map[string]int64)
    87  
    88  	go func() {
    89  		for p := range progressChan {
    90  			receivedProgress[p.ID] = p.Current
    91  		}
    92  		close(progressDone)
    93  	}()
    94  
    95  	var currentUploads int32
    96  	descriptors := uploadDescriptors(&currentUploads)
    97  
    98  	err := lum.Upload(context.Background(), descriptors, progress.ChanOutput(progressChan))
    99  	if err != nil {
   100  		t.Fatalf("upload error: %v", err)
   101  	}
   102  
   103  	close(progressChan)
   104  	<-progressDone
   105  }
   106  
   107  func TestCancelledUpload(t *testing.T) {
   108  	lum := NewLayerUploadManager(maxUploadConcurrency, func(m *LayerUploadManager) { m.waitDuration = time.Millisecond })
   109  
   110  	progressChan := make(chan progress.Progress)
   111  	progressDone := make(chan struct{})
   112  
   113  	go func() {
   114  		for range progressChan {
   115  		}
   116  		close(progressDone)
   117  	}()
   118  
   119  	ctx, cancel := context.WithCancel(context.Background())
   120  
   121  	go func() {
   122  		<-time.After(time.Millisecond)
   123  		cancel()
   124  	}()
   125  
   126  	descriptors := uploadDescriptors(nil)
   127  	err := lum.Upload(ctx, descriptors, progress.ChanOutput(progressChan))
   128  	if err != context.Canceled {
   129  		t.Fatal("expected upload to be cancelled")
   130  	}
   131  
   132  	close(progressChan)
   133  	<-progressDone
   134  }