blob: 1b92ced179e9ff76b4b17991740f8c7fd3ed986e [file] [log] [blame]
package dockerfile
import (
"strconv"
"strings"
"sync"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/builder"
"github.com/docker/docker/builder/remotecontext"
"github.com/pkg/errors"
)
// imageContexts is a helper for stacking up built image rootfs and reusing
// them as contexts
type imageContexts struct {
b *Builder
list []*imageMount
byName map[string]*imageMount
cache *pathCache
currentName string
}
func (ic *imageContexts) new(name string, increment bool) (*imageMount, error) {
im := &imageMount{ic: ic}
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)
}
ic.byName[name] = im
}
if increment {
ic.list = append(ic.list, im)
}
ic.currentName = name
return im, 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
}
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("invalid from flag value %d%s", i, extraMsg)
}
return nil
}
func (ic *imageContexts) get(indexOrName string) (*imageMount, error) {
index, err := strconv.Atoi(indexOrName)
if err == nil {
if err := ic.validate(index); err != nil {
return nil, err
}
return ic.list[index], nil
}
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)
}
return im, nil
}
func (ic *imageContexts) unmount() (retErr error) {
for _, im := range ic.list {
if err := im.unmount(); err != nil {
logrus.Error(err)
retErr = err
}
}
for _, im := range ic.byName {
if err := im.unmount(); err != nil {
logrus.Error(err)
retErr = err
}
}
return
}
func (ic *imageContexts) isCurrentTarget(target string) bool {
if target == "" {
return false
}
return strings.EqualFold(ic.currentName, target)
}
func (ic *imageContexts) getCache(id, path string) (interface{}, bool) {
if ic.cache != nil {
if id == "" {
return nil, false
}
return ic.cache.get(id + path)
}
return nil, false
}
func (ic *imageContexts) setCache(id, path string, v interface{}) {
if ic.cache != nil {
ic.cache.set(id+path, v)
}
}
// imageMount is a reference for getting access to a buildcontext that is backed
// by an existing image
type imageMount struct {
id string
ctx builder.Context
release func() error
ic *imageContexts
runConfig *container.Config
}
func (im *imageMount) context() (builder.Context, error) {
if im.ctx == nil {
if im.id == "" {
return nil, errors.Errorf("could not copy from empty context")
}
p, release, err := im.ic.b.docker.MountImage(im.id)
if err != nil {
return nil, errors.Wrapf(err, "failed to mount %s", im.id)
}
ctx, err := remotecontext.NewLazyContext(p)
if err != nil {
return nil, errors.Wrapf(err, "failed to create lazycontext for %s", p)
}
im.release = release
im.ctx = ctx
}
return im.ctx, 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
}
return nil
}
func (im *imageMount) ImageID() string {
return im.id
}
func (im *imageMount) RunConfig() *container.Config {
return im.runConfig
}
type pathCache struct {
mu sync.Mutex
items map[string]interface{}
}
func (c *pathCache) set(k string, v interface{}) {
c.mu.Lock()
if c.items == nil {
c.items = make(map[string]interface{})
}
c.items[k] = v
c.mu.Unlock()
}
func (c *pathCache) get(k string) (interface{}, bool) {
c.mu.Lock()
if c.items == nil {
c.mu.Unlock()
return nil, false
}
v, ok := c.items[k]
c.mu.Unlock()
return v, ok
}