diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_internal.go | 52 | ||||
-rw-r--r-- | libpod/define/errors.go | 4 | ||||
-rw-r--r-- | libpod/events.go | 13 | ||||
-rw-r--r-- | libpod/events/config.go | 3 | ||||
-rw-r--r-- | libpod/events/journal_linux.go | 3 | ||||
-rw-r--r-- | libpod/events/logfile.go | 17 | ||||
-rw-r--r-- | libpod/events/nullout.go | 6 | ||||
-rw-r--r-- | libpod/options.go | 13 | ||||
-rw-r--r-- | libpod/pod_api.go | 12 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 2 | ||||
-rw-r--r-- | libpod/volume.go | 36 | ||||
-rw-r--r-- | libpod/volume_inspect.go | 11 |
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 |