summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/images/list.go12
-rw-r--r--cmd/podman/inspect.go18
-rw-r--r--cmd/podman/main.go5
-rw-r--r--docs/source/markdown/podman-inspect.1.md14
-rw-r--r--libpod/container_internal.go52
-rw-r--r--libpod/options.go13
-rw-r--r--libpod/runtime_ctr.go2
-rw-r--r--libpod/volume.go36
-rw-r--r--libpod/volume_inspect.go11
-rw-r--r--pkg/api/handlers/libpod/images.go1
-rw-r--r--pkg/api/handlers/libpod/volumes.go30
-rw-r--r--pkg/api/handlers/types.go2
-rw-r--r--pkg/domain/entities/images.go2
-rw-r--r--pkg/domain/infra/abi/images_list.go2
-rw-r--r--pkg/domain/infra/abi/volumes.go26
-rw-r--r--pkg/spec/createconfig.go9
-rwxr-xr-xtest/apiv2/test-apiv22
-rw-r--r--test/e2e/run_userns_test.go27
-rw-r--r--utils/utils_supported.go4
19 files changed, 226 insertions, 42 deletions
diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go
index de7cca40d..53be82dda 100644
--- a/cmd/podman/images/list.go
+++ b/cmd/podman/images/list.go
@@ -128,7 +128,7 @@ func writeID(imgs []imageReporter) error {
func writeJSON(images []imageReporter) error {
type image struct {
entities.ImageSummary
- Created string
+ Created int64
CreatedAt string
}
@@ -136,8 +136,8 @@ func writeJSON(images []imageReporter) error {
for _, e := range images {
var h image
h.ImageSummary = e.ImageSummary
- h.Created = units.HumanDuration(time.Since(e.ImageSummary.Created)) + " ago"
- h.CreatedAt = e.ImageSummary.Created.Format(time.RFC3339Nano)
+ h.Created = e.ImageSummary.Created
+ h.CreatedAt = e.created().Format(time.RFC3339Nano)
h.RepoTags = nil
imgs = append(imgs, h)
@@ -284,11 +284,11 @@ func (i imageReporter) ID() string {
}
func (i imageReporter) Created() string {
- return units.HumanDuration(time.Since(i.ImageSummary.Created)) + " ago"
+ return units.HumanDuration(time.Since(i.created())) + " ago"
}
func (i imageReporter) created() time.Time {
- return i.ImageSummary.Created
+ return time.Unix(i.ImageSummary.Created, 0).UTC()
}
func (i imageReporter) Size() string {
@@ -302,7 +302,7 @@ func (i imageReporter) History() string {
}
func (i imageReporter) CreatedAt() string {
- return i.ImageSummary.Created.String()
+ return i.created().String()
}
func (i imageReporter) CreatedSince() string {
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index 12e11d0f5..befdeb445 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -8,12 +8,22 @@ import (
)
var (
+ inspectDescription = `Displays the low-level information on an object identified by name or ID.
+ For more inspection options, see:
+
+ podman container inspect
+ podman image inspect
+ podman network inspect
+ podman pod inspect
+ podman volume inspect`
+
// Command: podman _inspect_ Object_ID
inspectCmd = &cobra.Command{
- Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID} [...]",
- Short: "Display the configuration of object denoted by ID",
- Long: "Displays the low-level information on an object identified by name or ID",
- RunE: inspectExec,
+ Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID} [...]",
+ Short: "Display the configuration of object denoted by ID",
+ RunE: inspectExec,
+ Long: inspectDescription,
+ TraverseChildren: true,
Example: `podman inspect fedora
podman inspect --type image fedora
podman inspect CtrID ImgID
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 7a015b300..636538131 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -29,6 +29,11 @@ func main() {
return
}
+ // Hard code TMPDIR functions to use /var/tmp, if user did not override
+ if _, ok := os.LookupEnv("TMPDIR"); !ok {
+ os.Setenv("TMPDIR", "/var/tmp")
+ }
+
cfg := registry.PodmanConfig()
for _, c := range registry.Commands {
for _, m := range c.Mode {
diff --git a/docs/source/markdown/podman-inspect.1.md b/docs/source/markdown/podman-inspect.1.md
index 4998772c3..a1dcd1a0e 100644
--- a/docs/source/markdown/podman-inspect.1.md
+++ b/docs/source/markdown/podman-inspect.1.md
@@ -6,15 +6,21 @@ podman\-inspect - Display a container or image's configuration
## SYNOPSIS
**podman inspect** [*options*] *name* [...]
-**podman image inspect** [*options*] *image*
-
-**podman container inspect** [*options*] *container*
-
## DESCRIPTION
+
This displays the low-level information on containers and images identified by name or ID. By default, this will render
all results in a JSON array. If the container and image have the same name, this will return container JSON for
unspecified type. If a format is specified, the given template will be executed for each result.
+For more inspection options, see:
+
+ podman container inspect
+ podman image inspect
+ podman network inspect
+ podman pod inspect
+ podman volume inspect
+
+
## OPTIONS
**--type**, **-t**=*type*
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index db64f5eeb..27b795871 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/options.go b/libpod/options.go
index 28be1bc03..4041fb1cf 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1497,6 +1497,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/runtime_ctr.go b/libpod/runtime_ctr.go
index dd6602acb..74647dab8 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 b29ac7ddf..58d1f81a6 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 136f9da5e..2be0aeaec 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
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 54e202103..ebcb1f460 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -112,7 +112,6 @@ func GetImages(w http.ResponseWriter, r *http.Request) {
return
}
// libpod has additional fields that we need to populate.
- is.Created = img.Created()
is.ReadOnly = img.IsReadOnly()
summaries[j] = is
}
diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
index 4b3b5430b..6523244f3 100644
--- a/pkg/api/handlers/libpod/volumes.go
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -86,6 +86,17 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) {
utils.VolumeNotFound(w, name, err)
return
}
+ var uid, gid int
+ uid, err = vol.UID()
+ if err != nil {
+ utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err)
+ return
+ }
+ gid, err = vol.GID()
+ if err != nil {
+ utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err)
+ return
+ }
volResponse := entities.VolumeConfigResponse{
Name: vol.Name(),
Driver: vol.Driver(),
@@ -94,8 +105,8 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) {
Labels: vol.Labels(),
Scope: vol.Scope(),
Options: vol.Options(),
- UID: vol.UID(),
- GID: vol.GID(),
+ UID: uid,
+ GID: gid,
}
utils.WriteResponse(w, http.StatusOK, volResponse)
}
@@ -130,6 +141,17 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
}
volumeConfigs := make([]*entities.VolumeListReport, 0, len(vols))
for _, v := range vols {
+ var uid, gid int
+ uid, err = v.UID()
+ if err != nil {
+ utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err)
+ return
+ }
+ gid, err = v.GID()
+ if err != nil {
+ utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err)
+ return
+ }
config := entities.VolumeConfigResponse{
Name: v.Name(),
Driver: v.Driver(),
@@ -138,8 +160,8 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
Labels: v.Labels(),
Scope: v.Scope(),
Options: v.Options(),
- UID: v.UID(),
- GID: v.GID(),
+ UID: uid,
+ GID: gid,
}
volumeConfigs = append(volumeConfigs, &entities.VolumeListReport{VolumeConfigResponse: config})
}
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index c1e84ab5a..72e1a756e 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -221,7 +221,7 @@ func ImageToImageSummary(l *libpodImage.Image) (*entities.ImageSummary, error) {
ID: l.ID(),
ParentId: l.Parent,
RepoTags: repoTags,
- Created: l.Created(),
+ Created: l.Created().Unix(),
Size: int64(*size),
SharedSize: 0,
VirtualSize: l.VirtualSize,
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 81f52fef5..27f887e8e 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -53,7 +53,7 @@ type ImageSummary struct {
ID string `json:"Id"`
ParentId string `json:",omitempty"` // nolint
RepoTags []string `json:",omitempty"`
- Created time.Time `json:",omitempty"`
+ Created int64 `json:",omitempty"`
Size int64 `json:",omitempty"`
SharedSize int `json:",omitempty"`
VirtualSize int64 `json:",omitempty"`
diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go
index 98c041c15..92ab0a998 100644
--- a/pkg/domain/infra/abi/images_list.go
+++ b/pkg/domain/infra/abi/images_list.go
@@ -52,7 +52,7 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions)
ID: img.ID(),
ConfigDigest: string(img.ConfigDigest),
- Created: img.Created(),
+ Created: img.Created().Unix(),
Dangling: img.Dangling(),
Digest: string(img.Digest()),
Digests: digests,
diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go
index 702e11003..36847dd79 100644
--- a/pkg/domain/infra/abi/volumes.go
+++ b/pkg/domain/infra/abi/volumes.go
@@ -95,6 +95,15 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
}
reports := make([]*entities.VolumeInspectReport, 0, len(vols))
for _, v := range vols {
+ var uid, gid int
+ uid, err = v.UID()
+ if err != nil {
+ return nil, err
+ }
+ gid, err = v.GID()
+ if err != nil {
+ return nil, err
+ }
config := entities.VolumeConfigResponse{
Name: v.Name(),
Driver: v.Driver(),
@@ -103,8 +112,8 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
Labels: v.Labels(),
Scope: v.Scope(),
Options: v.Options(),
- UID: v.UID(),
- GID: v.GID(),
+ UID: uid,
+ GID: gid,
}
reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: &config})
}
@@ -141,6 +150,15 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL
}
reports := make([]*entities.VolumeListReport, 0, len(vols))
for _, v := range vols {
+ var uid, gid int
+ uid, err = v.UID()
+ if err != nil {
+ return nil, err
+ }
+ gid, err = v.GID()
+ if err != nil {
+ return nil, err
+ }
config := entities.VolumeConfigResponse{
Name: v.Name(),
Driver: v.Driver(),
@@ -149,8 +167,8 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL
Labels: v.Labels(),
Scope: v.Scope(),
Options: v.Options(),
- UID: v.UID(),
- GID: v.GID(),
+ UID: uid,
+ GID: gid,
}
reports = append(reports, &entities.VolumeListReport{VolumeConfigResponse: config})
}
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index e19c582b5..a04afa00f 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -287,10 +287,11 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l
options = append(options, libpod.WithCommand(c.UserCommand))
}
- // Add entrypoint unconditionally
- // If it's empty it's because it was explicitly set to "" or the image
- // does not have one
- options = append(options, libpod.WithEntrypoint(c.Entrypoint))
+ // Add entrypoint if it was set
+ // If it's empty it's because it was explicitly set to ""
+ if c.Entrypoint != nil {
+ options = append(options, libpod.WithEntrypoint(c.Entrypoint))
+ }
// TODO: MNT, USER, CGROUP
options = append(options, libpod.WithStopSignal(c.StopSignal))
diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2
index 7a3518df2..d0bf28b9a 100755
--- a/test/apiv2/test-apiv2
+++ b/test/apiv2/test-apiv2
@@ -84,7 +84,7 @@ function like() {
if expr "$actual" : "$expect" &>/dev/null; then
# On success, include expected value; this helps readers understand
- _show_ok 1 "$testname~$expect"
+ _show_ok 1 "$testname ('$actual') ~ $expect"
return
fi
_show_ok 0 "$testname" "~ $expect" "$actual"
diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go
index be0981408..3e55f56c0 100644
--- a/test/e2e/run_userns_test.go
+++ b/test/e2e/run_userns_test.go
@@ -245,4 +245,31 @@ var _ = Describe("Podman UserNS support", func() {
ok, _ := session.GrepString("4998")
Expect(ok).To(BeTrue())
})
+
+ It("podman --user with volume", func() {
+ tests := []struct {
+ uid, gid, arg, vol string
+ }{
+ {"0", "0", "0:0", "vol-0"},
+ {"1000", "0", "1000", "vol-1"},
+ {"1000", "1000", "1000:1000", "vol-2"},
+ }
+
+ for _, tt := range tests {
+ session := podmanTest.Podman([]string{"run", "-d", "--user", tt.arg, "--mount", "type=volume,src=" + tt.vol + ",dst=/home/user", "alpine", "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ inspectUID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .UID }}", tt.vol})
+ inspectUID.WaitWithDefaultTimeout()
+ Expect(inspectUID.ExitCode()).To(Equal(0))
+ Expect(inspectUID.OutputToString()).To(Equal(tt.uid))
+
+ // Make sure we're defaulting to 0.
+ inspectGID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .GID }}", tt.vol})
+ inspectGID.WaitWithDefaultTimeout()
+ Expect(inspectGID.ExitCode()).To(Equal(0))
+ Expect(inspectGID.OutputToString()).To(Equal(tt.gid))
+ }
+ })
})
diff --git a/utils/utils_supported.go b/utils/utils_supported.go
index 201ddb57b..4258e6d7a 100644
--- a/utils/utils_supported.go
+++ b/utils/utils_supported.go
@@ -64,7 +64,7 @@ func getCgroupProcess(procFile string) (string, error) {
cgroup := "/"
for scanner.Scan() {
line := scanner.Text()
- parts := strings.Split(line, ":")
+ parts := strings.SplitN(line, ":", 3)
if len(parts) != 3 {
return "", errors.Errorf("cannot parse cgroup line %q", line)
}
@@ -116,7 +116,7 @@ func MoveUnderCgroupSubtree(subtree string) error {
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
- parts := strings.Split(line, ":")
+ parts := strings.SplitN(line, ":", 3)
if len(parts) != 3 {
return errors.Errorf("cannot parse cgroup line %q", line)
}