2016-04-07 21:29:18 +00:00
|
|
|
package daemon
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
2017-05-16 23:56:56 +00:00
|
|
|
"runtime"
|
2016-04-07 21:29:18 +00:00
|
|
|
"strings"
|
|
|
|
|
2016-11-08 17:32:29 +00:00
|
|
|
dist "github.com/docker/distribution"
|
2017-01-26 00:54:18 +00:00
|
|
|
"github.com/docker/distribution/reference"
|
2016-09-06 18:18:12 +00:00
|
|
|
"github.com/docker/docker/api/types"
|
2016-04-07 21:29:18 +00:00
|
|
|
"github.com/docker/docker/distribution"
|
2016-12-12 23:05:53 +00:00
|
|
|
progressutils "github.com/docker/docker/distribution/utils"
|
2016-04-07 21:29:18 +00:00
|
|
|
"github.com/docker/docker/pkg/progress"
|
|
|
|
"github.com/docker/docker/registry"
|
2017-01-07 01:23:18 +00:00
|
|
|
"github.com/opencontainers/go-digest"
|
2016-04-07 21:29:18 +00:00
|
|
|
"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.
|
2017-05-17 20:33:33 +00:00
|
|
|
func (daemon *Daemon) PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
|
2016-04-07 21:29:18 +00:00
|
|
|
// 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, ":")
|
|
|
|
|
2017-01-26 00:54:18 +00:00
|
|
|
ref, err := reference.ParseNormalizedNamed(image)
|
2016-04-07 21:29:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if tag != "" {
|
|
|
|
// The "tag" could actually be a digest.
|
|
|
|
var dgst digest.Digest
|
2017-01-07 01:23:18 +00:00
|
|
|
dgst, err = digest.Parse(tag)
|
2016-04-07 21:29:18 +00:00
|
|
|
if err == nil {
|
2016-11-10 23:59:02 +00:00
|
|
|
ref, err = reference.WithDigest(reference.TrimNamed(ref), dgst)
|
2016-04-07 21:29:18 +00:00
|
|
|
} else {
|
|
|
|
ref, err = reference.WithTag(ref, tag)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-17 20:33:33 +00:00
|
|
|
return daemon.pullImageWithReference(ctx, ref, platform, metaHeaders, authConfig, outStream)
|
2016-04-07 21:29:18 +00:00
|
|
|
}
|
|
|
|
|
2017-05-17 20:33:33 +00:00
|
|
|
func (daemon *Daemon) pullImageWithReference(ctx context.Context, ref reference.Named, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
|
2016-04-07 21:29:18 +00:00
|
|
|
// 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() {
|
2016-12-12 23:05:53 +00:00
|
|
|
progressutils.WriteDistributionProgress(cancelFunc, outStream, progressChan)
|
2016-04-07 21:29:18 +00:00
|
|
|
close(writesDone)
|
|
|
|
}()
|
|
|
|
|
2017-05-17 20:33:33 +00:00
|
|
|
// Default to the host OS platform in case it hasn't been populated with an explicit value.
|
|
|
|
if platform == "" {
|
|
|
|
platform = runtime.GOOS
|
|
|
|
}
|
2017-05-16 23:56:56 +00:00
|
|
|
|
2016-04-07 21:29:18 +00:00
|
|
|
imagePullConfig := &distribution.ImagePullConfig{
|
2016-12-16 19:19:05 +00:00
|
|
|
Config: distribution.Config{
|
|
|
|
MetaHeaders: metaHeaders,
|
|
|
|
AuthConfig: authConfig,
|
|
|
|
ProgressOutput: progress.ChanOutput(progressChan),
|
|
|
|
RegistryService: daemon.RegistryService,
|
|
|
|
ImageEventLogger: daemon.LogImageEvent,
|
2017-05-17 20:33:33 +00:00
|
|
|
MetadataStore: daemon.stores[platform].distributionMetadataStore,
|
|
|
|
ImageStore: distribution.NewImageConfigStoreFromStore(daemon.stores[platform].imageStore),
|
|
|
|
ReferenceStore: daemon.stores[platform].referenceStore,
|
2016-12-16 19:19:05 +00:00
|
|
|
},
|
|
|
|
DownloadManager: daemon.downloadManager,
|
|
|
|
Schema2Types: distribution.ImageTypes,
|
2017-05-17 20:33:33 +00:00
|
|
|
Platform: platform,
|
2016-04-07 21:29:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err := distribution.Pull(ctx, ref, imagePullConfig)
|
|
|
|
close(progressChan)
|
|
|
|
<-writesDone
|
|
|
|
return err
|
|
|
|
}
|
2016-11-08 07:59:55 +00:00
|
|
|
|
2016-11-18 23:57:11 +00:00
|
|
|
// GetRepository returns a repository from the registry.
|
2017-05-01 23:14:59 +00:00
|
|
|
func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.Named, authConfig *types.AuthConfig) (dist.Repository, bool, error) {
|
2016-11-08 07:59:55 +00:00
|
|
|
// get repository info
|
|
|
|
repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
|
|
|
|
if err != nil {
|
2016-11-08 17:32:29 +00:00
|
|
|
return nil, false, err
|
2016-11-08 07:59:55 +00:00
|
|
|
}
|
|
|
|
// makes sure name is not empty or `scratch`
|
2017-01-26 00:54:18 +00:00
|
|
|
if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
|
2016-11-08 17:32:29 +00:00
|
|
|
return nil, false, err
|
2016-11-08 07:59:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// get endpoints
|
2017-01-26 00:54:18 +00:00
|
|
|
endpoints, err := daemon.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
|
2016-11-08 07:59:55 +00:00
|
|
|
if err != nil {
|
2016-11-08 17:32:29 +00:00
|
|
|
return nil, false, err
|
2016-11-08 07:59:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// retrieve repository
|
2016-11-08 17:32:29 +00:00
|
|
|
var (
|
|
|
|
confirmedV2 bool
|
|
|
|
repository dist.Repository
|
|
|
|
lastError error
|
|
|
|
)
|
|
|
|
|
|
|
|
for _, endpoint := range endpoints {
|
|
|
|
if endpoint.Version == registry.APIVersion1 {
|
|
|
|
continue
|
|
|
|
}
|
2016-11-08 07:59:55 +00:00
|
|
|
|
2016-11-08 17:32:29 +00:00
|
|
|
repository, confirmedV2, lastError = distribution.NewV2Repository(ctx, repoInfo, endpoint, nil, authConfig, "pull")
|
|
|
|
if lastError == nil && confirmedV2 {
|
|
|
|
break
|
2016-11-08 07:59:55 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-08 17:32:29 +00:00
|
|
|
return repository, confirmedV2, lastError
|
2016-11-08 07:59:55 +00:00
|
|
|
}
|