3a1279393f
Remove forked reference package. Use normalized named values everywhere and familiar functions to convert back to familiar strings for UX and storage compatibility. Enforce that the source repository in the distribution metadata is always a normalized string, ignore invalid values which are not. Update distribution tests to use normalized values. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
149 lines
4.4 KiB
Go
149 lines
4.4 KiB
Go
package daemon
|
|
|
|
import (
|
|
"io"
|
|
"strings"
|
|
|
|
dist "github.com/docker/distribution"
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/docker/docker/api/types"
|
|
"github.com/docker/docker/builder"
|
|
"github.com/docker/docker/distribution"
|
|
progressutils "github.com/docker/docker/distribution/utils"
|
|
"github.com/docker/docker/pkg/progress"
|
|
"github.com/docker/docker/registry"
|
|
"github.com/opencontainers/go-digest"
|
|
"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.ParseNormalizedNamed(image)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if tag != "" {
|
|
// The "tag" could actually be a digest.
|
|
var dgst digest.Digest
|
|
dgst, err = digest.Parse(tag)
|
|
if err == nil {
|
|
ref, err = reference.WithDigest(reference.TrimNamed(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.ParseNormalizedNamed(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ref = reference.TagNameOnly(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() {
|
|
progressutils.WriteDistributionProgress(cancelFunc, outStream, progressChan)
|
|
close(writesDone)
|
|
}()
|
|
|
|
imagePullConfig := &distribution.ImagePullConfig{
|
|
Config: distribution.Config{
|
|
MetaHeaders: metaHeaders,
|
|
AuthConfig: authConfig,
|
|
ProgressOutput: progress.ChanOutput(progressChan),
|
|
RegistryService: daemon.RegistryService,
|
|
ImageEventLogger: daemon.LogImageEvent,
|
|
MetadataStore: daemon.distributionMetadataStore,
|
|
ImageStore: distribution.NewImageConfigStoreFromStore(daemon.imageStore),
|
|
ReferenceStore: daemon.referenceStore,
|
|
},
|
|
DownloadManager: daemon.downloadManager,
|
|
Schema2Types: distribution.ImageTypes,
|
|
}
|
|
|
|
err := distribution.Pull(ctx, ref, imagePullConfig)
|
|
close(progressChan)
|
|
<-writesDone
|
|
return err
|
|
}
|
|
|
|
// GetRepository returns a repository from the registry.
|
|
func (daemon *Daemon) GetRepository(ctx context.Context, ref reference.NamedTagged, authConfig *types.AuthConfig) (dist.Repository, bool, error) {
|
|
// get repository info
|
|
repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
// makes sure name is not empty or `scratch`
|
|
if err := distribution.ValidateRepoName(repoInfo.Name); err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
// get endpoints
|
|
endpoints, err := daemon.RegistryService.LookupPullEndpoints(reference.Domain(repoInfo.Name))
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
// retrieve repository
|
|
var (
|
|
confirmedV2 bool
|
|
repository dist.Repository
|
|
lastError error
|
|
)
|
|
|
|
for _, endpoint := range endpoints {
|
|
if endpoint.Version == registry.APIVersion1 {
|
|
continue
|
|
}
|
|
|
|
repository, confirmedV2, lastError = distribution.NewV2Repository(ctx, repoInfo, endpoint, nil, authConfig, "pull")
|
|
if lastError == nil && confirmedV2 {
|
|
break
|
|
}
|
|
}
|
|
return repository, confirmedV2, lastError
|
|
}
|