diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go index 46c4a3a66d..be688c21f5 100644 --- a/cmd/podman/containers/run.go +++ b/cmd/podman/containers/run.go @@ -247,8 +247,5 @@ func run(cmd *cobra.Command, args []string) error { } } } - if cmd.Flag("gpus").Changed { - logrus.Info("--gpus is a Docker specific option and is a NOOP") - } return nil } diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go index 7759304544..f284c7fe16 100644 --- a/cmd/podman/machine/init.go +++ b/cmd/podman/machine/init.go @@ -126,8 +126,11 @@ func init() { _ = initCmd.RegisterFlagCompletionFunc(USBFlagName, completion.AutocompleteDefault) VolumeDriverFlagName := "volume-driver" - flags.StringVar(&initOpts.VolumeDriver, VolumeDriverFlagName, "", "Optional volume driver") + flags.String(VolumeDriverFlagName, "", "Optional volume driver") _ = initCmd.RegisterFlagCompletionFunc(VolumeDriverFlagName, completion.AutocompleteDefault) + if err := flags.MarkDeprecated(VolumeDriverFlagName, "will be ignored"); err != nil { + logrus.Error("unable to mark volume-driver flag deprecated") + } IgnitionPathFlagName := "ignition-path" flags.StringVar(&initOpts.IgnitionPath, IgnitionPathFlagName, "", "Path to ignition file") diff --git a/cmd/podman/system/connection/remove.go b/cmd/podman/system/connection/remove.go index 225b1f038f..3398a7601f 100644 --- a/cmd/podman/system/connection/remove.go +++ b/cmd/podman/system/connection/remove.go @@ -1,7 +1,6 @@ package connection import ( - "errors" "slices" "github.com/containers/common/pkg/config" @@ -14,10 +13,16 @@ import ( var ( // Skip creating engines since this command will obtain connection information to said engines. rmCmd = &cobra.Command{ - Use: "remove [options] NAME", - Aliases: []string{"rm"}, - Long: `Delete named destination from podman configuration`, - Short: "Delete named destination", + Use: "remove [options] NAME", + Aliases: []string{"rm"}, + Long: `Delete named destination from podman configuration`, + Short: "Delete named destination", + Args: func(cmd *cobra.Command, args []string) error { + if rmOpts.All { + return nil + } + return cobra.ExactArgs(1)(cmd, args) + }, ValidArgsFunction: common.AutocompleteSystemConnections, RunE: rm, Example: `podman system connection remove devl @@ -60,10 +65,6 @@ func rm(cmd *cobra.Command, args []string) error { return nil } - if len(args) != 1 { - return errors.New("accepts 1 arg(s), received 0") - } - delete(cfg.Connection.Connections, args[0]) if cfg.Connection.Default == args[0] { cfg.Connection.Default = "" diff --git a/cmd/quadlet/main.go b/cmd/quadlet/main.go index c443b05f26..eeab3d4c3f 100644 --- a/cmd/quadlet/main.go +++ b/cmd/quadlet/main.go @@ -158,9 +158,12 @@ func appendSubPaths(dirs []string, path string, isUserFlag bool, filterPtr func( } err = filepath.WalkDir(resolvedPath, func(_path string, info os.DirEntry, err error) error { - if info == nil || info.IsDir() { - if filterPtr == nil || filterPtr(_path, isUserFlag) { - dirs = append(dirs, _path) + // Ignore drop-in directory subpaths + if !strings.HasSuffix(_path, ".d") { + if info == nil || info.IsDir() { + if filterPtr == nil || filterPtr(_path, isUserFlag) { + dirs = append(dirs, _path) + } } } return err @@ -256,16 +259,11 @@ func loadUnitDropins(unit *parser.UnitFile, sourcePaths []string) error { } dropinDirs := []string{} + unitDropinPaths := unit.GetUnitDropinPaths() for _, sourcePath := range sourcePaths { - dropinDirs = append(dropinDirs, path.Join(sourcePath, unit.Filename+".d")) - } - - // For instantiated templates, also look in the non-instanced template dropin dirs - templateBase, templateInstance := unit.GetTemplateParts() - if templateBase != "" && templateInstance != "" { - for _, sourcePath := range sourcePaths { - dropinDirs = append(dropinDirs, path.Join(sourcePath, templateBase+".d")) + for _, dropinPath := range unitDropinPaths { + dropinDirs = append(dropinDirs, path.Join(sourcePath, dropinPath)) } } @@ -359,15 +357,14 @@ func enableServiceFile(outputPath string, service *parser.UnitFile) { } serviceFilename := service.Filename - templateBase, templateInstance := service.GetTemplateParts() + templateBase, templateInstance, isTemplate := service.GetTemplateParts() // For non-instantiated template service we only support installs if a // DefaultInstance is given. Otherwise we ignore the Install group, but // it is still useful when instantiating the unit via a symlink. - if templateBase != "" && templateInstance == "" { + if isTemplate && templateInstance == "" { if defaultInstance, ok := service.Lookup(quadlet.InstallGroup, "DefaultInstance"); ok { - parts := strings.SplitN(templateBase, "@", 2) - serviceFilename = parts[0] + "@" + defaultInstance + parts[1] + serviceFilename = templateBase + "@" + defaultInstance + filepath.Ext(serviceFilename) } else { serviceFilename = "" } diff --git a/contrib/pkginstaller/Makefile b/contrib/pkginstaller/Makefile index b9ac4b44ec..9f1687b955 100644 --- a/contrib/pkginstaller/Makefile +++ b/contrib/pkginstaller/Makefile @@ -8,7 +8,7 @@ else endif GVPROXY_VERSION ?= 0.7.3 VFKIT_VERSION ?= 0.5.1 -KRUNKIT_VERSION ?= 0.1.1 +KRUNKIT_VERSION ?= 0.1.2 GVPROXY_RELEASE_URL ?= https://github.com/containers/gvisor-tap-vsock/releases/download/v$(GVPROXY_VERSION)/gvproxy-darwin VFKIT_RELEASE_URL ?= https://github.com/crc-org/vfkit/releases/download/v$(VFKIT_VERSION)/vfkit-unsigned KRUNKIT_RELEASE_URL ?= https://github.com/containers/krunkit/releases/download/v$(KRUNKIT_VERSION)/krunkit-podman-unsigned-$(KRUNKIT_VERSION).tgz diff --git a/docs/source/markdown/podman-machine-init.1.md.in b/docs/source/markdown/podman-machine-init.1.md.in index ae8a1bf639..ccecf110e5 100644 --- a/docs/source/markdown/podman-machine-init.1.md.in +++ b/docs/source/markdown/podman-machine-init.1.md.in @@ -160,10 +160,6 @@ Example: `-v "$HOME/git:$HOME/git:ro,security_model=none"` Default volume mounts are defined in *containers.conf*. Unless changed, the default values is `$HOME:$HOME`. -#### **--volume-driver** - -Driver to use for mounting volumes from the host, such as `virtfs`. - ## EXAMPLES Initialize the default Podman machine, pulling the content from the internet. diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index 56732e11c0..81dd92c1e1 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -50,11 +50,15 @@ other sections are passed on untouched, allowing the use of any normal systemd c like dependencies or cgroup limits. The source files also support drop-ins in the same [way systemd does](https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html). -For a given source file (say `foo.container`), the corresponding `.d`directory (in this -case `foo.container.d`) will be scanned for files with a `.conf` extension that are merged into -the base file in alphabetical order. The format of these drop-in files is the same as the base file. -This is useful to alter or add configuration settings for a unit, without having to modify unit -files. +For a given source file (`foo.container`), the corresponding `.d` directory (`foo.container.d`) will +be scanned for files with a `.conf` extension, which are then merged into the base file in alphabetical +order. Top-level type drop-ins (`container.d`) will also be included. If the unit contains dashes ("-") +in the name (`foo-bar-baz.container`), then the drop-in directories generated by truncating the name after +the dash are searched as well (`foo-.container.d` and `foo-bar-.container.d`). Drop-in files with the same name +further down the hierarchy override those further up (`foo-bar-baz.container.d/10-override.conf` overrides +`foo-bar-.container.d/10-override.conf`, which overrides `foo-.service.d/10-override.conf`, which overrides +`container.d/10-override.conf`). The format of these drop-in files is the same as the base file. This is useful +to alter or add configuration settings for a unit, without having to modify unit files. For rootless containers, when administrators place Quadlet files in the /etc/containers/systemd/users directory, all users' sessions execute the @@ -302,6 +306,7 @@ Valid options for `[Container]` are listed below: | SecurityLabelNested=true | --security-opt label=nested | | SecurityLabelType=spc_t | --security-opt label=type:spc_t | | ShmSize=100m | --shm-size=100m | +| StopSignal=SIGINT | --stop-signal=SIGINT | | StopTimeout=20 | --stop-timeout=20 | | SubGIDMap=gtest | --subgidname=gtest | | SubUIDMap=utest | --subuidname=utest | @@ -727,6 +732,12 @@ Size of /dev/shm. This is equivalent to the Podman `--shm-size` option and generally has the form `number[unit]` +### `StopSignal=` + +Signal to stop a container. Default is **SIGTERM**. + +This is equivalent to the Podman `--stop-signal` option + ### `StopTimeout=` Seconds to wait before forcibly stopping the container. diff --git a/go.mod b/go.mod index 798748c6b8..f299af04e6 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/checkpoint-restore/go-criu/v7 v7.1.0 github.com/containernetworking/plugins v1.5.1 github.com/containers/buildah v1.36.1-0.20240715114330-4a82e0a3f382 - github.com/containers/common v0.59.1-0.20240715151621-fdf625dfee0e + github.com/containers/common v0.59.1-0.20240717135212-fdbae3a180cb github.com/containers/conmon v2.0.20+incompatible github.com/containers/gvisor-tap-vsock v0.7.4-0.20240515153903-01a1a0cd3f70 github.com/containers/image/v5 v5.31.1-0.20240711123249-1dbd8fbbe516 @@ -60,14 +60,14 @@ require ( github.com/opencontainers/runtime-tools v0.9.1-0.20230914150019-408c51e934dc github.com/opencontainers/selinux v1.11.0 github.com/openshift/imagebuilder v1.2.11 - github.com/rootless-containers/rootlesskit/v2 v2.1.0 + github.com/rootless-containers/rootlesskit/v2 v2.2.0 github.com/shirou/gopsutil/v3 v3.24.5 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 - github.com/vbauerster/mpb/v8 v8.7.3 + github.com/vbauerster/mpb/v8 v8.7.4 github.com/vishvananda/netlink v1.2.1-beta.2 go.etcd.io/bbolt v1.3.10 golang.org/x/crypto v0.25.0 @@ -200,7 +200,7 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect + github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/ulikunitz/xz v0.5.12 // indirect github.com/vbatts/tar-split v0.11.5 // indirect diff --git a/go.sum b/go.sum index d1f4205827..1ce96d6225 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ github.com/containernetworking/plugins v1.5.1 h1:T5ji+LPYjjgW0QM+KyrigZbLsZ8jaX+ github.com/containernetworking/plugins v1.5.1/go.mod h1:MIQfgMayGuHYs0XdNudf31cLLAC+i242hNm6KuDGqCM= github.com/containers/buildah v1.36.1-0.20240715114330-4a82e0a3f382 h1:NUScZGjAC6Cd1KuPcnCac10Q/gz01PULzh7Em/VXZOc= github.com/containers/buildah v1.36.1-0.20240715114330-4a82e0a3f382/go.mod h1:HlwJHYRlP5j8siiPY46I8py00hlGxWPC/vCZZ/01EEU= -github.com/containers/common v0.59.1-0.20240715151621-fdf625dfee0e h1:x6PiZObWn9XD9lcvC6ShqDvvoTsRBktY8ycuwhlWWug= -github.com/containers/common v0.59.1-0.20240715151621-fdf625dfee0e/go.mod h1:KrQ9y5qa7TBVzp7qs7I1MVi6Uxntu0hM5wjd5bmvMnM= +github.com/containers/common v0.59.1-0.20240717135212-fdbae3a180cb h1:9OgHqOACzWRzPFewtx/lOSKnPvWAMMlW3ruvXQL4fP4= +github.com/containers/common v0.59.1-0.20240717135212-fdbae3a180cb/go.mod h1:KrQ9y5qa7TBVzp7qs7I1MVi6Uxntu0hM5wjd5bmvMnM= github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg= github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/gvisor-tap-vsock v0.7.4-0.20240515153903-01a1a0cd3f70 h1:aACcXSIgcuPq5QdNZZ8B53BCdhqYvw33/8QmZWJATvg= @@ -294,15 +294,14 @@ github.com/hugelgupf/vmtest v0.0.0-20230810222836-f8c8e381617c h1:4A+BVHylCBQPxl github.com/hugelgupf/vmtest v0.0.0-20230810222836-f8c8e381617c/go.mod h1:d2FMzS0rIF+3Daufcw660EZfTJihdNPeEwBBJgO4Ap0= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c h1:P/3mFnHCv1A/ej4m8pF5EB6FUt9qEL2Q9lfrcUNwCYs= -github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4= +github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9 h1:LZJWucZz7ztCqY6Jsu7N9g124iJ2kt/O62j3+UchZFg= +github.com/insomniacslk/dhcp v0.0.0-20240710054256-ddd8a41251c9/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -407,9 +406,8 @@ github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f h1:/UDgs8FGMqw github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= -github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= @@ -435,8 +433,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rootless-containers/rootlesskit/v2 v2.1.0 h1:dKqduSlzo5TlTv7tTIoTct2cRUNQf+soqcs+6b1ynvE= -github.com/rootless-containers/rootlesskit/v2 v2.1.0/go.mod h1:W+5NaXv3l2sD4LiPxRWLOlY+p9H0+Ev71zel/zFRnLo= +github.com/rootless-containers/rootlesskit/v2 v2.2.0 h1:qTA3QyfsZSKmvKDL7m5lCl55OmJZXmKf381dSb5Ig9E= +github.com/rootless-containers/rootlesskit/v2 v2.2.0/go.mod h1:AyUlpgPnMv085smZXp5mt4D9q4mNjbHOcZwaVobRzyU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y= github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= @@ -502,16 +500,16 @@ github.com/u-root/gobusybox/src v0.0.0-20230806212452-e9366a5b9fdc h1:udgfN9Qy57 github.com/u-root/gobusybox/src v0.0.0-20230806212452-e9366a5b9fdc/go.mod h1:lYt+LVfZBBwDZ3+PHk4k/c/TnKOkjJXiJO73E32Mmpc= github.com/u-root/u-root v0.11.1-0.20230807200058-f87ad7ccb594 h1:1AIJqOtdEufYfGb3eRpdaqWONzBOpAwrg1fehbWg+Mg= github.com/u-root/u-root v0.11.1-0.20230807200058-f87ad7ccb594/go.mod h1:PQzg9XJGp6Y1hRmTUruSO7lR7kKR6FpoSObf5n5bTfE= -github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg= -github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= +github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM= +github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= -github.com/vbauerster/mpb/v8 v8.7.3 h1:n/mKPBav4FFWp5fH4U0lPpXfiOmCEgl5Yx/NM3tKJA0= -github.com/vbauerster/mpb/v8 v8.7.3/go.mod h1:9nFlNpDGVoTmQ4QvNjSLtwLmAFjwmq0XaAF26toHGNM= +github.com/vbauerster/mpb/v8 v8.7.4 h1:p4f16iMfUt3PkAC73SCzAtgtSf8TYDqEbJUT3odPrPo= +github.com/vbauerster/mpb/v8 v8.7.4/go.mod h1:r1B5k2Ljj5KJFCekfihbiqyV4VaaRTANYmvWA2btufI= github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= @@ -636,7 +634,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/hack/xref-docker-options b/hack/xref-docker-options index d3a37d5853..133d678f36 100755 --- a/hack/xref-docker-options +++ b/hack/xref-docker-options @@ -14,7 +14,6 @@ declare -a hidden=( "docker -D" # Hidden "docker -H" # Hidden - "docker .* --gpus" # Not likely to be implemented "docker .* --isolation" # Only for Windows containers "docker .* --kernel-memory" # CGroupsV1 only, present but hidden "docker .* --link" # Deprecated in docker diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go index 3b027e63ec..95f20f2fd1 100644 --- a/libpod/healthcheck.go +++ b/libpod/healthcheck.go @@ -4,6 +4,7 @@ package libpod import ( "bufio" + "bytes" "context" "errors" "fmt" @@ -87,30 +88,17 @@ func (c *Container) runHealthCheck(ctx context.Context, isStartup bool) (define. if len(newCommand) < 1 || newCommand[0] == "" { return define.HealthCheckNotDefined, "", fmt.Errorf("container %s has no defined healthcheck", c.ID()) } - rPipe, wPipe, err := os.Pipe() - if err != nil { - return define.HealthCheckInternalError, "", fmt.Errorf("unable to create pipe for healthcheck session: %w", err) - } - defer wPipe.Close() - defer rPipe.Close() streams := new(define.AttachStreams) + output := &bytes.Buffer{} streams.InputStream = bufio.NewReader(os.Stdin) - streams.OutputStream = wPipe - streams.ErrorStream = wPipe + streams.OutputStream = output + streams.ErrorStream = output streams.AttachOutput = true streams.AttachError = true streams.AttachInput = true - stdout := []string{} - go func() { - scanner := bufio.NewScanner(rPipe) - for scanner.Scan() { - stdout = append(stdout, scanner.Text()) - } - }() - logrus.Debugf("executing health check command %s for %s", strings.Join(newCommand, " "), c.ID()) timeStart := time.Now() hcResult := define.HealthCheckSuccess @@ -154,7 +142,7 @@ func (c *Container) runHealthCheck(ctx context.Context, isStartup bool) (define. } } - eventLog := strings.Join(stdout, "\n") + eventLog := output.String() if len(eventLog) > MaxHealthCheckLogLength { eventLog = eventLog[:MaxHealthCheckLogLength] } diff --git a/libpod/pod.go b/libpod/pod.go index 6057492417..1a3ee3cb42 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -459,7 +459,7 @@ type PodContainerStats struct { } // GetPodStats returns the stats for each of its containers -func (p *Pod) GetPodStats(previousContainerStats map[string]*define.ContainerStats) (map[string]*define.ContainerStats, error) { +func (p *Pod) GetPodStats() (map[string]*define.ContainerStats, error) { p.lock.Lock() defer p.lock.Unlock() @@ -472,15 +472,15 @@ func (p *Pod) GetPodStats(previousContainerStats map[string]*define.ContainerSta } newContainerStats := make(map[string]*define.ContainerStats) for _, c := range containers { - newStats, err := c.GetContainerStats(previousContainerStats[c.ID()]) - // If the container wasn't running, don't include it - // but also suppress the error - if err != nil && !errors.Is(err, define.ErrCtrStateInvalid) { + newStats, err := c.GetContainerStats(nil) + if err != nil { + // If the container wasn't running ignore it + if errors.Is(err, define.ErrCtrStateInvalid) || errors.Is(err, define.ErrCtrStopped) { + continue + } return nil, err } - if err == nil { - newContainerStats[c.ID()] = newStats - } + newContainerStats[c.ID()] = newStats } return newContainerStats, nil } diff --git a/libpod/sqlite_state.go b/libpod/sqlite_state.go index 912570fa7b..f51cdc3d7a 100644 --- a/libpod/sqlite_state.go +++ b/libpod/sqlite_state.go @@ -39,17 +39,13 @@ const ( sqliteOptionForeignKeys = "&_foreign_keys=1" // Make sure that transactions happen exclusively. sqliteOptionTXLock = "&_txlock=exclusive" - // Make sure busy timeout is set to high value to keep retrying when the db is locked. - // Timeout is in ms, so set it to 100s to have enough time to retry the operations. - sqliteOptionBusyTimeout = "&_busy_timeout=100000" // Assembled sqlite options used when opening the database. sqliteOptions = "db.sql?" + sqliteOptionLocation + sqliteOptionSynchronous + sqliteOptionForeignKeys + - sqliteOptionTXLock + - sqliteOptionBusyTimeout + sqliteOptionTXLock ) // NewSqliteState creates a new SQLite-backed state database. @@ -71,7 +67,18 @@ func NewSqliteState(runtime *Runtime) (_ State, defErr error) { return nil, fmt.Errorf("creating root directory: %w", err) } - conn, err := sql.Open("sqlite3", filepath.Join(basePath, sqliteOptions)) + // Make sure busy timeout is set to high value to keep retrying when the db is locked. + // Timeout is in ms, so set it to 100s to have enough time to retry the operations. + // Some users might want to experiment with different timeout values (#23236) + // DO NOT DOCUMENT or recommend PODMAN_SQLITE_BUSY_TIMEOUT outside of testing. + busyTimeout := "100000" + if env, ok := os.LookupEnv("PODMAN_SQLITE_BUSY_TIMEOUT"); ok { + logrus.Debugf("PODMAN_SQLITE_BUSY_TIMEOUT is set to %s", env) + busyTimeout = env + } + sqliteOptionBusyTimeout := "&_busy_timeout=" + busyTimeout + + conn, err := sql.Open("sqlite3", filepath.Join(basePath, sqliteOptions+sqliteOptionBusyTimeout)) if err != nil { return nil, fmt.Errorf("initializing sqlite database: %w", err) } diff --git a/pkg/autoupdate/autoupdate.go b/pkg/autoupdate/autoupdate.go index 5b2ac829f2..df6627e378 100644 --- a/pkg/autoupdate/autoupdate.go +++ b/pkg/autoupdate/autoupdate.go @@ -4,6 +4,7 @@ package autoupdate import ( "context" + "errors" "fmt" "os" "sort" @@ -367,12 +368,15 @@ func (u *updater) assembleTasks(ctx context.Context) []error { u.unitToTasks = make(map[string][]*task) - errors := []error{} + errs := []error{} for _, c := range allContainers { ctr := c state, err := ctr.State() if err != nil { - errors = append(errors, err) + // container may have been removed in the meantime ignore it and not print errors + if !errors.Is(err, define.ErrNoSuchCtr) { + errs = append(errs, err) + } continue } // Only update running containers. @@ -389,7 +393,7 @@ func (u *updater) assembleTasks(ctx context.Context) []error { } policy, err := LookupPolicy(value) if err != nil { - errors = append(errors, err) + errs = append(errs, err) continue } if policy == PolicyDefault { @@ -400,11 +404,11 @@ func (u *updater) assembleTasks(ctx context.Context) []error { // stored as a label at container creation. unit, exists, err := u.systemdUnitForContainer(ctr, labels) if err != nil { - errors = append(errors, err) + errs = append(errs, err) continue } if !exists { - errors = append(errors, fmt.Errorf("auto-updating container %q: no %s label found", ctr.ID(), systemdDefine.EnvVariable)) + errs = append(errs, fmt.Errorf("auto-updating container %q: no %s label found", ctr.ID(), systemdDefine.EnvVariable)) continue } @@ -412,13 +416,13 @@ func (u *updater) assembleTasks(ctx context.Context) []error { image, exists := imageMap[id] if !exists { err := fmt.Errorf("internal error: no image found for ID %s", id) - errors = append(errors, err) + errs = append(errs, err) continue } rawImageName := ctr.RawImageName() if rawImageName == "" { - errors = append(errors, fmt.Errorf("locally auto-updating container %q: raw-image name is empty", ctr.ID())) + errs = append(errs, fmt.Errorf("locally auto-updating container %q: raw-image name is empty", ctr.ID())) continue } @@ -443,7 +447,7 @@ func (u *updater) assembleTasks(ctx context.Context) []error { u.unitToTasks[unit] = append(u.unitToTasks[unit], &t) } - return errors + return errs } // systemdUnitForContainer returns the name of the container's systemd unit. diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index b350c68e13..e2356e9d6b 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -1581,7 +1581,14 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri for _, ctr := range containers { stats, err := ctr.GetContainerStats(containerStats[ctr.ID()]) if err != nil { - if queryAll && (errors.Is(err, define.ErrCtrRemoved) || errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrStateInvalid)) { + if queryAll && + // All these errors might happen while we get stats, when we list all + // they must be skipped as they cause podman stats to stop and error otherwise. + // ErrCtrStopped can happen when the container process exited before we could + // update the container state + // https://github.com/containers/podman/issues/23334 + (errors.Is(err, define.ErrCtrRemoved) || errors.Is(err, define.ErrNoSuchCtr) || + errors.Is(err, define.ErrCtrStateInvalid) || errors.Is(err, define.ErrCtrStopped)) { continue } return nil, err diff --git a/pkg/domain/infra/abi/pods_stats.go b/pkg/domain/infra/abi/pods_stats.go index 8ece02c2c6..26d070344e 100644 --- a/pkg/domain/infra/abi/pods_stats.go +++ b/pkg/domain/infra/abi/pods_stats.go @@ -8,6 +8,7 @@ import ( "github.com/containers/common/pkg/cgroups" "github.com/containers/podman/v5/libpod" + "github.com/containers/podman/v5/libpod/define" "github.com/containers/podman/v5/pkg/domain/entities" "github.com/containers/podman/v5/pkg/rootless" "github.com/docker/go-units" @@ -37,8 +38,12 @@ func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, op func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.PodStatsReport, error) { reports := []*entities.PodStatsReport{} for i := range pods { // Access by index to prevent potential loop-variable leaks. - podStats, err := pods[i].GetPodStats(nil) + podStats, err := pods[i].GetPodStats() if err != nil { + // pod was removed, skip it + if errors.Is(err, define.ErrNoSuchPod) { + continue + } return nil, err } podID := pods[i].ID()[:12] diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 2da7b6e5fc..5da8aba326 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -198,23 +198,24 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System iid, _ := c.Image() state, err := c.State() if err != nil { + if errors.Is(err, define.ErrNoSuchCtr) { + continue + } return nil, fmt.Errorf("failed to get state of container %s: %w", c.ID(), err) } conSize, err := c.RootFsSize() if err != nil { - if errors.Is(err, storage.ErrContainerUnknown) { - logrus.Error(fmt.Errorf("failed to get root file system size of container %s: %w", c.ID(), err)) - } else { - return nil, fmt.Errorf("failed to get root file system size of container %s: %w", c.ID(), err) + if errors.Is(err, storage.ErrContainerUnknown) || errors.Is(err, define.ErrNoSuchCtr) { + continue } + return nil, fmt.Errorf("failed to get root file system size of container %s: %w", c.ID(), err) } rwsize, err := c.RWSize() if err != nil { - if errors.Is(err, storage.ErrContainerUnknown) { - logrus.Error(fmt.Errorf("failed to get read/write size of container %s: %w", c.ID(), err)) - } else { - return nil, fmt.Errorf("failed to get read/write size of container %s: %w", c.ID(), err) + if errors.Is(err, storage.ErrContainerUnknown) || errors.Is(err, define.ErrNoSuchCtr) { + continue } + return nil, fmt.Errorf("failed to get read/write size of container %s: %w", c.ID(), err) } report := entities.SystemDfContainerReport{ ContainerID: c.ID(), @@ -241,6 +242,9 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System var reclaimableSize int64 mountPoint, err := v.MountPoint() if err != nil { + if errors.Is(err, define.ErrNoSuchVolume) { + continue + } return nil, err } if mountPoint == "" { @@ -255,6 +259,9 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System } inUse, err := v.VolumeInUse() if err != nil { + if errors.Is(err, define.ErrNoSuchVolume) { + continue + } return nil, err } if len(inUse) == 0 { diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 2b8daeba78..0fb142b549 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -38,7 +38,11 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) filters := make(map[string][]string, len(opts.Filter)) for _, filter := range opts.Filter { f := strings.SplitN(filter, "=", 2) - filters[f[0]] = append(filters[f[0]], f[1]) + if len(f) > 1 { + filters[f[0]] = append(filters[f[0]], f[1]) + } else { + filters[f[0]] = append(filters[f[0]], "") + } } options := new(images.ListOptions).WithAll(opts.All).WithFilters(filters) psImages, err := images.List(ir.ClientCtx, options) diff --git a/pkg/farm/farm.go b/pkg/farm/farm.go index a1cc62610a..cc8d803fb2 100644 --- a/pkg/farm/farm.go +++ b/pkg/farm/farm.go @@ -61,7 +61,7 @@ func newFarmWithBuilders(_ context.Context, name string, cons []config.Connectio defer fmt.Printf("Builder %q ready\n", con.Name) builderMutex.Lock() defer builderMutex.Unlock() - farm.builders[name] = engine + farm.builders[con.Name] = engine return nil }) } diff --git a/pkg/machine/compression/decompress.go b/pkg/machine/compression/decompress.go index c13335307b..5e2448f5db 100644 --- a/pkg/machine/compression/decompress.go +++ b/pkg/machine/compression/decompress.go @@ -66,18 +66,27 @@ func runDecompression(d decompressor, decompressedFilePath string) (retErr error } defer d.close() - initMsg := progressBarPrefix + ": " + filepath.Base(decompressedFilePath) - finalMsg := initMsg + ": done" + filesize := d.compressedFileSize() + // bar.ProxyReader() returns nil when the bar is finished. + // When the size is set to 0 the bar will finfish immediately, but this happens + // in a different goroutine so it is race and only happens sometimes. + // In general if the input is an empty file then we do not have to display a + // progress bar at all as there is nothing to copy/extract really + // https://github.com/containers/podman/issues/23281 + if filesize > 0 { + initMsg := progressBarPrefix + ": " + filepath.Base(decompressedFilePath) + finalMsg := initMsg + ": done" - p, bar := utils.ProgressBar(initMsg, d.compressedFileSize(), finalMsg) - // Wait for bars to complete and then shut down the bars container - defer p.Wait() + p, bar := utils.ProgressBar(initMsg, filesize, finalMsg) + // Wait for bars to complete and then shut down the bars container + defer p.Wait() - compressedFileReaderProxy := bar.ProxyReader(compressedFileReader) - // Interrupts the bar goroutine. It's important that - // bar.Abort(false) is called before p.Wait(), otherwise - // can hang. - defer bar.Abort(false) + compressedFileReader = bar.ProxyReader(compressedFileReader) + // Interrupts the bar goroutine. It's important that + // bar.Abort(false) is called before p.Wait(), otherwise + // can hang. + defer bar.Abort(false) + } var decompressedFileWriter *os.File @@ -94,7 +103,7 @@ func runDecompression(d decompressor, decompressedFilePath string) (retErr error } }() - if err = d.decompress(decompressedFileWriter, compressedFileReaderProxy); err != nil { + if err = d.decompress(decompressedFileWriter, compressedFileReader); err != nil { logrus.Errorf("Error extracting compressed file: %q", err) return err } diff --git a/pkg/machine/define/initopts.go b/pkg/machine/define/initopts.go index fcf922958b..4ddf87ca19 100644 --- a/pkg/machine/define/initopts.go +++ b/pkg/machine/define/initopts.go @@ -8,7 +8,6 @@ type InitOptions struct { IgnitionPath string Image string Volumes []string - VolumeDriver string IsDefault bool Memory uint64 Name string diff --git a/pkg/machine/e2e/basic_test.go b/pkg/machine/e2e/basic_test.go index 79d97eaa12..daed59828f 100644 --- a/pkg/machine/e2e/basic_test.go +++ b/pkg/machine/e2e/basic_test.go @@ -117,6 +117,33 @@ var _ = Describe("run basic podman commands", func() { Expect(findmnt.outputToString()).To(ContainSubstring("virtiofs")) }) + It("Volume should be disabled by command line", func() { + skipIfWSL("Requires standard volume handling") + skipIfVmtype(define.AppleHvVirt, "Skipped on Apple platform") + skipIfVmtype(define.LibKrun, "Skipped on Apple platform") + + name := randomString() + i := new(initMachine).withImage(mb.imagePath).withNow() + + // Empty arg forces no volumes + i.withVolume("") + session, err := mb.setName(name).setCmd(i).run() + Expect(err).ToNot(HaveOccurred()) + Expect(session).To(Exit(0)) + + ssh9p := new(sshMachine).withSSHCommand([]string{"findmnt", "-no", "FSTYPE", "-t", "9p"}) + findmnt9p, err := mb.setName(name).setCmd(ssh9p).run() + Expect(err).ToNot(HaveOccurred()) + Expect(findmnt9p).To(Exit(0)) + Expect(findmnt9p.outputToString()).To(BeEmpty()) + + sshVirtiofs := new(sshMachine).withSSHCommand([]string{"findmnt", "-no", "FSTYPE", "-t", "virtiofs"}) + findmntVirtiofs, err := mb.setName(name).setCmd(sshVirtiofs).run() + Expect(err).ToNot(HaveOccurred()) + Expect(findmntVirtiofs).To(Exit(0)) + Expect(findmntVirtiofs.outputToString()).To(BeEmpty()) + }) + It("Podman ops with port forwarding and gvproxy", func() { name := randomString() i := new(initMachine) diff --git a/pkg/machine/qemu/stubber.go b/pkg/machine/qemu/stubber.go index 576d30bbcc..903d56c285 100644 --- a/pkg/machine/qemu/stubber.go +++ b/pkg/machine/qemu/stubber.go @@ -168,9 +168,13 @@ func (q *QEMUStubber) StartVM(mc *vmconfigs.MachineConfig) (func() error, func() if err != nil { return nil, nil, err } - spawner, err := newVirtiofsdSpawner(runtime) - if err != nil { - return nil, nil, err + + var spawner *virtiofsdSpawner + if len(mc.Mounts) > 0 { + spawner, err = newVirtiofsdSpawner(runtime) + if err != nil { + return nil, nil, err + } } for _, hostmnt := range mc.Mounts { diff --git a/pkg/machine/shim/volume.go b/pkg/machine/shim/volume.go index a145f58474..e67e577dbe 100644 --- a/pkg/machine/shim/volume.go +++ b/pkg/machine/shim/volume.go @@ -8,6 +8,9 @@ import ( func CmdLineVolumesToMounts(volumes []string, volumeType vmconfigs.VolumeMountType) []*vmconfigs.Mount { mounts := []*vmconfigs.Mount{} for i, volume := range volumes { + if volume == "" { + continue + } var mount vmconfigs.Mount tag, source, target, readOnly, _ := vmconfigs.SplitVolume(i, volume) switch volumeType { diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index 243d238715..755e04c08f 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -76,7 +76,8 @@ func GetContainerLists(runtime *libpod.Runtime, options entities.ContainerListOp for _, con := range cons { listCon, err := ListContainerBatch(runtime, con, options) switch { - case errors.Is(err, define.ErrNoSuchCtr): + // ignore both no ctr and no such pod errors as it means the ctr is gone now + case errors.Is(err, define.ErrNoSuchCtr), errors.Is(err, define.ErrNoSuchPod): continue case err != nil: return nil, err @@ -148,6 +149,7 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities networks []string healthStatus string restartCount uint + podName string ) batchErr := ctr.Batch(func(c *libpod.Container) error { @@ -201,10 +203,6 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities return err } - if !opts.Size && !opts.Namespace { - return nil - } - if opts.Namespace { ctrPID := strconv.Itoa(pid) cgroup, _ = getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup")) @@ -231,6 +229,14 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities size.RootFsSize = rootFsSize size.RwSize = rwSize } + + if opts.Pod && len(conConfig.Pod) > 0 { + podName, err = rt.GetPodName(conConfig.Pod) + if err != nil { + return fmt.Errorf("could not find container %s pod (id %s) in state: %w", conConfig.ID, conConfig.Pod, err) + } + } + return nil }) if batchErr != nil { @@ -256,6 +262,7 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities Networks: networks, Pid: pid, Pod: conConfig.Pod, + PodName: podName, Ports: portMappings, Restarts: restartCount, Size: size, @@ -263,16 +270,6 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities State: conState.String(), Status: healthStatus, } - if opts.Pod && len(conConfig.Pod) > 0 { - podName, err := rt.GetPodName(conConfig.Pod) - if err != nil { - if errors.Is(err, define.ErrNoSuchCtr) { - return entities.ListContainer{}, fmt.Errorf("could not find container %s pod (id %s) in state: %w", conConfig.ID, conConfig.Pod, define.ErrNoSuchPod) - } - return entities.ListContainer{}, err - } - ps.PodName = podName - } if opts.Namespace { ps.Namespaces = entities.ListContainerNamespaces{ diff --git a/pkg/systemd/parser/unitfile.go b/pkg/systemd/parser/unitfile.go index 9ecbf4a158..84be6ab623 100644 --- a/pkg/systemd/parser/unitfile.go +++ b/pkg/systemd/parser/unitfile.go @@ -939,14 +939,63 @@ func (f *UnitFile) PrependUnitLine(groupName string, key string, value string) { group.prependLine(newUnitLine(key, value, false)) } -func (f *UnitFile) GetTemplateParts() (string, string) { +func (f *UnitFile) GetTemplateParts() (string, string, bool) { ext := filepath.Ext(f.Filename) basename := strings.TrimSuffix(f.Filename, ext) parts := strings.SplitN(basename, "@", 2) if len(parts) < 2 { - return "", "" + return parts[0], "", false } - return parts[0] + "@" + ext, parts[1] + return parts[0], parts[1], true +} + +func (f *UnitFile) GetUnitDropinPaths() []string { + unitName, instanceName, isTemplate := f.GetTemplateParts() + + ext := filepath.Ext(f.Filename) + dropinExt := ext + ".d" + + dropinPaths := []string{} + + // Add top-level drop-in location (pod.d, container.d, etc) + topLevelDropIn := strings.TrimPrefix(dropinExt, ".") + dropinPaths = append(dropinPaths, topLevelDropIn) + + truncatedParts := strings.Split(unitName, "-") + // If the unit contains any '-', then there are truncated paths to search. + if len(truncatedParts) > 1 { + // We don't need the last item because that would be the full path + truncatedParts = truncatedParts[:len(truncatedParts)-1] + // Truncated instance names are not included in the drop-in search path + // i.e. template-unit@base-instance.service does not search template-unit@base-.service + // So we only search truncations of the template name, i.e. template-@.service, and unit name, i.e. template-.service + // or only the unit name if it is not a template. + for i := range truncatedParts { + truncatedUnitPath := strings.Join(truncatedParts[:i+1], "-") + "-" + dropinPaths = append(dropinPaths, truncatedUnitPath+dropinExt) + // If the unit is a template, add the truncated template name as well. + if isTemplate { + truncatedTemplatePath := truncatedUnitPath + "@" + dropinPaths = append(dropinPaths, truncatedTemplatePath+dropinExt) + } + } + } + // For instanced templates, add the base template unit search path + if instanceName != "" { + dropinPaths = append(dropinPaths, unitName+"@"+dropinExt) + } + // Add the drop-in directory for the full filename + dropinPaths = append(dropinPaths, f.Filename+".d") + // Finally, reverse the list so that when drop-ins are parsed, + // the most specific are applied instead of the most broad. + // dropinPaths should be a list where the items are in order of specific -> broad + // i.e., the most specific search path is dropinPaths[0], and broadest search path is dropinPaths[len(dropinPaths)-1] + // Uses https://go.dev/wiki/SliceTricks#reversing + for i := len(dropinPaths)/2 - 1; i >= 0; i-- { + opp := len(dropinPaths) - 1 - i + dropinPaths[i], dropinPaths[opp] = dropinPaths[opp], dropinPaths[i] + } + return dropinPaths } func PathEscape(path string) string { diff --git a/pkg/systemd/parser/unitfile_test.go b/pkg/systemd/parser/unitfile_test.go index bb56d1b5fb..ea805ddcdc 100644 --- a/pkg/systemd/parser/unitfile_test.go +++ b/pkg/systemd/parser/unitfile_test.go @@ -1,6 +1,7 @@ package parser import ( + "reflect" "testing" "github.com/stretchr/testify/assert" @@ -226,6 +227,43 @@ Also=systemd-networkd-wait-online.service var samples = []string{memcachedService, systemdloginService, systemdnetworkdService} +const sampleDropinService string = "sample-unit.service" + +var sampleDropinServicePaths = []string{ + "sample-unit.service.d", + "sample-.service.d", + "service.d", +} + +const sampleDropinTemplate string = "sample-template-unit@.service" + +var sampleDropinTemplatePaths = []string{ + "sample-template-unit@.service.d", + "sample-template-@.service.d", + "sample-template-.service.d", + "sample-@.service.d", + "sample-.service.d", + "service.d", +} + +const sampleDropinTemplateInstance string = "sample-template-unit@base-instance.service" + +var sampleDropinTemplateInstancePaths = []string{ + "sample-template-unit@base-instance.service.d", + "sample-template-unit@.service.d", + "sample-template-@.service.d", + "sample-template-.service.d", + "sample-@.service.d", + "sample-.service.d", + "service.d", +} + +var sampleDropinPaths = map[string][]string{ + sampleDropinService: sampleDropinServicePaths, + sampleDropinTemplate: sampleDropinTemplatePaths, + sampleDropinTemplateInstance: sampleDropinTemplateInstancePaths, +} + func TestRanges_Roundtrip(t *testing.T) { for i := range samples { sample := samples[i] @@ -243,3 +281,14 @@ func TestRanges_Roundtrip(t *testing.T) { assert.Equal(t, sample, asStr) } } + +func TestUnitDropinPaths_Search(t *testing.T) { + for filename, expectedPaths := range sampleDropinPaths { + f := UnitFile{ + Filename: filename, + } + generatedPaths := f.GetUnitDropinPaths() + + assert.True(t, reflect.DeepEqual(expectedPaths, generatedPaths)) + } +} diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 98b0221d3a..c21ec2a151 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -143,6 +143,7 @@ const ( KeySecurityLabelType = "SecurityLabelType" KeySetWorkingDirectory = "SetWorkingDirectory" KeyShmSize = "ShmSize" + KeyStopSignal = "StopSignal" KeyStopTimeout = "StopTimeout" KeySubGIDMap = "SubGIDMap" KeySubnet = "Subnet" @@ -242,6 +243,7 @@ var ( KeySecurityLabelNested: true, KeySecurityLabelType: true, KeyShmSize: true, + KeyStopSignal: true, KeyStopTimeout: true, KeySubGIDMap: true, KeySubUIDMap: true, @@ -843,6 +845,10 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse return nil, err } + if stopSignal, ok := container.Lookup(ContainerGroup, KeyStopSignal); ok && len(stopSignal) > 0 { + podman.add("--stop-signal", stopSignal) + } + if stopTimeout, ok := container.Lookup(ContainerGroup, KeyStopTimeout); ok && len(stopTimeout) > 0 { podman.add("--stop-timeout", stopTimeout) } diff --git a/test/apiv2/python/requirements.txt b/test/apiv2/python/requirements.txt index a743f43cf2..89217678e2 100644 --- a/test/apiv2/python/requirements.txt +++ b/test/apiv2/python/requirements.txt @@ -1,6 +1,6 @@ requests-mock~=1.12.1 requests~=2.32.3 -setuptools~=70.3.0 +setuptools~=71.0.0 python-dateutil~=2.9.0 PyYAML~=6.0.0 openapi-schema-validator~=0.6.2 diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go index b219b00f7c..de71a38ca0 100644 --- a/test/e2e/healthcheck_run_test.go +++ b/test/e2e/healthcheck_run_test.go @@ -41,25 +41,22 @@ var _ = Describe("Podman healthcheck run", func() { }) It("podman run healthcheck and logs should contain healthcheck output", func() { - session := podmanTest.Podman([]string{"run", "--name", "test-logs", "-dt", "--health-interval", "1s", "--health-cmd", "echo working", "busybox", "sleep", "3600"}) + session := podmanTest.Podman([]string{"run", "--name", "test-logs", "-dt", "--health-interval", "1s", + // echo -n is important for https://github.com/containers/podman/issues/23332 + "--health-cmd", "echo -n working", ALPINE, "sleep", "3600"}) session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) - // Buy a little time to get container running - for i := 0; i < 5; i++ { - hc := podmanTest.Podman([]string{"healthcheck", "run", "test-logs"}) - hc.WaitWithDefaultTimeout() - exitCode := hc.ExitCode() - if exitCode == 0 || i == 4 { - break - } - time.Sleep(1 * time.Second) - } - - hc := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.State.Healthcheck.Log}}", "test-logs"}) + hc := podmanTest.Podman([]string{"healthcheck", "run", "test-logs"}) hc.WaitWithDefaultTimeout() Expect(hc).Should(ExitCleanly()) - Expect(hc.OutputToString()).To(ContainSubstring("working")) + + // using json formatter here to make sure the newline is not part of the output string an just added by podman inspect + hc = podmanTest.Podman([]string{"container", "inspect", "--format", "{{json (index .State.Healthcheck.Log 0).Output}}", "test-logs"}) + hc.WaitWithDefaultTimeout() + Expect(hc).Should(ExitCleanly()) + // exact output match for https://github.com/containers/podman/issues/23332 + Expect(string(hc.Out.Contents())).To(Equal("\"working\"\n")) }) It("podman healthcheck from image's config (not container config)", func() { diff --git a/test/e2e/quadlet/stopsignal.container b/test/e2e/quadlet/stopsignal.container new file mode 100644 index 0000000000..b48d8cda9d --- /dev/null +++ b/test/e2e/quadlet/stopsignal.container @@ -0,0 +1,5 @@ +## assert-podman-args "--stop-signal" "SIGKILL" + +[Container] +Image=localhost/imagename +StopSignal=SIGKILL diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 1d3b4b7644..7ba4e6269b 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -854,6 +854,7 @@ BOGUS=foo Entry("selinux.container", "selinux.container", 0, ""), Entry("shmsize.container", "shmsize.container", 0, ""), Entry("shortname.container", "shortname.container", 0, "Warning: shortname.container specifies the image \"shortname\" which not a fully qualified image name. This is not ideal for performance and security reasons. See the podman-pull manpage discussion of short-name-aliases.conf for details."), + Entry("stopsigal.container", "stopsignal.container", 0, ""), Entry("stoptimeout.container", "stoptimeout.container", 0, ""), Entry("subidmapping.container", "subidmapping.container", 0, ""), Entry("subidmapping-with-remap.container", "subidmapping-with-remap.container", 1, "converting \"subidmapping-with-remap.container\": deprecated Remap keys are set along with explicit mapping keys"), diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index cc47434b2d..813de9f737 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -3,10 +3,12 @@ package integration import ( "encoding/json" "fmt" + "io" "net" "os" "strconv" "strings" + "sync" "syscall" "github.com/containernetworking/plugins/pkg/ns" @@ -543,56 +545,58 @@ EXPOSE 2004-2005/tcp`, ALPINE) } }) - It("podman run network bind to 127.0.0.1", func() { - slirp4netnsHelp := SystemExec("slirp4netns", []string{"--help"}) - Expect(slirp4netnsHelp).Should(ExitCleanly()) - networkConfiguration := "slirp4netns:outbound_addr=127.0.0.1,allow_host_loopback=true" - port := strconv.Itoa(GetPort()) - - if strings.Contains(slirp4netnsHelp.OutputToString(), "outbound-addr") { - ncListener := StartSystemExec("nc", []string{"-v", "-n", "-l", "-p", port}) - session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, "-dt", ALPINE, "nc", "-w", "2", "10.0.2.2", port}) - session.WaitWithDefaultTimeout() - ncListener.WaitWithDefaultTimeout() - - Expect(session).Should(ExitCleanly()) - Expect(ncListener).Should(Exit(0)) - Expect(ncListener.ErrorToString()).To(ContainSubstring("Connection from 127.0.0.1")) - } else { - session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, "-dt", ALPINE, "nc", "-w", "2", "10.0.2.2", port}) - session.WaitWithDefaultTimeout() - Expect(session).To(ExitWithError(125, "outbound_addr not supported")) + for _, local := range []bool{true, false} { + local := local + testName := "HostIP" + if local { + testName = "127.0.0.1" } - }) + It(fmt.Sprintf("podman run network slirp4netns bind to %s", testName), func() { + ip := "127.0.0.1" + if !local { + // Determine our likeliest outgoing IP address + conn, err := net.Dial("udp", "8.8.8.8:80") + Expect(err).ToNot(HaveOccurred()) - It("podman run network bind to HostIP", func() { - // Determine our likeliest outgoing IP address - conn, err := net.Dial("udp", "8.8.8.8:80") - Expect(err).ToNot(HaveOccurred()) + defer conn.Close() + ip = conn.LocalAddr().(*net.UDPAddr).IP.String() + } + port := strconv.Itoa(GetPort()) - defer conn.Close() - ip := conn.LocalAddr().(*net.UDPAddr).IP - port := strconv.Itoa(GetPort()) + networkConfiguration := fmt.Sprintf("slirp4netns:outbound_addr=%s,allow_host_loopback=true", ip) - slirp4netnsHelp := SystemExec("slirp4netns", []string{"--help"}) - Expect(slirp4netnsHelp).Should(ExitCleanly()) - networkConfiguration := fmt.Sprintf("slirp4netns:outbound_addr=%s,allow_host_loopback=true", ip.String()) + listener, err := net.Listen("tcp", ":"+port) + Expect(err).ToNot(HaveOccurred()) + defer listener.Close() - if strings.Contains(slirp4netnsHelp.OutputToString(), "outbound-addr") { - ncListener := StartSystemExec("nc", []string{"-v", "-n", "-l", "-p", port}) - session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, ALPINE, "nc", "-w", "2", "10.0.2.2", port}) - session.Wait(30) - ncListener.Wait(30) + msg := RandomString(10) + wg := &sync.WaitGroup{} + wg.Add(1) + // now use a new goroutine to start accepting connection in the background and make the checks there + go func() { + defer GinkgoRecover() + defer wg.Done() + conn, err := listener.Accept() + Expect(err).ToNot(HaveOccurred(), "accept new connection") + defer conn.Close() + addr := conn.RemoteAddr() + // addr will be in the form ip:port, we don't care about the port as it is random + Expect(addr.String()).To(HavePrefix(ip+":"), "remote address") + gotBytes, err := io.ReadAll(conn) + Expect(err).ToNot(HaveOccurred(), "read from connection") + Expect(string(gotBytes)).To(Equal(msg), "received correct message from container") + }() + session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, ALPINE, "sh", "-c", "echo -n " + msg + " | nc -w 30 10.0.2.2 " + port}) + session.WaitWithDefaultTimeout() Expect(session).Should(ExitCleanly()) - Expect(ncListener).Should(Exit(0)) - Expect(ncListener.ErrorToString()).To(ContainSubstring("Connection from " + ip.String())) - } else { - session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, ALPINE, "nc", "-w", "2", "10.0.2.2", port}) - session.Wait(30) - Expect(session).To(ExitWithError(125, "outbound_addr not supported")) - } - }) + + // explicitly close the socket here before we wait to unlock Accept() calls in case of hangs + listener.Close() + // wait for the checks in the goroutine to be done + wg.Wait() + }) + } It("podman run network expose ports in image metadata", func() { session := podmanTest.Podman([]string{"create", "--name", "test", "-t", "-P", NGINX_IMAGE}) diff --git a/test/python/requirements.txt b/test/python/requirements.txt index 8dc07b72ea..2fa190c048 100644 --- a/test/python/requirements.txt +++ b/test/python/requirements.txt @@ -1,7 +1,7 @@ docker~=6.1.0 requests-mock~=1.12.1 requests~=2.32.3 -setuptools~=70.3.0 +setuptools~=71.0.0 python-dateutil~=2.9.0 PyYAML~=6.0.0 openapi-schema-validator~=0.6.2 diff --git a/test/system/010-images.bats b/test/system/010-images.bats index 43dd1bcfe9..1548615a71 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -62,31 +62,33 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z # podman history is persistent: it permanently alters our base image. # Create a dummy image here so we leave our setup as we found it. # Multiple --name options confirm command-line override (last one wins) - run_podman run --name ignore-me --name my-container $IMAGE true - run_podman commit my-container my-test-image + cname=c_$(safename) + iname=i_$(safename) + run_podman run --name ignore-me --name $cname $IMAGE true + run_podman commit $cname $iname - run_podman images my-test-image --format '{{ .History }}' - is "$output" "localhost/my-test-image:latest" "image history with initial name" + run_podman images $iname --format '{{ .History }}' + is "$output" "localhost/$iname:latest" "image history with initial name" - # Generate two randomish tags; 'tr' because they must be all lower-case - rand_name1="test-image-history-$(random_string 10 | tr A-Z a-z)" - rand_name2="test-image-history-$(random_string 10 | tr A-Z a-z)" + # Generate two randomish tags + rand_name1="test-image-history-1-$(safename)" + rand_name2="test-image-history-2-$(safename)" # Tag once, rmi, and make sure the tag name appears in history - run_podman tag my-test-image $rand_name1 + run_podman tag $iname $rand_name1 run_podman rmi $rand_name1 - run_podman images my-test-image --format '{{ .History }}' - is "$output" "localhost/my-test-image:latest, localhost/${rand_name1}:latest" "image history after one tag" + run_podman images $iname --format '{{ .History }}' + is "$output" "localhost/$iname:latest, localhost/${rand_name1}:latest" "image history after one tag" # Repeat with second tag. Now both tags should be in history - run_podman tag my-test-image $rand_name2 + run_podman tag $iname $rand_name2 run_podman rmi $rand_name2 - run_podman images my-test-image --format '{{ .History }}' - is "$output" "localhost/my-test-image:latest, localhost/${rand_name2}:latest, localhost/${rand_name1}:latest" \ + run_podman images $iname --format '{{ .History }}' + is "$output" "localhost/$iname:latest, localhost/${rand_name2}:latest, localhost/${rand_name1}:latest" \ "image history after two tags" - run_podman rmi my-test-image - run_podman rm my-container + run_podman rmi $iname + run_podman rm $cname } @test "podman images - filter" { @@ -102,26 +104,31 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z # Create a dummy container, then commit that as an image. We will # now be able to use before/after/since queries - run_podman run --name mytinycontainer $IMAGE true - run_podman commit -q mytinycontainer mynewimage + cname=c_$(safename) + iname=i_$(safename) + run_podman run --name $cname $IMAGE true + run_podman commit -q $cname $iname new_iid=$output # (refactor common options for legibility) opts='--noheading --no-trunc --format={{.ID}}--{{.Repository}}:{{.Tag}}' run_podman images ${opts} --filter=after=$iid - is "$output" "sha256:$new_iid--localhost/mynewimage:latest" "filter: after" + is "$output" "sha256:$new_iid--localhost/$iname:latest" "filter: after" # Same thing, with 'since' instead of 'after' run_podman images ${opts} --filter=since=$iid - is "$output" "sha256:$new_iid--localhost/mynewimage:latest" "filter: since" + is "$output" "sha256:$new_iid--localhost/$iname:latest" "filter: since" - run_podman images ${opts} --filter=before=mynewimage + run_podman images ${opts} --filter=before=$iname is "$output" "sha256:$iid--$IMAGE" "filter: before" + run_podman 125 image list -f json + is "$output" 'Error: invalid image filter "json": must be in the format "filter=value or filter!=value"' "Invalid filter" + # Clean up - run_podman rmi mynewimage - run_podman rm mytinycontainer + run_podman rmi $iname + run_podman rm $cname } # Regression test for https://github.com/containers/podman/issues/7651 @@ -238,7 +245,7 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z } @test "podman images - rmi -af removes all containers and pods" { - pname=$(random_string) + pname=p_$(safename) run_podman create --pod new:$pname $IMAGE run_podman inspect --format '{{.ID}}' $IMAGE @@ -274,7 +281,7 @@ Deleted: $pauseID" "infra images gets removed as well" } @test "podman images - rmi -f can remove infra images" { - pname=$(random_string) + pname=p_$(safename) run_podman create --pod new:$pname $IMAGE pauseImage=$(pause_image) @@ -299,8 +306,7 @@ Deleted: $pauseID" } @test "podman rmi --ignore" { - random_image_name=$(random_string) - random_image_name=${random_image_name,,} # name must be lowercase + random_image_name=i_$(safename) run_podman 1 rmi $random_image_name is "$output" "Error: $random_image_name: image not known.*" run_podman rmi --ignore $random_image_name @@ -313,8 +319,7 @@ Deleted: $pauseID" run_podman image rm --force bogus is "$output" "" "Should print no output" - random_image_name=$(random_string) - random_image_name=${random_image_name,,} # name must be lowercase + random_image_name=i_$(safename) run_podman image tag $IMAGE $random_image_name run_podman image rm --force bogus $random_image_name assert "$output" = "Untagged: localhost/$random_image_name:latest" "removed image" @@ -324,24 +329,26 @@ Deleted: $pauseID" } @test "podman images - commit docker with comment" { - run_podman run --name my-container -d $IMAGE top - run_podman 125 commit -m comment my-container my-test-image + cname=c_$(safename) + iname=i_$(safename) + run_podman run --name $cname -d $IMAGE top + run_podman 125 commit -m comment $cname $iname assert "$output" == "Error: messages are only compatible with the docker image format (-f docker)" "podman should fail unless docker format" # Without -q: verbose output, but only on podman-local, not remote - run_podman commit my-container --format docker -m comment my-test-image1 + run_podman commit $cname --format docker -m comment ${iname}_2 if ! is_remote; then assert "$output" =~ "Getting image.*Writing manif" \ "Without -q, verbose output" fi # With -q, both local and remote: only an image ID - run_podman commit -q my-container --format docker -m comment my-test-image2 + run_podman commit -q $cname --format docker -m comment ${iname}_3 assert "$output" =~ "^[0-9a-f]{64}\$" \ "With -q, output is a commit ID, no warnings or other output" - run_podman rmi my-test-image1 my-test-image2 - run_podman rm my-container --force -t 0 + run_podman rmi ${iname}_2 ${iname}_3 + run_podman rm $cname --force -t 0 } @test "podman pull image with additional store" { diff --git a/test/system/030-run.bats b/test/system/030-run.bats index 5f91adb48d..4080a15792 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -122,7 +122,7 @@ echo $rand | 0 | $rand } @test "podman run --name" { - randomname=$(random_string 30) + randomname=c_$(safename) run_podman run -d --name $randomname $IMAGE sleep inf cid=$output @@ -192,7 +192,8 @@ echo $rand | 0 | $rand run_podman 1 image exists $NONLOCAL_IMAGE # Run a container, without --rm; this should block subsequent --rmi - run_podman run --name /keepme $NONLOCAL_IMAGE /bin/true + cname=c_$(safename) + run_podman run --name /$cname $NONLOCAL_IMAGE /bin/true run_podman image exists $NONLOCAL_IMAGE # Now try running with --rmi : it should succeed, but not remove the image @@ -202,7 +203,7 @@ echo $rand | 0 | $rand run_podman image exists $NONLOCAL_IMAGE # Remove the stray container, and run one more time with --rmi. - run_podman rm /keepme + run_podman rm /$cname run_podman run --rmi $NONLOCAL_IMAGE /bin/true run_podman 1 image exists $NONLOCAL_IMAGE @@ -220,7 +221,7 @@ echo $rand | 0 | $rand # on write. echo "$(random_string 120)" > $cidfile - cname=$(random_string) + cname=c_$(safename) run_podman run --name $cname \ --conmon-pidfile=$pidfile \ --cidfile=$cidfile \ @@ -255,23 +256,25 @@ echo $rand | 0 | $rand skip_if_remote "podman-remote does not support docker-archive" # Create an image that, when run, outputs a random magic string + cname=c_$(safename) expect=$(random_string 20) - run_podman run --name myc --entrypoint="[\"/bin/echo\",\"$expect\"]" $IMAGE + run_podman run --name $cname --entrypoint="[\"/bin/echo\",\"$expect\"]" $IMAGE is "$output" "$expect" "podman run --entrypoint echo-randomstring" # Save it as a tar archive - run_podman commit myc myi + iname=i_$(safename) + run_podman commit $cname $iname archive=$PODMAN_TMPDIR/archive.tar - run_podman save --quiet myi -o $archive + run_podman save --quiet $iname -o $archive is "$output" "" "podman save" # Clean up image and container from container storage... - run_podman rmi myi - run_podman rm myc + run_podman rmi $iname + run_podman rm $cname # ... then confirm we can run from archive. This re-imports the image # and runs it, producing our random string as the last line. - run_podman run docker-archive:$archive + run_podman run --name ${cname}_2 docker-archive:$archive is "${lines[0]}" "Getting image source signatures" "podman run docker-archive, first line of output" is "$output" ".*Copying blob" "podman run docker-archive" is "$output" ".*Copying config" "podman run docker-archive" @@ -279,8 +282,8 @@ echo $rand | 0 | $rand is "${lines[-1]}" "$expect" "podman run docker-archive: expected random string output" # Clean up container as well as re-imported image - run_podman rm -a - run_podman rmi myi + run_podman rm ${cname}_2 + run_podman rmi $iname # Repeat the above, with podman-create and podman-start. run_podman create docker-archive:$archive @@ -291,7 +294,7 @@ echo $rand | 0 | $rand # Clean up. run_podman rm $cid - run_podman rmi myi + run_podman rmi $iname } # #6735 : complex interactions with multiple user namespaces @@ -407,15 +410,16 @@ json-file | f while read driver do_check; do msg=$(random_string 15) - run_podman run --name myctr --log-driver $driver $IMAGE echo $msg + cname=c_$(safename) + run_podman run --name $cname --log-driver $driver $IMAGE echo $msg is "$output" "$msg" "basic output sanity check (driver=$driver)" # Simply confirm that podman preserved our argument as-is - run_podman inspect --format '{{.HostConfig.LogConfig.Type}}' myctr + run_podman inspect --format '{{.HostConfig.LogConfig.Type}}' $cname is "$output" "$driver" "podman inspect: driver" # If LogPath is non-null, check that it exists and has a valid log - run_podman inspect --format '{{.HostConfig.LogConfig.Path}}' myctr + run_podman inspect --format '{{.HostConfig.LogConfig.Path}}' $cname if [[ $do_check != '-' ]]; then is "$output" "/.*" "LogPath (driver=$driver)" if ! test -e "$output"; then @@ -433,17 +437,17 @@ json-file | f # Cannot perform check : else - run_podman logs myctr + run_podman logs $cname is "$output" "$msg" "podman logs, with driver '$driver'" fi else - run_podman 125 logs myctr + run_podman 125 logs $cname if ! is_remote; then is "$output" ".*this container is using the 'none' log driver, cannot read logs.*" \ "podman logs, with driver 'none', should fail with error" fi fi - run_podman rm myctr + run_podman rm $cname done < <(parse_table "$tests") # Invalid log-driver argument @@ -462,13 +466,14 @@ json-file | f pidfile="${PODMAN_TMPDIR}/$(random_string 20)" # Multiple --log-driver options to confirm that last one wins - run_podman run --name myctr --log-driver=none --log-driver journald \ + cname=c_$(safename) + run_podman run --name $cname --log-driver=none --log-driver journald \ --conmon-pidfile $pidfile $IMAGE echo $msg journalctl --output cat _PID=$(cat $pidfile) is "$output" "$msg" "check that journalctl output equals the container output" - run_podman rm myctr + run_podman rm $cname } @test "podman run --tz" { @@ -530,18 +535,20 @@ json-file | f rm -f $new_runtime } -@test "podman --noout run should print output" { - run_podman --noout run -d --name test $IMAGE echo hi +@test "podman --noout run should not print output" { + cname=c_$(safename) + run_podman --noout run -d --name $cname $IMAGE echo hi is "$output" "" "output should be empty" - run_podman wait test - run_podman --noout rm test + run_podman wait $cname + run_podman --noout rm $cname is "$output" "" "output should be empty" } -@test "podman --noout create should print output" { - run_podman --noout create --name test $IMAGE echo hi +@test "podman --noout create should not print output" { + cname=c_$(safename) + run_podman --noout create --name $cname $IMAGE echo hi is "$output" "" "output should be empty" - run_podman --noout rm test + run_podman --noout rm $cname is "$output" "" "output should be empty" } @@ -549,15 +556,16 @@ json-file | f outfile=${PODMAN_TMPDIR}/out-results # first we'll need to run something, write its output to a file, and then read its contents. - run_podman --out $outfile run -d --name test $IMAGE echo hola + cname=c_$(safename) + run_podman --out $outfile run -d --name $cname $IMAGE echo hola is "$output" "" "output should be redirected" - run_podman wait test + run_podman wait $cname # compare the container id against the one in the file - run_podman container inspect --format '{{.Id}}' test + run_podman container inspect --format '{{.Id}}' $cname is "$output" "$(<$outfile)" "container id should match" - run_podman --out /dev/null rm test + run_podman --out /dev/null rm $cname is "$output" "" "output should be empty" } @@ -565,22 +573,22 @@ json-file | f outfile=${PODMAN_TMPDIR}/out-results # first we'll need to run something, write its output to a file, and then read its contents. - run_podman --out $outfile create --name test $IMAGE echo hola + cname=c_$(safename) + run_podman --out $outfile create --name $cname $IMAGE echo hola is "$output" "" "output should be redirected" # compare the container id against the one in the file - run_podman container inspect --format '{{.Id}}' test + run_podman container inspect --format '{{.Id}}' $cname is "$output" "$(<$outfile)" "container id should match" - run_podman --out /dev/null rm test + run_podman --out /dev/null rm $cname is "$output" "" "output should be empty" } # Regression test for issue #8082 @test "podman run : look up correct image name" { - # Create a 2nd tag for the local image. Force to lower case, and apply it. - local newtag="localhost/$(random_string 10)/$(random_string 8)" - newtag=${newtag,,} + # Create a 2nd tag for the local image. + local newtag="localhost/r_$(safename)/i_$(safename)" run_podman tag $IMAGE $newtag # Create a container with the 2nd tag and make sure that it's being @@ -596,7 +604,7 @@ json-file | f newtag2="${newtag}:$(random_string 6|tr A-Z a-z)" run_podman tag $IMAGE $newtag2 - cname="$(random_string 14|tr A-Z a-z)" + cname="c_$(safename)" run_podman create --name $cname $newtag2 run_podman inspect --format "{{.ImageName}}" $cname is "$output" "$newtag2" \ @@ -621,7 +629,7 @@ json-file | f } @test "podman inspect includes image data" { - randomname=$(random_string 30) + randomname=c_$(safename) run_podman inspect $IMAGE --format "{{.ID}}" expected="$IMAGE $output" @@ -650,7 +658,7 @@ json-file | f run_podman inspect --format '{{.ID}}' $IMAGE local iid="$output" - random_cname=c$(random_string 15 | tr A-Z a-z) + random_cname=c_$(safename) local rootless=0 if is_rootless; then rootless=1 @@ -708,8 +716,8 @@ json-file | f run_podman image mount $IMAGE romount="$output" - randomname=$(random_string 30) - # FIXME FIXME FIXME: Remove :O once (if) #14504 is fixed! + randomname=c_$(safename) + # :O (overlay) required with rootfs; see #14504 run_podman run --name=$randomname --rootfs $romount:O echo "Hello world" is "$output" "Hello world" @@ -730,7 +738,7 @@ json-file | f char_count=700000 # Container name; primarily needed when running podman-remote - cname=mybigdatacontainer + cname=c_$(safename) # This is one of those cases where BATS is not the best test framework. # We can't do any output redirection, because 'run' overrides it so @@ -744,14 +752,15 @@ json-file | f is "${lines[1]}" "$char_count+0 records out" "dd: number of records out" # We don't have many tests for '-l'. This is as good a place as any - if ! is_remote; then - cname=-l + local dash_l=-l + if is_remote; then + dash_l=$cname fi # Now find that log file, and count the NULs in it. # The log file is of the form ' ', where P|F # is Partial/Full; I think that's called "kubernetes log format"? - run_podman inspect $cname --format '{{.HostConfig.LogConfig.Path}}' + run_podman inspect $dash_l --format '{{.HostConfig.LogConfig.Path}}' logfile="$output" count_zero=$(tr -cd '\0' <$logfile | wc -c) @@ -768,14 +777,14 @@ json-file | f } @test "podman run --timeout - basic test" { - cid=timeouttest + cname=c_$(safename) t0=$SECONDS - run_podman 255 run --name $cid --timeout 2 $IMAGE sleep 60 + run_podman 255 run --name $cname --timeout 2 $IMAGE sleep 60 t1=$SECONDS # Confirm that container is stopped. Podman-remote unfortunately # cannot tell the difference between "stopped" and "exited", and # spits them out interchangeably, so we need to recognize either. - run_podman inspect --format '{{.State.Status}} {{.State.ExitCode}}' $cid + run_podman inspect --format '{{.State.Status}} {{.State.ExitCode}}' $cname is "$output" "\\(stopped\|exited\\) \-1" \ "Status and exit code of stopped container" @@ -785,7 +794,7 @@ json-file | f assert "$delta_t" -gt 1 "podman stop: ran too quickly!" assert "$delta_t" -le 6 "podman stop: took too long" - run_podman rm $cid + run_podman rm $cname } @test "podman run no /etc/mtab " { @@ -1029,21 +1038,26 @@ EOF @test "podman run failed --rm " { port=$(random_free_port) + # Container names must sort alphanumerically + c_ok=c1_$(safename) + c_fail_no_rm=c2_$(safename) + c_fail_with_rm=c3_$(safename) + # Run two containers with the same port bindings. The second must fail - run_podman run -p $port:80 --rm -d --name c_ok $IMAGE top - run_podman 126 run -p $port:80 -d --name c_fail_no_rm $IMAGE top - run_podman 126 run -p $port:80 --rm -d --name c_fail_with_rm $IMAGE top + run_podman run -p $port:80 --rm -d --name $c_ok $IMAGE top + run_podman 126 run -p $port:80 -d --name $c_fail_no_rm $IMAGE top + run_podman 126 run -p $port:80 --rm -d --name $c_fail_with_rm $IMAGE top # Prior to #15060, the third container would still show up in ps -a run_podman ps -a --sort names --format '{{.Image}}--{{.Names}}' - is "$output" "$IMAGE--c_fail_no_rm -$IMAGE--c_ok" \ + is "$output" "$IMAGE--${c_ok} +$IMAGE--${c_fail_no_rm}" \ "podman ps -a shows running & failed containers, but not failed-with-rm" - run_podman container rm -f -t 0 c_ok c_fail_no_rm + run_podman container rm -f -t 0 $c_ok $c_fail_no_rm } @test "podman run --attach stdin prints container ID" { - ctr_name="container-$(random_string 5)" + ctr_name="container-$(safename)" run_podman run --name $ctr_name --attach stdin $IMAGE echo hello run_output=$output run_podman inspect --format "{{.Id}}" $ctr_name @@ -1191,7 +1205,7 @@ EOF } @test "podman run bad --name" { - randomname=$(random_string 30) + randomname=c_$(safename) run_podman 125 create --name "$randomname/bad" $IMAGE run_podman create --name "/$randomname" $IMAGE run_podman ps -a --filter name="^/$randomname$" --format '{{ .Names }}' @@ -1253,7 +1267,7 @@ EOF run_podman run --uidmap=0:1000:200 --rm --rootfs "$romount:idmap=uids=@2000-1-1;gids=@2000-1-1" stat -c %u:%g /testfile is "$output" "1:1" - myvolume=my-volume-$(random_string) + myvolume=my-volume-$(safename) run_podman volume create $myvolume mkdir $romount/volume run_podman run --rm --uidmap=0:1000:10000 -v volume:/volume:idmap --rootfs $romount stat -c %u:%g /volume @@ -1265,7 +1279,7 @@ EOF @test "podman run --restart=always -- wait" { # regression test for #18572 to make sure Podman waits less than 20 seconds - ctr=$(random_string) + ctr=c_$(safename) run_podman run -d --restart=always --name=$ctr $IMAGE false PODMAN_TIMEOUT=20 run_podman wait $ctr is "$output" "1" "container should exit 1" @@ -1284,7 +1298,7 @@ cat >$containersconf <>"${f}" + echo "${content}" >>"${f}" + done < <(parse_table "${dropin_files}") + + # Create the base quadlet file + quadlet_base="${PODMAN_TMPDIR}/${quadlet_file}" + cat > "${quadlet_base}" <> (append), not > (overwrite), for multi-pod test + cat >>$TESTYAML <>$TESTYAML + echo " $annotations" >>$TESTYAML + fi + if [[ -n "$labels" ]]; then + echo " labels:" >>$TESTYAML + echo " $labels" >>$TESTYAML + fi + if [[ -n "$PODNAME" ]]; then + echo " name: $PODNAME" >>$TESTYAML + fi + + # We always have spec and container lines... + echo "spec:" >>$TESTYAML + echo " containers:" >>$TESTYAML + # ...but command is optional. If absent, assume our caller will fill it in. + if [[ -n "$command" ]]; then + cat <>$TESTYAML - command: - - /home/podman/pause - env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - - name: container - value: podman - image: $IMAGE - name: test + - $command + image: $image + name: $CTRNAME resources: {} +EOF + + # only makes sense when command is given + if [[ -n "$volume" ]]; then + cat <>$TESTYAML securityContext: runAsUser: 1000 runAsGroup: 3000 @@ -51,7 +95,7 @@ spec: capabilities: {} privileged: false seLinuxOptions: - level: \"s0:c1,c2\" + level: "s0:c1,c2" readOnlyRootFilesystem: false volumeMounts: - mountPath: /testdir:z @@ -59,20 +103,29 @@ spec: workingDir: / volumes: - hostPath: - path: TESTDIR + path: $volume type: Directory name: home-podman-testdir -status: {} -" +EOF + fi + + # Done. + echo "status: {}" >>$TESTYAML + fi + + # For debugging + echo "# test yaml:" + sed -e "s/^/ /g" <$TESTYAML +} RELABEL="system_u:object_r:container_file_t:s0" @test "podman kube with stdin" { TESTDIR=$PODMAN_TMPDIR/testdir mkdir -p $TESTDIR - echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml + _write_test_yaml command=top volume=$TESTDIR - run_podman kube play - < $PODMAN_TMPDIR/test.yaml + run_podman kube play - < $TESTYAML if selinux_enabled; then run ls -Zd $TESTDIR is "$output" "${RELABEL} $TESTDIR" "selinux relabel should have happened" @@ -84,8 +137,7 @@ RELABEL="system_u:object_r:container_file_t:s0" run_podman 1 image exists registry.k8s.io/pause run_podman image exists $(pause_image) - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod + run_podman pod rm -t 0 -f $PODNAME } @test "podman play" { @@ -93,98 +145,35 @@ RELABEL="system_u:object_r:container_file_t:s0" # "podman kube" is an option. TESTDIR=$PODMAN_TMPDIR/testdir mkdir -p $TESTDIR - echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml - run_podman play kube $PODMAN_TMPDIR/test.yaml + _write_test_yaml command=top volume=$TESTDIR + run_podman play kube $TESTYAML if selinux_enabled; then run ls -Zd $TESTDIR is "$output" "${RELABEL} $TESTDIR" "selinux relabel should have happened" fi # Now rerun twice to make sure nothing gets removed - run_podman 125 play kube $PODMAN_TMPDIR/test.yaml + run_podman 125 play kube $TESTYAML is "$output" ".* is in use: pod already exists" - run_podman 125 play kube $PODMAN_TMPDIR/test.yaml + run_podman 125 play kube $TESTYAML is "$output" ".* is in use: pod already exists" - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod -} - -# helper function: writes a yaml file with customizable values -function _write_test_yaml() { - local outfile=$PODMAN_TMPDIR/test.yaml - - # Function args must all be of the form 'keyword=value' (value may be null) - local annotations= - local labels="app: test" - local name="test_pod" - local command="" - local image="$IMAGE" - local ctrname="test" - for i;do - # This will error on 'foo=' (no value). That's totally OK. - local value=$(expr "$i" : '[^=]*=\(.*\)') - case "$i" in - annotations=*) annotations="$value" ;; - labels=*) labels="$value" ;; - name=*) name="$value" ;; - command=*) command="$value" ;; - image=*) image="$value" ;; - ctrname=*) ctrname="$value" ;; - *) die "_write_test_yaml: cannot grok '$i'" ;; - esac - done - - # These three header lines are common to all yamls. - # Note: use >> (append), not > (overwrite), for multi-pod test - cat >>$outfile <>$outfile - echo " $annotations" >>$outfile - fi - if [[ -n "$labels" ]]; then - echo " labels:" >>$outfile - echo " $labels" >>$outfile - fi - if [[ -n "$name" ]]; then - echo " name: $name" >>$outfile - fi - - # We always have spec and container lines... - echo "spec:" >>$outfile - echo " containers:" >>$outfile - # ...but command is optional. If absent, assume our caller will fill it in. - if [[ -n "$command" ]]; then - cat <>$outfile - - command: - - $command - image: $image - name: $ctrname - resources: {} -status: {} -EOF - fi + run_podman pod rm -t 0 -f $PODNAME } @test "podman play --service-container" { skip_if_remote "service containers only work locally" # Create the YAMl file - yaml_source="$PODMAN_TMPDIR/test.yaml" _write_test_yaml command=top # Run `play kube` in the background as it will wait for the service # container to exit. timeout --foreground -v --kill=10 60 \ - $PODMAN play kube --service-container=true --log-driver journald $yaml_source &>/dev/null & + $PODMAN play kube --service-container=true --log-driver journald $TESTYAML &>/dev/null & # Wait for the container to be running - container_a=test_pod-test + container_a=$PODCTRNAME container_running= for i in $(seq 1 20); do run_podman "?" container wait $container_a --condition="running" @@ -202,7 +191,7 @@ EOF # The name of the service container is predictable: the first 12 characters # of the hash of the YAML file followed by the "-service" suffix - yaml_sha=$(sha256sum $yaml_source) + yaml_sha=$(sha256sum $TESTYAML) service_container="${yaml_sha:0:12}-service" # Make sure that the service container exists and runs. @@ -216,14 +205,14 @@ EOF # 1) The pod transitions to Exited # 2) The service container is stopped # #) The service container is marked as a service container - run_podman stop test_pod-test - _ensure_pod_state test_pod Exited + run_podman stop $PODCTRNAME + _ensure_pod_state $PODNAME Exited _ensure_container_running $service_container false run_podman container inspect $service_container --format "{{.IsService}}" is "$output" "true" # Restart the pod, make sure the service is running again - run_podman pod restart test_pod + run_podman pod restart $PODNAME run_podman container inspect $service_container --format "{{.State.Running}}" is "$output" "true" @@ -234,82 +223,81 @@ EOF is "$output" "Error: container .* is the service container of pod(s) .* and cannot be removed without removing the pod(s)" # Kill the pod and make sure the service is not running - run_podman pod kill test_pod + run_podman pod kill $PODNAME _ensure_container_running $service_container false # Remove the pod and make sure the service is removed along with it - run_podman pod rm test_pod + run_podman pod rm $PODNAME run_podman 1 container exists $service_container } @test "podman kube --network" { - TESTDIR=$PODMAN_TMPDIR/testdir - mkdir -p $TESTDIR - echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml + _write_test_yaml command=top - run_podman kube play --network host $PODMAN_TMPDIR/test.yaml + run_podman kube play --network host $TESTYAML is "$output" "Pod:.*" "podman kube play should work with --network host" - run_podman pod inspect --format "{{.InfraConfig.HostNetwork}}" test_pod + run_podman pod inspect --format "{{.InfraConfig.HostNetwork}}" $PODNAME is "$output" "true" ".InfraConfig.HostNetwork" - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod + run_podman pod rm -t 0 -f $PODNAME if has_slirp4netns; then - run_podman kube play --network slirp4netns:port_handler=slirp4netns $PODMAN_TMPDIR/test.yaml + run_podman kube play --network slirp4netns:port_handler=slirp4netns $TESTYAML run_podman pod inspect --format {{.InfraContainerID}} "${lines[1]}" infraID="$output" run_podman container inspect --format "{{.HostConfig.NetworkMode}}" $infraID is "$output" "slirp4netns" "network mode slirp4netns is set for the container" fi - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod + run_podman pod rm -t 0 -f $PODNAME - run_podman kube play --network none $PODMAN_TMPDIR/test.yaml + run_podman kube play --network none $TESTYAML run_podman pod inspect --format {{.InfraContainerID}} "${lines[1]}" infraID="$output" run_podman container inspect --format "{{.HostConfig.NetworkMode}}" $infraID is "$output" "none" "network mode none is set for the container" - run_podman kube down $PODMAN_TMPDIR/test.yaml - run_podman 125 inspect test_pod-test - is "$output" ".*Error: no such object: \"test_pod-test\"" - run_podman pod rm -a - run_podman rm -a + run_podman container exists $PODCTRNAME + run_podman kube down $TESTYAML + run_podman 1 container exists $PODCTRNAME } @test "podman kube play read-only" { YAML=$PODMAN_TMPDIR/test.yml + podname="p-$(safename)" + c1name="c1-$(safename)" + c2name="c2-$(safename)" + c3name="c3-$(safename)" + # --restart=no is crucial: without that, the "podman wait" below # will spin for indeterminate time. - run_podman create --pod new:pod1 --restart=no --name test1 $IMAGE touch /testrw - run_podman create --pod pod1 --read-only --restart=no --name test2 $IMAGE touch /testro - run_podman create --pod pod1 --read-only --restart=no --name test3 $IMAGE sh -c "echo "#!echo hi" > /tmp/testtmp; chmod +x /tmp/test/tmp; /tmp/testtmp" + run_podman create --pod new:$podname --restart=no --name $c1name $IMAGE touch /testrw + run_podman create --pod $podname --read-only --restart=no --name $c2name $IMAGE touch /testro + run_podman create --pod $podname --read-only --restart=no --name $c3name $IMAGE sh -c "echo "#!echo hi" > /tmp/testtmp; chmod +x /tmp/test/tmp; /tmp/testtmp" # Generate and run from yaml. (The "cat" is for debugging failures) - run_podman kube generate pod1 -f $YAML + run_podman kube generate $podname -f $YAML cat $YAML run_podman kube play --replace $YAML # Wait for all containers and check their exit statuses - run_podman wait pod1-test1 pod1-test2 pod1-test3 + run_podman wait ${podname}-${c1name} ${podname}-${c2name} ${podname}-${c3name} is "${lines[0]}" 0 "exit status: touch /file on read/write container" is "${lines[1]}" 1 "exit status: touch /file on read-only container" is "${lines[2]}" 0 "exit status: touch on /tmp is always ok, even on read-only container" # Confirm config settings - run_podman container inspect --format '{{.HostConfig.ReadonlyRootfs}}' pod1-test1 pod1-test2 pod1-test3 + run_podman container inspect --format '{{.HostConfig.ReadonlyRootfs}}' ${podname}-${c1name} ${podname}-${c2name} ${podname}-${c3name} is "${lines[0]}" "false" "ReadonlyRootfs - container 1" is "${lines[1]}" "true" "ReadonlyRootfs - container 2" is "${lines[2]}" "true" "ReadonlyRootfs - container 3" # Clean up run_podman kube down - < $YAML - run_podman 1 container exists pod1-test1 - run_podman 1 container exists pod1-test2 - run_podman 1 container exists pod1-test3 + run_podman 1 container exists ${podname}-${c1name} + run_podman 1 container exists ${podname}-${c2name} + run_podman 1 container exists ${podname}-${c3name} } @test "podman kube play read-only from containers.conf" { @@ -321,101 +309,103 @@ EOF YAML=$PODMAN_TMPDIR/test.yml + podname="p-$(safename)" + c1name="c1-$(safename)" + c2name="c2-$(safename)" + c3name="c3-$(safename)" + # --restart=no is crucial: without that, the "podman wait" below # will spin for indeterminate time. - CONTAINERS_CONF_OVERRIDE="$containersconf" run_podman create --pod new:pod1 --read-only=false --restart=no --name test1 $IMAGE touch /testrw - CONTAINERS_CONF_OVERRIDE="$containersconf" run_podman create --pod pod1 --restart=no --name test2 $IMAGE touch /testro - CONTAINERS_CONF_OVERRIDE="$containersconf" run_podman create --pod pod1 --restart=no --name test3 $IMAGE touch /tmp/testtmp + CONTAINERS_CONF_OVERRIDE="$containersconf" run_podman create --pod new:$podname --read-only=false --restart=no --name $c1name $IMAGE touch /testrw + CONTAINERS_CONF_OVERRIDE="$containersconf" run_podman create --pod $podname --restart=no --name $c2name $IMAGE touch /testro + CONTAINERS_CONF_OVERRIDE="$containersconf" run_podman create --pod $podname --restart=no --name $c3name $IMAGE touch /tmp/testtmp # Inspect settings in created containers - CONTAINERS_CONF_OVERRIDE="$containersconf" run_podman container inspect --format '{{.HostConfig.ReadonlyRootfs}}' test1 test2 test3 + CONTAINERS_CONF_OVERRIDE="$containersconf" run_podman container inspect --format '{{.HostConfig.ReadonlyRootfs}}' $c1name $c2name $c3name is "${lines[0]}" "false" "ReadonlyRootfs - container 1, created" is "${lines[1]}" "true" "ReadonlyRootfs - container 2, created" is "${lines[2]}" "true" "ReadonlyRootfs - container 3, created" # Now generate and run kube.yaml on a machine without the defaults set - CONTAINERS_CONF_OVERRIDE="$containersconf" run_podman kube generate pod1 -f $YAML + CONTAINERS_CONF_OVERRIDE="$containersconf" run_podman kube generate $podname -f $YAML cat $YAML run_podman kube play --replace $YAML # Wait for all containers and check their exit statuses - run_podman wait pod1-test1 pod1-test2 pod1-test3 + run_podman wait ${podname}-${c1name} ${podname}-${c2name} ${podname}-${c3name} is "${lines[0]}" 0 "exit status: touch /file on read/write container" is "${lines[1]}" 1 "exit status: touch /file on read-only container" is "${lines[2]}" 0 "exit status: touch on /tmp is always ok, even on read-only container" # Confirm settings again - run_podman container inspect --format '{{.HostConfig.ReadonlyRootfs}}' pod1-test1 pod1-test2 pod1-test3 + run_podman container inspect --format '{{.HostConfig.ReadonlyRootfs}}' ${podname}-${c1name} ${podname}-${c2name} ${podname}-${c3name} is "${lines[0]}" "false" "ReadonlyRootfs - container 1, post-run" is "${lines[1]}" "true" "ReadonlyRootfs - container 2, post-run" is "${lines[2]}" "true" "ReadonlyRootfs - container 3, post-run" # Clean up run_podman kube down - < $YAML - run_podman 1 container exists pod1-test1 - run_podman 1 container exists pod1-test2 - run_podman 1 container exists pod1-test3 + run_podman 1 container exists ${podname}-${c1name} + run_podman 1 container exists ${podname}-${c2name} + run_podman 1 container exists ${podname}-${c3name} } @test "podman play with user from image" { - TESTDIR=$PODMAN_TMPDIR/testdir - mkdir -p $TESTDIR + imgname="i-$(safename)" + _write_test_yaml command=id image=$imgname - _write_test_yaml command=id image=userimage - -cat > $PODMAN_TMPDIR/Containerfile << _EOF + cat > $PODMAN_TMPDIR/Containerfile << _EOF from $IMAGE USER bin _EOF # Unset the PATH during build and make sure that all default env variables # are correctly set for the created container. - run_podman build --unsetenv PATH -t userimage $PODMAN_TMPDIR - run_podman image inspect userimage --format "{{.Config.Env}}" + run_podman build --unsetenv PATH -t $imgname $PODMAN_TMPDIR + run_podman image inspect $imgname --format "{{.Config.Env}}" is "$output" "\[\]" "image does not set PATH - env is empty" - run_podman play kube --start=false $PODMAN_TMPDIR/test.yaml - run_podman inspect --format "{{ .Config.User }}" test_pod-test + run_podman play kube --start=false $TESTYAML + run_podman inspect --format "{{ .Config.User }}" $PODCTRNAME is "$output" bin "expect container within pod to run as the bin user" - run_podman inspect --format "{{ .Config.Env }}" test_pod-test + run_podman inspect --format "{{ .Config.Env }}" $PODCTRNAME is "$output" ".*PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin.*" "expect PATH to be set" is "$output" ".*container=podman.*" "expect container to be set" - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod - run_podman rmi -f userimage:latest + run_podman pod rm -t 0 -f $PODNAME + run_podman rmi -f $imgname } @test "podman play --build --context-dir" { skip_if_remote "--build is not supported in context remote" - mkdir -p $PODMAN_TMPDIR/userimage - cat > $PODMAN_TMPDIR/userimage/Containerfile << _EOF + imgname="userimage-$(safename)" + + mkdir -p $PODMAN_TMPDIR/$imgname + cat > $PODMAN_TMPDIR/$imgname/Containerfile << _EOF from $IMAGE USER bin _EOF - _write_test_yaml command=id image=quay.io/libpod/userimage - run_podman 125 play kube --build --start=false $PODMAN_TMPDIR/test.yaml - assert "$output" =~ "initializing source docker://quay.io/libpod/userimage:latest: reading manifest latest in " + _write_test_yaml command=id image=quay.io/libpod/$imgname + run_podman 125 play kube --build --start=false $TESTYAML + assert "$output" =~ "initializing source docker://quay.io/libpod/$imgname:latest: reading manifest latest in " - run_podman play kube --replace --context-dir=$PODMAN_TMPDIR --build --start=false $PODMAN_TMPDIR/test.yaml - run_podman inspect --format "{{ .Config.User }}" test_pod-test + run_podman play kube --replace --context-dir=$PODMAN_TMPDIR --build --start=false $TESTYAML + run_podman inspect --format "{{ .Config.User }}" $PODCTRNAME is "$output" bin "expect container within pod to run as the bin user" - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod - run_podman rmi -f userimage:latest + run_podman pod rm -t 0 -f $PODNAME + run_podman rmi -f $imgname cd $PODMAN_TMPDIR - run_podman play kube --replace --build --start=false $PODMAN_TMPDIR/test.yaml - run_podman inspect --format "{{ .Config.User }}" test_pod-test + run_podman play kube --replace --build --start=false $TESTYAML + run_podman inspect --format "{{ .Config.User }}" $PODCTRNAME is "$output" bin "expect container within pod to run as the bin user" - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod - run_podman rmi -f userimage:latest + run_podman pod rm -t 0 -f $PODNAME + run_podman rmi -f $imgname } # Occasionally a remnant storage container is left behind which causes @@ -423,80 +413,72 @@ _EOF # storage container name using buildah to make sure --replace, still # functions proplery by removing the storage container. @test "podman kube play --replace external storage" { - TESTDIR=$PODMAN_TMPDIR/testdir - mkdir -p $TESTDIR - echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml - run_podman play kube $PODMAN_TMPDIR/test.yaml - # Force removal of container - run_podman rm --force -t0 test_pod-test - # Create external container using buildah with same name - buildah from --name test_pod-test $IMAGE - # --replace deletes the buildah container and replace it with new one - run_podman play kube --replace $PODMAN_TMPDIR/test.yaml + _write_test_yaml - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod - run_podman rmi -f userimage:latest + run_podman play kube $TESTYAML + # Force removal of container + run_podman rm --force -t0 $PODCTRNAME + # Create external container using buildah with same name + buildah from --name $PODCTRNAME $IMAGE + # --replace deletes the buildah container and replace it with new one + run_podman play kube --replace $TESTYAML + + run_podman pod rm -t 0 -f $PODNAME } @test "podman kube --annotation" { - TESTDIR=$PODMAN_TMPDIR/testdir + _write_test_yaml command=/home/podman/pause + RANDOMSTRING=$(random_string 15) ANNOTATION_WITH_COMMA="comma,$(random_string 5)" - mkdir -p $TESTDIR - echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml run_podman kube play --annotation "name=$RANDOMSTRING" \ - --annotation "anno=$ANNOTATION_WITH_COMMA" $PODMAN_TMPDIR/test.yaml - run_podman inspect --format "{{ .Config.Annotations }}" test_pod-test + --annotation "anno=$ANNOTATION_WITH_COMMA" $TESTYAML + run_podman inspect --format "{{ .Config.Annotations }}" $PODCTRNAME is "$output" ".*name:$RANDOMSTRING" "Annotation should be added to pod" is "$output" ".*anno:$ANNOTATION_WITH_COMMA" "Annotation with comma should be added to pod" # invalid annotation - run_podman 125 kube play --annotation "val" $PODMAN_TMPDIR/test.yaml + run_podman 125 kube play --annotation "val" $TESTYAML assert "$output" == "Error: annotation \"val\" must include an '=' sign" "invalid annotation error" - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod + run_podman pod rm -t 0 -f $PODNAME } @test "podman play Yaml deprecated --no-trunc annotation" { + skip "FIXME: I can't figure out what this test is supposed to do" RANDOMSTRING=$(random_string 65) _write_test_yaml "annotations=test: ${RANDOMSTRING}" command=id - run_podman play kube --no-trunc - < $PODMAN_TMPDIR/test.yaml + run_podman play kube --no-trunc - < $TESTYAML } @test "podman kube play - default log driver" { - TESTDIR=$PODMAN_TMPDIR/testdir - mkdir -p $TESTDIR - echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml + _write_test_yaml command=top # Get the default log driver run_podman info --format "{{.Host.LogDriver}}" default_driver=$output # Make sure that the default log driver is used - run_podman kube play $PODMAN_TMPDIR/test.yaml - run_podman inspect --format "{{.HostConfig.LogConfig.Type}}" test_pod-test + run_podman kube play $TESTYAML + run_podman inspect --format "{{.HostConfig.LogConfig.Type}}" $PODCTRNAME is "$output" "$default_driver" "play kube uses default log driver" - run_podman kube down $PODMAN_TMPDIR/test.yaml - run_podman 125 inspect test_pod-test - is "$output" ".*Error: no such object: \"test_pod-test\"" - run_podman pod rm -a - run_podman rm -a + run_podman kube down $TESTYAML + run_podman 125 inspect $PODCTRNAME + is "$output" ".*Error: no such object: \"$PODCTRNAME\"" } @test "podman kube play - URL" { - TESTDIR=$PODMAN_TMPDIR/testdir - mkdir -p $TESTDIR - echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml - echo READY > $PODMAN_TMPDIR/ready + _write_test_yaml command=top + + echo READY > $PODMAN_TMPDIR/ready HOST_PORT=$(random_free_port) SERVER=http://127.0.0.1:$HOST_PORT - run_podman run -d --name myyaml -p "$HOST_PORT:80" \ - -v $PODMAN_TMPDIR/test.yaml:/var/www/testpod.yaml:Z \ + serverctr="yamlserver-$(safename)" + run_podman run -d --name $serverctr -p "$HOST_PORT:80" \ + -v $TESTYAML:/var/www/testpod.yaml:Z \ -v $PODMAN_TMPDIR/ready:/var/www/ready:Z \ -w /var/www \ $IMAGE /bin/busybox-extras httpd -f -p 80 @@ -505,58 +487,54 @@ _EOF wait_for_command_output "curl -s -S $SERVER/ready" "READY" run_podman kube play $SERVER/testpod.yaml - run_podman inspect test_pod-test --format "{{.State.Running}}" + run_podman inspect $PODCTRNAME --format "{{.State.Running}}" is "$output" "true" run_podman kube down $SERVER/testpod.yaml - run_podman 125 inspect test_pod-test - is "$output" ".*Error: no such object: \"test_pod-test\"" + run_podman 125 inspect $PODCTRNAME + is "$output" ".*Error: no such object: \"$PODCTRNAME\"" - run_podman pod rm -a -f - run_podman rm -a -f -t0 + run_podman rm -f -t0 $serverctr } @test "podman play with init container" { _write_test_yaml command= - cat >>$PODMAN_TMPDIR/test.yaml <>$TESTYAML <>$PODMAN_TMPDIR/test.yaml <>$TESTYAML <>$yaml_source + echo "---" >>$TESTYAML fi done # Run `play kube` in the background as it will wait for the service # container to exit. timeout --foreground -v --kill=10 60 \ - $PODMAN play kube --service-container=true --log-driver journald $yaml_source &>/dev/null & + $PODMAN play kube --service-container=true --log-driver journald $TESTYAML &>/dev/null & # The name of the service container is predictable: the first 12 characters # of the hash of the YAML file followed by the "-service" suffix - yaml_sha=$(sha256sum $yaml_source) + yaml_sha=$(sha256sum $TESTYAML) service_container="${yaml_sha:0:12}-service" # Wait for the containers to be running - container_1=pod1-ctr1 - container_2=pod2-ctr2 + container_1="${podnamebase}-1-${ctrnamebase}-1" + container_2="${podnamebase}-2-${ctrnamebase}-2" containers_running= for i in $(seq 1 20); do run_podman "?" container wait $container_1 $container_2 $service_container --condition="running" @@ -603,38 +582,48 @@ EOF # Stop the pods, make sure that no ugly error logs show up and that the # service container will implicitly get stopped as well - run_podman pod stop pod1 pod2 + run_podman pod stop ${podnamebase}-1 ${podnamebase}-2 assert "$output" !~ "Stopping" _ensure_container_running $service_container false - run_podman kube down $yaml_source + run_podman kube down $TESTYAML } @test "podman kube generate filetype" { YAML=$PODMAN_TMPDIR/test.yml - run_podman create --pod new:pod1 --security-opt label=level:s0:c1,c2 --security-opt label=filetype:usr_t -v myvol:/myvol --name test1 $IMAGE true - run_podman kube generate pod1 -f $YAML + + podname="p-$(safename)" + ctrname="c-$(safename)" + volname="v-$(safename)" + + run_podman create --pod new:$podname \ + --security-opt label=level:s0:c1,c2 \ + --security-opt label=filetype:usr_t \ + -v $volname:/myvol --name $ctrname $IMAGE true + run_podman kube generate $podname -f $YAML run cat $YAML is "$output" ".*filetype: usr_t" "Generated YAML file should contain filetype usr_t" - run_podman pod rm --force pod1 - run_podman volume rm -t -1 myvol --force + run_podman pod rm --force $podname + run_podman volume rm -t -1 $volname --force run_podman kube play $YAML if selinux_enabled; then - run_podman inspect pod1-test1 --format "{{ .MountLabel }}" + run_podman inspect ${podname}-${ctrname} --format "{{ .MountLabel }}" is "$output" "system_u:object_r:usr_t:s0:c1,c2" "Generated container should use filetype usr_t" - run_podman volume inspect myvol --format '{{ .Mountpoint }}' + run_podman volume inspect $volname --format '{{ .Mountpoint }}' path=${output} run ls -Zd $path is "$output" "system_u:object_r:usr_t:s0 $path" "volume should be labeled with usr_t type" fi run_podman kube down $YAML - run_podman volume rm myvol --force + run_podman volume rm $volname --force } # kube play --wait=true, where we clear up the created containers, pods, and volumes when a kill or sigterm is triggered @test "podman kube play --wait with siginterrupt" { - cname=c$(random_string 15) + podname="p-$(safename)" + ctrname="c-$(safename)" + fname="$PODMAN_TMPDIR/play_kube_wait_$(random_string 6).yaml" echo " apiVersion: v1 @@ -642,11 +631,11 @@ kind: Pod metadata: labels: app: test - name: test_pod + name: $podname spec: restartPolicy: Never containers: - - name: server + - name: $ctrname image: $IMAGE command: - top @@ -665,11 +654,13 @@ spec: # there should be no containers running or created run_podman ps -aq - is "$output" "" "There should be no containers" - run_podman rmi $(pause_image) + assert "$output" !~ "$(safename)" "No containers created by this test" } @test "podman kube play --wait - wait for pod to exit" { + podname="p-$(safename)" + ctrname="c-$(safename)" + fname="$PODMAN_TMPDIR/play_kube_wait_$(random_string 6).yaml" echo " apiVersion: v1 @@ -677,15 +668,15 @@ kind: Pod metadata: labels: app: test - name: test_pod + name: $podname spec: restartPolicy: Never containers: - - name: server + - name: $ctrname image: $IMAGE command: - echo - - "hello" + - hello " > $fname run_podman kube play --wait $fname @@ -693,12 +684,15 @@ spec: # debug to see what container is being left behind after the cleanup # there should be no containers running or created run_podman ps -a --noheading - is "$output" "" "There should be no containers" + assert "$output" !~ "$(safename)" "No containers created by this test" run_podman pod ps - run_podman rmi $(pause_image) + assert "$output" !~ "$(safename)" "No pods created by this test" } @test "podman kube play with configmaps" { + foovalue="foo-$(safename)" + barvalue="bar-$(safename)" + configmap_file=${PODMAN_TMPDIR}/play_kube_configmap_configmaps$(random_string 6),withcomma.yaml echo " --- @@ -707,16 +701,19 @@ kind: ConfigMap metadata: name: foo data: - value: foo + value: $foovalue --- apiVersion: v1 kind: ConfigMap metadata: name: bar data: - value: bar + value: $barvalue " > $configmap_file + podname="p-$(safename)" + ctrname="c-$(safename)" + pod_file=${PODMAN_TMPDIR}/play_kube_configmap_pod$(random_string 6).yaml echo " apiVersion: v1 @@ -724,11 +721,11 @@ kind: Pod metadata: labels: app: test - name: test_pod + name: $podname spec: restartPolicy: Never containers: - - name: server + - name: $ctrname image: $IMAGE env: - name: FOO @@ -749,30 +746,28 @@ spec: " > $pod_file run_podman kube play --configmap=$configmap_file $pod_file - run_podman wait test_pod-server + run_podman wait $podname-$ctrname # systemd logs are unreliable; we may need to retry a few times # https://github.com/systemd/systemd/issues/28650 local retries=10 while [[ $retries -gt 0 ]]; do - run_podman logs test_pod-server + run_podman logs $podname-$ctrname test -n "$output" && break sleep 0.1 retries=$((retries - 1)) done assert "$retries" -gt 0 "Timed out waiting for podman logs" - assert "$output" = "foo:bar" "output from podman logs" + assert "$output" = "$foovalue:$barvalue" "output from podman logs" run_podman kube down $pod_file } @test "podman kube with --authfile=/tmp/bogus" { - TESTDIR=$PODMAN_TMPDIR/testdir - mkdir -p $TESTDIR - echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml + _write_test_yaml bogus=$PODMAN_TMPDIR/bogus-authfile - run_podman 125 kube play --authfile=$bogus - < $PODMAN_TMPDIR/test.yaml + run_podman 125 kube play --authfile=$bogus - < $TESTYAML is "$output" "Error: credential file is not accessible: faccessat $bogus: no such file or directory" \ "$command should fail with not such file" } @@ -788,8 +783,8 @@ spec: umask = "0472" EOF - ctr="ctr" - ctrInPod="ctr-pod-ctr" + ctr="c-$(safename)" + ctrInPod="${ctr}-pod-${ctr}" run_podman create --restart never --name $ctr $IMAGE sh -c "touch /umask-test;stat -c '%a' /umask-test" run_podman kube generate -f $YAML $ctr @@ -808,53 +803,53 @@ EOF assert "$output" = "204" "stat() on created file" run_podman kube down $YAML - run_podman pod rm -a - run_podman rm -a + run_podman rm $ctr } @test "podman kube generate tmpfs on /tmp" { - local yaml=$PODMAN_TMPDIR/test.yaml - _write_test_yaml command=/home/podman/pause - run_podman kube play $yaml - run_podman exec test_pod-test sh -c "mount | grep /tmp" - assert "$output" !~ "noexec" "mounts on /tmp should not be noexec" - run_podman kube down $yaml + _write_test_yaml command=/home/podman/pause + run_podman kube play $TESTYAML + run_podman exec $PODCTRNAME sh -c "mount | grep /tmp" + assert "$output" !~ "noexec" "mounts on /tmp should not be noexec" + run_podman kube down $TESTYAML } @test "podman kube play - pull policy" { skip_if_remote "pull debug logs only work locally" - yaml_source="$PODMAN_TMPDIR/test.yaml" _write_test_yaml command=true # Exploit a debug message to make sure the expected pull policy is used - run_podman --debug kube play $yaml_source + run_podman --debug kube play $TESTYAML assert "$output" =~ "Pulling image $IMAGE \(policy\: missing\)" "default pull policy is missing" - run_podman kube down $yaml_source + run_podman kube down $TESTYAML - local_image="localhost/name:latest" + local_image="localhost/i-$(safename):latest" run_podman tag $IMAGE $local_image - rm $yaml_source + rm $TESTYAML _write_test_yaml command=true image=$local_image - run_podman --debug kube play $yaml_source + run_podman --debug kube play $TESTYAML assert "$output" =~ "Pulling image $local_image \(policy\: newer\)" "pull policy is set to newhen pulling latest tag" - run_podman kube down $yaml_source + run_podman kube down $TESTYAML run_podman rmi $local_image } @test "podman kube play healthcheck should wait initialDelaySeconds before updating status (healthy)" { + podname="liveness-exec-$(safename)" + ctrname="liveness-ctr-$(safename)" + fname="$PODMAN_TMPDIR/play_kube_healthy_$(random_string 6).yaml" echo " apiVersion: v1 kind: Pod metadata: labels: - name: liveness-exec + name: $podname spec: containers: - - name: liveness + - name: $ctrname image: $IMAGE args: - /bin/sh @@ -871,7 +866,7 @@ spec: " > $fname run_podman kube play $fname - ctrName="liveness-exec-liveness" + ctrName="$podname-$ctrname" # Keep checking status. For the first 2 seconds it must be 'starting' t0=$SECONDS @@ -892,21 +887,23 @@ spec: done assert $output == "2-healthy" "After 3 seconds" - run_podman pod rm -fa -t0 - run_podman rm -a + run_podman kube down $fname } @test "podman kube play healthcheck should wait initialDelaySeconds before updating status (unhealthy)" { + podname="liveness-exec-$(safename)" + ctrname="liveness-ctr-$(safename)" + fname="$PODMAN_TMPDIR/play_kube_unhealthy_$(random_string 6).yaml" echo " apiVersion: v1 kind: Pod metadata: labels: - name: liveness-exec + name: $podname spec: containers: - - name: liveness + - name: $ctrname image: $IMAGE args: - /bin/sh @@ -923,7 +920,7 @@ spec: " > $fname run_podman kube play $fname - ctrName="liveness-exec-liveness" + ctrName="$podname-$ctrname" # Keep checking status. For the first 2 seconds it must be 'starting' t0=$SECONDS @@ -944,19 +941,19 @@ spec: done assert $output == "2-unhealthy" "After 3 seconds" - run_podman pod rm -fa -t0 - run_podman rm -a + run_podman kube down $fname } @test "podman play --build private registry" { skip_if_remote "--build is not supported in context remote" local registry=localhost:${PODMAN_LOGIN_REGISTRY_PORT} - local from_image=$registry/quadlet_image_test:$(random_string) + local from_image=$registry/quadlet_image_test-$(safename):$(random_string) local authfile=$PODMAN_TMPDIR/authfile.json - mkdir -p $PODMAN_TMPDIR/userimage - cat > $PODMAN_TMPDIR/userimage/Containerfile << _EOF + userimage=userimage-$(safename) + mkdir -p $PODMAN_TMPDIR/$userimage + cat > $PODMAN_TMPDIR/$userimage/Containerfile << _EOF from $from_image USER bin _EOF @@ -976,22 +973,25 @@ _EOF # Remove the local image to make sure it will be pulled again run_podman image rm --ignore $from_image - _write_test_yaml command=id image=userimage - run_podman 125 play kube --build --start=false $PODMAN_TMPDIR/test.yaml + _write_test_yaml command=id image=$userimage + run_podman 125 play kube --build --start=false $TESTYAML assert "$output" "=~" \ - "Error: short-name resolution enforced but cannot prompt without a TTY|Resolving \"userimage\" using unqualified-search registries" \ + "Error: short-name resolution enforced but cannot prompt without a TTY|Resolving \"$userimage\" using unqualified-search registries" \ "The error message does match any of the expected ones" - run_podman play kube --replace --context-dir=$PODMAN_TMPDIR --tls-verify=false --authfile=$authfile --build --start=false $PODMAN_TMPDIR/test.yaml - run_podman inspect --format "{{ .Config.User }}" test_pod-test + run_podman play kube --replace --context-dir=$PODMAN_TMPDIR --tls-verify=false --authfile=$authfile --build --start=false $TESTYAML + run_podman inspect --format "{{ .Config.User }}" $PODCTRNAME is "$output" bin "expect container within pod to run as the bin user" - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod - run_podman rmi -f userimage:latest $from_image + run_podman kube down $TESTYAML + run_podman rmi -f $userimage $from_image } @test "podman play with automount volume" { + imgname="automount-test-$(safename)" + podname="p-$(safename)" + ctrname="c-$(safename)" + cat >$PODMAN_TMPDIR/Containerfile < $fname - run_podman kube play --annotation "io.podman.annotations.kube.image.volumes.mount/testctr=automount_test" $fname + run_podman kube play --annotation "io.podman.annotations.kube.image.volumes.mount/$ctrname=$imgname" $fname - run_podman run --rm automount_test ls /test1 + run_podman run --rm $imgname ls -x /test1 + assert "$output" = "a b c" "ls /test1 on image" run_out_test1="$output" - run_podman exec test_pod-testctr ls /test1 + run_podman exec $podname-$ctrname ls -x /test1 assert "$output" = "$run_out_test1" "matching ls run/exec volume path test1" - run_podman run --rm automount_test ls /test2 + run_podman run --rm $imgname ls -x /test2 + assert "$output" = "asdf ejgre lteghe" "ls /test2 on image" run_out_test2="$output" - run_podman exec test_pod-testctr ls /test2 + run_podman exec $podname-$ctrname ls -x /test2 assert "$output" = "$run_out_test2" "matching ls run/exec volume path test2" - run_podman rm -f -t 0 -a - run_podman rmi automount_test + run_podman kube down $fname + run_podman rmi $imgname } diff --git a/test/system/helpers.bash b/test/system/helpers.bash index fedb9d19aa..671f4b3ac0 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -1153,6 +1153,28 @@ function random_string() { head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length } +############## +# safename # Returns a pseudorandom string suitable for container/image/etc names +############## +# +# Name will include the bats test number and a pseudorandom element, +# eg "t123-xyz123". safename() will return the same string across +# multiple invocations within a given test; this makes it easier for +# a maintainer to see common name patterns. +# +# String is lower-case so it can be used as an image name +# +function safename() { + # FIXME: I don't think these can ever fail. Remove checks once I'm sure. + test -n "$BATS_SUITE_TMPDIR" + test -n "$BATS_SUITE_TEST_NUMBER" + safenamepath=$BATS_SUITE_TMPDIR/.safename.$BATS_SUITE_TEST_NUMBER + if [[ ! -e $safenamepath ]]; then + echo -n "t${BATS_SUITE_TEST_NUMBER}-$(random_string 8 | tr A-Z a-z)" >$safenamepath + fi + cat $safenamepath +} + ######################### # find_exec_pid_files # Returns nothing or exec_pid hash files ######################### diff --git a/vendor/github.com/containers/common/libimage/filters.go b/vendor/github.com/containers/common/libimage/filters.go index f0cf2e5b3c..2465d370d2 100644 --- a/vendor/github.com/containers/common/libimage/filters.go +++ b/vendor/github.com/containers/common/libimage/filters.go @@ -88,6 +88,8 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp return tree, nil } + filterInvalidValue := `invalid image filter %q: must be in the format "filter=value or filter!=value"` + var wantedReferenceMatches, unwantedReferenceMatches []string filters := map[string][]filterFunc{} duplicate := map[string]string{} @@ -101,7 +103,7 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp } else { split = strings.SplitN(f, "=", 2) if len(split) != 2 { - return nil, fmt.Errorf("invalid image filter %q: must be in the format %q", f, "filter=value or filter!=value") + return nil, fmt.Errorf(filterInvalidValue, f) } } @@ -195,7 +197,7 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp filter = filterBefore(until) default: - return nil, fmt.Errorf("unsupported image filter %q", key) + return nil, fmt.Errorf(filterInvalidValue, key) } if negate { filter = negateFilter(filter) diff --git a/vendor/github.com/u-root/uio/ulog/log.go b/vendor/github.com/u-root/uio/ulog/log.go index fbf00f720e..9c68805035 100644 --- a/vendor/github.com/u-root/uio/ulog/log.go +++ b/vendor/github.com/u-root/uio/ulog/log.go @@ -16,7 +16,6 @@ import "log" // It puts your information somewhere for safekeeping. type Logger interface { Printf(format string, v ...interface{}) - Print(v ...interface{}) } // Log is a Logger that prints to the log package's default logger. @@ -24,8 +23,8 @@ var Log Logger = log.Default() type emptyLogger struct{} +// Printf implements Logger.Printf. func (emptyLogger) Printf(format string, v ...interface{}) {} -func (emptyLogger) Print(v ...interface{}) {} // Null is a logger that prints nothing. var Null Logger = emptyLogger{} diff --git a/vendor/github.com/vbauerster/mpb/v8/bar_filler_bar.go b/vendor/github.com/vbauerster/mpb/v8/bar_filler_bar.go index 5d7837ad9b..3860d2f65b 100644 --- a/vendor/github.com/vbauerster/mpb/v8/bar_filler_bar.go +++ b/vendor/github.com/vbauerster/mpb/v8/bar_filler_bar.go @@ -233,7 +233,7 @@ func (s *bFiller) Fill(w io.Writer, stat decor.Statistics) error { var tip component var refilling, filling, padding []byte var fillCount int - curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width))) + curWidth := int(internal.PercentageRound(stat.Total, stat.Current, int64(width))) if curWidth != 0 { if !stat.Completed || s.tipOnComplete { @@ -241,20 +241,19 @@ func (s *bFiller) Fill(w io.Writer, stat decor.Statistics) error { s.tip.count++ fillCount += tip.width } - if stat.Refill != 0 { - refWidth := int(internal.PercentageRound(stat.Total, stat.Refill, uint(width))) + switch refWidth := 0; { + case stat.Refill != 0: + refWidth = int(internal.PercentageRound(stat.Total, stat.Refill, int64(width))) curWidth -= refWidth refWidth += curWidth + fallthrough + default: for w := s.components[iFiller].width; curWidth-fillCount >= w; fillCount += w { filling = append(filling, s.components[iFiller].bytes...) } for w := s.components[iRefiller].width; refWidth-fillCount >= w; fillCount += w { refilling = append(refilling, s.components[iRefiller].bytes...) } - } else { - for w := s.components[iFiller].width; curWidth-fillCount >= w; fillCount += w { - filling = append(filling, s.components[iFiller].bytes...) - } } } diff --git a/vendor/github.com/vbauerster/mpb/v8/decor/eta.go b/vendor/github.com/vbauerster/mpb/v8/decor/eta.go index c4cb2a14bf..cbe657f1f8 100644 --- a/vendor/github.com/vbauerster/mpb/v8/decor/eta.go +++ b/vendor/github.com/vbauerster/mpb/v8/decor/eta.go @@ -81,15 +81,15 @@ func (d *movingAverageETA) Decor(s Statistics) (string, int) { func (d *movingAverageETA) EwmaUpdate(n int64, dur time.Duration) { if n <= 0 { d.zDur += dur - } else { - durPerItem := float64(d.zDur+dur) / float64(n) - if math.IsInf(durPerItem, 0) || math.IsNaN(durPerItem) { - d.zDur += dur - return - } - d.zDur = 0 - d.average.Add(durPerItem) + return } + durPerItem := float64(d.zDur+dur) / float64(n) + if math.IsInf(durPerItem, 0) || math.IsNaN(durPerItem) { + d.zDur += dur + return + } + d.zDur = 0 + d.average.Add(durPerItem) } // AverageETA decorator. It's wrapper of NewAverageETA. diff --git a/vendor/github.com/vbauerster/mpb/v8/decor/percentage.go b/vendor/github.com/vbauerster/mpb/v8/decor/percentage.go index 9709c196cc..298fc1a163 100644 --- a/vendor/github.com/vbauerster/mpb/v8/decor/percentage.go +++ b/vendor/github.com/vbauerster/mpb/v8/decor/percentage.go @@ -61,7 +61,7 @@ func NewPercentage(format string, wcc ...WC) Decorator { format = "% d" } f := func(s Statistics) string { - p := internal.Percentage(s.Total, s.Current, 100) + p := internal.PercentageRound(s.Total, s.Current, 100) return fmt.Sprintf(format, percentageType(p)) } return Any(f, wcc...) diff --git a/vendor/github.com/vbauerster/mpb/v8/decor/speed.go b/vendor/github.com/vbauerster/mpb/v8/decor/speed.go index a90a8edd8a..50415fd9e7 100644 --- a/vendor/github.com/vbauerster/mpb/v8/decor/speed.go +++ b/vendor/github.com/vbauerster/mpb/v8/decor/speed.go @@ -96,15 +96,15 @@ func (d *movingAverageSpeed) Decor(_ Statistics) (string, int) { func (d *movingAverageSpeed) EwmaUpdate(n int64, dur time.Duration) { if n <= 0 { d.zDur += dur - } else { - durPerByte := float64(d.zDur+dur) / float64(n) - if math.IsInf(durPerByte, 0) || math.IsNaN(durPerByte) { - d.zDur += dur - return - } - d.zDur = 0 - d.average.Add(durPerByte) + return } + durPerByte := float64(d.zDur+dur) / float64(n) + if math.IsInf(durPerByte, 0) || math.IsNaN(durPerByte) { + d.zDur += dur + return + } + d.zDur = 0 + d.average.Add(durPerByte) } // AverageSpeed decorator with dynamic unit measure adjustment. It's diff --git a/vendor/github.com/vbauerster/mpb/v8/internal/percentage.go b/vendor/github.com/vbauerster/mpb/v8/internal/percentage.go index 4bc36f5ba8..988227d37d 100644 --- a/vendor/github.com/vbauerster/mpb/v8/internal/percentage.go +++ b/vendor/github.com/vbauerster/mpb/v8/internal/percentage.go @@ -3,17 +3,20 @@ package internal import "math" // Percentage is a helper function, to calculate percentage. -func Percentage(total, current int64, width uint) float64 { - if total <= 0 { +func Percentage(total, current, width uint) float64 { + if total == 0 { return 0 } if current >= total { return float64(width) } - return float64(int64(width)*current) / float64(total) + return float64(width*current) / float64(total) } // PercentageRound same as Percentage but with math.Round. -func PercentageRound(total, current int64, width uint) float64 { - return math.Round(Percentage(total, current, width)) +func PercentageRound(total, current, width int64) float64 { + if total < 0 || current < 0 || width < 0 { + return 0 + } + return math.Round(Percentage(uint(total), uint(current), uint(width))) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 2aef6aa018..b45bfcd6d0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -169,7 +169,7 @@ github.com/containers/buildah/pkg/sshagent github.com/containers/buildah/pkg/util github.com/containers/buildah/pkg/volumes github.com/containers/buildah/util -# github.com/containers/common v0.59.1-0.20240715151621-fdf625dfee0e +# github.com/containers/common v0.59.1-0.20240717135212-fdbae3a180cb ## explicit; go 1.21.0 github.com/containers/common/internal github.com/containers/common/internal/attributedstring @@ -949,8 +949,8 @@ github.com/proglottis/gpgme # github.com/rivo/uniseg v0.4.7 ## explicit; go 1.18 github.com/rivo/uniseg -# github.com/rootless-containers/rootlesskit/v2 v2.1.0 -## explicit; go 1.19 +# github.com/rootless-containers/rootlesskit/v2 v2.2.0 +## explicit; go 1.21.0 github.com/rootless-containers/rootlesskit/v2/pkg/api github.com/rootless-containers/rootlesskit/v2/pkg/lowlevelmsgutil github.com/rootless-containers/rootlesskit/v2/pkg/port @@ -1062,8 +1062,8 @@ github.com/twitchyliquid64/golang-asm/objabi github.com/twitchyliquid64/golang-asm/src github.com/twitchyliquid64/golang-asm/sys github.com/twitchyliquid64/golang-asm/unsafeheader -# github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 -## explicit; go 1.16 +# github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 +## explicit; go 1.21 github.com/u-root/uio/ulog # github.com/ugorji/go/codec v1.2.12 ## explicit; go 1.11 @@ -1079,7 +1079,7 @@ github.com/ulikunitz/xz/lzma github.com/vbatts/tar-split/archive/tar github.com/vbatts/tar-split/tar/asm github.com/vbatts/tar-split/tar/storage -# github.com/vbauerster/mpb/v8 v8.7.3 +# github.com/vbauerster/mpb/v8 v8.7.4 ## explicit; go 1.17 github.com/vbauerster/mpb/v8 github.com/vbauerster/mpb/v8/cwriter