summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/stale.yml25
-rwxr-xr-xAPI.md8
-rw-r--r--Makefile2
-rw-r--r--README.md2
-rw-r--r--cmd/podman/cliconfig/config.go1
-rw-r--r--cmd/podman/main_local.go9
-rw-r--r--cmd/podman/network_create.go2
-rw-r--r--cmd/podman/run.go1
-rw-r--r--cmd/podman/shared/volumes_shared.go62
-rw-r--r--cmd/podman/stats.go7
-rw-r--r--cmd/podman/varlink/io.podman.varlink4
-rw-r--r--cmd/podman/volume_create.go2
-rw-r--r--completions/bash/podman1
-rw-r--r--docs/podman-create.1.md40
-rw-r--r--docs/podman-network-create.1.md5
-rw-r--r--docs/podman-run.1.md28
-rw-r--r--docs/podman-volume-create.1.md13
-rw-r--r--docs/rtd/Makefile123
-rw-r--r--docs/rtd/make.bat35
-rw-r--r--docs/rtd/requirements.txt4
-rw-r--r--docs/rtd/source/Commands.rst107
-rw-r--r--docs/rtd/source/Introduction.rst2
-rw-r--r--docs/rtd/source/Reference.rst2
-rw-r--r--docs/rtd/source/Tutorials.rst2
-rw-r--r--docs/rtd/source/conf.py57
-rw-r--r--docs/rtd/source/index.rst26
-rw-r--r--docs/rtd/source/man/generate.rst6
-rw-r--r--docs/rtd/source/man/healthcheck.rst4
-rw-r--r--docs/rtd/source/man/image.rst35
-rw-r--r--docs/rtd/source/man/managecontainers.rst64
-rw-r--r--docs/rtd/source/man/network.rst10
-rw-r--r--docs/rtd/source/man/play.rst4
-rw-r--r--docs/rtd/source/man/pod.rst30
-rw-r--r--docs/rtd/source/man/system.rst12
-rw-r--r--docs/rtd/source/man/volume.rst11
-rw-r--r--docs/tutorials/rootless_tutorial.md4
-rw-r--r--libpod/container.go9
-rw-r--r--libpod/container_api.go44
-rw-r--r--libpod/container_internal.go45
-rw-r--r--libpod/container_internal_linux.go1
-rw-r--r--libpod/networking_linux.go28
-rw-r--r--libpod/oci_conmon_linux.go6
-rw-r--r--libpod/options.go16
-rw-r--r--libpod/runtime.go52
-rw-r--r--libpod/runtime_ctr.go31
-rw-r--r--libpod/runtime_volume_linux.go9
-rw-r--r--libpod/volume_inspect.go3
-rw-r--r--pkg/adapter/containers.go12
-rw-r--r--pkg/adapter/containers_remote.go9
-rw-r--r--pkg/adapter/network.go14
-rw-r--r--pkg/adapter/runtime.go7
-rw-r--r--pkg/adapter/runtime_remote.go37
-rw-r--r--pkg/network/config.go14
-rw-r--r--pkg/network/devices.go17
-rw-r--r--pkg/network/files.go26
-rw-r--r--pkg/network/netconflist.go21
-rw-r--r--pkg/spec/spec.go24
-rw-r--r--pkg/spec/storage.go22
-rw-r--r--pkg/util/utils.go2
-rw-r--r--pkg/varlinkapi/attach.go1
-rw-r--r--pkg/varlinkapi/virtwriter/virtwriter.go24
-rw-r--r--pkg/varlinkapi/volumes.go25
-rw-r--r--test/e2e/run_signal_test.go7
-rw-r--r--test/e2e/run_volume_test.go84
-rw-r--r--test/e2e/stats_test.go11
-rw-r--r--test/e2e/volume_create_test.go20
-rw-r--r--test/e2e/volume_inspect_test.go13
-rw-r--r--test/system/015-help.bats8
68 files changed, 1221 insertions, 171 deletions
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 000000000..44cb82ff0
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,25 @@
+name: Mark stale issues and pull requests
+
+# Please refer to https://github.com/actions/stale/blob/master/action.yml
+# to see all config knobs of the stale action.
+
+on:
+ schedule:
+ - cron: "0 0 * * *"
+
+jobs:
+ stale:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/stale@v1
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ stale-issue-message: 'This issue had no activity for 30 days. In the absence of activity or the "do-not-close" label, the issue will be automatically closed within 7 days.'
+ stale-pr-message: 'This pull request had no activity for 30 days. In the absence of activity or the "do-not-close" label, the pull request will be automatically closed within 7 days.'
+ stale-issue-label: 'stale-issue'
+ stale-pr-label: 'stale-pr'
+ days-before-stale: 30
+ days-before-close: 7
+ exempt-pr-label: 'do-not-close'
diff --git a/API.md b/API.md
index a2a093bec..1cbdacb12 100755
--- a/API.md
+++ b/API.md
@@ -107,6 +107,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func InspectPod(name: string) string](#InspectPod)
+[func InspectVolume(name: string) string](#InspectVolume)
+
[func KillContainer(name: string, signal: int) string](#KillContainer)
[func KillPod(name: string, signal: int) string](#KillPod)
@@ -804,6 +806,12 @@ method InspectPod(name: [string](https://godoc.org/builtin#string)) [string](htt
InspectPod takes the name or ID of an image and returns a string representation of data associated with the
pod. You must serialize the string into JSON to use it further. A [PodNotFound](#PodNotFound) error will
be returned if the pod cannot be found.
+### <a name="InspectVolume"></a>func InspectVolume
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method InspectVolume(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
+InspectVolume inspects a single volume. Returns inspect JSON in the form of a
+string.
### <a name="KillContainer"></a>func KillContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
diff --git a/Makefile b/Makefile
index a19d4848f..77502f911 100644
--- a/Makefile
+++ b/Makefile
@@ -454,7 +454,7 @@ uninstall:
.PHONY: .gitvalidation
.gitvalidation: .gopathok
- GIT_CHECK_EXCLUDE="./vendor" $(GOBIN)/git-validation -v -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..$(HEAD)
+ GIT_CHECK_EXCLUDE="./vendor:docs/rtd/make.bat" $(GOBIN)/git-validation -v -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..$(HEAD)
.PHONY: install.tools
install.tools: .install.gitvalidation .install.gometalinter .install.md2man .install.ginkgo .install.golangci-lint ## Install needed tools
diff --git a/README.md b/README.md
index 485ca7b6f..166a4bebe 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
Libpod provides a library for applications looking to use the Container Pod concept,
popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`. Podman manages pods, containers, container images, and container volumes.
-* [Latest Version: 1.6.0](https://github.com/containers/libpod/releases/latest)
+* [Latest Version: 1.6.2](https://github.com/containers/libpod/releases/latest)
* [Continuous Integration:](contrib/cirrus/README.md) [![Build Status](https://api.cirrus-ci.com/github/containers/libpod.svg)](https://cirrus-ci.com/github/containers/libpod/master)
* [GoDoc: ![GoDoc](https://godoc.org/github.com/containers/libpod/libpod?status.svg)](https://godoc.org/github.com/containers/libpod/libpod)
* Automated continuous release downloads (including remote-client):
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index 4831b7971..86258a543 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -267,6 +267,7 @@ type MountValues struct {
type NetworkCreateValues struct {
PodmanCommand
Driver string
+ DisableDNS bool
Gateway net.IP
Internal bool
IPamDriver string
diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go
index bdffb6b1e..202d93b35 100644
--- a/cmd/podman/main_local.go
+++ b/cmd/podman/main_local.go
@@ -174,14 +174,13 @@ func setupRootless(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
-
+ conf, err := runtime.GetConfig()
+ if err != nil {
+ return err
+ }
if !ownsCgroup {
unitName := fmt.Sprintf("podman-%d.scope", os.Getpid())
if err := utils.RunUnderSystemdScope(os.Getpid(), "user.slice", unitName); err != nil {
- conf, err2 := runtime.GetConfig()
- if err2 != nil {
- return err2
- }
if conf.CgroupManager == libpod.SystemdCgroupsManager {
logrus.Warnf("Failed to add podman to systemd sandbox cgroup: %v", err)
} else {
diff --git a/cmd/podman/network_create.go b/cmd/podman/network_create.go
index 11f13faad..6710883ae 100644
--- a/cmd/podman/network_create.go
+++ b/cmd/podman/network_create.go
@@ -46,7 +46,7 @@ func init() {
// TODO enable when IPv6 is working
//flags.BoolVar(&networkCreateCommand.IPV6, "IPv6", false, "enable IPv6 networking")
flags.IPNetVar(&networkCreateCommand.Network, "subnet", net.IPNet{}, "subnet in CIDR format")
-
+ flags.BoolVar(&networkCreateCommand.DisableDNS, "disable-dns", false, "disable dns plugin")
}
func networkcreateCmd(c *cliconfig.NetworkCreateValues) error {
diff --git a/cmd/podman/run.go b/cmd/podman/run.go
index 4836c99dc..7aa4cb3c4 100644
--- a/cmd/podman/run.go
+++ b/cmd/podman/run.go
@@ -45,7 +45,6 @@ func runCmd(c *cliconfig.RunValues) error {
span, _ := opentracing.StartSpanFromContext(Ctx, "runCmd")
defer span.Finish()
}
-
if err := createInit(&c.PodmanCommand); err != nil {
return err
}
diff --git a/cmd/podman/shared/volumes_shared.go b/cmd/podman/shared/volumes_shared.go
index 912615cad..74c0ce011 100644
--- a/cmd/podman/shared/volumes_shared.go
+++ b/cmd/podman/shared/volumes_shared.go
@@ -2,8 +2,13 @@ package shared
import (
"context"
+ "strconv"
+ "strings"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// Remove given set of volumes
@@ -45,3 +50,60 @@ func SharedRemoveVolumes(ctx context.Context, runtime *libpod.Runtime, vols []st
return success, failed, nil
}
+
+// Handle volume options from CLI.
+// Parse "o" option to find UID, GID.
+func ParseVolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) {
+ libpodOptions := []libpod.VolumeCreateOption{}
+ volumeOptions := make(map[string]string)
+
+ for key, value := range opts {
+ switch key {
+ case "o":
+ // o has special handling to parse out UID, GID.
+ // These are separate Libpod options.
+ splitVal := strings.Split(value, ",")
+ finalVal := []string{}
+ for _, o := range splitVal {
+ // Options will be formatted as either "opt" or
+ // "opt=value"
+ splitO := strings.SplitN(o, "=", 2)
+ switch strings.ToLower(splitO[0]) {
+ case "uid":
+ if len(splitO) != 2 {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "uid option must provide a UID")
+ }
+ intUID, err := strconv.Atoi(splitO[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot convert UID %s to integer", splitO[1])
+ }
+ logrus.Debugf("Removing uid= from options and adding WithVolumeUID for UID %d", intUID)
+ libpodOptions = append(libpodOptions, libpod.WithVolumeUID(intUID))
+ case "gid":
+ if len(splitO) != 2 {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "gid option must provide a GID")
+ }
+ intGID, err := strconv.Atoi(splitO[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot convert GID %s to integer", splitO[1])
+ }
+ logrus.Debugf("Removing gid= from options and adding WithVolumeGID for GID %d", intGID)
+ libpodOptions = append(libpodOptions, libpod.WithVolumeGID(intGID))
+ default:
+ finalVal = append(finalVal, o)
+ }
+ }
+ if len(finalVal) > 0 {
+ volumeOptions[key] = strings.Join(finalVal, ",")
+ }
+ default:
+ volumeOptions[key] = value
+ }
+ }
+
+ if len(volumeOptions) > 0 {
+ libpodOptions = append(libpodOptions, libpod.WithVolumeOptions(volumeOptions))
+ }
+
+ return libpodOptions, nil
+}
diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go
index 25514ec75..f8c476386 100644
--- a/cmd/podman/stats.go
+++ b/cmd/podman/stats.go
@@ -35,7 +35,7 @@ var (
statsDescription = "Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers."
_statsCommand = &cobra.Command{
- Use: "stats [flags] CONTAINER [CONTAINER...]",
+ Use: "stats [flags] [CONTAINER...]",
Short: "Display a live stream of container resource usage statistics",
Long: statsDescription,
RunE: func(cmd *cobra.Command, args []string) error {
@@ -44,9 +44,6 @@ var (
statsCommand.Remote = remoteclient
return statsCmd(&statsCommand)
},
- Args: func(cmd *cobra.Command, args []string) error {
- return checkAllAndLatest(cmd, args, false)
- },
Example: `podman stats --all --no-stream
podman stats ctrID
podman stats --no-stream --format "table {{.ID}} {{.Name}} {{.MemUsage}}" ctrID`,
@@ -92,8 +89,6 @@ func statsCmd(c *cliconfig.StatsValues) error {
if ctr > 1 {
return errors.Errorf("--all, --latest and containers cannot be used together")
- } else if ctr == 0 {
- return errors.Errorf("you must specify --all, --latest, or at least one container")
}
runtime, err := libpodruntime.GetRuntime(getContext(), &c.PodmanCommand)
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 13e8394fb..dca366bc5 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -1268,6 +1268,10 @@ method VolumeRemove(options: VolumeRemoveOpts) -> (successes: []string, failures
# GetVolumes gets slice of the volumes on a remote host
method GetVolumes(args: []string, all: bool) -> (volumes: []Volume)
+# InspectVolume inspects a single volume. Returns inspect JSON in the form of a
+# string.
+method InspectVolume(name: string) -> (volume: string)
+
# VolumesPrune removes unused volumes on the host
method VolumesPrune() -> (prunedNames: []string, prunedErrors: []string)
diff --git a/cmd/podman/volume_create.go b/cmd/podman/volume_create.go
index 617f701a4..e5a576749 100644
--- a/cmd/podman/volume_create.go
+++ b/cmd/podman/volume_create.go
@@ -37,7 +37,7 @@ func init() {
flags := volumeCreateCommand.Flags()
flags.StringVar(&volumeCreateCommand.Driver, "driver", "", "Specify volume driver name (default local)")
flags.StringSliceVarP(&volumeCreateCommand.Label, "label", "l", []string{}, "Set metadata for a volume (default [])")
- flags.StringSliceVarP(&volumeCreateCommand.Opt, "opt", "o", []string{}, "Set driver specific options (default [])")
+ flags.StringArrayVarP(&volumeCreateCommand.Opt, "opt", "o", []string{}, "Set driver specific options (default [])")
}
func volumeCreateCmd(c *cliconfig.VolumeCreateValues) error {
diff --git a/completions/bash/podman b/completions/bash/podman
index 2a55183bd..0abf9e738 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -982,6 +982,7 @@ _podman_network_create() {
--subnet
"
local boolean_options="
+ --disable-dns
--help
-h
--internal
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index 701f8b0fc..6617850fd 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -515,13 +515,16 @@ This works for both background and foreground containers.
**--network**, **--net**="*bridge*"
Set the Network mode for the container. Invalid if using **--dns**, **--dns-option**, or **--dns-search** with **--network** that is set to 'none' or 'container:<name|id>'.
- 'bridge': create a network stack on the default bridge
- 'none': no networking
- 'container:<name|id>': reuse another container's network stack
- 'host': use the Podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
- '<network-name>|<network-id>': connect to a user-defined network
- 'ns:<path>': path to a network namespace to join
- 'slirp4netns': use slirp4netns to create a user network stack. This is the default for rootless containers
+
+Valid values are:
+
+- `bridge`: create a network stack on the default bridge
+- `none`: no networking
+- `container:<name|id>`: reuse another container's network stack
+- `host`: use the Podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
+- `<network-name>|<network-id>`: connect to a user-defined network, multiple networks should be comma separated
+- `ns:<path>`: path to a network namespace to join
+- `slirp4netns`: use slirp4netns to create a user network stack. This is the default for rootless containers
**--network-alias**=*alias*
@@ -626,6 +629,7 @@ If container is running in --read-only mode, then mount a read-write tmpfs on /r
Restart policy to follow when containers exit.
Restart policy will not take effect if a container is stopped via the `podman kill` or `podman stop` commands.
+
Valid values are:
- `no` : Do not restart containers on exit
@@ -800,7 +804,7 @@ Set the UTS mode for the container
**ns**: specify the user namespace to use.
Note: the host mode gives the container access to changing the host's hostname and is therefore considered insecure.
-**--volume**, **-v**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*]
+**--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, podman
bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the podman
@@ -810,11 +814,23 @@ container. The `OPTIONS` are a comma delimited list and can be:
* [z|Z]
* [`[r]shared`|`[r]slave`|`[r]private`]
-The `CONTAINER-DIR` must be an absolute path such as `/src/docs`. The `HOST-DIR`
-must be an absolute path as well. Podman bind-mounts the `HOST-DIR` to the
-path you specify. For example, if you supply the `/foo` value, Podman creates a bind-mount.
+The `CONTAINER-DIR` must be an absolute path such as `/src/docs`. The volume
+will be mounted into the container at this directory.
+
+Volumes may specify a source as well, as either a directory on the host or the
+name of a named volume. If no source is given, the volume will be created as an
+anonymous named volume with a randomly generated name, and will be removed when
+the container is removed via the `--rm` flag or `podman rm --volumes`.
+
+If a volume source is specified, it must be a path on the host or the name of a
+named volume. Host paths are allowed to be absolute or relative; relative paths
+are resolved relative to the directory Podman is run in. Any source that does
+not begin with a `.` or `/` it will be treated as the name of a named volume.
+If a volume with that name does not exist, it will be created. Volumes created
+with names are not anonymous and are not removed by `--rm` and
+`podman rm --volumes`.
-You can specify multiple **-v** options to mount one or more mounts to a
+You can specify multiple **-v** options to mount one or more volumes into a
container.
You can add `:ro` or `:rw` suffix to a volume to mount it read-only or
diff --git a/docs/podman-network-create.1.md b/docs/podman-network-create.1.md
index 0679d8ee2..c281d50d9 100644
--- a/docs/podman-network-create.1.md
+++ b/docs/podman-network-create.1.md
@@ -15,6 +15,11 @@ If no options are provided, Podman will assign a free subnet and name for your n
Upon completion of creating the network, Podman will display the path to the newly added network file.
## OPTIONS
+**--disable-dns**
+
+Disables the DNS plugin for this network which if enabled, can perform container to container name
+resolution.
+
**-d**, , **--driver**
Driver to manage the network (default "bridge"). Currently on `bridge` is supported.
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index 602aa69ed..d6d8f4c1e 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -526,11 +526,14 @@ This works for both background and foreground containers.
**--network**, **--net**=*node*
Set the Network mode for the container. Invalid if using **--dns**, **--dns-option**, or **--dns-search** with **--network** that is set to 'none' or 'container:<name|id>'.
+
+Valid values are:
+
- `bridge`: create a network stack on the default bridge
- `none`: no networking
- `container:<name|id>`: reuse another container's network stack
- `host`: use the Podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
-- `<network-name>|<network-id>`: connect to a user-defined network
+- `<network-name>|<network-id>`: connect to a user-defined network, multiple networks should be comma separated
- `ns:<path>`: path to a network namespace to join
- `slirp4netns`: use slirp4netns to create a user network stack. This is the default for rootless containers
@@ -645,6 +648,7 @@ If container is running in --read-only mode, then mount a read-write tmpfs on /r
Restart policy to follow when containers exit.
Restart policy will not take effect if a container is stopped via the `podman kill` or `podman stop` commands.
+
Valid values are:
- `no` : Do not restart containers on exit
@@ -839,7 +843,7 @@ Set the UTS mode for the container
**NOTE**: the host mode gives the container access to changing the host's hostname and is therefore considered insecure.
-**--volume**, **-v**[=*[HOST-DIR-OR-VOUME-NAME:CONTAINER-DIR[:OPTIONS]]*]
+**--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, Podman
bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the Podman
@@ -853,11 +857,23 @@ create one.
* [`z`|`Z`]
* [`[r]shared`|`[r]slave`|`[r]private`]
-The `/CONTAINER-DIR` must be an absolute path such as `/src/docs`. The `/HOST-DIR`
-must be an absolute path as well. Podman bind-mounts the `HOST-DIR` to the
-path you specify. For example, if you supply the `/foo` value, Podman creates a bind-mount.
+The `CONTAINER-DIR` must be an absolute path such as `/src/docs`. The volume
+will be mounted into the container at this directory.
+
+Volumes may specify a source as well, as either a directory on the host or the
+name of a named volume. If no source is given, the volume will be created as an
+anonymous named volume with a randomly generated name, and will be removed when
+the container is removed via the `--rm` flag or `podman rm --volumes`.
+
+If a volume source is specified, it must be a path on the host or the name of a
+named volume. Host paths are allowed to be absolute or relative; relative paths
+are resolved relative to the directory Podman is run in. Any source that does
+not begin with a `.` or `/` it will be treated as the name of a named volume.
+If a volume with that name does not exist, it will be created. Volumes created
+with names are not anonymous and are not removed by `--rm` and
+`podman rm --volumes`.
-You can specify multiple **-v** options to mount one or more mounts to a
+You can specify multiple **-v** options to mount one or more volumes into a
container.
You can add `:ro` or `:rw` suffix to a volume to mount it read-only or
diff --git a/docs/podman-volume-create.1.md b/docs/podman-volume-create.1.md
index 6612b0ad2..b354f396f 100644
--- a/docs/podman-volume-create.1.md
+++ b/docs/podman-volume-create.1.md
@@ -30,6 +30,13 @@ Set metadata for a volume (e.g., --label mykey=value).
**-o**, **--opt**=*option*
Set driver specific options.
+For the default driver, `local`, this allows a volume to be configured to mount a filesystem on the host.
+For the `local` driver the following options are supported: `type`, `device`, and `o`.
+The `type` option sets the type of the filesystem to be mounted, and is equivalent to the `-t` flag to **mount(8)**.
+The `device` option sets the device to be mounted, and is equivalent to the `device` argument to **mount(8)**.
+The `o` option sets options for the mount, and is equivalent to the `-o` flag to **mount(8)** with two exceptions.
+The `o` option supports `uid` and `gid` options to set the UID and GID of the created volume that are not normally supported by **mount(8)**.
+Using volume options with the `local` driver requires root privileges.
## EXAMPLES
@@ -40,11 +47,13 @@ $ podman volume create
$ podman volume create --label foo=bar myvol
-$ podman volume create --opt device=tmpfs --opt type=tmpfs --opt o=nodev,noexec myvol
+# podman volume create --opt device=tmpfs --opt type=tmpfs --opt o=nodev,noexec myvol
+
+# podman volume create --opt device=tmpfs --opt type=tmpfs --opt o=uid=1000,gid=1000 testvol
```
## SEE ALSO
-podman-volume(1)
+podman-volume(1), mount(8)
## HISTORY
November 2018, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/rtd/Makefile b/docs/rtd/Makefile
new file mode 100644
index 000000000..50af6490a
--- /dev/null
+++ b/docs/rtd/Makefile
@@ -0,0 +1,123 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = source
+BUILDDIR = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+clean:
+ rm -fr build/
+ rm -f source/man/podman-*.1.md
+
+copy:
+ cp -v ../podman-attach.1.md source/man/
+ cp -v ../podman-build.1.md source/man/
+ cp -v ../podman-commit.1.md source/man/
+ # container
+ cp -v ../podman-container-checkpoint.1.md source/man/
+ cp -v ../podman-container-exists.1.md source/man/
+ cp -v ../podman-container-restore.1.md source/man/
+ cp -v ../podman-container-cleanup.1.md source/man/
+ cp -v ../podman-container-prune.1.md source/man/
+ cp -v ../podman-container-runlabel.1.md source/man/
+ cp -v ../podman-cp.1.md source/man/
+ cp -v ../podman-create.1.md source/man/
+ cp -v ../podman-diff.1.md source/man/
+ cp -v ../podman-events.1.md source/man/
+ cp -v ../podman-exec.1.md source/man/
+ cp -v ../podman-export.1.md source/man/
+ # generate
+ cp -v ../podman-generate-systemd.1.md source/man/
+ cp -v ../podman-generate-kube.1.md source/man/
+ # healthcheck
+ cp -v ../podman-healthcheck-run.1.md source/man/
+ #cp -v ../podman-help.1.md source/
+ cp -v ../podman-history.1.md source/man/
+ # image
+ cp -v ../podman-image-prune.1.md source/man/
+ cp -v ../podman-image-tree.1.md source/man/
+ cp -v ../podman-image-trust.1.md source/man/
+ cp -v ../podman-image-exists.1.md source/man/
+ cp -v ../podman-image-sign.1.md source/man/
+ cp -v ../podman-images.1.md source/man/
+ cp -v ../podman-import.1.md source/man/
+ cp -v ../podman-info.1.md source/man/
+ cp -v ../podman-init.1.md source/man/
+ cp -v ../podman-inspect.1.md source/man/
+ cp -v ../podman-kill.1.md source/man/
+ cp -v ../podman-load.1.md source/man/
+ cp -v ../podman-login.1.md source/man/
+ cp -v ../podman-logout.1.md source/man/
+ cp -v ../podman-logs.1.md source/man/
+ cp -v ../podman-mount.1.md source/man/
+ # network
+ cp -v ../podman-network-create.1.md source/man/
+ cp -v ../podman-network-ls.1.md source/man/
+ cp -v ../podman-network-inspect.1.md source/man/
+ cp -v ../podman-network-rm.1.md source/man/
+ cp -v ../podman-pause.1.md source/man/
+ # play
+ cp -v ../podman-play-kube.1.md source/man/
+ # pod
+ cp -v ../podman-pod-create.1.md source/man/
+ cp -v ../podman-pod-pause.1.md source/man/
+ cp -v ../podman-pod-rm.1.md source/man/
+ cp -v ../podman-pod-top.1.md source/man/
+ cp -v ../podman-pod-exists.1.md source/man/
+ cp -v ../podman-pod-prune.1.md source/man/
+ cp -v ../podman-pod-start.1.md source/man/
+ cp -v ../podman-pod-unpause.1.md source/man/
+ cp -v ../podman-pod-inspect.1.md source/man/
+ cp -v ../podman-pod-ps.1.md source/man/
+ cp -v ../podman-pod-stats.1.md source/man/
+ cp -v ../podman-pod-kill.1.md source/man/
+ cp -v ../podman-pod-restart.1.md source/man/
+ cp -v ../podman-pod-stop.1.md source/man/
+ cp -v ../podman-port.1.md source/man/
+ cp -v ../podman-ps.1.md source/man/
+ cp -v ../podman-pull.1.md source/man/
+ cp -v ../podman-push.1.md source/man/
+ cp -v ../podman-restart.1.md source/man/
+ cp -v ../podman-rm.1.md source/man/
+ cp -v ../podman-rmi.1.md source/man/
+ cp -v ../podman-run.1.md source/man/
+ cp -v ../podman-save.1.md source/man/
+ cp -v ../podman-search.1.md source/man/
+ cp -v ../podman-start.1.md source/man/
+ cp -v ../podman-stats.1.md source/man/
+ cp -v ../podman-stop.1.md source/man/
+ # system
+ cp -v ../podman-system-migrate.1.md source/man/
+ cp -v ../podman-system-renumber.1.md source/man/
+ cp -v ../podman-system-df.1.md source/man/
+ cp -v ../podman-system-prune.1.md source/man/
+ cp -v ../podman-top.1.md source/man/
+ cp -v ../podman-umount.1.md source/man/
+ cp -v ../podman-unpause.1.md source/man/
+ cp -v ../podman-unshare.1.md source/man/
+ cp -v ../podman-varlink.1.md source/man/
+ cp -v ../podman-version.1.md source/man/
+ # volume
+ cp -v ../podman-volume-inspect.1.md source/man/
+ cp -v ../podman-volume-prune.1.md source/man/
+ cp -v ../podman-volume-create.1.md source/man/
+ cp -v ../podman-volume-ls.1.md source/man/
+ cp -v ../podman-volume-rm.1.md source/man/
+ cp -v ../podman-wait.1.md source/man/
+
+.PHONY: help Makefile copy
+
+html: copy
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/rtd/make.bat b/docs/rtd/make.bat
new file mode 100644
index 000000000..6247f7e23
--- /dev/null
+++ b/docs/rtd/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=source
+set BUILDDIR=build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/docs/rtd/requirements.txt b/docs/rtd/requirements.txt
new file mode 100644
index 000000000..44af373ac
--- /dev/null
+++ b/docs/rtd/requirements.txt
@@ -0,0 +1,4 @@
+# requirements file for readthedocs pip installs
+
+# use md instead of rst
+recommonmark
diff --git a/docs/rtd/source/Commands.rst b/docs/rtd/source/Commands.rst
new file mode 100644
index 000000000..f6ba5b20d
--- /dev/null
+++ b/docs/rtd/source/Commands.rst
@@ -0,0 +1,107 @@
+Commands
+========
+
+
+:doc:`attach <man/podman-attach.1>` Attach to a running container
+
+:doc:`build <man/podman-build.1>` Build an image using instructions from Containerfiles
+
+:doc:`commit <man/podman-commit.1>` Create new image based on the changed container
+
+:doc:`containers <man/managecontainers>` Manage Containers
+
+:doc:`cp <man/podman-cp.1>` Copy files/folders between a container and the local filesystem
+
+:doc:`create <man/podman-create.1>` Create but do not start a container
+
+:doc:`diff <man/podman-diff.1>` Inspect changes on container's file systems
+
+:doc:`events <man/podman-events.1>` Show podman events
+
+:doc:`exec <man/podman-exec.1>` Run a process in a running container
+
+:doc:`export <man/podman-export.1>` Export container's filesystem contents as a tar archive
+
+:doc:`generate <man/generate>` Generated structured data
+
+:doc:`healthcheck <man/healthcheck>` Manage Healthcheck
+
+:doc:`history <man/podman-history.1>` Show history of a specified image
+
+:doc:`image <man/image>` Manage images
+
+:doc:`images <man/podman-images.1>` List images in local storage
+
+:doc:`import <man/podman-import.1>` Import a tarball to create a filesystem image
+
+:doc:`info <man/podman-info.1>` Display podman system information
+
+:doc:`init <man/podman-init.1>` Initialize one or more containers
+
+:doc:`inspect <man/podman-inspect.1>` Display the configuration of a container or image
+
+:doc:`kill <man/podman-kill.1>` Kill one or more running containers with a specific signal
+
+:doc:`load <man/podman-load.1>` Load an image from container archive
+
+:doc:`login <man/podman-login.1>` Login to a container registry
+
+:doc:`logout <man/podman-logout.1>` Logout of a container registry
+
+:doc:`logs <man/podman-logs.1>` Fetch the logs of a container
+
+:doc:`mount <man/podman-mount.1>` Mount a working container's root filesystem
+
+:doc:`network <man/network>` Manage Networks
+
+:doc:`pause <man/podman-pause.1>` Pause all the processes in one or more containers
+
+:doc:`play <man/play>` Play a pod
+
+:doc:`pod <man/pod>` Manage pods
+
+:doc:`port <man/podman-port.1>` List port mappings or a specific mapping for the container
+
+:doc:`ps <man/podman-ps.1>` List containers
+
+:doc:`pull <man/podman-pull.1>` Pull an image from a registry
+
+:doc:`push <man/podman-push.1>` Push an image to a specified destination
+
+:doc:`restart <man/podman-restart.1>` Restart one or more containers
+
+:doc:`rm <man/podman-rm.1>` Remove one or more containers
+
+:doc:`rmi <man/podman-rmi.1>` Removes one or more images from local storage
+
+:doc:`run <man/podman-run.1>` Run a command in a new container
+
+:doc:`save <man/podman-save.1>` Save image to an archive
+
+:doc:`search <man/podman-search.1>` Search registry for image
+
+:doc:`start <man/podman-start.1>` Start one or more containers
+
+:doc:`stats <man/podman-stats.1>` Display a live stream of container resource usage statistics
+
+:doc:`stop <man/podman-stop.1>` Stop one or more containers
+
+:doc:`system <man/system>` Manage podman
+
+:doc:`tag <man/podman-tag.1>` Add an additional name to a local image
+
+:doc:`top <man/podman-top.1>` Display the running processes of a container
+
+:doc:`umount <man/podman-umount.1>` Unmounts working container's root filesystem
+
+:doc:`unpause <man/podman-unpause.1>` Unpause the processes in one or more containers
+
+:doc:`unshare <man/podman-unshare.1>` Run a command in a modified user namespace
+
+:doc:`varlink <man/podman-varlink.1>` Run varlink interface
+
+:doc:`version <man/podman-version.1>` Display the Podman Version Information
+
+:doc:`volume <man/volume>` Manage volumes
+
+:doc:`wait <man/podman-wait.1>` Block on one or more containers \ No newline at end of file
diff --git a/docs/rtd/source/Introduction.rst b/docs/rtd/source/Introduction.rst
new file mode 100644
index 000000000..c516b3317
--- /dev/null
+++ b/docs/rtd/source/Introduction.rst
@@ -0,0 +1,2 @@
+Introduction
+============
diff --git a/docs/rtd/source/Reference.rst b/docs/rtd/source/Reference.rst
new file mode 100644
index 000000000..9a771c87f
--- /dev/null
+++ b/docs/rtd/source/Reference.rst
@@ -0,0 +1,2 @@
+Reference
+=========
diff --git a/docs/rtd/source/Tutorials.rst b/docs/rtd/source/Tutorials.rst
new file mode 100644
index 000000000..0c7e28c3b
--- /dev/null
+++ b/docs/rtd/source/Tutorials.rst
@@ -0,0 +1,2 @@
+Tutorials
+=========
diff --git a/docs/rtd/source/conf.py b/docs/rtd/source/conf.py
new file mode 100644
index 000000000..d95290f72
--- /dev/null
+++ b/docs/rtd/source/conf.py
@@ -0,0 +1,57 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'Podman'
+copyright = '2019, team'
+author = 'team'
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'recommonmark',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = []
+
+master_doc = 'index'
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'alabaster'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+
+# -- Extension configuration -------------------------------------------------
diff --git a/docs/rtd/source/index.rst b/docs/rtd/source/index.rst
new file mode 100644
index 000000000..9dd61a6a6
--- /dev/null
+++ b/docs/rtd/source/index.rst
@@ -0,0 +1,26 @@
+.. Podman documentation master file, created by
+ sphinx-quickstart on Tue Oct 22 15:20:30 2019.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to Podman's documentation!
+==================================
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+ Introduction
+ Commands
+ Reference
+ Tutorials
+
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/rtd/source/man/generate.rst b/docs/rtd/source/man/generate.rst
new file mode 100644
index 000000000..e82a15735
--- /dev/null
+++ b/docs/rtd/source/man/generate.rst
@@ -0,0 +1,6 @@
+Generate
+========
+
+:doc:`kube <podman-generate-kube.1>` Generate Kubernetes pod YAML from a container or pod
+
+:doc:`systemd <podman-generate-systemd.1>` Generate a systemd unit file for a Podman container
diff --git a/docs/rtd/source/man/healthcheck.rst b/docs/rtd/source/man/healthcheck.rst
new file mode 100644
index 000000000..697c1358b
--- /dev/null
+++ b/docs/rtd/source/man/healthcheck.rst
@@ -0,0 +1,4 @@
+HealthCheck
+===========
+
+:doc:`run <podman-healthcheck-run.1>` run the health check of a container
diff --git a/docs/rtd/source/man/image.rst b/docs/rtd/source/man/image.rst
new file mode 100644
index 000000000..ad963cd41
--- /dev/null
+++ b/docs/rtd/source/man/image.rst
@@ -0,0 +1,35 @@
+Image
+=====
+
+
+:doc:`build <podman-build.1>` Build an image using instructions from Containerfiles
+
+:doc:`exists <podman-image-exists.1>` Check if an image exists in local storage
+
+:doc:`history <podman-history.1>` Show history of a specified image
+
+:doc:`import <podman-import.1>` Import a tarball to create a filesystem image
+
+:doc:`inspect <podman-inspect.1>` Display the configuration of an image
+
+:doc:`list <podman-images.1>` List images in local storage
+
+:doc:`load <podman-load.1>` Load an image from container archive
+
+:doc:`prune <podman-image-prune.1>` Remove unused images
+
+:doc:`pull <podman-pull.1>` Pull an image from a registry
+
+:doc:`push <podman-push.1>` Push an image to a specified destination
+
+:doc:`rm <podman-rmi.1>` Removes one or more images from local storage
+
+:doc:`save <podman-save.1>` Save image to an archive
+
+:doc:`sign <podman-image-sign.1>` Sign an image
+
+:doc:`tag <podman-tag.1>` Add an additional name to a local image
+
+:doc:`tree <podman-image-tree.1>` Prints layer hierarchy of an image in a tree format
+
+:doc:`trust <podman-image-trust.1>` Manage container image trust policy
diff --git a/docs/rtd/source/man/managecontainers.rst b/docs/rtd/source/man/managecontainers.rst
new file mode 100644
index 000000000..20e8c0679
--- /dev/null
+++ b/docs/rtd/source/man/managecontainers.rst
@@ -0,0 +1,64 @@
+Manage Containers
+=================
+
+:doc:`attach <podman-attach.1>` Attach to a running container
+
+:doc:`checkpoint <podman-container-checkpoint.1>` Checkpoints one or more containers
+
+:doc:`cleanup <podman-container-cleanup.1>` Cleanup network and mountpoints of one or more containers
+
+:doc:`commit <podman-commit.1>` Create new image based on the changed container
+
+:doc:`cp <podman-cp.1>` Copy files/folders between a container and the local filesystem
+
+:doc:`create <podman-create.1>` Create but do not start a container
+
+:doc:`diff <podman-diff.1>` Inspect changes on container's file systems
+
+:doc:`exec <podman-exec.1>` Run a process in a running container
+
+:doc:`exists <podman-exists.1>` Check if a container exists in local storage
+
+:doc:`export <podman-export.1>` Export container's filesystem contents as a tar archive
+
+:doc:`init <podman-init.1>` Initialize one or more containers
+
+:doc:`inspect <podman-inspect.1>` Display the configuration of a container or image
+
+:doc:`kill <podman-kill.1>` Kill one or more running containers with a specific signal
+
+:doc:`list <podman-ps.1>` List containers
+
+:doc:`logs <podman-logs.1>` Fetch the logs of a container
+
+:doc:`mount <podman-mount.1>` Mount a working container's root filesystem
+
+:doc:`pause <podman-pause.1>` Pause all the processes in one or more containers
+
+:doc:`port <podman-port.1>` List port mappings or a specific mapping for the container
+
+:doc:`restart <podman-restart.1>` Restart one or more containers
+
+:doc:`prune <podman-container-prune.1>` Remove all stopped containers
+
+:doc:`restore <podman-container-restore.1>` Restores one or more containers from a checkpoint
+
+:doc:`rm <podman-rm.1>` Remove one or more containers
+
+:doc:`run <podman-run.1>` Run a command in a new container
+
+:doc:`runlabel <podman-container-runlabel.1>` Execute the command described by an image label
+
+:doc:`start <podman-start.1>` Start one or more containers
+
+:doc:`stats <podman-stats.1>` Display a live stream of container resource usage statistics
+
+:doc:`stop <podman-stop.1>` Stop one or more containers
+
+:doc:`top <podman-top.1>` Display the running processes of a container
+
+:doc:`umount <podman-umount.1>` Unmounts working container's root filesystem
+
+:doc:`unpause <podman-unpause.1>` Unpause the processes in one or more containers
+
+:doc:`wait <podman-wait.1>` Block on one or more containers
diff --git a/docs/rtd/source/man/network.rst b/docs/rtd/source/man/network.rst
new file mode 100644
index 000000000..6d6a4c022
--- /dev/null
+++ b/docs/rtd/source/man/network.rst
@@ -0,0 +1,10 @@
+Network
+=====
+
+:doc:`create <podman-network-create.1>` network create
+
+:doc:`inspect <podman-network-inspect.1>` network inspect
+
+:doc:`ls <podman-network-ls.1>` network list
+
+:doc:`rm <podman-network-rm.1>` network rm \ No newline at end of file
diff --git a/docs/rtd/source/man/play.rst b/docs/rtd/source/man/play.rst
new file mode 100644
index 000000000..93e1a9a1e
--- /dev/null
+++ b/docs/rtd/source/man/play.rst
@@ -0,0 +1,4 @@
+Play
+====
+
+:doc:`kube <podman-play-kube.1>` Play a pod based on Kubernetes YAML
diff --git a/docs/rtd/source/man/pod.rst b/docs/rtd/source/man/pod.rst
new file mode 100644
index 000000000..13c1740f8
--- /dev/null
+++ b/docs/rtd/source/man/pod.rst
@@ -0,0 +1,30 @@
+Pod
+===
+
+:doc:`create <podman-pod-create.1>` Create a new empty pod
+
+:doc:`exists <podman-pod-exists.1>` Check if a pod exists in local storage
+
+:doc:`inspect <podman-pod-inspect.1>` Displays a pod configuration
+
+:doc:`kill <podman-pod-kill.1>` Send the specified signal or SIGKILL to containers in pod
+
+:doc:`pause <podman-pause.1>` Pause one or more pods
+
+:doc:`prune <podman-pod-prune.1>` Remove all stopped pods
+
+:doc:`ps <podman-pod-ps.1>` List pods
+
+:doc:`restart <podman-pod-restart.1>` Restart one or more pods
+
+:doc:`rm <podman-pod-rm.1>` Remove one or more pods
+
+:doc:`start <podman-pod-start.1>` Start one or more pods
+
+:doc:`stats <podman-pod-stats.1>` Display a live stream of resource usage statistics for the containers in one or more pods
+
+:doc:`stop <podman-pod-stop.1>` Stop one or more pods
+
+:doc:`top <podman-pod-top.1>` Display the running processes of containers in a pod
+
+:doc:`unpause <podman-pod-unpause.1>` Unpause one or more pods
diff --git a/docs/rtd/source/man/system.rst b/docs/rtd/source/man/system.rst
new file mode 100644
index 000000000..764ec01c1
--- /dev/null
+++ b/docs/rtd/source/man/system.rst
@@ -0,0 +1,12 @@
+System
+======
+
+:doc:`df <podman-system-df.1>` Show podman disk usage
+
+:doc:`info <podman-info.1>` Display podman system information
+
+:doc:`migrate <podman-system-migrate.1>` Migrate containers
+
+:doc:`prune <podman-system-prune.1>` Remove unused data
+
+:doc:`renumber <podman-system-renumber.1>` Migrate lock numbers
diff --git a/docs/rtd/source/man/volume.rst b/docs/rtd/source/man/volume.rst
new file mode 100644
index 000000000..ee18e4b2e
--- /dev/null
+++ b/docs/rtd/source/man/volume.rst
@@ -0,0 +1,11 @@
+Volume
+======
+:doc:`create <podman-volume-create.1>` Create a new volume
+
+:doc:`inspect <podman-volume-inspect.1>` Display detailed information on one or more volumes
+
+:doc:`ls <podman-volume-ls.1>` List volumes
+
+:doc:`prune <podman-volume-prune.1>` Remove all unused volumes
+
+:doc:`rm <podman-volume-rm.1>` Remove one or more volumes \ No newline at end of file
diff --git a/docs/tutorials/rootless_tutorial.md b/docs/tutorials/rootless_tutorial.md
index ed700485a..9a31826bd 100644
--- a/docs/tutorials/rootless_tutorial.md
+++ b/docs/tutorials/rootless_tutorial.md
@@ -13,7 +13,7 @@ The alternative OCI runtime support for cgroup V2 can be turned on at the comma
```
sudo podman --runtime /usr/bin/crun
```
-or by changing the value for the "Default OCI runtime" in the libpod.conf file either at the system level or at the [#user-configuration-files](user level) from `runtime = "runc"` to `runtime = "crun"`.
+or by changing the value for the "Default OCI runtime" in the libpod.conf file either at the system level or at the [user level](#user-configuration-files) from `runtime = "runc"` to `runtime = "crun"`.
## Administrator Actions
@@ -59,7 +59,7 @@ The format of this file is USERNAME:UID:RANGE
This means the user johndoe is allocated UIDS 100000-165535 as well as their standard UID in the /etc/passwd file. NOTE: this is not currently supported with network installs. These files must be available locally to the host machine. It is not possible to configure this with LDAP or Active Directory.
-If you update either the /etc/subuid or the /etc/subgid file, you need to stop all the running containers owned by the user and kill the pause process that is running on the system for that user. This can be done automatically by using the `[podman system migrate](https://github.com/containers/libpod/blob/master/docs/podman-system-migrate.1.md)` command which will stop all the containers for the user and will kill the pause process.
+If you update either the /etc/subuid or the /etc/subgid file, you need to stop all the running containers owned by the user and kill the pause process that is running on the system for that user. This can be done automatically by using the [`podman system migrate`](https://github.com/containers/libpod/blob/master/docs/podman-system-migrate.1.md) command which will stop all the containers for the user and will kill the pause process.
Rather than updating the files directly, the usermod program can be used to assign UIDs and GIDs to a user.
diff --git a/libpod/container.go b/libpod/container.go
index 7be73b3c3..fc9ef0c86 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -1185,3 +1185,12 @@ func (c *Container) HasHealthCheck() bool {
func (c *Container) HealthCheckConfig() *manifest.Schema2HealthConfig {
return c.config.HealthCheckConfig
}
+
+// AutoRemove indicates whether the container will be removed after it is executed
+func (c *Container) AutoRemove() bool {
+ spec := c.config.Spec
+ if spec.Annotations == nil {
+ return false
+ }
+ return c.Spec().Annotations[InspectAnnotationAutoremove] == InspectResponseTrue
+}
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 759a7067e..1b2d52ce3 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -32,9 +32,7 @@ func (c *Container) Init(ctx context.Context) (err error) {
}
}
- if !(c.state.State == define.ContainerStateConfigured ||
- c.state.State == define.ContainerStateStopped ||
- c.state.State == define.ContainerStateExited) {
+ if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateStopped, define.ContainerStateExited) {
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s has already been created in runtime", c.ID())
}
@@ -176,15 +174,12 @@ func (c *Container) StopWithTimeout(timeout uint) error {
}
}
- if c.state.State == define.ContainerStateConfigured ||
- c.state.State == define.ContainerStateUnknown ||
- c.state.State == define.ContainerStatePaused {
- return errors.Wrapf(define.ErrCtrStateInvalid, "can only stop created, running, or stopped containers. %s is in state %s", c.ID(), c.state.State.String())
+ if c.ensureState(define.ContainerStateStopped, define.ContainerStateExited) {
+ return define.ErrCtrStopped
}
- if c.state.State == define.ContainerStateStopped ||
- c.state.State == define.ContainerStateExited {
- return define.ErrCtrStopped
+ if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) {
+ return errors.Wrapf(define.ErrCtrStateInvalid, "can only stop created or running containers. %s is in state %s", c.ID(), c.state.State.String())
}
return c.stop(timeout, false)
@@ -201,6 +196,7 @@ func (c *Container) Kill(signal uint) error {
}
}
+ // TODO: Is killing a paused container OK?
if c.state.State != define.ContainerStateRunning {
return errors.Wrapf(define.ErrCtrStateInvalid, "can only kill running containers. %s is in state %s", c.ID(), c.state.State.String())
}
@@ -234,10 +230,7 @@ func (c *Container) Exec(tty, privileged bool, env map[string]string, cmd []stri
}
}
- conState := c.state.State
-
- // TODO can probably relax this once we track exec sessions
- if conState != define.ContainerStateRunning {
+ if c.state.State != define.ContainerStateRunning {
return define.ExecErrorCodeCannotInvoke, errors.Wrapf(define.ErrCtrStateInvalid, "cannot exec into container that is not running")
}
@@ -391,11 +384,10 @@ func (c *Container) Attach(streams *AttachStreams, keys string, resize <-chan re
c.lock.Unlock()
}
- if c.state.State != define.ContainerStateCreated &&
- c.state.State != define.ContainerStateRunning &&
- c.state.State != define.ContainerStateExited {
+ if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) {
return errors.Wrapf(define.ErrCtrStateInvalid, "can only attach to created or running containers")
}
+
defer c.newContainerEvent(events.Attach)
return c.attach(streams, keys, resize, false, nil)
}
@@ -432,7 +424,7 @@ func (c *Container) Unmount(force bool) error {
return errors.Wrapf(err, "can't determine how many times %s is mounted, refusing to unmount", c.ID())
}
if mounted == 1 {
- if c.state.State == define.ContainerStateRunning || c.state.State == define.ContainerStatePaused {
+ if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) {
return errors.Wrapf(define.ErrCtrStateInvalid, "cannot unmount storage for container %s as it is running or paused", c.ID())
}
if len(c.state.ExecSessions) != 0 {
@@ -574,7 +566,7 @@ func (c *Container) Cleanup(ctx context.Context) error {
}
// Check if state is good
- if c.state.State == define.ContainerStateRunning || c.state.State == define.ContainerStatePaused {
+ if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateStopped, define.ContainerStateExited) {
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is running or paused, refusing to clean up", c.ID())
}
@@ -652,9 +644,7 @@ func (c *Container) Sync() error {
// If runtime knows about the container, update its status in runtime
// And then save back to disk
- if (c.state.State != define.ContainerStateUnknown) &&
- (c.state.State != define.ContainerStateConfigured) &&
- (c.state.State != define.ContainerStateExited) {
+ if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopped) {
oldState := c.state.State
if err := c.ociRuntime.UpdateContainerStatus(c); err != nil {
return err
@@ -666,6 +656,7 @@ func (c *Container) Sync() error {
}
}
}
+
defer c.newContainerEvent(events.Sync)
return nil
}
@@ -840,12 +831,3 @@ func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOpti
defer c.newContainerEvent(events.Restore)
return c.restore(ctx, options)
}
-
-// AutoRemove indicates whether the container will be removed after it is executed
-func (c *Container) AutoRemove() bool {
- spec := c.config.Spec
- if spec.Annotations == nil {
- return false
- }
- return c.Spec().Annotations[InspectAnnotationAutoremove] == InspectResponseTrue
-}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 0043c9651..028d7601d 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -328,7 +328,7 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, er
// Is the container running again?
// If so, we don't have to do anything
- if c.state.State == define.ContainerStateRunning || c.state.State == define.ContainerStatePaused {
+ if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) {
return false, nil
} else if c.state.State == define.ContainerStateUnknown {
return false, errors.Wrapf(define.ErrInternal, "invalid container state encountered in restart attempt!")
@@ -359,8 +359,7 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, er
if err := c.reinit(ctx, true); err != nil {
return false, err
}
- } else if c.state.State == define.ContainerStateConfigured ||
- c.state.State == define.ContainerStateExited {
+ } else if c.ensureState(define.ContainerStateConfigured, define.ContainerStateExited) {
// Initialize the container
if err := c.init(ctx, true); err != nil {
return false, err
@@ -372,6 +371,18 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (restarted bool, er
return true, nil
}
+// Ensure that the container is in a specific state or state.
+// Returns true if the container is in one of the given states,
+// or false otherwise.
+func (c *Container) ensureState(states ...define.ContainerStatus) bool {
+ for _, state := range states {
+ if state == c.state.State {
+ return true
+ }
+ }
+ return false
+}
+
// Sync this container with on-disk state and runtime status
// Should only be called with container lock held
// This function should suffice to ensure a container's state is accurate and
@@ -382,9 +393,7 @@ func (c *Container) syncContainer() error {
}
// If runtime knows about the container, update its status in runtime
// And then save back to disk
- if (c.state.State != define.ContainerStateUnknown) &&
- (c.state.State != define.ContainerStateConfigured) &&
- (c.state.State != define.ContainerStateExited) {
+ if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStateStopped, define.ContainerStatePaused) {
oldState := c.state.State
if err := c.checkExitFile(); err != nil {
@@ -516,7 +525,7 @@ func (c *Container) setupStorage(ctx context.Context) error {
// Tear down a container's storage prior to removal
func (c *Container) teardownStorage() error {
- if c.state.State == define.ContainerStateRunning || c.state.State == define.ContainerStatePaused {
+ if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) {
return errors.Wrapf(define.ErrCtrStateInvalid, "cannot remove storage for container %s as it is running or paused", c.ID())
}
@@ -721,10 +730,7 @@ func (c *Container) save() error {
// Otherwise, this function will return with error if there are dependencies of this container that aren't running.
func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err error) {
// Container must be created or stopped to be started
- if !(c.state.State == define.ContainerStateConfigured ||
- c.state.State == define.ContainerStateCreated ||
- c.state.State == define.ContainerStateStopped ||
- c.state.State == define.ContainerStateExited) {
+ if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateStopped, define.ContainerStateExited) {
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s must be in Created or Stopped state to be started", c.ID())
}
@@ -755,8 +761,7 @@ func (c *Container) prepareToStart(ctx context.Context, recursive bool) (err err
if err := c.reinit(ctx, false); err != nil {
return err
}
- } else if c.state.State == define.ContainerStateConfigured ||
- c.state.State == define.ContainerStateExited {
+ } else if c.ensureState(define.ContainerStateConfigured, define.ContainerStateExited) {
// Or initialize it if necessary
if err := c.init(ctx, false); err != nil {
return err
@@ -987,7 +992,7 @@ func (c *Container) cleanupRuntime(ctx context.Context) error {
// If the container is not ContainerStateStopped or
// ContainerStateCreated, do nothing.
- if c.state.State != define.ContainerStateStopped && c.state.State != define.ContainerStateCreated {
+ if !c.ensureState(define.ContainerStateStopped, define.ContainerStateCreated) {
return nil
}
@@ -1078,8 +1083,7 @@ func (c *Container) initAndStart(ctx context.Context) (err error) {
if err := c.reinit(ctx, false); err != nil {
return err
}
- } else if c.state.State == define.ContainerStateConfigured ||
- c.state.State == define.ContainerStateExited {
+ } else if c.ensureState(define.ContainerStateConfigured, define.ContainerStateExited) {
if err := c.init(ctx, false); err != nil {
return err
}
@@ -1205,7 +1209,7 @@ func (c *Container) unpause() error {
// Internal, non-locking function to restart a container
func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err error) {
- if c.state.State == define.ContainerStateUnknown || c.state.State == define.ContainerStatePaused {
+ if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStateStopped, define.ContainerStateExited) {
return errors.Wrapf(define.ErrCtrStateInvalid, "unable to restart a container in a paused or unknown state")
}
@@ -1733,9 +1737,8 @@ func (c *Container) checkReadyForRemoval() error {
return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is in invalid state", c.ID())
}
- if c.state.State == define.ContainerStateRunning ||
- c.state.State == define.ContainerStatePaused {
- return errors.Wrapf(define.ErrCtrStateInvalid, "cannot remove container %s as it is %s - running or paused containers cannot be removed", c.ID(), c.state.State.String())
+ if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) {
+ return errors.Wrapf(define.ErrCtrStateInvalid, "cannot remove container %s as it is %s - running or paused containers cannot be removed without force", c.ID(), c.state.State.String())
}
if len(c.state.ExecSessions) != 0 {
@@ -1816,7 +1819,7 @@ func (c *Container) sortUserVolumes(ctrSpec *spec.Spec) ([]*ContainerNamedVolume
// Check for an exit file, and handle one if present
func (c *Container) checkExitFile() error {
// If the container's not running, nothing to do.
- if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused {
+ if !c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) {
return nil
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index b7d353327..283d38a0f 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -550,6 +550,7 @@ func (c *Container) setupSystemd(mounts []spec.Mount, g generate.Generator) erro
Options: []string{"bind", "nodev", "noexec", "nosuid"},
}
g.AddMount(systemdMnt)
+ g.AddLinuxMaskedPaths("/sys/fs/cgroup/systemd/release_agent")
}
return nil
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 4360c8c15..daa0619a2 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -5,6 +5,7 @@ package libpod
import (
"crypto/rand"
"fmt"
+ "io/ioutil"
"net"
"os"
"os/exec"
@@ -131,7 +132,7 @@ func checkSlirpFlags(path string) (bool, bool, bool, error) {
cmd := exec.Command(path, "--help")
out, err := cmd.CombinedOutput()
if err != nil {
- return false, false, false, err
+ return false, false, false, errors.Wrapf(err, "slirp4netns %q", out)
}
return strings.Contains(string(out), "--disable-host-loopback"), strings.Contains(string(out), "--mtu"), strings.Contains(string(out), "--enable-sandbox"), nil
}
@@ -158,6 +159,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
havePortMapping := len(ctr.Config().PortMappings) > 0
apiSocket := filepath.Join(ctr.runtime.config.TmpDir, fmt.Sprintf("%s.net", ctr.config.ID))
+ logPath := filepath.Join(ctr.runtime.config.TmpDir, fmt.Sprintf("slirp4netns-%s.log", ctr.config.ID))
cmdArgs := []string{}
if havePortMapping {
@@ -165,7 +167,7 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
}
dhp, mtu, sandbox, err := checkSlirpFlags(path)
if err != nil {
- return errors.Wrapf(err, "error checking slirp4netns binary %s", path)
+ return errors.Wrapf(err, "error checking slirp4netns binary %s: %q", path, err)
}
if dhp {
cmdArgs = append(cmdArgs, "--disable-host-loopback")
@@ -210,6 +212,18 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
// Leak one end of the pipe in slirp4netns, the other will be sent to conmon
cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessSlirpSyncR, syncW)
+ logFile, err := os.Create(logPath)
+ if err != nil {
+ return errors.Wrapf(err, "failed to open slirp4netns log file %s", logPath)
+ }
+ defer logFile.Close()
+ // Unlink immediately the file so we won't need to worry about cleaning it up later.
+ // It is still accessible through the open fd logFile.
+ if err := os.Remove(logPath); err != nil {
+ return errors.Wrapf(err, "delete file %s", logPath)
+ }
+ cmd.Stdout = logFile
+ cmd.Stderr = logFile
if err := cmd.Start(); err != nil {
return errors.Wrapf(err, "failed to start slirp4netns process")
}
@@ -238,7 +252,15 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
continue
}
if status.Exited() {
- return errors.New("slirp4netns failed")
+ // Seek at the beginning of the file and read all its content
+ if _, err := logFile.Seek(0, 0); err != nil {
+ logrus.Errorf("could not seek log file: %q", err)
+ }
+ logContent, err := ioutil.ReadAll(logFile)
+ if err != nil {
+ return errors.Wrapf(err, "slirp4netns failed")
+ }
+ return errors.Errorf("slirp4netns failed: %q", logContent)
}
if status.Signaled() {
return errors.New("slirp4netns killed by signal")
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 658a2fe4e..448e05bdf 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -602,7 +602,7 @@ func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options
if err != nil {
return -1, nil, errors.Wrapf(err, "cannot start container %s", c.ID())
}
- if err := r.moveConmonToCgroupAndSignal(c, execCmd, parentStartPipe, sessionID); err != nil {
+ if err := r.moveConmonToCgroupAndSignal(c, execCmd, parentStartPipe); err != nil {
return -1, nil, err
}
@@ -986,7 +986,7 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
if err != nil {
return err
}
- if err := r.moveConmonToCgroupAndSignal(ctr, cmd, parentStartPipe, ctr.ID()); err != nil {
+ if err := r.moveConmonToCgroupAndSignal(ctr, cmd, parentStartPipe); err != nil {
return err
}
/* Wait for initial setup and fork, and reap child */
@@ -1213,7 +1213,7 @@ func startCommandGivenSelinux(cmd *exec.Cmd) error {
// moveConmonToCgroupAndSignal gets a container's cgroupParent and moves the conmon process to that cgroup
// it then signals for conmon to start by sending nonse data down the start fd
-func (r *ConmonOCIRuntime) moveConmonToCgroupAndSignal(ctr *Container, cmd *exec.Cmd, startFd *os.File, uuid string) error {
+func (r *ConmonOCIRuntime) moveConmonToCgroupAndSignal(ctr *Container, cmd *exec.Cmd, startFd *os.File) error {
mustCreateCgroup := true
// If cgroup creation is disabled - just signal.
if ctr.config.NoCgroups {
diff --git a/libpod/options.go b/libpod/options.go
index ddc5993af..f779b0413 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1014,6 +1014,13 @@ func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netmo
ctr.config.NetMode = namespaces.NetworkMode(netmode)
ctr.config.CreateNetNS = true
ctr.config.PortMappings = portMappings
+
+ if rootless.IsRootless() {
+ if len(networks) > 0 {
+ return errors.New("cannot use CNI networks with rootless containers")
+ }
+ }
+
ctr.config.Networks = networks
return nil
@@ -1487,6 +1494,8 @@ func WithVolumeLabels(labels map[string]string) VolumeCreateOption {
}
// WithVolumeOptions sets the options of the volume.
+// If the "local" driver has been selected, options will be validated. There are
+// currently 3 valid options for the "local" driver - o, type, and device.
func WithVolumeOptions(options map[string]string) VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
@@ -1495,6 +1504,13 @@ func WithVolumeOptions(options map[string]string) VolumeCreateOption {
volume.config.Options = make(map[string]string)
for key, value := range options {
+ switch key {
+ case "type", "device", "o":
+ volume.config.Options[key] = value
+ default:
+ return errors.Wrapf(define.ErrInvalidArg, "unrecognized volume option %q is not supported with local driver", key)
+ }
+
volume.config.Options[key] = value
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 93d6fbead..0405a9b85 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -518,6 +518,17 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options ..
return nil, err
}
+ // storage.conf
+ storageConfFile, err := storage.DefaultConfigFile(rootless.IsRootless())
+ if err != nil {
+ return nil, err
+ }
+
+ createStorageConfFile := false
+ if _, err := os.Stat(storageConfFile); os.IsNotExist(err) {
+ createStorageConfFile = true
+ }
+
defRunConf, err := defaultRuntimeConfig()
if err != nil {
return nil, err
@@ -692,27 +703,21 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options ..
}
if rootless.IsRootless() && configPath == "" {
- configPath, err := getRootlessConfigPath()
- if err != nil {
- return nil, err
- }
-
- // storage.conf
- storageConfFile, err := storage.DefaultConfigFile(rootless.IsRootless())
- if err != nil {
- return nil, err
- }
- if _, err := os.Stat(storageConfFile); os.IsNotExist(err) {
+ if createStorageConfFile {
if err := util.WriteStorageConfigFile(&runtime.config.StorageConfig, storageConfFile); err != nil {
return nil, errors.Wrapf(err, "cannot write config file %s", storageConfFile)
}
}
+ configPath, err := getRootlessConfigPath()
+ if err != nil {
+ return nil, err
+ }
if configPath != "" {
- if err := os.MkdirAll(filepath.Dir(configPath), 0755); err != nil {
+ if err := os.MkdirAll(filepath.Dir(configPath), 0711); err != nil {
return nil, err
}
- file, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+ file, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
if err != nil && !os.IsExist(err) {
return nil, errors.Wrapf(err, "cannot open file %s", configPath)
}
@@ -1500,6 +1505,25 @@ func (r *Runtime) GetOCIRuntimePath() string {
// TODO Once runc has support for cgroups, this function should be removed.
func cgroupV2Check(configPath string, tmpConfig *RuntimeConfig) error {
if !tmpConfig.CgroupCheck && rootless.IsRootless() {
+ if tmpConfig.CgroupManager == SystemdCgroupsManager {
+ // If we are running rootless and the systemd manager is requested, be sure that dbus is accessible
+ session := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
+ hasSession := session != ""
+ if hasSession && strings.HasPrefix(session, "unix:path=") {
+ _, err := os.Stat(strings.TrimPrefix(session, "unix:path="))
+ hasSession = err == nil
+ }
+
+ if !hasSession {
+ logrus.Warningf("The cgroups manager is set to systemd but there is no systemd user session available")
+ logrus.Warningf("For using systemd, you may need to login using an user session")
+ logrus.Warningf("Alternatively, you can enable lingering with: `loginctl enable-linger %d` (possibily as root)", rootless.GetRootlessUID())
+ logrus.Warningf("Falling back to --cgroup-manager=cgroupfs")
+
+ tmpConfig.CgroupManager = CgroupfsCgroupsManager
+ }
+
+ }
cgroupsV2, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return err
@@ -1513,7 +1537,7 @@ func cgroupV2Check(configPath string, tmpConfig *RuntimeConfig) error {
}
tmpConfig.CgroupCheck = true
tmpConfig.OCIRuntime = path
- file, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE, 0666)
+ file, err := os.OpenFile(configPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return errors.Wrapf(err, "cannot open file %s", configPath)
}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 411264d25..2b214d572 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -295,21 +295,32 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (c *Contai
// Maintain an array of them - we need to lock them later.
ctrNamedVolumes := make([]*Volume, 0, len(ctr.config.NamedVolumes))
for _, vol := range ctr.config.NamedVolumes {
- // Check if it exists already
- dbVol, err := r.state.Volume(vol.Name)
- if err == nil {
- ctrNamedVolumes = append(ctrNamedVolumes, dbVol)
- // The volume exists, we're good
- continue
- } else if errors.Cause(err) != define.ErrNoSuchVolume {
- return nil, errors.Wrapf(err, "error retrieving named volume %s for new container", vol.Name)
+ isAnonymous := false
+ if vol.Name == "" {
+ // Anonymous volume. We'll need to create it.
+ // It needs a name first.
+ vol.Name = stringid.GenerateNonCryptoID()
+ isAnonymous = true
+ } else {
+ // Check if it exists already
+ dbVol, err := r.state.Volume(vol.Name)
+ if err == nil {
+ ctrNamedVolumes = append(ctrNamedVolumes, dbVol)
+ // The volume exists, we're good
+ continue
+ } else if errors.Cause(err) != define.ErrNoSuchVolume {
+ return nil, errors.Wrapf(err, "error retrieving named volume %s for new container", vol.Name)
+ }
}
logrus.Debugf("Creating new volume %s for container", vol.Name)
// The volume does not exist, so we need to create it.
- newVol, err := r.newVolume(ctx, WithVolumeName(vol.Name), withSetCtrSpecific(),
- WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID()))
+ volOptions := []VolumeCreateOption{WithVolumeName(vol.Name), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID())}
+ if isAnonymous {
+ volOptions = append(volOptions, withSetCtrSpecific())
+ }
+ newVol, err := r.newVolume(ctx, volOptions...)
if err != nil {
return nil, errors.Wrapf(err, "error creating named volume %q", vol.Name)
}
diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go
index ba4fff4be..5b05acea4 100644
--- a/libpod/runtime_volume_linux.go
+++ b/libpod/runtime_volume_linux.go
@@ -48,6 +48,15 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption)
}
volume.config.CreatedTime = time.Now()
+ // Check if volume with given name exists.
+ exists, err := r.state.HasVolume(volume.config.Name)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error checking if volume with name %s exists", volume.config.Name)
+ }
+ if exists {
+ return nil, errors.Wrapf(define.ErrVolumeExists, "volume with name %s already exists", volume.config.Name)
+ }
+
if volume.config.Driver == define.VolumeDriverLocal {
logrus.Debugf("Validating options for local driver")
// Validate options
diff --git a/libpod/volume_inspect.go b/libpod/volume_inspect.go
index 87ed9d340..c333b8961 100644
--- a/libpod/volume_inspect.go
+++ b/libpod/volume_inspect.go
@@ -62,6 +62,9 @@ func (v *Volume) Inspect() (*InspectVolumeData, error) {
}
data.Scope = v.Scope()
data.Options = make(map[string]string)
+ for k, v := range v.config.Options {
+ data.Options[k] = v
+ }
data.UID = v.config.UID
data.GID = v.config.GID
data.ContainerSpecific = v.config.IsCtrSpecific
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index 5c33467a7..ae91bd812 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -269,7 +269,7 @@ func (r *LocalRuntime) UmountRootFilesystems(ctx context.Context, cli *cliconfig
logrus.Debugf("Error umounting container %s, storage.ErrLayerNotMounted", ctr.ID())
continue
}
- failures[ctr.ID()] = errors.Wrapf(err, "error unmounting continaner %s", ctr.ID())
+ failures[ctr.ID()] = errors.Wrapf(err, "error unmounting container %s", ctr.ID())
} else {
ok = append(ok, ctr.ID())
}
@@ -437,8 +437,12 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode
}
if c.IsSet("rm") {
- if err := r.Runtime.RemoveContainer(ctx, ctr, false, false); err != nil {
- logrus.Errorf("Error removing container %s: %v", ctr.ID(), err)
+ if err := r.Runtime.RemoveContainer(ctx, ctr, false, true); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ logrus.Warnf("Container %s does not exist: %v", ctr.ID(), err)
+ } else {
+ logrus.Errorf("Error removing container %s: %v", ctr.ID(), err)
+ }
}
}
@@ -1053,7 +1057,7 @@ func (r *LocalRuntime) CleanupContainers(ctx context.Context, cli *cliconfig.Cle
// Only used when cleaning up containers
func removeContainer(ctx context.Context, ctr *libpod.Container, runtime *LocalRuntime) error {
- if err := runtime.RemoveContainer(ctx, ctr, false, false); err != nil {
+ if err := runtime.RemoveContainer(ctx, ctr, false, true); err != nil {
return errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID())
}
return nil
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index f7cb28b0c..f4e83a975 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -1092,6 +1092,7 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std
// These are the special writers that encode input from the client.
varlinkStdinWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdin)
varlinkResizeWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.TerminalResize)
+ varlinkHangupWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.HangUpFromClient)
go func() {
// Read from the wire and direct to stdout or stderr
@@ -1117,7 +1118,6 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std
}
}
}()
-
if stdin != nil {
// Takes stdinput and sends it over the wire after being encoded
go func() {
@@ -1126,7 +1126,12 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std
sendGenericError(ecChan)
errChan <- err
}
-
+ _, err := varlinkHangupWriter.Write([]byte("EOF"))
+ if err != nil {
+ logrus.Errorf("unable to notify server to hangup: %q", err)
+ }
+ err = varlinkStdinWriter.Close()
+ errChan <- err
}()
}
return errChan
diff --git a/pkg/adapter/network.go b/pkg/adapter/network.go
index d407984ce..9659ae339 100644
--- a/pkg/adapter/network.go
+++ b/pkg/adapter/network.go
@@ -155,15 +155,14 @@ func (r *LocalRuntime) removeNetwork(ctx context.Context, name string, container
// NetworkCreate creates a CNI network
func (r *LocalRuntime) NetworkCreate(cli *cliconfig.NetworkCreateValues) (string, error) {
- var (
- err error
- )
-
isGateway := true
ipMasq := true
subnet := &cli.Network
ipRange := cli.IPRange
-
+ runtimeConfig, err := r.GetConfig()
+ if err != nil {
+ return "", err
+ }
// if range is provided, make sure it is "in" network
if cli.IsSet("subnet") {
// if network is provided, does it conflict with existing CNI or live networks
@@ -245,6 +244,11 @@ func (r *LocalRuntime) NetworkCreate(cli *cliconfig.NetworkCreateValues) (string
plugins = append(plugins, bridge)
plugins = append(plugins, network.NewPortMapPlugin())
plugins = append(plugins, network.NewFirewallPlugin())
+ // if we find the dnsname plugin, we add configuration for it
+ if network.HasDNSNamePlugin(runtimeConfig.CNIPluginDir) && !cli.DisableDNS {
+ // Note: in the future we might like to allow for dynamic domain names
+ plugins = append(plugins, network.NewDNSNamePlugin(network.DefaultPodmanDomainName))
+ }
ncList["plugins"] = plugins
b, err := json.MarshalIndent(ncList, "", " ")
if err != nil {
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index 0706d4b6a..84d43c337 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -186,7 +186,12 @@ func (r *LocalRuntime) CreateVolume(ctx context.Context, c *cliconfig.VolumeCrea
}
if len(opts) != 0 {
- options = append(options, libpod.WithVolumeOptions(opts))
+ // We need to process -o for uid, gid
+ parsedOptions, err := shared.ParseVolumeOptions(opts)
+ if err != nil {
+ return "", err
+ }
+ options = append(options, parsedOptions...)
}
newVolume, err := r.NewVolume(ctx, options...)
if err != nil {
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index 3b808a2ee..870e86896 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -659,12 +659,39 @@ func (r *LocalRuntime) Push(ctx context.Context, srcName, destination, manifestM
}
// InspectVolumes returns a slice of volumes based on an arg list or --all
-func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*Volume, error) {
- reply, err := iopodman.GetVolumes().Call(r.Conn, c.InputArgs, c.All)
- if err != nil {
- return nil, err
+func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeInspectValues) ([]*libpod.InspectVolumeData, error) {
+ var (
+ inspectData []*libpod.InspectVolumeData
+ volumes []string
+ )
+
+ if c.All {
+ allVolumes, err := r.Volumes(ctx)
+ if err != nil {
+ return nil, err
+ }
+ for _, vol := range allVolumes {
+ volumes = append(volumes, vol.Name())
+ }
+ } else {
+ for _, arg := range c.InputArgs {
+ volumes = append(volumes, arg)
+ }
}
- return varlinkVolumeToVolume(r, reply), nil
+
+ for _, vol := range volumes {
+ jsonString, err := iopodman.InspectVolume().Call(r.Conn, vol)
+ if err != nil {
+ return nil, err
+ }
+ inspectJSON := new(libpod.InspectVolumeData)
+ if err := json.Unmarshal([]byte(jsonString), inspectJSON); err != nil {
+ return nil, errors.Wrapf(err, "error unmarshalling inspect JSON for volume %s", vol)
+ }
+ inspectData = append(inspectData, inspectJSON)
+ }
+
+ return inspectData, nil
}
// Volumes returns a slice of adapter.volumes based on information about libpod
diff --git a/pkg/network/config.go b/pkg/network/config.go
index 7eaa83833..37eb0dd64 100644
--- a/pkg/network/config.go
+++ b/pkg/network/config.go
@@ -14,6 +14,9 @@ const (
// CNIDeviceName is the default network device name and in
// reality should have an int appended to it (cni-podman4)
CNIDeviceName = "cni-podman"
+ // DefaultPodmanDomainName is used for the dnsname plugin to define
+ // a localized domain name for a created network
+ DefaultPodmanDomainName = "dns.podman"
)
// GetDefaultPodmanNetwork outputs the default network for podman
@@ -97,3 +100,14 @@ type FirewallConfig struct {
func (f FirewallConfig) Bytes() ([]byte, error) {
return json.MarshalIndent(f, "", "\t")
}
+
+// DNSNameConfig describes the dns container name resolution plugin config
+type DNSNameConfig struct {
+ PluginType string `json:"type"`
+ DomainName string `json:"domainName"`
+}
+
+// Bytes outputs the configuration as []byte
+func (d DNSNameConfig) Bytes() ([]byte, error) {
+ return json.MarshalIndent(d, "", "\t")
+}
diff --git a/pkg/network/devices.go b/pkg/network/devices.go
index 85068a7d1..78e1a5aa5 100644
--- a/pkg/network/devices.go
+++ b/pkg/network/devices.go
@@ -24,19 +24,26 @@ func GetFreeDeviceName() (string, error) {
if err != nil {
return "", err
}
+ bridgeNames, err := GetBridgeNamesFromFileSystem()
+ if err != nil {
+ return "", err
+ }
for {
deviceName = fmt.Sprintf("%s%d", CNIDeviceName, deviceNum)
- logrus.Debugf("checking if device name %s exists in other cni networks", deviceName)
+ logrus.Debugf("checking if device name %q exists in other cni networks", deviceName)
if util.StringInSlice(deviceName, networkNames) {
deviceNum++
continue
}
- logrus.Debugf("checking if device name %s exists in live networks", deviceName)
- if !util.StringInSlice(deviceName, liveNetworksNames) {
+ logrus.Debugf("checking if device name %q exists in live networks", deviceName)
+ if util.StringInSlice(deviceName, liveNetworksNames) {
+ deviceNum++
+ continue
+ }
+ logrus.Debugf("checking if device name %q already exists as a bridge name ", deviceName)
+ if !util.StringInSlice(deviceName, bridgeNames) {
break
}
- // TODO Still need to check the bridge names for a conflict but I dont know
- // how to get them yet!
deviceNum++
}
return deviceName, nil
diff --git a/pkg/network/files.go b/pkg/network/files.go
index d55ec2dfd..2f3932974 100644
--- a/pkg/network/files.go
+++ b/pkg/network/files.go
@@ -129,3 +129,29 @@ func GetInterfaceNameFromConfig(path string) (string, error) {
}
return name, nil
}
+
+// GetBridgeNamesFromFileSystem is a convenience function to get all the bridge
+// names from the configured networks
+func GetBridgeNamesFromFileSystem() ([]string, error) {
+ var bridgeNames []string
+ networks, err := LoadCNIConfsFromDir(CNIConfigDir)
+ if err != nil {
+ return nil, err
+ }
+ for _, n := range networks {
+ var name string
+ // iterate network conflists
+ for _, cniplugin := range n.Plugins {
+ // iterate plugins
+ if cniplugin.Network.Type == "bridge" {
+ plugin := make(map[string]interface{})
+ if err := json.Unmarshal(cniplugin.Bytes, &plugin); err != nil {
+ continue
+ }
+ name = plugin["bridge"].(string)
+ }
+ }
+ bridgeNames = append(bridgeNames, name)
+ }
+ return bridgeNames, nil
+}
diff --git a/pkg/network/netconflist.go b/pkg/network/netconflist.go
index c3b11b409..e19051b88 100644
--- a/pkg/network/netconflist.go
+++ b/pkg/network/netconflist.go
@@ -2,6 +2,8 @@ package network
import (
"net"
+ "os"
+ "path/filepath"
)
// NcList describes a generic map
@@ -111,3 +113,22 @@ func NewFirewallPlugin() FirewallConfig {
Backend: "iptables",
}
}
+
+// NewDNSNamePlugin creates the dnsname config with a given
+// domainname
+func NewDNSNamePlugin(domainName string) DNSNameConfig {
+ return DNSNameConfig{
+ PluginType: "dnsname",
+ DomainName: domainName,
+ }
+}
+
+// HasDNSNamePlugin looks to see if the dnsname cni plugin is present
+func HasDNSNamePlugin(paths []string) bool {
+ for _, p := range paths {
+ if _, err := os.Stat(filepath.Join(p, "dnsname")); err == nil {
+ return true
+ }
+ }
+ return false
+}
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 8f00d3270..da5c14948 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -300,6 +300,15 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
blockAccessToKernelFilesystems(config, &g)
+ var runtimeConfig *libpod.RuntimeConfig
+
+ if runtime != nil {
+ runtimeConfig, err = runtime.GetConfig()
+ if err != nil {
+ return nil, err
+ }
+ }
+
// RESOURCES - PIDS
if config.Resources.PidsLimit > 0 {
// if running on rootless on a cgroupv1 machine or using the cgroupfs manager, pids
@@ -312,11 +321,7 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
if err != nil {
return nil, err
}
- runtimeConfig, err := runtime.GetConfig()
- if err != nil {
- return nil, err
- }
- if (!cgroup2 || runtimeConfig.CgroupManager != libpod.SystemdCgroupsManager) && config.Resources.PidsLimit == sysinfo.GetDefaultPidsLimit() {
+ if (!cgroup2 || (runtimeConfig != nil && runtimeConfig.CgroupManager != libpod.SystemdCgroupsManager)) && config.Resources.PidsLimit == sysinfo.GetDefaultPidsLimit() {
setPidLimit = false
}
}
@@ -411,10 +416,13 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
if !addedResources {
configSpec.Linux.Resources = &spec.LinuxResources{}
}
- if addedResources && !cgroup2 {
- return nil, errors.New("invalid configuration, cannot set resources with rootless containers not using cgroups v2 unified mode")
+
+ canUseResources := cgroup2 && runtimeConfig != nil && (runtimeConfig.CgroupManager == libpod.SystemdCgroupsManager)
+
+ if addedResources && !canUseResources {
+ return nil, errors.New("invalid configuration, cannot specify resource limits without cgroups v2 and --cgroup-manager=systemd")
}
- if !cgroup2 {
+ if !canUseResources {
// Force the resources block to be empty instead of having default values.
configSpec.Linux.Resources = &spec.LinuxResources{}
}
diff --git a/pkg/spec/storage.go b/pkg/spec/storage.go
index 93919dd0a..095534589 100644
--- a/pkg/spec/storage.go
+++ b/pkg/spec/storage.go
@@ -11,7 +11,6 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/util"
pmount "github.com/containers/storage/pkg/mount"
- "github.com/containers/storage/pkg/stringid"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -648,7 +647,7 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string
mounts := make(map[string]spec.Mount)
volumes := make(map[string]*libpod.ContainerNamedVolume)
- volumeFormatErr := errors.Errorf("incorrect volume format, should be host-dir:ctr-dir[:option]")
+ volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
for _, vol := range config.Volumes {
var (
@@ -665,7 +664,11 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string
src = splitVol[0]
if len(splitVol) == 1 {
- dest = src
+ // This is an anonymous named volume. Only thing given
+ // is destination.
+ // Name/source will be blank, and populated by libpod.
+ src = ""
+ dest = splitVol[0]
} else if len(splitVol) > 1 {
dest = splitVol[1]
}
@@ -675,8 +678,11 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string
}
}
- if err := parse.ValidateVolumeHostDir(src); err != nil {
- return nil, nil, err
+ // Do not check source dir for anonymous volumes
+ if len(splitVol) > 1 {
+ if err := parse.ValidateVolumeHostDir(src); err != nil {
+ return nil, nil, err
+ }
}
if err := parse.ValidateVolumeCtrDir(dest); err != nil {
return nil, nil, err
@@ -732,13 +738,13 @@ func (config *CreateConfig) getImageVolumes() (map[string]spec.Mount, map[string
Destination: cleanDest,
Source: TypeTmpfs,
Type: TypeTmpfs,
- Options: []string{"rprivate", "rw", "nodev"},
+ Options: []string{"rprivate", "rw", "nodev", "exec"},
}
mounts[vol] = mount
} else {
+ // Anonymous volumes have no name.
namedVolume := new(libpod.ContainerNamedVolume)
- namedVolume.Name = stringid.GenerateNonCryptoID()
- namedVolume.Options = []string{"rprivate", "rw", "nodev"}
+ namedVolume.Options = []string{"rprivate", "rw", "nodev", "exec"}
namedVolume.Dest = cleanDest
volumes[vol] = namedVolume
}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 0190b106d..d9a84e4e5 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -318,7 +318,7 @@ func WriteStorageConfigFile(storageOpts *storage.StoreOptions, storageConf strin
if err := os.MkdirAll(filepath.Dir(storageConf), 0755); err != nil {
return err
}
- storageFile, err := os.OpenFile(storageConf, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+ storageFile, err := os.OpenFile(storageConf, os.O_RDWR|os.O_TRUNC, 0600)
if err != nil {
return errors.Wrapf(err, "cannot open %s", storageConf)
}
diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go
index f8557ae0c..37adbbf55 100644
--- a/pkg/varlinkapi/attach.go
+++ b/pkg/varlinkapi/attach.go
@@ -70,7 +70,6 @@ func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys st
}
reader, writer, _, pw, streams := setupStreams(call)
-
go func() {
if err := virtwriter.Reader(reader, nil, nil, pw, resize, nil); err != nil {
errChan <- err
diff --git a/pkg/varlinkapi/virtwriter/virtwriter.go b/pkg/varlinkapi/virtwriter/virtwriter.go
index 27ecd1f52..dd171943f 100644
--- a/pkg/varlinkapi/virtwriter/virtwriter.go
+++ b/pkg/varlinkapi/virtwriter/virtwriter.go
@@ -5,6 +5,7 @@ import (
"encoding/binary"
"encoding/json"
"io"
+ "time"
"github.com/pkg/errors"
"k8s.io/client-go/tools/remotecommand"
@@ -26,8 +27,14 @@ const (
TerminalResize SocketDest = iota
// Quit and detach
Quit SocketDest = iota
+ // Quit from the client
+ HangUpFromClient SocketDest = iota
)
+// ClientHangup signifies that the client wants to drop its
+// connection from the server
+var ClientHangup = errors.New("client hangup")
+
// IntToSocketDest returns a socketdest based on integer input
func IntToSocketDest(i int) SocketDest {
switch i {
@@ -41,6 +48,8 @@ func IntToSocketDest(i int) SocketDest {
return TerminalResize
case Quit.Int():
return Quit
+ case HangUpFromClient.Int():
+ return HangUpFromClient
default:
return ToStderr
}
@@ -65,7 +74,7 @@ func NewVirtWriteCloser(w *bufio.Writer, dest SocketDest) VirtWriteCloser {
// Close is a required method for a writecloser
func (v VirtWriteCloser) Close() error {
- return nil
+ return v.writer.Flush()
}
// Write prepends a header to the input message. The header is
@@ -96,7 +105,6 @@ func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remote
if r == nil {
return errors.Errorf("Reader must not be nil")
}
-
for {
n, err := io.ReadFull(r, headerBytes)
if err != nil {
@@ -107,7 +115,6 @@ func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remote
}
messageSize = int64(binary.BigEndian.Uint32(headerBytes[4:8]))
-
switch IntToSocketDest(int(headerBytes[0])) {
case ToStdout:
if output != nil {
@@ -161,7 +168,16 @@ func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remote
execEcChan <- int(ecInt)
}
return nil
-
+ case HangUpFromClient:
+ // This sleep allows the pipes to flush themselves before tearing everything down.
+ // It makes me sick to do it but after a full day I cannot put my finger on the race
+ // that occurs when closing things up. It would require a significant rewrite of code
+ // to make the pipes close down properly. Given that we are currently discussing a
+ // rewrite of all things remote, this hardly seems worth resolving.
+ //
+ // reproducer: echo hello | (podman-remote run -i alpine cat)
+ time.Sleep(1 * time.Second)
+ return ClientHangup
default:
// Something really went wrong
return errors.New("unknown multiplex destination")
diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go
index b41eb5086..2dddd3008 100644
--- a/pkg/varlinkapi/volumes.go
+++ b/pkg/varlinkapi/volumes.go
@@ -3,6 +3,8 @@
package varlinkapi
import (
+ "encoding/json"
+
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
@@ -22,7 +24,11 @@ func (i *LibpodAPI) VolumeCreate(call iopodman.VarlinkCall, options iopodman.Vol
volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(options.Labels))
}
if len(options.Options) > 0 {
- volumeOptions = append(volumeOptions, libpod.WithVolumeOptions(options.Options))
+ parsedOptions, err := shared.ParseVolumeOptions(options.Options)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ volumeOptions = append(volumeOptions, parsedOptions...)
}
newVolume, err := i.Runtime.NewVolume(getContext(), volumeOptions...)
if err != nil {
@@ -80,6 +86,23 @@ func (i *LibpodAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all boo
return call.ReplyGetVolumes(volumes)
}
+// InspectVolume inspects a single volume, returning its JSON as a string.
+func (i *LibpodAPI) InspectVolume(call iopodman.VarlinkCall, name string) error {
+ vol, err := i.Runtime.LookupVolume(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ inspectOut, err := vol.Inspect()
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ inspectJSON, err := json.Marshal(inspectOut)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyInspectVolume(string(inspectJSON))
+}
+
// VolumesPrune removes unused images via a varlink call
func (i *LibpodAPI) VolumesPrune(call iopodman.VarlinkCall) error {
var errs []string
diff --git a/test/e2e/run_signal_test.go b/test/e2e/run_signal_test.go
index 1dbac1dc9..1d57e6211 100644
--- a/test/e2e/run_signal_test.go
+++ b/test/e2e/run_signal_test.go
@@ -19,6 +19,7 @@ import (
)
const sigCatch = "trap \"echo FOO >> /h/fifo \" 8; echo READY >> /h/fifo; while :; do sleep 0.25; done"
+const sigCatch2 = "trap \"echo Received\" SIGFPE; while :; do sleep 0.25; done"
var _ = Describe("Podman run with --sig-proxy", func() {
var (
@@ -110,11 +111,11 @@ var _ = Describe("Podman run with --sig-proxy", func() {
})
Specify("signals are not forwarded to container with sig-proxy false", func() {
- signal := syscall.SIGPOLL
+ signal := syscall.SIGFPE
if rootless.IsRootless() {
podmanTest.RestoreArtifact(fedoraMinimal)
}
- session, pid := podmanTest.PodmanPID([]string{"run", "--name", "test2", "--sig-proxy=false", fedoraMinimal, "bash", "-c", sigCatch})
+ session, pid := podmanTest.PodmanPID([]string{"run", "--name", "test2", "--sig-proxy=false", fedoraMinimal, "bash", "-c", sigCatch2})
ok := WaitForContainer(podmanTest)
Expect(ok).To(BeTrue())
@@ -132,7 +133,7 @@ var _ = Describe("Podman run with --sig-proxy", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(137))
- ok, _ = session.GrepString(fmt.Sprintf("Received %d", signal))
+ ok, _ = session.GrepString("Received")
Expect(ok).To(BeFalse())
})
diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go
index 94bfebab7..c96059787 100644
--- a/test/e2e/run_volume_test.go
+++ b/test/e2e/run_volume_test.go
@@ -280,4 +280,88 @@ var _ = Describe("Podman run with volumes", func() {
session2.WaitWithDefaultTimeout()
Expect(session2.ExitCode()).To(Equal(0))
})
+
+ It("podman run with anonymous volume", func() {
+ list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"})
+ list1.WaitWithDefaultTimeout()
+ Expect(list1.ExitCode()).To(Equal(0))
+ Expect(list1.OutputToString()).To(Equal(""))
+
+ session := podmanTest.Podman([]string{"create", "-v", "/test", ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"})
+ list2.WaitWithDefaultTimeout()
+ Expect(list2.ExitCode()).To(Equal(0))
+ arr := list2.OutputToStringArray()
+ Expect(len(arr)).To(Equal(1))
+ Expect(arr[0]).To(Not(Equal("")))
+ })
+
+ It("podman rm -v removes anonymous volume", func() {
+ list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"})
+ list1.WaitWithDefaultTimeout()
+ Expect(list1.ExitCode()).To(Equal(0))
+ Expect(list1.OutputToString()).To(Equal(""))
+
+ ctrName := "testctr"
+ session := podmanTest.Podman([]string{"create", "--name", ctrName, "-v", "/test", ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"})
+ list2.WaitWithDefaultTimeout()
+ Expect(list2.ExitCode()).To(Equal(0))
+ arr := list2.OutputToStringArray()
+ Expect(len(arr)).To(Equal(1))
+ Expect(arr[0]).To(Not(Equal("")))
+
+ remove := podmanTest.Podman([]string{"rm", "-v", ctrName})
+ remove.WaitWithDefaultTimeout()
+ Expect(remove.ExitCode()).To(Equal(0))
+
+ list3 := podmanTest.Podman([]string{"volume", "list", "--quiet"})
+ list3.WaitWithDefaultTimeout()
+ Expect(list3.ExitCode()).To(Equal(0))
+ Expect(list3.OutputToString()).To(Equal(""))
+ })
+
+ It("podman rm -v retains named volume", func() {
+ list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"})
+ list1.WaitWithDefaultTimeout()
+ Expect(list1.ExitCode()).To(Equal(0))
+ Expect(list1.OutputToString()).To(Equal(""))
+
+ ctrName := "testctr"
+ volName := "testvol"
+ session := podmanTest.Podman([]string{"create", "--name", ctrName, "-v", fmt.Sprintf("%s:/test", volName), ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"})
+ list2.WaitWithDefaultTimeout()
+ Expect(list2.ExitCode()).To(Equal(0))
+ arr := list2.OutputToStringArray()
+ Expect(len(arr)).To(Equal(1))
+ Expect(arr[0]).To(Equal(volName))
+
+ remove := podmanTest.Podman([]string{"rm", "-v", ctrName})
+ remove.WaitWithDefaultTimeout()
+ Expect(remove.ExitCode()).To(Equal(0))
+
+ list3 := podmanTest.Podman([]string{"volume", "list", "--quiet"})
+ list3.WaitWithDefaultTimeout()
+ Expect(list3.ExitCode()).To(Equal(0))
+ arr2 := list3.OutputToStringArray()
+ Expect(len(arr2)).To(Equal(1))
+ Expect(arr2[0]).To(Equal(volName))
+ })
+
+ It("podman run image volume is not noexec", func() {
+ session := podmanTest.Podman([]string{"run", "--rm", redis, "grep", "/data", "/proc/self/mountinfo"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(session.OutputToString()).To(Not(ContainSubstring("noexec")))
+ })
})
diff --git a/test/e2e/stats_test.go b/test/e2e/stats_test.go
index 4000ab33a..fbf7c9920 100644
--- a/test/e2e/stats_test.go
+++ b/test/e2e/stats_test.go
@@ -11,6 +11,8 @@ import (
. "github.com/onsi/gomega"
)
+// TODO: we need to check the output. Currently, we only check the exit codes
+// which is not enough.
var _ = Describe("Podman stats", func() {
var (
tempdir string
@@ -61,6 +63,15 @@ var _ = Describe("Podman stats", func() {
Expect(session.ExitCode()).To(Equal(0))
})
+ It("podman stats on all running containers", func() {
+ session := podmanTest.RunTopContainer("")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"stats", "--no-stream"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
It("podman stats only output cids", func() {
session := podmanTest.RunTopContainer("")
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/volume_create_test.go b/test/e2e/volume_create_test.go
index 41107b5ba..71023f9e2 100644
--- a/test/e2e/volume_create_test.go
+++ b/test/e2e/volume_create_test.go
@@ -1,6 +1,7 @@
package integration
import (
+ "fmt"
"os"
. "github.com/containers/libpod/test/utils"
@@ -63,4 +64,23 @@ var _ = Describe("Podman volume create", func() {
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError())
})
+
+ It("podman create volume with o=uid,gid", func() {
+ volName := "testVol"
+ uid := "3000"
+ gid := "4000"
+ session := podmanTest.Podman([]string{"volume", "create", "--opt", fmt.Sprintf("o=uid=%s,gid=%s", uid, gid), volName})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ inspectUID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .UID }}", volName})
+ inspectUID.WaitWithDefaultTimeout()
+ Expect(inspectUID.ExitCode()).To(Equal(0))
+ Expect(inspectUID.OutputToString()).To(Equal(uid))
+
+ inspectGID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .GID }}", volName})
+ inspectGID.WaitWithDefaultTimeout()
+ Expect(inspectGID.ExitCode()).To(Equal(0))
+ Expect(inspectGID.OutputToString()).To(Equal(gid))
+ })
})
diff --git a/test/e2e/volume_inspect_test.go b/test/e2e/volume_inspect_test.go
index 0683c6bbf..5015e0535 100644
--- a/test/e2e/volume_inspect_test.go
+++ b/test/e2e/volume_inspect_test.go
@@ -2,6 +2,7 @@ package integration
import (
"os"
+ "strings"
. "github.com/containers/libpod/test/utils"
. "github.com/onsi/ginkgo"
@@ -74,4 +75,16 @@ var _ = Describe("Podman volume inspect", func() {
Expect(session.OutputToStringArray()[0]).To(Equal(volName1))
Expect(session.OutputToStringArray()[1]).To(Equal(volName2))
})
+
+ It("inspect volume finds options", func() {
+ volName := "testvol"
+ session := podmanTest.Podman([]string{"volume", "create", "--opt", "type=tmpfs", volName})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"volume", "inspect", volName})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ Expect(strings.Contains(inspect.OutputToString(), "tmpfs")).To(BeTrue())
+ })
})
diff --git a/test/system/015-help.bats b/test/system/015-help.bats
index a987f04bc..fd4be87b2 100644
--- a/test/system/015-help.bats
+++ b/test/system/015-help.bats
@@ -55,9 +55,11 @@ function check_help() {
# If usage has required arguments, try running without them
if expr "$usage" : '.*\[flags\] [A-Z]' >/dev/null; then
- dprint "podman $@ $cmd (without required args)"
- run_podman 125 "$@" $cmd
- is "$output" "Error:"
+ if [ "$cmd" != "stats"]; then
+ dprint "podman $@ $cmd (without required args)"
+ run_podman 125 "$@" $cmd
+ is "$output" "Error:"
+ fi
fi
count=$(expr $count + 1)