github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/daemon/volumes.go (about) 1 package daemon 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/Sirupsen/logrus" 11 dockererrors "github.com/docker/docker/api/errors" 12 "github.com/docker/docker/api/types" 13 containertypes "github.com/docker/docker/api/types/container" 14 mounttypes "github.com/docker/docker/api/types/mount" 15 "github.com/docker/docker/container" 16 "github.com/docker/docker/volume" 17 "github.com/docker/docker/volume/drivers" 18 "github.com/opencontainers/runc/libcontainer/label" 19 ) 20 21 var ( 22 // ErrVolumeReadonly is used to signal an error when trying to copy data into 23 // a volume mount that is not writable. 24 ErrVolumeReadonly = errors.New("mounted volume is marked read-only") 25 ) 26 27 type mounts []container.Mount 28 29 // volumeToAPIType converts a volume.Volume to the type used by the Engine API 30 func volumeToAPIType(v volume.Volume) *types.Volume { 31 tv := &types.Volume{ 32 Name: v.Name(), 33 Driver: v.DriverName(), 34 } 35 if v, ok := v.(volume.DetailedVolume); ok { 36 tv.Labels = v.Labels() 37 tv.Options = v.Options() 38 tv.Scope = v.Scope() 39 } 40 41 return tv 42 } 43 44 // Len returns the number of mounts. Used in sorting. 45 func (m mounts) Len() int { 46 return len(m) 47 } 48 49 // Less returns true if the number of parts (a/b/c would be 3 parts) in the 50 // mount indexed by parameter 1 is less than that of the mount indexed by 51 // parameter 2. Used in sorting. 52 func (m mounts) Less(i, j int) bool { 53 return m.parts(i) < m.parts(j) 54 } 55 56 // Swap swaps two items in an array of mounts. Used in sorting 57 func (m mounts) Swap(i, j int) { 58 m[i], m[j] = m[j], m[i] 59 } 60 61 // parts returns the number of parts in the destination of a mount. Used in sorting. 62 func (m mounts) parts(i int) int { 63 return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator)) 64 } 65 66 // registerMountPoints initializes the container mount points with the configured volumes and bind mounts. 67 // It follows the next sequence to decide what to mount in each final destination: 68 // 69 // 1. Select the previously configured mount points for the containers, if any. 70 // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination. 71 // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations. 72 // 4. Cleanup old volumes that are about to be reassigned. 73 func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) { 74 binds := map[string]bool{} 75 mountPoints := map[string]*volume.MountPoint{} 76 defer func() { 77 // clean up the container mountpoints once return with error 78 if retErr != nil { 79 for _, m := range mountPoints { 80 if m.Volume == nil { 81 continue 82 } 83 daemon.volumes.Dereference(m.Volume, container.ID) 84 } 85 } 86 }() 87 88 dereferenceIfExists := func(destination string) { 89 if v, ok := mountPoints[destination]; ok { 90 logrus.Debugf("Duplicate mount point '%s'", destination) 91 if v.Volume != nil { 92 daemon.volumes.Dereference(v.Volume, container.ID) 93 } 94 } 95 } 96 97 // 1. Read already configured mount points. 98 for destination, point := range container.MountPoints { 99 mountPoints[destination] = point 100 } 101 102 // 2. Read volumes from other containers. 103 for _, v := range hostConfig.VolumesFrom { 104 containerID, mode, err := volume.ParseVolumesFrom(v) 105 if err != nil { 106 return err 107 } 108 109 c, err := daemon.GetContainer(containerID) 110 if err != nil { 111 return err 112 } 113 114 for _, m := range c.MountPoints { 115 cp := &volume.MountPoint{ 116 Name: m.Name, 117 Source: m.Source, 118 RW: m.RW && volume.ReadWrite(mode), 119 Driver: m.Driver, 120 Destination: m.Destination, 121 Propagation: m.Propagation, 122 Spec: m.Spec, 123 CopyData: false, 124 } 125 126 if len(cp.Source) == 0 { 127 v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID) 128 if err != nil { 129 return err 130 } 131 cp.Volume = v 132 } 133 dereferenceIfExists(cp.Destination) 134 mountPoints[cp.Destination] = cp 135 } 136 } 137 138 // 3. Read bind mounts 139 for _, b := range hostConfig.Binds { 140 bind, err := volume.ParseMountRaw(b, hostConfig.VolumeDriver) 141 if err != nil { 142 return err 143 } 144 145 // #10618 146 _, tmpfsExists := hostConfig.Tmpfs[bind.Destination] 147 if binds[bind.Destination] || tmpfsExists { 148 return fmt.Errorf("Duplicate mount point '%s'", bind.Destination) 149 } 150 151 if bind.Type == mounttypes.TypeVolume { 152 // create the volume 153 v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil) 154 if err != nil { 155 return err 156 } 157 bind.Volume = v 158 bind.Source = v.Path() 159 // bind.Name is an already existing volume, we need to use that here 160 bind.Driver = v.DriverName() 161 if bind.Driver == volume.DefaultDriverName { 162 setBindModeIfNull(bind) 163 } 164 } 165 166 binds[bind.Destination] = true 167 dereferenceIfExists(bind.Destination) 168 mountPoints[bind.Destination] = bind 169 } 170 171 for _, cfg := range hostConfig.Mounts { 172 mp, err := volume.ParseMountSpec(cfg) 173 if err != nil { 174 return dockererrors.NewBadRequestError(err) 175 } 176 177 if binds[mp.Destination] { 178 return fmt.Errorf("Duplicate mount point '%s'", cfg.Target) 179 } 180 181 if mp.Type == mounttypes.TypeVolume { 182 var v volume.Volume 183 if cfg.VolumeOptions != nil { 184 var driverOpts map[string]string 185 if cfg.VolumeOptions.DriverConfig != nil { 186 driverOpts = cfg.VolumeOptions.DriverConfig.Options 187 } 188 v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, driverOpts, cfg.VolumeOptions.Labels) 189 } else { 190 v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, nil, nil) 191 } 192 if err != nil { 193 return err 194 } 195 196 if err := label.Relabel(mp.Source, container.MountLabel, false); err != nil { 197 return err 198 } 199 mp.Volume = v 200 mp.Name = v.Name() 201 mp.Driver = v.DriverName() 202 203 // only use the cached path here since getting the path is not necessary right now and calling `Path()` may be slow 204 if cv, ok := v.(interface { 205 CachedPath() string 206 }); ok { 207 mp.Source = cv.CachedPath() 208 } 209 } 210 211 binds[mp.Destination] = true 212 dereferenceIfExists(mp.Destination) 213 mountPoints[mp.Destination] = mp 214 } 215 216 container.Lock() 217 218 // 4. Cleanup old volumes that are about to be reassigned. 219 for _, m := range mountPoints { 220 if m.BackwardsCompatible() { 221 if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil { 222 daemon.volumes.Dereference(mp.Volume, container.ID) 223 } 224 } 225 } 226 container.MountPoints = mountPoints 227 228 container.Unlock() 229 230 return nil 231 } 232 233 // lazyInitializeVolume initializes a mountpoint's volume if needed. 234 // This happens after a daemon restart. 235 func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error { 236 if len(m.Driver) > 0 && m.Volume == nil { 237 v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID) 238 if err != nil { 239 return err 240 } 241 m.Volume = v 242 } 243 return nil 244 } 245 246 func backportMountSpec(container *container.Container) error { 247 for target, m := range container.MountPoints { 248 if m.Spec.Type != "" { 249 // if type is set on even one mount, no need to migrate 250 return nil 251 } 252 if m.Name != "" { 253 m.Type = mounttypes.TypeVolume 254 m.Spec.Type = mounttypes.TypeVolume 255 256 // make sure this is not an anyonmous volume before setting the spec source 257 if _, exists := container.Config.Volumes[target]; !exists { 258 m.Spec.Source = m.Name 259 } 260 if container.HostConfig.VolumeDriver != "" { 261 m.Spec.VolumeOptions = &mounttypes.VolumeOptions{ 262 DriverConfig: &mounttypes.Driver{Name: container.HostConfig.VolumeDriver}, 263 } 264 } 265 if strings.Contains(m.Mode, "nocopy") { 266 if m.Spec.VolumeOptions == nil { 267 m.Spec.VolumeOptions = &mounttypes.VolumeOptions{} 268 } 269 m.Spec.VolumeOptions.NoCopy = true 270 } 271 } else { 272 m.Type = mounttypes.TypeBind 273 m.Spec.Type = mounttypes.TypeBind 274 m.Spec.Source = m.Source 275 if m.Propagation != "" { 276 m.Spec.BindOptions = &mounttypes.BindOptions{ 277 Propagation: m.Propagation, 278 } 279 } 280 } 281 282 m.Spec.Target = m.Destination 283 if !m.RW { 284 m.Spec.ReadOnly = true 285 } 286 } 287 return container.ToDiskLocking() 288 } 289 290 func (daemon *Daemon) traverseLocalVolumes(fn func(volume.Volume) error) error { 291 localVolumeDriver, err := volumedrivers.GetDriver(volume.DefaultDriverName) 292 if err != nil { 293 return fmt.Errorf("can't retrieve local volume driver: %v", err) 294 } 295 vols, err := localVolumeDriver.List() 296 if err != nil { 297 return fmt.Errorf("can't retrieve local volumes: %v", err) 298 } 299 300 for _, v := range vols { 301 name := v.Name() 302 _, err := daemon.volumes.Get(name) 303 if err != nil { 304 logrus.Warnf("failed to retrieve volume %s from store: %v", name, err) 305 } 306 307 err = fn(v) 308 if err != nil { 309 return err 310 } 311 } 312 313 return nil 314 }