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 := ®istry.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 }