From 046178e55f72ed9db7cf5898d3be91b0112ab94f Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Tue, 27 Aug 2019 15:25:54 -0400 Subject: Add function for looking up volumes by partial name This isn't included in Docker, but seems handy enough. Use the new API for 'volume rm' and 'volume inspect'. Fixes #3891 Signed-off-by: Matthew Heon --- libpod/boltdb_state.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++ libpod/in_memory_state.go | 35 ++++++++++++++++++++++++ libpod/runtime_volume.go | 46 +++++++++++--------------------- libpod/state.go | 3 +++ 4 files changed, 121 insertions(+), 31 deletions(-) (limited to 'libpod') diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 31b551b36..4e7f78f13 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -1735,6 +1735,74 @@ func (s *BoltState) Volume(name string) (*Volume, error) { return volume, nil } +// LookupVolume locates a volume from a partial name. +func (s *BoltState) LookupVolume(name string) (*Volume, error) { + if name == "" { + return nil, define.ErrEmptyID + } + + if !s.valid { + return nil, define.ErrDBClosed + } + + volName := []byte(name) + + volume := new(Volume) + volume.config = new(VolumeConfig) + + db, err := s.getDBCon() + if err != nil { + return nil, err + } + defer s.deferredCloseDBCon(db) + + err = db.View(func(tx *bolt.Tx) error { + volBkt, err := getVolBucket(tx) + if err != nil { + return err + } + + allVolsBkt, err := getAllVolsBucket(tx) + if err != nil { + return err + } + + // Check for exact match on name + volDB := volBkt.Bucket(volName) + if volDB != nil { + return s.getVolumeFromDB(volName, volume, volBkt) + } + + // No exact match. Search all names. + foundMatch := false + err = allVolsBkt.ForEach(func(checkName, checkName2 []byte) error { + if strings.HasPrefix(string(checkName), name) { + if foundMatch { + return errors.Wrapf(define.ErrVolumeExists, "more than one result for volume name %q", name) + } + foundMatch = true + volName = checkName + } + return nil + }) + if err != nil { + return err + } + + if !foundMatch { + return errors.Wrapf(define.ErrNoSuchVolume, "no volume with name %q found", name) + } + + return s.getVolumeFromDB(volName, volume, volBkt) + }) + if err != nil { + return nil, err + } + + return volume, nil + +} + // HasVolume returns true if the given volume exists in the state, otherwise it returns false func (s *BoltState) HasVolume(name string) (bool, error) { if name == "" { diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go index 280ae5f5c..a008fcb39 100644 --- a/libpod/in_memory_state.go +++ b/libpod/in_memory_state.go @@ -459,6 +459,41 @@ func (s *InMemoryState) Volume(name string) (*Volume, error) { return vol, nil } +// LookupVolume finds a volume from an unambiguous partial ID. +func (s *InMemoryState) LookupVolume(name string) (*Volume, error) { + if name == "" { + return nil, define.ErrEmptyID + } + + vol, ok := s.volumes[name] + if ok { + return vol, nil + } + + // Alright, we've failed to find by full name. Now comes the expensive + // part. + // Loop through all volumes and look for matches. + var ( + foundMatch bool + candidate *Volume + ) + for volName, vol := range s.volumes { + if strings.HasPrefix(volName, name) { + if foundMatch { + return nil, errors.Wrapf(define.ErrVolumeExists, "more than one result for volume name %q", name) + } + candidate = vol + foundMatch = true + } + } + + if !foundMatch { + return nil, errors.Wrapf(define.ErrNoSuchVolume, "no volume with name %q found", name) + } + + return candidate, nil +} + // HasVolume checks if a volume with the given name is present in the state func (s *InMemoryState) HasVolume(name string) (bool, error) { if name == "" { diff --git a/libpod/runtime_volume.go b/libpod/runtime_volume.go index 512e778a1..a6ab748e5 100644 --- a/libpod/runtime_volume.go +++ b/libpod/runtime_volume.go @@ -6,7 +6,6 @@ import ( "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // Contains the public Runtime API for volumes @@ -43,40 +42,25 @@ func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force bool) error return r.removeVolume(ctx, v, force) } -// RemoveVolumes removes a slice of volumes or all with a force bool -func (r *Runtime) RemoveVolumes(ctx context.Context, volumes []string, all, force bool) ([]string, error) { - var ( - vols []*Volume - err error - deletedVols []string - ) - if all { - vols, err = r.Volumes() - if err != nil { - return nil, errors.Wrapf(err, "unable to get all volumes") - } - } else { - for _, i := range volumes { - vol, err := r.GetVolume(i) - if err != nil { - return nil, err - } - vols = append(vols, vol) - } +// GetVolume retrieves a volume given its full name. +func (r *Runtime) GetVolume(name string) (*Volume, error) { + r.lock.RLock() + defer r.lock.RUnlock() + + if !r.valid { + return nil, define.ErrRuntimeStopped } - for _, vol := range vols { - if err := r.RemoveVolume(ctx, vol, force); err != nil { - return deletedVols, err - } - logrus.Debugf("removed volume %s", vol.Name()) - deletedVols = append(deletedVols, vol.Name()) + vol, err := r.state.Volume(name) + if err != nil { + return nil, err } - return deletedVols, nil + + return vol, nil } -// GetVolume retrieves a volume given its full name. -func (r *Runtime) GetVolume(name string) (*Volume, error) { +// LookupVolume retrieves a volume by unambigious partial name. +func (r *Runtime) LookupVolume(name string) (*Volume, error) { r.lock.RLock() defer r.lock.RUnlock() @@ -84,7 +68,7 @@ func (r *Runtime) GetVolume(name string) (*Volume, error) { return nil, define.ErrRuntimeStopped } - vol, err := r.state.Volume(name) + vol, err := r.state.LookupVolume(name) if err != nil { return nil, err } diff --git a/libpod/state.go b/libpod/state.go index db4667ad6..40080d2cc 100644 --- a/libpod/state.go +++ b/libpod/state.go @@ -190,6 +190,9 @@ type State interface { // Volume accepts full name of volume // If the volume doesn't exist, an error will be returned Volume(volName string) (*Volume, error) + // LookupVolume accepts an unambiguous partial name or full name of a + // volume. Ambiguous names will result in an error. + LookupVolume(name string) (*Volume, error) // HasVolume returns true if volName exists in the state, // otherwise it returns false HasVolume(volName string) (bool, error) -- cgit v1.2.3-54-g00ecf