2018-02-05 21:05:59 +00:00
|
|
|
package xfer // import "github.com/docker/docker/distribution/xfer"
|
2015-11-14 00:59:01 +00:00
|
|
|
|
|
|
|
import (
|
2018-04-19 22:30:59 +00:00
|
|
|
"context"
|
2015-11-14 00:59:01 +00:00
|
|
|
"errors"
|
|
|
|
"time"
|
|
|
|
|
2023-09-13 15:41:45 +00:00
|
|
|
"github.com/containerd/log"
|
2016-03-01 18:56:05 +00:00
|
|
|
"github.com/docker/distribution"
|
2015-11-14 00:59:01 +00:00
|
|
|
"github.com/docker/docker/layer"
|
|
|
|
"github.com/docker/docker/pkg/progress"
|
|
|
|
)
|
|
|
|
|
|
|
|
const maxUploadAttempts = 5
|
|
|
|
|
|
|
|
// LayerUploadManager provides task management and progress reporting for
|
|
|
|
// uploads.
|
|
|
|
type LayerUploadManager struct {
|
2022-01-22 11:19:42 +00:00
|
|
|
tm *transferManager
|
2016-12-23 20:09:29 +00:00
|
|
|
waitDuration time.Duration
|
2015-11-14 00:59:01 +00:00
|
|
|
}
|
|
|
|
|
2016-12-21 06:43:28 +00:00
|
|
|
// SetConcurrency sets the max concurrent uploads for each push
|
2016-05-06 04:45:55 +00:00
|
|
|
func (lum *LayerUploadManager) SetConcurrency(concurrency int) {
|
2022-01-22 11:40:35 +00:00
|
|
|
lum.tm.setConcurrency(concurrency)
|
2016-05-06 04:45:55 +00:00
|
|
|
}
|
|
|
|
|
2015-11-14 00:59:01 +00:00
|
|
|
// NewLayerUploadManager returns a new LayerUploadManager.
|
2016-12-23 20:09:29 +00:00
|
|
|
func NewLayerUploadManager(concurrencyLimit int, options ...func(*LayerUploadManager)) *LayerUploadManager {
|
|
|
|
manager := LayerUploadManager{
|
2022-01-22 11:19:42 +00:00
|
|
|
tm: newTransferManager(concurrencyLimit),
|
2016-12-23 20:09:29 +00:00
|
|
|
waitDuration: time.Second,
|
2015-11-14 00:59:01 +00:00
|
|
|
}
|
2016-12-23 20:09:29 +00:00
|
|
|
for _, option := range options {
|
|
|
|
option(&manager)
|
|
|
|
}
|
|
|
|
return &manager
|
2015-11-14 00:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type uploadTransfer struct {
|
2022-01-22 12:11:24 +00:00
|
|
|
transfer
|
2015-11-14 00:59:01 +00:00
|
|
|
|
2016-03-01 18:56:05 +00:00
|
|
|
remoteDescriptor distribution.Descriptor
|
|
|
|
err error
|
2015-11-14 00:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// An UploadDescriptor references a layer that may need to be uploaded.
|
|
|
|
type UploadDescriptor interface {
|
|
|
|
// Key returns the key used to deduplicate uploads.
|
|
|
|
Key() string
|
|
|
|
// ID returns the ID for display purposes.
|
|
|
|
ID() string
|
|
|
|
// DiffID should return the DiffID for this layer.
|
|
|
|
DiffID() layer.DiffID
|
|
|
|
// Upload is called to perform the Upload.
|
2016-03-01 18:56:05 +00:00
|
|
|
Upload(ctx context.Context, progressOutput progress.Output) (distribution.Descriptor, error)
|
|
|
|
// SetRemoteDescriptor provides the distribution.Descriptor that was
|
|
|
|
// returned by Upload. This descriptor is not to be confused with
|
|
|
|
// the UploadDescriptor interface, which is used for internally
|
|
|
|
// identifying layers that are being uploaded.
|
|
|
|
SetRemoteDescriptor(descriptor distribution.Descriptor)
|
2015-11-14 00:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Upload is a blocking function which ensures the listed layers are present on
|
|
|
|
// the remote registry. It uses the string returned by the Key method to
|
|
|
|
// deduplicate uploads.
|
2015-12-11 00:27:58 +00:00
|
|
|
func (lum *LayerUploadManager) Upload(ctx context.Context, layers []UploadDescriptor, progressOutput progress.Output) error {
|
2015-11-14 00:59:01 +00:00
|
|
|
var (
|
|
|
|
uploads []*uploadTransfer
|
2016-03-01 18:56:05 +00:00
|
|
|
dedupDescriptors = make(map[string]*uploadTransfer)
|
2015-11-14 00:59:01 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
for _, descriptor := range layers {
|
|
|
|
progress.Update(progressOutput, descriptor.ID(), "Preparing")
|
|
|
|
|
|
|
|
key := descriptor.Key()
|
|
|
|
if _, present := dedupDescriptors[key]; present {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
xferFunc := lum.makeUploadFunc(descriptor)
|
2022-01-22 11:40:35 +00:00
|
|
|
upload, watcher := lum.tm.transfer(descriptor.Key(), xferFunc, progressOutput)
|
2022-02-18 13:03:35 +00:00
|
|
|
defer upload.release(watcher)
|
2015-11-14 00:59:01 +00:00
|
|
|
uploads = append(uploads, upload.(*uploadTransfer))
|
2016-03-01 18:56:05 +00:00
|
|
|
dedupDescriptors[key] = upload.(*uploadTransfer)
|
2015-11-14 00:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, upload := range uploads {
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
2015-12-11 00:27:58 +00:00
|
|
|
return ctx.Err()
|
2022-02-18 13:03:35 +00:00
|
|
|
case <-upload.transfer.done():
|
2015-11-14 00:59:01 +00:00
|
|
|
if upload.err != nil {
|
2015-12-11 00:27:58 +00:00
|
|
|
return upload.err
|
2015-11-14 00:59:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-03-01 18:56:05 +00:00
|
|
|
for _, l := range layers {
|
|
|
|
l.SetRemoteDescriptor(dedupDescriptors[l.Key()].remoteDescriptor)
|
|
|
|
}
|
2015-11-14 00:59:01 +00:00
|
|
|
|
2015-12-11 00:27:58 +00:00
|
|
|
return nil
|
2015-11-14 00:59:01 +00:00
|
|
|
}
|
|
|
|
|
2022-02-18 15:54:12 +00:00
|
|
|
func (lum *LayerUploadManager) makeUploadFunc(descriptor UploadDescriptor) doFunc {
|
2022-01-22 12:11:24 +00:00
|
|
|
return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) transfer {
|
2015-11-14 00:59:01 +00:00
|
|
|
u := &uploadTransfer{
|
2022-01-22 12:11:24 +00:00
|
|
|
transfer: newTransfer(),
|
2015-11-14 00:59:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
defer func() {
|
|
|
|
close(progressChan)
|
|
|
|
}()
|
|
|
|
|
|
|
|
progressOutput := progress.ChanOutput(progressChan)
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-start:
|
|
|
|
default:
|
|
|
|
progress.Update(progressOutput, descriptor.ID(), "Waiting")
|
|
|
|
<-start
|
|
|
|
}
|
|
|
|
|
|
|
|
retries := 0
|
|
|
|
for {
|
2022-02-18 13:03:35 +00:00
|
|
|
remoteDescriptor, err := descriptor.Upload(u.transfer.context(), progressOutput)
|
2015-11-14 00:59:01 +00:00
|
|
|
if err == nil {
|
2016-03-01 18:56:05 +00:00
|
|
|
u.remoteDescriptor = remoteDescriptor
|
2015-11-14 00:59:01 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// If an error was returned because the context
|
|
|
|
// was cancelled, we shouldn't retry.
|
|
|
|
select {
|
2022-02-18 13:03:35 +00:00
|
|
|
case <-u.transfer.context().Done():
|
2015-11-14 00:59:01 +00:00
|
|
|
u.err = err
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
retries++
|
|
|
|
if _, isDNR := err.(DoNotRetry); isDNR || retries == maxUploadAttempts {
|
2023-06-23 00:33:17 +00:00
|
|
|
log.G(context.TODO()).Errorf("Upload failed: %v", err)
|
2015-11-14 00:59:01 +00:00
|
|
|
u.err = err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-06-23 00:33:17 +00:00
|
|
|
log.G(context.TODO()).Errorf("Upload failed, retrying: %v", err)
|
2015-11-14 00:59:01 +00:00
|
|
|
delay := retries * 5
|
2016-12-23 20:09:29 +00:00
|
|
|
ticker := time.NewTicker(lum.waitDuration)
|
2015-11-14 00:59:01 +00:00
|
|
|
|
|
|
|
selectLoop:
|
|
|
|
for {
|
2016-03-04 15:15:44 +00:00
|
|
|
progress.Updatef(progressOutput, descriptor.ID(), "Retrying in %d second%s", delay, (map[bool]string{true: "s"})[delay != 1])
|
2015-11-14 00:59:01 +00:00
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
|
|
|
delay--
|
|
|
|
if delay == 0 {
|
|
|
|
ticker.Stop()
|
|
|
|
break selectLoop
|
|
|
|
}
|
2022-02-18 13:03:35 +00:00
|
|
|
case <-u.transfer.context().Done():
|
2015-11-14 00:59:01 +00:00
|
|
|
ticker.Stop()
|
|
|
|
u.err = errors.New("upload cancelled during retry delay")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return u
|
|
|
|
}
|
|
|
|
}
|