| package dockerfile // import "github.com/docker/docker/builder/dockerfile" | |
| import ( | |
| "context" | |
| "runtime" | |
| "github.com/containerd/containerd/platforms" | |
| "github.com/docker/docker/api/types/backend" | |
| "github.com/docker/docker/builder" | |
| dockerimage "github.com/docker/docker/image" | |
| ocispec "github.com/opencontainers/image-spec/specs-go/v1" | |
| "github.com/pkg/errors" | |
| "github.com/sirupsen/logrus" | |
| ) | |
| type getAndMountFunc func(context.Context, string, bool, *ocispec.Platform) (builder.Image, builder.ROLayer, error) | |
| // imageSources mounts images and provides a cache for mounted images. It tracks | |
| // all images so they can be unmounted at the end of the build. | |
| type imageSources struct { | |
| byImageID map[string]*imageMount | |
| mounts []*imageMount | |
| getImage getAndMountFunc | |
| } | |
| func newImageSources(options builderOptions) *imageSources { | |
| getAndMount := func(ctx context.Context, idOrRef string, localOnly bool, platform *ocispec.Platform) (builder.Image, builder.ROLayer, error) { | |
| pullOption := backend.PullOptionNoPull | |
| if !localOnly { | |
| if options.Options.PullParent { | |
| pullOption = backend.PullOptionForcePull | |
| } else { | |
| pullOption = backend.PullOptionPreferLocal | |
| } | |
| } | |
| return options.Backend.GetImageAndReleasableLayer(ctx, idOrRef, backend.GetImageAndLayerOptions{ | |
| PullOption: pullOption, | |
| AuthConfig: options.Options.AuthConfigs, | |
| Output: options.ProgressWriter.Output, | |
| Platform: platform, | |
| }) | |
| } | |
| return &imageSources{ | |
| byImageID: make(map[string]*imageMount), | |
| getImage: getAndMount, | |
| } | |
| } | |
| func (m *imageSources) Get(ctx context.Context, idOrRef string, localOnly bool, platform *ocispec.Platform) (*imageMount, error) { | |
| if im, ok := m.byImageID[idOrRef]; ok { | |
| return im, nil | |
| } | |
| image, layer, err := m.getImage(ctx, idOrRef, localOnly, platform) | |
| if err != nil { | |
| return nil, err | |
| } | |
| im := newImageMount(image, layer) | |
| m.Add(im, platform) | |
| return im, nil | |
| } | |
| func (m *imageSources) Unmount() (retErr error) { | |
| for _, im := range m.mounts { | |
| if err := im.unmount(); err != nil { | |
| logrus.Error(err) | |
| retErr = err | |
| } | |
| } | |
| return | |
| } | |
| func (m *imageSources) Add(im *imageMount, platform *ocispec.Platform) { | |
| switch im.image { | |
| case nil: | |
| // Set the platform for scratch images | |
| if platform == nil { | |
| p := platforms.DefaultSpec() | |
| platform = &p | |
| } | |
| // Windows does not support scratch except for LCOW | |
| os := platform.OS | |
| if runtime.GOOS == "windows" { | |
| os = "linux" | |
| } | |
| im.image = &dockerimage.Image{V1Image: dockerimage.V1Image{ | |
| OS: os, | |
| Architecture: platform.Architecture, | |
| Variant: platform.Variant, | |
| }} | |
| default: | |
| m.byImageID[im.image.ImageID()] = im | |
| } | |
| m.mounts = append(m.mounts, im) | |
| } | |
| // imageMount is a reference to an image that can be used as a builder.Source | |
| type imageMount struct { | |
| image builder.Image | |
| layer builder.ROLayer | |
| } | |
| func newImageMount(image builder.Image, layer builder.ROLayer) *imageMount { | |
| im := &imageMount{image: image, layer: layer} | |
| return im | |
| } | |
| func (im *imageMount) unmount() error { | |
| if im.layer == nil { | |
| return nil | |
| } | |
| if err := im.layer.Release(); err != nil { | |
| return errors.Wrapf(err, "failed to unmount previous build image %s", im.image.ImageID()) | |
| } | |
| im.layer = nil | |
| return nil | |
| } | |
| func (im *imageMount) Image() builder.Image { | |
| return im.image | |
| } | |
| func (im *imageMount) NewRWLayer() (builder.RWLayer, error) { | |
| return im.layer.NewRWLayer() | |
| } | |
| func (im *imageMount) ImageID() string { | |
| return im.image.ImageID() | |
| } |