github.com/dougm/docker@v1.5.0/volumes/repository.go (about)

     1  package volumes
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"sync"
     9  
    10  	log "github.com/Sirupsen/logrus"
    11  	"github.com/docker/docker/daemon/graphdriver"
    12  	"github.com/docker/docker/utils"
    13  )
    14  
    15  type Repository struct {
    16  	configPath string
    17  	driver     graphdriver.Driver
    18  	volumes    map[string]*Volume
    19  	lock       sync.Mutex
    20  }
    21  
    22  func NewRepository(configPath string, driver graphdriver.Driver) (*Repository, error) {
    23  	abspath, err := filepath.Abs(configPath)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	// Create the config path
    29  	if err := os.MkdirAll(abspath, 0700); err != nil && !os.IsExist(err) {
    30  		return nil, err
    31  	}
    32  
    33  	repo := &Repository{
    34  		driver:     driver,
    35  		configPath: abspath,
    36  		volumes:    make(map[string]*Volume),
    37  	}
    38  
    39  	return repo, repo.restore()
    40  }
    41  
    42  func (r *Repository) newVolume(path string, writable bool) (*Volume, error) {
    43  	var (
    44  		isBindMount bool
    45  		err         error
    46  		id          = utils.GenerateRandomID()
    47  	)
    48  	if path != "" {
    49  		isBindMount = true
    50  	}
    51  
    52  	if path == "" {
    53  		path, err = r.createNewVolumePath(id)
    54  		if err != nil {
    55  			return nil, err
    56  		}
    57  	}
    58  	path = filepath.Clean(path)
    59  
    60  	// Ignore the error here since the path may not exist
    61  	// Really just want to make sure the path we are using is real(or non-existant)
    62  	if cleanPath, err := filepath.EvalSymlinks(path); err == nil {
    63  		path = cleanPath
    64  	}
    65  
    66  	v := &Volume{
    67  		ID:          id,
    68  		Path:        path,
    69  		repository:  r,
    70  		Writable:    writable,
    71  		containers:  make(map[string]struct{}),
    72  		configPath:  r.configPath + "/" + id,
    73  		IsBindMount: isBindMount,
    74  	}
    75  
    76  	if err := v.initialize(); err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	return v, r.add(v)
    81  }
    82  
    83  func (r *Repository) restore() error {
    84  	dir, err := ioutil.ReadDir(r.configPath)
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	for _, v := range dir {
    90  		id := v.Name()
    91  		vol := &Volume{
    92  			ID:         id,
    93  			configPath: r.configPath + "/" + id,
    94  			containers: make(map[string]struct{}),
    95  		}
    96  		if err := vol.FromDisk(); err != nil {
    97  			if !os.IsNotExist(err) {
    98  				log.Debugf("Error restoring volume: %v", err)
    99  				continue
   100  			}
   101  			if err := vol.initialize(); err != nil {
   102  				log.Debugf("%s", err)
   103  				continue
   104  			}
   105  		}
   106  		if err := r.add(vol); err != nil {
   107  			log.Debugf("Error restoring volume: %v", err)
   108  		}
   109  	}
   110  	return nil
   111  }
   112  
   113  func (r *Repository) Get(path string) *Volume {
   114  	r.lock.Lock()
   115  	vol := r.get(path)
   116  	r.lock.Unlock()
   117  	return vol
   118  }
   119  
   120  func (r *Repository) get(path string) *Volume {
   121  	path, err := filepath.EvalSymlinks(path)
   122  	if err != nil {
   123  		return nil
   124  	}
   125  	return r.volumes[filepath.Clean(path)]
   126  }
   127  
   128  func (r *Repository) Add(volume *Volume) error {
   129  	r.lock.Lock()
   130  	defer r.lock.Unlock()
   131  	return r.add(volume)
   132  }
   133  
   134  func (r *Repository) add(volume *Volume) error {
   135  	if vol := r.get(volume.Path); vol != nil {
   136  		return fmt.Errorf("Volume exists: %s", volume.ID)
   137  	}
   138  	r.volumes[volume.Path] = volume
   139  	return nil
   140  }
   141  
   142  func (r *Repository) Remove(volume *Volume) {
   143  	r.lock.Lock()
   144  	r.remove(volume)
   145  	r.lock.Unlock()
   146  }
   147  
   148  func (r *Repository) remove(volume *Volume) {
   149  	delete(r.volumes, volume.Path)
   150  }
   151  
   152  func (r *Repository) Delete(path string) error {
   153  	r.lock.Lock()
   154  	defer r.lock.Unlock()
   155  	path, err := filepath.EvalSymlinks(path)
   156  	if err != nil {
   157  		return err
   158  	}
   159  	volume := r.get(filepath.Clean(path))
   160  	if volume == nil {
   161  		return fmt.Errorf("Volume %s does not exist", path)
   162  	}
   163  
   164  	containers := volume.Containers()
   165  	if len(containers) > 0 {
   166  		return fmt.Errorf("Volume %s is being used and cannot be removed: used by containers %s", volume.Path, containers)
   167  	}
   168  
   169  	if err := os.RemoveAll(volume.configPath); err != nil {
   170  		return err
   171  	}
   172  
   173  	if !volume.IsBindMount {
   174  		if err := r.driver.Remove(volume.ID); err != nil {
   175  			if !os.IsNotExist(err) {
   176  				return err
   177  			}
   178  		}
   179  	}
   180  
   181  	r.remove(volume)
   182  	return nil
   183  }
   184  
   185  func (r *Repository) createNewVolumePath(id string) (string, error) {
   186  	if err := r.driver.Create(id, ""); err != nil {
   187  		return "", err
   188  	}
   189  
   190  	path, err := r.driver.Get(id, "")
   191  	if err != nil {
   192  		return "", fmt.Errorf("Driver %s failed to get volume rootfs %s: %v", r.driver, id, err)
   193  	}
   194  
   195  	return path, nil
   196  }
   197  
   198  func (r *Repository) FindOrCreateVolume(path string, writable bool) (*Volume, error) {
   199  	r.lock.Lock()
   200  	defer r.lock.Unlock()
   201  
   202  	if path == "" {
   203  		return r.newVolume(path, writable)
   204  	}
   205  
   206  	if v := r.get(path); v != nil {
   207  		return v, nil
   208  	}
   209  
   210  	return r.newVolume(path, writable)
   211  }