github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/daemon/images/image_builder.go (about)

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