Expose a smaller interface for the Builder retrieving images from daemon

Removes 3 methods from the builder.Backend interface
Remove the coupling between imageContexts, imageMounts and the builder.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
Daniel Nephin 2017-03-27 18:36:28 -07:00
parent a3eebaf509
commit b3bc7b28d0
12 changed files with 238 additions and 208 deletions

View File

@ -22,3 +22,10 @@ type BuildConfig struct {
ProgressWriter ProgressWriter
Options *types.ImageBuildOptions
}
// GetImageAndLayerOptions are the options supported by GetImageAndLayer
type GetImageAndLayerOptions struct {
ForcePull bool
AuthConfig map[string]types.AuthConfig
Output io.Writer
}

View File

@ -34,12 +34,8 @@ type Source interface {
// Backend abstracts calls to a Docker Daemon.
type Backend interface {
// TODO: use digest reference instead of name
GetImageAndLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (Image, ReleaseableLayer, error)
// GetImageOnBuild looks up a Docker image referenced by `name`.
GetImageOnBuild(name string) (Image, error)
// PullOnBuild tells Docker to pull image referenced by `name`.
PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (Image, error)
// ContainerAttachRaw attaches to container.
ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool, attached chan struct{}) error
// ContainerCreate creates a new Docker container and returns potential warnings
@ -62,15 +58,6 @@ type Backend interface {
// TODO: extract in the builder instead of passing `decompress`
// TODO: use containerd/fs.changestream instead as a source
CopyOnBuild(containerID string, destPath string, srcRoot string, srcPath string, decompress bool) error
// MountImage returns mounted path with rootfs of an image.
MountImage(name string) (string, func() error, error)
}
// Image represents a Docker image used by the builder.
type Image interface {
ImageID() string
RunConfig() *container.Config
}
// Result is the output produced by a Builder
@ -92,3 +79,15 @@ type ImageCache interface {
// and runconfig equals `cfg`. A cache miss is expected to return an empty ID and a nil error.
GetCache(parentID string, cfg *container.Config) (imageID string, err error)
}
// Image represents a Docker image used by the builder.
type Image interface {
ImageID() string
RunConfig() *container.Config
}
// ReleaseableLayer is an image layer that can be mounted and released
type ReleaseableLayer interface {
Release() error
Mount() (string, error)
}

View File

@ -45,7 +45,10 @@ type BuildManager struct {
// NewBuildManager creates a BuildManager
func NewBuildManager(b builder.Backend) *BuildManager {
return &BuildManager{backend: b, pathCache: &syncmap.Map{}}
return &BuildManager{
backend: b,
pathCache: &syncmap.Map{},
}
}
// Build starts a new build from a BuildConfig
@ -122,8 +125,8 @@ func newBuilder(clientCtx context.Context, options builderOptions) *Builder {
docker: options.Backend,
tmpContainers: map[string]struct{}{},
buildArgs: newBuildArgs(config.BuildArgs),
imageContexts: &imageContexts{},
}
b.imageContexts = &imageContexts{b: b, cache: options.PathCache}
return b
}

View File

@ -20,9 +20,9 @@ import (
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/docker/builder"
"github.com/docker/docker/pkg/signal"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
@ -160,23 +160,29 @@ func dispatchCopy(req dispatchRequest) error {
}
flFrom := req.flags.AddString("from", "")
if err := req.flags.Parse(); err != nil {
return err
}
var im *imageMount
if flFrom.IsUsed() {
var err error
im, err = req.builder.imageContexts.get(flFrom.Value)
if err != nil {
return err
}
im, err := req.builder.getImageMount(flFrom)
if err != nil {
return errors.Wrapf(err, "invalid from flag value %s", flFrom.Value)
}
return req.builder.runContextCommand(req, false, false, "COPY", im)
}
func (b *Builder) getImageMount(fromFlag *Flag) (*imageMount, error) {
if !fromFlag.IsUsed() {
// TODO: this could return the mount in the default case as well
return nil, nil
}
im, err := b.imageContexts.getMount(fromFlag.Value)
if err != nil || im != nil {
return im, err
}
return b.getImage(fromFlag.Value)
}
// FROM imagename[:tag | @digest] [AS build-stage-name]
//
func from(req dispatchRequest) error {
@ -192,20 +198,17 @@ func from(req dispatchRequest) error {
req.builder.resetImageCache()
req.state.noBaseImage = false
req.state.stageName = stageName
if _, err := req.builder.imageContexts.add(stageName); err != nil {
return err
}
image, err := req.builder.getFromImage(req.state, req.shlex, req.args[0])
image, err := req.builder.getFromImage(req.shlex, req.args[0])
if err != nil {
return err
}
switch image {
case nil:
if image == nil {
req.state.imageID = ""
req.state.noBaseImage = true
default:
req.builder.imageContexts.update(image.ImageID(), image.RunConfig())
image = newImageMount(nil, nil)
}
if err := req.builder.imageContexts.add(stageName, image); err != nil {
return err
}
req.state.baseImage = image
@ -228,7 +231,7 @@ func parseBuildStageName(args []string) (string, error) {
return stageName, nil
}
func (b *Builder) getFromImage(dispatchState *dispatchState, shlex *ShellLex, name string) (builder.Image, error) {
func (b *Builder) getFromImage(shlex *ShellLex, name string) (*imageMount, error) {
substitutionArgs := []string{}
for key, value := range b.buildArgs.GetAllMeta() {
substitutionArgs = append(substitutionArgs, key+"="+value)
@ -254,7 +257,19 @@ func (b *Builder) getFromImage(dispatchState *dispatchState, shlex *ShellLex, na
}
return nil, nil
}
return pullOrGetImage(b, name)
return b.getImage(name)
}
func (b *Builder) getImage(name string) (*imageMount, error) {
image, layer, err := b.docker.GetImageAndLayer(b.clientCtx, name, backend.GetImageAndLayerOptions{
ForcePull: b.options.PullParent,
AuthConfig: b.options.AuthConfigs,
Output: b.Output,
})
if err != nil {
return nil, err
}
return newImageMount(image, layer), nil
}
// ONBUILD RUN echo yo
@ -801,29 +816,3 @@ func errBlankCommandNames(command string) error {
func errTooManyArguments(command string) error {
return fmt.Errorf("Bad input to %s, too many arguments", command)
}
// mountByRef creates an imageMount from a reference. pulling the image if needed.
func mountByRef(b *Builder, name string) (*imageMount, error) {
image, err := pullOrGetImage(b, name)
if err != nil {
return nil, err
}
im := b.imageContexts.newImageMount(image.ImageID())
return im, nil
}
func pullOrGetImage(b *Builder, name string) (builder.Image, error) {
var image builder.Image
if !b.options.PullParent {
image, _ = b.docker.GetImageOnBuild(name)
// TODO: shouldn't we error out if error is different from "not found" ?
}
if image == nil {
var err error
image, err = b.docker.PullOnBuild(b.clientCtx, name, b.options.AuthConfigs, b.Output)
if err != nil {
return nil, err
}
}
return image, nil
}

View File

@ -47,16 +47,17 @@ func defaultDispatchReq(builder *Builder, args ...string) dispatchRequest {
}
func newBuilderWithMockBackend() *Builder {
mockBackend := &MockBackend{}
b := &Builder{
options: &types.ImageBuildOptions{},
docker: &MockBackend{},
docker: mockBackend,
buildArgs: newBuildArgs(make(map[string]*string)),
tmpContainers: make(map[string]struct{}),
Stdout: new(bytes.Buffer),
clientCtx: context.Background(),
disableCommit: true,
imageContexts: &imageContexts{},
}
b.imageContexts = &imageContexts{b: b}
return b
}
@ -198,12 +199,12 @@ func TestFromScratch(t *testing.T) {
func TestFromWithArg(t *testing.T) {
tag, expected := ":sometag", "expectedthisid"
getImage := func(name string) (builder.Image, error) {
getImage := func(name string) (builder.Image, builder.ReleaseableLayer, error) {
assert.Equal(t, "alpine"+tag, name)
return &mockImage{id: "expectedthisid"}, nil
return &mockImage{id: "expectedthisid"}, nil, nil
}
b := newBuilderWithMockBackend()
b.docker.(*MockBackend).getImageOnBuildFunc = getImage
b.docker.(*MockBackend).getImageFunc = getImage
require.NoError(t, arg(defaultDispatchReq(b, "THETAG="+tag)))
req := defaultDispatchReq(b, "alpine${THETAG}")
@ -219,12 +220,12 @@ func TestFromWithArg(t *testing.T) {
func TestFromWithUndefinedArg(t *testing.T) {
tag, expected := "sometag", "expectedthisid"
getImage := func(name string) (builder.Image, error) {
getImage := func(name string) (builder.Image, builder.ReleaseableLayer, error) {
assert.Equal(t, "alpine", name)
return &mockImage{id: "expectedthisid"}, nil
return &mockImage{id: "expectedthisid"}, nil, nil
}
b := newBuilderWithMockBackend()
b.docker.(*MockBackend).getImageOnBuildFunc = getImage
b.docker.(*MockBackend).getImageFunc = getImage
b.options.BuildArgs = map[string]*string{"THETAG": &tag}
req := defaultDispatchReq(b, "alpine${THETAG}")
@ -474,9 +475,11 @@ func TestRunWithBuildArgs(t *testing.T) {
b.imageCache = imageCache
mockBackend := b.docker.(*MockBackend)
mockBackend.getImageOnBuildImage = &mockImage{
id: "abcdef",
config: &container.Config{Cmd: origCmd},
mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ReleaseableLayer, error) {
return &mockImage{
id: "abcdef",
config: &container.Config{Cmd: origCmd},
}, nil, nil
}
mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) {
// Check the runConfig.Cmd sent to create()

View File

@ -19,49 +19,40 @@ type pathCache interface {
// imageContexts is a helper for stacking up built image rootfs and reusing
// them as contexts
type imageContexts struct {
b *Builder
list []*imageMount // indexed list of stages
implicitMounts []*imageMount // implicitly mounted images
byName map[string]*imageMount
cache pathCache
list []*imageMount
byName map[string]*imageMount
cache pathCache
}
func (ic *imageContexts) newImageMount(id string) *imageMount {
return &imageMount{ic: ic, id: id}
}
func (ic *imageContexts) add(name string) (*imageMount, error) {
im := &imageMount{ic: ic}
func (ic *imageContexts) add(name string, im *imageMount) error {
if len(name) > 0 {
if ic.byName == nil {
ic.byName = make(map[string]*imageMount)
}
if _, ok := ic.byName[name]; ok {
return nil, errors.Errorf("duplicate name %s", name)
return errors.Errorf("duplicate name %s", name)
}
ic.byName[name] = im
}
ic.list = append(ic.list, im)
return im, nil
return nil
}
func (ic *imageContexts) update(imageID string, runConfig *container.Config) {
ic.list[len(ic.list)-1].id = imageID
ic.list[len(ic.list)-1].runConfig = runConfig
ic.list[len(ic.list)-1].update(imageID, runConfig)
}
func (ic *imageContexts) validate(i int) error {
if i < 0 || i >= len(ic.list)-1 {
var extraMsg string
if i == len(ic.list)-1 {
extraMsg = " refers current build block"
return errors.Errorf("%d refers to current build stage", i)
}
return errors.Errorf("invalid from flag value %d%s", i, extraMsg)
return errors.Errorf("index out of bounds")
}
return nil
}
func (ic *imageContexts) get(indexOrName string) (*imageMount, error) {
func (ic *imageContexts) getMount(indexOrName string) (*imageMount, error) {
index, err := strconv.Atoi(indexOrName)
if err == nil {
if err := ic.validate(index); err != nil {
@ -72,12 +63,7 @@ func (ic *imageContexts) get(indexOrName string) (*imageMount, error) {
if im, ok := ic.byName[strings.ToLower(indexOrName)]; ok {
return im, nil
}
im, err := mountByRef(ic.b, indexOrName)
if err != nil {
return nil, errors.Wrapf(err, "invalid from flag value %s", indexOrName)
}
ic.implicitMounts = append(ic.implicitMounts, im)
return im, nil
return nil, nil
}
func (ic *imageContexts) unmount() (retErr error) {
@ -98,6 +84,7 @@ func (ic *imageContexts) unmount() (retErr error) {
return
}
// TODO: remove getCache/setCache from imageContexts
func (ic *imageContexts) getCache(id, path string) (interface{}, bool) {
if ic.cache != nil {
if id == "" {
@ -114,48 +101,56 @@ func (ic *imageContexts) setCache(id, path string, v interface{}) {
}
}
// imageMount is a reference for getting access to a buildcontext that is backed
// by an existing image
// imageMount is a reference to an image that can be used as a builder.Source
type imageMount struct {
id string
source builder.Source
release func() error
ic *imageContexts
runConfig *container.Config
layer builder.ReleaseableLayer
}
func newImageMount(image builder.Image, layer builder.ReleaseableLayer) *imageMount {
im := &imageMount{layer: layer}
if image != nil {
im.update(image.ImageID(), image.RunConfig())
}
return im
}
func (im *imageMount) context() (builder.Source, error) {
if im.source == nil {
if im.id == "" {
return nil, errors.Errorf("could not copy from empty context")
if im.id == "" || im.layer == nil {
return nil, errors.Errorf("empty context")
}
p, release, err := im.ic.b.docker.MountImage(im.id)
mountPath, err := im.layer.Mount()
if err != nil {
return nil, errors.Wrapf(err, "failed to mount %s", im.id)
}
source, err := remotecontext.NewLazyContext(p)
source, err := remotecontext.NewLazyContext(mountPath)
if err != nil {
return nil, errors.Wrapf(err, "failed to create lazycontext for %s", p)
return nil, errors.Wrapf(err, "failed to create lazycontext for %s", mountPath)
}
im.release = release
im.source = source
}
return im.source, nil
}
func (im *imageMount) unmount() error {
if im.release != nil {
if err := im.release(); err != nil {
return errors.Wrapf(err, "failed to unmount previous build image %s", im.id)
}
im.release = nil
if err := im.layer.Release(); err != nil {
return errors.Wrapf(err, "failed to unmount previous build image %s", im.id)
}
return nil
}
func (im *imageMount) update(imageID string, runConfig *container.Config) {
im.id = imageID
im.runConfig = runConfig
}
func (im *imageMount) ImageID() string {
return im.id
}
func (im *imageMount) RunConfig() *container.Config {
return im.runConfig
}

View File

@ -372,7 +372,7 @@ func (b *Builder) calcCopyInfo(cmdName, origPath string, allowLocalDecompression
if imageSource != nil {
source, err = imageSource.context()
if err != nil {
return nil, err
return nil, errors.Wrapf(err, "failed to copy")
}
}

View File

@ -15,30 +15,15 @@ import (
// MockBackend implements the builder.Backend interface for unit testing
type MockBackend struct {
getImageOnBuildFunc func(string) (builder.Image, error)
getImageOnBuildImage *mockImage
containerCreateFunc func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error)
commitFunc func(string, *backend.ContainerCommitConfig) (string, error)
}
func (m *MockBackend) GetImageOnBuild(name string) (builder.Image, error) {
if m.getImageOnBuildFunc != nil {
return m.getImageOnBuildFunc(name)
}
if m.getImageOnBuildImage != nil {
return m.getImageOnBuildImage, nil
}
return &mockImage{id: "theid"}, nil
containerCreateFunc func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error)
commitFunc func(string, *backend.ContainerCommitConfig) (string, error)
getImageFunc func(string) (builder.Image, builder.ReleaseableLayer, error)
}
func (m *MockBackend) TagImageWithReference(image.ID, reference.Named) error {
return nil
}
func (m *MockBackend) PullOnBuild(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (builder.Image, error) {
return nil, nil
}
func (m *MockBackend) ContainerAttachRaw(cID string, stdin io.ReadCloser, stdout, stderr io.Writer, stream bool, attached chan struct{}) error {
return nil
}
@ -89,8 +74,12 @@ func (m *MockBackend) SquashImage(from string, to string) (string, error) {
return "", nil
}
func (m *MockBackend) MountImage(name string) (string, func() error, error) {
return "", func() error { return nil }, nil
func (m *MockBackend) GetImageAndLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ReleaseableLayer, error) {
if m.getImageFunc != nil {
return m.getImageFunc(refOrID)
}
return &mockImage{id: "theid"}, &mockLayer{}, nil
}
type mockImage struct {
@ -116,3 +105,13 @@ func (mic *mockImageCache) GetCache(parentID string, cfg *container.Config) (str
}
return "", nil
}
type mockLayer struct{}
func (l *mockLayer) Release() error {
return nil
}
func (l *mockLayer) Mount() (string, error) {
return "mountPath", nil
}

View File

@ -8,12 +8,10 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/container"
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/chrootarchive"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/symlink"
"github.com/docker/docker/pkg/system"
"github.com/pkg/errors"
@ -470,34 +468,3 @@ func (daemon *Daemon) CopyOnBuild(cID, destPath, srcRoot, srcPath string, decomp
return fixPermissions(fullSrcPath, destPath, rootUID, rootGID, destExists)
}
// MountImage returns mounted path with rootfs of an image.
func (daemon *Daemon) MountImage(name string) (string, func() error, error) {
img, err := daemon.GetImage(name)
if err != nil {
return "", nil, errors.Wrapf(err, "no such image: %s", name)
}
mountID := stringid.GenerateRandomID()
rwLayer, err := daemon.layerStore.CreateRWLayer(mountID, img.RootFS.ChainID(), nil)
if err != nil {
return "", nil, errors.Wrap(err, "failed to create rwlayer")
}
mountPath, err := rwLayer.Mount("")
if err != nil {
metadata, releaseErr := daemon.layerStore.ReleaseRWLayer(rwLayer)
if releaseErr != nil {
err = errors.Wrapf(err, "failed to release rwlayer: %s", releaseErr.Error())
}
layer.LogReleaseMetadata(metadata)
return "", nil, errors.Wrap(err, "failed to mount rwlayer")
}
return mountPath, func() error {
rwLayer.Unmount()
metadata, err := daemon.layerStore.ReleaseRWLayer(rwLayer)
layer.LogReleaseMetadata(metadata)
return err
}, nil
}

108
daemon/build.go Normal file
View File

@ -0,0 +1,108 @@
package daemon
import (
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/builder"
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/registry"
"github.com/pkg/errors"
"golang.org/x/net/context"
"io"
)
type releaseableLayer struct {
rwLayer layer.RWLayer
release func(layer.RWLayer) error
mount func() (layer.RWLayer, error)
}
func (rl *releaseableLayer) Release() error {
if rl.rwLayer == nil {
return nil
}
rl.rwLayer.Unmount()
return rl.release(rl.rwLayer)
}
func (rl *releaseableLayer) Mount() (string, error) {
var err error
// daemon.layerStore.CreateRWLayer(mountID, img.RootFS.ChainID(), nil)
rl.rwLayer, err = rl.mount()
if err != nil {
return "", errors.Wrap(err, "failed to create rwlayer")
}
mountPath, err := rl.rwLayer.Mount("")
if err != nil {
releaseErr := rl.release(rl.rwLayer)
if releaseErr != nil {
err = errors.Wrapf(err, "failed to release rwlayer: %s", releaseErr.Error())
}
return "", errors.Wrap(err, "failed to mount rwlayer")
}
return mountPath, err
}
func (daemon *Daemon) getReleasableLayerForImage(img *image.Image) (*releaseableLayer, error) {
mountFunc := func() (layer.RWLayer, error) {
mountID := stringid.GenerateRandomID()
return daemon.layerStore.CreateRWLayer(mountID, img.RootFS.ChainID(), nil)
}
releaseFunc := func(rwLayer layer.RWLayer) error {
metadata, err := daemon.layerStore.ReleaseRWLayer(rwLayer)
layer.LogReleaseMetadata(metadata)
return err
}
return &releaseableLayer{mount: mountFunc, release: releaseFunc}, nil
}
// TODO: could this use the regular daemon PullImage ?
func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer) (*image.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)
}
// GetImageAndLayer returns an image and releaseable layer for a reference or ID
func (daemon *Daemon) GetImageAndLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ReleaseableLayer, error) {
if !opts.ForcePull {
image, _ := daemon.GetImage(refOrID)
// TODO: shouldn't we error out if error is different from "not found" ?
if image != nil {
layer, err := daemon.getReleasableLayerForImage(image)
return image, layer, err
}
}
image, err := daemon.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output)
if err != nil {
return nil, nil, err
}
layer, err := daemon.getReleasableLayerForImage(image)
return image, layer, err
}

View File

@ -4,7 +4,6 @@ import (
"fmt"
"github.com/docker/distribution/reference"
"github.com/docker/docker/builder"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/stringid"
)
@ -75,12 +74,3 @@ func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
}
return daemon.imageStore.Get(imgID)
}
// GetImageOnBuild looks up a Docker image referenced by `name`.
func (daemon *Daemon) GetImageOnBuild(name string) (builder.Image, error) {
img, err := daemon.GetImage(name)
if err != nil {
return nil, err
}
return img, nil
}

View File

@ -7,7 +7,6 @@ import (
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"
@ -46,35 +45,6 @@ func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHead
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.