107 lines
3.1 KiB
Go
107 lines
3.1 KiB
Go
|
package daemon
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/docker/distribution/digest"
|
||
|
"github.com/docker/docker/builder"
|
||
|
"github.com/docker/docker/distribution"
|
||
|
"github.com/docker/docker/pkg/progress"
|
||
|
"github.com/docker/docker/reference"
|
||
|
"github.com/docker/docker/registry"
|
||
|
"github.com/docker/engine-api/types"
|
||
|
"golang.org/x/net/context"
|
||
|
)
|
||
|
|
||
|
// PullImage initiates a pull operation. image is the repository name to pull, and
|
||
|
// tag may be either empty, or indicate a specific tag to pull.
|
||
|
func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
|
||
|
// Special case: "pull -a" may send an image name with a
|
||
|
// trailing :. This is ugly, but let's not break API
|
||
|
// compatibility.
|
||
|
image = strings.TrimSuffix(image, ":")
|
||
|
|
||
|
ref, err := reference.ParseNamed(image)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if tag != "" {
|
||
|
// The "tag" could actually be a digest.
|
||
|
var dgst digest.Digest
|
||
|
dgst, err = digest.ParseDigest(tag)
|
||
|
if err == nil {
|
||
|
ref, err = reference.WithDigest(ref, dgst)
|
||
|
} else {
|
||
|
ref, err = reference.WithTag(ref, tag)
|
||
|
}
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return daemon.pullImageWithReference(ctx, ref, metaHeaders, authConfig, outStream)
|
||
|
}
|
||
|
|
||
|
// PullOnBuild tells Docker to pull image referenced by `name`.
|
||
|
func (daemon *Daemon) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
|
||
|
ref, err := reference.ParseNamed(name)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
ref = reference.WithDefaultTag(ref)
|
||
|
|
||
|
pullRegistryAuth := &types.AuthConfig{}
|
||
|
if len(authConfigs) > 0 {
|
||
|
// The request came with a full auth config file, we prefer to use that
|
||
|
repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
resolvedConfig := registry.ResolveAuthConfig(
|
||
|
authConfigs,
|
||
|
repoInfo.Index,
|
||
|
)
|
||
|
pullRegistryAuth = &resolvedConfig
|
||
|
}
|
||
|
|
||
|
if err := daemon.pullImageWithReference(ctx, ref, nil, pullRegistryAuth, output); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return daemon.GetImage(name)
|
||
|
}
|
||
|
|
||
|
func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.Named, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
|
||
|
// Include a buffer so that slow client connections don't affect
|
||
|
// transfer performance.
|
||
|
progressChan := make(chan progress.Progress, 100)
|
||
|
|
||
|
writesDone := make(chan struct{})
|
||
|
|
||
|
ctx, cancelFunc := context.WithCancel(ctx)
|
||
|
|
||
|
go func() {
|
||
|
writeDistributionProgress(cancelFunc, outStream, progressChan)
|
||
|
close(writesDone)
|
||
|
}()
|
||
|
|
||
|
imagePullConfig := &distribution.ImagePullConfig{
|
||
|
MetaHeaders: metaHeaders,
|
||
|
AuthConfig: authConfig,
|
||
|
ProgressOutput: progress.ChanOutput(progressChan),
|
||
|
RegistryService: daemon.RegistryService,
|
||
|
ImageEventLogger: daemon.LogImageEvent,
|
||
|
MetadataStore: daemon.distributionMetadataStore,
|
||
|
ImageStore: daemon.imageStore,
|
||
|
ReferenceStore: daemon.referenceStore,
|
||
|
DownloadManager: daemon.downloadManager,
|
||
|
}
|
||
|
|
||
|
err := distribution.Pull(ctx, ref, imagePullConfig)
|
||
|
close(progressChan)
|
||
|
<-writesDone
|
||
|
return err
|
||
|
}
|