Merge current main into v5.2 branch

We want everything from main into the v5.2 branch. We'll consider
ourselves officially branched after v5.2.0-rc2 releases.

Signed-off-by: Matt Heon <mheon@redhat.com>
This commit is contained in:
Matt Heon 2024-07-22 13:01:33 -04:00
commit 579f36a061
49 changed files with 942 additions and 661 deletions

View file

@ -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
}

View file

@ -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")

View file

@ -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 = ""

View file

@ -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 = ""
}

View file

@ -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

View file

@ -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.

View file

@ -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.

8
go.mod
View file

@ -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

27
go.sum
View file

@ -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=

View file

@ -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

View file

@ -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]
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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.

View file

@ -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

View file

@ -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]

View file

@ -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 {

View file

@ -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)

View file

@ -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
})
}

View file

@ -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
}

View file

@ -8,7 +8,6 @@ type InitOptions struct {
IgnitionPath string
Image string
Volumes []string
VolumeDriver string
IsDefault bool
Memory uint64
Name string

View file

@ -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)

View file

@ -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 {

View file

@ -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 {

View file

@ -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{

View file

@ -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 {

View file

@ -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))
}
}

View file

@ -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)
}

View file

@ -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

View file

@ -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() {

View file

@ -0,0 +1,5 @@
## assert-podman-args "--stop-signal" "SIGKILL"
[Container]
Image=localhost/imagename
StopSignal=SIGKILL

View file

@ -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"),

View file

@ -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})

View file

@ -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

View file

@ -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" {

View file

@ -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 '<timestamp> <P|F> <data>', 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 <<EOF
[engine]
static_dir="$static_dir"
EOF
ctr=$(random_string)
ctr=c_$(safename)
CONTAINERS_CONF_OVERRIDE=$containersconf PODMAN_TIMEOUT=20 run_podman run --name=$ctr $IMAGE true
CONTAINERS_CONF_OVERRIDE=$containersconf PODMAN_TIMEOUT=20 run_podman inspect --format "{{.ID}}" $ctr
cid="$output"
@ -1370,7 +1384,7 @@ search | $IMAGE |
}
@test "podman create container with conflicting name" {
local cname="$(random_string 10 | tr A-Z a-z)"
local cname=c_$(safename)
local output_msg_ext="^Error: .*: the container name \"$cname\" is already in use by .* You have to remove that container to be able to reuse that name: that name is already in use by an external entity, or use --replace to instruct Podman to do so."
local output_msg="^Error: .*: the container name \"$cname\" is already in use by .* You have to remove that container to be able to reuse that name: that name is already in use, or use --replace to instruct Podman to do so."
if is_remote; then
@ -1433,12 +1447,13 @@ search | $IMAGE |
}
@test "podman run - rm pod if container creation failed with -pod new:" {
run_podman run -d --name foobar $IMAGE hostname
cname=c_$(safename)
run_podman run -d --name $cname $IMAGE hostname
cid=$output
podname=pod$(random_string)
run_podman 125 run --rm --pod "new:$podname" --name foobar $IMAGE hostname
is "$output" ".*creating container storage: the container name \"foobar\" is already in use by"
podname=pod_$(safename)
run_podman 125 run --rm --pod "new:$podname" --name $cname $IMAGE hostname
is "$output" ".*creating container storage: the container name \"$cname\" is already in use by"
# pod should've been cleaned up
# if container creation failed
@ -1467,27 +1482,28 @@ search | $IMAGE |
@test "podman run - stopping loop" {
skip_if_remote "this doesn't work with with the REST service"
run_podman run -d --name testctr --stop-timeout 240 $IMAGE sh -c 'echo READY; sleep 999'
cname=c_$(safename)
run_podman run -d --name $cname --stop-timeout 240 $IMAGE sh -c 'echo READY; sleep 999'
cid="$output"
wait_for_ready testctr
wait_for_ready $cname
run_podman inspect --format '{{ .State.ConmonPid }}' testctr
run_podman inspect --format '{{ .State.ConmonPid }}' $cname
conmon_pid=$output
${PODMAN} stop testctr &
${PODMAN} stop $cname &
stop_pid=$!
timeout=20
while :;do
sleep 0.5
run_podman container inspect --format '{{.State.Status}}' testctr
run_podman container inspect --format '{{.State.Status}}' $cname
if [[ "$output" = "stopping" ]]; then
break
fi
timeout=$((timeout - 1))
if [[ $timeout == 0 ]]; then
run_podman ps -a
die "Timed out waiting for testctr to acknowledge signal"
die "Timed out waiting for container to acknowledge signal"
fi
done
@ -1496,14 +1512,14 @@ search | $IMAGE |
# Unclear why `-t0` is required here, works locally without.
# But it shouldn't hurt and does make the test pass...
PODMAN_TIMEOUT=5 run_podman 125 stop -t0 testctr
PODMAN_TIMEOUT=5 run_podman 125 stop -t0 $cname
is "$output" "Error: container .* conmon exited prematurely, exit code could not be retrieved: internal libpod error" "correct error on missing conmon"
# This should be safe because stop is guaranteed to call cleanup?
run_podman inspect --format "{{ .State.Status }}" testctr
run_podman inspect --format "{{ .State.Status }}" $cname
is "$output" "exited" "container has successfully transitioned to exited state after stop"
run_podman rm -f -t0 testctr
run_podman rm -f -t0 $cname
}
# vim: filetype=sh

View file

@ -59,7 +59,7 @@ function _check_health {
Status | \"healthy\"
FailingStreak | 0
Log[-1].ExitCode | 0
Log[-1].Output | \"Life is Good on stdout\\\nLife is Good on stderr\"
Log[-1].Output | \"Life is Good on stdout\\\nLife is Good on stderr\\\n\"
" "$current_time" "healthy"
current_time=$(date --iso-8601=seconds)
@ -71,7 +71,7 @@ Log[-1].Output | \"Life is Good on stdout\\\nLife is Good on stderr\"
Status | \"healthy\"
FailingStreak | [123]
Log[-1].ExitCode | 1
Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\"
Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\\\n\"
" "$current_time" "healthy"
# Check that we now we do have valid podman units with this
@ -85,7 +85,7 @@ Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\"
Status | \"unhealthy\"
FailingStreak | [3456]
Log[-1].ExitCode | 1
Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\"
Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\\\n\"
" "$current_time" "unhealthy"
# now the on-failure should kick in and kill the container

View file

@ -64,14 +64,18 @@ function run_quadlet() {
assert $status -eq 0 "Failed to convert quadlet file: $sourcefile"
is "$output" "" "quadlet should report no errors"
run cat $UNIT_DIR/$service
assert $status -eq 0 "Could not cat $UNIT_DIR/$service"
echo "$output"
local content="$output"
# Ensure this is teared down
UNIT_FILES+=("$UNIT_DIR/$service")
QUADLET_SERVICE_NAME="$service"
QUADLET_SERVICE_CONTENT="$content"
QUADLET_SYSLOG_ID="$(basename $service .service)"
QUADLET_CONTAINER_NAME="systemd-$QUADLET_SYSLOG_ID"
cat $UNIT_DIR/$QUADLET_SERVICE_NAME
}
function service_setup() {
@ -1599,4 +1603,58 @@ EOF
run_podman rmi $untagged_image:latest $built_image $(pause_image)
run_podman network rm podman-default-kube-network
}
@test "quadlet - drop-in files" {
local quadlet_tmpdir="${PODMAN_TMPDIR}/dropins"
local quadlet_file="truncated-$(random_string).container"
local -A dropin_dirs=(
[toplevel]=container.d
[truncated]=truncated-.container.d
[quadlet]="${quadlet_file}.d"
)
# Table of drop-in .conf files. Format is:
#
# apply | dir | filename | [Section] | Content=...
local dropin_files="
y | toplevel | 10 | [Unit] | Description=Test File for Dropin Configuration
n | toplevel | 99 | [Install] | WantedBy=default.target
y | truncated | 50 | [Container] | ContainerName=truncated-dropins
n | truncated | 99 | [Service] | Restart=always
n | truncated | 99 | [Install] | WantedBy=multiuser.target
y | quadlet | 99 | [Service] | RestartSec=60s
"
# Pass 1: Create all drop-in directories and files
while read apply dir file section content; do
local d="${quadlet_tmpdir}/${dropin_dirs[${dir}]}"
mkdir -p "${d}"
local f="${d}/${file}.conf"
echo "${section}" >>"${f}"
echo "${content}" >>"${f}"
done < <(parse_table "${dropin_files}")
# Create the base quadlet file
quadlet_base="${PODMAN_TMPDIR}/${quadlet_file}"
cat > "${quadlet_base}" <<EOF
[Container]
Image="${IMAGE}"
EOF
# Generate the quadlet file from the base file and any drop-in .conf files.
run_quadlet "${quadlet_base}" "${quadlet_tmpdir}"
# Pass 2: test whether the expected .conf files are applied
# and the overridden .conf files are not.
while read apply dir file section content; do
if [[ "${apply}" = "y" ]]; then
assert "${QUADLET_SERVICE_CONTENT}" =~ "${content}" "Set in ${dir}/${file}.conf"
else
assert "${QUADLET_SERVICE_CONTENT}" !~ "${content}" "Set in ${dir}/${file}.conf but should have been overridden"
fi
done < <(parse_table "${dropin_files}")
}
# vim: filetype=sh

View file

@ -149,7 +149,7 @@ function check_subcommand() {
# ...or machine. But podman machine is ultra-finicky, it fails as root
# or if qemu is missing. Instead of checking for all the possible ways
# to skip it, just try running init. If it works, we can test it.
run_podman '?' machine init --image-path=/dev/null mymachine
run_podman '?' machine init --image=/dev/null mymachine
if [[ $status -eq 0 ]]; then
can_run_podman_machine=true
extra_args_table+="

File diff suppressed because it is too large Load diff

View file

@ -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
#########################

View file

@ -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)

View file

@ -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{}

View file

@ -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...)
}
}
}

View file

@ -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.

View file

@ -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...)

View file

@ -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

View file

@ -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)))
}

12
vendor/modules.txt vendored
View file

@ -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