109 lines
3.2 KiB
Go
109 lines
3.2 KiB
Go
|
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
|
||
|
}
|