summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/source/markdown/podman-create.1.md10
-rw-r--r--docs/source/markdown/podman-pause.1.md2
-rw-r--r--docs/source/markdown/podman-run.1.md10
-rw-r--r--libpod/container_internal_linux.go1
-rw-r--r--libpod/container_path_resolution.go2
-rw-r--r--libpod/oci_conmon_linux.go2
-rw-r--r--libpod/runtime.go4
-rw-r--r--pkg/api/handlers/libpod/volumes.go17
-rw-r--r--pkg/domain/entities/volumes.go4
-rw-r--r--pkg/specgen/generate/storage.go3
-rw-r--r--pkg/specgenutil/volumes.go12
-rw-r--r--test/apiv2/python/rest_api/test_v2_0_0_volume.py67
-rw-r--r--test/e2e/run_test.go8
-rw-r--r--test/system/070-build.bats9
-rw-r--r--utils/utils.go16
15 files changed, 147 insertions, 20 deletions
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index ea9042861..3ff736adb 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -638,6 +638,16 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
. U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+ Options specific to devpts:
+
+ · uid: UID of the file owner (default 0).
+
+ · gid: GID of the file owner (default 0).
+
+ · mode: permission mask for the file (default 600).
+
+ · max: maximum number of PTYs (default 1048576).
+
#### **--name**=*name*
Assign a name to the container
diff --git a/docs/source/markdown/podman-pause.1.md b/docs/source/markdown/podman-pause.1.md
index 123362822..90ea1e32e 100644
--- a/docs/source/markdown/podman-pause.1.md
+++ b/docs/source/markdown/podman-pause.1.md
@@ -31,7 +31,7 @@ podman pause 860a4b23
Pause all **running** containers.
```
-podman stop -a
+podman pause -a
```
## SEE ALSO
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index b5f3130fe..a1170253f 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -665,6 +665,16 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
. U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+ Options specific to devpts:
+
+ · uid: UID of the file owner (default 0).
+
+ · gid: GID of the file owner (default 0).
+
+ · mode: permission mask for the file (default 600).
+
+ · max: maximum number of PTYs (default 1048576).
+
#### **--name**=*name*
Assign a name to the container.
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index d3151f7e0..2fd519990 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -1546,6 +1546,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
logrus.Debugf("Restored container %s", c.ID())
c.state.State = define.ContainerStateRunning
+ c.state.Checkpointed = false
if !options.Keep {
// Delete all checkpoint related files. At this point, in theory, all files
diff --git a/libpod/container_path_resolution.go b/libpod/container_path_resolution.go
index bb2ef1a73..7db23b783 100644
--- a/libpod/container_path_resolution.go
+++ b/libpod/container_path_resolution.go
@@ -161,7 +161,7 @@ func isPathOnBindMount(c *Container, containerPath string) bool {
if cleanedContainerPath == filepath.Clean(m.Destination) {
return true
}
- for dest := m.Destination; dest != "/"; dest = filepath.Dir(dest) {
+ for dest := m.Destination; dest != "/" && dest != "."; dest = filepath.Dir(dest) {
if cleanedContainerPath == dest {
return true
}
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 1719b2dfa..db906fabb 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -1016,7 +1016,7 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
}
if ctr.config.CgroupsMode == cgroupSplit {
- if err := utils.MoveUnderCgroupSubtree("supervisor"); err != nil {
+ if err := utils.MoveUnderCgroupSubtree("runtime"); err != nil {
return err
}
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 950ac65eb..b01f8dd13 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -545,9 +545,7 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
if became {
// Check if the pause process was created. If it was created, then
// move it to its own systemd scope.
- if _, err = os.Stat(pausePid); err == nil {
- utils.MovePauseProcessToScope(pausePid)
- }
+ utils.MovePauseProcessToScope(pausePid)
os.Exit(ret)
}
}
diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
index 3ba39b860..ffdb30551 100644
--- a/pkg/api/handlers/libpod/volumes.go
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -29,12 +29,13 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
}{
// override any golang type defaults
}
- input := entities.VolumeCreateOptions{}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError,
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
return
}
+
+ input := entities.VolumeCreateOptions{}
// decode params from body
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
@@ -47,9 +48,19 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
if len(input.Driver) > 0 {
volumeOptions = append(volumeOptions, libpod.WithVolumeDriver(input.Driver))
}
- if len(input.Label) > 0 {
- volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(input.Label))
+
+ // Label provided for compatibility.
+ labels := make(map[string]string, len(input.Label)+len(input.Labels))
+ for k, v := range input.Label {
+ labels[k] = v
}
+ for k, v := range input.Labels {
+ labels[k] = v
+ }
+ if len(labels) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(labels))
+ }
+
if len(input.Options) > 0 {
parsedOptions, err := parse.VolumeOptions(input.Options)
if err != nil {
diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go
index 2ecfb4446..9b2a170e2 100644
--- a/pkg/domain/entities/volumes.go
+++ b/pkg/domain/entities/volumes.go
@@ -78,8 +78,10 @@ type VolumeCreateOptions struct {
Name string `schema:"name"`
// Volume driver to use
Driver string `schema:"driver"`
- // User-defined key/value metadata.
+ // User-defined key/value metadata. Provided for compatibility
Label map[string]string `schema:"label"`
+ // User-defined key/value metadata. Preferred field, will override Label
+ Labels map[string]string `schema:"labels"`
// Mapping of driver options and values.
Options map[string]string `schema:"opts"`
}
diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go
index 3fde1a1b4..30248a886 100644
--- a/pkg/specgen/generate/storage.go
+++ b/pkg/specgen/generate/storage.go
@@ -214,9 +214,6 @@ func getImageVolumes(ctx context.Context, img *libimage.Image, s *specgen.SpecGe
}
for volume := range inspect.Config.Volumes {
logrus.Debugf("Image has volume at %q", volume)
- if err = parse.ValidateVolumeCtrDir(volume); err != nil {
- return nil, nil, err
- }
cleanDest := filepath.Clean(volume)
switch mode {
case "", "anonymous":
diff --git a/pkg/specgenutil/volumes.go b/pkg/specgenutil/volumes.go
index 3ce96164f..184bfadf8 100644
--- a/pkg/specgenutil/volumes.go
+++ b/pkg/specgenutil/volumes.go
@@ -360,7 +360,7 @@ func getBindMount(args []string) (spec.Mount, error) {
// Since Docker ignores this option so shall we.
continue
default:
- return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0])
}
}
@@ -460,7 +460,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) {
// Since Docker ignores this option so shall we.
continue
default:
- return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0])
}
}
@@ -483,6 +483,8 @@ func getDevptsMount(args []string) (spec.Mount, error) {
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
switch kv[0] {
+ case "uid", "gid", "mode", "ptxmode", "newinstance", "max":
+ newMount.Options = append(newMount.Options, val)
case "target", "dst", "destination":
if len(kv) == 1 {
return newMount, errors.Wrapf(optionArgError, kv[0])
@@ -493,7 +495,7 @@ func getDevptsMount(args []string) (spec.Mount, error) {
newMount.Destination = filepath.Clean(kv[1])
setDest = true
default:
- return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0])
}
}
@@ -573,7 +575,7 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) {
// Since Docker ignores this option so shall we.
continue
default:
- return nil, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ return nil, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0])
}
}
@@ -624,7 +626,7 @@ func getImageVolume(args []string) (*specgen.ImageVolume, error) {
// Since Docker ignores this option so shall we.
continue
default:
- return nil, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ return nil, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0])
}
}
diff --git a/test/apiv2/python/rest_api/test_v2_0_0_volume.py b/test/apiv2/python/rest_api/test_v2_0_0_volume.py
index f5231e17c..2156318b0 100644
--- a/test/apiv2/python/rest_api/test_v2_0_0_volume.py
+++ b/test/apiv2/python/rest_api/test_v2_0_0_volume.py
@@ -7,7 +7,7 @@ from .fixtures import APITestCase
class VolumeTestCase(APITestCase):
- def test_volume(self):
+ def test_volume_crud(self):
name = f"Volume_{random.getrandbits(160):x}"
ls = requests.get(self.podman_url + "/v1.40/volumes")
@@ -70,6 +70,71 @@ class VolumeTestCase(APITestCase):
self.assertIn(name, payload["VolumesDeleted"])
self.assertGreater(payload["SpaceReclaimed"], 0)
+ def test_volume_label(self):
+ name = f"Volume_{random.getrandbits(160):x}"
+ expected = {
+ "Production": "False",
+ "Database": "Foxbase",
+ }
+
+ create = requests.post(
+ self.podman_url + "/v4.0.0/libpod/volumes/create",
+ json={"name": name, "label": expected},
+ )
+ self.assertEqual(create.status_code, 201, create.text)
+
+ inspect = requests.get(self.podman_url + f"/v4.0.0/libpod/volumes/{name}/json")
+ self.assertEqual(inspect.status_code, 200, inspect.text)
+
+ volume = inspect.json()
+ self.assertIn("Labels", volume)
+ self.assertNotIn("Label", volume)
+ self.assertDictEqual(expected, volume["Labels"])
+
+ def test_volume_labels(self):
+ name = f"Volume_{random.getrandbits(160):x}"
+ expected = {
+ "Production": "False",
+ "Database": "Foxbase",
+ }
+
+ create = requests.post(
+ self.podman_url + "/v4.0.0/libpod/volumes/create",
+ json={"name": name, "labels": expected},
+ )
+ self.assertEqual(create.status_code, 201, create.text)
+
+ inspect = requests.get(self.podman_url + f"/v4.0.0/libpod/volumes/{name}/json")
+ self.assertEqual(inspect.status_code, 200, inspect.text)
+
+ volume = inspect.json()
+ self.assertIn("Labels", volume)
+ self.assertDictEqual(expected, volume["Labels"])
+
+ def test_volume_label_override(self):
+ name = f"Volume_{random.getrandbits(160):x}"
+ create = requests.post(
+ self.podman_url + "/v4.0.0/libpod/volumes/create",
+ json={
+ "Name": name,
+ "Label": {
+ "Database": "dbase",
+ },
+ "Labels": {
+ "Database": "sqlserver",
+ },
+ },
+ )
+ self.assertEqual(create.status_code, 201, create.text)
+
+ inspect = requests.get(self.podman_url + f"/v4.0.0/libpod/volumes/{name}/json")
+ self.assertEqual(inspect.status_code, 200, inspect.text)
+
+ volume = inspect.json()
+ self.assertIn("Labels", volume)
+ self.assertNotIn("Label", volume)
+ self.assertDictEqual({"Database": "sqlserver"}, volume["Labels"])
+
if __name__ == "__main__":
unittest.main()
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 2e0f626d0..95660bfc9 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -1198,6 +1198,14 @@ USER mail`, BB)
Expect(session.OutputToString()).To(ContainSubstring("devpts"))
})
+ It("podman run --mount type=devpts,target=/dev/pts with uid, gid and mode", func() {
+ // runc doesn't seem to honor uid= so avoid testing it
+ session := podmanTest.Podman([]string{"run", "-t", "--mount", "type=devpts,target=/dev/pts,uid=1000,gid=1001,mode=123", fedoraMinimal, "stat", "-c%g-%a", "/dev/pts/0"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).To(ContainSubstring("1001-123"))
+ })
+
It("podman run --pod automatically", func() {
session := podmanTest.Podman([]string{"run", "-d", "--pod", "new:foobar", ALPINE, "nc", "-l", "-p", "8686"})
session.WaitWithDefaultTimeout()
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index d3dc14d81..1d24eef15 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -39,6 +39,7 @@ EOF
cat >$dockerfile <<EOF
FROM $IMAGE
RUN echo $rand_content > /$rand_filename
+VOLUME ['/etc/foo', '/etc/bar']
EOF
run_podman buildx build --load -t build_test --format=docker $tmpdir
@@ -47,6 +48,14 @@ EOF
run_podman run --rm build_test cat /$rand_filename
is "$output" "$rand_content" "reading generated file in image"
+ # Make sure the volumes are created at surprising yet Docker-compatible
+ # destinations (see bugzilla.redhat.com/show_bug.cgi?id=2014149).
+ run_podman run --rm build_test find /[ /etc/bar\] -print
+ is "$output" "/\[
+/\[/etc
+/\[/etc/foo,
+/etc/bar]" "weird VOLUME gets converted to directories with brackets and comma"
+
run_podman rmi -f build_test
}
diff --git a/utils/utils.go b/utils/utils.go
index 55af41bb3..109ae088b 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -14,6 +14,7 @@ import (
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/cgroups"
"github.com/containers/storage/pkg/archive"
+ "github.com/godbus/dbus/v5"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -177,13 +178,26 @@ func RunsOnSystemd() bool {
func moveProcessToScope(pidPath, slice, scope string) error {
data, err := ioutil.ReadFile(pidPath)
if err != nil {
+ // do not raise an error if the file doesn't exist
+ if os.IsNotExist(err) {
+ return nil
+ }
return errors.Wrapf(err, "cannot read pid file %s", pidPath)
}
pid, err := strconv.ParseUint(string(data), 10, 0)
if err != nil {
return errors.Wrapf(err, "cannot parse pid file %s", pidPath)
}
- return RunUnderSystemdScope(int(pid), slice, scope)
+ err = RunUnderSystemdScope(int(pid), slice, scope)
+
+ // If the PID is not valid anymore, do not return an error.
+ if dbusErr, ok := err.(dbus.Error); ok {
+ if dbusErr.Name == "org.freedesktop.DBus.Error.UnixProcessIdUnknown" {
+ return nil
+ }
+ }
+
+ return err
}
// MovePauseProcessToScope moves the pause process used for rootless mode to keep the namespaces alive to