diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/boltdb_state.go | 68 | ||||
-rw-r--r-- | libpod/container_internal.go | 100 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 4 | ||||
-rw-r--r-- | libpod/in_memory_state.go | 35 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 4 | ||||
-rw-r--r-- | libpod/runtime_volume.go | 46 | ||||
-rw-r--r-- | libpod/state.go | 3 | ||||
-rw-r--r-- | libpod/volume.go | 7 | ||||
-rw-r--r-- | libpod/volume_internal.go | 2 |
9 files changed, 201 insertions, 68 deletions
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/container_internal.go b/libpod/container_internal.go index 1ca20a05d..ac565fdad 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -21,6 +21,7 @@ import ( "github.com/containers/storage" "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/mount" + "github.com/cyphar/filepath-securejoin" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" @@ -1242,43 +1243,82 @@ func (c *Container) mountStorage() (_ string, Err error) { }() } + // We need to mount the container before volumes - to ensure the copyup + // works properly. + mountPoint := c.config.Rootfs + if mountPoint == "" { + mountPoint, err = c.mount() + if err != nil { + return "", err + } + defer func() { + if Err != nil { + if err := c.unmount(false); err != nil { + logrus.Errorf("Error unmounting container %s after mount error: %v", c.ID(), err) + } + } + }() + } + // Request a mount of all named volumes for _, v := range c.config.NamedVolumes { - vol, err := c.runtime.state.Volume(v.Name) + vol, err := c.mountNamedVolume(v, mountPoint) if err != nil { - return "", errors.Wrapf(err, "error retrieving named volume %s for container %s", v.Name, c.ID()) + return "", err } - - if vol.needsMount() { + defer func() { + if Err == nil { + return + } vol.lock.Lock() - if err := vol.mount(); err != nil { - vol.lock.Unlock() - return "", errors.Wrapf(err, "error mounting volume %s for container %s", vol.Name(), c.ID()) + if err := vol.unmount(false); err != nil { + logrus.Errorf("Error unmounting volume %s after error mounting container %s: %v", vol.Name(), c.ID(), err) } vol.lock.Unlock() - defer func() { - if Err == nil { - return - } - vol.lock.Lock() - if err := vol.unmount(false); err != nil { - logrus.Errorf("Error unmounting volume %s after error mounting container %s: %v", vol.Name(), c.ID(), err) - } - vol.lock.Unlock() - }() - } + }() } - // TODO: generalize this mount code so it will mount every mount in ctr.config.Mounts - mountPoint := c.config.Rootfs - if mountPoint == "" { - mountPoint, err = c.mount() - if err != nil { - return "", err + return mountPoint, nil +} + +// Mount a single named volume into the container. +// If necessary, copy up image contents into the volume. +// Does not verify that the name volume given is actually present in container +// config. +// Returns the volume that was mounted. +func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string) (*Volume, error) { + vol, err := c.runtime.state.Volume(v.Name) + if err != nil { + return nil, errors.Wrapf(err, "error retrieving named volume %s for container %s", v.Name, c.ID()) + } + + vol.lock.Lock() + defer vol.lock.Unlock() + if vol.needsMount() { + if err := vol.mount(); err != nil { + return nil, errors.Wrapf(err, "error mounting volume %s for container %s", vol.Name(), c.ID()) } } + // The volume may need a copy-up. Check the state. + if err := vol.update(); err != nil { + return nil, err + } + if vol.state.NeedsCopyUp { + logrus.Debugf("Copying up contents from container %s to volume %s", c.ID(), vol.Name()) + srcDir, err := securejoin.SecureJoin(mountpoint, v.Dest) + if err != nil { + return nil, errors.Wrapf(err, "error calculating destination path to copy up container %s volume %s", c.ID(), vol.Name()) + } + if err := c.copyWithTarFromImage(srcDir, vol.MountPoint()); err != nil && !os.IsNotExist(err) { + return nil, errors.Wrapf(err, "error copying content from container %s into volume %s", c.ID(), vol.Name()) + } - return mountPoint, nil + vol.state.NeedsCopyUp = false + if err := vol.save(); err != nil { + return nil, err + } + } + return vol, nil } // cleanupStorage unmounts and cleans up the container's root filesystem @@ -1622,15 +1662,11 @@ func (c *Container) unmount(force bool) error { } // this should be from chrootarchive. -func (c *Container) copyWithTarFromImage(src, dest string) error { - mountpoint, err := c.mount() - if err != nil { - return err - } +// Container MUST be mounted before calling. +func (c *Container) copyWithTarFromImage(source, dest string) error { a := archive.NewDefaultArchiver() - source := filepath.Join(mountpoint, src) - if err = c.copyOwnerAndPerms(source, dest); err != nil { + if err := c.copyOwnerAndPerms(source, dest); err != nil { return err } return a.CopyWithTar(source, dest) diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 5dc53582f..21429c09e 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -115,7 +115,9 @@ func (c *Container) prepare() (Err error) { createErr = createNetNSErr } if mountStorageErr != nil { - logrus.Errorf("Error preparing container %s: %v", c.ID(), createErr) + if createErr != nil { + logrus.Errorf("Error preparing container %s: %v", c.ID(), createErr) + } createErr = mountStorageErr } 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_ctr.go b/libpod/runtime_ctr.go index e421c09f0..bffce7bca 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -306,10 +306,6 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (c *Contai return nil, errors.Wrapf(err, "error creating named volume %q", vol.Name) } - if err := ctr.copyWithTarFromImage(vol.Dest, newVol.MountPoint()); err != nil && !os.IsNotExist(err) { - return nil, errors.Wrapf(err, "Failed to copy content into new volume mount %q", vol.Name) - } - ctrNamedVolumes = append(ctrNamedVolumes, newVol) } 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) diff --git a/libpod/volume.go b/libpod/volume.go index b4de3aedc..c4771bbb8 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -57,6 +57,13 @@ type VolumeState struct { // On incrementing from 0, the volume will be mounted on the host. // On decrementing to 0, the volume will be unmounted on the host. MountCount uint `json:"mountCount"` + // NeedsCopyUp indicates that the next time the volume is mounted into + // a container, the container will "copy up" the contents of the + // mountpoint into the volume. + // This should only be done once. As such, this is set at container + // create time, then cleared after the copy up is done and never set + // again. + NeedsCopyUp bool `json:"notYetMounted,omitempty"` } // Name retrieves the volume's name diff --git a/libpod/volume_internal.go b/libpod/volume_internal.go index 2e886e1b0..42b935e7c 100644 --- a/libpod/volume_internal.go +++ b/libpod/volume_internal.go @@ -11,9 +11,11 @@ import ( func newVolume(runtime *Runtime) (*Volume, error) { volume := new(Volume) volume.config = new(VolumeConfig) + volume.state = new(VolumeState) volume.runtime = runtime volume.config.Labels = make(map[string]string) volume.config.Options = make(map[string]string) + volume.state.NeedsCopyUp = true return volume, nil } |