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