2018-02-07 20:52:47 +00:00
package images // import "github.com/docker/docker/daemon/images"
2017-03-28 01:36:28 +00:00
import (
2018-04-19 22:30:59 +00:00
"context"
2023-09-06 09:50:03 +00:00
"fmt"
2017-05-25 21:03:29 +00:00
"io"
2018-06-26 21:49:33 +00:00
"runtime"
2017-05-25 21:03:29 +00:00
2021-01-13 01:14:36 +00:00
"github.com/containerd/containerd/platforms"
2023-09-13 15:41:45 +00:00
"github.com/containerd/log"
2023-08-30 16:31:46 +00:00
"github.com/distribution/reference"
2017-03-28 01:36:28 +00:00
"github.com/docker/docker/api/types/backend"
2022-03-03 09:28:39 +00:00
"github.com/docker/docker/api/types/registry"
2017-03-28 01:36:28 +00:00
"github.com/docker/docker/builder"
2021-01-13 01:14:36 +00:00
"github.com/docker/docker/errdefs"
2017-03-28 01:36:28 +00:00
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
2021-01-13 01:14:36 +00:00
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/pkg/streamformatter"
2017-03-28 01:36:28 +00:00
"github.com/docker/docker/pkg/stringid"
2022-03-03 09:28:39 +00:00
registrypkg "github.com/docker/docker/registry"
2023-04-13 12:19:51 +00:00
"github.com/opencontainers/go-digest"
2023-05-08 09:57:52 +00:00
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2017-03-28 01:36:28 +00:00
"github.com/pkg/errors"
)
2018-02-16 21:50:57 +00:00
type roLayer struct {
2017-06-20 00:15:23 +00:00
released bool
2017-05-05 22:52:11 +00:00
layerStore layer . Store
roLayer layer . Layer
2017-03-28 01:36:28 +00:00
}
2023-04-13 12:19:51 +00:00
func ( l * roLayer ) ContentStoreDigest ( ) digest . Digest {
return ""
}
2018-02-16 21:50:57 +00:00
func ( l * roLayer ) DiffID ( ) layer . DiffID {
if l . roLayer == nil {
return layer . DigestSHA256EmptyTar
2017-05-25 21:03:29 +00:00
}
2018-02-16 21:50:57 +00:00
return l . roLayer . DiffID ( )
}
2017-05-25 21:03:29 +00:00
2018-02-16 21:50:57 +00:00
func ( l * roLayer ) Release ( ) error {
if l . released {
return nil
2017-03-28 01:36:28 +00:00
}
2018-02-16 21:50:57 +00:00
if l . roLayer != nil {
metadata , err := l . layerStore . Release ( l . roLayer )
2017-07-19 21:35:23 +00:00
layer . LogReleaseMetadata ( metadata )
if err != nil {
2018-02-16 21:50:57 +00:00
return errors . Wrap ( err , "failed to release ROLayer" )
2017-07-19 21:35:23 +00:00
}
}
2018-02-16 21:50:57 +00:00
l . roLayer = nil
l . released = true
return nil
2017-05-05 22:52:11 +00:00
}
2018-02-16 21:50:57 +00:00
func ( l * roLayer ) NewRWLayer ( ) ( builder . RWLayer , error ) {
2017-05-25 21:03:29 +00:00
var chainID layer . ChainID
2018-02-16 21:50:57 +00:00
if l . roLayer != nil {
chainID = l . roLayer . ChainID ( )
2017-05-25 21:03:29 +00:00
}
2018-02-16 21:50:57 +00:00
mountID := stringid . GenerateRandomID ( )
newLayer , err := l . layerStore . CreateRWLayer ( mountID , chainID , nil )
2017-05-25 21:03:29 +00:00
if err != nil {
2018-02-16 21:50:57 +00:00
return nil , errors . Wrap ( err , "failed to create rwlayer" )
2017-05-25 21:03:29 +00:00
}
2018-02-16 21:50:57 +00:00
rwLayer := & rwLayer { layerStore : l . layerStore , rwLayer : newLayer }
fs , err := newLayer . Mount ( "" )
2017-05-25 21:03:29 +00:00
if err != nil {
2018-02-16 21:50:57 +00:00
rwLayer . Release ( )
2017-05-25 21:03:29 +00:00
return nil , err
}
2018-02-16 21:50:57 +00:00
rwLayer . fs = fs
return rwLayer , nil
2017-05-05 22:52:11 +00:00
}
2018-02-16 21:50:57 +00:00
type rwLayer struct {
released bool
layerStore layer . Store
rwLayer layer . RWLayer
2022-09-23 18:21:31 +00:00
fs string
2017-05-14 18:18:48 +00:00
}
2022-09-23 18:21:31 +00:00
func ( l * rwLayer ) Root ( ) string {
2018-02-16 21:50:57 +00:00
return l . fs
2017-05-25 21:03:29 +00:00
}
2018-02-16 21:50:57 +00:00
func ( l * rwLayer ) Commit ( ) ( builder . ROLayer , error ) {
stream , err := l . rwLayer . TarStream ( )
if err != nil {
return nil , err
2017-05-05 22:52:11 +00:00
}
2018-02-16 21:50:57 +00:00
defer stream . Close ( )
var chainID layer . ChainID
if parent := l . rwLayer . Parent ( ) ; parent != nil {
chainID = parent . ChainID ( )
2017-07-19 21:35:23 +00:00
}
2018-02-16 21:50:57 +00:00
newLayer , err := l . layerStore . Register ( stream , chainID )
2017-03-28 01:36:28 +00:00
if err != nil {
2018-02-16 21:50:57 +00:00
return nil , err
2017-03-28 01:36:28 +00:00
}
2018-02-16 21:50:57 +00:00
// TODO: An optimization would be to handle empty layers before returning
return & roLayer { layerStore : l . layerStore , roLayer : newLayer } , nil
2017-03-28 01:36:28 +00:00
}
2018-02-16 21:50:57 +00:00
func ( l * rwLayer ) Release ( ) error {
if l . released {
2017-05-05 22:52:11 +00:00
return nil
2017-03-28 01:36:28 +00:00
}
2018-02-16 21:50:57 +00:00
2022-09-23 16:25:19 +00:00
if l . fs != "" {
2018-02-16 21:50:57 +00:00
if err := l . rwLayer . Unmount ( ) ; err != nil {
return errors . Wrap ( err , "failed to unmount RWLayer" )
}
2022-09-23 16:25:19 +00:00
l . fs = ""
2018-02-16 21:50:57 +00:00
}
metadata , err := l . layerStore . ReleaseRWLayer ( l . rwLayer )
2017-05-05 22:52:11 +00:00
layer . LogReleaseMetadata ( metadata )
2017-07-19 21:35:23 +00:00
if err != nil {
2018-02-16 21:50:57 +00:00
return errors . Wrap ( err , "failed to release RWLayer" )
2017-07-19 21:35:23 +00:00
}
2018-02-16 21:50:57 +00:00
l . released = true
return nil
2017-05-05 22:52:11 +00:00
}
2017-03-28 01:36:28 +00:00
2018-02-16 21:50:57 +00:00
func newROLayerForImage ( img * image . Image , layerStore layer . Store ) ( builder . ROLayer , error ) {
2017-05-25 21:03:29 +00:00
if img == nil || img . RootFS . ChainID ( ) == "" {
2018-02-16 21:50:57 +00:00
return & roLayer { layerStore : layerStore } , nil
2017-03-28 01:36:28 +00:00
}
2017-05-05 22:52:11 +00:00
// Hold a reference to the image layer so that it can't be removed before
// it is released
2022-07-18 11:22:12 +00:00
lyr , err := layerStore . Get ( img . RootFS . ChainID ( ) )
2017-05-05 22:52:11 +00:00
if err != nil {
return nil , errors . Wrapf ( err , "failed to get layer for image %s" , img . ImageID ( ) )
}
2022-07-18 11:22:12 +00:00
return & roLayer { layerStore : layerStore , roLayer : lyr } , nil
2017-03-28 01:36:28 +00:00
}
// TODO: could this use the regular daemon PullImage ?
2023-05-08 09:57:52 +00:00
func ( i * ImageService ) pullForBuilder ( ctx context . Context , name string , authConfigs map [ string ] registry . AuthConfig , output io . Writer , platform * ocispec . Platform ) ( * image . Image , error ) {
2017-03-28 01:36:28 +00:00
ref , err := reference . ParseNormalizedNamed ( name )
if err != nil {
return nil , err
}
ref = reference . TagNameOnly ( ref )
2022-03-03 09:28:39 +00:00
pullRegistryAuth := & registry . AuthConfig { }
2017-03-28 01:36:28 +00:00
if len ( authConfigs ) > 0 {
2017-05-05 22:52:11 +00:00
// The request came with a full auth config, use it
2018-02-02 22:18:46 +00:00
repoInfo , err := i . registryService . ResolveRepository ( ref )
2017-03-28 01:36:28 +00:00
if err != nil {
return nil , err
}
2022-03-03 09:28:39 +00:00
resolvedConfig := registrypkg . ResolveAuthConfig ( authConfigs , repoInfo . Index )
2017-03-28 01:36:28 +00:00
pullRegistryAuth = & resolvedConfig
}
2018-06-26 21:49:33 +00:00
if err := i . pullImageWithReference ( ctx , ref , platform , nil , pullRegistryAuth , output ) ; err != nil {
2017-03-28 01:36:28 +00:00
return nil , err
}
2021-01-13 01:14:36 +00:00
2024-01-20 15:01:43 +00:00
img , err := i . GetImage ( ctx , name , backend . GetImageOpts { Platform : platform } )
2021-01-13 01:14:36 +00:00
if errdefs . IsNotFound ( err ) && img != nil && platform != nil {
2023-05-08 09:57:52 +00:00
imgPlat := ocispec . Platform {
2021-01-13 01:14:36 +00:00
OS : img . OS ,
Architecture : img . BaseImgArch ( ) ,
Variant : img . BaseImgVariant ( ) ,
}
p := * platform
if ! platforms . Only ( p ) . Match ( imgPlat ) {
po := streamformatter . NewJSONProgressOutput ( output , false )
progress . Messagef ( po , "" , `
WARNING : Pulled image with specified platform ( % s ) , but the resulting image ' s configured platform ( % s ) does not match .
This is most likely caused by a bug in the build system that created the fetched image ( % s ) .
Please notify the image author to correct the configuration . ` ,
platforms . Format ( p ) , platforms . Format ( imgPlat ) , name ,
)
2023-06-23 00:33:17 +00:00
log . G ( ctx ) . WithError ( err ) . WithField ( "image" , name ) . Warn ( "Ignoring error about platform mismatch where the manifest list points to an image whose configuration does not match the platform in the manifest." )
2021-01-13 01:14:36 +00:00
err = nil
}
}
return img , err
2017-03-28 01:36:28 +00:00
}
2017-05-05 22:52:11 +00:00
// GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID.
// Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent
// leaking of layers.
2018-02-07 20:52:47 +00:00
func ( i * ImageService ) GetImageAndReleasableLayer ( ctx context . Context , refOrID string , opts backend . GetImageAndLayerOptions ) ( builder . Image , builder . ROLayer , error ) {
2023-09-06 09:50:03 +00:00
if refOrID == "" { // FROM scratch
2019-05-14 22:48:17 +00:00
if runtime . GOOS == "windows" {
2023-09-06 09:50:03 +00:00
return nil , nil , fmt . Errorf ( ` "FROM scratch" is not supported on Windows ` )
2019-05-14 22:48:17 +00:00
}
2018-06-26 21:49:33 +00:00
if opts . Platform != nil {
2023-09-06 09:50:03 +00:00
if err := image . CheckOS ( opts . Platform . OS ) ; err != nil {
return nil , nil , err
}
2017-11-20 16:33:20 +00:00
}
2022-07-18 11:22:12 +00:00
lyr , err := newROLayerForImage ( nil , i . layerStore )
return nil , lyr , err
2017-05-25 21:03:29 +00:00
}
2017-06-20 00:15:23 +00:00
if opts . PullOption != backend . PullOptionForcePull {
2024-01-20 15:01:43 +00:00
img , err := i . GetImage ( ctx , refOrID , backend . GetImageOpts { Platform : opts . Platform } )
2017-06-20 00:15:23 +00:00
if err != nil && opts . PullOption == backend . PullOptionNoPull {
return nil , nil , err
}
2022-08-09 12:42:50 +00:00
if err != nil && ! errdefs . IsNotFound ( err ) {
return nil , nil , err
}
2022-07-18 11:22:12 +00:00
if img != nil {
2023-09-06 10:35:50 +00:00
if err := image . CheckOS ( img . OperatingSystem ( ) ) ; err != nil {
return nil , nil , err
2017-11-20 16:33:20 +00:00
}
2022-07-18 11:22:12 +00:00
lyr , err := newROLayerForImage ( img , i . layerStore )
return img , lyr , err
2017-03-28 01:36:28 +00:00
}
}
2022-07-18 11:22:12 +00:00
img , err := i . pullForBuilder ( ctx , refOrID , opts . AuthConfig , opts . Output , opts . Platform )
2017-05-05 22:52:11 +00:00
if err != nil {
return nil , nil , err
}
2023-09-06 10:35:50 +00:00
if err := image . CheckOS ( img . OperatingSystem ( ) ) ; err != nil {
return nil , nil , err
2017-11-20 16:33:20 +00:00
}
2022-07-18 11:22:12 +00:00
lyr , err := newROLayerForImage ( img , i . layerStore )
return img , lyr , err
2017-03-28 01:36:28 +00:00
}
2017-05-14 18:18:48 +00:00
// CreateImage creates a new image by adding a config and ID to the image store.
// This is similar to LoadImage() except that it receives JSON encoded bytes of
// an image instead of a tar archive.
2023-04-13 12:19:51 +00:00
func ( i * ImageService ) CreateImage ( ctx context . Context , config [ ] byte , parent string , _ digest . Digest ) ( builder . Image , error ) {
2018-02-02 22:18:46 +00:00
id , err := i . imageStore . Create ( config )
2017-05-14 18:18:48 +00:00
if err != nil {
2017-05-25 21:03:29 +00:00
return nil , errors . Wrapf ( err , "failed to create image" )
2017-05-14 18:18:48 +00:00
}
if parent != "" {
2018-02-02 22:18:46 +00:00
if err := i . imageStore . SetParent ( id , image . ID ( parent ) ) ; err != nil {
2017-05-25 21:03:29 +00:00
return nil , errors . Wrapf ( err , "failed to set parent %s" , parent )
2017-05-14 18:18:48 +00:00
}
}
2023-12-01 09:24:25 +00:00
if err := i . imageStore . SetBuiltLocally ( id ) ; err != nil {
return nil , errors . Wrapf ( err , "failed to mark image %s as built locally" , id )
}
2017-05-25 21:03:29 +00:00
2018-02-02 22:18:46 +00:00
return i . imageStore . Get ( id )
2017-05-14 18:18:48 +00:00
}