aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml20
-rw-r--r--.gitignore2
-rwxr-xr-xcontrib/cirrus/logcollector.sh5
-rwxr-xr-xcontrib/cirrus/runner.sh10
-rw-r--r--docs/source/Commands.rst1
-rw-r--r--docs/source/markdown/podman-build.1.md4
-rw-r--r--docs/source/markdown/podman-create.1.md24
-rw-r--r--docs/source/markdown/podman-image-trust.1.md8
-rw-r--r--docs/source/markdown/podman-images.1.md4
-rw-r--r--docs/source/markdown/podman-import.1.md2
-rw-r--r--docs/source/markdown/podman-info.1.md4
-rw-r--r--docs/source/markdown/podman-logs.1.md2
-rw-r--r--docs/source/markdown/podman-network-create.1.md2
-rw-r--r--docs/source/markdown/podman-play-kube.1.md2
-rw-r--r--docs/source/markdown/podman-pod-create.1.md14
-rw-r--r--docs/source/markdown/podman-pod-inspect.1.md2
-rw-r--r--docs/source/markdown/podman-restart.1.md2
-rw-r--r--docs/source/markdown/podman-run.1.md6
-rw-r--r--docs/source/markdown/podman-system-connection-add.1.md4
-rw-r--r--docs/source/markdown/podman-system-df.1.md2
-rw-r--r--docs/source/markdown/podman-volume-create.1.md4
-rw-r--r--docs/source/markdown/podman-volume-inspect.1.md4
-rw-r--r--docs/source/markdown/podman-volume-ls.1.md4
-rw-r--r--docs/source/markdown/podman-volume-prune.1.md2
-rw-r--r--docs/source/markdown/podman-volume-rm.1.md4
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rwxr-xr-xhack/xref-helpmsgs-manpages11
-rw-r--r--libpod/boltdb_state.go295
-rw-r--r--libpod/boltdb_state_internal.go148
-rw-r--r--libpod/container.go28
-rw-r--r--libpod/container_config.go3
-rw-r--r--libpod/container_internal.go18
-rw-r--r--libpod/define/container_inspect.go2
-rw-r--r--libpod/define/errors.go9
-rw-r--r--libpod/in_memory_state.go281
-rw-r--r--libpod/networking_linux.go47
-rw-r--r--libpod/rootless_cni_linux.go22
-rw-r--r--libpod/runtime_ctr.go7
-rw-r--r--libpod/state.go18
-rw-r--r--libpod/state_test.go218
-rw-r--r--test/e2e/logs_test.go18
-rw-r--r--test/e2e/run_volume_test.go8
-rw-r--r--vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/parent.go2
-rw-r--r--vendor/modules.txt2
45 files changed, 419 insertions, 862 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index ea09d691e..4156e3082 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -17,6 +17,9 @@ env:
CIRRUS_SHELL: "/bin/bash"
# Save a little typing (path relative to $CIRRUS_WORKING_DIR)
SCRIPT_BASE: "./contrib/cirrus"
+ # Runner statistics log file path/name
+ STATS_LOGFILE_SFX: 'runner_stats.log'
+ STATS_LOGFILE: '$GOSRC/${CIRRUS_TASK_NAME}-${STATS_LOGFILE_SFX}'
####
#### Cache-image names to test with (double-quotes around names are critical)
@@ -75,7 +78,11 @@ ext_svc_check_task:
TEST_FLAVOR: ext_svc
CTR_FQIN: ${FEDORA_CONTAINER_FQIN}
setup_script: &setup '$GOSRC/$SCRIPT_BASE/setup_environment.sh'
- main_script: &main '$GOSRC/$SCRIPT_BASE/runner.sh'
+ main_script: &main '/usr/bin/time --verbose --output="$STATS_LOGFILE" $GOSRC/$SCRIPT_BASE/runner.sh'
+ always: &runner_stats
+ runner_stats_artifacts:
+ path: ./*-${STATS_LOGFILE_SFX}
+ type: text/plain
# Execute some quick checks to confirm this YAML file and all
@@ -91,6 +98,7 @@ automation_task:
TEST_ENVIRON: container
setup_script: *setup
main_script: *main
+ always: *runner_stats
# This task use to be called 'gating', however that name is being
@@ -126,6 +134,7 @@ smoke_task:
make install.tools
setup_script: *setup
main_script: *main
+ always: *runner_stats
# N/B: This task is critical. It builds all binaries and release archives
@@ -185,6 +194,7 @@ build_task:
setup_script: *setup
main_script: *main
always: &binary_artifacts
+ <<: *runner_stats
gosrc_artifacts:
path: ./* # Grab everything in top-level $GOSRC
type: application/octet-stream
@@ -218,6 +228,7 @@ validate_task:
clone_script: *noop
setup_script: *setup
main_script: *main
+ always: *runner_stats
# Exercise the "libpod" API with a small set of common
@@ -237,6 +248,7 @@ bindings_task:
clone_script: *noop # Comes from cache
setup_script: *setup
main_script: *main
+ always: *runner_stats
# Build the "libpod" API documentation `swagger.yaml` for eventual
@@ -273,6 +285,7 @@ endpoint_task:
clone_script: *full_clone # build-cache not available to container tasks
setup_script: *setup
main_script: *main
+ always: *runner_stats
# Check that all included go modules from other sources match
@@ -291,6 +304,7 @@ vendor_task:
clone_script: *full_clone # build-cache not available to container tasks
setup_script: *setup
main_script: *main
+ always: *runner_stats
# There are several other important variations of podman which
@@ -392,6 +406,7 @@ docker-py_test_task:
clone_script: *noop # Comes from cache
setup_script: *setup
main_script: *main
+ always: *runner_stats
# Does exactly what it says, execute the podman unit-tests on all primary
@@ -410,6 +425,7 @@ unit_test_task:
gopath_cache: *ro_gopath_cache
setup_script: *setup
main_script: *main
+ always: *runner_stats
apiv2_test_task:
@@ -426,6 +442,7 @@ apiv2_test_task:
setup_script: *setup
main_script: *main
always: &logs_artifacts
+ <<: *runner_stats
# Required for `contrib/cirrus/logformatter` to work properly
html_artifacts:
path: ./*.html
@@ -436,6 +453,7 @@ apiv2_test_task:
audit_log_script: '$SCRIPT_BASE/logcollector.sh audit'
journal_script: '$SCRIPT_BASE/logcollector.sh journal'
podman_system_info_script: '$SCRIPT_BASE/logcollector.sh podman'
+ time_script: '$SCRIPT_BASE/logcollector.sh time'
# Execute the podman integration tests on all primary platforms and release
diff --git a/.gitignore b/.gitignore
index 434a12759..9e444442a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,5 @@ release.txt
/test/goecho/goecho
.vscode*
result
+# Necessary to prevent hack/tree-status.sh false-positive
+/*runner_stats.log
diff --git a/contrib/cirrus/logcollector.sh b/contrib/cirrus/logcollector.sh
index 746bccec9..7bf651b36 100755
--- a/contrib/cirrus/logcollector.sh
+++ b/contrib/cirrus/logcollector.sh
@@ -76,5 +76,10 @@ case $1 in
# Any not-present packages will be listed as such
$PKG_LST_CMD "${PKG_NAMES[@]}" | sort -u
;;
+ time)
+ # Assumed to be empty/undefined outside of Cirrus-CI (.cirrus.yml)
+ # shellcheck disable=SC2154
+ if [[ -r "$STATS_LOGFILE" ]]; then cat "$STATS_LOGFILE"; fi
+ ;;
*) die "Warning, $(basename $0) doesn't know how to handle the parameter '$1'"
esac
diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh
index 330343e29..bf2b1a52b 100755
--- a/contrib/cirrus/runner.sh
+++ b/contrib/cirrus/runner.sh
@@ -260,6 +260,16 @@ dotest() {
|& logformatter
}
+# Nearly every task in .cirrus.yml makes use of this shell script
+# wrapped by /usr/bin/time to collect runtime statistics. Because the
+# --output option is used to log stats to a file, every child-process
+# inherits an open FD3 pointing at the log. However, some testing
+# operations depend on making use of FD3, and so it must be explicitly
+# closed here (and for all further child-processes).
+# STATS_LOGFILE assumed empty/undefined outside of Cirrus-CI (.cirrus.yml)
+# shellcheck disable=SC2154
+exec 3<&-
+
msg "************************************************************"
# Required to be defined by caller
# shellcheck disable=SC2154
diff --git a/docs/source/Commands.rst b/docs/source/Commands.rst
index 096bdbedf..881dcb4b6 100644
--- a/docs/source/Commands.rst
+++ b/docs/source/Commands.rst
@@ -3,6 +3,7 @@
Commands
========
+:doc:`Podman <markdown/podman.1>` (Pod Manager) Global Options
:doc:`attach <markdown/podman-attach.1>` Attach to a running container
diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md
index a411d32ab..f9568b5d3 100644
--- a/docs/source/markdown/podman-build.1.md
+++ b/docs/source/markdown/podman-build.1.md
@@ -394,7 +394,7 @@ The format of `LIMIT` is `<number>[<unit>]`. Unit can be `b` (bytes),
`k` (kilobytes), `m` (megabytes), or `g` (gigabytes). If you don't specify a
unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
-#### **--net**, **--network**=*string*
+#### **--network**=*mode*
Sets the configuration for network namespaces when handling `RUN` instructions.
@@ -405,7 +405,7 @@ Valid _mode_ values are:
container full access to local system services such as D-bus and is therefore
considered insecure.
- **ns:**_path_: path to a network namespace to join.
-- `private`: create a new namespace for the container (default).
+- **private**: create a new namespace for the container (default).
#### **--no-cache**
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 5922e0675..a6083201d 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -342,7 +342,7 @@ The initialization time needed for a container to bootstrap. The value can be ex
The maximum time allowed to complete the healthcheck before an interval is considered failed. Like start-period, the
value can be expressed in a time format such as `1m22s`. The default value is `30s`.
-#### **-h**, **--hostname**=*name*
+#### **--hostname**=*name*, **-h**
Container host name
@@ -574,20 +574,20 @@ to the container with **--name** then it will generate a random
string name. The name is useful any place you need to identify a container.
This works for both background and foreground containers.
-#### **--network**, **--net**="*bridge*"
+#### **--network**=*bridge*, **--net**
-Set the Network mode for the container. Invalid if using **--dns**, **--dns-opt**, or **--dns-search** with **--network** that is set to 'none' or 'container:<name|id>'.
+Set the network mode for the container. Invalid if using **--dns**, **--dns-opt**, or **--dns-search** with **--network** that is set to **none** or **container:**_id_.
-Valid values are:
+Valid _mode_ 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
-- `private`: create a new namespace for the container (default)
-- `slirp4netns[:OPTIONS,...]`: use slirp4netns to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options:
+- **bridge**: create a network stack on the default bridge;
+- **none**: no networking;
+- **container:**_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-id_: connect to a user-defined network, multiple networks should be comma separated;
+- **ns:**_path_: path to a network namespace to join;
+- **private**: create a new namespace for the container (default)
+- **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options:
- **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false.
- **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`).
- **enable_ipv6=true|false**: Enable IPv6. Default is false. (Required for `outbound_addr6`).
diff --git a/docs/source/markdown/podman-image-trust.1.md b/docs/source/markdown/podman-image-trust.1.md
index 29dfee87e..002f42bff 100644
--- a/docs/source/markdown/podman-image-trust.1.md
+++ b/docs/source/markdown/podman-image-trust.1.md
@@ -37,15 +37,15 @@ Require signature (“signedBy”).
Trust may be updated using the command **podman image trust set** for an existing trust scope.
## OPTIONS
-#### **-h**, **--help**
+#### **--help**, **-h**
Print usage statement.
-#### **-f**, **--pubkeysfile**=*KEY1*
+#### **--pubkeysfile**=*KEY1*, **-f**
A path to an exported public key on the local system. Key paths
will be referenced in policy.json. Any path to a file may be used but locating the file in **/etc/pki/containers** is recommended. Options may be used multiple times to
require an image be signed by multiple keys. The **--pubkeysfile** option is required for the **signedBy** type.
-#### **-t**, **--type**=*value*
+#### **--type**=*value*, **-t**
The trust type for this policy entry.
Accepted values:
**signedBy** (default): Require signatures with corresponding list of
@@ -59,7 +59,7 @@ Trust may be updated using the command **podman image trust set** for an existin
#### **--raw**
Output trust policy file as raw JSON
-#### **-j**, **--json**
+#### **--json**, **-j**
Output trust as JSON for machine parsing
## EXAMPLES
diff --git a/docs/source/markdown/podman-images.1.md b/docs/source/markdown/podman-images.1.md
index 531570666..9ee62ef2b 100644
--- a/docs/source/markdown/podman-images.1.md
+++ b/docs/source/markdown/podman-images.1.md
@@ -15,7 +15,7 @@ Displays locally stored images, their names, and their IDs.
## OPTIONS
-#### **-a**, **--all**
+#### **--all**, **-a**
Show all images (by default filter out the intermediate image layers). The default is false.
@@ -23,7 +23,7 @@ Show all images (by default filter out the intermediate image layers). The defau
Show image digests
-#### **-f**, **--filter**=*filter*
+#### **--filter**=*filter*, **-f**
Filter output based on conditions provided
diff --git a/docs/source/markdown/podman-import.1.md b/docs/source/markdown/podman-import.1.md
index ba512ca12..ebedf90b4 100644
--- a/docs/source/markdown/podman-import.1.md
+++ b/docs/source/markdown/podman-import.1.md
@@ -18,7 +18,7 @@ Note: `:` is a restricted character and cannot be part of the file name.
## OPTIONS
-#### **-c**, **--change**=*instruction*
+#### **--change**=*instruction*, **-c**
Apply the following possible instructions to the created image:
**CMD** | **ENTRYPOINT** | **ENV** | **EXPOSE** | **LABEL** | **STOPSIGNAL** | **USER** | **VOLUME** | **WORKDIR**
diff --git a/docs/source/markdown/podman-info.1.md b/docs/source/markdown/podman-info.1.md
index 78895fa15..dd01a0f49 100644
--- a/docs/source/markdown/podman-info.1.md
+++ b/docs/source/markdown/podman-info.1.md
@@ -15,11 +15,11 @@ Displays information pertinent to the host, current storage stats, configured co
## OPTIONS
-#### **-D**, **--debug**
+#### **--debug**, **-D**
Show additional information
-#### **-f**, **--format**=*format*
+#### **--format**=*format*, **-f**
Change output format to "json" or a Go template.
diff --git a/docs/source/markdown/podman-logs.1.md b/docs/source/markdown/podman-logs.1.md
index c93e4c9d7..772668c8b 100644
--- a/docs/source/markdown/podman-logs.1.md
+++ b/docs/source/markdown/podman-logs.1.md
@@ -30,7 +30,7 @@ to run containers such as CRI-O, the last started container could be from either
The latest option is not supported on the remote client.
-#### **-n**, **--names**
+#### **--names**, **-n**
Output the container name in the log
diff --git a/docs/source/markdown/podman-network-create.1.md b/docs/source/markdown/podman-network-create.1.md
index 8b8399c3e..cbf9d26dc 100644
--- a/docs/source/markdown/podman-network-create.1.md
+++ b/docs/source/markdown/podman-network-create.1.md
@@ -22,7 +22,7 @@ Upon completion of creating the network, Podman will display the path to the new
Disables the DNS plugin for this network which if enabled, can perform container to container name
resolution.
-#### **-d**, **--driver**
+#### **--driver**, **-d**
Driver to manage the network (default "bridge"). Currently only `bridge` is supported.
diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md
index c8391861d..e14d1ed79 100644
--- a/docs/source/markdown/podman-play-kube.1.md
+++ b/docs/source/markdown/podman-play-kube.1.md
@@ -46,7 +46,7 @@ value can be entered. The password is entered without echo.
Set logging driver for all created containers.
-#### **--network**=*cni networks*
+#### **--network**=*networks*, **--net**
A comma-separated list of the names of CNI networks the pod should join.
diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md
index 6fd42f800..22eb07bf3 100644
--- a/docs/source/markdown/podman-pod-create.1.md
+++ b/docs/source/markdown/podman-pod-create.1.md
@@ -63,7 +63,7 @@ The image that will be created for the infra container. Default: "k8s.gcr.io/pau
Set a static IP for the pod's shared network.
-#### **-l**, **--label**=*label*
+#### **--label**=*label*, **-l**
Add metadata to a pod (e.g., --label com.example.key=value).
@@ -75,17 +75,17 @@ Read in a line delimited file of labels.
Set a static MAC address for the pod's shared network.
-#### **-n**, **--name**=*name*
+#### **--name**=*name*, **-n**
Assign a name to the pod.
-#### **--network**=*mode*
+#### **--network**=*mode*, **--net**
Set network mode for the pod. Supported values are
-- `bridge`: Create a network stack on the default bridge. This is the default for rootful containers.
-- `host`: Do not create a network namespace, all containers in the pod will use the host's network. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
+- **bridge**: Create a network stack on the default bridge. This is the default for rootful containers.
+- **host**: Do not create a network namespace, all containers in the pod will use the host's network. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
- Comma-separated list of the names of CNI networks the pod should join.
-- `slirp4netns[:OPTIONS,...]`: use slirp4netns to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options:
+- **slirp4netns[:OPTIONS,...]**: use slirp4netns to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options:
- **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false.
- **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`).
- **enable_ipv6=true|false**: Enable IPv6. Default is false. (Required for `outbound_addr6`).
@@ -108,7 +108,7 @@ Disable creation of /etc/hosts for the pod.
Write the pod ID to the file.
-#### **-p**, **--publish**=*port*
+#### **--publish**=*port*, **-p**
Publish a port or range of ports from the pod to the host.
diff --git a/docs/source/markdown/podman-pod-inspect.1.md b/docs/source/markdown/podman-pod-inspect.1.md
index ece3ab885..99cac6f9c 100644
--- a/docs/source/markdown/podman-pod-inspect.1.md
+++ b/docs/source/markdown/podman-pod-inspect.1.md
@@ -18,7 +18,7 @@ to run pods such as CRI-O, the last started pod could be from either of those me
The latest option is not supported on the remote client.
-#### **-f**, **--format**=*format*
+#### **--format**=*format*, **-f**
Change the default output format. This can be of a supported type like 'json'
or a Go template.
diff --git a/docs/source/markdown/podman-restart.1.md b/docs/source/markdown/podman-restart.1.md
index 60c90ada1..acca0ccfe 100644
--- a/docs/source/markdown/podman-restart.1.md
+++ b/docs/source/markdown/podman-restart.1.md
@@ -26,7 +26,7 @@ The latest option is not supported on the remote client.
#### **--running**
Restart all containers that are already in the *running* state.
-#### **-t**, **--time**=*time*
+#### **--time**=*time*, **-t**
Timeout to wait before forcibly stopping the container.
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index f67b7c652..db9c98073 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -380,7 +380,7 @@ value can be expressed in a time format such as **1m22s**. The default value is
Print usage statement
-#### **-h**, **--hostname**=*name*
+#### **--hostname**=*name*, **-h**
Container host name
@@ -600,7 +600,7 @@ to the container with **--name** then it will generate a random
string name. The name is useful any place you need to identify a container.
This works for both background and foreground containers.
-#### **--network**, **--net**=*mode*
+#### **--network**=*mode*, **--net**
Set the network mode for the container. Invalid if using **--dns**, **--dns-opt**, or **--dns-search** with **--network** that is set to **none** or **container:**_id_.
@@ -612,7 +612,7 @@ Valid _mode_ values are:
- **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-id_: connect to a user-defined network, multiple networks should be comma separated;
- **ns:**_path_: path to a network namespace to join;
-- `private`: create a new namespace for the container (default)
+- **private**: create a new namespace for the container (default)
- **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options:
- **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false.
- **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`).
diff --git a/docs/source/markdown/podman-system-connection-add.1.md b/docs/source/markdown/podman-system-connection-add.1.md
index c8c6476bf..32c16d6a1 100644
--- a/docs/source/markdown/podman-system-connection-add.1.md
+++ b/docs/source/markdown/podman-system-connection-add.1.md
@@ -15,7 +15,7 @@ The user will be prompted for the remote ssh login password or key file pass phr
## OPTIONS
-#### **-d**, **--default**=*false*
+#### **--default**=*false*, **-d**
Make the new destination the default for this user.
@@ -25,7 +25,7 @@ Path to ssh identity file. If the identity file has been encrypted, Podman promp
If no identity file is provided and no user is given, Podman defaults to the user running the podman command.
Podman prompts for the login password on the remote server.
-#### **-p**, **--port**=*port*
+#### **--port**=*port*, **-p**
Port for ssh destination. The default value is `22`.
diff --git a/docs/source/markdown/podman-system-df.1.md b/docs/source/markdown/podman-system-df.1.md
index 6c21a20b4..f3db2d020 100644
--- a/docs/source/markdown/podman-system-df.1.md
+++ b/docs/source/markdown/podman-system-df.1.md
@@ -14,7 +14,7 @@ Show podman disk usage
Pretty-print images using a Go template
-#### **-v**, **--verbose**[=*true|false*]
+#### **--verbose**, **-v**
Show detailed information on space usage
## EXAMPLE
diff --git a/docs/source/markdown/podman-volume-create.1.md b/docs/source/markdown/podman-volume-create.1.md
index 4fd911c1f..118f024df 100644
--- a/docs/source/markdown/podman-volume-create.1.md
+++ b/docs/source/markdown/podman-volume-create.1.md
@@ -23,11 +23,11 @@ Specify the volume driver name (default local).
Print usage statement
-#### **-l**, **--label**=*label*
+#### **--label**=*label*, **-l**
Set metadata for a volume (e.g., --label mykey=value).
-#### **-o**, **--opt**=*option*
+#### **--opt**=*option*, **-o**
Set driver specific options.
For the default driver, `local`, this allows a volume to be configured to mount a filesystem on the host.
diff --git a/docs/source/markdown/podman-volume-inspect.1.md b/docs/source/markdown/podman-volume-inspect.1.md
index e6be69e73..ea0ee91b4 100644
--- a/docs/source/markdown/podman-volume-inspect.1.md
+++ b/docs/source/markdown/podman-volume-inspect.1.md
@@ -16,11 +16,11 @@ Volumes can be queried individually by providing their full name or a unique par
## OPTIONS
-#### **-a**, **--all**
+#### **--all**, **-a**
Inspect all volumes.
-#### **-f**, **--format**=*format*
+#### **--format**=*format*, **-f**
Format volume output using Go template
diff --git a/docs/source/markdown/podman-volume-ls.1.md b/docs/source/markdown/podman-volume-ls.1.md
index a3f3ff9ba..98c3fae54 100644
--- a/docs/source/markdown/podman-volume-ls.1.md
+++ b/docs/source/markdown/podman-volume-ls.1.md
@@ -14,7 +14,7 @@ flag. Use the **--quiet** flag to print only the volume names.
## OPTIONS
-#### **-f**, **--filter**=*filter*
+#### **--filter**=*filter*, **-f**
Filter volume output.
@@ -26,7 +26,7 @@ Format volume output using Go template.
Print usage statement.
-#### **-q**, **--quiet**
+#### **--quiet**, **-q**
Print volume output in quiet mode. Only print the volume names.
diff --git a/docs/source/markdown/podman-volume-prune.1.md b/docs/source/markdown/podman-volume-prune.1.md
index 097dded86..b5f1b7e94 100644
--- a/docs/source/markdown/podman-volume-prune.1.md
+++ b/docs/source/markdown/podman-volume-prune.1.md
@@ -14,7 +14,7 @@ unused volumes. To bypass the confirmation, use the **--force** flag.
## OPTIONS
-#### **-f**, **--force**
+#### **--force**, **-f**
Do not prompt for confirmation.
diff --git a/docs/source/markdown/podman-volume-rm.1.md b/docs/source/markdown/podman-volume-rm.1.md
index 5f2137f73..c066d1c6e 100644
--- a/docs/source/markdown/podman-volume-rm.1.md
+++ b/docs/source/markdown/podman-volume-rm.1.md
@@ -15,11 +15,11 @@ Volumes can be removed individually by providing their full name or a unique par
## OPTIONS
-#### **-a**, **--all**
+#### **--all**, **-a**
Remove all volumes.
-#### **-f**, **--force**
+#### **--force**, **-f**
Remove a volume by force.
If it is being used by containers, the containers will be removed first.
diff --git a/go.mod b/go.mod
index 6be91ee6e..28e577a39 100644
--- a/go.mod
+++ b/go.mod
@@ -48,7 +48,7 @@ require (
github.com/opentracing/opentracing-go v1.2.0
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
- github.com/rootless-containers/rootlesskit v0.11.0
+ github.com/rootless-containers/rootlesskit v0.11.1
github.com/sirupsen/logrus v1.7.0
github.com/spf13/cobra v1.1.1
github.com/spf13/pflag v1.0.5
diff --git a/go.sum b/go.sum
index 6961e74ee..ab8ed4246 100644
--- a/go.sum
+++ b/go.sum
@@ -459,8 +459,8 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rootless-containers/rootlesskit v0.11.0 h1:Yn51y7RU6vE3oKrMv54qBCWDM3K6c5cmEtqc9j332D4=
-github.com/rootless-containers/rootlesskit v0.11.0/go.mod h1:pCUqFJBGOIonbjQBaxSVnk3w3KnK2drqjllgpgvNnO8=
+github.com/rootless-containers/rootlesskit v0.11.1 h1:c0ffwH/D7wU8+IphrvoLoQO3EQArMcdGxw40A5zkynk=
+github.com/rootless-containers/rootlesskit v0.11.1/go.mod h1:pCUqFJBGOIonbjQBaxSVnk3w3KnK2drqjllgpgvNnO8=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U=
diff --git a/hack/xref-helpmsgs-manpages b/hack/xref-helpmsgs-manpages
index 082cc63f2..c5447c5d6 100755
--- a/hack/xref-helpmsgs-manpages
+++ b/hack/xref-helpmsgs-manpages
@@ -331,8 +331,15 @@ sub podman_man {
@most_recent_flags = ();
# As of PR #8292, all options are <h4> and anchored
if ($line =~ s/^\#{4}\s+//) {
- # Handle any variation of '**--foo**, **-f**'
- while ($line =~ s/^\*\*((--[a-z0-9-]+)|(-.))\*\*(,\s+)?//g) {
+ # If option has long and short form, long must come first.
+ # This is a while-loop because there may be multiple long
+ # option names, e.g. --net/--network
+ while ($line =~ s/^\*\*(--[a-z0-9-]+)\*\*(=\*[a-zA-Z0-9-]+\*)?(,\s+)?//g) {
+ $man{$1} = 1;
+ push @most_recent_flags, $1;
+ }
+ # Short form
+ if ($line =~ s/^\*\*(-[a-zA-Z0-9])\*\*(=\*[a-zA-Z0-9-]+\*)?//g) {
$man{$1} = 1;
# Keep track of them, in case we see 'Not implemented' below
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index 0b9b353c7..be0adfe6a 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -94,7 +94,6 @@ func NewBoltState(path string, runtime *Runtime) (State, error) {
volBkt,
allVolsBkt,
execBkt,
- aliasesBkt,
runtimeConfigBkt,
}
@@ -972,6 +971,58 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
return ctrs, nil
}
+// GetNetworks returns the CNI networks this container is a part of.
+func (s *BoltState) GetNetworks(ctr *Container) ([]string, error) {
+ if !s.valid {
+ return nil, define.ErrDBClosed
+ }
+
+ if !ctr.valid {
+ return nil, define.ErrCtrRemoved
+ }
+
+ if s.namespace != "" && s.namespace != ctr.config.Namespace {
+ return nil, errors.Wrapf(define.ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
+ }
+
+ ctrID := []byte(ctr.ID())
+
+ db, err := s.getDBCon()
+ if err != nil {
+ return nil, err
+ }
+ defer s.deferredCloseDBCon(db)
+
+ networks := []string{}
+
+ err = db.View(func(tx *bolt.Tx) error {
+ ctrBucket, err := getCtrBucket(tx)
+ if err != nil {
+ return err
+ }
+
+ dbCtr := ctrBucket.Bucket(ctrID)
+ if dbCtr == nil {
+ ctr.valid = false
+ return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID())
+ }
+
+ ctrNetworkBkt := dbCtr.Bucket(networksBkt)
+ if ctrNetworkBkt == nil {
+ return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not joined to any CNI networks", ctr.ID())
+ }
+
+ return ctrNetworkBkt.ForEach(func(network, v []byte) error {
+ networks = append(networks, string(network))
+ return nil
+ })
+ })
+ if err != nil {
+ return nil, err
+ }
+ return networks, nil
+}
+
// GetNetworkAliases retrieves the network aliases for the given container in
// the given CNI network.
func (s *BoltState) GetNetworkAliases(ctr *Container, network string) ([]string, error) {
@@ -1032,7 +1083,8 @@ func (s *BoltState) GetNetworkAliases(ctr *Container, network string) ([]string,
netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network))
if netAliasesBkt == nil {
- return errors.Wrapf(define.ErrNoAliasesForNetwork, "container %s has no aliases for network %q", ctr.ID(), network)
+ // No aliases for this specific network.
+ return nil
}
return netAliasesBkt.ForEach(func(alias, v []byte) error {
@@ -1120,10 +1172,9 @@ func (s *BoltState) GetAllNetworkAliases(ctr *Container) (map[string][]string, e
return aliases, nil
}
-// SetNetworkAliases sets network aliases for the given container in the given
-// network. All existing aliases for that network (if any exist) will be removed,
-// to be replaced by the new aliases given.
-func (s *BoltState) SetNetworkAliases(ctr *Container, network string, aliases []string) error {
+// NetworkConnect adds the given container to the given network. If aliases are
+// specified, those will be added to the given network.
+func (s *BoltState) NetworkConnect(ctr *Container, network string, aliases []string) error {
if !s.valid {
return define.ErrDBClosed
}
@@ -1154,90 +1205,60 @@ func (s *BoltState) SetNetworkAliases(ctr *Container, network string, aliases []
return err
}
- allAliasesBucket, err := getAliasesBucket(tx)
- if err != nil {
- return err
- }
-
- netAllAliasesBucket, err := allAliasesBucket.CreateBucketIfNotExists([]byte(network))
- if err != nil {
- return errors.Wrapf(err, "error creating network aliases bucket for network %s", network)
- }
-
dbCtr := ctrBucket.Bucket(ctrID)
if dbCtr == nil {
ctr.valid = false
return errors.Wrapf(define.ErrNoSuchCtr, "container %s does not exist in database", ctr.ID())
}
- ctrAliasesBkt := dbCtr.Bucket(aliasesBkt)
- if ctrAliasesBkt == nil {
- return errors.Wrapf(define.ErrNoAliases, "container %s has no network aliases", ctr.ID())
+ ctrAliasesBkt, err := dbCtr.CreateBucketIfNotExists(aliasesBkt)
+ if err != nil {
+ return errors.Wrapf(err, "error creating aliases bucket for container %s", ctr.ID())
}
ctrNetworksBkt := dbCtr.Bucket(networksBkt)
if ctrNetworksBkt == nil {
- return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to any CNI networks, so cannot add aliases", ctr.ID())
+ ctrNetworksBkt, err = dbCtr.CreateBucket(networksBkt)
+ if err != nil {
+ return errors.Wrapf(err, "error creating networks bucket for container %s", ctr.ID())
+ }
+ ctrNetworks := ctr.config.Networks
+ if len(ctrNetworks) == 0 {
+ ctrNetworks = []string{ctr.runtime.netPlugin.GetDefaultNetworkName()}
+ }
+ // Copy in all the container's CNI networks
+ for _, net := range ctrNetworks {
+ if err := ctrNetworksBkt.Put([]byte(net), ctrID); err != nil {
+ return errors.Wrapf(err, "error adding container %s network %s to DB", ctr.ID(), net)
+ }
+ }
}
netConnected := ctrNetworksBkt.Get([]byte(network))
- if netConnected == nil {
- return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to CNI network %q, so cannot add aliases for this network", ctr.ID(), network)
- }
-
- namesBucket, err := getNamesBucket(tx)
- if err != nil {
- return err
+ if netConnected != nil {
+ return errors.Wrapf(define.ErrNetworkExists, "container %s is already connected to CNI network %q", ctr.ID(), network)
}
- // Check if the container already has network aliases for this network.
- netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network))
- if netAliasesBkt != nil {
- // We have aliases. Have to remove them.
- forEachErr := netAliasesBkt.ForEach(func(alias, v []byte) error {
- // Relies on errors.Wrapf(nil, ...) returning
- // nil.
- return errors.Wrapf(netAllAliasesBucket.Delete(alias), "error removing alias %q from network %q when changing aliases for container %s", string(alias), network, ctr.ID())
- })
- if forEachErr != nil {
- return forEachErr
- }
+ // Add the network
+ if err := ctrNetworksBkt.Put([]byte(network), ctrID); err != nil {
+ return errors.Wrapf(err, "error adding container %s to network %s in DB", ctr.ID(), network)
}
- if netAliasesBkt == nil {
- newBkt, err := ctrAliasesBkt.CreateBucket([]byte(network))
- if err != nil {
- return errors.Wrapf(err, "could not create bucket for network aliases for network %q", network)
- }
- netAliasesBkt = newBkt
+ ctrNetAliasesBkt, err := ctrAliasesBkt.CreateBucketIfNotExists([]byte(network))
+ if err != nil {
+ return errors.Wrapf(err, "error adding container %s network aliases bucket for network %s", ctr.ID(), network)
}
-
for _, alias := range aliases {
- // Check if safe to use
- aliasExists := netAllAliasesBucket.Get([]byte(alias))
- if aliasExists != nil {
- return errors.Wrapf(define.ErrAliasExists, "network alias %q already exists in network %q (used by container %s)", alias, network, string(aliasExists))
- }
- nameExists := namesBucket.Get([]byte(alias))
- if nameExists != nil {
- return errors.Wrapf(define.ErrCtrExists, "a container or pod already uses the name %q, cannot add network alias for container %s", alias, ctr.ID())
- }
-
- // Add alias
- if err := netAliasesBkt.Put([]byte(alias), ctrID); err != nil {
- return errors.Wrapf(err, "error adding container %s network %q alias %q to DB", ctr.ID(), network, alias)
- }
- if err := netAllAliasesBucket.Put([]byte(alias), ctrID); err != nil {
- return errors.Wrapf(err, "error adding container %s network %q alias %q to all aliases in DB", ctr.ID(), network, alias)
+ if err := ctrNetAliasesBkt.Put([]byte(alias), ctrID); err != nil {
+ return errors.Wrapf(err, "error adding container %s network alias %s for network %s", ctr.ID(), alias, network)
}
}
-
return nil
})
}
-// RemoveNetworkAliases removes network aliases of the given container in the
-// given network.
-func (s *BoltState) RemoveNetworkAliases(ctr *Container, network string) error {
+// NetworkDisconnect disconnects the container from the given network, also
+// removing any aliases in the network.
+func (s *BoltState) NetworkDisconnect(ctr *Container, network string) error {
if !s.valid {
return define.ErrDBClosed
}
@@ -1268,16 +1289,6 @@ func (s *BoltState) RemoveNetworkAliases(ctr *Container, network string) error {
return err
}
- allAliasesBucket, err := getAliasesBucket(tx)
- if err != nil {
- return err
- }
-
- netAllAliasesBucket, err := allAliasesBucket.CreateBucketIfNotExists([]byte(network))
- if err != nil {
- return errors.Wrapf(err, "error creating network aliases bucket for network %s", network)
- }
-
dbCtr := ctrBucket.Bucket(ctrID)
if dbCtr == nil {
ctr.valid = false
@@ -1291,141 +1302,27 @@ func (s *BoltState) RemoveNetworkAliases(ctr *Container, network string) error {
ctrNetworksBkt := dbCtr.Bucket(networksBkt)
if ctrNetworksBkt == nil {
- return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to any CNI networks, so cannot add aliases", ctr.ID())
+ return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not connected to any CNI networks, so cannot disconnect", ctr.ID())
}
netConnected := ctrNetworksBkt.Get([]byte(network))
if netConnected == nil {
- return errors.Wrapf(define.ErrInvalidArg, "container %s is not connected to CNI network %q, so cannot add aliases for this network", ctr.ID(), network)
- }
-
- // Check if the container already has network aliases for this network.
- netAliasesBkt := ctrAliasesBkt.Bucket([]byte(network))
- if netAliasesBkt != nil {
- // We have aliases. Remove them.
- forEachErr := netAliasesBkt.ForEach(func(alias, v []byte) error {
- // Relies on errors.Wrapf(nil, ...) returning
- // nil.
- return errors.Wrapf(netAllAliasesBucket.Delete(alias), "error removing alias %q from network %q when changing aliases for container %s", string(alias), network, ctr.ID())
- })
- if forEachErr != nil {
- return forEachErr
- }
+ return errors.Wrapf(define.ErrNoSuchNetwork, "container %s is not connected to CNI network %q", ctr.ID(), network)
}
- return nil
- })
-}
-
-// Get all network aliases for a single CNI network. Returns a map of alias to
-// container ID.
-func (s *BoltState) GetAllAliasesForNetwork(network string) (map[string]string, error) {
- if !s.valid {
- return nil, define.ErrDBClosed
- }
-
- if network == "" {
- return nil, errors.Wrapf(define.ErrInvalidArg, "network name must not be empty")
- }
-
- db, err := s.getDBCon()
- if err != nil {
- return nil, err
- }
- defer s.deferredCloseDBCon(db)
-
- aliases := make(map[string]string)
-
- err = db.View(func(tx *bolt.Tx) error {
- aliasBucket, err := getAliasesBucket(tx)
- if err != nil {
- return err
+ if err := ctrNetworksBkt.Delete([]byte(network)); err != nil {
+ return errors.Wrapf(err, "error removing container %s from network %s", ctr.ID(), network)
}
- dbAlias := aliasBucket.Bucket([]byte(network))
- if dbAlias == nil {
- // We can't tell if the network exists, or doesn't exist
- // So... Assume it exists, but has no aliases.
+ bktExists := ctrAliasesBkt.Bucket([]byte(network))
+ if bktExists == nil {
return nil
}
- return dbAlias.ForEach(func(alias, ctrId []byte) error {
- aliases[string(alias)] = string(ctrId)
- return nil
- })
- })
- if err != nil {
- return nil, err
- }
-
- return aliases, nil
-}
-
-// RemoveAllAliasesForNetwork removes all the aliases in a given CNI network, as
-// part of that network being removed.
-func (s *BoltState) RemoveAllAliasesForNetwork(network string) error {
- if !s.valid {
- return define.ErrDBClosed
- }
-
- if network == "" {
- return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
- }
-
- db, err := s.getDBCon()
- if err != nil {
- return err
- }
- defer s.deferredCloseDBCon(db)
-
- return db.Update(func(tx *bolt.Tx) error {
- allCtrsBucket, err := getAllCtrsBucket(tx)
- if err != nil {
- return err
+ if err := ctrAliasesBkt.DeleteBucket([]byte(network)); err != nil {
+ return errors.Wrapf(err, "error removing container %s network aliases for network %s", ctr.ID(), network)
}
- ctrBucket, err := getCtrBucket(tx)
- if err != nil {
- return err
- }
-
- allAliasesBucket, err := getAliasesBucket(tx)
- if err != nil {
- return err
- }
-
- checkAliasesBucketExists := allAliasesBucket.Bucket([]byte(network))
- if checkAliasesBucketExists != nil {
- if err := allAliasesBucket.DeleteBucket([]byte(network)); err != nil {
- return errors.Wrapf(err, "error removing network %s aliases bucket from DB", network)
- }
- }
-
- // Iterate through all containers and remove their aliases
- // bucket for the network.
- return allCtrsBucket.ForEach(func(ctrID, ctrName []byte) error {
- dbCtr := ctrBucket.Bucket(ctrID)
- if dbCtr == nil {
- // DB State is inconsistent... but we can't do
- // anything about it.
- // Log and move on.
- logrus.Errorf("Container %s listed in all containers, but has no bucket!", string(ctrID))
- return nil
- }
-
- dbCtrAliases := dbCtr.Bucket(aliasesBkt)
- if dbCtrAliases == nil {
- // Container has no aliases, this is OK.
- return nil
- }
-
- ctrNetAliases := dbCtrAliases.Bucket([]byte(network))
- if ctrNetAliases != nil {
- if err := dbCtrAliases.DeleteBucket([]byte(network)); err != nil {
- return errors.Wrapf(err, "error removing bucket for network aliases for network %s from container %s", network, string(ctrID))
- }
- }
- return nil
- })
+ return nil
})
}
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index a48de3092..c06fedd3e 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -354,14 +354,6 @@ func getExecBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
return bkt, nil
}
-func getAliasesBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
- bkt := tx.Bucket(aliasesBkt)
- if bkt == nil {
- return nil, errors.Wrapf(define.ErrDBBadConfig, "aliases bucket not found in DB")
- }
- return bkt, nil
-}
-
func getRuntimeConfigBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
bkt := tx.Bucket(runtimeConfigBkt)
if bkt == nil {
@@ -584,11 +576,6 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
return err
}
- allAliasesBkt, err := getAliasesBucket(tx)
- if err != nil {
- return err
- }
-
// If a pod was given, check if it exists
var podDB *bolt.Bucket
var podCtrs *bolt.Bucket
@@ -635,41 +622,20 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
return errors.Wrapf(err, "name \"%s\" is in use", ctr.Name())
}
+ allNets := make(map[string]bool)
+
// Check that we don't have any empty network names
for _, net := range ctr.config.Networks {
if net == "" {
return errors.Wrapf(define.ErrInvalidArg, "network names cannot be an empty string")
}
+ allNets[net] = true
}
- // If we have network aliases, check if they are already in use.
- for net, aliases := range ctr.config.NetworkAliases {
- // Aliases cannot conflict with container names.
- for _, alias := range aliases {
- aliasExist := namesBucket.Get([]byte(alias))
- if aliasExist != nil {
- return errors.Wrapf(define.ErrCtrExists, "alias %q conflicts with existing container/pod name", alias)
- }
- }
-
- netAliasesBkt := allAliasesBkt.Bucket([]byte(net))
- if netAliasesBkt != nil {
- for _, alias := range aliases {
- aliasExist := netAliasesBkt.Get([]byte(alias))
- if aliasExist != nil {
- return errors.Wrapf(define.ErrAliasExists, "network alias %q already exists for network %q", net, alias)
- }
- }
- }
- hasNet := false
- for _, testNet := range ctr.config.Networks {
- if testNet == net {
- hasNet = true
- break
- }
- }
- if !hasNet {
- return errors.Wrapf(define.ErrInvalidArg, "container %s has network aliases for network %q but is not part of that network", ctr.ID(), net)
+ // Each network we have aliases for, must exist in networks
+ for net := range ctr.config.NetworkAliases {
+ if !allNets[net] {
+ return errors.Wrapf(define.ErrNoSuchNetwork, "container %s has network aliases for network %q but is not part of that network", ctr.ID(), net)
}
}
@@ -690,63 +656,6 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error {
return errors.Wrapf(err, "error adding container %s to all containers bucket in DB", ctr.ID())
}
- // Check aliases for all networks, remove conflicts with the
- // container name.
- for _, net := range ctr.config.Networks {
- netAliasesBkt := allAliasesBkt.Bucket([]byte(net))
- if netAliasesBkt == nil {
- continue
- }
-
- otherCtrID := netAliasesBkt.Get(ctrName)
- if otherCtrID == nil {
- continue
- }
-
- if err := netAliasesBkt.Delete(ctrName); err != nil {
- return errors.Wrapf(err, "error removing container %s name from network aliases for network %s", ctr.ID(), net)
- }
-
- // We now need to remove from the other container.
- // To do this, we work through the container bucket,
- // then its aliases bucket, then its aliases for this
- // specific network, then we remove the alias.
- // Just slightly ridiculous. Just slightly.
- otherCtr := ctrBucket.Bucket(otherCtrID)
- if otherCtr == nil {
- // The state is inconsistent, but we can't do
- // much...
- logrus.Errorf("Container %s referred to by network alias but not present in state", string(otherCtrID))
- continue
- }
- otherCtrAliases := otherCtr.Bucket(aliasesBkt)
- if otherCtrAliases == nil {
- logrus.Errorf("Container %s is missing aliases but but has an alias", string(otherCtrID))
- continue
- }
- otherCtrNetworkAliases := otherCtrAliases.Bucket([]byte(net))
- if otherCtrNetworkAliases == nil {
- logrus.Errorf("Container %s is missing network aliases bucket for network %s but has alias in that network", string(otherCtrID), net)
- }
- if otherCtrNetworkAliases.Get(ctrName) != nil {
- if err := otherCtrNetworkAliases.Delete(ctrName); err != nil {
- return errors.Wrapf(err, "error removing container %s name from network %s aliases of container %s", ctr.Name(), net, string(otherCtrID))
- }
- }
- }
-
- for net, aliases := range ctr.config.NetworkAliases {
- netAliasesBkt, err := allAliasesBkt.CreateBucketIfNotExists([]byte(net))
- if err != nil {
- return errors.Wrapf(err, "error creating network aliases bucket for network %q", net)
- }
- for _, alias := range aliases {
- if err := netAliasesBkt.Put([]byte(alias), ctrID); err != nil {
- return errors.Wrapf(err, "error adding container %s network alias %q to network %q", ctr.ID(), alias, net)
- }
- }
- }
-
newCtrBkt, err := ctrBucket.CreateBucket(ctrID)
if err != nil {
return errors.Wrapf(err, "error adding container %s bucket to DB", ctr.ID())
@@ -998,49 +907,6 @@ func (s *BoltState) removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error
return errors.Wrapf(define.ErrCtrExists, "container %s is a dependency of the following containers: %s", ctr.ID(), strings.Join(deps, ", "))
}
- // Does the container have any network aliases?
- ctrNetAliasesBkt := ctrExists.Bucket(aliasesBkt)
- if ctrNetAliasesBkt != nil {
- allAliasesBkt, err := getAliasesBucket(tx)
- if err != nil {
- return err
- }
- ctrNetworksBkt := ctrExists.Bucket(networksBkt)
- // Internal state mismatch if this doesn't exist - we'll just
- // assume there are no aliases in that case.
- if ctrNetworksBkt != nil {
- // This is a little gross. Iterate through all networks
- // the container is joined to. Check if we have aliases
- // for them. If we do have such aliases, remove all of
- // then from the global aliases table for that network.
- err = ctrNetworksBkt.ForEach(func(network, v []byte) error {
- netAliasesBkt := ctrNetAliasesBkt.Bucket(network)
- if netAliasesBkt == nil {
- return nil
- }
- netAllAliasesBkt := allAliasesBkt.Bucket(network)
- if netAllAliasesBkt == nil {
- // Again the state is inconsistent here,
- // but the best we can do is try and
- // recover by ignoring it.
- return nil
- }
- return netAliasesBkt.ForEach(func(alias, v []byte) error {
- // We don't want to hard-fail on a
- // missing alias, so continue if we hit
- // errors.
- if err := netAllAliasesBkt.Delete(alias); err != nil {
- logrus.Errorf("Error removing alias %q from network %q when removing container %s", string(alias), string(network), ctr.ID())
- }
- return nil
- })
- })
- if err != nil {
- return err
- }
- }
- }
-
if err := ctrBucket.DeleteBucket(ctrID); err != nil {
return errors.Wrapf(define.ErrInternal, "error deleting container %s from DB", ctr.ID())
}
diff --git a/libpod/container.go b/libpod/container.go
index ea5a6e09c..580fa7b3d 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -1085,3 +1085,31 @@ func (c *Container) Timezone() string {
func (c *Container) Umask() string {
return c.config.Umask
}
+
+// Networks gets all the networks this container is connected to.
+// Please do NOT use ctr.config.Networks, as this can be changed from those
+// values at runtime via network connect and disconnect.
+// If the container is configured to use CNI and this function returns an empty
+// array, the container will still be connected to the default network.
+func (c *Container) Networks() ([]string, error) {
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return nil, err
+ }
+ }
+
+ return c.networks()
+}
+
+// Unlocked accessor for networks
+func (c *Container) networks() ([]string, error) {
+ networks, err := c.runtime.state.GetNetworks(c)
+ if err != nil && errors.Cause(err) == define.ErrNoSuchNetwork {
+ return c.config.Networks, nil
+ }
+
+ return networks, err
+}
diff --git a/libpod/container_config.go b/libpod/container_config.go
index d73fbb42f..cc3ad25ea 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -236,6 +236,9 @@ type ContainerNetworkConfig struct {
// Will be appended to host's host file
HostAdd []string `json:"hostsAdd,omitempty"`
// Network names (CNI) to add container to. Empty to use default network.
+ // Please note that these can be altered at runtime. The actual list is
+ // stored in the DB and should be retrieved from there; this is only the
+ // set of networks the container was *created* with.
Networks []string `json:"networks,omitempty"`
// Network mode specified for the default network.
NetMode namespaces.NetworkMode `json:"networkMode,omitempty"`
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 0aeaae43d..108954bad 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -641,12 +641,17 @@ func (c *Container) removeIPv4Allocations() error {
cniDefaultNetwork = c.runtime.netPlugin.GetDefaultNetworkName()
}
+ networks, err := c.networks()
+ if err != nil {
+ return err
+ }
+
switch {
- case len(c.config.Networks) > 0 && len(c.config.Networks) != len(c.state.NetworkStatus):
- return errors.Wrapf(define.ErrInternal, "network mismatch: asked to join %d CNI networks but got %d CNI results", len(c.config.Networks), len(c.state.NetworkStatus))
- case len(c.config.Networks) == 0 && len(c.state.NetworkStatus) != 1:
+ case len(networks) > 0 && len(networks) != len(c.state.NetworkStatus):
+ return errors.Wrapf(define.ErrInternal, "network mismatch: asked to join %d CNI networks but got %d CNI results", len(networks), len(c.state.NetworkStatus))
+ case len(networks) == 0 && len(c.state.NetworkStatus) != 1:
return errors.Wrapf(define.ErrInternal, "network mismatch: did not specify CNI networks but joined more than one (%d)", len(c.state.NetworkStatus))
- case len(c.config.Networks) == 0 && cniDefaultNetwork == "":
+ case len(networks) == 0 && cniDefaultNetwork == "":
return errors.Wrapf(define.ErrInternal, "could not retrieve name of CNI default network")
}
@@ -656,11 +661,11 @@ func (c *Container) removeIPv4Allocations() error {
continue
}
candidate := ""
- if len(c.config.Networks) > 0 {
+ if len(networks) > 0 {
// CNI returns networks in order we passed them.
// So our index into results should be our index
// into networks.
- candidate = filepath.Join(cniNetworksDir, c.config.Networks[index], ctrIP.Address.IP.String())
+ candidate = filepath.Join(cniNetworksDir, networks[index], ctrIP.Address.IP.String())
} else {
candidate = filepath.Join(cniNetworksDir, cniDefaultNetwork, ctrIP.Address.IP.String())
}
@@ -1503,6 +1508,7 @@ func (c *Container) mountStorage() (_ string, deferredErr error) {
// config.
// Returns the volume that was mounted.
func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string) (*Volume, error) {
+ logrus.Debugf("Going to mount named volume %s", v.Name)
vol, err := c.runtime.state.Volume(v.Name)
if err != nil {
return nil, errors.Wrapf(err, "error retrieving named volume %s for container %s", v.Name, c.ID())
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 38b3a6686..775965477 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -575,6 +575,8 @@ type InspectAdditionalNetwork struct {
// Links is presently unused and maintained exclusively for
// compatibility.
Links []string `json:"Links"`
+ // Aliases are any network aliases the container has in this network.
+ Aliases []string `json:"Aliases,omitempty"`
}
// InspectNetworkSettings holds information about the network settings of the
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index 27c5febf4..471827b7c 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -33,9 +33,6 @@ var (
// ErrNoAliases indicates that the container does not have any network
// aliases.
ErrNoAliases = errors.New("no aliases for container")
- // ErrNoAliasesForNetwork indicates that the container has no aliases
- // for a specific network.
- ErrNoAliasesForNetwork = errors.New("no aliases for network")
// ErrCtrExists indicates a container with the same name or ID already
// exists
@@ -49,9 +46,9 @@ var (
// ErrExecSessionExists indicates an exec session with the same ID
// already exists.
ErrExecSessionExists = errors.New("exec session already exists")
- // ErrAliasExists indicates that a network alias with the same name
- // already exists in the network.
- ErrAliasExists = errors.New("alias already exists")
+ // ErrNetworkExists indicates that a network with the given name already
+ // exists.
+ ErrNetworkExists = errors.New("network already exists")
// ErrCtrStateInvalid indicates a container is in an improper state for
// the requested operation
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go
index ba4c70c6b..6c0cde531 100644
--- a/libpod/in_memory_state.go
+++ b/libpod/in_memory_state.go
@@ -31,8 +31,7 @@ type InMemoryState struct {
ctrExecSessions map[string][]string
// Maps pod ID to a map of container ID to container struct.
podContainers map[string]map[string]*Container
- // Maps network name to alias to container ID
- networkAliases map[string]map[string]string
+ ctrNetworks map[string][]string
// Maps container ID to network name to list of aliases.
ctrNetworkAliases map[string]map[string][]string
// Global name registry - ensures name uniqueness and performs lookups.
@@ -69,7 +68,7 @@ func NewInMemoryState() (State, error) {
state.podContainers = make(map[string]map[string]*Container)
- state.networkAliases = make(map[string]map[string]string)
+ state.ctrNetworks = make(map[string][]string)
state.ctrNetworkAliases = make(map[string]map[string][]string)
state.nameIndex = registrar.NewRegistrar()
@@ -293,7 +292,7 @@ func (s *InMemoryState) AddContainer(ctr *Container) error {
}
// Check network aliases
- for network, aliases := range ctr.config.NetworkAliases {
+ for network := range ctr.config.NetworkAliases {
inNet := false
for _, net := range ctr.config.Networks {
if net == network {
@@ -304,19 +303,6 @@ func (s *InMemoryState) AddContainer(ctr *Container) error {
if !inNet {
return errors.Wrapf(define.ErrInvalidArg, "container %s has network aliases for network %q but is not joined to network", ctr.ID(), network)
}
-
- allNetAliases, ok := s.networkAliases[network]
- if ok {
- for _, alias := range aliases {
- // Check if alias is a name
- if _, err := s.nameIndex.Get(alias); err == nil {
- return define.ErrInvalidArg
- }
- if _, ok := allNetAliases[alias]; ok {
- return define.ErrAliasExists
- }
- }
- }
}
// There are potential race conditions with this
@@ -375,46 +361,17 @@ func (s *InMemoryState) AddContainer(ctr *Container) error {
s.addCtrToVolDependsMap(ctr.ID(), vol.Name)
}
- for _, network := range ctr.config.Networks {
- allNetAliases, ok := s.networkAliases[network]
- if !ok {
- continue
- }
- otherCtrID, ok := allNetAliases[ctr.Name()]
- if !ok {
- continue
- }
- delete(allNetAliases, ctr.Name())
-
- otherCtrAliases, ok := s.ctrNetworkAliases[otherCtrID]
- if !ok {
- continue
- }
- otherCtrNetAliases, ok := otherCtrAliases[network]
- if !ok {
- continue
- }
- newAliases := []string{}
- for _, alias := range otherCtrNetAliases {
- if alias != ctr.Name() {
- newAliases = append(newAliases, alias)
- }
+ // Add networks
+ newNets := make([]string, 0, len(ctr.config.Networks))
+ for _, net := range ctr.config.Networks {
+ if net == "" {
+ return define.ErrInvalidArg
}
- otherCtrAliases[network] = newAliases
+ newNets = append(newNets, net)
}
+ s.ctrNetworks[ctr.ID()] = newNets
// Add network aliases
- for network, aliases := range ctr.config.NetworkAliases {
- allNetAliases, ok := s.networkAliases[network]
- if !ok {
- allNetAliases = make(map[string]string)
- s.networkAliases[network] = allNetAliases
- }
-
- for _, alias := range aliases {
- allNetAliases[alias] = ctr.ID()
- }
- }
s.ctrNetworkAliases[ctr.ID()] = ctr.config.NetworkAliases
return nil
@@ -480,18 +437,12 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error {
}
// Remove our network aliases
- ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()]
- if ok {
- for network, aliases := range ctrAliases {
- netAliases, ok := s.networkAliases[network]
- if ok {
- for _, alias := range aliases {
- delete(netAliases, alias)
- }
- }
- }
+ if _, ok := s.ctrNetworkAliases[ctr.ID()]; ok {
delete(s.ctrNetworkAliases, ctr.ID())
}
+ if _, ok := s.ctrNetworks[ctr.ID()]; ok {
+ delete(s.ctrNetworks, ctr.ID())
+ }
return nil
}
@@ -569,6 +520,26 @@ func (s *InMemoryState) AllContainers() ([]*Container, error) {
return ctrs, nil
}
+// Get all networks this container is present in.
+func (s *InMemoryState) GetNetworks(ctr *Container) ([]string, error) {
+ if !ctr.valid {
+ return nil, define.ErrCtrRemoved
+ }
+
+ ctr, ok := s.containers[ctr.ID()]
+ if !ok {
+ ctr.valid = false
+ return nil, define.ErrNoSuchCtr
+ }
+
+ ctrNetworks, ok := s.ctrNetworks[ctr.ID()]
+ if !ok {
+ return nil, define.ErrNoSuchNetwork
+ }
+
+ return ctrNetworks, nil
+}
+
// GetNetworkAliases returns network aliases for the given container in the
// given network.
func (s *InMemoryState) GetNetworkAliases(ctr *Container, network string) ([]string, error) {
@@ -582,6 +553,7 @@ func (s *InMemoryState) GetNetworkAliases(ctr *Container, network string) ([]str
ctr, ok := s.containers[ctr.ID()]
if !ok {
+ ctr.valid = false
return nil, define.ErrNoSuchCtr
}
@@ -615,6 +587,7 @@ func (s *InMemoryState) GetAllNetworkAliases(ctr *Container) (map[string][]strin
ctr, ok := s.containers[ctr.ID()]
if !ok {
+ ctr.valid = false
return nil, define.ErrNoSuchCtr
}
@@ -626,9 +599,8 @@ func (s *InMemoryState) GetAllNetworkAliases(ctr *Container) (map[string][]strin
return ctrAliases, nil
}
-// SetNetworkAliases sets network aliases for the given container in the given
-// network.
-func (s *InMemoryState) SetNetworkAliases(ctr *Container, network string, aliases []string) error {
+// NetworkConnect connects to the given network
+func (s *InMemoryState) NetworkConnect(ctr *Container, network string, aliases []string) error {
if !ctr.valid {
return define.ErrCtrRemoved
}
@@ -639,55 +611,37 @@ func (s *InMemoryState) SetNetworkAliases(ctr *Container, network string, aliase
ctr, ok := s.containers[ctr.ID()]
if !ok {
+ ctr.valid = false
return define.ErrNoSuchCtr
}
inNet := false
- for _, net := range ctr.config.Networks {
+ ctrNetworks, ok := s.ctrNetworks[ctr.ID()]
+ if !ok {
+ return define.ErrNoSuchNetwork
+ }
+ for _, net := range ctrNetworks {
if net == network {
inNet = true
}
}
- if !inNet {
- return define.ErrInvalidArg
+ if inNet {
+ return define.ErrNoSuchNetwork
}
+ s.ctrNetworks[ctr.ID()] = append(ctrNetworks, network)
ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()]
if !ok {
ctrAliases = make(map[string][]string)
s.ctrNetworkAliases[ctr.ID()] = ctrAliases
}
- netAliases, ok := ctrAliases[network]
- if !ok {
- netAliases = []string{}
- ctrAliases[network] = netAliases
- }
-
- allAliases, ok := s.networkAliases[network]
- if !ok {
- allAliases = make(map[string]string)
- s.networkAliases[network] = allAliases
- }
-
- for _, alias := range netAliases {
- delete(allAliases, alias)
- }
-
- for _, newAlias := range aliases {
- if _, ok := allAliases[newAlias]; ok {
- return define.ErrAliasExists
- }
- allAliases[newAlias] = ctr.ID()
- }
-
ctrAliases[network] = aliases
return nil
}
-// RemoveNetworkAliases removes network aliases from the given container in the
-// given network.
-func (s *InMemoryState) RemoveNetworkAliases(ctr *Container, network string) error {
+// Disconnect from the given network and remove all aliases in it.
+func (s *InMemoryState) NetworkDisconnect(ctr *Container, network string) error {
if !ctr.valid {
return define.ErrCtrRemoved
}
@@ -698,73 +652,36 @@ func (s *InMemoryState) RemoveNetworkAliases(ctr *Container, network string) err
ctr, ok := s.containers[ctr.ID()]
if !ok {
+ ctr.valid = false
return define.ErrNoSuchCtr
}
+ ctrNetworks, ok := s.ctrNetworks[ctr.ID()]
+ if !ok {
+ return define.ErrNoSuchNetwork
+ }
inNet := false
- for _, net := range ctr.config.Networks {
+ remainingNets := make([]string, 0, len(ctrNetworks))
+ for _, net := range ctrNetworks {
if net == network {
inNet = true
+ break
+ } else {
+ remainingNets = append(remainingNets, net)
}
}
if !inNet {
- return define.ErrInvalidArg
+ return define.ErrNoSuchNetwork
}
+ s.ctrNetworks[ctr.ID()] = remainingNets
ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()]
if !ok {
ctrAliases = make(map[string][]string)
s.ctrNetworkAliases[ctr.ID()] = ctrAliases
}
- netAliases, ok := ctrAliases[network]
- if !ok {
- netAliases = []string{}
- ctrAliases[network] = netAliases
- }
-
- allAliases, ok := s.networkAliases[network]
- if !ok {
- allAliases = make(map[string]string)
- s.networkAliases[network] = allAliases
- }
-
- for _, alias := range netAliases {
- delete(allAliases, alias)
- }
-
- return nil
-}
-
-// GetAllAliasesForNetwork gets all the aliases for a single network.
-func (s *InMemoryState) GetAllAliasesForNetwork(network string) (map[string]string, error) {
- if network == "" {
- return nil, errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
- }
-
- allAliases, ok := s.networkAliases[network]
- if !ok {
- // Can't tell if the network exists.
- // Assume it does.
- return map[string]string{}, nil
- }
-
- return allAliases, nil
-}
-
-// RemoveAllAliasesForNetwork removes all the aliases for a given network.
-func (s *InMemoryState) RemoveAllAliasesForNetwork(network string) error {
- if network == "" {
- return errors.Wrapf(define.ErrInvalidArg, "network names must not be empty")
- }
-
- if _, ok := s.networkAliases[network]; ok {
- delete(s.networkAliases, network)
- }
-
- for _, ctrAliases := range s.ctrNetworkAliases {
- if _, ok := ctrAliases[network]; ok {
- delete(ctrAliases, network)
- }
+ if _, ok := ctrAliases[network]; ok {
+ delete(ctrAliases, network)
}
return nil
@@ -1422,7 +1339,7 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
}
// Check network aliases
- for network, aliases := range ctr.config.NetworkAliases {
+ for network := range ctr.config.NetworkAliases {
inNet := false
for _, net := range ctr.config.Networks {
if net == network {
@@ -1433,19 +1350,6 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
if !inNet {
return errors.Wrapf(define.ErrInvalidArg, "container %s has network aliases for network %q but is not joined to network", ctr.ID(), network)
}
-
- allNetAliases, ok := s.networkAliases[network]
- if ok {
- for _, alias := range aliases {
- // Check if alias is a name
- if _, err := s.nameIndex.Get(alias); err == nil {
- return define.ErrInvalidArg
- }
- if _, ok := allNetAliases[alias]; ok {
- return define.ErrAliasExists
- }
- }
- }
}
// Retrieve pod containers list
@@ -1525,46 +1429,17 @@ func (s *InMemoryState) AddContainerToPod(pod *Pod, ctr *Container) error {
s.addCtrToVolDependsMap(ctr.ID(), vol.Name)
}
- for _, network := range ctr.config.Networks {
- allNetAliases, ok := s.networkAliases[network]
- if !ok {
- continue
- }
- otherCtrID, ok := allNetAliases[ctr.Name()]
- if !ok {
- continue
- }
- delete(allNetAliases, ctr.Name())
-
- otherCtrAliases, ok := s.ctrNetworkAliases[otherCtrID]
- if !ok {
- continue
- }
- otherCtrNetAliases, ok := otherCtrAliases[network]
- if !ok {
- continue
- }
- newAliases := []string{}
- for _, alias := range otherCtrNetAliases {
- if alias != ctr.Name() {
- newAliases = append(newAliases, alias)
- }
+ // Add networks
+ newNets := make([]string, 0, len(ctr.config.Networks))
+ for _, net := range ctr.config.Networks {
+ if net == "" {
+ return define.ErrInvalidArg
}
- otherCtrAliases[network] = newAliases
+ newNets = append(newNets, net)
}
+ s.ctrNetworks[ctr.ID()] = newNets
// Add network aliases
- for network, aliases := range ctr.config.NetworkAliases {
- allNetAliases, ok := s.networkAliases[network]
- if !ok {
- allNetAliases = make(map[string]string)
- s.networkAliases[network] = allNetAliases
- }
-
- for _, alias := range aliases {
- allNetAliases[alias] = ctr.ID()
- }
- }
s.ctrNetworkAliases[ctr.ID()] = ctr.config.NetworkAliases
return nil
@@ -1648,18 +1523,12 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
}
// Remove our network aliases
- ctrAliases, ok := s.ctrNetworkAliases[ctr.ID()]
- if ok {
- for network, aliases := range ctrAliases {
- netAliases, ok := s.networkAliases[network]
- if ok {
- for _, alias := range aliases {
- delete(netAliases, alias)
- }
- }
- }
+ if _, ok := s.ctrNetworkAliases[ctr.ID()]; ok {
delete(s.ctrNetworkAliases, ctr.ID())
}
+ if _, ok := s.ctrNetworks[ctr.ID()]; ok {
+ delete(s.ctrNetworks, ctr.ID())
+ }
return nil
}
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 5aed630de..fed90cfc3 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -104,7 +104,11 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Re
podName := getCNIPodName(ctr)
- podNetwork := r.getPodNetwork(ctr.ID(), podName, ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP, requestedMAC)
+ networks, err := ctr.networks()
+ if err != nil {
+ return nil, err
+ }
+ podNetwork := r.getPodNetwork(ctr.ID(), podName, ctrNS.Path(), networks, ctr.config.PortMappings, requestedIP, requestedMAC)
aliases, err := ctr.runtime.state.GetAllNetworkAliases(ctr)
if err != nil {
return nil, err
@@ -209,7 +213,11 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
if ctr.config.NetMode.IsSlirp4netns() {
return r.setupSlirp4netns(ctr)
}
- if len(ctr.config.Networks) > 0 {
+ networks, err := ctr.networks()
+ if err != nil {
+ return err
+ }
+ if len(networks) > 0 {
// set up port forwarder for CNI-in-slirp4netns
netnsPath := ctr.state.NetNS.Path()
// TODO: support slirp4netns port forwarder as well
@@ -725,6 +733,11 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID())
+ networks, err := ctr.networks()
+ if err != nil {
+ return err
+ }
+
// rootless containers do not use the CNI plugin directly
if !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() {
var requestedIP net.IP
@@ -745,7 +758,7 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
requestedMAC = ctr.config.StaticMAC
}
- podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP, requestedMAC)
+ podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), networks, ctr.config.PortMappings, requestedIP, requestedMAC)
if err := r.netPlugin.TearDownPod(podNetwork); err != nil {
return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", ctr.ID())
@@ -753,7 +766,7 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
}
// CNI-in-slirp4netns
- if rootless.IsRootless() && len(ctr.config.Networks) != 0 {
+ if rootless.IsRootless() && len(networks) != 0 {
if err := DeallocRootlessCNI(context.Background(), ctr); err != nil {
return errors.Wrapf(err, "error tearing down CNI-in-slirp4netns for container %s", ctr.ID())
}
@@ -839,13 +852,18 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
settings := new(define.InspectNetworkSettings)
settings.Ports = makeInspectPortBindings(c.config.PortMappings)
+ networks, err := c.networks()
+ if err != nil {
+ return nil, err
+ }
+
// We can't do more if the network is down.
if c.state.NetNS == nil {
// We still want to make dummy configurations for each CNI net
// the container joined.
- if len(c.config.Networks) > 0 {
- settings.Networks = make(map[string]*define.InspectAdditionalNetwork, len(c.config.Networks))
- for _, net := range c.config.Networks {
+ if len(networks) > 0 {
+ settings.Networks = make(map[string]*define.InspectAdditionalNetwork, len(networks))
+ for _, net := range networks {
cniNet := new(define.InspectAdditionalNetwork)
cniNet.NetworkID = net
settings.Networks[net] = cniNet
@@ -864,16 +882,16 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
}
// If we have CNI networks - handle that here
- if len(c.config.Networks) > 0 {
- if len(c.config.Networks) != len(c.state.NetworkStatus) {
- return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI networks but have information on %d networks", len(c.config.Networks), len(c.state.NetworkStatus))
+ if len(networks) > 0 {
+ if len(networks) != len(c.state.NetworkStatus) {
+ return nil, errors.Wrapf(define.ErrInternal, "network inspection mismatch: asked to join %d CNI networks but have information on %d networks", len(networks), len(c.state.NetworkStatus))
}
settings.Networks = make(map[string]*define.InspectAdditionalNetwork)
// CNI results should be in the same order as the list of
// networks we pass into CNI.
- for index, name := range c.config.Networks {
+ for index, name := range networks {
cniResult := c.state.NetworkStatus[index]
addedNet := new(define.InspectAdditionalNetwork)
addedNet.NetworkID = name
@@ -882,6 +900,13 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
if err != nil {
return nil, err
}
+
+ aliases, err := c.runtime.state.GetNetworkAliases(c, name)
+ if err != nil {
+ return nil, err
+ }
+ addedNet.Aliases = aliases
+
addedNet.InspectBasicNetworkConfig = basicConfig
settings.Networks[name] = addedNet
diff --git a/libpod/rootless_cni_linux.go b/libpod/rootless_cni_linux.go
index 3d4ff6e86..1d6158cc2 100644
--- a/libpod/rootless_cni_linux.go
+++ b/libpod/rootless_cni_linux.go
@@ -40,8 +40,12 @@ const (
//
// AllocRootlessCNI does not lock c. c should be already locked.
func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes.Result, error) {
- if len(c.config.Networks) == 0 {
- return nil, nil, errors.New("allocRootlessCNI shall not be called when len(c.config.Networks) == 0")
+ networks, err := c.networks()
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(networks) == 0 {
+ return nil, nil, errors.New("rootless CNI networking requires that the container has joined at least one CNI network")
}
l, err := getRootlessCNIInfraLock(c.runtime)
if err != nil {
@@ -54,8 +58,8 @@ func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes.
return nil, nil, err
}
k8sPodName := getCNIPodName(c) // passed to CNI as K8S_POD_NAME
- cniResults := make([]*cnitypes.Result, len(c.config.Networks))
- for i, nw := range c.config.Networks {
+ cniResults := make([]*cnitypes.Result, len(networks))
+ for i, nw := range networks {
cniRes, err := rootlessCNIInfraCallAlloc(infra, c.ID(), nw, k8sPodName)
if err != nil {
return nil, nil, err
@@ -77,8 +81,12 @@ func AllocRootlessCNI(ctx context.Context, c *Container) (ns.NetNS, []*cnitypes.
//
// DeallocRootlessCNI does not lock c. c should be already locked.
func DeallocRootlessCNI(ctx context.Context, c *Container) error {
- if len(c.config.Networks) == 0 {
- return errors.New("deallocRootlessCNI shall not be called when len(c.config.Networks) == 0")
+ networks, err := c.networks()
+ if err != nil {
+ return err
+ }
+ if len(networks) == 0 {
+ return errors.New("rootless CNI networking requires that the container has joined at least one CNI network")
}
l, err := getRootlessCNIInfraLock(c.runtime)
if err != nil {
@@ -91,7 +99,7 @@ func DeallocRootlessCNI(ctx context.Context, c *Container) error {
return nil
}
var errs *multierror.Error
- for _, nw := range c.config.Networks {
+ for _, nw := range networks {
err := rootlessCNIInfraCallDelloc(infra, c.ID(), nw)
if err != nil {
errs = multierror.Append(errs, err)
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index c84268889..14b537ca2 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -345,8 +345,15 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
// Lock all named volumes we are adding ourself to, to ensure we can't
// use a volume being removed.
+ volsLocked := make(map[string]bool)
for _, namedVol := range ctrNamedVolumes {
toLock := namedVol
+ // Ensure that we don't double-lock a named volume that is used
+ // more than once.
+ if volsLocked[namedVol.Name()] {
+ continue
+ }
+ volsLocked[namedVol.Name()] = true
toLock.lock.Lock()
defer toLock.lock.Unlock()
}
diff --git a/libpod/state.go b/libpod/state.go
index 183f773b5..074d21740 100644
--- a/libpod/state.go
+++ b/libpod/state.go
@@ -98,20 +98,18 @@ type State interface {
// returned.
AllContainers() ([]*Container, error)
+ // Get networks the container is currently connected to.
+ GetNetworks(ctr *Container) ([]string, error)
// Get network aliases for the given container in the given network.
GetNetworkAliases(ctr *Container, network string) ([]string, error)
// Get all network aliases for the given container.
GetAllNetworkAliases(ctr *Container) (map[string][]string, error)
- // Set network aliases for the given container in the given network.
- SetNetworkAliases(ctr *Container, network string, aliases []string) error
- // Remove network aliases for the given container in the given network.
- RemoveNetworkAliases(ctr *Container, network string) error
- // GetAllAliasesForNetwork returns all the aliases for a given
- // network. Returns a map of alias to container ID.
- GetAllAliasesForNetwork(network string) (map[string]string, error)
- // RemoveAllAliasesForNetwork removes all the aliases for a given
- // network.
- RemoveAllAliasesForNetwork(network string) error
+ // Add the container to the given network, adding the given aliases
+ // (if present).
+ NetworkConnect(ctr *Container, network string, aliases []string) error
+ // Remove the container from the given network, removing all aliases for
+ // the container in that network in the process.
+ NetworkDisconnect(ctr *Container, network string) error
// Return a container config from the database by full ID
GetContainerConfig(id string) (*ContainerConfig, error)
diff --git a/libpod/state_test.go b/libpod/state_test.go
index cf41270bf..da28f3d3f 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -1345,224 +1345,6 @@ func TestAddContainerNetworkAliasesButNoMatchingNetwork(t *testing.T) {
})
}
-func TestAddContainerNetworkAliasConflictWithName(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr1, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- testCtr1.config.Networks = []string{netName}
- testCtr1.config.NetworkAliases = make(map[string][]string)
- testCtr1.config.NetworkAliases[netName] = []string{"alias1"}
-
- testCtr2, err := getTestCtr2(manager)
- assert.NoError(t, err)
-
- testCtr2.config.Networks = []string{netName}
- testCtr2.config.NetworkAliases = make(map[string][]string)
- testCtr2.config.NetworkAliases[netName] = []string{testCtr1.Name()}
-
- err = state.AddContainer(testCtr1)
- assert.NoError(t, err)
-
- err = state.AddContainer(testCtr2)
- assert.Error(t, err)
- })
-}
-
-func TestAddContainerNetworkAliasConflictWithAlias(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr1, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- aliasName := "alias1"
- testCtr1.config.Networks = []string{netName}
- testCtr1.config.NetworkAliases = make(map[string][]string)
- testCtr1.config.NetworkAliases[netName] = []string{aliasName}
-
- testCtr2, err := getTestCtr2(manager)
- assert.NoError(t, err)
-
- testCtr2.config.Networks = []string{netName}
- testCtr2.config.NetworkAliases = make(map[string][]string)
- testCtr2.config.NetworkAliases[netName] = []string{aliasName}
-
- err = state.AddContainer(testCtr1)
- assert.NoError(t, err)
-
- err = state.AddContainer(testCtr2)
- assert.Error(t, err)
- })
-}
-
-func TestAddContainerNetworkAliasConflictWithAliasButDifferentNets(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr1, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- aliasName := "alias1"
- testCtr1.config.Networks = []string{netName}
- testCtr1.config.NetworkAliases = make(map[string][]string)
- testCtr1.config.NetworkAliases[netName] = []string{aliasName}
-
- testCtr2, err := getTestCtr2(manager)
- assert.NoError(t, err)
-
- netName2 := "testnet2"
- testCtr2.config.Networks = []string{netName2}
- testCtr2.config.NetworkAliases = make(map[string][]string)
- testCtr2.config.NetworkAliases[netName2] = []string{aliasName}
-
- err = state.AddContainer(testCtr1)
- assert.NoError(t, err)
-
- err = state.AddContainer(testCtr2)
- assert.NoError(t, err)
- })
-}
-
-func TestAddContainerNameConflictsWithAliasRemovesAlias(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr1, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- testCtr2, err := getTestCtr2(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- aliasName := testCtr2.Name()
- testCtr1.config.Networks = []string{netName}
- testCtr1.config.NetworkAliases = make(map[string][]string)
- testCtr1.config.NetworkAliases[netName] = []string{aliasName}
-
- testCtr2.config.Networks = []string{netName}
-
- err = state.AddContainer(testCtr1)
- assert.NoError(t, err)
-
- err = state.AddContainer(testCtr2)
- assert.NoError(t, err)
-
- aliases, err := state.GetNetworkAliases(testCtr1, netName)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(aliases))
- })
-}
-
-func TestNetworkAliasAddAndRemoveSingleContainer(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- testCtr.config.Networks = []string{netName}
- testCtr.config.NetworkAliases = make(map[string][]string)
- testCtr.config.NetworkAliases[netName] = []string{"alias1"}
-
- startAliases, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(startAliases))
-
- err = state.AddContainer(testCtr)
- assert.NoError(t, err)
-
- oneAlias, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(oneAlias))
- assert.Equal(t, testCtr.ID(), oneAlias["alias1"])
-
- allAliases, err := state.GetAllNetworkAliases(testCtr)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(allAliases))
- netAliases, ok := allAliases[netName]
- assert.True(t, ok)
- assert.Equal(t, 1, len(netAliases))
- assert.Equal(t, "alias1", netAliases[0])
-
- ctrNetAliases, err := state.GetNetworkAliases(testCtr, netName)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(ctrNetAliases))
- assert.Equal(t, "alias1", ctrNetAliases[0])
-
- err = state.RemoveContainer(testCtr)
- assert.NoError(t, err)
-
- noAliases, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(noAliases))
- })
-}
-
-func TestNetworkAliasAddAndRemoveTwoContainers(t *testing.T) {
- runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- testCtr1, err := getTestCtr1(manager)
- assert.NoError(t, err)
-
- netName := "testnet"
- testCtr1.config.Networks = []string{netName}
- testCtr1.config.NetworkAliases = make(map[string][]string)
- testCtr1.config.NetworkAliases[netName] = []string{"alias1"}
-
- testCtr2, err := getTestCtr2(manager)
- assert.NoError(t, err)
-
- testCtr2.config.Networks = []string{netName}
- testCtr2.config.NetworkAliases = make(map[string][]string)
- testCtr2.config.NetworkAliases[netName] = []string{"alias2"}
-
- startAliases, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(startAliases))
-
- err = state.AddContainer(testCtr1)
- assert.NoError(t, err)
-
- oneAlias, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(oneAlias))
- assert.Equal(t, testCtr1.ID(), oneAlias["alias1"])
-
- err = state.AddContainer(testCtr2)
- assert.NoError(t, err)
-
- twoAliases, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 2, len(twoAliases))
- assert.Equal(t, testCtr1.ID(), twoAliases["alias1"])
- assert.Equal(t, testCtr2.ID(), twoAliases["alias2"])
-
- allAliases, err := state.GetAllNetworkAliases(testCtr1)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(allAliases))
- netAliases, ok := allAliases[netName]
- assert.True(t, ok)
- assert.Equal(t, 1, len(netAliases))
- assert.Equal(t, "alias1", netAliases[0])
-
- ctrNetAliases, err := state.GetNetworkAliases(testCtr1, netName)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(ctrNetAliases))
- assert.Equal(t, "alias1", ctrNetAliases[0])
-
- err = state.RemoveContainer(testCtr2)
- assert.NoError(t, err)
-
- oneAlias, err = state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 1, len(oneAlias))
- assert.Equal(t, testCtr1.ID(), oneAlias["alias1"])
-
- err = state.RemoveContainer(testCtr1)
- assert.NoError(t, err)
-
- noAliases, err := state.GetAllAliasesForNetwork(netName)
- assert.NoError(t, err)
- assert.Equal(t, 0, len(noAliases))
- })
-}
-
func TestCannotUseBadIDAsDependency(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
testCtr, err := getTestCtr1(manager)
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index 4214bd50e..a749a86ff 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -337,4 +337,22 @@ var _ = Describe("Podman logs", func() {
Expect(results).To(Exit(0))
Expect(results.OutputToString()).To(Equal("podman podman podman"))
})
+
+ It("Make sure logs match expected length", func() {
+ logc := podmanTest.Podman([]string{"run", "-t", "--name", "test", ALPINE, "sh", "-c", "echo 1; echo 2"})
+ logc.WaitWithDefaultTimeout()
+ Expect(logc).To(Exit(0))
+
+ wait := podmanTest.Podman([]string{"wait", "test"})
+ wait.WaitWithDefaultTimeout()
+ Expect(wait).To(Exit(0))
+
+ results := podmanTest.Podman([]string{"logs", "test"})
+ results.WaitWithDefaultTimeout()
+ Expect(results).To(Exit(0))
+ outlines := results.OutputToStringArray()
+ Expect(len(outlines)).To(Equal(2))
+ Expect(outlines[0]).To(Equal("1\r"))
+ Expect(outlines[1]).To(Equal("2\r"))
+ })
})
diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go
index 1c8a67123..7c74cea78 100644
--- a/test/e2e/run_volume_test.go
+++ b/test/e2e/run_volume_test.go
@@ -540,4 +540,12 @@ VOLUME /test/`
session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:%s:O", mountPath, mountDest), "--mount", fmt.Sprintf("type=tmpfs,target=%s", mountDest), ALPINE})
Expect(session.ExitCode()).To(Not(Equal(0)))
})
+
+ It("same volume in multiple places does not deadlock", func() {
+ volName := "testVol1"
+ session := podmanTest.Podman([]string{"run", "-t", "-i", "-v", fmt.Sprintf("%s:/test1", volName), "-v", fmt.Sprintf("%s:/test2", volName), "--rm", ALPINE, "sh", "-c", "mount | grep /test"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ Expect(len(session.OutputToStringArray())).To(Equal(2))
+ })
})
diff --git a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/parent.go b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/parent.go
index 49252a06d..f6e5e56ed 100644
--- a/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/parent.go
+++ b/vendor/github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/parent.go
@@ -111,7 +111,7 @@ func annotateEPERM(origErr error, spec port.Spec) error {
// origErr is unrelated to ip_unprivileged_port_start
return origErr
}
- text := fmt.Sprintf("cannot expose privileged port %d, you might need to add \"net.ipv4.ip_unprivileged_port_start=0\" (currently %d) to /etc/sysctl.conf", spec.ParentPort, start)
+ text := fmt.Sprintf("cannot expose privileged port %d, you can add 'net.ipv4.ip_unprivileged_port_start=%d' to /etc/sysctl.conf (currently %d)", spec.ParentPort, spec.ParentPort, start)
if filepath.Base(os.Args[0]) == "rootlesskit" {
// NOTE: The following sentence is appended only if Args[0] == "rootlesskit", because it does not apply to Podman (as of Podman v1.9).
// Podman launches the parent driver in the child user namespace (but in the parent network namespace), which disables the file capability.
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 91e9ee224..6264a8d51 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -478,7 +478,7 @@ github.com/prometheus/common/model
# github.com/prometheus/procfs v0.0.3
github.com/prometheus/procfs
github.com/prometheus/procfs/internal/fs
-# github.com/rootless-containers/rootlesskit v0.11.0
+# github.com/rootless-containers/rootlesskit v0.11.1
github.com/rootless-containers/rootlesskit/pkg/msgutil
github.com/rootless-containers/rootlesskit/pkg/port
github.com/rootless-containers/rootlesskit/pkg/port/builtin