|
@@ -1,22 +1,77 @@
|
|
|
package store
|
|
|
|
|
|
import (
|
|
|
+ "bytes"
|
|
|
+ "encoding/json"
|
|
|
+ "os"
|
|
|
+ "path/filepath"
|
|
|
"sync"
|
|
|
+ "time"
|
|
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
|
+ "github.com/boltdb/bolt"
|
|
|
"github.com/docker/docker/pkg/locker"
|
|
|
"github.com/docker/docker/volume"
|
|
|
"github.com/docker/docker/volume/drivers"
|
|
|
)
|
|
|
|
|
|
+const (
|
|
|
+ volumeDataDir = "volumes"
|
|
|
+ volumeBucketName = "volumes"
|
|
|
+)
|
|
|
+
|
|
|
+type volumeMetadata struct {
|
|
|
+ Name string
|
|
|
+ Labels map[string]string
|
|
|
+}
|
|
|
+
|
|
|
+type volumeWithLabels struct {
|
|
|
+ volume.Volume
|
|
|
+ labels map[string]string
|
|
|
+}
|
|
|
+
|
|
|
+func (v volumeWithLabels) Labels() map[string]string {
|
|
|
+ return v.labels
|
|
|
+}
|
|
|
+
|
|
|
// New initializes a VolumeStore to keep
|
|
|
// reference counting of volumes in the system.
|
|
|
-func New() *VolumeStore {
|
|
|
- return &VolumeStore{
|
|
|
- locks: &locker.Locker{},
|
|
|
- names: make(map[string]volume.Volume),
|
|
|
- refs: make(map[string][]string),
|
|
|
+func New(rootPath string) (*VolumeStore, error) {
|
|
|
+ vs := &VolumeStore{
|
|
|
+ locks: &locker.Locker{},
|
|
|
+ names: make(map[string]volume.Volume),
|
|
|
+ refs: make(map[string][]string),
|
|
|
+ labels: make(map[string]map[string]string),
|
|
|
}
|
|
|
+
|
|
|
+ if rootPath != "" {
|
|
|
+ // initialize metadata store
|
|
|
+ volPath := filepath.Join(rootPath, volumeDataDir)
|
|
|
+ if err := os.MkdirAll(volPath, 750); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ dbPath := filepath.Join(volPath, "metadata.db")
|
|
|
+
|
|
|
+ var err error
|
|
|
+ vs.db, err = bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 1 * time.Second})
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ // initialize volumes bucket
|
|
|
+ if err := vs.db.Update(func(tx *bolt.Tx) error {
|
|
|
+ if _, err := tx.CreateBucketIfNotExists([]byte(volumeBucketName)); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+ }); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return vs, nil
|
|
|
}
|
|
|
|
|
|
func (s *VolumeStore) getNamed(name string) (volume.Volume, bool) {
|
|
@@ -39,6 +94,7 @@ func (s *VolumeStore) purge(name string) {
|
|
|
s.globalLock.Lock()
|
|
|
delete(s.names, name)
|
|
|
delete(s.refs, name)
|
|
|
+ delete(s.labels, name)
|
|
|
s.globalLock.Unlock()
|
|
|
}
|
|
|
|
|
@@ -51,6 +107,9 @@ type VolumeStore struct {
|
|
|
names map[string]volume.Volume
|
|
|
// refs stores the volume name and the list of things referencing it
|
|
|
refs map[string][]string
|
|
|
+ // labels stores volume labels for each volume
|
|
|
+ labels map[string]map[string]string
|
|
|
+ db *bolt.DB
|
|
|
}
|
|
|
|
|
|
// List proxies to all registered volume drivers to get the full list of volumes
|
|
@@ -137,12 +196,12 @@ func (s *VolumeStore) list() ([]volume.Volume, []string, error) {
|
|
|
// CreateWithRef creates a volume with the given name and driver and stores the ref
|
|
|
// This is just like Create() except we store the reference while holding the lock.
|
|
|
// This ensures there's no race between creating a volume and then storing a reference.
|
|
|
-func (s *VolumeStore) CreateWithRef(name, driverName, ref string, opts map[string]string) (volume.Volume, error) {
|
|
|
+func (s *VolumeStore) CreateWithRef(name, driverName, ref string, opts, labels map[string]string) (volume.Volume, error) {
|
|
|
name = normaliseVolumeName(name)
|
|
|
s.locks.Lock(name)
|
|
|
defer s.locks.Unlock(name)
|
|
|
|
|
|
- v, err := s.create(name, driverName, opts)
|
|
|
+ v, err := s.create(name, driverName, opts, labels)
|
|
|
if err != nil {
|
|
|
return nil, &OpErr{Err: err, Name: name, Op: "create"}
|
|
|
}
|
|
@@ -152,12 +211,12 @@ func (s *VolumeStore) CreateWithRef(name, driverName, ref string, opts map[strin
|
|
|
}
|
|
|
|
|
|
// Create creates a volume with the given name and driver.
|
|
|
-func (s *VolumeStore) Create(name, driverName string, opts map[string]string) (volume.Volume, error) {
|
|
|
+func (s *VolumeStore) Create(name, driverName string, opts, labels map[string]string) (volume.Volume, error) {
|
|
|
name = normaliseVolumeName(name)
|
|
|
s.locks.Lock(name)
|
|
|
defer s.locks.Unlock(name)
|
|
|
|
|
|
- v, err := s.create(name, driverName, opts)
|
|
|
+ v, err := s.create(name, driverName, opts, labels)
|
|
|
if err != nil {
|
|
|
return nil, &OpErr{Err: err, Name: name, Op: "create"}
|
|
|
}
|
|
@@ -169,7 +228,7 @@ func (s *VolumeStore) Create(name, driverName string, opts map[string]string) (v
|
|
|
// If a volume with the name is already known, it will ask the stored driver for the volume.
|
|
|
// If the passed in driver name does not match the driver name which is stored for the given volume name, an error is returned.
|
|
|
// It is expected that callers of this function hold any necessary locks.
|
|
|
-func (s *VolumeStore) create(name, driverName string, opts map[string]string) (volume.Volume, error) {
|
|
|
+func (s *VolumeStore) create(name, driverName string, opts, labels map[string]string) (volume.Volume, error) {
|
|
|
// Validate the name in a platform-specific manner
|
|
|
valid, err := volume.IsVolumeNameValid(name)
|
|
|
if err != nil {
|
|
@@ -205,7 +264,33 @@ func (s *VolumeStore) create(name, driverName string, opts map[string]string) (v
|
|
|
if v, _ := vd.Get(name); v != nil {
|
|
|
return v, nil
|
|
|
}
|
|
|
- return vd.Create(name, opts)
|
|
|
+ v, err := vd.Create(name, opts)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ s.labels[name] = labels
|
|
|
+
|
|
|
+ if s.db != nil {
|
|
|
+ metadata := &volumeMetadata{
|
|
|
+ Name: name,
|
|
|
+ Labels: labels,
|
|
|
+ }
|
|
|
+
|
|
|
+ volData, err := json.Marshal(metadata)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := s.db.Update(func(tx *bolt.Tx) error {
|
|
|
+ b := tx.Bucket([]byte(volumeBucketName))
|
|
|
+ err := b.Put([]byte(name), volData)
|
|
|
+ return err
|
|
|
+ }); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return volumeWithLabels{v, labels}, nil
|
|
|
}
|
|
|
|
|
|
// GetWithRef gets a volume with the given name from the passed in driver and stores the ref
|
|
@@ -227,6 +312,9 @@ func (s *VolumeStore) GetWithRef(name, driverName, ref string) (volume.Volume, e
|
|
|
}
|
|
|
|
|
|
s.setNamed(v, ref)
|
|
|
+ if labels, ok := s.labels[name]; ok {
|
|
|
+ return volumeWithLabels{v, labels}, nil
|
|
|
+ }
|
|
|
return v, nil
|
|
|
}
|
|
|
|
|
@@ -248,13 +336,43 @@ func (s *VolumeStore) Get(name string) (volume.Volume, error) {
|
|
|
// if the driver is unknown it probes all drivers until it finds the first volume with that name.
|
|
|
// it is expected that callers of this function hold any necessary locks
|
|
|
func (s *VolumeStore) getVolume(name string) (volume.Volume, error) {
|
|
|
+ labels := map[string]string{}
|
|
|
+
|
|
|
+ if s.db != nil {
|
|
|
+ // get meta
|
|
|
+ if err := s.db.Update(func(tx *bolt.Tx) error {
|
|
|
+ b := tx.Bucket([]byte(volumeBucketName))
|
|
|
+ data := b.Get([]byte(name))
|
|
|
+
|
|
|
+ if string(data) == "" {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ var meta volumeMetadata
|
|
|
+ buf := bytes.NewBuffer(data)
|
|
|
+
|
|
|
+ if err := json.NewDecoder(buf).Decode(&meta); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ labels = meta.Labels
|
|
|
+
|
|
|
+ return nil
|
|
|
+ }); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
logrus.Debugf("Getting volume reference for name: %s", name)
|
|
|
if v, exists := s.names[name]; exists {
|
|
|
vd, err := volumedrivers.GetDriver(v.DriverName())
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
- return vd.Get(name)
|
|
|
+ vol, err := vd.Get(name)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return volumeWithLabels{vol, labels}, nil
|
|
|
}
|
|
|
|
|
|
logrus.Debugf("Probing all drivers for volume with name: %s", name)
|
|
@@ -268,7 +386,8 @@ func (s *VolumeStore) getVolume(name string) (volume.Volume, error) {
|
|
|
if err != nil {
|
|
|
continue
|
|
|
}
|
|
|
- return v, nil
|
|
|
+
|
|
|
+ return volumeWithLabels{v, labels}, nil
|
|
|
}
|
|
|
return nil, errNoSuchVolume
|
|
|
}
|
|
@@ -289,7 +408,8 @@ func (s *VolumeStore) Remove(v volume.Volume) error {
|
|
|
}
|
|
|
|
|
|
logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name)
|
|
|
- if err := vd.Remove(v); err != nil {
|
|
|
+ vol := withoutLabels(v)
|
|
|
+ if err := vd.Remove(vol); err != nil {
|
|
|
return &OpErr{Err: err, Name: name, Op: "remove"}
|
|
|
}
|
|
|
|
|
@@ -372,3 +492,11 @@ func (s *VolumeStore) filter(vols []volume.Volume, f filterFunc) []volume.Volume
|
|
|
}
|
|
|
return ls
|
|
|
}
|
|
|
+
|
|
|
+func withoutLabels(v volume.Volume) volume.Volume {
|
|
|
+ if vol, ok := v.(volumeWithLabels); ok {
|
|
|
+ return vol.Volume
|
|
|
+ }
|
|
|
+
|
|
|
+ return v
|
|
|
+}
|