github.com/kobeld/docker@v1.12.0-rc1/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/docker/docker/container" 11 "github.com/docker/docker/volume" 12 "github.com/docker/engine-api/types" 13 containertypes "github.com/docker/engine-api/types/container" 14 ) 15 16 var ( 17 // ErrVolumeReadonly is used to signal an error when trying to copy data into 18 // a volume mount that is not writable. 19 ErrVolumeReadonly = errors.New("mounted volume is marked read-only") 20 ) 21 22 type mounts []container.Mount 23 24 // volumeToAPIType converts a volume.Volume to the type used by the remote API 25 func volumeToAPIType(v volume.Volume) *types.Volume { 26 tv := &types.Volume{ 27 Name: v.Name(), 28 Driver: v.DriverName(), 29 } 30 if v, ok := v.(volume.LabeledVolume); ok { 31 tv.Labels = v.Labels() 32 } 33 34 if v, ok := v.(volume.ScopedVolume); ok { 35 tv.Scope = v.Scope() 36 } 37 return tv 38 } 39 40 // Len returns the number of mounts. Used in sorting. 41 func (m mounts) Len() int { 42 return len(m) 43 } 44 45 // Less returns true if the number of parts (a/b/c would be 3 parts) in the 46 // mount indexed by parameter 1 is less than that of the mount indexed by 47 // parameter 2. Used in sorting. 48 func (m mounts) Less(i, j int) bool { 49 return m.parts(i) < m.parts(j) 50 } 51 52 // Swap swaps two items in an array of mounts. Used in sorting 53 func (m mounts) Swap(i, j int) { 54 m[i], m[j] = m[j], m[i] 55 } 56 57 // parts returns the number of parts in the destination of a mount. Used in sorting. 58 func (m mounts) parts(i int) int { 59 return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator)) 60 } 61 62 // registerMountPoints initializes the container mount points with the configured volumes and bind mounts. 63 // It follows the next sequence to decide what to mount in each final destination: 64 // 65 // 1. Select the previously configured mount points for the containers, if any. 66 // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination. 67 // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations. 68 // 4. Cleanup old volumes that are about to be reassigned. 69 func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) { 70 binds := map[string]bool{} 71 mountPoints := map[string]*volume.MountPoint{} 72 defer func() { 73 // clean up the container mountpoints once return with error 74 if retErr != nil { 75 for _, m := range mountPoints { 76 if m.Volume == nil { 77 continue 78 } 79 daemon.volumes.Dereference(m.Volume, container.ID) 80 } 81 } 82 }() 83 84 // 1. Read already configured mount points. 85 for name, point := range container.MountPoints { 86 mountPoints[name] = point 87 } 88 89 // 2. Read volumes from other containers. 90 for _, v := range hostConfig.VolumesFrom { 91 containerID, mode, err := volume.ParseVolumesFrom(v) 92 if err != nil { 93 return err 94 } 95 96 c, err := daemon.GetContainer(containerID) 97 if err != nil { 98 return err 99 } 100 101 for _, m := range c.MountPoints { 102 cp := &volume.MountPoint{ 103 Name: m.Name, 104 Source: m.Source, 105 RW: m.RW && volume.ReadWrite(mode), 106 Driver: m.Driver, 107 Destination: m.Destination, 108 Propagation: m.Propagation, 109 Named: m.Named, 110 } 111 112 if len(cp.Source) == 0 { 113 v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID) 114 if err != nil { 115 return err 116 } 117 cp.Volume = v 118 } 119 120 mountPoints[cp.Destination] = cp 121 } 122 } 123 124 // 3. Read bind mounts 125 for _, b := range hostConfig.Binds { 126 // #10618 127 bind, err := volume.ParseMountSpec(b, hostConfig.VolumeDriver) 128 if err != nil { 129 return err 130 } 131 132 if binds[bind.Destination] { 133 return fmt.Errorf("Duplicate mount point '%s'", bind.Destination) 134 } 135 136 if len(bind.Name) > 0 { 137 // create the volume 138 v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil) 139 if err != nil { 140 return err 141 } 142 bind.Volume = v 143 bind.Source = v.Path() 144 // bind.Name is an already existing volume, we need to use that here 145 bind.Driver = v.DriverName() 146 bind.Named = true 147 if bind.Driver == "local" { 148 bind = setBindModeIfNull(bind) 149 } 150 } 151 152 binds[bind.Destination] = true 153 mountPoints[bind.Destination] = bind 154 } 155 156 container.Lock() 157 158 // 4. Cleanup old volumes that are about to be reassigned. 159 for _, m := range mountPoints { 160 if m.BackwardsCompatible() { 161 if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil { 162 daemon.volumes.Dereference(mp.Volume, container.ID) 163 } 164 } 165 } 166 container.MountPoints = mountPoints 167 168 container.Unlock() 169 170 return nil 171 } 172 173 // lazyInitializeVolume initializes a mountpoint's volume if needed. 174 // This happens after a daemon restart. 175 func (daemon *Daemon) lazyInitializeVolume(containerID string, m *volume.MountPoint) error { 176 if len(m.Driver) > 0 && m.Volume == nil { 177 v, err := daemon.volumes.GetWithRef(m.Name, m.Driver, containerID) 178 if err != nil { 179 return err 180 } 181 m.Volume = v 182 } 183 return nil 184 }