diff options
55 files changed, 1242 insertions, 468 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 5a5d69193..b4823e1d2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,207 +1,170 @@ # Release Notes + ## 3.0.0 ### Features -- Add ability to set system wide options for slirp4netns -- Add --cidfile to container kill -- Add commas between mount options -- Add compose regression to ci -- Add containerenv information to /run/.containerenv -- Add default sysctls for pod infra containers -- Add --filter to podman system prune -- Adding json formatting to `--list-tags` option in `podman search` command. -- Add mask and unmask option to --security-opt -- Add 'MemUsageBytes' format option -- Add more information and examples on podman and pipes -- Add network filter for podman ps and pod ps -- Add Networks format placeholder to podman ps and pod ps -- Add pod filter for ps -- Add podman network create option for bridge mtu -- Add podman network create option for bridge vlan -- Add pre checkpoint -- Add Security information to podman info -- Add support for Gentoo file to package query -- Add support for network ids -- Add support for pacman package version query -- Add support for persistent volume claims in kube files -- Add support for --platform -- Add systempaths=unconfined option -- Add volume filters to system prune -- Add volume prune --filter support -- Allow podman push to push manifest lists -- Allow users to specify TMPDIR in containers.conf -- Always add the default gateway to the cni config file -- Drop default log-level from error to warn -- Enable short-name aliasing -- Generate kube on multiple containers -- Generate systemd: do not set `KillMode` -- Image sign using per user registries.d -- Implement pod-network-reload -- Include named volumes in container migration -- Initial implementation of renaming containers -- Initial implementation of volume plugins -- Network connect disconnect on non-running containers -- Not use local image create/add manifest -- Podman network label support -- Prepare support in kube play for other volume types than hostPath -- Remote copy -- Remove the ability to use [name:tag] in podman load command -- Remove varlink support from Podman -- Sign multi-arch images -- Support --network=default as if it was private -- Support Unix timestamps for `podman logs --since` +- Podman now features initial support for Docker Compose. +- Added the `podman rename` command, which allows containers to be renamed after they are created ([#1925](https://github.com/containers/podman/issues/1925)). +- The Podman remote client now supports the `podman copy` command. +- A new command, `podman network reload`, has been added. This command will re-configure the network of all running containers, and can be used to recreate firewall rules lost when the system firewall was reloaded (e.g. via `firewall-cmd --reload`). +- Podman networks now have IDs. They can be seen in `podman network ls` and can be used when removing and inspecting networks. Existing networks receive IDs automatically. +- Podman networks now also support labels. They can be added via the `--label` option to `network create`, and `podman network ls` can filter labels based on them. +- The `podman network create` command now supports setting bridge MTU and VLAN through the `--opt` option ([#8454](https://github.com/containers/podman/issues/8454)). +- The `podman container checkpoint` and `podman container restore` commands can now checkpoint and restore containers that include volumes. +- The `podman container checkpoint` command now supports the `--with-previous` and `--pre-checkpoint` options, and the `podman container restore` command now support the `--import-previous` option. These add support for two-step checkpointing with lowered dump times. +- The `podman push` command can now push manifest lists. Podman will first attempt to push as an image, then fall back to pushing as a manifest list if that fails. +- The `podman generate kube` command can now be run on multiple containers at once, and will generate a single pod containing all of them. +- The `podman generate kube` and `podman play kube` commands now support Kubernetes DNS configuration, and will preserve custom DNS configuration when exporting or importing YAML ([#9132](https://github.com/containers/podman/issues/9132)). +- The `podman generate kube` command now properly supports generating YAML for containers and pods creating using host networking (`--net=host`) ([#9077](https://github.com/containers/podman/issues/9077)). +- The `podman kill` command now supports a `--cidfile` option to kill containers given a file containing the container's ID ([#8443](https://github.com/containers/podman/issues/8443)). +- The `podman pod create` command now supports the `--net=none` option ([#9165](https://github.com/containers/podman/issues/9165)). +- The `podman volume create` command can now specify volume UID and GID as options with the `UID` and `GID` fields passed to the the `--opt` option. +- Initial support has been added for Docker Volume Plugins. Podman can now define available plugins in `containers.conf` and use them to create volumes with `podman volume create --driver`. +- The `podman run` and `podman create` commands now support a new option, `--platform`, to specify the platform of the image to be used when creating the container. +- The `--security-opt` option to `podman run` and `podman create` now supports the `systempaths=unconfined` option to unrestrict access to all paths in the container, as well as `mask` and `unmask` options to allow more granular restriction of container paths. +- The `podman stats --format` command now supports a new format specified, `MemUsageBytes`, which prints the raw bytes of memory consumed by a container without human-readable formatting [#8945](https://github.com/containers/podman/issues/8945). +- The `podman ps` command can now filter containers based on what pod they are joined to via the `pod` filter ([#8512](https://github.com/containers/podman/issues/8512)). +- The `podman pod ps` command can now filter pods based on what networks they are joined to via the `network` filter. +- The `podman pod ps` command can now print information on what networks a pod is joined to via the `.Networks` specifier to the `--format` option. +- The `podman system prune` command now supports filtering what containers, pods, images, and volumes will be pruned. +- The `podman volume prune` commands now supports filtering what volumes will be pruned. +- The `podman system prune` command now includes information on space reclaimed ([#8658](https://github.com/containers/podman/issues/8658)). +- The `podman info` command will now properly print information about packages in use on Gentoo and Arch systems. +- The `containers.conf` file now contains an option for disabling creation of a new kernel keyring on container creation ([#8384](https://github.com/containers/podman/issues/8384)). +- The `podman image sign` command can now sign multi-arch images by producing a signature for each image in a given manifest list. +- The `podman image sign` command, when run as rootless, now supports per-user registry configuration files in `$HOME/.config/containers/registries.d`. +- Configuration options for `slirp4netns` can now be set system-wide via the `NetworkCmdOptions` configuration option in `containers.conf`. +- The MTU of `slirp4netns` can now be configured via the `mtu=` network command option (e.g. `podman run --net slirp4netns:mtu=9000`). + +### Security +- A fix for CVE-2021-20199 is included. Podman between v1.8.0 and v2.2.1 used `127.0.0.1` as the source address for all traffic forwarded into rootless containers by a forwarded port; this has been changed to address the issue. + ### Changes -- Add LogSize to container inspect -- Allow image errors to bubble up from lower level functions. -- Change name of imageVolumes in container config JSON -- Cleanup CNI Networks on reboot -- Consolidate filter logic to pkg subdirectory -- Make `podman stats` slirp check more robust -- More /var/run -> /run -- Prefer read/write images over read/only images -- Refactor kube.ToSpecGen parameters to struct -- Rename AutocompletePortCommand func -- Repeat system pruning until there is nothing removed -- Switch references of /var/run -> /run -- Use HTTPProxy settings from containers.conf -- Use Libpod tmpdir for pause path -- Use Options as CRImportCheckpoint() argument -- Use Options as exportCheckpoint() argument -- Use PasswordCallback instead of Password for ssh -- Use abi PodPs implementation for libpod/pods/json endpoint -- Validate that the bridge option is supported -- archive: move stat-header handling into copy package -- libpod, conmon: change log level for rootless -- libpod: change function to accept ExecOptions -- libpod: handle single user mapped as root -- make podman play use ENVs from image -- pkg/copy: introduce a Copier -- podman events allow future time for --until -- podman.service should be an exec service not a notify service -- rewrite podman-cp -- rootless: add function to retrieve gid/uid mappings -- rootless: automatically split userns ranges -- runtime: set XDG_* env variables if missing -- shell completion for the network flag -- specgen: improve heuristic for /sys bind mount -- systemd: make rundir always accessible +- Shortname aliasing support has now been turned on by default. All Podman commands that must pull an image will, if a TTY is available, prompt the user about what image to pull. +- The `podman load` command no longer accepts a `NAME[:TAG]` argument. The presence of this argument broke CLI compatibility with Docker by making `docker load` commands unusable with Podman ([#7387](https://github.com/containers/podman/issues/7387)). +- The Go bindings for the HTTP API have been rewritten with a focus on limiting dependency footprint and improving extensibility. Read more [here](https://github.com/containers/podman/blob/v3.0/pkg/bindings/README.md). +- The legacy Varlink API has been completely removed from Podman. +- The default log level for Podman has been changed from Error to Warn. +- The `podman network create` command can now create `macvlan` networks using the `--driver macvlan` option for Docker compatibility. The existing `--macvlan` flag has been deprecated and will be removed in Podman 4.0 some time next year. +- The `podman inspect` command has had the `LogPath` and `LogTag` fields moved into the `LogConfig` structure (from the root of the Inspect structure). The maximum size of the log file is also included. +- The `podman generate systemd` command no longer generates unit files using the deprecated `KillMode=none` option ([#8615](https://github.com/containers/podman/issues/8615)). +- The `podman stop` command now releases the container lock while waiting for it to stop - as such, commands like `podman ps` will no longer block until `podman stop` completes ([#8501](https://github.com/containers/podman/issues/8501)). +- Networks created with `podman network create --internal` no longer use the `dnsname` plugin. This configuration never functioned as expected. +- Error messages for the remote Podman client have been improved when it cannot connect to a Podman service. +- Error messages for `podman run` when an invalid SELinux is specified have been improved. +- Rootless Podman features improved support for containers with a single user mapped into the rootless user namespace. +- Pod infra containers now respect default sysctls specified in `containers.conf` allowing for advanced configuration of the namespaces they will share. +- SSH public key handling for remote Podman has been improved. + ### Bugfixes -- Close image rawSource when each loop ends -- Containers should not get inheritable caps by default -- Correct port range logic for port generation -- Correct which network commands can be run as rootless -- Disable CGv1 pod stats on net=host post -- Do not error on installing duplicate shutdown handler -- Do not ignore infra command from config files -- Do not mount sysfs as rootless in more cases -- Do not pull if image domain is localhost -- Do not use "true" after "syslog" in exit commands -- Do not validate the volume source path in specgen -- Don't accidently remove XDG_RUNTIME_DIR when reseting storage -- Ensure that `podman play kube` actually reports errors -- Ensure that user-specified HOSTNAME is honored -- Ensure we do not edit container config in Exec -- Exorcise Driver code from libpod/define -- Expose Height/Width fields to decoder -- Expose security attribute errors with their own messages -- Fix Wrong image tag is used when creating a container from an image with multiple tags -- Fix `podman images...` missing headers in table templates -- Fix build for mips architecture -- Fix build for mips architecture follow-up -- Fix custom mac address with a custom cni network -- Fix extra quotation mark in manpages. -- Fix missing options in volumes display while setting uid and gid -- Fix missing podman-container-rename man page link -- Fix network ls --filter invalid value flake -- Fix option names --subuidname and --subgidname -- Fix panic in libpod images exists endpoint -- Fix podman build --logfile -- Fix podman logs read partial log lines -- Fix problems reported by staticcheck -- Fix problems with network remove -- Fix shell completion for ps --filter ancestor -- Fix some nit -- Fix spelling mistakes -- Fix storage.conf to define driver in the VM -- Fix support for rpmbuild < 4.12.0. -- Fix: unpause not supported for CGv1 rootless -- Fxes /etc/hosts duplicated every time after container restarted in a pod -- Handle --rm when starting a container -- Handle podman exec capabilities correctly -- Honor the --layers flag -- Ignore containers.conf sysctls when sharing namespaces -- Improve error message when the the podman service is not enabled -- Make podman generate systemd --new flag parsing more robust -- Pass down EnableKeyring from containers.conf to conmon -- Properly handle --cap-add all when running with a --user flag -- Revert "Allow multiple --network flags for podman run/create" -- Revert e6fbc15f26b2a609936dfc11732037c70ee14cba -- Revert the custom cobra vendor -- Rework pruning to report reclaimed space -- Set NetNS mode instead of value -- The slirp4netns sandbox requires pivot_root -- close journald when reading -- container create: do not clear image name -- container stop: release lock before calling the runtime -- exec: honor --privileged -- fix: disable seccomp by default when privileged. -- image list: ignore bare manifest list -- network: disallow CNI networks with user namespaces -- oci: keep LC_ env variables to conmon -- oci: use /proc/self/fd/FD to open unix socket -- pass full NetworkMode to ParseNetworkNamespace -- play kube: fix args/command handling -- play kube: set entrypoint when interpreting Command -- podman build --force-rm defaults to true in code -- podman logs honor stderr correctly -- podman, exec: move conmon to the correct cgroup -- podman-remote fix sending tar content -- podman: drop checking valid rootless UID -- re-open container log files -- security: honor systempaths=unconfined for ro paths +- Fixed a bug where the `podman history --no-trunc` command would truncate the `Created By` field ([#9120](https://github.com/containers/podman/issues/9120)). +- Fixed a bug where root containers that did not explicitly specify a CNI network to join did not generate an entry for the network in use in the `Networks` field of the output of `podman inspect` ([#6618](https://github.com/containers/podman/issues/6618)). +- Fixed a bug where, under some circumstances, container working directories specified by the image (via the `WORKDIR` instruction) but not present in the image, would not be created ([#9040](https://github.com/containers/podman/issues/9040)). +- Fixed a bug where the `podman generate systemd` command would generate invalid unit files if the container was creating using a command line that included doubled braces (`{{` and `}}`), e.g. `--log-opt-tag={{.Name}}` ([#9034](https://github.com/containers/podman/issues/9034)). +- Fixed a bug where the `podman generate systemd --new` command could generate unit files including invalid Podman commands if the container was created using merged short options (e.g. `podman run -dt`) ([#8847](https://github.com/containers/podman/issues/8847)). +- Fixed a bug where the `podman generate systemd --new` command could generate unit files that did not handle Podman commands including some special characters (e.g. `$`) ([#9176](https://github.com/containers/podman/issues/9176) +- Fixed a bug where rootless containers joining CNI networks could not set a static IP address ([#7842](https://github.com/containers/podman/issues/7842)). +- Fixed a bug where rootless containers joining CNI networks could not set network aliases ([#8567](https://github.com/containers/podman/issues/8567)). +- Fixed a bug where the remote client could, under some circumstances, not include the `Containerfile` when sending build context to the server ([#8374](https://github.com/containers/podman/issues/8374)). +- Fixed a bug where rootless Podman did not mount `/sys` as a new `sysfs` in some circumstances where it was acceptable. +- Fixed a bug where rootless containers that both joined a user namespace and a CNI networks would cause a segfault. These options are incompatible and now return an error. +- Fixed a bug where the `podman play kube` command did not properly handle `CMD` and `ARGS` from images ([#8803](https://github.com/containers/podman/issues/8803)). +- Fixed a bug where the `podman play kube` command did not properly handle environment variables from images ([#8608](https://github.com/containers/podman/issues/8608)). +- Fixed a bug where the `podman play kube` command did not properly print errors that occurred when starting containers. +- Fixed a bug where the `podman play kube` command errored when `hostNetwork` was used ([#8790](https://github.com/containers/podman/issues/8790)). +- Fixed a bug where the `podman play kube` command would always pull images when the `:latest` tag was specified, even if the image was available locally ([#7838](https://github.com/containers/podman/issues/7838)). +- Fixed a bug where the `podman play kube` command did not properly handle SELinux configuration, rending YAML with custom SELinux configuration unusable ([#8710](https://github.com/containers/podman/issues/8710)). +- Fixed a bug where the `podman generate kube` command incorrectly populated the `args` and `command` fields of generated YAML ([#9211](https://github.com/containers/podman/issues/9211)). +- Fixed a bug where containers in a pod would create a duplicate entry in the pod's shared `/etc/hosts` file every time the container restarted ([#8921](https://github.com/containers/podman/issues/8921)). +- Fixed a bug where the `podman search --list-tags` command did not support the `--format` option ([#8740](https://github.com/containers/podman/issues/8740)). +- Fixed a bug where the `http_proxy` option in `containers.conf` was not being respected, and instead was set unconditionally to true ([#8843](https://github.com/containers/podman/issues/8843)). +- Fixed a bug where rootless Podman could, on systems with a recent Conmon and users with a long username, fail to attach to containers ([#8798](https://github.com/containers/podman/issues/8798)). +- Fixed a bug where the `podman images` command would break and fail to display any images if an empty manifest list was present in storage ([#8931](https://github.com/containers/podman/issues/8931)). +- Fixed a bug where locale environment variables were not properly passed on to Conmon. +- Fixed a bug where Podman would not build on the MIPS architecture ([#8782](https://github.com/containers/podman/issues/8782)). +- Fixed a bug where rootless Podman could fail to properly configure user namespaces for rootless containers when the user specified a `--uidmap` option that included a mapping beginning with UID `0`. +- Fixed a bug where the `podman logs` command using the `k8s-file` backend did not properly handle partial log lines with a length of 1 ([#8879](https://github.com/containers/podman/issues/8879)). +- Fixed a bug where the `podman logs` command with the `--follow` option did not properly handle log rotation ([#8733](https://github.com/containers/podman/issues/8733)). +- Fixed a bug where user-specified `HOSTNAME` environment variables were overwritten by Podman ([#8886](https://github.com/containers/podman/issues/8886)). +- Fixed a bug where Podman would applied default sysctls from `containers.conf` in too many situations (e.g. applying network sysctls when the container shared its network with a pod). +- Fixed a bug where Podman did not properly handle cases where a secondary image store was in use and an image was present in both the secondary and primary stores ([#8176](https://github.com/containers/podman/issues/8176)). +- Fixed a bug where systemd-managed rootless Podman containers where the user in the container was not root could fail as the container's PID file was not accessible to systemd on the host ([#8506](https://github.com/containers/podman/issues/8506)). +- Fixed a bug where the `--privileged` option to `podman run` and `podman create` would, under some circumstances, not disable Seccomp ([#8849](https://github.com/containers/podman/issues/8849)). +- Fixed a bug where the `podman exec` command did not properly add capabilities when the container or exec session were run with `--privileged`. +- Fixed a bug where rootless Podman would use the `--enable-sandbox` option to `slirp4netns` unconditionally, even when `pivot_root` was disabled, rendering `slirp4netns` unusable when `pivot_root` was disabled ([#8846](https://github.com/containers/podman/issues/8846)). +- Fixed a bug where `podman build --logfile` did not actually write the build's log to the logfile. +- Fixed a bug where the `podman system service` command did not close STDIN, and could display user-interactive prompts ([#8700](https://github.com/containers/podman/issues/8700)). +- Fixed a bug where the `podman system reset` command could, under some circumstances, remove all the contents of the `XDG_RUNTIME_DIR` directory ([#8680](https://github.com/containers/podman/issues/8680)). +- Fixed a bug where the `podman network create` command created CNI configurations that did not include a default gateway ([#8748](https://github.com/containers/podman/issues/8748)). +- Fixed a bug where the `podman.service` systemd unit provided by default used the wrong service type, and would cause systemd to not correctly register the service as started ([#8751](https://github.com/containers/podman/issues/8751)). +- Fixed a bug where, if the `TMPDIR` environment variable was set for the container engine in `containers.conf`, it was being ignored. +- Fixed a bug where the `podman events` command did not properly handle future times given to the `--until` option ([#8694](https://github.com/containers/podman/issues/8694)). +- Fixed a bug where the `podman logs` command wrote container `STDERR` logs to `STDOUT` instead of `STDERR` ([#8683](https://github.com/containers/podman/issues/8683)). +- Fixed a bug where containers created from an image with multiple tags would report that they were created from the wrong tag ([#8547](https://github.com/containers/podman/issues/8547)). +- Fixed a bug where container capabilities were not set properly when the `--cap-add=all` and `--user` options to `podman create` and `podman run` were combined. +- Fixed a bug where the `--layers` option to `podman build` was nonfunctional ([#8643](https://github.com/containers/podman/issues/8643)). +- Fixed a bug where the `podman system prune` command did not act recursively, and thus would leave images, containers, pods, and volumes present that would be removed by a subsequent call to `podman system prune` ([#7990](https://github.com/containers/podman/issues/7990)). +- Fixed a bug where the `--publish` option to `podman run` and `podman create` did not properly handle ports specified as a range of ports with no host port specified ([#8650](https://github.com/containers/podman/issues/8650)). +- Fixed a bug where `--format` did not support JSON output for individual fields ([#8444](https://github.com/containers/podman/issues/8444)). +- Fixed a bug where the `podman stats` command would fail when run on root containers using the `slirp4netns` network mode ([#7883](https://github.com/containers/podman/issues/7883)). +- Fixed a bug where the Podman remote client would ask for a password even if the server's SSH daemon did not support password authentication ([#8498](https://github.com/containers/podman/issues/8498)). +- Fixed a bug where the `podman stats` command would fail if the system did not support one or more of the cgroup controllers Podman supports ([#8588](https://github.com/containers/podman/issues/8588)). +- Fixed a bug where the `--mount` option to `podman create` and `podman run` did not ignore the `consistency` mount option. +- Fixed a bug where failures during the resizing of a container's TTY would print the wrong error. +- Fixed a bug where the `podman network disconnect` command could cause the `podman inspect` command to fail for a container until it was restarted ([#9234](https://github.com/containers/podman/issues/9234)). +- Fixed a bug where containers created from a read-only rootfs (using the `--rootfs` option to `podman create` and `podman run`) would fail ([#9230](https://github.com/containers/podman/issues/9230)). + ### API -- Add API for communicating with Docker volume plugins -- Change bindings to stop two API calls for ping -- Close the stdin/tty when using podman as a restAPI. -- Compat api containers/json add support for filters -- Container rename bindings -- Do not pass name argument to Load API -- Docker compat API - /images/search returns wrong structure (#7857) -- Docker compat API - containers create ignores the name -- Fix some network compat api problems -- Jira RUN-1106 Container handlers updates -- Jira RUN-1106 Image handlers updates -- Jira RUN-1106 Network handlers updates -- Jira RUN-1106 System handlers updates -- Jira RUN-1106 Volumes handlers updates -- Makefile: add target to generate bindings -- More docker compat API fixes -- Podman image bindings for 3.0 -- REST API v2 - ping - fix typo in header -- REST API v2 - ping - remove newline from response to improve Docker compatibility -- Reduce general binding binary size -- Restore compatible API for prune endpoints -- compat create should use bindings -- hack/podman-socat captures the API stream -- libpod API: pull: fix channel race -- misc bindings to podman v3 -- pkg/copy: add parsing API -- podman v3 container bindings -- podman v3 pod bindings +- Libpod API version has been bumped to v3.0.0. +- All Libpod Pod APIs have been modified to properly report errors with individual containers. Cases where the operation as a whole succeeded but individual containers failed now report an HTTP 409 error ([#8865](https://github.com/containers/podman/issues/8865)). +- The Compat API for Containers now supports the Rename and Copy APIs. +- Fixed a bug where the Compat Prune APIs (for volumes, containers, and images) did not return the amount of space reclaimed in their responses. +- Fixed a bug where the Compat and Libpod Exec APIs for Containers would drop errors that occurred prior to the exec session successfully starting (e.g. a "no such file" error if an invalid executable was passed) ([#8281](https://github.com/containers/podman/issues/8281)) +- Fixed a bug where the Volumes field in the Compat Create API for Containers was being ignored ([#8649](https://github.com/containers/podman/issues/8649)). +- Fixed a bug where the NetworkMode field in the Compat Create API for Containers was not handling some values, e.g. `container:`, correctly. +- Fixed a bug where the Compat Create API for Containers did not set container name properly. +- Fixed a bug where containers created using the Compat Create API unconditionally used Kubernetes file logging (the default specified in `containers.conf` is now used). +- Fixed a bug where the Compat Inspect API for Containers could include container states not recognized by Docker. +- Fixed a bug where Podman did not properly clean up after calls to the Events API when the `journald` backend was in use, resulting in a leak of file descriptors ([#8864](https://github.com/containers/podman/issues/8864)). +- Fixed a bug where the Libpod Pull endpoint for Images could fail with an `index out of range` error under certain circumstances ([#8870](https://github.com/containers/podman/issues/8870)). +- Fixed a bug where the Libpod Exists endpoint for Images could panic. +- Fixed a bug where the Compat List API for Containers did not support all filters ([#8860](https://github.com/containers/podman/issues/8860)). +- Fixed a bug where the Compat List API for Containers did not properly populate the Status field. +- Fixed a bug where the Compat and Libpod Resize APIs for Containers ignored the height and width parameters ([#7102](https://github.com/containers/podman/issues/7102)). +- Fixed a bug where the Compat Search API for Images returned an incorrectly-formatted JSON response ([#8758](https://github.com/containers/podman/pull/8758)). +- Fixed a bug where the Compat Load API for Images did not properly clean up temporary files. +- Fixed a bug where the Compat Create API for Networks could panic when an empty IPAM configuration was specified. +- Fixed a bug where the Compat Inspect and List APIs for Networks did not include Scope. + +### Misc +- Updated Buildah to v1.19.2 +- Updated the containers/storage library to v1.24.5 +- Updated the containers/common library to v0.33.4 + +## v2.2.1 +### Changes +- Due to a conflict with a previously-removed field, we were forced to modify the way image volumes (mounting images into containers using `--mount type=image`) were handled in the database. As a result, containers created in Podman 2.2.0 with image volumes will not have them in v2.2.1, and these containers will need to be re-created. + +### Bugfixes +- Fixed a bug where rootless Podman would, on systems without the `XDG_RUNTIME_DIR` environment variable defined, use an incorrect path for the PID file of the Podman pause process, causing Podman to fail to start ([#8539](https://github.com/containers/podman/issues/8539)). +- Fixed a bug where containers created using Podman v1.7 and earlier were unusable in Podman due to JSON decode errors ([#8613](https://github.com/containers/podman/issues/8613)). +- Fixed a bug where Podman could retrieve invalid cgroup paths, instead of erroring, for containers that were not running. +- Fixed a bug where the `podman system reset` command would print a warning about a duplicate shutdown handler being registered. +- Fixed a bug where rootless Podman would attempt to mount `sysfs` in circumstances where it was not allowed; some OCI runtimes (notably `crun`) would fall back to alternatives and not fail, but others (notably `runc`) would fail to run containers. +- Fixed a bug where the `podman run` and `podman create` commands would fail to create containers from untagged images ([#8558](https://github.com/containers/podman/issues/8558)). +- Fixed a bug where remote Podman would prompt for a password even when the server did not support password authentication ([#8498](https://github.com/containers/podman/issues/8498)). +- Fixed a bug where the `podman exec` command did not move the Conmon process for the exec session into the correct cgroup. +- Fixed a bug where shell completion for the `ancestor` option to `podman ps --filter` did not work correctly. +- Fixed a bug where detached containers would not properly clean themselves up (or remove themselves if `--rm` was set) if the Podman command that created them was invoked with `--log-level=debug`. + +### API +- Fixed a bug where the Compat Create endpoint for Containers did not properly handle the `Binds` and `Mounts` parameters in `HostConfig`. +- Fixed a bug where the Compat Create endpoint for Containers ignored the `Name` query parameter. +- Fixed a bug where the Compat Create endpoint for Containers did not properly handle the "default" value for `NetworkMode` (this value is used extensively by `docker-compose`) ([#8544](https://github.com/containers/podman/issues/8544)). +- Fixed a bug where the Compat Build endpoint for Images would sometimes incorrectly use the `target` query parameter as the image's tag. + ### Misc -- Bump github.com/containernetworking/plugins from 0.8.7 to 0.9.0 -- Bump github.com/containers/common from 0.30.0 to 0.31.1 -- Bump github.com/containers/image/v5 from 5.8.1 to 5.9.0 -- Bump github.com/containers/storage from 1.24.1 to 1.24.5 -- Bump github.com/cri-o/ocicni to latest master -- Bump github.com/google/uuid from 1.1.2 to 1.1.5 -- Bump github.com/onsi/gomega from 1.10.3 to 1.10.4 -- Bump github.com/opencontainers/selinux from 1.6.0 to 1.8.0 -- Bump github.com/stretchr/testify from 1.6.1 to 1.7.0 -- Bump k8s.io/apimachinery from 0.19.4 to 0.20.2 -- Bump master to v3.0.0-dev -- Bump to containers/buildah 1.9.2 -- Bump version in README to v2.2.0 -- vendor containers/psgo@v1.5.2 +- Podman v2.2.0 vendored a non-released, custom version of the `github.com/spf13/cobra` package; this has been reverted to the latest upstream release to aid in packaging. +- Updated the containers/image library to v5.9.0 ## 2.2.0 ### Features @@ -344,7 +307,7 @@ - Fixed a bug where a client disconnecting from the Libpod or Compat events endpoints could result in the server using 100% CPU ([#7946](https://github.com/containers/podman/issues/7946)). - Fixed a bug where the "no such image" error message sent by the Compat Inspect endpoint for Images returned a 404 status code with an error that was improperly formatted for Docker compatibility. - Fixed a bug where the Compat Create endpoint for networks did not properly set a default for the `driver` parameter if it was not provided by the client. -- Fixed a bug where the Compat Inspect endpoint for images did not populate the `RootFS` field of the response. +- Fixed a bug where the Compat Inspect endpoint for images did not populate the `RootFS`, `VirtualSize`, `ParentId`, `Architecture`, `Os`, and `OsVersion` fields of the response. - Fixed a bug where the Compat Inspect endpoint for images would omit the `ParentId` field if the image had no parent, and the `Created` field if the image did not have a creation time. - Fixed a bug where the Compat Remove endpoint for Networks did not support the `Force` query parameter. diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index c9a3c5e94..09dd74e20 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -817,6 +817,7 @@ func AutocompleteNetworkFlag(cmd *cobra.Command, args []string, toComplete strin "allow_host_loopback=": getBoolCompletion, "cidr=": nil, "enable_ipv6=": getBoolCompletion, + "mtu=": nil, "outbound_addr=": nil, "outbound_addr6=": nil, "port_handler=": func(_ string) ([]string, cobra.ShellCompDirective) { diff --git a/cmd/podman/common/volumes.go b/cmd/podman/common/volumes.go index a6e6faeca..2a598d7a5 100644 --- a/cmd/podman/common/volumes.go +++ b/cmd/podman/common/volumes.go @@ -353,6 +353,10 @@ func getBindMount(args []string) (spec.Mount, error) { default: return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) } + case "consistency": + // Often used on MACs and mistakenly on Linux platforms. + // Since Docker ignores this option so shall we. + continue default: return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) } @@ -437,6 +441,10 @@ func getTmpfsMount(args []string) (spec.Mount, error) { } newMount.Destination = filepath.Clean(kv[1]) setDest = true + case "consistency": + // Often used on MACs and mistakenly on Linux platforms. + // Since Docker ignores this option so shall we. + continue default: return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) } @@ -534,6 +542,10 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { } newVolume.Dest = filepath.Clean(kv[1]) setDest = true + case "consistency": + // Often used on MACs and mistakenly on Linux platforms. + // Since Docker ignores this option so shall we. + continue default: return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) } @@ -581,6 +593,10 @@ func getImageVolume(args []string) (*specgen.ImageVolume, error) { default: return nil, errors.Wrapf(util.ErrBadMntOption, "invalid rw value %q", kv[1]) } + case "consistency": + // Often used on MACs and mistakenly on Linux platforms. + // Since Docker ignores this option so shall we. + continue default: return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) } diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index d997ea344..23fb323a0 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -171,33 +171,6 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } - createOptions.Net.Network = specgen.Namespace{} - if cmd.Flag("network").Changed { - netInput, err := cmd.Flags().GetString("network") - if err != nil { - return err - } - parts := strings.SplitN(netInput, ":", 2) - - n := specgen.Namespace{} - switch { - case netInput == "bridge": - n.NSMode = specgen.Bridge - case netInput == "host": - n.NSMode = specgen.Host - case netInput == "slirp4netns", strings.HasPrefix(netInput, "slirp4netns:"): - n.NSMode = specgen.Slirp - if len(parts) > 1 { - createOptions.Net.NetworkOptions = make(map[string][]string) - createOptions.Net.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") - } - default: - // Container and NS mode are presently unsupported - n.NSMode = specgen.Bridge - createOptions.Net.CNINetworks = strings.Split(netInput, ",") - } - createOptions.Net.Network = n - } if len(createOptions.Net.PublishPorts) > 0 { if !createOptions.Infra { return errors.Errorf("you must have an infra container to publish port bindings to the host") diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 8deaa8540..02eeb557c 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -635,6 +635,7 @@ Valid _mode_ values are: - **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. + - **mtu=MTU**: Specify the MTU to use for this network. (Default is `65520`). - **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`). - **outbound_addr=INTERFACE**: Specify the outbound interface slirp should bind to (ipv4 traffic only). diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index 74c231184..edcd63935 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -671,6 +671,7 @@ Valid _mode_ values are: - **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. + - **mtu=MTU**: Specify the MTU to use for this network. (Default is `65520`). - **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`). - **outbound_addr=INTERFACE**: Specify the outbound interface slirp should bind to (ipv4 traffic only). diff --git a/libpod/container.go b/libpod/container.go index 58bf95470..ed7535bc8 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -1073,6 +1073,18 @@ func networkDisabled(c *Container) (bool, error) { return false, nil } +func (c *Container) HostNetwork() bool { + if c.config.CreateNetNS || c.config.NetNsCtr != "" { + return false + } + for _, ns := range c.config.Spec.Linux.Namespaces { + if ns.Type == spec.NetworkNamespace { + return false + } + } + return true +} + // ContainerState returns containerstate struct func (c *Container) ContainerState() (*ContainerState, error) { if !c.batched { diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 6c9489a08..ba85a1f47 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -213,6 +213,7 @@ func (c *Container) resolveWorkDir() error { // we need to return the full error. return errors.Wrapf(err, "error detecting workdir %q on container %s", workdir, c.ID()) } + return nil } // Ensure container entrypoint is created (if required). diff --git a/libpod/kube.go b/libpod/kube.go index 753c58099..f9ead027d 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -49,6 +49,7 @@ func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) { } extraHost := make([]v1.HostAlias, 0) + hostNetwork := false if p.HasInfraContainer() { infraContainer, err := p.getInfraContainer() if err != nil { @@ -69,9 +70,9 @@ func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) { return nil, servicePorts, err } servicePorts = containerPortsToServicePorts(ports) - + hostNetwork = p.config.InfraContainer.HostNetwork } - pod, err := p.podWithContainers(allContainers, ports) + pod, err := p.podWithContainers(allContainers, ports, hostNetwork) if err != nil { return nil, servicePorts, err } @@ -167,13 +168,14 @@ func containersToServicePorts(containers []v1.Container) []v1.ServicePort { return sps } -func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPort) (*v1.Pod, error) { +func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPort, hostNetwork bool) (*v1.Pod, error) { deDupPodVolumes := make(map[string]*v1.Volume) first := true podContainers := make([]v1.Container, 0, len(containers)) + dnsInfo := v1.PodDNSConfig{} for _, ctr := range containers { if !ctr.IsInfra() { - ctr, volumes, err := containerToV1Container(ctr) + ctr, volumes, _, err := containerToV1Container(ctr) if err != nil { return nil, err } @@ -196,6 +198,22 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor vol := vol deDupPodVolumes[vol.Name] = &vol } + } else { + _, _, infraDNS, err := containerToV1Container(ctr) + if err != nil { + return nil, err + } + if infraDNS != nil { + if servers := infraDNS.Nameservers; len(servers) > 0 { + dnsInfo.Nameservers = servers + } + if searches := infraDNS.Searches; len(searches) > 0 { + dnsInfo.Searches = searches + } + if options := infraDNS.Options; len(options) > 0 { + dnsInfo.Options = options + } + } } } podVolumes := make([]v1.Volume, 0, len(deDupPodVolumes)) @@ -203,10 +221,10 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor podVolumes = append(podVolumes, *vol) } - return addContainersAndVolumesToPodObject(podContainers, podVolumes, p.Name()), nil + return addContainersAndVolumesToPodObject(podContainers, podVolumes, p.Name(), &dnsInfo, hostNetwork), nil } -func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1.Volume, podName string) *v1.Pod { +func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1.Volume, podName string, dnsOptions *v1.PodDNSConfig, hostNetwork bool) *v1.Pod { tm := v12.TypeMeta{ Kind: "Pod", APIVersion: "v1", @@ -225,8 +243,12 @@ func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1. CreationTimestamp: v12.Now(), } ps := v1.PodSpec{ - Containers: containers, - Volumes: volumes, + Containers: containers, + Volumes: volumes, + HostNetwork: hostNetwork, + } + if dnsOptions != nil { + ps.DNSConfig = dnsOptions } p := v1.Pod{ TypeMeta: tm, @@ -241,32 +263,69 @@ func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1. func simplePodWithV1Containers(ctrs []*Container) (*v1.Pod, error) { kubeCtrs := make([]v1.Container, 0, len(ctrs)) kubeVolumes := make([]v1.Volume, 0) + hostNetwork := true + podDNS := v1.PodDNSConfig{} for _, ctr := range ctrs { - kubeCtr, kubeVols, err := containerToV1Container(ctr) + if !ctr.HostNetwork() { + hostNetwork = false + } + kubeCtr, kubeVols, ctrDNS, err := containerToV1Container(ctr) if err != nil { return nil, err } kubeCtrs = append(kubeCtrs, kubeCtr) kubeVolumes = append(kubeVolumes, kubeVols...) - } - return addContainersAndVolumesToPodObject(kubeCtrs, kubeVolumes, strings.ReplaceAll(ctrs[0].Name(), "_", "")), nil + // Combine DNS information in sum'd structure + if ctrDNS != nil { + // nameservers + if servers := ctrDNS.Nameservers; servers != nil { + if podDNS.Nameservers == nil { + podDNS.Nameservers = make([]string, 0) + } + for _, s := range servers { + if !util.StringInSlice(s, podDNS.Nameservers) { // only append if it does not exist + podDNS.Nameservers = append(podDNS.Nameservers, s) + } + } + } + // search domains + if domains := ctrDNS.Searches; domains != nil { + if podDNS.Searches == nil { + podDNS.Searches = make([]string, 0) + } + for _, d := range domains { + if !util.StringInSlice(d, podDNS.Searches) { // only append if it does not exist + podDNS.Searches = append(podDNS.Searches, d) + } + } + } + // dns options + if options := ctrDNS.Options; options != nil { + if podDNS.Options == nil { + podDNS.Options = make([]v1.PodDNSConfigOption, 0) + } + podDNS.Options = append(podDNS.Options, options...) + } + } // end if ctrDNS + } + return addContainersAndVolumesToPodObject(kubeCtrs, kubeVolumes, strings.ReplaceAll(ctrs[0].Name(), "_", ""), &podDNS, hostNetwork), nil } // containerToV1Container converts information we know about a libpod container // to a V1.Container specification. -func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) { +func containerToV1Container(c *Container) (v1.Container, []v1.Volume, *v1.PodDNSConfig, error) { kubeContainer := v1.Container{} kubeVolumes := []v1.Volume{} kubeSec, err := generateKubeSecurityContext(c) if err != nil { - return kubeContainer, kubeVolumes, err + return kubeContainer, kubeVolumes, nil, err } if len(c.config.Spec.Linux.Devices) > 0 { // TODO Enable when we can support devices and their names kubeContainer.VolumeDevices = generateKubeVolumeDeviceFromLinuxDevice(c.Spec().Linux.Devices) - return kubeContainer, kubeVolumes, errors.Wrapf(define.ErrNotImplemented, "linux devices") + return kubeContainer, kubeVolumes, nil, errors.Wrapf(define.ErrNotImplemented, "linux devices") } if len(c.config.UserVolumes) > 0 { @@ -274,7 +333,7 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) { // Volume names need to be coordinated "globally" in the kube files. volumeMounts, volumes, err := libpodMountsToKubeVolumeMounts(c) if err != nil { - return kubeContainer, kubeVolumes, err + return kubeContainer, kubeVolumes, nil, err } kubeContainer.VolumeMounts = volumeMounts kubeVolumes = append(kubeVolumes, volumes...) @@ -282,34 +341,33 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) { envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env) if err != nil { - return kubeContainer, kubeVolumes, err + return kubeContainer, kubeVolumes, nil, err } portmappings, err := c.PortMappings() if err != nil { - return kubeContainer, kubeVolumes, err + return kubeContainer, kubeVolumes, nil, err } ports, err := ocicniPortMappingToContainerPort(portmappings) if err != nil { - return kubeContainer, kubeVolumes, err + return kubeContainer, kubeVolumes, nil, err } - containerCommands := c.Command() - kubeContainer.Name = removeUnderscores(c.Name()) + // Handle command and arguments. + if ep := c.Entrypoint(); len(ep) > 0 { + // If we have an entrypoint, set the container's command as + // arguments. + kubeContainer.Command = ep + kubeContainer.Args = c.Command() + } else { + kubeContainer.Command = c.Command() + } + kubeContainer.Name = removeUnderscores(c.Name()) _, image := c.Image() kubeContainer.Image = image kubeContainer.Stdin = c.Stdin() - // prepend the entrypoint of the container to command - if ep := c.Entrypoint(); len(c.Entrypoint()) > 0 { - ep = append(ep, containerCommands...) - containerCommands = ep - } - kubeContainer.Command = containerCommands - // TODO need to figure out how we handle command vs entry point. Kube appears to prefer entrypoint. - // right now we just take the container's command - //container.Args = args kubeContainer.WorkingDir = c.WorkingDir() kubeContainer.Ports = ports // This should not be applicable @@ -355,7 +413,38 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) { } } - return kubeContainer, kubeVolumes, nil + // Obtain the DNS entries from the container + dns := v1.PodDNSConfig{} + + // DNS servers + if servers := c.config.DNSServer; len(servers) > 0 { + dnsServers := make([]string, 0) + for _, server := range servers { + dnsServers = append(dnsServers, server.String()) + } + dns.Nameservers = dnsServers + } + + // DNS search domains + if searches := c.config.DNSSearch; len(searches) > 0 { + dns.Searches = searches + } + + // DNS options + if options := c.config.DNSOption; len(options) > 0 { + dnsOptions := make([]v1.PodDNSConfigOption, 0) + for _, option := range options { + // the option can be "k:v" or just "k", no delimiter is required + opts := strings.SplitN(option, ":", 2) + dnsOpt := v1.PodDNSConfigOption{ + Name: opts[0], + Value: &opts[1], + } + dnsOptions = append(dnsOptions, dnsOpt) + } + dns.Options = dnsOptions + } + return kubeContainer, kubeVolumes, &dns, nil } // ocicniPortMappingToContainerPort takes an ocicni portmapping and converts diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index ffd39dfa9..9edea4fea 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -15,6 +15,7 @@ import ( "path/filepath" "regexp" "sort" + "strconv" "strings" "syscall" "time" @@ -42,6 +43,9 @@ const ( // slirp4netnsDNS is the IP for the built-in DNS server in the slirp network slirp4netnsDNS = "10.0.2.3" + + // slirp4netnsMTU the default MTU override + slirp4netnsMTU = 65520 ) // Get an OCICNI network config @@ -282,6 +286,7 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error { enableIPv6 := false outboundAddr := "" outboundAddr6 := "" + mtu := slirp4netnsMTU if ctr.config.NetworkOptions != nil { slirpOptions = append(slirpOptions, ctr.config.NetworkOptions["slirp4netns"]...) @@ -345,6 +350,11 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error { } } outboundAddr6 = value + case "mtu": + mtu, err = strconv.Atoi(value) + if mtu < 68 || err != nil { + return errors.Errorf("invalid mtu %q", value) + } default: return errors.Errorf("unknown option for slirp4netns: %q", o) } @@ -358,8 +368,8 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error { if disableHostLoopback && slirpFeatures.HasDisableHostLoopback { cmdArgs = append(cmdArgs, "--disable-host-loopback") } - if slirpFeatures.HasMTU { - cmdArgs = append(cmdArgs, "--mtu", "65520") + if mtu > -1 && slirpFeatures.HasMTU { + cmdArgs = append(cmdArgs, fmt.Sprintf("--mtu=%d", mtu)) } if !noPivotRoot && slirpFeatures.HasEnableSandbox { cmdArgs = append(cmdArgs, "--enable-sandbox") @@ -1158,7 +1168,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro // update network status if container is not running networkStatus := c.state.NetworkStatus // clip out the index of the network - tmpNetworkStatus := make([]*cnitypes.Result, len(networkStatus)-1) + tmpNetworkStatus := make([]*cnitypes.Result, 0, len(networkStatus)-1) for k, v := range networkStatus { if index != k { tmpNetworkStatus = append(tmpNetworkStatus, v) diff --git a/libpod/options.go b/libpod/options.go index c7bac7e1f..20f62ee37 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -2190,13 +2190,37 @@ func WithPodNetworks(networks []string) PodCreateOption { } } +// WithPodNoNetwork tells the pod to disable external networking. +func WithPodNoNetwork() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot disable pod networking as no infra container is being created") + } + + if len(pod.config.InfraContainer.PortBindings) > 0 || + pod.config.InfraContainer.StaticIP != nil || + pod.config.InfraContainer.StaticMAC != nil || + len(pod.config.InfraContainer.Networks) > 0 || + pod.config.InfraContainer.HostNetwork { + return errors.Wrapf(define.ErrInvalidArg, "cannot disable pod network if network-related configuration is specified") + } + + pod.config.InfraContainer.NoNetwork = true + + return nil + } +} + // WithPodHostNetwork tells the pod to use the host's network namespace. func WithPodHostNetwork() PodCreateOption { return func(pod *Pod) error { if pod.valid { return define.ErrPodFinalized } - if !pod.config.InfraContainer.HasInfraContainer { return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod host networking as no infra container is being created") } @@ -2204,7 +2228,8 @@ func WithPodHostNetwork() PodCreateOption { if len(pod.config.InfraContainer.PortBindings) > 0 || pod.config.InfraContainer.StaticIP != nil || pod.config.InfraContainer.StaticMAC != nil || - len(pod.config.InfraContainer.Networks) > 0 { + len(pod.config.InfraContainer.Networks) > 0 || + pod.config.InfraContainer.NoNetwork { return errors.Wrapf(define.ErrInvalidArg, "cannot set host network if network-related configuration is specified") } diff --git a/libpod/pod.go b/libpod/pod.go index c8f62ca18..784c2cf5e 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -93,6 +93,7 @@ type podState struct { type InfraContainerConfig struct { ConmonPidFile string `json:"conmonPidFile"` HasInfraContainer bool `json:"makeInfraContainer"` + NoNetwork bool `json:"noNetwork,omitempty"` HostNetwork bool `json:"infraHostNetwork,omitempty"` PortBindings []ocicni.PortMapping `json:"infraPortBindings"` StaticIP net.IP `json:"staticIP,omitempty"` diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index dd957527d..564851f4e 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -94,8 +94,16 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm } } - // Since user namespace sharing is not implemented, we only need to check if it's rootless - if !p.config.InfraContainer.HostNetwork { + switch { + case p.config.InfraContainer.HostNetwork: + if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { + return nil, errors.Wrapf(err, "error removing network namespace from pod %s infra container", p.ID()) + } + case p.config.InfraContainer.NoNetwork: + // Do nothing - we have a network namespace by default, + // but should not configure slirp. + default: + // Since user namespace sharing is not implemented, we only need to check if it's rootless netmode := "bridge" if isRootless || p.config.InfraContainer.Slirp4netns { netmode = "slirp4netns" @@ -106,8 +114,6 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawIm // PostConfigureNetNS should not be set since user namespace sharing is not implemented // and rootless networking no longer supports post configuration setup options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, false, netmode, p.config.InfraContainer.Networks)) - } else if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { - return nil, errors.Wrapf(err, "error removing network namespace from pod %s infra container", p.ID()) } // For each option in InfraContainerConfig - if set, pass into diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 5c5586323..a8f850823 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -20,6 +20,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" + "github.com/docker/go-units" "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/pkg/errors" @@ -263,6 +264,7 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error sizeRootFs int64 sizeRW int64 state define.ContainerStatus + status string ) if state, err = l.State(); err != nil { @@ -273,6 +275,35 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error stateStr = "created" } + if state == define.ContainerStateConfigured || state == define.ContainerStateCreated { + status = "Created" + } else if state == define.ContainerStateStopped || state == define.ContainerStateExited { + exitCode, _, err := l.ExitCode() + if err != nil { + return nil, err + } + finishedTime, err := l.FinishedTime() + if err != nil { + return nil, err + } + status = fmt.Sprintf("Exited (%d) %s ago", exitCode, units.HumanDuration(time.Since(finishedTime))) + } else if state == define.ContainerStateRunning || state == define.ContainerStatePaused { + startedTime, err := l.StartedTime() + if err != nil { + return nil, err + } + status = fmt.Sprintf("Up %s", units.HumanDuration(time.Since(startedTime))) + if state == define.ContainerStatePaused { + status += " (Paused)" + } + } else if state == define.ContainerStateRemoving { + status = "Removal In Progress" + } else if state == define.ContainerStateStopping { + status = "Stopping" + } else { + status = "Unknown" + } + if sz { if sizeRW, err = l.RWSize(); err != nil { return nil, err @@ -294,7 +325,7 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error SizeRootFs: sizeRootFs, Labels: l.Labels(), State: stateStr, - Status: "", + Status: status, HostConfig: struct { NetworkMode string `json:",omitempty"` }{ diff --git a/pkg/api/handlers/compat/resize.go b/pkg/api/handlers/compat/resize.go index cc8c6ef0a..a769ae1b5 100644 --- a/pkg/api/handlers/compat/resize.go +++ b/pkg/api/handlers/compat/resize.go @@ -84,5 +84,5 @@ func ResizeTTY(w http.ResponseWriter, r *http.Request) { // reasons. status = http.StatusCreated } - utils.WriteResponse(w, status, "") + w.WriteHeader(status) } diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 2409d3a20..2c35dd191 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -139,19 +139,20 @@ func PodStop(w http.ResponseWriter, r *http.Request) { logrus.Errorf("Error cleaning up pod %s container %s: %v", pod.ID(), id, err) } } - var errs []error //nolint + + report := entities.PodStopReport{Id: pod.ID()} for id, err := range responses { - errs = append(errs, errors.Wrapf(err, "error stopping container %s", id)) + report.Errs = append(report.Errs, errors.Wrapf(err, "error stopping container %s", id)) } - report := entities.PodStopReport{ - Errs: errs, - Id: pod.ID(), + + code := http.StatusOK + if len(report.Errs) > 0 { + code = http.StatusConflict } - utils.WriteResponse(w, http.StatusOK, report) + utils.WriteResponse(w, code, report) } func PodStart(w http.ResponseWriter, r *http.Request) { - var errs []error //nolint runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -168,19 +169,23 @@ func PodStart(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusNotModified, "") return } + responses, err := pod.Start(r.Context()) if err != nil && errors.Cause(err) != define.ErrPodPartialFail { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + utils.Error(w, "Something went wrong", http.StatusConflict, err) return } + + report := entities.PodStartReport{Id: pod.ID()} for id, err := range responses { - errs = append(errs, errors.Wrapf(err, "error starting container %s", id)) + report.Errs = append(report.Errs, errors.Wrapf(err, "error starting container "+id)) } - report := entities.PodStartReport{ - Errs: errs, - Id: pod.ID(), + + code := http.StatusOK + if len(report.Errs) > 0 { + code = http.StatusConflict } - utils.WriteResponse(w, http.StatusOK, report) + utils.WriteResponse(w, code, report) } func PodDelete(w http.ResponseWriter, r *http.Request) { @@ -209,14 +214,11 @@ func PodDelete(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } - report := entities.PodRmReport{ - Id: pod.ID(), - } + report := entities.PodRmReport{Id: pod.ID()} utils.WriteResponse(w, http.StatusOK, report) } func PodRestart(w http.ResponseWriter, r *http.Request) { - var errs []error //nolint runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -229,14 +231,17 @@ func PodRestart(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } + + report := entities.PodRestartReport{Id: pod.ID()} for id, err := range responses { - errs = append(errs, errors.Wrapf(err, "error restarting container %s", id)) + report.Errs = append(report.Errs, errors.Wrapf(err, "error restarting container %s", id)) } - report := entities.PodRestartReport{ - Errs: errs, - Id: pod.ID(), + + code := http.StatusOK + if len(report.Errs) > 0 { + code = http.StatusConflict } - utils.WriteResponse(w, http.StatusOK, report) + utils.WriteResponse(w, code, report) } func PodPrune(w http.ResponseWriter, r *http.Request) { @@ -267,7 +272,6 @@ func PodPruneHelper(r *http.Request) ([]*entities.PodPruneReport, error) { } func PodPause(w http.ResponseWriter, r *http.Request) { - var errs []error //nolint runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -280,18 +284,20 @@ func PodPause(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } + + report := entities.PodPauseReport{Id: pod.ID()} for id, v := range responses { - errs = append(errs, errors.Wrapf(v, "error pausing container %s", id)) + report.Errs = append(report.Errs, errors.Wrapf(v, "error pausing container %s", id)) } - report := entities.PodPauseReport{ - Errs: errs, - Id: pod.ID(), + + code := http.StatusOK + if len(report.Errs) > 0 { + code = http.StatusConflict } - utils.WriteResponse(w, http.StatusOK, report) + utils.WriteResponse(w, code, report) } func PodUnpause(w http.ResponseWriter, r *http.Request) { - var errs []error //nolint runtime := r.Context().Value("runtime").(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -304,14 +310,17 @@ func PodUnpause(w http.ResponseWriter, r *http.Request) { utils.Error(w, "failed to pause pod", http.StatusInternalServerError, err) return } + + report := entities.PodUnpauseReport{Id: pod.ID()} for id, v := range responses { - errs = append(errs, errors.Wrapf(v, "error unpausing container %s", id)) + report.Errs = append(report.Errs, errors.Wrapf(v, "error unpausing container %s", id)) } - report := entities.PodUnpauseReport{ - Errs: errs, - Id: pod.ID(), + + code := http.StatusOK + if len(report.Errs) > 0 { + code = http.StatusConflict } - utils.WriteResponse(w, http.StatusOK, &report) + utils.WriteResponse(w, code, &report) } func PodTop(w http.ResponseWriter, r *http.Request) { @@ -361,7 +370,6 @@ func PodKill(w http.ResponseWriter, r *http.Request) { runtime = r.Context().Value("runtime").(*libpod.Runtime) decoder = r.Context().Value("decoder").(*schema.Decoder) signal = "SIGKILL" - errs []error //nolint ) query := struct { Signal string `schema:"signal"` @@ -413,16 +421,18 @@ func PodKill(w http.ResponseWriter, r *http.Request) { return } + report := &entities.PodKillReport{Id: pod.ID()} for _, v := range responses { if v != nil { - errs = append(errs, v) + report.Errs = append(report.Errs, v) } } - report := &entities.PodKillReport{ - Errs: errs, - Id: pod.ID(), + + code := http.StatusOK + if len(report.Errs) > 0 { + code = http.StatusConflict } - utils.WriteResponse(w, http.StatusOK, report) + utils.WriteResponse(w, code, report) } func PodExists(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index 517dccad0..b3c674788 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -1,16 +1,17 @@ package utils import ( - "encoding/json" "fmt" "io" "net/http" "net/url" "os" "strings" + "unsafe" "github.com/blang/semver" "github.com/gorilla/mux" + jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -43,8 +44,8 @@ var ( // clients to shop for the Version they wish to support APIVersion = map[VersionTree]map[VersionLevel]semver.Version{ LibpodTree: { - CurrentAPIVersion: semver.MustParse("2.0.0"), - MinimalAPIVersion: semver.MustParse("2.0.0"), + CurrentAPIVersion: semver.MustParse("3.0.0"), + MinimalAPIVersion: semver.MustParse("3.0.0"), }, CompatTree: { CurrentAPIVersion: semver.MustParse("1.40.0"), @@ -144,6 +145,50 @@ func WriteResponse(w http.ResponseWriter, code int, value interface{}) { } } +func init() { + jsoniter.RegisterTypeEncoderFunc("error", MarshalErrorJSON, MarshalErrorJSONIsEmpty) + jsoniter.RegisterTypeEncoderFunc("[]error", MarshalErrorSliceJSON, MarshalErrorSliceJSONIsEmpty) +} + +var json = jsoniter.ConfigCompatibleWithStandardLibrary + +// MarshalErrorJSON writes error to stream as string +func MarshalErrorJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { + p := *((*error)(ptr)) + if p == nil { + stream.WriteNil() + } else { + stream.WriteString(p.Error()) + } +} + +// MarshalErrorSliceJSON writes []error to stream as []string JSON blob +func MarshalErrorSliceJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { + a := *((*[]error)(ptr)) + switch { + case len(a) == 0: + stream.WriteNil() + default: + stream.WriteArrayStart() + for i, e := range a { + if i > 0 { + stream.WriteMore() + } + stream.WriteString(e.Error()) + } + stream.WriteArrayEnd() + } +} + +func MarshalErrorJSONIsEmpty(_ unsafe.Pointer) bool { + return false +} + +func MarshalErrorSliceJSONIsEmpty(_ unsafe.Pointer) bool { + return false +} + +// WriteJSON writes an interface value encoded as JSON to w func WriteJSON(w http.ResponseWriter, code int, value interface{}) { // FIXME: we don't need to write the header in all/some circumstances. w.Header().Set("Content-Type", "application/json") diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go index 105de4ee7..4873eb926 100644 --- a/pkg/api/server/register_pods.go +++ b/pkg/api/server/register_pods.go @@ -43,6 +43,11 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/definitions/IdResponse" // 400: // $ref: "#/responses/BadParamError" + // 409: + // description: status conflict + // schema: + // type: string + // description: message describing error // 500: // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/create"), s.APIHandler(libpod.PodCreate)).Methods(http.MethodPost) @@ -149,7 +154,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // 404: // $ref: "#/responses/NoSuchPod" // 409: - // $ref: "#/responses/ConflictError" + // $ref: "#/responses/PodKillReport" // 500: // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name}/kill"), s.APIHandler(libpod.PodKill)).Methods(http.MethodPost) @@ -170,6 +175,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: '#/responses/PodPauseReport' // 404: // $ref: "#/responses/NoSuchPod" + // 409: + // $ref: '#/responses/PodPauseReport' // 500: // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name}/pause"), s.APIHandler(libpod.PodPause)).Methods(http.MethodPost) @@ -189,6 +196,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: '#/responses/PodRestartReport' // 404: // $ref: "#/responses/NoSuchPod" + // 409: + // $ref: "#/responses/PodRestartReport" // 500: // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name}/restart"), s.APIHandler(libpod.PodRestart)).Methods(http.MethodPost) @@ -210,6 +219,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/PodAlreadyStartedError" // 404: // $ref: "#/responses/NoSuchPod" + // 409: + // $ref: '#/responses/PodStartReport' // 500: // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name}/start"), s.APIHandler(libpod.PodStart)).Methods(http.MethodPost) @@ -237,6 +248,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/BadParamError" // 404: // $ref: "#/responses/NoSuchPod" + // 409: + // $ref: "#/responses/PodStopReport" // 500: // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name}/stop"), s.APIHandler(libpod.PodStop)).Methods(http.MethodPost) @@ -256,6 +269,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: '#/responses/PodUnpauseReport' // 404: // $ref: "#/responses/NoSuchPod" + // 409: + // $ref: '#/responses/PodUnpauseReport' // 500: // $ref: "#/responses/InternalError" r.Handle(VersionedPath("/libpod/pods/{name}/unpause"), s.APIHandler(libpod.PodUnpause)).Methods(http.MethodPost) diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index 69ae7a32f..586cdec8c 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -348,7 +348,7 @@ func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, i resizeErr = ResizeContainerTTY(ctx, id, new(ResizeTTYOptions).WithHeight(h).WithWidth(w)) } if resizeErr != nil { - logrus.Warnf("failed to resize TTY: %v", err) + logrus.Warnf("failed to resize TTY: %v", resizeErr) } } } diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index c200dd01a..285fd093a 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -24,6 +24,7 @@ var ( ErrCgroupDeleted = errors.New("cgroup deleted") // ErrCgroupV1Rootless means the cgroup v1 were attempted to be used in rootless environment ErrCgroupV1Rootless = errors.New("no support for CGroups V1 in rootless environments") + ErrStatCgroup = errors.New("no cgroup available for gathering user statistics") ) // CgroupControl controls a cgroup hierarchy @@ -525,10 +526,19 @@ func (c *CgroupControl) AddPid(pid int) error { // Stat returns usage statistics for the cgroup func (c *CgroupControl) Stat() (*Metrics, error) { m := Metrics{} + found := false for _, h := range handlers { if err := h.Stat(c, &m); err != nil { - return nil, err + if !os.IsNotExist(errors.Cause(err)) { + return nil, err + } + logrus.Warningf("Failed to retrieve cgroup stats: %v", err) + continue } + found = true + } + if !found { + return nil, ErrStatCgroup } return &m, nil } diff --git a/pkg/cgroups/cgroups_test.go b/pkg/cgroups/cgroups_test.go new file mode 100644 index 000000000..54315f7be --- /dev/null +++ b/pkg/cgroups/cgroups_test.go @@ -0,0 +1,32 @@ +package cgroups + +import ( + "testing" + + "github.com/containers/podman/v2/pkg/rootless" + spec "github.com/opencontainers/runtime-spec/specs-go" +) + +func TestCreated(t *testing.T) { + // tests only works in rootless mode + if rootless.IsRootless() { + return + } + + var resources spec.LinuxResources + cgr, err := New("machine.slice", &resources) + if err != nil { + t.Error(err) + } + if err := cgr.Delete(); err != nil { + t.Error(err) + } + + cgr, err = NewSystemd("machine.slice") + if err != nil { + t.Error(err) + } + if err := cgr.Delete(); err != nil { + t.Error(err) + } +} diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index e39a700eb..98ab82259 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -3,6 +3,7 @@ package kube import ( "context" "fmt" + "net" "strings" "github.com/containers/common/pkg/parse" @@ -44,6 +45,31 @@ func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec) podPorts := getPodPorts(podYAML.Spec.Containers) p.PortMappings = podPorts + if dnsConfig := podYAML.Spec.DNSConfig; dnsConfig != nil { + // name servers + if dnsServers := dnsConfig.Nameservers; len(dnsServers) > 0 { + servers := make([]net.IP, 0) + for _, server := range dnsServers { + servers = append(servers, net.ParseIP(server)) + } + p.DNSServer = servers + } + // search domans + if domains := dnsConfig.Searches; len(domains) > 0 { + p.DNSSearch = domains + } + // dns options + if options := dnsConfig.Options; len(options) > 0 { + dnsOptions := make([]string, 0) + for _, opts := range options { + d := opts.Name + if opts.Value != nil { + d += ":" + *opts.Value + } + dnsOptions = append(dnsOptions, d) + } + } + } return p, nil } @@ -256,16 +282,16 @@ func setupSecurityContext(s *specgen.SpecGenerator, containerYAML v1.Container) if seopt := containerYAML.SecurityContext.SELinuxOptions; seopt != nil { if seopt.User != "" { - s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("role:%s", seopt.User)) + s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("user:%s", seopt.User)) } if seopt.Role != "" { s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("role:%s", seopt.Role)) } if seopt.Type != "" { - s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("role:%s", seopt.Type)) + s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("type:%s", seopt.Type)) } if seopt.Level != "" { - s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("role:%s", seopt.Level)) + s.SelinuxOpts = append(s.SelinuxOpts, fmt.Sprintf("level:%s", seopt.Level)) } } if caps := containerYAML.SecurityContext.Capabilities; caps != nil { diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 43caf0fe9..645bf7a47 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -102,6 +102,9 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod case specgen.Slirp: logrus.Debugf("Pod will use slirp4netns") options = append(options, libpod.WithPodSlirp4netns(p.NetworkOptions)) + case specgen.NoNetwork: + logrus.Debugf("Pod will not use networking") + options = append(options, libpod.WithPodNoNetwork()) default: return nil, errors.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode) } diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index de6751a17..e9902319c 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -60,13 +60,21 @@ func filterPodFlags(command []string) []string { return processed } -// quoteArguments makes sure that all arguments with at least one whitespace +// escapeSystemdArguments makes sure that all arguments with at least one whitespace // are quoted to make sure those are interpreted as one argument instead of -// multiple ones. -func quoteArguments(command []string) []string { +// multiple ones. Also make sure to escape all characters which have a special +// meaning to systemd -> $,% and \ +// see: https://www.freedesktop.org/software/systemd/man/systemd.service.html#Command%20lines +func escapeSystemdArguments(command []string) []string { for i := range command { + command[i] = strings.ReplaceAll(command[i], "$", "$$") + command[i] = strings.ReplaceAll(command[i], "%", "%%") if strings.ContainsAny(command[i], " \t") { command[i] = strconv.Quote(command[i]) + } else if strings.Contains(command[i], `\`) { + // strconv.Quote also escapes backslashes so + // we should replace only if strconv.Quote was not used + command[i] = strings.ReplaceAll(command[i], `\`, `\\`) } } return command diff --git a/pkg/systemd/generate/common_test.go b/pkg/systemd/generate/common_test.go index d0ec5637c..a0691d1ad 100644 --- a/pkg/systemd/generate/common_test.go +++ b/pkg/systemd/generate/common_test.go @@ -29,7 +29,7 @@ func TestFilterPodFlags(t *testing.T) { } } -func TestQuoteArguments(t *testing.T) { +func TestEscapeSystemdArguments(t *testing.T) { tests := []struct { input []string output []string @@ -46,10 +46,46 @@ func TestQuoteArguments(t *testing.T) { []string{"foo", "bar=\"arg with\ttab\""}, []string{"foo", "\"bar=\\\"arg with\\ttab\\\"\""}, }, + { + []string{"$"}, + []string{"$$"}, + }, + { + []string{"foo", "command with dollar sign $"}, + []string{"foo", "\"command with dollar sign $$\""}, + }, + { + []string{"foo", "command with two dollar signs $$"}, + []string{"foo", "\"command with two dollar signs $$$$\""}, + }, + { + []string{"%"}, + []string{"%%"}, + }, + { + []string{"foo", "command with percent sign %"}, + []string{"foo", "\"command with percent sign %%\""}, + }, + { + []string{"foo", "command with two percent signs %%"}, + []string{"foo", "\"command with two percent signs %%%%\""}, + }, + { + []string{`\`}, + []string{`\\`}, + }, + { + []string{"foo", `command with backslash \`}, + []string{"foo", `"command with backslash \\"`}, + }, + { + []string{"foo", `command with two backslashs \\`}, + []string{"foo", `"command with two backslashs \\\\"`}, + }, } for _, test := range tests { - quoted := quoteArguments(test.input) + quoted := escapeSystemdArguments(test.input) assert.Equal(t, test.output, quoted) } } diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index 5f52b0a77..abe159812 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -204,7 +204,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst startCommand := []string{info.Executable} if index > 2 { // include root flags - info.RootFlags = strings.Join(quoteArguments(info.CreateCommand[1:index-1]), " ") + info.RootFlags = strings.Join(escapeSystemdArguments(info.CreateCommand[1:index-1]), " ") startCommand = append(startCommand, info.CreateCommand[1:index-1]...) } startCommand = append(startCommand, @@ -279,7 +279,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst } } startCommand = append(startCommand, remainingCmd...) - startCommand = quoteArguments(startCommand) + startCommand = escapeSystemdArguments(startCommand) info.ExecStartPre = "/bin/rm -f {{{{.PIDFile}}}} {{{{.ContainerIDFile}}}}" info.ExecStart = strings.Join(startCommand, " ") diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index 96d95644b..be14e4c28 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -352,6 +352,30 @@ Type=forking [Install] WantedBy=multi-user.target default.target ` + + goodNewWithSpecialChars := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name test awesome-image:latest sh -c "kill $$$$ && echo %%\\" +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 +ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` tests := []struct { name string info containerInfo @@ -647,6 +671,22 @@ WantedBy=multi-user.target default.target true, false, }, + {"good with special chars", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "create", "--name", "test", "awesome-image:latest", "sh", "-c", "kill $$ && echo %\\"}, + EnvVariable: EnvVariable, + }, + goodNewWithSpecialChars, + true, + false, + }, } for _, tt := range tests { test := tt diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index c7e3aa955..d6ede19af 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -269,7 +269,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) return "", errors.Errorf("pod does not appear to be created via `podman pod create`: %v", info.CreateCommand) } podRootArgs = info.CreateCommand[1 : podCreateIndex-1] - info.RootFlags = strings.Join(quoteArguments(podRootArgs), " ") + info.RootFlags = strings.Join(escapeSystemdArguments(podRootArgs), " ") podCreateArgs = filterPodFlags(info.CreateCommand[podCreateIndex+1:]) } // We're hard-coding the first five arguments and append the @@ -306,7 +306,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) } startCommand = append(startCommand, podCreateArgs...) - startCommand = quoteArguments(startCommand) + startCommand = escapeSystemdArguments(startCommand) info.ExecStartPre1 = "/bin/rm -f {{{{.PIDFile}}}} {{{{.PodIDFile}}}}" info.ExecStartPre2 = strings.Join(startCommand, " ") diff --git a/pkg/terminal/console_windows.go b/pkg/terminal/console_windows.go index c7691857c..08e66cb3a 100644 --- a/pkg/terminal/console_windows.go +++ b/pkg/terminal/console_windows.go @@ -30,7 +30,7 @@ func setConsoleMode(handle windows.Handle, flags uint32) error { if err := windows.SetConsoleMode(handle, mode|flags); err != nil { // In similar code, it is not considered an error if we cannot set the // console mode. Following same line of thinking here. - logrus.WithError(err).Error("Failed to set console mode for cli") + logrus.WithError(err).Debug("Failed to set console mode for cli") } return nil diff --git a/pkg/util/mountOpts.go b/pkg/util/mountOpts.go index 580aaf4f2..b3a38f286 100644 --- a/pkg/util/mountOpts.go +++ b/pkg/util/mountOpts.go @@ -86,6 +86,10 @@ func ProcessOptions(options []string, isTmpfs bool, sourcePath string) ([]string return nil, errors.Wrapf(ErrDupeMntOption, "the 'tmpcopyup' or 'notmpcopyup' option can only be set once") } foundCopyUp = true + case "consistency": + // Often used on MACs and mistakenly on Linux platforms. + // Since Docker ignores this option so shall we. + continue case "notmpcopyup": if !isTmpfs { return nil, errors.Wrapf(ErrBadMntOption, "the 'notmpcopyup' option is only allowed with tmpfs mounts") diff --git a/test/apiv2/01-basic.at b/test/apiv2/01-basic.at index f550d5fc3..1ddf49c6f 100644 --- a/test/apiv2/01-basic.at +++ b/test/apiv2/01-basic.at @@ -18,8 +18,8 @@ t HEAD libpod/_ping 200 for i in /version version; do t GET $i 200 \ .Components[0].Name="Podman Engine" \ - .Components[0].Details.APIVersion=2.0.0 \ - .Components[0].Details.MinAPIVersion=2.0.0 \ + .Components[0].Details.APIVersion=3.0.0 \ + .Components[0].Details.MinAPIVersion=3.0.0 \ .Components[0].Details.Os=linux \ .ApiVersion=1.40 \ .MinAPIVersion=1.24 \ diff --git a/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py index cc66dd5af..520aa161b 100644 --- a/test/apiv2/rest_api/test_rest_v2_0_0.py +++ b/test/apiv2/rest_api/test_rest_v2_0_0.py @@ -1,7 +1,6 @@ import json import os import random -import shutil import string import subprocess import sys @@ -163,7 +162,7 @@ class TestApi(unittest.TestCase): r = requests.post(_url(ctnr("/containers/{}/resize?h=43&w=80"))) self.assertIn(r.status_code, (200, 409), r.text) if r.status_code == 200: - self.assertIsNone(r.text) + self.assertEqual(r.text, "", r.text) def test_attach_containers(self): self.skipTest("FIXME: Test timeouts") @@ -586,6 +585,117 @@ class TestApi(unittest.TestCase): # self.assertIn(img["Id"], prune_payload["ImagesDeleted"][1]["Deleted"]) self.assertIsNotNone(prune_payload["ImagesDeleted"][1]["Deleted"]) + def test_status_compat(self): + r = requests.post( + PODMAN_URL + "/v1.40/containers/create?name=topcontainer", + json={"Cmd": ["top"], "Image": "alpine:latest"}, + ) + self.assertEqual(r.status_code, 201, r.text) + payload = json.loads(r.text) + container_id = payload["Id"] + self.assertIsNotNone(container_id) + + r = requests.get( + PODMAN_URL + "/v1.40/containers/json", + params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, + ) + self.assertEqual(r.status_code, 200, r.text) + payload = json.loads(r.text) + self.assertEqual(payload[0]["Status"], "Created") + + r = requests.post(PODMAN_URL + f"/v1.40/containers/{container_id}/start") + self.assertEqual(r.status_code, 204, r.text) + + r = requests.get( + PODMAN_URL + "/v1.40/containers/json", + params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, + ) + self.assertEqual(r.status_code, 200, r.text) + payload = json.loads(r.text) + self.assertTrue(str(payload[0]["Status"]).startswith("Up")) + + r = requests.post(PODMAN_URL + f"/v1.40/containers/{container_id}/pause") + self.assertEqual(r.status_code, 204, r.text) + + r = requests.get( + PODMAN_URL + "/v1.40/containers/json", + params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, + ) + self.assertEqual(r.status_code, 200, r.text) + payload = json.loads(r.text) + self.assertTrue(str(payload[0]["Status"]).startswith("Up")) + self.assertTrue(str(payload[0]["Status"]).endswith("(Paused)")) + + r = requests.post(PODMAN_URL + f"/v1.40/containers/{container_id}/unpause") + self.assertEqual(r.status_code, 204, r.text) + r = requests.post(PODMAN_URL + f"/v1.40/containers/{container_id}/stop") + self.assertEqual(r.status_code, 204, r.text) + + r = requests.get( + PODMAN_URL + "/v1.40/containers/json", + params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, + ) + self.assertEqual(r.status_code, 200, r.text) + payload = json.loads(r.text) + self.assertTrue(str(payload[0]["Status"]).startswith("Exited")) + + r = requests.delete(PODMAN_URL + f"/v1.40/containers/{container_id}") + self.assertEqual(r.status_code, 204, r.text) + + def test_pod_start_conflict(self): + """Verify issue #8865""" + + pod_name = list() + pod_name.append("Pod_" + "".join(random.choice(string.ascii_letters) for i in range(10))) + pod_name.append("Pod_" + "".join(random.choice(string.ascii_letters) for i in range(10))) + + r = requests.post( + _url("/pods/create"), + json={ + "name": pod_name[0], + "no_infra": False, + "portmappings": [{"host_ip": "127.0.0.1", "host_port": 8889, "container_port": 89}], + }, + ) + self.assertEqual(r.status_code, 201, r.text) + r = requests.post( + _url("/containers/create"), + json={ + "pod": pod_name[0], + "image": "docker.io/alpine:latest", + "command": ["top"], + }, + ) + self.assertEqual(r.status_code, 201, r.text) + + r = requests.post( + _url("/pods/create"), + json={ + "name": pod_name[1], + "no_infra": False, + "portmappings": [{"host_ip": "127.0.0.1", "host_port": 8889, "container_port": 89}], + }, + ) + self.assertEqual(r.status_code, 201, r.text) + r = requests.post( + _url("/containers/create"), + json={ + "pod": pod_name[1], + "image": "docker.io/alpine:latest", + "command": ["top"], + }, + ) + self.assertEqual(r.status_code, 201, r.text) + + r = requests.post(_url(f"/pods/{pod_name[0]}/start")) + self.assertEqual(r.status_code, 200, r.text) + + r = requests.post(_url(f"/pods/{pod_name[1]}/start")) + self.assertEqual(r.status_code, 409, r.text) + + start = json.loads(r.text) + self.assertGreater(len(start["Errs"]), 0, r.text) + if __name__ == "__main__": unittest.main() diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go index 59b52bff7..41ad9640c 100644 --- a/test/e2e/common_test.go +++ b/test/e2e/common_test.go @@ -514,27 +514,15 @@ func (s *PodmanSessionIntegration) InspectPodArrToJSON() []define.InspectPodData // CreatePod creates a pod with no infra container // it optionally takes a pod name -func (p *PodmanTestIntegration) CreatePod(name string) (*PodmanSessionIntegration, int, string) { - var podmanArgs = []string{"pod", "create", "--infra=false", "--share", ""} - if name != "" { - podmanArgs = append(podmanArgs, "--name", name) +func (p *PodmanTestIntegration) CreatePod(options map[string][]string) (*PodmanSessionIntegration, int, string) { + var args = []string{"pod", "create", "--infra=false", "--share", ""} + for k, values := range options { + for _, v := range values { + args = append(args, k+"="+v) + } } - session := p.Podman(podmanArgs) - session.WaitWithDefaultTimeout() - return session, session.ExitCode(), session.OutputToString() -} -// CreatePod creates a pod with no infra container and some labels. -// it optionally takes a pod name -func (p *PodmanTestIntegration) CreatePodWithLabels(name string, labels map[string]string) (*PodmanSessionIntegration, int, string) { - var podmanArgs = []string{"pod", "create", "--infra=false", "--share", ""} - if name != "" { - podmanArgs = append(podmanArgs, "--name", name) - } - for labelKey, labelValue := range labels { - podmanArgs = append(podmanArgs, "--label", fmt.Sprintf("%s=%s", labelKey, labelValue)) - } - session := p.Podman(podmanArgs) + session := p.Podman(args) session.WaitWithDefaultTimeout() return session, session.ExitCode(), session.OutputToString() } diff --git a/test/e2e/exists_test.go b/test/e2e/exists_test.go index 480bfe5fc..306e8c250 100644 --- a/test/e2e/exists_test.go +++ b/test/e2e/exists_test.go @@ -83,7 +83,7 @@ var _ = Describe("Podman image|container exists", func() { }) It("podman pod exists in local storage by name", func() { - setup, _, _ := podmanTest.CreatePod("foobar") + setup, _, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar"}}) setup.WaitWithDefaultTimeout() Expect(setup).Should(Exit(0)) @@ -92,7 +92,7 @@ var _ = Describe("Podman image|container exists", func() { Expect(session).Should(Exit(0)) }) It("podman pod exists in local storage by container ID", func() { - setup, _, podID := podmanTest.CreatePod("") + setup, _, podID := podmanTest.CreatePod(nil) setup.WaitWithDefaultTimeout() Expect(setup).Should(Exit(0)) @@ -101,7 +101,7 @@ var _ = Describe("Podman image|container exists", func() { Expect(session).Should(Exit(0)) }) It("podman pod exists in local storage by short container ID", func() { - setup, _, podID := podmanTest.CreatePod("") + setup, _, podID := podmanTest.CreatePod(nil) setup.WaitWithDefaultTimeout() Expect(setup).Should(Exit(0)) diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 239817e6c..bcfab0f68 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -1,6 +1,7 @@ package integration import ( + "io/ioutil" "os" "path/filepath" "strconv" @@ -60,6 +61,7 @@ var _ = Describe("Podman generate kube", func() { pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) + Expect(pod.Spec.HostNetwork).To(Equal(false)) numContainers := 0 for range pod.Spec.Containers { @@ -130,7 +132,7 @@ var _ = Describe("Podman generate kube", func() { }) It("podman generate kube on pod", func() { - _, rc, _ := podmanTest.CreatePod("toppod") + _, rc, _ := podmanTest.CreatePod(map[string][]string{"--name": {"toppod"}}) Expect(rc).To(Equal(0)) session := podmanTest.RunTopContainerInPod("topcontainer", "toppod") @@ -144,6 +146,7 @@ var _ = Describe("Podman generate kube", func() { pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) + Expect(pod.Spec.HostNetwork).To(Equal(false)) numContainers := 0 for range pod.Spec.Containers { @@ -152,6 +155,40 @@ var _ = Describe("Podman generate kube", func() { Expect(numContainers).To(Equal(1)) }) + It("podman generate kube on pod with host network", func() { + podSession := podmanTest.Podman([]string{"pod", "create", "--name", "testHostNetwork", "--network", "host"}) + podSession.WaitWithDefaultTimeout() + Expect(podSession.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"create", "--name", "topcontainer", "--pod", "testHostNetwork", "--network", "host", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", "testHostNetwork"}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + Expect(pod.Spec.HostNetwork).To(Equal(true)) + }) + + It("podman generate kube on container with host network", func() { + session := podmanTest.RunTopContainerWithArgs("topcontainer", []string{"--network", "host"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", "topcontainer"}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + Expect(pod.Spec.HostNetwork).To(Equal(true)) + }) + It("podman generate kube on pod with hostAliases", func() { podName := "testHost" testIP := "127.0.0.1" @@ -185,7 +222,7 @@ var _ = Describe("Podman generate kube", func() { }) It("podman generate service kube on pod", func() { - _, rc, _ := podmanTest.CreatePod("toppod") + _, rc, _ := podmanTest.CreatePod(map[string][]string{"--name": {"toppod"}}) Expect(rc).To(Equal(0)) session := podmanTest.RunTopContainerInPod("topcontainer", "toppod") @@ -337,7 +374,7 @@ var _ = Describe("Podman generate kube", func() { It("podman generate and reimport kube on pod", func() { podName := "toppod" - _, rc, _ := podmanTest.CreatePod(podName) + _, rc, _ := podmanTest.CreatePod(map[string][]string{"--name": {podName}}) Expect(rc).To(Equal(0)) session := podmanTest.Podman([]string{"create", "--pod", podName, "--name", "test1", ALPINE, "top"}) @@ -376,7 +413,7 @@ var _ = Describe("Podman generate kube", func() { It("podman generate with user and reimport kube on pod", func() { podName := "toppod" - _, rc, _ := podmanTest.CreatePod(podName) + _, rc, _ := podmanTest.CreatePod(map[string][]string{"--name": {podName}}) Expect(rc).To(Equal(0)) session := podmanTest.Podman([]string{"create", "--pod", podName, "--name", "test1", "--user", "100:200", ALPINE, "top"}) @@ -540,4 +577,126 @@ var _ = Describe("Podman generate kube", func() { kube.WaitWithDefaultTimeout() Expect(kube.ExitCode()).ToNot(Equal(0)) }) + + It("podman generate kube on a container with dns options", func() { + top := podmanTest.Podman([]string{"run", "-dt", "--name", "top", "--dns", "8.8.8.8", "--dns-search", "foobar.com", "--dns-opt", "color:blue", ALPINE, "top"}) + top.WaitWithDefaultTimeout() + Expect(top.ExitCode()).To(BeZero()) + + kube := podmanTest.Podman([]string{"generate", "kube", "top"}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + Expect(StringInSlice("8.8.8.8", pod.Spec.DNSConfig.Nameservers)).To(BeTrue()) + Expect(StringInSlice("foobar.com", pod.Spec.DNSConfig.Searches)).To(BeTrue()) + Expect(len(pod.Spec.DNSConfig.Options)).To(BeNumerically(">", 0)) + Expect(pod.Spec.DNSConfig.Options[0].Name).To(Equal("color")) + Expect(*pod.Spec.DNSConfig.Options[0].Value).To(Equal("blue")) + }) + + It("podman generate kube multiple contianer dns servers and options are cumulative", func() { + top1 := podmanTest.Podman([]string{"run", "-dt", "--name", "top1", "--dns", "8.8.8.8", "--dns-search", "foobar.com", ALPINE, "top"}) + top1.WaitWithDefaultTimeout() + Expect(top1.ExitCode()).To(BeZero()) + + top2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top2", "--dns", "8.7.7.7", "--dns-search", "homer.com", ALPINE, "top"}) + top2.WaitWithDefaultTimeout() + Expect(top2.ExitCode()).To(BeZero()) + + kube := podmanTest.Podman([]string{"generate", "kube", "top1", "top2"}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + Expect(StringInSlice("8.8.8.8", pod.Spec.DNSConfig.Nameservers)).To(BeTrue()) + Expect(StringInSlice("8.7.7.7", pod.Spec.DNSConfig.Nameservers)).To(BeTrue()) + Expect(StringInSlice("foobar.com", pod.Spec.DNSConfig.Searches)).To(BeTrue()) + Expect(StringInSlice("homer.com", pod.Spec.DNSConfig.Searches)).To(BeTrue()) + }) + + It("podman generate kube on a pod with dns options", func() { + top := podmanTest.Podman([]string{"run", "--pod", "new:pod1", "-dt", "--name", "top", "--dns", "8.8.8.8", "--dns-search", "foobar.com", "--dns-opt", "color:blue", ALPINE, "top"}) + top.WaitWithDefaultTimeout() + Expect(top.ExitCode()).To(BeZero()) + + kube := podmanTest.Podman([]string{"generate", "kube", "pod1"}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + Expect(StringInSlice("8.8.8.8", pod.Spec.DNSConfig.Nameservers)).To(BeTrue()) + Expect(StringInSlice("foobar.com", pod.Spec.DNSConfig.Searches)).To(BeTrue()) + Expect(len(pod.Spec.DNSConfig.Options)).To(BeNumerically(">", 0)) + Expect(pod.Spec.DNSConfig.Options[0].Name).To(Equal("color")) + Expect(*pod.Spec.DNSConfig.Options[0].Value).To(Equal("blue")) + }) + + It("podman generate kube - set entrypoint as command", func() { + session := podmanTest.Podman([]string{"create", "--pod", "new:testpod", "--entrypoint", "/bin/sleep", ALPINE, "10s"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", "testpod"}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + // Now make sure that the container's command is set to the + // entrypoint and it's arguments to "10s". + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + containers := pod.Spec.Containers + Expect(len(containers)).To(Equal(1)) + + Expect(containers[0].Command).To(Equal([]string{"/bin/sleep"})) + Expect(containers[0].Args).To(Equal([]string{"10s"})) + }) + + It("podman generate kube - use entrypoint from image", func() { + // Build an image with an entrypoint. + containerfile := `FROM quay.io/libpod/alpine:latest +ENTRYPOINT /bin/sleep` + + targetPath, err := CreateTempDirInTempDir() + Expect(err).To(BeNil()) + containerfilePath := filepath.Join(targetPath, "Containerfile") + err = ioutil.WriteFile(containerfilePath, []byte(containerfile), 0644) + Expect(err).To(BeNil()) + + image := "generatekube:test" + session := podmanTest.Podman([]string{"build", "-f", containerfilePath, "-t", image}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"create", "--pod", "new:testpod", image, "10s"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", "testpod"}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + // Now make sure that the container's command is set to the + // entrypoint and it's arguments to "10s". + pod := new(v1.Pod) + err = yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + containers := pod.Spec.Containers + Expect(len(containers)).To(Equal(1)) + + Expect(containers[0].Command).To(Equal([]string{"/bin/sh", "-c", "/bin/sleep"})) + Expect(containers[0].Args).To(Equal([]string{"10s"})) + }) }) diff --git a/test/e2e/network_connect_disconnect_test.go b/test/e2e/network_connect_disconnect_test.go index dd94bd7ca..cc23b10c1 100644 --- a/test/e2e/network_connect_disconnect_test.go +++ b/test/e2e/network_connect_disconnect_test.go @@ -74,6 +74,11 @@ var _ = Describe("Podman network connect and disconnect", func() { dis.WaitWithDefaultTimeout() Expect(dis.ExitCode()).To(BeZero()) + inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect.OutputToString()).To(Equal("0")) + exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) exec.WaitWithDefaultTimeout() Expect(exec.ExitCode()).ToNot(BeZero()) @@ -146,6 +151,11 @@ var _ = Describe("Podman network connect and disconnect", func() { connect.WaitWithDefaultTimeout() Expect(connect.ExitCode()).To(BeZero()) + inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect.OutputToString()).To(Equal("2")) + exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth1"}) exec.WaitWithDefaultTimeout() Expect(exec.ExitCode()).To(BeZero()) @@ -167,6 +177,11 @@ var _ = Describe("Podman network connect and disconnect", func() { dis.WaitWithDefaultTimeout() Expect(dis.ExitCode()).To(BeZero()) + inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect.OutputToString()).To(Equal("2")) + start := podmanTest.Podman([]string{"start", "test"}) start.WaitWithDefaultTimeout() Expect(start.ExitCode()).To(BeZero()) @@ -202,6 +217,11 @@ var _ = Describe("Podman network connect and disconnect", func() { dis.WaitWithDefaultTimeout() Expect(dis.ExitCode()).To(BeZero()) + inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect.OutputToString()).To(Equal("1")) + start := podmanTest.Podman([]string{"start", "test"}) start.WaitWithDefaultTimeout() Expect(start.ExitCode()).To(BeZero()) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 5930462d5..2e5c72b0e 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -13,6 +13,7 @@ import ( . "github.com/containers/podman/v2/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/opencontainers/selinux/go-selinux" ) var unknownKindYaml = ` @@ -26,6 +27,49 @@ spec: hostname: unknown ` +var selinuxLabelPodYaml = ` +apiVersion: v1 +kind: Pod +metadata: + creationTimestamp: "2021-02-02T22:18:20Z" + labels: + app: label-pod + name: label-pod +spec: + containers: + - command: + - top + - -d + - "1.5" + env: + - name: PATH + value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + - name: TERM + value: xterm + - name: container + value: podman + - name: HOSTNAME + value: label-pod + image: quay.io/libpod/alpine:latest + name: test + securityContext: + allowPrivilegeEscalation: true + capabilities: + drop: + - CAP_MKNOD + - CAP_NET_RAW + - CAP_AUDIT_WRITE + privileged: false + readOnlyRootFilesystem: false + seLinuxOptions: + user: unconfined_u + role: system_r + type: spc_t + level: s0 + workingDir: / +status: {} +` + var configMapYamlTemplate = ` apiVersion: v1 kind: ConfigMap @@ -803,6 +847,24 @@ var _ = Describe("Podman play kube", func() { }) + It("podman play kube fail with custom selinux label", func() { + if !selinux.GetEnabled() { + Skip("SELinux not enabled") + } + err := writeYaml(selinuxLabelPodYaml, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube.ExitCode()).To(Equal(0)) + + inspect := podmanTest.Podman([]string{"inspect", "label-pod-test", "--format", "'{{ .ProcessLabel }}'"}) + inspect.WaitWithDefaultTimeout() + label := inspect.OutputToString() + + Expect(label).To(ContainSubstring("unconfined_u:system_r:spc_t:s0")) + }) + It("podman play kube fail with nonexistent authfile", func() { err := generateKubeYaml("pod", getPod(), kubeYaml) Expect(err).To(BeNil()) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 9c448a81e..e57712f62 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -38,7 +38,7 @@ var _ = Describe("Podman pod create", func() { }) It("podman create pod", func() { - _, ec, podID := podmanTest.CreatePod("") + _, ec, podID := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) check := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc"}) @@ -50,7 +50,7 @@ var _ = Describe("Podman pod create", func() { It("podman create pod with name", func() { name := "test" - _, ec, _ := podmanTest.CreatePod(name) + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {name}}) Expect(ec).To(Equal(0)) check := podmanTest.Podman([]string{"pod", "ps", "--no-trunc"}) @@ -61,10 +61,10 @@ var _ = Describe("Podman pod create", func() { It("podman create pod with doubled name", func() { name := "test" - _, ec, _ := podmanTest.CreatePod(name) + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {name}}) Expect(ec).To(Equal(0)) - _, ec2, _ := podmanTest.CreatePod(name) + _, ec2, _ := podmanTest.CreatePod(map[string][]string{"--name": {name}}) Expect(ec2).To(Not(Equal(0))) check := podmanTest.Podman([]string{"pod", "ps", "-q"}) @@ -78,7 +78,7 @@ var _ = Describe("Podman pod create", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec, _ := podmanTest.CreatePod(name) + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {name}}) Expect(ec).To(Not(Equal(0))) check := podmanTest.Podman([]string{"pod", "ps", "-q"}) @@ -476,4 +476,29 @@ entrypoint ["/fromimage"] Expect(status3.ExitCode()).To(Equal(0)) Expect(strings.Contains(status3.OutputToString(), "Degraded")).To(BeTrue()) }) + + It("podman create with unsupported network options", func() { + podCreate := podmanTest.Podman([]string{"pod", "create", "--network", "container:doesnotmatter"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate.ErrorToString()).To(ContainSubstring("pods presently do not support network mode container")) + + podCreate = podmanTest.Podman([]string{"pod", "create", "--network", "ns:/does/not/matter"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate.ErrorToString()).To(ContainSubstring("pods presently do not support network mode path")) + }) + + It("podman pod create with --net=none", func() { + podName := "testPod" + podCreate := podmanTest.Podman([]string{"pod", "create", "--network", "none", "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(0)) + + session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "ip", "-o", "-4", "addr"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("inet 127.0.0.1/8 scope host lo")) + Expect(len(session.OutputToStringArray())).To(Equal(1)) + }) }) diff --git a/test/e2e/pod_inspect_test.go b/test/e2e/pod_inspect_test.go index fd9589afe..d9c4a393a 100644 --- a/test/e2e/pod_inspect_test.go +++ b/test/e2e/pod_inspect_test.go @@ -41,7 +41,7 @@ var _ = Describe("Podman pod inspect", func() { }) It("podman inspect a pod", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) diff --git a/test/e2e/pod_kill_test.go b/test/e2e/pod_kill_test.go index 710147893..06d244f99 100644 --- a/test/e2e/pod_kill_test.go +++ b/test/e2e/pod_kill_test.go @@ -40,7 +40,7 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill a pod by id", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -58,7 +58,7 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill a pod by id with TERM", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -72,7 +72,7 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill a pod by name", func() { - _, ec, podid := podmanTest.CreatePod("test1") + _, ec, podid := podmanTest.CreatePod(map[string][]string{"--name": {"test1"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -86,7 +86,7 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill a pod by id with a bogus signal", func() { - _, ec, podid := podmanTest.CreatePod("test1") + _, ec, podid := podmanTest.CreatePod(map[string][]string{"--name": {"test1"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -100,14 +100,14 @@ var _ = Describe("Podman pod kill", func() { }) It("podman pod kill latest pod", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec, podid2 := podmanTest.CreatePod("") + _, ec, podid2 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", podid2) @@ -128,7 +128,7 @@ var _ = Describe("Podman pod kill", func() { It("podman pod kill all", func() { SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -139,7 +139,7 @@ var _ = Describe("Podman pod kill", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec, podid2 := podmanTest.CreatePod("") + _, ec, podid2 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", podid2) diff --git a/test/e2e/pod_pause_test.go b/test/e2e/pod_pause_test.go index 3dabf7b4a..0c1b39f38 100644 --- a/test/e2e/pod_pause_test.go +++ b/test/e2e/pod_pause_test.go @@ -48,7 +48,7 @@ var _ = Describe("Podman pod pause", func() { }) It("podman pod pause a created pod by id", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "pause", podid}) @@ -57,7 +57,7 @@ var _ = Describe("Podman pod pause", func() { }) It("podman pod pause a running pod by id", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -78,7 +78,7 @@ var _ = Describe("Podman pod pause", func() { }) It("podman unpause a running pod by id", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -93,7 +93,7 @@ var _ = Describe("Podman pod pause", func() { }) It("podman pod pause a running pod by name", func() { - _, ec, _ := podmanTest.CreatePod("test1") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"test1"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", "test1") diff --git a/test/e2e/pod_prune_test.go b/test/e2e/pod_prune_test.go index 0346cfdc8..d1ebf7249 100644 --- a/test/e2e/pod_prune_test.go +++ b/test/e2e/pod_prune_test.go @@ -33,7 +33,7 @@ var _ = Describe("Podman pod prune", func() { }) It("podman pod prune empty pod", func() { - _, ec, _ := podmanTest.CreatePod("") + _, ec, _ := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "prune", "--force"}) @@ -42,7 +42,7 @@ var _ = Describe("Podman pod prune", func() { }) It("podman pod prune doesn't remove a pod with a running container", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) ec2 := podmanTest.RunTopContainerInPod("", podid) @@ -59,7 +59,7 @@ var _ = Describe("Podman pod prune", func() { }) It("podman pod prune removes a pod with a stopped container", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) _, ec2, _ := podmanTest.RunLsContainerInPod("", podid) diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go index 9f63c1d5d..c20cb44e7 100644 --- a/test/e2e/pod_ps_test.go +++ b/test/e2e/pod_ps_test.go @@ -43,7 +43,7 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps default", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -57,7 +57,7 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps quiet flag", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) _, ec, _ = podmanTest.RunLsContainerInPod("", podid) @@ -71,7 +71,7 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps no-trunc", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) _, ec2, _ := podmanTest.RunLsContainerInPod("", podid) @@ -86,10 +86,10 @@ var _ = Describe("Podman ps", func() { It("podman pod ps latest", func() { SkipIfRemote("--latest flag n/a") - _, ec, podid1 := podmanTest.CreatePod("") + _, ec, podid1 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) - _, ec2, podid2 := podmanTest.CreatePod("") + _, ec2, podid2 := podmanTest.CreatePod(nil) Expect(ec2).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--latest"}) @@ -100,7 +100,7 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps id filter flag", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "ps", "--filter", fmt.Sprintf("id=%s", podid)}) @@ -109,9 +109,9 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps filter name regexp", func() { - _, ec, podid := podmanTest.CreatePod("mypod") + _, ec, podid := podmanTest.CreatePod(map[string][]string{"--name": {"mypod"}}) Expect(ec).To(Equal(0)) - _, ec2, _ := podmanTest.CreatePod("mypod1") + _, ec2, _ := podmanTest.CreatePod(map[string][]string{"--name": {"mypod1"}}) Expect(ec2).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "name=mypod"}) @@ -138,13 +138,13 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps --sort by name", func() { - _, ec, _ := podmanTest.CreatePod("") + _, ec, _ := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) - _, ec2, _ := podmanTest.CreatePod("") + _, ec2, _ := podmanTest.CreatePod(nil) Expect(ec2).To(Equal(0)) - _, ec3, _ := podmanTest.CreatePod("") + _, ec3, _ := podmanTest.CreatePod(nil) Expect(ec3).To(Equal(0)) session := podmanTest.Podman([]string{"pod", "ps", "--sort=name", "--format", "{{.Name}}"}) @@ -159,7 +159,7 @@ var _ = Describe("Podman ps", func() { It("podman pod ps --ctr-names", func() { SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("test1", podid) @@ -177,14 +177,14 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps filter ctr attributes", func() { - _, ec, podid1 := podmanTest.CreatePod("") + _, ec, podid1 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("test1", podid1) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec2, podid2 := podmanTest.CreatePod("") + _, ec2, podid2 := podmanTest.CreatePod(nil) Expect(ec2).To(Equal(0)) _, ec3, cid := podmanTest.RunLsContainerInPod("test2", podid2) @@ -214,7 +214,7 @@ var _ = Describe("Podman ps", func() { Expect(session.OutputToString()).To(ContainSubstring(podid2)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid1))) - _, ec3, podid3 := podmanTest.CreatePod("") + _, ec3, podid3 := podmanTest.CreatePod(nil) Expect(ec3).To(Equal(0)) session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-number=1"}) @@ -259,23 +259,20 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps filter labels", func() { - _, ec, podid1 := podmanTest.CreatePod("") - Expect(ec).To(Equal(0)) + s, _, podid1 := podmanTest.CreatePod(nil) + Expect(s).To(Exit(0)) - _, ec, podid2 := podmanTest.CreatePodWithLabels("", map[string]string{ - "app": "myapp", - "io.podman.test.key": "irrelevant-value", + s, _, podid2 := podmanTest.CreatePod(map[string][]string{ + "--label": {"app=myapp", "io.podman.test.key=irrelevant-value"}, }) - Expect(ec).To(Equal(0)) + Expect(s).To(Exit(0)) - _, ec, podid3 := podmanTest.CreatePodWithLabels("", map[string]string{ - "app": "test", - }) - Expect(ec).To(Equal(0)) + s, _, podid3 := podmanTest.CreatePod(map[string][]string{"--label": {"app=test"}}) + Expect(s).To(Exit(0)) session := podmanTest.Podman([]string{"pod", "ps", "--no-trunc", "--filter", "label=app", "--filter", "label=app=myapp"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).To(Exit(0)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid1))) Expect(session.OutputToString()).To(ContainSubstring(podid2)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid3))) @@ -359,13 +356,13 @@ var _ = Describe("Podman ps", func() { }) It("podman pod ps format with labels", func() { - _, ec, _ := podmanTest.CreatePod("") + _, ec, _ := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) - _, ec1, _ := podmanTest.CreatePodWithLabels("", map[string]string{ - "io.podman.test.label": "value1", - "io.podman.test.key": "irrelevant-value", - }) + _, ec1, _ := podmanTest.CreatePod(map[string][]string{"--label": { + "io.podman.test.label=value1", + "io.podman.test.key=irrelevant-value", + }}) Expect(ec1).To(Equal(0)) session := podmanTest.Podman([]string{"pod", "ps", "--format", "{{.Labels}}"}) diff --git a/test/e2e/pod_restart_test.go b/test/e2e/pod_restart_test.go index b358c2c7a..c6b1a0d46 100644 --- a/test/e2e/pod_restart_test.go +++ b/test/e2e/pod_restart_test.go @@ -39,7 +39,7 @@ var _ = Describe("Podman pod restart", func() { }) It("podman pod restart single empty pod", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"pod", "restart", podid}) @@ -48,7 +48,7 @@ var _ = Describe("Podman pod restart", func() { }) It("podman pod restart single pod by name", func() { - _, ec, _ := podmanTest.CreatePod("foobar99") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("test1", "foobar99") @@ -68,14 +68,14 @@ var _ = Describe("Podman pod restart", func() { }) It("podman pod restart multiple pods", func() { - _, ec, _ := podmanTest.CreatePod("foobar99") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("test1", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec, _ = podmanTest.CreatePod("foobar100") + _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("test2", "foobar100") @@ -106,14 +106,14 @@ var _ = Describe("Podman pod restart", func() { }) It("podman pod restart all pods", func() { - _, ec, _ := podmanTest.CreatePod("foobar99") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("test1", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec, _ = podmanTest.CreatePod("foobar100") + _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("test2", "foobar100") @@ -134,14 +134,14 @@ var _ = Describe("Podman pod restart", func() { }) It("podman pod restart latest pod", func() { - _, ec, _ := podmanTest.CreatePod("foobar99") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("test1", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec, _ = podmanTest.CreatePod("foobar100") + _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("test2", "foobar100") @@ -166,7 +166,7 @@ var _ = Describe("Podman pod restart", func() { }) It("podman pod restart multiple pods with bogus", func() { - _, ec, podid1 := podmanTest.CreatePod("foobar99") + _, ec, podid1 := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", "foobar99") diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index 24e945d5a..40a903cd0 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -37,7 +37,7 @@ var _ = Describe("Podman pod rm", func() { }) It("podman pod rm empty pod", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"pod", "rm", podid}) @@ -61,10 +61,10 @@ var _ = Describe("Podman pod rm", func() { }) It("podman pod rm latest pod", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) - _, ec2, podid2 := podmanTest.CreatePod("pod2") + _, ec2, podid2 := podmanTest.CreatePod(map[string][]string{"--name": {"pod2"}}) Expect(ec2).To(Equal(0)) latest := "--latest" @@ -83,7 +83,7 @@ var _ = Describe("Podman pod rm", func() { }) It("podman pod rm removes a pod with a container", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) _, ec2, _ := podmanTest.RunLsContainerInPod("", podid) @@ -99,7 +99,7 @@ var _ = Describe("Podman pod rm", func() { }) It("podman pod rm -f does remove a running container", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -117,10 +117,10 @@ var _ = Describe("Podman pod rm", func() { It("podman pod rm -a doesn't remove a running container", func() { fmt.Printf("To start, there are %d pods\n", podmanTest.NumberOfPods()) - _, ec, podid1 := podmanTest.CreatePod("") + _, ec, podid1 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) - _, ec, _ = podmanTest.CreatePod("") + _, ec, _ = podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) fmt.Printf("Started %d pods\n", podmanTest.NumberOfPods()) @@ -154,13 +154,13 @@ var _ = Describe("Podman pod rm", func() { }) It("podman pod rm -fa removes everything", func() { - _, ec, podid1 := podmanTest.CreatePod("") + _, ec, podid1 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) - _, ec, podid2 := podmanTest.CreatePod("") + _, ec, podid2 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) - _, ec, _ = podmanTest.CreatePod("") + _, ec, _ = podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid1) @@ -199,7 +199,7 @@ var _ = Describe("Podman pod rm", func() { }) It("podman rm bogus pod and a running pod", func() { - _, ec, podid1 := podmanTest.CreatePod("") + _, ec, podid1 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("test1", podid1) @@ -217,7 +217,7 @@ var _ = Describe("Podman pod rm", func() { It("podman rm --ignore bogus pod and a running pod", func() { - _, ec, podid1 := podmanTest.CreatePod("") + _, ec, podid1 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("test1", podid1) diff --git a/test/e2e/pod_start_test.go b/test/e2e/pod_start_test.go index 63a915548..e14796ab3 100644 --- a/test/e2e/pod_start_test.go +++ b/test/e2e/pod_start_test.go @@ -10,6 +10,7 @@ import ( . "github.com/containers/podman/v2/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pod start", func() { @@ -43,7 +44,7 @@ var _ = Describe("Podman pod start", func() { }) It("podman pod start single empty pod", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"pod", "start", podid}) @@ -52,7 +53,7 @@ var _ = Describe("Podman pod start", func() { }) It("podman pod start single pod by name", func() { - _, ec, _ := podmanTest.CreatePod("foobar99") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "ls"}) @@ -65,14 +66,14 @@ var _ = Describe("Podman pod start", func() { }) It("podman pod start multiple pods", func() { - _, ec, podid1 := podmanTest.CreatePod("foobar99") + _, ec, podid1 := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec2, podid2 := podmanTest.CreatePod("foobar100") + _, ec2, podid2 := podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec2).To(Equal(0)) session = podmanTest.Podman([]string{"create", "--pod", "foobar100", ALPINE, "top"}) @@ -85,15 +86,45 @@ var _ = Describe("Podman pod start", func() { Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) }) + It("multiple pods in conflict", func() { + podName := []string{"Pod_" + RandomString(10), "Pod_" + RandomString(10)} + + pod, _, podid1 := podmanTest.CreatePod(map[string][]string{ + "--infra": {"true"}, + "--name": {podName[0]}, + "--publish": {"127.0.0.1:8080:80"}, + }) + Expect(pod).To(Exit(0)) + + session := podmanTest.Podman([]string{"create", "--pod", podName[0], ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + + pod, _, podid2 := podmanTest.CreatePod(map[string][]string{ + "--infra": {"true"}, + "--name": {podName[1]}, + "--publish": {"127.0.0.1:8080:80"}, + }) + Expect(pod).To(Exit(0)) + + session = podmanTest.Podman([]string{"create", "--pod", podName[1], ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + + session = podmanTest.Podman([]string{"pod", "start", podid1, podid2}) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(125)) + }) + It("podman pod start all pods", func() { - _, ec, _ := podmanTest.CreatePod("foobar99") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec, _ = podmanTest.CreatePod("foobar100") + _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.Podman([]string{"create", "--pod", "foobar100", ALPINE, "top"}) @@ -107,14 +138,14 @@ var _ = Describe("Podman pod start", func() { }) It("podman pod start latest pod", func() { - _, ec, _ := podmanTest.CreatePod("foobar99") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec, _ = podmanTest.CreatePod("foobar100") + _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.Podman([]string{"create", "--pod", "foobar100", ALPINE, "top"}) @@ -132,7 +163,7 @@ var _ = Describe("Podman pod start", func() { }) It("podman pod start multiple pods with bogus", func() { - _, ec, podid := podmanTest.CreatePod("foobar99") + _, ec, podid := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go index 1709b4f81..073d4752b 100644 --- a/test/e2e/pod_stats_test.go +++ b/test/e2e/pod_stats_test.go @@ -50,7 +50,7 @@ var _ = Describe("Podman pod stats", func() { }) It("podman stats on a specific running pod", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -67,7 +67,7 @@ var _ = Describe("Podman pod stats", func() { }) It("podman stats on a specific running pod with shortID", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -84,7 +84,7 @@ var _ = Describe("Podman pod stats", func() { }) It("podman stats on a specific running pod with name", func() { - _, ec, podid := podmanTest.CreatePod("test") + _, ec, podid := podmanTest.CreatePod(map[string][]string{"--name": {"test"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -101,7 +101,7 @@ var _ = Describe("Podman pod stats", func() { }) It("podman stats on running pods", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -118,7 +118,7 @@ var _ = Describe("Podman pod stats", func() { }) It("podman stats on all pods", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -135,7 +135,7 @@ var _ = Describe("Podman pod stats", func() { }) It("podman stats with json output", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -152,7 +152,7 @@ var _ = Describe("Podman pod stats", func() { Expect(stats.IsJSONOutputValid()).To(BeTrue()) }) It("podman stats with GO template", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -164,7 +164,7 @@ var _ = Describe("Podman pod stats", func() { }) It("podman stats with invalid GO template", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) diff --git a/test/e2e/pod_stop_test.go b/test/e2e/pod_stop_test.go index 4eb897786..30a5632d0 100644 --- a/test/e2e/pod_stop_test.go +++ b/test/e2e/pod_stop_test.go @@ -47,7 +47,7 @@ var _ = Describe("Podman pod stop", func() { }) It("podman stop bogus pod and a running pod", func() { - _, ec, podid1 := podmanTest.CreatePod("") + _, ec, podid1 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("test1", podid1) @@ -61,7 +61,7 @@ var _ = Describe("Podman pod stop", func() { It("podman stop --ignore bogus pod and a running pod", func() { - _, ec, podid1 := podmanTest.CreatePod("") + _, ec, podid1 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("test1", podid1) @@ -78,7 +78,7 @@ var _ = Describe("Podman pod stop", func() { }) It("podman pod stop single empty pod", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"pod", "stop", podid}) @@ -87,7 +87,7 @@ var _ = Describe("Podman pod stop", func() { }) It("podman pod stop single pod by name", func() { - _, ec, _ := podmanTest.CreatePod("foobar99") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", "foobar99") @@ -101,14 +101,14 @@ var _ = Describe("Podman pod stop", func() { }) It("podman pod stop multiple pods", func() { - _, ec, podid1 := podmanTest.CreatePod("foobar99") + _, ec, podid1 := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec2, podid2 := podmanTest.CreatePod("foobar100") + _, ec2, podid2 := podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec2).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", "foobar100") @@ -122,14 +122,14 @@ var _ = Describe("Podman pod stop", func() { }) It("podman pod stop all pods", func() { - _, ec, _ := podmanTest.CreatePod("foobar99") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec, _ = podmanTest.CreatePod("foobar100") + _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", "foobar100") @@ -143,14 +143,14 @@ var _ = Describe("Podman pod stop", func() { }) It("podman pod stop latest pod", func() { - _, ec, _ := podmanTest.CreatePod("foobar99") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - _, ec, _ = podmanTest.CreatePod("foobar100") + _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", "foobar100") @@ -168,7 +168,7 @@ var _ = Describe("Podman pod stop", func() { }) It("podman pod stop multiple pods with bogus", func() { - _, ec, podid1 := podmanTest.CreatePod("foobar99") + _, ec, podid1 := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", "foobar99") diff --git a/test/e2e/pod_top_test.go b/test/e2e/pod_top_test.go index 9e3570360..e191b44fc 100644 --- a/test/e2e/pod_top_test.go +++ b/test/e2e/pod_top_test.go @@ -47,7 +47,7 @@ var _ = Describe("Podman top", func() { }) It("podman pod top on non-running pod", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"top", podid}) @@ -56,7 +56,7 @@ var _ = Describe("Podman top", func() { }) It("podman pod top on pod", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top", "-d", "2"}) @@ -73,7 +73,7 @@ var _ = Describe("Podman top", func() { }) It("podman pod top with options", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top", "-d", "2"}) @@ -87,7 +87,7 @@ var _ = Describe("Podman top", func() { }) It("podman pod top on pod invalid options", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top", "-d", "2"}) @@ -104,7 +104,7 @@ var _ = Describe("Podman top", func() { }) It("podman pod top on pod with containers in same pid namespace", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top", "-d", "2"}) @@ -123,7 +123,7 @@ var _ = Describe("Podman top", func() { }) It("podman pod top on pod with containers in different namespace", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top", "-d", "2"}) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 13701fc3b..1b3dca7b7 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -389,7 +389,7 @@ var _ = Describe("Podman ps", func() { }) It("podman --pod", func() { - _, ec, podid := podmanTest.CreatePod("") + _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) @@ -406,7 +406,7 @@ var _ = Describe("Podman ps", func() { It("podman --pod with a non-empty pod name", func() { podName := "testPodName" - _, ec, podid := podmanTest.CreatePod(podName) + _, ec, podid := podmanTest.CreatePod(map[string][]string{"--name": {podName}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podName) diff --git a/test/e2e/restart_test.go b/test/e2e/restart_test.go index 584ccd22b..76362dcbf 100644 --- a/test/e2e/restart_test.go +++ b/test/e2e/restart_test.go @@ -197,10 +197,10 @@ var _ = Describe("Podman restart", func() { Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1]))) }) - It("Podman restart a container in a pod and hosts shouln't duplicated", func() { + It("Podman restart a container in a pod and hosts should not duplicated", func() { // Fixes: https://github.com/containers/podman/issues/8921 - _, ec, _ := podmanTest.CreatePod("foobar99") + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {"foobar99"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("host-restart-test", "foobar99") diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index ebea2132a..676f24e5d 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -376,6 +376,13 @@ var _ = Describe("Podman run networking", func() { Expect(session.ExitCode()).To(Equal(0)) }) + It("podman run slirp4netns network with mtu", func() { + session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:mtu=9000", ALPINE, "ip", "addr"}) + session.Wait(30) + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring("mtu 9000")) + }) + It("podman run slirp4netns network with different cidr", func() { slirp4netnsHelp := SystemExec("slirp4netns", []string{"--help"}) Expect(slirp4netnsHelp.ExitCode()).To(Equal(0)) diff --git a/test/e2e/run_volume_test.go b/test/e2e/run_volume_test.go index 7c74cea78..bc89b59de 100644 --- a/test/e2e/run_volume_test.go +++ b/test/e2e/run_volume_test.go @@ -110,7 +110,7 @@ var _ = Describe("Podman run with volumes", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(ContainSubstring(dest + " ro")) - session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",shared", ALPINE, "grep", dest, "/proc/self/mountinfo"}) + session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",consistency=delegated,shared", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) found, matches := session.GrepString(dest) diff --git a/test/system/030-run.bats b/test/system/030-run.bats index dcf1da370..98e34238e 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -608,6 +608,19 @@ json-file | f # a subdir of a volume. run_podman run --rm --workdir /IamNotOntheImage -v $testdir/content:/IamNotOntheImage/foo $IMAGE cat foo is "$output" "$randomcontent" "cat random content" + + # Make sure that running on a read-only rootfs works (#9230). + if ! is_rootless && ! is_remote; then + # image mount is hard to test as a rootless user + # and does not work remotely + run_podman image mount $IMAGE + romount="$output" + + run_podman run --rm --rootfs $romount echo "Hello world" + is "$output" "Hello world" + + run_podman image unmount $IMAGE + fi } # vim: filetype=sh diff --git a/test/utils/utils.go b/test/utils/utils.go index f21584537..6790f31cd 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -467,11 +467,14 @@ func Containerized() bool { return false } +func init() { + rand.Seed(GinkgoRandomSeed()) +} + var randomLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") // RandomString returns a string of given length composed of random characters func RandomString(n int) string { - rand.Seed(GinkgoRandomSeed()) b := make([]rune, n) for i := range b { |