github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/images/image_builder.go (about)

     1  package images // import "github.com/docker/docker/daemon/images"
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"runtime"
     7  
     8  	"github.com/containerd/containerd/platforms"
     9  	"github.com/docker/distribution/reference"
    10  	"github.com/docker/docker/api/types/backend"
    11  	imagetypes "github.com/docker/docker/api/types/image"
    12  	"github.com/docker/docker/api/types/registry"
    13  	"github.com/docker/docker/builder"
    14  	"github.com/docker/docker/errdefs"
    15  	"github.com/docker/docker/image"
    16  	"github.com/docker/docker/layer"
    17  	"github.com/docker/docker/pkg/progress"
    18  	"github.com/docker/docker/pkg/streamformatter"
    19  	"github.com/docker/docker/pkg/stringid"
    20  	"github.com/docker/docker/pkg/system"
    21  	registrypkg "github.com/docker/docker/registry"
    22  	specs "github.com/opencontainers/image-spec/specs-go/v1"
    23  	"github.com/pkg/errors"
    24  	"github.com/sirupsen/logrus"
    25  )
    26  
    27  type roLayer struct {
    28  	released   bool
    29  	layerStore layer.Store
    30  	roLayer    layer.Layer
    31  }
    32  
    33  func (l *roLayer) DiffID() layer.DiffID {
    34  	if l.roLayer == nil {
    35  		return layer.DigestSHA256EmptyTar
    36  	}
    37  	return l.roLayer.DiffID()
    38  }
    39  
    40  func (l *roLayer) Release() error {
    41  	if l.released {
    42  		return nil
    43  	}
    44  	if l.roLayer != nil {
    45  		metadata, err := l.layerStore.Release(l.roLayer)
    46  		layer.LogReleaseMetadata(metadata)
    47  		if err != nil {
    48  			return errors.Wrap(err, "failed to release ROLayer")
    49  		}
    50  	}
    51  	l.roLayer = nil
    52  	l.released = true
    53  	return nil
    54  }
    55  
    56  func (l *roLayer) NewRWLayer() (builder.RWLayer, error) {
    57  	var chainID layer.ChainID
    58  	if l.roLayer != nil {
    59  		chainID = l.roLayer.ChainID()
    60  	}
    61  
    62  	mountID := stringid.GenerateRandomID()
    63  	newLayer, err := l.layerStore.CreateRWLayer(mountID, chainID, nil)
    64  	if err != nil {
    65  		return nil, errors.Wrap(err, "failed to create rwlayer")
    66  	}
    67  
    68  	rwLayer := &rwLayer{layerStore: l.layerStore, rwLayer: newLayer}
    69  
    70  	fs, err := newLayer.Mount("")
    71  	if err != nil {
    72  		rwLayer.Release()
    73  		return nil, err
    74  	}
    75  
    76  	rwLayer.fs = fs
    77  
    78  	return rwLayer, nil
    79  }
    80  
    81  type rwLayer struct {
    82  	released   bool
    83  	layerStore layer.Store
    84  	rwLayer    layer.RWLayer
    85  	fs         string
    86  }
    87  
    88  func (l *rwLayer) Root() string {
    89  	return l.fs
    90  }
    91  
    92  func (l *rwLayer) Commit() (builder.ROLayer, error) {
    93  	stream, err := l.rwLayer.TarStream()
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	defer stream.Close()
    98  
    99  	var chainID layer.ChainID
   100  	if parent := l.rwLayer.Parent(); parent != nil {
   101  		chainID = parent.ChainID()
   102  	}
   103  
   104  	newLayer, err := l.layerStore.Register(stream, chainID)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	// TODO: An optimization would be to handle empty layers before returning
   109  	return &roLayer{layerStore: l.layerStore, roLayer: newLayer}, nil
   110  }
   111  
   112  func (l *rwLayer) Release() error {
   113  	if l.released {
   114  		return nil
   115  	}
   116  
   117  	if l.fs != "" {
   118  		if err := l.rwLayer.Unmount(); err != nil {
   119  			return errors.Wrap(err, "failed to unmount RWLayer")
   120  		}
   121  		l.fs = ""
   122  	}
   123  
   124  	metadata, err := l.layerStore.ReleaseRWLayer(l.rwLayer)
   125  	layer.LogReleaseMetadata(metadata)
   126  	if err != nil {
   127  		return errors.Wrap(err, "failed to release RWLayer")
   128  	}
   129  	l.released = true
   130  	return nil
   131  }
   132  
   133  func newROLayerForImage(img *image.Image, layerStore layer.Store) (builder.ROLayer, error) {
   134  	if img == nil || img.RootFS.ChainID() == "" {
   135  		return &roLayer{layerStore: layerStore}, nil
   136  	}
   137  	// Hold a reference to the image layer so that it can't be removed before
   138  	// it is released
   139  	lyr, err := layerStore.Get(img.RootFS.ChainID())
   140  	if err != nil {
   141  		return nil, errors.Wrapf(err, "failed to get layer for image %s", img.ImageID())
   142  	}
   143  	return &roLayer{layerStore: layerStore, roLayer: lyr}, nil
   144  }
   145  
   146  // TODO: could this use the regular daemon PullImage ?
   147  func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConfigs map[string]registry.AuthConfig, output io.Writer, platform *specs.Platform) (*image.Image, error) {
   148  	ref, err := reference.ParseNormalizedNamed(name)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	ref = reference.TagNameOnly(ref)
   153  
   154  	pullRegistryAuth := &registry.AuthConfig{}
   155  	if len(authConfigs) > 0 {
   156  		// The request came with a full auth config, use it
   157  		repoInfo, err := i.registryService.ResolveRepository(ref)
   158  		if err != nil {
   159  			return nil, err
   160  		}
   161  
   162  		resolvedConfig := registrypkg.ResolveAuthConfig(authConfigs, repoInfo.Index)
   163  		pullRegistryAuth = &resolvedConfig
   164  	}
   165  
   166  	if err := i.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	img, err := i.GetImage(ctx, name, imagetypes.GetImageOpts{Platform: platform})
   171  	if errdefs.IsNotFound(err) && img != nil && platform != nil {
   172  		imgPlat := specs.Platform{
   173  			OS:           img.OS,
   174  			Architecture: img.BaseImgArch(),
   175  			Variant:      img.BaseImgVariant(),
   176  		}
   177  
   178  		p := *platform
   179  		if !platforms.Only(p).Match(imgPlat) {
   180  			po := streamformatter.NewJSONProgressOutput(output, false)
   181  			progress.Messagef(po, "", `
   182  WARNING: Pulled image with specified platform (%s), but the resulting image's configured platform (%s) does not match.
   183  This is most likely caused by a bug in the build system that created the fetched image (%s).
   184  Please notify the image author to correct the configuration.`,
   185  				platforms.Format(p), platforms.Format(imgPlat), name,
   186  			)
   187  			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.")
   188  			err = nil
   189  		}
   190  	}
   191  	return img, err
   192  }
   193  
   194  // GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID.
   195  // Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent
   196  // leaking of layers.
   197  func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) {
   198  	if refOrID == "" { // ie FROM scratch
   199  		os := runtime.GOOS
   200  		if runtime.GOOS == "windows" {
   201  			os = "linux"
   202  		}
   203  		if opts.Platform != nil {
   204  			os = opts.Platform.OS
   205  		}
   206  		if !system.IsOSSupported(os) {
   207  			return nil, nil, system.ErrNotSupportedOperatingSystem
   208  		}
   209  		lyr, err := newROLayerForImage(nil, i.layerStore)
   210  		return nil, lyr, err
   211  	}
   212  
   213  	if opts.PullOption != backend.PullOptionForcePull {
   214  		img, err := i.GetImage(ctx, refOrID, imagetypes.GetImageOpts{Platform: opts.Platform})
   215  		if err != nil && opts.PullOption == backend.PullOptionNoPull {
   216  			return nil, nil, err
   217  		}
   218  		if err != nil && !errdefs.IsNotFound(err) {
   219  			return nil, nil, err
   220  		}
   221  		if img != nil {
   222  			if !system.IsOSSupported(img.OperatingSystem()) {
   223  				return nil, nil, system.ErrNotSupportedOperatingSystem
   224  			}
   225  			lyr, err := newROLayerForImage(img, i.layerStore)
   226  			return img, lyr, err
   227  		}
   228  	}
   229  
   230  	img, err := i.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.Platform)
   231  	if err != nil {
   232  		return nil, nil, err
   233  	}
   234  	if !system.IsOSSupported(img.OperatingSystem()) {
   235  		return nil, nil, system.ErrNotSupportedOperatingSystem
   236  	}
   237  	lyr, err := newROLayerForImage(img, i.layerStore)
   238  	return img, lyr, err
   239  }
   240  
   241  // CreateImage creates a new image by adding a config and ID to the image store.
   242  // This is similar to LoadImage() except that it receives JSON encoded bytes of
   243  // an image instead of a tar archive.
   244  func (i *ImageService) CreateImage(config []byte, parent string) (builder.Image, error) {
   245  	id, err := i.imageStore.Create(config)
   246  	if err != nil {
   247  		return nil, errors.Wrapf(err, "failed to create image")
   248  	}
   249  
   250  	if parent != "" {
   251  		if err := i.imageStore.SetParent(id, image.ID(parent)); err != nil {
   252  			return nil, errors.Wrapf(err, "failed to set parent %s", parent)
   253  		}
   254  	}
   255  
   256  	return i.imageStore.Get(id)
   257  }