github.com/rhatdan/docker@v0.7.7-0.20180119204836-47a0dcbcd20a/daemon/build.go (about) 1 package daemon 2 3 import ( 4 "io" 5 6 "github.com/docker/distribution/reference" 7 "github.com/docker/docker/api/types" 8 "github.com/docker/docker/api/types/backend" 9 "github.com/docker/docker/builder" 10 "github.com/docker/docker/image" 11 "github.com/docker/docker/layer" 12 "github.com/docker/docker/pkg/containerfs" 13 "github.com/docker/docker/pkg/idtools" 14 "github.com/docker/docker/pkg/stringid" 15 "github.com/docker/docker/pkg/system" 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() (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) 71 if err != nil { 72 return nil, err 73 } 74 // TODO: An optimization would 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, os 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, os, 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 if !system.IsOSSupported(opts.OS) { 176 return nil, nil, system.ErrNotSupportedOperatingSystem 177 } 178 layer, err := newReleasableLayerForImage(nil, daemon.layerStores[opts.OS]) 179 return nil, layer, err 180 } 181 182 if opts.PullOption != backend.PullOptionForcePull { 183 image, err := daemon.GetImage(refOrID) 184 if err != nil && opts.PullOption == backend.PullOptionNoPull { 185 return nil, nil, err 186 } 187 // TODO: shouldn't we error out if error is different from "not found" ? 188 if image != nil { 189 if !system.IsOSSupported(image.OperatingSystem()) { 190 return nil, nil, system.ErrNotSupportedOperatingSystem 191 } 192 layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()]) 193 return image, layer, err 194 } 195 } 196 197 image, err := daemon.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.OS) 198 if err != nil { 199 return nil, nil, err 200 } 201 if !system.IsOSSupported(image.OperatingSystem()) { 202 return nil, nil, system.ErrNotSupportedOperatingSystem 203 } 204 layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()]) 205 return image, layer, err 206 } 207 208 // CreateImage creates a new image by adding a config and ID to the image store. 209 // This is similar to LoadImage() except that it receives JSON encoded bytes of 210 // an image instead of a tar archive. 211 func (daemon *Daemon) CreateImage(config []byte, parent string) (builder.Image, error) { 212 id, err := daemon.imageStore.Create(config) 213 if err != nil { 214 return nil, errors.Wrapf(err, "failed to create image") 215 } 216 217 if parent != "" { 218 if err := daemon.imageStore.SetParent(id, image.ID(parent)); err != nil { 219 return nil, errors.Wrapf(err, "failed to set parent %s", parent) 220 } 221 } 222 223 return daemon.imageStore.Get(id) 224 } 225 226 // IDMappings returns uid/gid mappings for the builder 227 func (daemon *Daemon) IDMappings() *idtools.IDMappings { 228 return daemon.idMappings 229 }