// volumeExists returns if the volume is still present in the driver. // An error is returned if there was an issue communicating with the driver. func volumeExists(v volume.Volume) (bool, error) { exists, err := lookupVolume(v.DriverName(), v.Name()) if err != nil { return false, err } return exists != nil, nil }
// volumeToAPIType converts a volume.Volume to the type used by the remote API func volumeToAPIType(v volume.Volume) *types.Volume { return &types.Volume{ Name: v.Name(), Driver: v.DriverName(), Mountpoint: v.Path(), } }
func removeVolume(v volume.Volume) error { vd, err := getVolumeDriver(v.DriverName()) if err != nil { return nil } return vd.Remove(v) }
// Remove removes the requested volume. A volume is not removed if the usage count is > 0 func (s *VolumeStore) Remove(v volume.Volume) error { name := normaliseVolumeName(v.Name()) s.locks.Lock(name) defer s.locks.Unlock(name) logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name) vc, exists := s.get(name) if !exists { return ErrNoSuchVolume } if vc.count > 0 { return ErrVolumeInUse } vd, err := volumedrivers.GetDriver(vc.DriverName()) if err != nil { return err } if err := vd.Remove(vc.Volume); err != nil { return err } s.remove(name) return nil }
func (container *Container) addMountPointWithVolume(destination string, vol volume.Volume, rw bool) { container.MountPoints[destination] = &mountPoint{ Name: vol.Name(), Driver: vol.DriverName(), Destination: destination, RW: rw, Volume: vol, } }
// AddMountPointWithVolume adds a new mount point configured with a volume to the container. func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) { container.MountPoints[destination] = &volume.MountPoint{ Name: vol.Name(), Driver: vol.DriverName(), Destination: destination, RW: rw, Volume: vol, CopyData: volume.DefaultCopyMode, } }
// volumeToAPIType converts a volume.Volume to the type used by the remote API func volumeToAPIType(v volume.Volume) *types.Volume { tv := &types.Volume{ Name: v.Name(), Driver: v.DriverName(), } if v, ok := v.(interface { Labels() map[string]string }); ok { tv.Labels = v.Labels() } return tv }
// Decrement decrements the usage count of the passed in volume by 1 func (s *volumeStore) Decrement(v volume.Volume) { s.mu.Lock() defer s.mu.Unlock() logrus.Debugf("Decrementing volume reference: driver %s, name %s", v.DriverName(), v.Name()) vc, exists := s.vols[v.Name()] if !exists { return } vc.count-- return }
// Increment increments the usage count of the passed in volume by 1 func (s *VolumeStore) Increment(v volume.Volume) { s.mu.Lock() defer s.mu.Unlock() logrus.Debugf("Incrementing volume reference: driver %s, name %s", v.DriverName(), v.Name()) vc, exists := s.vols[v.Name()] if !exists { s.vols[v.Name()] = &volumeCounter{v, 1} return } vc.count++ }
// Increment increments the usage count of the passed in volume by 1 func (s *VolumeStore) Increment(v volume.Volume) { name := normaliseVolumeName(v.Name()) s.locks.Lock(name) defer s.locks.Unlock(name) logrus.Debugf("Incrementing volume reference: driver %s, name %s", v.DriverName(), v.Name()) vc, exists := s.get(name) if !exists { s.set(name, &volumeCounter{v, 1}) return } vc.count++ }
// volumeToAPIType converts a volume.Volume to the type used by the remote API func volumeToAPIType(v volume.Volume) *types.Volume { tv := &types.Volume{ Name: v.Name(), Driver: v.DriverName(), } if v, ok := v.(volume.DetailedVolume); ok { tv.Labels = v.Labels() tv.Options = v.Options() tv.Scope = v.Scope() } return tv }
// Decrement decrements the usage count of the passed in volume by 1 func (s *VolumeStore) Decrement(v volume.Volume) { s.mu.Lock() defer s.mu.Unlock() name := normaliseVolumeName(v.Name()) logrus.Debugf("Decrementing volume reference: driver %s, name %s", v.DriverName(), name) vc, exists := s.vols[name] if !exists { return } if vc.count == 0 { return } vc.count-- }
// Decrement decrements the usage count of the passed in volume by 1 func (s *VolumeStore) Decrement(v volume.Volume) { name := normaliseVolumeName(v.Name()) s.locks.Lock(name) defer s.locks.Unlock(name) logrus.Debugf("Decrementing volume reference: driver %s, name %s", v.DriverName(), v.Name()) vc, exists := s.get(name) if !exists { return } if vc.count == 0 { return } vc.count-- }
// volumeToAPIType converts a volume.Volume to the type used by the remote API func volumeToAPIType(v volume.Volume) *types.Volume { tv := &types.Volume{ Name: v.Name(), Driver: v.DriverName(), Size: -1, RefCount: -1, } if v, ok := v.(volume.LabeledVolume); ok { tv.Labels = v.Labels() } if v, ok := v.(volume.ScopedVolume); ok { tv.Scope = v.Scope() } return tv }
// volumeExists returns if the volume is still present in the driver. // An error is returned if there was an issue communicating with the driver. func volumeExists(v volume.Volume) (bool, error) { vd, err := volumedrivers.GetDriver(v.DriverName()) if err != nil { return false, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", v.Name(), v.DriverName()) } exists, err := vd.Get(v.Name()) if err != nil { err = errors.Cause(err) if _, ok := err.(net.Error); ok { return false, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", v.Name(), v.DriverName()) } // At this point, the error could be anything from the driver, such as "no such volume" // Let's not check an error here, and instead check if the driver returned a volume } if exists == nil { return false, nil } return true, nil }
// Remove removes the requested volume. A volume is not removed if it has any refs func (s *VolumeStore) Remove(v volume.Volume) error { name := normaliseVolumeName(v.Name()) s.locks.Lock(name) defer s.locks.Unlock(name) if refs, exists := s.refs[name]; exists && len(refs) > 0 { return &OpErr{Err: errVolumeInUse, Name: v.Name(), Op: "remove", Refs: refs} } vd, err := volumedrivers.GetDriver(v.DriverName()) if err != nil { return &OpErr{Err: err, Name: vd.Name(), Op: "remove"} } logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name) if err := vd.Remove(v); err != nil { return &OpErr{Err: err, Name: name, Op: "remove"} } s.purge(name) return nil }
// Remove removes the requested volume. A volume is not removed if it has any refs func (s *VolumeStore) Remove(v volume.Volume) error { name := normaliseVolumeName(v.Name()) s.locks.Lock(name) defer s.locks.Unlock(name) if s.hasRef(name) { return &OpErr{Err: errVolumeInUse, Name: v.Name(), Op: "remove", Refs: s.getRefs(name)} } vd, err := volumedrivers.GetDriver(v.DriverName()) if err != nil { return &OpErr{Err: err, Name: vd.Name(), Op: "remove"} } logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name) vol := unwrapVolume(v) if err := vd.Remove(vol); err != nil { return &OpErr{Err: err, Name: name, Op: "remove"} } s.Purge(name) return nil }
// Remove removes the requested volume. A volume is not removed if the usage count is > 0 func (s *volumeStore) Remove(v volume.Volume) error { s.mu.Lock() defer s.mu.Unlock() name := v.Name() logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name) vc, exists := s.vols[name] if !exists { return ErrNoSuchVolume } if vc.count != 0 { return ErrVolumeInUse } vd, err := getVolumeDriver(vc.DriverName()) if err != nil { return err } if err := vd.Remove(vc.Volume); err != nil { return err } delete(s.vols, name) return nil }
// restore is called when a new volume store is created. // It's primary purpose is to ensure that all drivers' refcounts are set based // on known volumes after a restart. // This only attempts to track volumes that are actually stored in the on-disk db. // It does not probe the available drivers to find anything that may have been added // out of band. func (s *VolumeStore) restore() { var entries []*dbEntry s.db.View(func(tx *bolt.Tx) error { entries = listEntries(tx) return nil }) chRemove := make(chan []byte, len(entries)) var wg sync.WaitGroup for _, entry := range entries { wg.Add(1) // this is potentially a very slow operation, so do it in a goroutine go func(entry *dbEntry) { defer wg.Done() var meta volumeMetadata if len(entry.Value) != 0 { if err := json.Unmarshal(entry.Value, &meta); err != nil { logrus.Errorf("Error while reading volume metadata for volume %q: %v", string(entry.Key), err) // don't return here, we can try with `getVolume` below } } var v volume.Volume var err error if meta.Driver != "" { v, err = lookupVolume(meta.Driver, string(entry.Key)) if err != nil && err != errNoSuchVolume { logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", string(entry.Key)).Warn("Error restoring volume") return } if v == nil { // doesn't exist in the driver, remove it from the db chRemove <- entry.Key return } } else { v, err = s.getVolume(string(entry.Key)) if err != nil { if err == errNoSuchVolume { chRemove <- entry.Key } return } meta.Driver = v.DriverName() if err := s.setMeta(v.Name(), meta); err != nil { logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", v.Name()).Warn("Error updating volume metadata on restore") } } // increment driver refcount volumedrivers.CreateDriver(meta.Driver) // cache the volume s.globalLock.Lock() s.options[v.Name()] = meta.Options s.labels[v.Name()] = meta.Labels s.names[v.Name()] = v s.globalLock.Unlock() }(entry) } wg.Wait() close(chRemove) s.db.Update(func(tx *bolt.Tx) error { for k := range chRemove { if err := removeMeta(tx, string(k)); err != nil { logrus.Warnf("Error removing stale entry from volume db: %v", err) } } return nil }) }
// registerMountPoints initializes the container mount points with the configured volumes and bind mounts. // It follows the next sequence to decide what to mount in each final destination: // // 1. Select the previously configured mount points for the containers, if any. // 2. Select the volumes mounted from another containers. Overrides previously configured mount point destination. // 3. Select the bind mounts set by the client. Overrides previously configured mount point destinations. // 4. Cleanup old volumes that are about to be reassigned. func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) { binds := map[string]bool{} mountPoints := map[string]*volume.MountPoint{} defer func() { // clean up the container mountpoints once return with error if retErr != nil { for _, m := range mountPoints { if m.Volume == nil { continue } daemon.volumes.Dereference(m.Volume, container.ID) } } }() // 1. Read already configured mount points. for destination, point := range container.MountPoints { mountPoints[destination] = point } // 2. Read volumes from other containers. for _, v := range hostConfig.VolumesFrom { containerID, mode, err := volume.ParseVolumesFrom(v) if err != nil { return err } c, err := daemon.GetContainer(containerID) if err != nil { return err } for _, m := range c.MountPoints { cp := &volume.MountPoint{ Name: m.Name, Source: m.Source, RW: m.RW && volume.ReadWrite(mode), Driver: m.Driver, Destination: m.Destination, Propagation: m.Propagation, Spec: m.Spec, CopyData: false, } if len(cp.Source) == 0 { v, err := daemon.volumes.GetWithRef(cp.Name, cp.Driver, container.ID) if err != nil { return err } cp.Volume = v } mountPoints[cp.Destination] = cp } } // 3. Read bind mounts for _, b := range hostConfig.Binds { bind, err := volume.ParseMountRaw(b, hostConfig.VolumeDriver) if err != nil { return err } // #10618 _, tmpfsExists := hostConfig.Tmpfs[bind.Destination] if binds[bind.Destination] || tmpfsExists { return fmt.Errorf("Duplicate mount point '%s'", bind.Destination) } if bind.Type == mounttypes.TypeVolume { // create the volume v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil) if err != nil { return err } bind.Volume = v bind.Source = v.Path() // bind.Name is an already existing volume, we need to use that here bind.Driver = v.DriverName() if bind.Driver == volume.DefaultDriverName { setBindModeIfNull(bind) } } binds[bind.Destination] = true mountPoints[bind.Destination] = bind } for _, cfg := range hostConfig.Mounts { mp, err := volume.ParseMountSpec(cfg) if err != nil { return dockererrors.NewBadRequestError(err) } if binds[mp.Destination] { return fmt.Errorf("Duplicate mount point '%s'", cfg.Target) } if mp.Type == mounttypes.TypeVolume { var v volume.Volume if cfg.VolumeOptions != nil { var driverOpts map[string]string if cfg.VolumeOptions.DriverConfig != nil { driverOpts = cfg.VolumeOptions.DriverConfig.Options } v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, driverOpts, cfg.VolumeOptions.Labels) } else { v, err = daemon.volumes.CreateWithRef(mp.Name, mp.Driver, container.ID, nil, nil) } if err != nil { return err } if err := label.Relabel(mp.Source, container.MountLabel, false); err != nil { return err } mp.Volume = v mp.Name = v.Name() mp.Driver = v.DriverName() // only use the cached path here since getting the path is not necessary right now and calling `Path()` may be slow if cv, ok := v.(interface { CachedPath() string }); ok { mp.Source = cv.CachedPath() } } binds[mp.Destination] = true mountPoints[mp.Destination] = mp } container.Lock() // 4. Cleanup old volumes that are about to be reassigned. for _, m := range mountPoints { if m.BackwardsCompatible() { if mp, exists := container.MountPoints[m.Destination]; exists && mp.Volume != nil { daemon.volumes.Dereference(mp.Volume, container.ID) } } } container.MountPoints = mountPoints container.Unlock() return nil }
// restore is called when a new volume store is created. // It's primary purpose is to ensure that all drivers' refcounts are set based // on known volumes after a restart. // This only attempts to track volumes that are actually stored in the on-disk db. // It does not probe the available drivers to find anything that may have been added // out of band. func (s *VolumeStore) restore() { var ls []volumeMetadata s.db.View(func(tx *bolt.Tx) error { ls = listMeta(tx) return nil }) chRemove := make(chan *volumeMetadata, len(ls)) var wg sync.WaitGroup for _, meta := range ls { wg.Add(1) // this is potentially a very slow operation, so do it in a goroutine go func(meta volumeMetadata) { defer wg.Done() var v volume.Volume var err error if meta.Driver != "" { v, err = lookupVolume(meta.Driver, meta.Name) if err != nil && err != errNoSuchVolume { logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", meta.Name).Warn("Error restoring volume") return } if v == nil { // doesn't exist in the driver, remove it from the db chRemove <- &meta return } } else { v, err = s.getVolume(meta.Name) if err != nil { if err == errNoSuchVolume { chRemove <- &meta } return } meta.Driver = v.DriverName() if err := s.setMeta(v.Name(), meta); err != nil { logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", v.Name()).Warn("Error updating volume metadata on restore") } } // increment driver refcount volumedrivers.CreateDriver(meta.Driver) // cache the volume s.globalLock.Lock() s.options[v.Name()] = meta.Options s.labels[v.Name()] = meta.Labels s.names[v.Name()] = v s.globalLock.Unlock() }(meta) } wg.Wait() close(chRemove) s.db.Update(func(tx *bolt.Tx) error { for meta := range chRemove { if err := removeMeta(tx, meta.Name); err != nil { logrus.WithField("volume", meta.Name).Warnf("Error removing stale entry from volume db: %v", err) } } return nil }) }