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