From fa75599569c8da7dc59a472c7381f3453cf6c637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Sun, 30 Jun 2024 20:21:09 +0200 Subject: [PATCH 01/31] Remove the unused machine volume-driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver is now hardcoded again, and there can only be one type of mounts at a time (which one changes over time) Revert "Make it possible to select the volume driver" This reverts commit 6630e5cf66cf76aefcfe9caebe5df4f37dd0bdd5. Signed-off-by: Anders F Björklund --- cmd/podman/machine/init.go | 4 ---- docs/source/markdown/podman-machine-init.1.md.in | 4 ---- pkg/machine/define/initopts.go | 1 - 3 files changed, 9 deletions(-) diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go index bc3aac3dd3..54d0a1e38e 100644 --- a/cmd/podman/machine/init.go +++ b/cmd/podman/machine/init.go @@ -123,10 +123,6 @@ func init() { "USB Host passthrough: bus=$1,devnum=$2 or vendor=$1,product=$2") _ = initCmd.RegisterFlagCompletionFunc(USBFlagName, completion.AutocompleteDefault) - VolumeDriverFlagName := "volume-driver" - flags.StringVar(&initOpts.VolumeDriver, VolumeDriverFlagName, "", "Optional volume driver") - _ = initCmd.RegisterFlagCompletionFunc(VolumeDriverFlagName, completion.AutocompleteDefault) - IgnitionPathFlagName := "ignition-path" flags.StringVar(&initOpts.IgnitionPath, IgnitionPathFlagName, "", "Path to ignition file") _ = initCmd.RegisterFlagCompletionFunc(IgnitionPathFlagName, completion.AutocompleteDefault) 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/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 From d26f0ca90f9e6327939d024f632f1511d4d86917 Mon Sep 17 00:00:00 2001 From: Arthur Sengileyev Date: Mon, 8 Jul 2024 18:52:50 +0300 Subject: [PATCH 02/31] Implement disable default mounts via command line Signed-off-by: Arthur Sengileyev --- pkg/machine/e2e/basic_test.go | 27 +++++++++++++++++++++++++++ pkg/machine/qemu/stubber.go | 10 +++++++--- pkg/machine/shim/volume.go | 3 +++ 3 files changed, 37 insertions(+), 3 deletions(-) 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 { From b28027148bb29e0279761f2b49bd3bb37b57cb48 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Mon, 15 Jul 2024 10:35:17 -0600 Subject: [PATCH 03/31] System tests: safe container/image/volume/etc names Many system tests use hardcoded names for containers, images, and everything. This has worked because system tests run serially. It will not work if we ever run in parallel. Create a new safename() helper, and use it as follows: myctr=c_$(safename) myvol1=v1_$(safename) ... Find current instances of hardcoded names, and replace with safe ones. Whether or not we ever end up parallelizing system tests, this is simply good practice. There are far too many instances to fix in one (reviewable) PR. This is commit 1 of N. Signed-off-by: Ed Santiago --- test/system/010-images.bats | 72 ++++++++-------- test/system/030-run.bats | 168 ++++++++++++++++++++---------------- test/system/helpers.bash | 14 +++ 3 files changed, 144 insertions(+), 110 deletions(-) diff --git a/test/system/010-images.bats b/test/system/010-images.bats index 43dd1bcfe9..1664f9fa39 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,28 @@ 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" # 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 +242,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 +278,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 +303,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 +316,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 +326,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 < Date: Mon, 15 Jul 2024 12:39:30 +0200 Subject: [PATCH 04/31] Keep the volume-driver flag deprecated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Anders F Björklund --- cmd/podman/machine/init.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmd/podman/machine/init.go b/cmd/podman/machine/init.go index 54d0a1e38e..d8007f49da 100644 --- a/cmd/podman/machine/init.go +++ b/cmd/podman/machine/init.go @@ -123,6 +123,13 @@ func init() { "USB Host passthrough: bus=$1,devnum=$2 or vendor=$1,product=$2") _ = initCmd.RegisterFlagCompletionFunc(USBFlagName, completion.AutocompleteDefault) + VolumeDriverFlagName := "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") _ = initCmd.RegisterFlagCompletionFunc(IgnitionPathFlagName, completion.AutocompleteDefault) From 692d2a5b08fc7b4664cdaf6a7d3e171d89c647d6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 04:11:53 +0000 Subject: [PATCH 05/31] fix(deps): update module github.com/rootless-containers/rootlesskit/v2 to v2.2.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 19 ++++++++----------- vendor/github.com/u-root/uio/ulog/log.go | 3 +-- vendor/modules.txt | 8 ++++---- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 798748c6b8..7f9960f99b 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ 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 @@ -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..72dce56702 100644 --- a/go.sum +++ b/go.sum @@ -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,8 +500,8 @@ 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= @@ -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/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/modules.txt b/vendor/modules.txt index 2aef6aa018..2a5b6ff87f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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 From 1ec3edd3f6dd1be2fe7ab09ec1c98afcab4bdc34 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Mon, 15 Jul 2024 16:22:47 -0400 Subject: [PATCH 06/31] Do not crash on invalid filters Vendor in latest containers/common Fixes #23120 Signed-off-by: Daniel J Walsh --- go.mod | 2 +- go.sum | 4 ++-- pkg/domain/infra/tunnel/images.go | 6 +++++- test/system/010-images.bats | 3 +++ vendor/github.com/containers/common/libimage/filters.go | 6 ++++-- vendor/modules.txt | 2 +- 6 files changed, 16 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 798748c6b8..84b07d30e6 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 diff --git a/go.sum b/go.sum index d1f4205827..e717934312 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= 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/test/system/010-images.bats b/test/system/010-images.bats index 1664f9fa39..1548615a71 100644 --- a/test/system/010-images.bats +++ b/test/system/010-images.bats @@ -123,6 +123,9 @@ Labels.created_at | 20[0-9-]\\\+T[0-9:]\\\+Z 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 $iname run_podman rm $cname 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/modules.txt b/vendor/modules.txt index 2aef6aa018..6074813d69 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 From 5666100c2ee30a004f27ffd159b30ccbdab37ba5 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Wed, 17 Jul 2024 12:01:12 -0400 Subject: [PATCH 07/31] Remove references and checks for --gpus No way to test this unless running on a nvidia platform. Signed-off-by: Daniel J Walsh --- cmd/podman/containers/run.go | 3 --- hack/xref-docker-options | 1 - 2 files changed, 4 deletions(-) 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/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 From 3c52ef43f57df73e4b5efbbebc790e19f766e88f Mon Sep 17 00:00:00 2001 From: benniekiss <63211101+benniekiss@users.noreply.github.com> Date: Fri, 12 Jul 2024 13:39:45 -0400 Subject: [PATCH 08/31] Expand drop-in search paths * top-level (pod.d) * truncated (unit-.container.d) Signed-off-by: Bennie Milburn-Town <63211101+benniekiss@users.noreply.github.com> --- cmd/quadlet/main.go | 27 ++++---- docs/source/markdown/podman-systemd.unit.5.md | 14 +++-- pkg/systemd/parser/unitfile.go | 55 +++++++++++++++- pkg/systemd/parser/unitfile_test.go | 49 +++++++++++++++ test/system/252-quadlet.bats | 62 ++++++++++++++++++- 5 files changed, 182 insertions(+), 25 deletions(-) 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/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index 56732e11c0..21e3bb2c27 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 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/test/system/252-quadlet.bats b/test/system/252-quadlet.bats index c09e49ed9b..8d8494d35a 100644 --- a/test/system/252-quadlet.bats +++ b/test/system/252-quadlet.bats @@ -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}" < Date: Wed, 17 Jul 2024 22:13:43 +0000 Subject: [PATCH 09/31] chore(deps): update dependency setuptools to v71 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- test/apiv2/python/requirements.txt | 2 +- test/python/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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 From 517c6e6f1095692edffa0b544604f4bdd4e16a01 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Wed, 17 Jul 2024 13:08:48 -0600 Subject: [PATCH 10/31] 700-play.bats: move _write_test_yaml up near top This is almost a NOP; it's needed for making subsequent commits reviewable. Signed-off-by: Ed Santiago --- test/system/700-play.bats | 90 +++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/test/system/700-play.bats b/test/system/700-play.bats index 7c26a4fa8e..c53577822c 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -65,51 +65,6 @@ spec: status: {} " -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 - - run_podman kube play - < $PODMAN_TMPDIR/test.yaml - if selinux_enabled; then - run ls -Zd $TESTDIR - is "$output" "${RELABEL} $TESTDIR" "selinux relabel should have happened" - fi - - # Make sure that the K8s pause image isn't pulled but the local podman-pause is built. - run_podman images - run_podman 1 image exists k8s.gcr.io/pause - 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 -} - -@test "podman play" { - # Testing that the "podman play" cmd still works now that - # "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 - 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 - is "$output" ".* is in use: pod already exists" - run_podman 125 play kube $PODMAN_TMPDIR/test.yaml - 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 @@ -171,6 +126,51 @@ EOF fi } +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 + + run_podman kube play - < $PODMAN_TMPDIR/test.yaml + if selinux_enabled; then + run ls -Zd $TESTDIR + is "$output" "${RELABEL} $TESTDIR" "selinux relabel should have happened" + fi + + # Make sure that the K8s pause image isn't pulled but the local podman-pause is built. + run_podman images + run_podman 1 image exists k8s.gcr.io/pause + 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 +} + +@test "podman play" { + # Testing that the "podman play" cmd still works now that + # "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 + 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 + is "$output" ".* is in use: pod already exists" + run_podman 125 play kube $PODMAN_TMPDIR/test.yaml + is "$output" ".* is in use: pod already exists" + + run_podman stop -a -t 0 + run_podman pod rm -t 0 -f test_pod +} + @test "podman play --service-container" { skip_if_remote "service containers only work locally" From 48aea083c093d3e9520c0ea2042036c7166bd6f4 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Wed, 17 Jul 2024 13:40:13 -0600 Subject: [PATCH 11/31] 700-play.bats: refactor clumsy yamlfile creation Remnant from the very early days of this test file. There's a boilerplate $testYaml string used in many tests; each use requires three clunky lines of prep. Most of those were not needed; we can (and now do) use _write_test_yaml() instead. There are still two instances that could not be fixed in this commit. I will do those next. This commit is kept relatively simple for ease of review. Signed-off-by: Ed Santiago --- test/system/700-play.bats | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/test/system/700-play.bats b/test/system/700-play.bats index c53577822c..4a50679e8c 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -124,6 +124,10 @@ EOF status: {} EOF fi + + # For debugging + echo "# test yaml:" + sed -e "s/^/ /g" <$outfile } RELABEL="system_u:object_r:container_file_t:s0" @@ -243,9 +247,7 @@ RELABEL="system_u:object_r:container_file_t:s0" } @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 run_podman kube play --network host $PODMAN_TMPDIR/test.yaml is "$output" "Pod:.*" "podman kube play should work with --network host" @@ -423,9 +425,7 @@ _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 + _write_test_yaml run_podman play kube $PODMAN_TMPDIR/test.yaml # Force removal of container run_podman rm --force -t0 test_pod-test @@ -436,15 +436,15 @@ _EOF run_podman stop -a -t 0 run_podman pod rm -t 0 -f test_pod + run_podman rm test_pod-test run_podman rmi -f userimage:latest } @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 @@ -467,9 +467,7 @@ _EOF } @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 @@ -487,10 +485,9 @@ _EOF } @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 @@ -767,9 +764,7 @@ spec: } @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 From 987d15a3786e6c5c7a17a143e3069a3ba69471c9 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Wed, 17 Jul 2024 15:55:07 -0600 Subject: [PATCH 12/31] 700-play.bats: eliminate $testYaml Get rid of the last two instances of the clunky $testYaml writing, by adding a 'volume=' arg to _write_test_yaml() Signed-off-by: Ed Santiago --- test/system/700-play.bats | 78 ++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/test/system/700-play.bats b/test/system/700-play.bats index 4a50679e8c..e3e1071920 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -22,49 +22,6 @@ function teardown() { basic_teardown } -testYaml=" -apiVersion: v1 -kind: Pod -metadata: - labels: - app: test - name: test_pod -spec: - containers: - - 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 - resources: {} - securityContext: - runAsUser: 1000 - runAsGroup: 3000 - fsGroup: 2000 - allowPrivilegeEscalation: true - capabilities: {} - privileged: false - seLinuxOptions: - level: \"s0:c1,c2\" - readOnlyRootFilesystem: false - volumeMounts: - - mountPath: /testdir:z - name: home-podman-testdir - workingDir: / - volumes: - - hostPath: - path: TESTDIR - type: Directory - name: home-podman-testdir -status: {} -" - # helper function: writes a yaml file with customizable values function _write_test_yaml() { local outfile=$PODMAN_TMPDIR/test.yaml @@ -76,6 +33,7 @@ function _write_test_yaml() { local command="" local image="$IMAGE" local ctrname="test" + local volume for i;do # This will error on 'foo=' (no value). That's totally OK. local value=$(expr "$i" : '[^=]*=\(.*\)') @@ -86,6 +44,7 @@ function _write_test_yaml() { command=*) command="$value" ;; image=*) image="$value" ;; ctrname=*) ctrname="$value" ;; + volume=*) volume="$value" ;; *) die "_write_test_yaml: cannot grok '$i'" ;; esac done @@ -121,8 +80,35 @@ EOF image: $image name: $ctrname resources: {} -status: {} EOF + + # only makes sense when command is given + if [[ -n "$volume" ]]; then + cat <>$outfile + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + allowPrivilegeEscalation: true + capabilities: {} + privileged: false + seLinuxOptions: + level: "s0:c1,c2" + readOnlyRootFilesystem: false + volumeMounts: + - mountPath: /testdir:z + name: home-podman-testdir + workingDir: / + volumes: + - hostPath: + path: $volume + type: Directory + name: home-podman-testdir +EOF + fi + + # Done. + echo "status: {}" >>$outfile fi # For debugging @@ -135,7 +121,7 @@ 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 if selinux_enabled; then @@ -158,7 +144,7 @@ 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 + _write_test_yaml command=top volume=$TESTDIR run_podman play kube $PODMAN_TMPDIR/test.yaml if selinux_enabled; then run ls -Zd $TESTDIR From 6d01ce417d16a504e05719560d813aa7fb0c7c5e Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Wed, 17 Jul 2024 16:03:27 -0600 Subject: [PATCH 13/31] 700-kube.bats: refactor $PODMAN_TMPDIR/test.yaml Many instances. Simplify by having _write_test_yaml() define the variable TESTYAML and make it available to callers. Global replace, with care taken to undo any instances where _write_test_yaml() is not invoked first. Signed-off-by: Ed Santiago --- test/system/700-play.bats | 115 +++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 59 deletions(-) diff --git a/test/system/700-play.bats b/test/system/700-play.bats index e3e1071920..019ceeb259 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -24,7 +24,8 @@ function teardown() { # helper function: writes a yaml file with customizable values function _write_test_yaml() { - local outfile=$PODMAN_TMPDIR/test.yaml + # This is available to our caller + TESTYAML=$PODMAN_TMPDIR/test.yaml # Function args must all be of the form 'keyword=value' (value may be null) local annotations= @@ -51,30 +52,30 @@ function _write_test_yaml() { # These three header lines are common to all yamls. # Note: use >> (append), not > (overwrite), for multi-pod test - cat >>$outfile <>$TESTYAML <>$outfile - echo " $annotations" >>$outfile + echo " annotations:" >>$TESTYAML + echo " $annotations" >>$TESTYAML fi if [[ -n "$labels" ]]; then - echo " labels:" >>$outfile - echo " $labels" >>$outfile + echo " labels:" >>$TESTYAML + echo " $labels" >>$TESTYAML fi if [[ -n "$name" ]]; then - echo " name: $name" >>$outfile + echo " name: $name" >>$TESTYAML fi # We always have spec and container lines... - echo "spec:" >>$outfile - echo " containers:" >>$outfile + echo "spec:" >>$TESTYAML + echo " containers:" >>$TESTYAML # ...but command is optional. If absent, assume our caller will fill it in. if [[ -n "$command" ]]; then - cat <>$outfile + cat <>$TESTYAML - command: - $command image: $image @@ -84,7 +85,7 @@ EOF # only makes sense when command is given if [[ -n "$volume" ]]; then - cat <>$outfile + cat <>$TESTYAML securityContext: runAsUser: 1000 runAsGroup: 3000 @@ -108,12 +109,12 @@ EOF fi # Done. - echo "status: {}" >>$outfile + echo "status: {}" >>$TESTYAML fi # For debugging echo "# test yaml:" - sed -e "s/^/ /g" <$outfile + sed -e "s/^/ /g" <$TESTYAML } RELABEL="system_u:object_r:container_file_t:s0" @@ -123,7 +124,7 @@ RELABEL="system_u:object_r:container_file_t:s0" mkdir -p $TESTDIR _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" @@ -145,16 +146,16 @@ RELABEL="system_u:object_r:container_file_t:s0" TESTDIR=$PODMAN_TMPDIR/testdir mkdir -p $TESTDIR _write_test_yaml command=top volume=$TESTDIR - run_podman play kube $PODMAN_TMPDIR/test.yaml + 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 @@ -165,13 +166,12 @@ RELABEL="system_u:object_r:container_file_t:s0" 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 @@ -192,7 +192,7 @@ RELABEL="system_u:object_r:container_file_t:s0" # 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. @@ -235,7 +235,7 @@ RELABEL="system_u:object_r:container_file_t:s0" @test "podman kube --network" { _write_test_yaml - 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 @@ -244,7 +244,7 @@ RELABEL="system_u:object_r:container_file_t:s0" run_podman pod rm -t 0 -f test_pod 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 @@ -254,13 +254,13 @@ RELABEL="system_u:object_r:container_file_t:s0" run_podman stop -a -t 0 run_podman pod rm -t 0 -f test_pod - 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 kube down $TESTYAML run_podman 125 inspect test_pod-test is "$output" ".*Error: no such object: \"test_pod-test\"" run_podman pod rm -a @@ -363,7 +363,7 @@ _EOF run_podman image inspect userimage --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 play kube --start=false $TESTYAML run_podman inspect --format "{{ .Config.User }}" test_pod-test is "$output" bin "expect container within pod to run as the bin user" run_podman inspect --format "{{ .Config.Env }}" test_pod-test @@ -385,10 +385,10 @@ 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 + run_podman 125 play kube --build --start=false $TESTYAML assert "$output" =~ "initializing source docker://quay.io/libpod/userimage:latest: reading manifest latest in " - run_podman play kube --replace --context-dir=$PODMAN_TMPDIR --build --start=false $PODMAN_TMPDIR/test.yaml + run_podman play kube --replace --context-dir=$PODMAN_TMPDIR --build --start=false $TESTYAML run_podman inspect --format "{{ .Config.User }}" test_pod-test is "$output" bin "expect container within pod to run as the bin user" @@ -397,7 +397,7 @@ _EOF run_podman rmi -f userimage:latest cd $PODMAN_TMPDIR - run_podman play kube --replace --build --start=false $PODMAN_TMPDIR/test.yaml + run_podman play kube --replace --build --start=false $TESTYAML run_podman inspect --format "{{ .Config.User }}" test_pod-test is "$output" bin "expect container within pod to run as the bin user" @@ -412,13 +412,13 @@ _EOF # functions proplery by removing the storage container. @test "podman kube play --replace external storage" { _write_test_yaml - run_podman play kube $PODMAN_TMPDIR/test.yaml + run_podman play kube $TESTYAML # 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 + run_podman play kube --replace $TESTYAML run_podman stop -a -t 0 run_podman pod rm -t 0 -f test_pod @@ -432,13 +432,13 @@ _EOF RANDOMSTRING=$(random_string 15) ANNOTATION_WITH_COMMA="comma,$(random_string 5)" run_podman kube play --annotation "name=$RANDOMSTRING" \ - --annotation "anno=$ANNOTATION_WITH_COMMA" $PODMAN_TMPDIR/test.yaml + --annotation "anno=$ANNOTATION_WITH_COMMA" $TESTYAML run_podman inspect --format "{{ .Config.Annotations }}" test_pod-test 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 @@ -449,7 +449,7 @@ _EOF 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" { @@ -459,11 +459,11 @@ _EOF default_driver=$output # Make sure that the default log driver is used - run_podman kube play $PODMAN_TMPDIR/test.yaml + run_podman kube play $TESTYAML run_podman inspect --format "{{.HostConfig.LogConfig.Type}}" test_pod-test is "$output" "$default_driver" "play kube uses default log driver" - run_podman kube down $PODMAN_TMPDIR/test.yaml + run_podman kube down $TESTYAML run_podman 125 inspect test_pod-test is "$output" ".*Error: no such object: \"test_pod-test\"" run_podman pod rm -a @@ -479,7 +479,7 @@ _EOF 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 \ + -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 @@ -500,7 +500,7 @@ _EOF @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 @@ -590,7 +589,7 @@ EOF 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" { @@ -753,7 +752,7 @@ spec: _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" } @@ -794,33 +793,31 @@ EOF } @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 kube play $TESTYAML 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 + 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" 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 } @@ -958,12 +955,12 @@ _EOF 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 + 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" \ "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 play kube --replace --context-dir=$PODMAN_TMPDIR --tls-verify=false --authfile=$authfile --build --start=false $TESTYAML run_podman inspect --format "{{ .Config.User }}" test_pod-test is "$output" bin "expect container within pod to run as the bin user" From 380ed3a40dc75118a6182a26c1378cd4253db339 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Tue, 16 Jul 2024 12:11:50 -0600 Subject: [PATCH 14/31] safename: consistent within same test, and, dashes Make safename() invocations consistent within the same test. This puts the onus on the caller to add a unique element when calling multiple times, e.g. "ctr1-$(safename)". This is not too much of a burden. Major benefit is making it easy for a reader to associate containers, pods, volumes, images within a given test. And, use dashes, not underscores. "podman generate kube" removes underscores, making it very difficult to do things like "podman inspect $podname" (because we need to generate "$podname_with_underscores_removed") Signed-off-by: Ed Santiago --- test/system/helpers.bash | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 45421822bf..671f4b3ac0 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -1157,14 +1157,22 @@ function random_string() { # safename # Returns a pseudorandom string suitable for container/image/etc names ############## # -# Name will include the bats test number, eg "t123_xyz123". When/if we -# ever parallelize system tests, this will make it possible to check -# for leaks and identify the test that leaked. +# 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() { - echo "t${BATS_SUITE_TEST_NUMBER}_$(random_string 8 | tr A-Z a-z)" + # 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 } ######################### From 2f7fd64e986867b1ebc2127ef56fa9134225bc08 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Wed, 17 Jul 2024 17:59:50 -0600 Subject: [PATCH 15/31] 700-play.bats: use unique pod/container/image/volume names The end goal is making this test file parallel-safe, by: 1) Having all tests use unique names for all objects; and 2) Not doing "rm -a" or "expect ps to be empty". This commit is not enough to make tests parallel-safe. The rest of the changes are not relevant for now. This set of changes is _necessary_ for parallelizing, and is _meaningful_ (good practice) for current linear-testing podman without introducing any unnecessary cruft. Signed-off-by: Ed Santiago --- test/system/700-play.bats | 358 ++++++++++++++++++++------------------ 1 file changed, 191 insertions(+), 167 deletions(-) diff --git a/test/system/700-play.bats b/test/system/700-play.bats index 019ceeb259..6229a858d4 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -24,27 +24,28 @@ function teardown() { # helper function: writes a yaml file with customizable values function _write_test_yaml() { - # This is available to our caller + # All of these are available to our caller TESTYAML=$PODMAN_TMPDIR/test.yaml + PODNAME="p-$(safename)" + CTRNAME="c-$(safename)" + PODCTRNAME="$PODNAME-$CTRNAME" # 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" - local volume + local volume= 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" ;; + name=*) PODNAME="$value" ;; command=*) command="$value" ;; image=*) image="$value" ;; - ctrname=*) ctrname="$value" ;; + ctrname=*) CTRNAME="$value" ;; volume=*) volume="$value" ;; *) die "_write_test_yaml: cannot grok '$i'" ;; esac @@ -66,8 +67,8 @@ EOF echo " labels:" >>$TESTYAML echo " $labels" >>$TESTYAML fi - if [[ -n "$name" ]]; then - echo " name: $name" >>$TESTYAML + if [[ -n "$PODNAME" ]]; then + echo " name: $PODNAME" >>$TESTYAML fi # We always have spec and container lines... @@ -79,7 +80,7 @@ EOF - command: - $command image: $image - name: $ctrname + name: $CTRNAME resources: {} EOF @@ -136,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" { @@ -158,8 +158,7 @@ RELABEL="system_u:object_r:container_file_t:s0" 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 + run_podman pod rm -t 0 -f $PODNAME } @test "podman play --service-container" { @@ -174,7 +173,7 @@ RELABEL="system_u:object_r:container_file_t:s0" $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" @@ -206,14 +205,14 @@ RELABEL="system_u:object_r:container_file_t:s0" # 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" @@ -224,24 +223,23 @@ RELABEL="system_u:object_r:container_file_t:s0" 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" { - _write_test_yaml + _write_test_yaml command=top 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 $TESTYAML @@ -251,8 +249,7 @@ RELABEL="system_u:object_r:container_file_t:s0" 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 $TESTYAML run_podman pod inspect --format {{.InfraContainerID}} "${lines[1]}" @@ -260,44 +257,47 @@ RELABEL="system_u:object_r:container_file_t:s0" run_podman container inspect --format "{{.HostConfig.NetworkMode}}" $infraID is "$output" "none" "network mode none is set for the container" + run_podman container exists $PODCTRNAME run_podman kube down $TESTYAML - 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 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" { @@ -309,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 $TESTYAML - run_podman inspect --format "{{ .Config.User }}" test_pod-test + 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 + _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/userimage:latest: reading manifest latest in " + 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 $TESTYAML - run_podman inspect --format "{{ .Config.User }}" test_pod-test + 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 $TESTYAML - run_podman inspect --format "{{ .Config.User }}" test_pod-test + 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 @@ -412,18 +414,16 @@ _EOF # functions proplery by removing the storage container. @test "podman kube play --replace external storage" { _write_test_yaml + run_podman play kube $TESTYAML # Force removal of container - run_podman rm --force -t0 test_pod-test + run_podman rm --force -t0 $PODCTRNAME # Create external container using buildah with same name - buildah from --name test_pod-test $IMAGE + buildah from --name $PODCTRNAME $IMAGE # --replace deletes the buildah container and replace it with new one run_podman play kube --replace $TESTYAML - run_podman stop -a -t 0 - run_podman pod rm -t 0 -f test_pod - run_podman rm test_pod-test - run_podman rmi -f userimage:latest + run_podman pod rm -t 0 -f $PODNAME } @test "podman kube --annotation" { @@ -433,7 +433,7 @@ _EOF ANNOTATION_WITH_COMMA="comma,$(random_string 5)" run_podman kube play --annotation "name=$RANDOMSTRING" \ --annotation "anno=$ANNOTATION_WITH_COMMA" $TESTYAML - run_podman inspect --format "{{ .Config.Annotations }}" test_pod-test + 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" @@ -441,11 +441,11 @@ _EOF 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 @@ -460,14 +460,12 @@ _EOF # Make sure that the default log driver is used run_podman kube play $TESTYAML - run_podman inspect --format "{{.HostConfig.LogConfig.Type}}" test_pod-test + run_podman inspect --format "{{.HostConfig.LogConfig.Type}}" $PODCTRNAME is "$output" "$default_driver" "play kube uses default log driver" run_podman kube down $TESTYAML - 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 125 inspect $PODCTRNAME + is "$output" ".*Error: no such object: \"$PODCTRNAME\"" } @test "podman kube play - URL" { @@ -478,7 +476,8 @@ _EOF HOST_PORT=$(random_free_port) SERVER=http://127.0.0.1:$HOST_PORT - run_podman run -d --name myyaml -p "$HOST_PORT:80" \ + 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 \ @@ -488,14 +487,13 @@ _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" { @@ -505,18 +503,18 @@ _EOF - ls - /dev/shm/test1 image: $IMAGE - name: testCtr + name: ${CTRNAME}-test initContainers: - command: - touch - /dev/shm/test1 image: $IMAGE - name: initCtr + name: ${CTRNAME}-init EOF run_podman kube play $TESTYAML assert "$output" !~ "level=" "init containers should not generate logrus.Error" - run_podman inspect --format "{{.State.ExitCode}}" test_pod-testCtr + run_podman inspect --format "{{.State.ExitCode}}" ${PODNAME}-${CTRNAME}-test is "$output" "0" "init container should have created /dev/shm/test1" run_podman kube down $TESTYAML @@ -526,7 +524,7 @@ EOF HOST_PORT=$(random_free_port) _write_test_yaml cat >>$TESTYAML < $fname run_podman kube play --wait $fname @@ -675,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 " --- @@ -689,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 @@ -706,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 @@ -731,19 +746,19 @@ 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 } @@ -768,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 @@ -788,16 +803,15 @@ 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" { - _write_test_yaml command=/home/podman/pause - run_podman kube play $TESTYAML - 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 $TESTYAML + _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" { @@ -810,7 +824,7 @@ EOF assert "$output" =~ "Pulling image $IMAGE \(policy\: missing\)" "default pull policy is missing" run_podman kube down $TESTYAML - local_image="localhost/name:latest" + local_image="localhost/i-$(safename):latest" run_podman tag $IMAGE $local_image rm $TESTYAML _write_test_yaml command=true image=$local_image @@ -823,16 +837,19 @@ EOF } @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 @@ -849,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 @@ -870,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 @@ -901,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 @@ -922,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 @@ -954,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 + _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 $TESTYAML - run_podman inspect --format "{{ .Config.User }}" test_pod-test + 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 } From 02c094425f290c0885998499cf6c7d5b799ac66a Mon Sep 17 00:00:00 2001 From: Urvashi Mohnani Date: Wed, 17 Jul 2024 22:31:48 -0400 Subject: [PATCH 16/31] Fix name for builder in farm connection Ensure that the map of builders we create links the builder name and not the farm name to the image engine of that node. This was a regression introduced during the farm conf rework. Signed-off-by: Urvashi Mohnani --- pkg/farm/farm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 }) } From 1413a284098d7cd4f3c620696bb3d4b32025576b Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Wed, 17 Jul 2024 19:08:25 +0200 Subject: [PATCH 17/31] podman auto-update: fix ErrNoSuchCtr race If a container is removed during the loop we should just ignore it. No reason to log this as error to the user. Fixes #23279 Signed-off-by: Paul Holzinger --- pkg/autoupdate/autoupdate.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) 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. From 46f404489385afd47eb256dca964091e906ef1c1 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Wed, 17 Jul 2024 19:17:50 +0200 Subject: [PATCH 18/31] podman system df: fix fix ErrNoSuchCtr/Volume race When a container or volume is removed during the loop this is not a problem and we should just skip it as it is not a user bug and just a normal race. Signed-off-by: Paul Holzinger --- pkg/domain/infra/abi/system.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) 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 { From 7100ead475e6130c811c356d2ba1869a453af17d Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Wed, 17 Jul 2024 14:38:49 -0600 Subject: [PATCH 19/31] nc -p considered harmful nmap-ncat has been downgraded on Fedora, to 7.92. nc -l -p PORT requires 7.95. Switch to nc -l ADDR PORT. Signed-off-by: Ed Santiago --- test/system/500-networking.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index fb57afd4dd..0bd2257310 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -760,7 +760,7 @@ nameserver 8.8.8.8" "nameserver order is correct" cid="$output" # make sure binding the same port fails - run timeout 5 nc -l -p $port 127.0.0.1 + run timeout 5 nc -l 127.0.0.1 $port assert "$status" -eq 2 "ncat unexpected exit code" assert "$output" =~ "127.0.0.1:$port: Address already in use" "ncat error message" From f630eebcfac11887cc9e6f5b10360c1436407442 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 18 Jul 2024 13:17:49 +0200 Subject: [PATCH 20/31] pkg/machine/compression: skip decompress bar for empty file When the file is empty it is possible our code panics as bar.ProxyReader returns nil when the bar is finished which is the case for 0 size as it doesn't have to read anything from there. However as this happens on different goroutines it is race and most of the time still works. To fix this simply skip the progress bar setup for empty files. While at it fix the deprecated argument in the tests. Fixes #23281 Signed-off-by: Paul Holzinger --- pkg/machine/compression/decompress.go | 31 +++++++++++++++++---------- test/system/610-format.bats | 2 +- 2 files changed, 21 insertions(+), 12 deletions(-) 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/test/system/610-format.bats b/test/system/610-format.bats index 448d1e6ed6..165c50127d 100644 --- a/test/system/610-format.bats +++ b/test/system/610-format.bats @@ -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+=" From 85f4f89810b3211065e38f9d252e679948100c8c Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 18 Jul 2024 14:42:24 +0200 Subject: [PATCH 21/31] system connection remove: use Args function to validate Using the ExactArgs(1) function is better because we have less duplication of the error text and the ValidArgsFunction uses that to suggest shell completion. The command before this commit would suggest connection names even if there was already one arg on the cli set. However because there is the --all option we still must exclude that first. Signed-off-by: Paul Holzinger --- cmd/podman/system/connection/remove.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) 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 = "" From e1caf80e814a2e0ed026706f0a16371ca40dbc26 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 18 Jul 2024 14:08:11 +0200 Subject: [PATCH 22/31] podman ps: fix racy pod name query The pod name was queried without holding the container lock, thus it was possible that the pod was deleted in the meantime and podman just failed with "no such pod" as the errors.Is() check matched the wrong error. Move it into the locked code this should prevent anyone from removing the pod while the container is part of it. Also fix the returned error, there is no reason to special case one specific error just wrap any error here so callers at least know where it happened. However this is not good enough because the batch doesn't update the state which means it see everything before the container was locked. In this case it might be possible the ctr and pod was already removed so let the caller skip both ctr and pod removed errors. Fixes #23282 Signed-off-by: Paul Holzinger --- pkg/ps/ps.go | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) 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{ From 182224defbb4a2bf08a40dd6a7f3b1e158067c2e Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 18 Jul 2024 17:48:21 +0200 Subject: [PATCH 23/31] podman pod stats: fix pod rm race If a pod is removed when calling podman pod stats there is a race where the command might fail with no such pod. This is not a user error, like the ps/ls command skip it and move to the next one. Fixes #23327 Signed-off-by: Paul Holzinger --- pkg/domain/infra/abi/pods_stats.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/domain/infra/abi/pods_stats.go b/pkg/domain/infra/abi/pods_stats.go index 8ece02c2c6..841a8e2972 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" @@ -39,6 +40,10 @@ func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.Po for i := range pods { // Access by index to prevent potential loop-variable leaks. podStats, err := pods[i].GetPodStats(nil) if err != nil { + // pod was removed, skip it + if errors.Is(err, define.ErrNoSuchPod) { + continue + } return nil, err } podID := pods[i].ID()[:12] From 55749af0c7c7d8fed8e095a649d216450313fbdb Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Fri, 19 Jul 2024 13:21:34 +0200 Subject: [PATCH 24/31] podman stats: fix race when ctr process exists stats read from the cgroup, and in order to know the cgroup we check the pid for the cgroup. However there is a window where the pid exited and podman did not yet updated its internal state. In this case the code returns ErrCtrStopped so we should ignore this error as well. Fixes #23334 Signed-off-by: Paul Holzinger --- pkg/domain/infra/abi/containers.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 From 5a7199015ad4e981edbc6dd21657c7a772d67e4b Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Fri, 19 Jul 2024 13:35:49 +0200 Subject: [PATCH 25/31] Bump bundled krunkit to 0.1.2 Bump the bundled krunkit version from 0.1.1 to 0.1.2. Fixes: #23194 Signed-off-by: Sergio Lopez --- contrib/pkginstaller/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 5e8884ab0dfba9d6ce06d4263b80e00574c7f464 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Fri, 19 Jul 2024 15:11:58 +0200 Subject: [PATCH 26/31] libpod: correctly capture healthcheck output Using the scanner is just unnecessary complicated an buggy as it will not read the final line with a newline. There is also the problem that it happens in a separate goroutine so it could loose output if we read the array before the scanner was done. The API accepts a Writer so we can just directly use a bytes.Buffer which captures all output in memory without the need of another goroutine. This also means that now we always include the final newline in the output. I checked with docker and they do the same so this is good. Fixes #23332 Signed-off-by: Paul Holzinger --- libpod/healthcheck.go | 22 +++++----------------- test/e2e/healthcheck_run_test.go | 25 +++++++++++-------------- test/system/220-healthcheck.bats | 6 +++--- 3 files changed, 19 insertions(+), 34 deletions(-) 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/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/system/220-healthcheck.bats b/test/system/220-healthcheck.bats index 95c4e4646c..a35416eae1 100644 --- a/test/system/220-healthcheck.bats +++ b/test/system/220-healthcheck.bats @@ -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 From 742d29e9d8299bfb430b1b9bf1aa0f9c5135e168 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Jul 2024 15:19:17 +0000 Subject: [PATCH 27/31] Update module github.com/vbauerster/mpb/v8 to v8.7.4 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- .../vbauerster/mpb/v8/bar_filler_bar.go | 13 ++++++------- vendor/github.com/vbauerster/mpb/v8/decor/eta.go | 16 ++++++++-------- .../vbauerster/mpb/v8/decor/percentage.go | 2 +- .../github.com/vbauerster/mpb/v8/decor/speed.go | 16 ++++++++-------- .../vbauerster/mpb/v8/internal/percentage.go | 13 ++++++++----- vendor/modules.txt | 2 +- 8 files changed, 35 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index 0f76249915..f299af04e6 100644 --- a/go.mod +++ b/go.mod @@ -67,7 +67,7 @@ require ( 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 diff --git a/go.sum b/go.sum index 48c154ffa7..1ce96d6225 100644 --- a/go.sum +++ b/go.sum @@ -508,8 +508,8 @@ 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= 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 eea5162401..b45bfcd6d0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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 From 55b6e4c3e8bf10994a419cb314eaa296670d1d7d Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Mon, 22 Jul 2024 10:16:18 +0200 Subject: [PATCH 28/31] podman pod stats: fix race when ctr process exits Like commit 55749af0c7 but for podman *pod* stats not the normal podman stats. We must ignore ErrCtrStopped here as well as this will happen when the container process exited. While at it remove a useless argument from the function as it was always nil and restructure the logic flow to make it easier to read. Fixes #23334 Signed-off-by: Paul Holzinger --- libpod/pod.go | 16 ++++++++-------- pkg/domain/infra/abi/pods_stats.go | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) 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/pkg/domain/infra/abi/pods_stats.go b/pkg/domain/infra/abi/pods_stats.go index 841a8e2972..26d070344e 100644 --- a/pkg/domain/infra/abi/pods_stats.go +++ b/pkg/domain/infra/abi/pods_stats.go @@ -38,7 +38,7 @@ 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) { From 0ecd6fa59fd03d98fd20ebae80793d8ec7aa1187 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Sun, 21 Jul 2024 06:08:00 -0400 Subject: [PATCH 29/31] Add support for StopSignal in quadlet .container files Fixes: https://github.com/containers/podman/issues/23050 Signed-off-by: Daniel J Walsh --- docs/source/markdown/podman-systemd.unit.5.md | 7 +++++++ pkg/systemd/quadlet/quadlet.go | 6 ++++++ test/e2e/quadlet/stopsignal.container | 5 +++++ test/e2e/quadlet_test.go | 1 + 4 files changed, 19 insertions(+) create mode 100644 test/e2e/quadlet/stopsignal.container diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index 21e3bb2c27..81dd92c1e1 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -306,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 | @@ -731,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/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/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"), From b6b61a6a49ca5f8ffd42a43eb0d18dd1eab3aecb Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Fri, 19 Jul 2024 16:58:21 +0200 Subject: [PATCH 30/31] libpod: add hidden env to set sqlite timeout Some users want to experiment with different timeout values. Fixes #23236 Signed-off-by: Paul Holzinger --- libpod/sqlite_state.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) 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) } From 52094958651f697cba24dca50b4f370e15cf8396 Mon Sep 17 00:00:00 2001 From: Paul Holzinger Date: Thu, 18 Jul 2024 19:47:58 +0200 Subject: [PATCH 31/31] test/e2e: fix ncat tests These tests are flaking for unknown reasons. One problem could be ncat did not bound the port before we connect to it. Simple fix do not use ncat and just use the golang API to listen on the port without the extra ncat process. This should be race free in theory. Also do not run the nc container in the background, we want to see the errors from the ncat process in the container. And because both tests do the same thing deduplicate them into one that just uses a loop to create both tests. Fixes #23263 Signed-off-by: Paul Holzinger --- test/e2e/run_networking_test.go | 90 +++++++++++++++++---------------- 1 file changed, 47 insertions(+), 43 deletions(-) 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})