2018-02-07 15:52:47 -05:00
package images // import "github.com/docker/docker/daemon/images"
2017-03-27 21:36:28 -04:00
import (
2018-04-19 18:30:59 -04:00
"context"
2017-05-25 17:03:29 -04:00
"io"
2018-06-26 17:49:33 -04:00
"runtime"
2017-05-25 17:03:29 -04:00
2021-01-12 20:14:36 -05:00
"github.com/containerd/containerd/platforms"
2017-03-27 21:36:28 -04:00
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/builder"
2021-01-12 20:14:36 -05:00
"github.com/docker/docker/errdefs"
2017-03-27 21:36:28 -04:00
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
2017-08-03 20:22:00 -04:00
"github.com/docker/docker/pkg/containerfs"
2021-01-12 20:14:36 -05:00
"github.com/docker/docker/pkg/progress"
"github.com/docker/docker/pkg/streamformatter"
2017-03-27 21:36:28 -04:00
"github.com/docker/docker/pkg/stringid"
2017-11-20 11:33:20 -05:00
"github.com/docker/docker/pkg/system"
2017-03-27 21:36:28 -04:00
"github.com/docker/docker/registry"
2018-06-26 17:49:33 -04:00
specs "github.com/opencontainers/image-spec/specs-go/v1"
2017-03-27 21:36:28 -04:00
"github.com/pkg/errors"
2021-01-12 20:14:36 -05:00
"github.com/sirupsen/logrus"
2017-03-27 21:36:28 -04:00
)
2018-02-16 16:50:57 -05:00
type roLayer struct {
2017-06-19 20:15:23 -04:00
released bool
2017-05-05 18:52:11 -04:00
layerStore layer . Store
roLayer layer . Layer
2017-03-27 21:36:28 -04:00
}
2018-02-16 16:50:57 -05:00
func ( l * roLayer ) DiffID ( ) layer . DiffID {
if l . roLayer == nil {
return layer . DigestSHA256EmptyTar
2017-05-25 17:03:29 -04:00
}
2018-02-16 16:50:57 -05:00
return l . roLayer . DiffID ( )
}
2017-05-25 17:03:29 -04:00
2018-02-16 16:50:57 -05:00
func ( l * roLayer ) Release ( ) error {
if l . released {
return nil
2017-03-27 21:36:28 -04:00
}
2018-02-16 16:50:57 -05:00
if l . roLayer != nil {
metadata , err := l . layerStore . Release ( l . roLayer )
2017-07-19 17:35:23 -04:00
layer . LogReleaseMetadata ( metadata )
if err != nil {
2018-02-16 16:50:57 -05:00
return errors . Wrap ( err , "failed to release ROLayer" )
2017-07-19 17:35:23 -04:00
}
}
2018-02-16 16:50:57 -05:00
l . roLayer = nil
l . released = true
return nil
2017-05-05 18:52:11 -04:00
}
2018-02-16 16:50:57 -05:00
func ( l * roLayer ) NewRWLayer ( ) ( builder . RWLayer , error ) {
2017-05-25 17:03:29 -04:00
var chainID layer . ChainID
2018-02-16 16:50:57 -05:00
if l . roLayer != nil {
chainID = l . roLayer . ChainID ( )
2017-05-25 17:03:29 -04:00
}
2018-02-16 16:50:57 -05:00
mountID := stringid . GenerateRandomID ( )
newLayer , err := l . layerStore . CreateRWLayer ( mountID , chainID , nil )
2017-05-25 17:03:29 -04:00
if err != nil {
2018-02-16 16:50:57 -05:00
return nil , errors . Wrap ( err , "failed to create rwlayer" )
2017-05-25 17:03:29 -04:00
}
2018-02-16 16:50:57 -05:00
rwLayer := & rwLayer { layerStore : l . layerStore , rwLayer : newLayer }
fs , err := newLayer . Mount ( "" )
2017-05-25 17:03:29 -04:00
if err != nil {
2018-02-16 16:50:57 -05:00
rwLayer . Release ( )
2017-05-25 17:03:29 -04:00
return nil , err
}
2018-02-16 16:50:57 -05:00
rwLayer . fs = fs
return rwLayer , nil
2017-05-05 18:52:11 -04:00
}
2018-02-16 16:50:57 -05:00
type rwLayer struct {
released bool
layerStore layer . Store
rwLayer layer . RWLayer
fs containerfs . ContainerFS
2017-05-14 14:18:48 -04:00
}
2018-02-16 16:50:57 -05:00
func ( l * rwLayer ) Root ( ) containerfs . ContainerFS {
return l . fs
2017-05-25 17:03:29 -04:00
}
2018-02-16 16:50:57 -05:00
func ( l * rwLayer ) Commit ( ) ( builder . ROLayer , error ) {
stream , err := l . rwLayer . TarStream ( )
if err != nil {
return nil , err
2017-05-05 18:52:11 -04:00
}
2018-02-16 16:50:57 -05:00
defer stream . Close ( )
var chainID layer . ChainID
if parent := l . rwLayer . Parent ( ) ; parent != nil {
chainID = parent . ChainID ( )
2017-07-19 17:35:23 -04:00
}
2018-02-16 16:50:57 -05:00
newLayer , err := l . layerStore . Register ( stream , chainID )
2017-03-27 21:36:28 -04:00
if err != nil {
2018-02-16 16:50:57 -05:00
return nil , err
2017-03-27 21:36:28 -04:00
}
2018-02-16 16:50:57 -05:00
// TODO: An optimization would be to handle empty layers before returning
return & roLayer { layerStore : l . layerStore , roLayer : newLayer } , nil
2017-03-27 21:36:28 -04:00
}
2018-02-16 16:50:57 -05:00
func ( l * rwLayer ) Release ( ) error {
if l . released {
2017-05-05 18:52:11 -04:00
return nil
2017-03-27 21:36:28 -04:00
}
2018-02-16 16:50:57 -05:00
if l . fs != nil {
if err := l . rwLayer . Unmount ( ) ; err != nil {
return errors . Wrap ( err , "failed to unmount RWLayer" )
}
l . fs = nil
}
metadata , err := l . layerStore . ReleaseRWLayer ( l . rwLayer )
2017-05-05 18:52:11 -04:00
layer . LogReleaseMetadata ( metadata )
2017-07-19 17:35:23 -04:00
if err != nil {
2018-02-16 16:50:57 -05:00
return errors . Wrap ( err , "failed to release RWLayer" )
2017-07-19 17:35:23 -04:00
}
2018-02-16 16:50:57 -05:00
l . released = true
return nil
2017-05-05 18:52:11 -04:00
}
2017-03-27 21:36:28 -04:00
2018-02-16 16:50:57 -05:00
func newROLayerForImage ( img * image . Image , layerStore layer . Store ) ( builder . ROLayer , error ) {
2017-05-25 17:03:29 -04:00
if img == nil || img . RootFS . ChainID ( ) == "" {
2018-02-16 16:50:57 -05:00
return & roLayer { layerStore : layerStore } , nil
2017-03-27 21:36:28 -04:00
}
2017-05-05 18:52:11 -04:00
// Hold a reference to the image layer so that it can't be removed before
// it is released
2018-02-16 16:50:57 -05:00
layer , err := layerStore . Get ( img . RootFS . ChainID ( ) )
2017-05-05 18:52:11 -04:00
if err != nil {
return nil , errors . Wrapf ( err , "failed to get layer for image %s" , img . ImageID ( ) )
}
2018-02-16 16:50:57 -05:00
return & roLayer { layerStore : layerStore , roLayer : layer } , nil
2017-03-27 21:36:28 -04:00
}
// TODO: could this use the regular daemon PullImage ?
2018-06-26 17:49:33 -04:00
func ( i * ImageService ) pullForBuilder ( ctx context . Context , name string , authConfigs map [ string ] types . AuthConfig , output io . Writer , platform * specs . Platform ) ( * image . Image , error ) {
2017-03-27 21:36:28 -04:00
ref , err := reference . ParseNormalizedNamed ( name )
if err != nil {
return nil , err
}
ref = reference . TagNameOnly ( ref )
pullRegistryAuth := & types . AuthConfig { }
if len ( authConfigs ) > 0 {
2017-05-05 18:52:11 -04:00
// The request came with a full auth config, use it
2018-02-02 17:18:46 -05:00
repoInfo , err := i . registryService . ResolveRepository ( ref )
2017-03-27 21:36:28 -04:00
if err != nil {
return nil , err
}
resolvedConfig := registry . ResolveAuthConfig ( authConfigs , repoInfo . Index )
pullRegistryAuth = & resolvedConfig
}
2018-06-26 17:49:33 -04:00
if err := i . pullImageWithReference ( ctx , ref , platform , nil , pullRegistryAuth , output ) ; err != nil {
2017-03-27 21:36:28 -04:00
return nil , err
}
2021-01-12 20:14:36 -05:00
img , err := i . GetImage ( name , platform )
if errdefs . IsNotFound ( err ) && img != nil && platform != nil {
imgPlat := specs . Platform {
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 ,
)
logrus . 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." )
err = nil
}
}
return img , err
2017-03-27 21:36:28 -04:00
}
2017-05-05 18:52:11 -04: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 15:52:47 -05:00
func ( i * ImageService ) GetImageAndReleasableLayer ( ctx context . Context , refOrID string , opts backend . GetImageAndLayerOptions ) ( builder . Image , builder . ROLayer , error ) {
2018-02-23 18:29:26 -05:00
if refOrID == "" { // ie FROM scratch
2018-06-26 17:49:33 -04:00
os := runtime . GOOS
2019-05-14 18:48:17 -04:00
if runtime . GOOS == "windows" {
os = "linux"
}
2018-06-26 17:49:33 -04:00
if opts . Platform != nil {
os = opts . Platform . OS
}
if ! system . IsOSSupported ( os ) {
2017-11-20 11:33:20 -05:00
return nil , nil , system . ErrNotSupportedOperatingSystem
}
2021-03-19 10:34:08 -04:00
layer , err := newROLayerForImage ( nil , i . layerStore )
2017-05-25 17:03:29 -04:00
return nil , layer , err
}
2017-06-19 20:15:23 -04:00
if opts . PullOption != backend . PullOptionForcePull {
2020-03-19 16:54:48 -04:00
image , err := i . GetImage ( refOrID , opts . Platform )
2017-06-19 20:15:23 -04:00
if err != nil && opts . PullOption == backend . PullOptionNoPull {
return nil , nil , err
}
2017-03-27 21:36:28 -04:00
// TODO: shouldn't we error out if error is different from "not found" ?
if image != nil {
2017-11-20 11:33:20 -05:00
if ! system . IsOSSupported ( image . OperatingSystem ( ) ) {
return nil , nil , system . ErrNotSupportedOperatingSystem
}
2021-03-19 10:34:08 -04:00
layer , err := newROLayerForImage ( image , i . layerStore )
2017-05-05 18:52:11 -04:00
return image , layer , err
2017-03-27 21:36:28 -04:00
}
}
2018-06-26 17:49:33 -04:00
image , err := i . pullForBuilder ( ctx , refOrID , opts . AuthConfig , opts . Output , opts . Platform )
2017-05-05 18:52:11 -04:00
if err != nil {
return nil , nil , err
}
2017-11-20 11:33:20 -05:00
if ! system . IsOSSupported ( image . OperatingSystem ( ) ) {
return nil , nil , system . ErrNotSupportedOperatingSystem
}
2021-03-19 10:34:08 -04:00
layer , err := newROLayerForImage ( image , i . layerStore )
2017-05-05 18:52:11 -04:00
return image , layer , err
2017-03-27 21:36:28 -04:00
}
2017-05-14 14:18:48 -04: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.
2018-02-07 15:52:47 -05:00
func ( i * ImageService ) CreateImage ( config [ ] byte , parent string ) ( builder . Image , error ) {
2018-02-02 17:18:46 -05:00
id , err := i . imageStore . Create ( config )
2017-05-14 14:18:48 -04:00
if err != nil {
2017-05-25 17:03:29 -04:00
return nil , errors . Wrapf ( err , "failed to create image" )
2017-05-14 14:18:48 -04:00
}
if parent != "" {
2018-02-02 17:18:46 -05:00
if err := i . imageStore . SetParent ( id , image . ID ( parent ) ) ; err != nil {
2017-05-25 17:03:29 -04:00
return nil , errors . Wrapf ( err , "failed to set parent %s" , parent )
2017-05-14 14:18:48 -04:00
}
}
2017-05-25 17:03:29 -04:00
2018-02-02 17:18:46 -05:00
return i . imageStore . Get ( id )
2017-05-14 14:18:48 -04:00
}