| package store |
| |
| import ( |
| "errors" |
| "sync" |
| |
| "github.com/Sirupsen/logrus" |
| "github.com/docker/docker/volume" |
| "github.com/docker/docker/volume/drivers" |
| ) |
| |
| var ( |
| // ErrVolumeInUse is a typed error returned when trying to remove a volume that is currently in use by a container |
| ErrVolumeInUse = errors.New("volume is in use") |
| // ErrNoSuchVolume is a typed error returned if the requested volume doesn't exist in the volume store |
| ErrNoSuchVolume = errors.New("no such volume") |
| ) |
| |
| // New initializes a VolumeStore to keep |
| // reference counting of volumes in the system. |
| func New() *VolumeStore { |
| return &VolumeStore{ |
| vols: make(map[string]*volumeCounter), |
| } |
| } |
| |
| // VolumeStore is a struct that stores the list of volumes available and keeps track of their usage counts |
| type VolumeStore struct { |
| vols map[string]*volumeCounter |
| mu sync.Mutex |
| } |
| |
| // volumeCounter keeps track of references to a volume |
| type volumeCounter struct { |
| volume.Volume |
| count uint |
| } |
| |
| // AddAll adds a list of volumes to the store |
| func (s *VolumeStore) AddAll(vols []volume.Volume) { |
| for _, v := range vols { |
| s.vols[v.Name()] = &volumeCounter{v, 0} |
| } |
| } |
| |
| // Create tries to find an existing volume with the given name or create a new one from the passed in driver |
| func (s *VolumeStore) Create(name, driverName string, opts map[string]string) (volume.Volume, error) { |
| s.mu.Lock() |
| if vc, exists := s.vols[name]; exists { |
| v := vc.Volume |
| s.mu.Unlock() |
| return v, nil |
| } |
| s.mu.Unlock() |
| logrus.Debugf("Registering new volume reference: driver %s, name %s", driverName, name) |
| |
| vd, err := volumedrivers.GetDriver(driverName) |
| if err != nil { |
| return nil, err |
| } |
| |
| v, err := vd.Create(name, opts) |
| if err != nil { |
| return nil, err |
| } |
| |
| s.mu.Lock() |
| s.vols[v.Name()] = &volumeCounter{v, 0} |
| s.mu.Unlock() |
| |
| return v, nil |
| } |
| |
| // Get looks if a volume with the given name exists and returns it if so |
| func (s *VolumeStore) Get(name string) (volume.Volume, error) { |
| s.mu.Lock() |
| defer s.mu.Unlock() |
| vc, exists := s.vols[name] |
| if !exists { |
| return nil, ErrNoSuchVolume |
| } |
| return vc.Volume, 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 := volumedrivers.GetDriver(vc.DriverName()) |
| if err != nil { |
| return err |
| } |
| if err := vd.Remove(vc.Volume); err != nil { |
| return err |
| } |
| delete(s.vols, name) |
| return nil |
| } |
| |
| // 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++ |
| } |
| |
| // 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 |
| } |
| if vc.count == 0 { |
| return |
| } |
| vc.count-- |
| } |
| |
| // Count returns the usage count of the passed in volume |
| func (s *VolumeStore) Count(v volume.Volume) uint { |
| s.mu.Lock() |
| defer s.mu.Unlock() |
| vc, exists := s.vols[v.Name()] |
| if !exists { |
| return 0 |
| } |
| return vc.count |
| } |
| |
| // List returns all the available volumes |
| func (s *VolumeStore) List() []volume.Volume { |
| s.mu.Lock() |
| defer s.mu.Unlock() |
| var ls []volume.Volume |
| for _, vc := range s.vols { |
| ls = append(ls, vc.Volume) |
| } |
| return ls |
| } |
| |
| // FilterByDriver returns the available volumes filtered by driver name |
| func (s *VolumeStore) FilterByDriver(name string) []volume.Volume { |
| return s.filter(byDriver(name)) |
| } |
| |
| // filterFunc defines a function to allow filter volumes in the store |
| type filterFunc func(vol volume.Volume) bool |
| |
| // byDriver generates a filterFunc to filter volumes by their driver name |
| func byDriver(name string) filterFunc { |
| return func(vol volume.Volume) bool { |
| return vol.DriverName() == name |
| } |
| } |
| |
| // filter returns the available volumes filtered by a filterFunc function |
| func (s *VolumeStore) filter(f filterFunc) []volume.Volume { |
| s.mu.Lock() |
| defer s.mu.Unlock() |
| var ls []volume.Volume |
| for _, vc := range s.vols { |
| if f(vc.Volume) { |
| ls = append(ls, vc.Volume) |
| } |
| } |
| return ls |
| } |