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