diff options
37 files changed, 380 insertions, 858 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/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index 7afc5beb8..4f11fe08e 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/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 3a8566760..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()) } 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 4bc9381bd..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 @@ -556,7 +564,7 @@ func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath strin if stdoutStr != "" { // err contains full debug log and too verbose, so return stdoutStr logrus.Debug(err) - return errors.Errorf("failed to expose ports via rootlessport: %q", stdoutStr) + return errors.Errorf("rootlessport " + strings.TrimSuffix(stdoutStr, "\n")) } return err } @@ -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/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) |