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