From 6913e1466b4e8e0c3c768d2b2d9e92610c94ffa3 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Thu, 4 Feb 2021 13:24:04 -0500 Subject: Update release notes from master Signed-off-by: Matthew Heon --- RELEASE_NOTES.md | 340 +++++++++++++++++++++++-------------------------------- 1 file changed, 142 insertions(+), 198 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 5a5d69193..82ecd741a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,207 +1,151 @@ # 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 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 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`. + ### 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 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 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 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)). + ### 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 +- 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 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.1 + +## 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 +288,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. -- cgit v1.2.3-54-g00ecf From 5cfe809ef263dc9e1654357a7eae2447311586a6 Mon Sep 17 00:00:00 2001 From: Gerard Braad Date: Tue, 26 Jan 2021 15:29:56 +0800 Subject: Fix #9100 Change console mode message to debug [NO TESTS NEEDED] Signed-off-by: Gerard Braad --- pkg/terminal/console_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- cgit v1.2.3-54-g00ecf From b9c8c43c4531c197c36fe3dde92c44488b28be79 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Tue, 26 Jan 2021 10:51:01 -0500 Subject: Don't fail if one of the cgroups is not setup It is fairly common for certain cgroups controllers to not be enabled on a system. We should Warn when this happens versus failing, when doing podman stats command. This way users can get information from the other controllers. Fixes: https://github.com/containers/podman/issues/8588 Signed-off-by: Daniel J Walsh --- pkg/cgroups/cgroups.go | 12 +++++++++++- pkg/cgroups/cgroups_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 pkg/cgroups/cgroups_test.go 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) + } +} -- cgit v1.2.3-54-g00ecf From c749515745905ba510efacbf6813695bb553fe24 Mon Sep 17 00:00:00 2001 From: baude Date: Thu, 28 Jan 2021 15:20:15 -0600 Subject: Honor custom DNS in play|generate kube when creating kubernetes yaml from containers and pods, we should honor any custom dns settings the user provided. in the case of generate kube, these would be provided by --dns, --dns-search, and --dns-opt. if multiple containers are involved in the generate, the options will be cumulative and unique with the exception of dns-opt. when replaying a kube file that has kubernetes dns information, we now also add that information to the pod creation. the options for dnspolicy is not enabled as there seemed to be no direct correlation between kubernetes and podman. Fixes: #9132 Signed-off-by: baude --- libpod/kube.go | 112 +++++++++++++++++++++++++++++++++----- pkg/specgen/generate/kube/kube.go | 26 +++++++++ test/e2e/generate_kube_test.go | 63 +++++++++++++++++++++ 3 files changed, 187 insertions(+), 14 deletions(-) diff --git a/libpod/kube.go b/libpod/kube.go index 753c58099..b5197293e 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -171,9 +171,10 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor 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 +197,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 +220,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), 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) *v1.Pod { tm := v12.TypeMeta{ Kind: "Pod", APIVersion: "v1", @@ -228,6 +245,9 @@ func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1. Containers: containers, Volumes: volumes, } + if dnsOptions != nil { + ps.DNSConfig = dnsOptions + } p := v1.Pod{ TypeMeta: tm, ObjectMeta: om, @@ -241,32 +261,65 @@ 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) + podDNS := v1.PodDNSConfig{} for _, ctr := range ctrs { - kubeCtr, kubeVols, err := containerToV1Container(ctr) + 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), 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 +327,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,16 +335,16 @@ 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() @@ -355,7 +408,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/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index e39a700eb..0d7ee3ad2 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 } diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 239817e6c..8800f9057 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -540,4 +540,67 @@ 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")) + }) }) -- cgit v1.2.3-54-g00ecf From f5eeee110300210e5618b7f4a8b26ef20c49d2a2 Mon Sep 17 00:00:00 2001 From: Milivoje Legenovic Date: Wed, 27 Jan 2021 23:20:28 +0100 Subject: podman generate kube ignores --network=host Signed-off-by: Milivoje Legenovic --- libpod/container.go | 12 ++++++++++++ libpod/kube.go | 22 ++++++++++++++-------- test/e2e/generate_kube_test.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 8 deletions(-) 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/kube.go b/libpod/kube.go index b5197293e..bf314b9a3 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,7 +168,7 @@ 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)) @@ -220,10 +221,10 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor podVolumes = append(podVolumes, *vol) } - return addContainersAndVolumesToPodObject(podContainers, podVolumes, p.Name(), &dnsInfo), nil + return addContainersAndVolumesToPodObject(podContainers, podVolumes, p.Name(), &dnsInfo, hostNetwork), nil } -func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1.Volume, podName string, dnsOptions *v1.PodDNSConfig) *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", @@ -242,8 +243,9 @@ 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 @@ -261,8 +263,12 @@ 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 { + if !ctr.HostNetwork() { + hostNetwork = false + } kubeCtr, kubeVols, ctrDNS, err := containerToV1Container(ctr) if err != nil { return nil, err @@ -303,7 +309,7 @@ func simplePodWithV1Containers(ctrs []*Container) (*v1.Pod, error) { } } // end if ctrDNS } - return addContainersAndVolumesToPodObject(kubeCtrs, kubeVolumes, strings.ReplaceAll(ctrs[0].Name(), "_", ""), &podDNS), nil + return addContainersAndVolumesToPodObject(kubeCtrs, kubeVolumes, strings.ReplaceAll(ctrs[0].Name(), "_", ""), &podDNS, hostNetwork), nil } // containerToV1Container converts information we know about a libpod container diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 8800f9057..83b9cfb14 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -60,6 +60,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 { @@ -144,6 +145,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 +154,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" -- cgit v1.2.3-54-g00ecf From 8defc9cf4389eb8975e8438b0ef7f446312b416f Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Thu, 28 Jan 2021 17:45:56 -0500 Subject: Docker ignores mount flags that begin with constency Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1915332 ``` According to the Docker docs, the consistency option should be ignored on Linux. the possible values are 'cached', 'delegated', and 'consistent', but they should be ignored equally. This is a widely used option in scripts run by developer machines, as this makes file I/O less horribly slow on MacOS. ``` Signed-off-by: Daniel J Walsh --- cmd/podman/common/volumes.go | 16 ++++++++++++++++ pkg/util/mountOpts.go | 4 ++++ test/e2e/run_volume_test.go | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) 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/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/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) -- cgit v1.2.3-54-g00ecf From f4c828f827516684b33ff8f30cf4b266313c1964 Mon Sep 17 00:00:00 2001 From: Milivoje Legenovic Date: Sun, 31 Jan 2021 20:32:20 +0100 Subject: Endpoint that lists containers does not return correct Status value Eclipse and Intellij Docker plugin determines the state of the container via the Status field, returned from /containers/json call. Podman always returns empty string, and because of that, both IDEs show the wrong state of the container. Signed-off-by: Milivoje Legenovic Signed-off-by: Matthew Heon --- pkg/api/handlers/compat/containers.go | 33 ++++++++++++++++++++++- test/apiv2/rest_api/test_rest_v2_0_0.py | 48 ++++++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 2 deletions(-) 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/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py index cc66dd5af..ba83ae7c4 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 @@ -586,6 +585,53 @@ 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) + if __name__ == "__main__": unittest.main() -- cgit v1.2.3-54-g00ecf From be6afd10d98e01643ab2c6553126bc17dc6e7dfb Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Sun, 31 Jan 2021 15:46:39 +0100 Subject: Fix --network parsing for podman pod create The `--network` flag is parsed differently for `podman pod create`. This causes confusion and problems for users. The extra parsing logic ignored unsupported network options such as `none`, `container:...` and `ns:...` and instead interpreted them as cni network names. Tests are added to ensure the correct errors are shown. Signed-off-by: Paul Holzinger --- cmd/podman/pods/create.go | 27 --------------------------- test/e2e/pod_create_test.go | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 27 deletions(-) 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/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 9c448a81e..575f9df68 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -476,4 +476,21 @@ 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", "none"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate.ErrorToString()).To(ContainSubstring("pods presently do not support network mode none")) + + 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")) + }) }) -- cgit v1.2.3-54-g00ecf From 3b712d96157b718e76ea7fe5b5bb91a5ddd9f2a4 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Mon, 1 Feb 2021 14:40:12 +0100 Subject: Fix podman generate systemd --new special char handling In a systemd unit dollar and percent signs are used for variables. A backslash is used for escape sequences. If any of these characters are used in the create command we have to properly escape them so systemd does not try to interpret them. Fixes #9176 Signed-off-by: Paul Holzinger --- pkg/systemd/generate/common.go | 14 +++++++++--- pkg/systemd/generate/common_test.go | 40 +++++++++++++++++++++++++++++++-- pkg/systemd/generate/containers.go | 4 ++-- pkg/systemd/generate/containers_test.go | 40 +++++++++++++++++++++++++++++++++ pkg/systemd/generate/pods.go | 4 ++-- 5 files changed, 93 insertions(+), 9 deletions(-) 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 @@ -349,6 +349,30 @@ 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 +` + + 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 ` @@ -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, " ") -- cgit v1.2.3-54-g00ecf From 6d9217228b87e5ea5b0e215701d8e001c44e1917 Mon Sep 17 00:00:00 2001 From: bitstrings Date: Fri, 29 Jan 2021 23:37:14 -0500 Subject: Make slirp MTU configurable (network_cmd_options) The mtu default value is currently forced to 65520. This let the user control it using the config key network_cmd_options, i.e.: network_cmd_options=["mtu=9000"] Signed-off-by: bitstrings --- cmd/podman/common/completion.go | 1 + docs/source/markdown/podman-create.1.md | 1 + docs/source/markdown/podman-run.1.md | 1 + libpod/networking_linux.go | 14 ++++++++++++-- test/e2e/run_networking_test.go | 7 +++++++ 5 files changed, 22 insertions(+), 2 deletions(-) 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/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/networking_linux.go b/libpod/networking_linux.go index ffd39dfa9..2eabec634 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") 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)) -- cgit v1.2.3-54-g00ecf From 6213225c919b3b30a298a9a2fb676da9ba811e37 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 3 Feb 2021 14:35:42 +0100 Subject: bindings: attach: warn correct error The resize warning logged the wrong error. While this does not fix #9172, it may very well be helpful finding its root cause. [NO TESTS NEEDED] Signed-off-by: Valentin Rothberg --- pkg/bindings/containers/attach.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) } } } -- cgit v1.2.3-54-g00ecf From b576ddd9a2ebfa8b42777114ddc1fcc248d6d7fb Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Tue, 19 Jan 2021 09:16:01 -0700 Subject: Report StatusConflict on Pod opt partial failures - When one or more containers in the Pod reports an error on an operation report StatusConflict and report the error(s) - jsoniter type encoding used to marshal error as string using error.Error() - Update test framework to allow setting any flag when creating pods - Fix test_resize() result check Fixes #8865 Signed-off-by: Jhon Honce Signed-off-by: Matthew Heon --- pkg/api/handlers/compat/resize.go | 2 +- pkg/api/handlers/libpod/pods.go | 90 ++++++++++++++++++--------------- pkg/api/handlers/utils/handler.go | 47 ++++++++++++++++- pkg/api/server/register_pods.go | 17 ++++++- test/apiv2/rest_api/test_rest_v2_0_0.py | 86 +++++++++++++++++++++++++++---- test/e2e/common_test.go | 26 +++------- test/e2e/exists_test.go | 6 +-- test/e2e/generate_kube_test.go | 8 +-- test/e2e/pod_create_test.go | 10 ++-- test/e2e/pod_inspect_test.go | 2 +- test/e2e/pod_kill_test.go | 16 +++--- test/e2e/pod_pause_test.go | 8 +-- test/e2e/pod_prune_test.go | 6 +-- test/e2e/pod_ps_test.go | 59 ++++++++++----------- test/e2e/pod_restart_test.go | 18 +++---- test/e2e/pod_rm_test.go | 24 ++++----- test/e2e/pod_start_test.go | 49 ++++++++++++++---- test/e2e/pod_stats_test.go | 16 +++--- test/e2e/pod_stop_test.go | 22 ++++---- test/e2e/pod_top_test.go | 12 ++--- test/e2e/ps_test.go | 4 +- test/e2e/restart_test.go | 4 +- test/utils/utils.go | 5 +- 23 files changed, 345 insertions(+), 192 deletions(-) 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..ebbe7f24f 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" ) @@ -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/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py index ba83ae7c4..520aa161b 100644 --- a/test/apiv2/rest_api/test_rest_v2_0_0.py +++ b/test/apiv2/rest_api/test_rest_v2_0_0.py @@ -162,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,15 +586,19 @@ class TestApi(unittest.TestCase): 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"}) + 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}"]}}'}) + 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") @@ -602,8 +606,10 @@ class TestApi(unittest.TestCase): 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}"]}}'}) + 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")) @@ -611,8 +617,10 @@ class TestApi(unittest.TestCase): 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}"]}}'}) + 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")) @@ -623,8 +631,10 @@ class TestApi(unittest.TestCase): 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}"]}}'}) + 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")) @@ -632,6 +642,60 @@ class TestApi(unittest.TestCase): 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 83b9cfb14..dba366a1e 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -131,7 +131,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") @@ -221,7 +221,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") @@ -373,7 +373,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"}) @@ -412,7 +412,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"}) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 575f9df68..fc634d36f 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"}) 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/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 { -- cgit v1.2.3-54-g00ecf From 6bd3a6bcabda682243f531bacf3659b95da8590a Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 1 Feb 2021 13:53:14 -0500 Subject: Allow pods to use --net=none We need an extra field in the pod infra container config. We may want to reevaluate that struct at some point, as storing network modes as bools will rapidly become unsustainable, but that's a discussion for another time. Otherwise, straightforward plumbing. Fixes #9165 Signed-off-by: Matthew Heon --- libpod/options.go | 29 +++++++++++++++++++++++++++-- libpod/pod.go | 1 + libpod/runtime_pod_infra_linux.go | 14 ++++++++++---- pkg/specgen/generate/pod_create.go | 3 +++ test/e2e/pod_create_test.go | 20 ++++++++++++++------ 5 files changed, 55 insertions(+), 12 deletions(-) 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/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/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index fc634d36f..e57712f62 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -478,12 +478,7 @@ entrypoint ["/fromimage"] }) It("podman create with unsupported network options", func() { - podCreate := podmanTest.Podman([]string{"pod", "create", "--network", "none"}) - podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(125)) - Expect(podCreate.ErrorToString()).To(ContainSubstring("pods presently do not support network mode none")) - - podCreate = podmanTest.Podman([]string{"pod", "create", "--network", "container:doesnotmatter"}) + 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")) @@ -493,4 +488,17 @@ entrypoint ["/fromimage"] 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)) + }) }) -- cgit v1.2.3-54-g00ecf From d6ba4ab09808294f0d9cae0142a4dc97e9b9786d Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Wed, 3 Feb 2021 16:36:46 +0100 Subject: generate kube: handle entrypoint The spec of a Kube Container has a `Command` and `Args`. While both are slices, the `Command` is the counterpart of the entrypoint of a libpod container. Kube is also happily accepting the arguments to as following items in the slice but it's cleaner to move those to `Args`. Fixes: #9211 Signed-off-by: Valentin Rothberg --- libpod/kube.go | 21 +++++++-------- test/e2e/generate_kube_test.go | 60 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/libpod/kube.go b/libpod/kube.go index bf314b9a3..f9ead027d 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -353,22 +353,21 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, *v1.PodDNS 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 diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index dba366a1e..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" @@ -639,4 +640,63 @@ var _ = Describe("Podman generate kube", func() { 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"})) + }) }) -- cgit v1.2.3-54-g00ecf From 4793bf8ac2f01e9606233e0cfc49cad316921cb5 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Thu, 4 Feb 2021 16:08:38 -0500 Subject: Update release notes for v3.0.0-RC3 Signed-off-by: Matthew Heon --- RELEASE_NOTES.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 82ecd741a..3d5734138 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -13,7 +13,10 @@ - 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. @@ -30,6 +33,10 @@ - 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 - 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. @@ -37,6 +44,7 @@ - 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)). @@ -53,6 +61,7 @@ - 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)). @@ -63,6 +72,7 @@ - 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 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)). @@ -96,8 +106,12 @@ - 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. ### API +- 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)) @@ -110,6 +124,7 @@ - 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. -- cgit v1.2.3-54-g00ecf From 951879c69045c893c15c3eb902a54115f0e28c18 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 4 Feb 2021 19:41:30 +0100 Subject: Fix podman network disconnect wrong NetworkStatus number The allocated `tmpNetworkStatus` must be allocated with the length 0. Otherwise append would add new elements to the end of the slice and not at the beginning of the allocated memory. This caused inspect to fail since the number of networks did not matched the number of network statuses. Fixes #9234 Signed-off-by: Paul Holzinger --- libpod/networking_linux.go | 2 +- test/e2e/network_connect_disconnect_test.go | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 2eabec634..9edea4fea 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -1168,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/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()) -- cgit v1.2.3-54-g00ecf From 572b0803c7d5e5379e8d7ac5c133eb9c2c4a3ccf Mon Sep 17 00:00:00 2001 From: Steven Taylor Date: Tue, 2 Feb 2021 18:13:13 +0000 Subject: play kube selinux label issue play kube function not respecting selinux options in kube yaml, all options were being mapped to role. fixes issue 8710 Signed-off-by: Steven Taylor --- pkg/specgen/generate/kube/kube.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 0d7ee3ad2..98ab82259 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -282,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 { -- cgit v1.2.3-54-g00ecf From 204239169a59d790c2732947f39484d1bb6114a8 Mon Sep 17 00:00:00 2001 From: Steven Taylor Date: Wed, 3 Feb 2021 00:27:48 +0000 Subject: play kube selinux label test case test case added to e2e test suite to validate process label being correctly set on play kube Signed-off-by: Steven Taylor --- test/e2e/play_kube_test.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 5930462d5..9fbedc073 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -26,6 +26,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 +846,21 @@ var _ = Describe("Podman play kube", func() { }) + It("podman play kube fail with custom selinux label", func() { + 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("nconfined_u:system_r:spc_t:s0")) + }) + It("podman play kube fail with nonexistent authfile", func() { err := generateKubeYaml("pod", getPod(), kubeYaml) Expect(err).To(BeNil()) -- cgit v1.2.3-54-g00ecf From c9a6e6eaf35a3c604de13e34fad3d5b027c1e7cd Mon Sep 17 00:00:00 2001 From: Steven Taylor Date: Wed, 3 Feb 2021 23:35:14 +0000 Subject: play kube selinux test case fixed typo in the label comparison Signed-off-by: Steven Taylor --- test/e2e/play_kube_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 9fbedc073..e34063dc2 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -858,7 +858,7 @@ var _ = Describe("Podman play kube", func() { inspect.WaitWithDefaultTimeout() label := inspect.OutputToString() - Expect(label).To(ContainSubstring("nconfined_u:system_r:spc_t:s0")) + Expect(label).To(ContainSubstring("unconfined_u:system_r:spc_t:s0")) }) It("podman play kube fail with nonexistent authfile", func() { -- cgit v1.2.3-54-g00ecf From 9cf6b7f8dcfb1c6984ebf9cc49635ff97f29c4d7 Mon Sep 17 00:00:00 2001 From: Steven Taylor Date: Thu, 4 Feb 2021 19:57:08 +0000 Subject: play kube selinux test case added skip to test case where selinux not enabled Signed-off-by: Steven Taylor --- test/e2e/play_kube_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index e34063dc2..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 = ` @@ -847,6 +848,9 @@ 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()) -- cgit v1.2.3-54-g00ecf From 353c3b04d15dc4fb3e07f06d8227eed35f350ef1 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Thu, 4 Feb 2021 15:07:44 +0100 Subject: fix logic when not creating a workdir When resolving the workdir of a container, we may need to create unless the user set it explicitly on the command line. Otherwise, we just do a presence check. Unfortunately, there was a missing return that lead us to fall through into attempting to create and chown the workdir. That caused a regression when running on a read-only root fs. Fixes: #9230 Signed-off-by: Valentin Rothberg --- libpod/container_internal_linux.go | 1 + test/system/030-run.bats | 13 +++++++++++++ 2 files changed, 14 insertions(+) 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/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 -- cgit v1.2.3-54-g00ecf From 6c5e9d2f0a152427458b50d57115dc39a8e09b4c Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 1 Feb 2021 10:54:44 -0500 Subject: Bump remote API version to 3.0.0 Fixes #9175 Signed-off-by: Matthew Heon --- pkg/api/handlers/utils/handler.go | 4 ++-- test/apiv2/01-basic.at | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index ebbe7f24f..b3c674788 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -44,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"), 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 \ -- cgit v1.2.3-54-g00ecf From c5c946b18fda59ffba826603b7784dc6e7cafda0 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Fri, 5 Feb 2021 14:16:37 -0500 Subject: Further release notes for v3.0 Signed-off-by: Matthew Heon --- RELEASE_NOTES.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 3d5734138..b4823e1d2 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -72,6 +72,7 @@ - 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)). @@ -109,8 +110,11 @@ - 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 +- 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. @@ -134,7 +138,7 @@ ### Misc - Updated Buildah to v1.19.2 - Updated the containers/storage library to v1.24.5 -- Updated the containers/common library to v0.33.1 +- Updated the containers/common library to v0.33.4 ## v2.2.1 ### Changes -- cgit v1.2.3-54-g00ecf