summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_internal.go52
-rw-r--r--libpod/define/errors.go4
-rw-r--r--libpod/events.go13
-rw-r--r--libpod/events/config.go3
-rw-r--r--libpod/events/journal_linux.go3
-rw-r--r--libpod/events/logfile.go17
-rw-r--r--libpod/events/nullout.go6
-rw-r--r--libpod/options.go13
-rw-r--r--libpod/pod_api.go12
-rw-r--r--libpod/runtime_ctr.go2
-rw-r--r--libpod/volume.go36
-rw-r--r--libpod/volume_inspect.go11
12 files changed, 146 insertions, 26 deletions
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 934fe220f..7a547e565 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -1015,6 +1015,12 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error {
return err
}
+ for _, v := range c.config.NamedVolumes {
+ if err := c.chownVolume(v.Name); err != nil {
+ return err
+ }
+ }
+
// With the spec complete, do an OCI create
if err := c.ociRuntime.CreateContainer(c, nil); err != nil {
// Fedora 31 is carrying a patch to display improved error
@@ -1508,6 +1514,48 @@ func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string)
return vol, nil
}
+// Chown the specified volume if necessary.
+func (c *Container) chownVolume(volumeName string) error {
+ vol, err := c.runtime.state.Volume(volumeName)
+ if err != nil {
+ return errors.Wrapf(err, "error retrieving named volume %s for container %s", volumeName, c.ID())
+ }
+
+ uid := int(c.config.Spec.Process.User.UID)
+ gid := int(c.config.Spec.Process.User.GID)
+
+ vol.lock.Lock()
+ defer vol.lock.Unlock()
+
+ // The volume may need a copy-up. Check the state.
+ if err := vol.update(); err != nil {
+ return err
+ }
+
+ if vol.state.NeedsChown {
+ vol.state.NeedsChown = false
+ vol.state.UIDChowned = uid
+ vol.state.GIDChowned = gid
+
+ if err := vol.save(); err != nil {
+ return err
+ }
+ err := filepath.Walk(vol.MountPoint(), func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ if err := os.Chown(path, uid, gid); err != nil {
+ return err
+ }
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
// cleanupStorage unmounts and cleans up the container's root filesystem
func (c *Container) cleanupStorage() error {
if !c.state.Mounted {
@@ -1854,8 +1902,8 @@ func (c *Container) unmount(force bool) error {
// this should be from chrootarchive.
// Container MUST be mounted before calling.
func (c *Container) copyWithTarFromImage(source, dest string) error {
- a := archive.NewDefaultArchiver()
-
+ mappings := idtools.NewIDMappingsFromMaps(c.config.IDMappings.UIDMap, c.config.IDMappings.GIDMap)
+ a := archive.NewArchiver(mappings)
if err := c.copyOwnerAndPerms(source, dest); err != nil {
return err
}
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index d41e00648..1e9179353 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -70,6 +70,10 @@ var (
// ErrInternal indicates an internal library error
ErrInternal = errors.New("internal libpod error")
+ // ErrPodPartialFail indicates that a pod operation was only partially
+ // successful, and some containers within the pod failed.
+ ErrPodPartialFail = errors.New("some containers failed")
+
// ErrDetach indicates that an attach session was manually detached by
// the user.
ErrDetach = utils.ErrDetach
diff --git a/libpod/events.go b/libpod/events.go
index 74c910004..7560940a5 100644
--- a/libpod/events.go
+++ b/libpod/events.go
@@ -1,6 +1,7 @@
package libpod
import (
+ "context"
"fmt"
"github.com/containers/libpod/v2/libpod/events"
@@ -75,16 +76,16 @@ func (v *Volume) newVolumeEvent(status events.Status) {
// Events is a wrapper function for everyone to begin tailing the events log
// with options
-func (r *Runtime) Events(options events.ReadOptions) error {
+func (r *Runtime) Events(ctx context.Context, options events.ReadOptions) error {
eventer, err := r.newEventer()
if err != nil {
return err
}
- return eventer.Read(options)
+ return eventer.Read(ctx, options)
}
// GetEvents reads the event log and returns events based on input filters
-func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) {
+func (r *Runtime) GetEvents(ctx context.Context, filters []string) ([]*events.Event, error) {
var readErr error
eventChannel := make(chan *events.Event)
options := events.ReadOptions{
@@ -98,7 +99,7 @@ func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) {
return nil, err
}
go func() {
- readErr = eventer.Read(options)
+ readErr = eventer.Read(ctx, options)
}()
if readErr != nil {
return nil, readErr
@@ -112,7 +113,7 @@ func (r *Runtime) GetEvents(filters []string) ([]*events.Event, error) {
// GetLastContainerEvent takes a container name or ID and an event status and returns
// the last occurrence of the container event
-func (r *Runtime) GetLastContainerEvent(nameOrID string, containerEvent events.Status) (*events.Event, error) {
+func (r *Runtime) GetLastContainerEvent(ctx context.Context, nameOrID string, containerEvent events.Status) (*events.Event, error) {
// check to make sure the event.Status is valid
if _, err := events.StringToStatus(containerEvent.String()); err != nil {
return nil, err
@@ -122,7 +123,7 @@ func (r *Runtime) GetLastContainerEvent(nameOrID string, containerEvent events.S
fmt.Sprintf("event=%s", containerEvent),
"type=container",
}
- containerEvents, err := r.GetEvents(filters)
+ containerEvents, err := r.GetEvents(ctx, filters)
if err != nil {
return nil, err
}
diff --git a/libpod/events/config.go b/libpod/events/config.go
index 8fe551c5d..c34408e63 100644
--- a/libpod/events/config.go
+++ b/libpod/events/config.go
@@ -1,6 +1,7 @@
package events
import (
+ "context"
"time"
"github.com/pkg/errors"
@@ -52,7 +53,7 @@ type Eventer interface {
// Write an event to a backend
Write(event Event) error
// Read an event from the backend
- Read(options ReadOptions) error
+ Read(ctx context.Context, options ReadOptions) error
// String returns the type of event logger
String() string
}
diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go
index 482435038..d341ca7b5 100644
--- a/libpod/events/journal_linux.go
+++ b/libpod/events/journal_linux.go
@@ -3,6 +3,7 @@
package events
import (
+ "context"
"fmt"
"strconv"
"time"
@@ -53,7 +54,7 @@ func (e EventJournalD) Write(ee Event) error {
}
// Read reads events from the journal and sends qualified events to the event channel
-func (e EventJournalD) Read(options ReadOptions) error {
+func (e EventJournalD) Read(ctx context.Context, options ReadOptions) error {
defer close(options.EventChannel)
eventOptions, err := generateEventOptions(options.Filters, options.Since, options.Until)
if err != nil {
diff --git a/libpod/events/logfile.go b/libpod/events/logfile.go
index 93e6fa3c9..28d0dc07e 100644
--- a/libpod/events/logfile.go
+++ b/libpod/events/logfile.go
@@ -1,6 +1,7 @@
package events
import (
+ "context"
"fmt"
"os"
@@ -40,7 +41,7 @@ func (e EventLogFile) Write(ee Event) error {
}
// Reads from the log file
-func (e EventLogFile) Read(options ReadOptions) error {
+func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error {
defer close(options.EventChannel)
eventOptions, err := generateEventOptions(options.Filters, options.Since, options.Until)
if err != nil {
@@ -50,6 +51,17 @@ func (e EventLogFile) Read(options ReadOptions) error {
if err != nil {
return err
}
+ funcDone := make(chan bool)
+ copy := true
+ go func() {
+ select {
+ case <-funcDone:
+ // Do nothing
+ case <-ctx.Done():
+ copy = false
+ t.Kill(errors.New("hangup by client"))
+ }
+ }()
for line := range t.Lines {
event, err := newEventFromJSONString(line.Text)
if err != nil {
@@ -65,10 +77,11 @@ func (e EventLogFile) Read(options ReadOptions) error {
for _, filter := range eventOptions {
include = include && filter(event)
}
- if include {
+ if include && copy {
options.EventChannel <- event
}
}
+ funcDone <- true
return nil
}
diff --git a/libpod/events/nullout.go b/libpod/events/nullout.go
index f3b36e609..3eca9e8db 100644
--- a/libpod/events/nullout.go
+++ b/libpod/events/nullout.go
@@ -1,5 +1,9 @@
package events
+import (
+ "context"
+)
+
// EventToNull is an eventer type that only performs write operations
// and only writes to /dev/null. It is meant for unittests only
type EventToNull struct{}
@@ -10,7 +14,7 @@ func (e EventToNull) Write(ee Event) error {
}
// Read does nothing. Do not use it.
-func (e EventToNull) Read(options ReadOptions) error {
+func (e EventToNull) Read(ctx context.Context, options ReadOptions) error {
return nil
}
diff --git a/libpod/options.go b/libpod/options.go
index 573806308..3120a35d7 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1485,6 +1485,19 @@ func WithVolumeGID(gid int) VolumeCreateOption {
}
}
+// WithVolumeNeedsChown sets the NeedsChown flag for the volume.
+func WithVolumeNeedsChown() VolumeCreateOption {
+ return func(volume *Volume) error {
+ if volume.valid {
+ return define.ErrVolumeFinalized
+ }
+
+ volume.state.NeedsChown = true
+
+ return nil
+ }
+}
+
// withSetAnon sets a bool notifying libpod that this volume is anonymous and
// should be removed when containers using it are removed and volumes are
// specified for removal.
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index 14e6647be..a02b171e1 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -59,7 +59,7 @@ func (p *Pod) Start(ctx context.Context) (map[string]error, error) {
}
if len(ctrErrors) > 0 {
- return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error starting some containers")
+ return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error starting some containers")
}
defer p.newPodEvent(events.Start)
return nil, nil
@@ -139,7 +139,7 @@ func (p *Pod) StopWithTimeout(ctx context.Context, cleanup bool, timeout int) (m
}
if len(ctrErrors) > 0 {
- return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error stopping some containers")
+ return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error stopping some containers")
}
defer p.newPodEvent(events.Stop)
return nil, nil
@@ -208,7 +208,7 @@ func (p *Pod) Pause() (map[string]error, error) {
}
if len(ctrErrors) > 0 {
- return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error pausing some containers")
+ return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error pausing some containers")
}
defer p.newPodEvent(events.Pause)
return nil, nil
@@ -267,7 +267,7 @@ func (p *Pod) Unpause() (map[string]error, error) {
}
if len(ctrErrors) > 0 {
- return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error unpausing some containers")
+ return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error unpausing some containers")
}
defer p.newPodEvent(events.Unpause)
@@ -321,7 +321,7 @@ func (p *Pod) Restart(ctx context.Context) (map[string]error, error) {
}
if len(ctrErrors) > 0 {
- return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error stopping some containers")
+ return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error stopping some containers")
}
p.newPodEvent(events.Stop)
p.newPodEvent(events.Start)
@@ -387,7 +387,7 @@ func (p *Pod) Kill(signal uint) (map[string]error, error) {
}
if len(ctrErrors) > 0 {
- return ctrErrors, errors.Wrapf(define.ErrCtrExists, "error killing some containers")
+ return ctrErrors, errors.Wrapf(define.ErrPodPartialFail, "error killing some containers")
}
defer p.newPodEvent(events.Kill)
return nil, nil
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 4010c9bea..c073aabb5 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -309,7 +309,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
logrus.Debugf("Creating new volume %s for container", vol.Name)
// The volume does not exist, so we need to create it.
- volOptions := []VolumeCreateOption{WithVolumeName(vol.Name), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID())}
+ volOptions := []VolumeCreateOption{WithVolumeName(vol.Name), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID()), WithVolumeNeedsChown()}
if isAnonymous {
volOptions = append(volOptions, withSetAnon())
}
diff --git a/libpod/volume.go b/libpod/volume.go
index 72b080d1a..438957086 100644
--- a/libpod/volume.go
+++ b/libpod/volume.go
@@ -64,6 +64,14 @@ type VolumeState struct {
// create time, then cleared after the copy up is done and never set
// again.
NeedsCopyUp bool `json:"notYetMounted,omitempty"`
+ // NeedsChown indicates that the next time the volume is mounted into
+ // a container, the container will chown the volume to the container process
+ // UID/GID.
+ NeedsChown bool `json:"notYetChowned,omitempty"`
+ // UIDChowned is the UID the volume was chowned to.
+ UIDChowned int `json:"uidChowned,omitempty"`
+ // GIDChowned is the GID the volume was chowned to.
+ GIDChowned int `json:"gidChowned,omitempty"`
}
// Name retrieves the volume's name
@@ -113,13 +121,33 @@ func (v *Volume) Anonymous() bool {
}
// UID returns the UID the volume will be created as.
-func (v *Volume) UID() int {
- return v.config.UID
+func (v *Volume) UID() (int, error) {
+ v.lock.Lock()
+ defer v.lock.Unlock()
+
+ if !v.valid {
+ return -1, define.ErrVolumeRemoved
+ }
+
+ if v.state.UIDChowned > 0 {
+ return v.state.UIDChowned, nil
+ }
+ return v.config.UID, nil
}
// GID returns the GID the volume will be created as.
-func (v *Volume) GID() int {
- return v.config.GID
+func (v *Volume) GID() (int, error) {
+ v.lock.Lock()
+ defer v.lock.Unlock()
+
+ if !v.valid {
+ return -1, define.ErrVolumeRemoved
+ }
+
+ if v.state.GIDChowned > 0 {
+ return v.state.GIDChowned, nil
+ }
+ return v.config.GID, nil
}
// CreatedTime returns the time the volume was created at. It was not tracked
diff --git a/libpod/volume_inspect.go b/libpod/volume_inspect.go
index 5258792eb..85848f84f 100644
--- a/libpod/volume_inspect.go
+++ b/libpod/volume_inspect.go
@@ -65,8 +65,15 @@ func (v *Volume) Inspect() (*InspectVolumeData, error) {
for k, v := range v.config.Options {
data.Options[k] = v
}
- data.UID = v.config.UID
- data.GID = v.config.GID
+ var err error
+ data.UID, err = v.UID()
+ if err != nil {
+ return nil, err
+ }
+ data.GID, err = v.GID()
+ if err != nil {
+ return nil, err
+ }
data.Anonymous = v.config.IsAnon
return data, nil