summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/apiv2/27-containersEvents.at4
-rw-r--r--test/e2e/config.go2
-rw-r--r--test/e2e/events_test.go21
-rw-r--r--test/e2e/volume_ls_test.go31
-rw-r--r--test/e2e/volume_plugin_test.go68
-rw-r--r--test/system/150-login.bats2
-rw-r--r--test/system/160-volumes.bats23
-rw-r--r--test/system/200-pod.bats26
-rw-r--r--test/system/500-networking.bats8
-rw-r--r--test/system/helpers.bash31
-rw-r--r--test/testvol/Containerfile9
-rw-r--r--test/testvol/create.go26
-rw-r--r--test/testvol/list.go32
-rw-r--r--test/testvol/main.go23
-rw-r--r--test/testvol/remove.go26
-rw-r--r--test/testvol/util.go29
16 files changed, 346 insertions, 15 deletions
diff --git a/test/apiv2/27-containersEvents.at b/test/apiv2/27-containersEvents.at
index a86f2e353..e0a66e0ac 100644
--- a/test/apiv2/27-containersEvents.at
+++ b/test/apiv2/27-containersEvents.at
@@ -18,6 +18,10 @@ t GET "libpod/events?stream=false&since=$START" 200 \
'select(.status | contains("died")).Action=died' \
'select(.status | contains("died")).Actor.Attributes.containerExitCode=1'
+t GET "libpod/events?stream=false&since=$START" 200 \
+ 'select(.status | contains("start")).Action=start' \
+ 'select(.status | contains("start")).HealthStatus='\
+
# compat api, uses status=die (#12643)
t GET "events?stream=false&since=$START" 200 \
'select(.status | contains("start")).Action=start' \
diff --git a/test/e2e/config.go b/test/e2e/config.go
index 2ca8e2a15..fbcc9dfff 100644
--- a/test/e2e/config.go
+++ b/test/e2e/config.go
@@ -15,7 +15,7 @@ var (
healthcheck = "quay.io/libpod/alpine_healthcheck:latest"
ImageCacheDir = "/tmp/podman/imagecachedir"
fedoraToolbox = "registry.fedoraproject.org/fedora-toolbox:36"
- volumeTest = "quay.io/libpod/volume-plugin-test-img:latest"
+ volumeTest = "quay.io/libpod/volume-plugin-test-img:20220623"
// This image has seccomp profiles that blocks all syscalls.
// The intention behind blocking all syscalls is to prevent
diff --git a/test/e2e/events_test.go b/test/e2e/events_test.go
index 725118ab0..528fa143d 100644
--- a/test/e2e/events_test.go
+++ b/test/e2e/events_test.go
@@ -216,4 +216,25 @@ var _ = Describe("Podman events", func() {
Expect(result.OutputToString()).To(ContainSubstring("create"))
})
+ It("podman events health_status generated", func() {
+ session := podmanTest.Podman([]string{"run", "--name", "test-hc", "-dt", "--health-cmd", "echo working", "busybox"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ for i := 0; i < 5; i++ {
+ hc := podmanTest.Podman([]string{"healthcheck", "run", "test-hc"})
+ hc.WaitWithDefaultTimeout()
+ exitCode := hc.ExitCode()
+ if exitCode == 0 || i == 4 {
+ break
+ }
+ time.Sleep(1 * time.Second)
+ }
+
+ result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "event=health_status"})
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(len(result.OutputToStringArray())).To(BeNumerically(">=", 1), "Number of health_status events")
+ })
+
})
diff --git a/test/e2e/volume_ls_test.go b/test/e2e/volume_ls_test.go
index 19f87fb8a..dcfb13f4e 100644
--- a/test/e2e/volume_ls_test.go
+++ b/test/e2e/volume_ls_test.go
@@ -152,6 +152,37 @@ var _ = Describe("Podman volume ls", func() {
Expect(lsDangling).Should(Exit(0))
Expect(lsDangling.OutputToString()).To(ContainSubstring(volName1))
})
+
+ It("podman ls volume with --filter name", func() {
+ volName1 := "volume1"
+ session := podmanTest.Podman([]string{"volume", "create", volName1})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ volName2 := "volume2"
+ session2 := podmanTest.Podman([]string{"volume", "create", volName2})
+ session2.WaitWithDefaultTimeout()
+ Expect(session2).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"volume", "ls", "--filter", "name=volume1*"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToStringArray()).To(HaveLen(3))
+ Expect(session.OutputToStringArray()[1]).To(ContainSubstring(volName1))
+ Expect(session.OutputToStringArray()[2]).To(ContainSubstring(volName2))
+
+ session = podmanTest.Podman([]string{"volume", "ls", "--filter", "name=volumex"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToStringArray()).To(BeEmpty())
+
+ session = podmanTest.Podman([]string{"volume", "ls", "--filter", "name=volume1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToStringArray()).To(HaveLen(2))
+ Expect(session.OutputToStringArray()[1]).To(ContainSubstring(volName1))
+ })
+
It("podman ls volume with multiple --filter flag", func() {
session := podmanTest.Podman([]string{"volume", "create", "--label", "foo=bar", "myvol"})
volName := session.OutputToString()
diff --git a/test/e2e/volume_plugin_test.go b/test/e2e/volume_plugin_test.go
index 4700afdb5..b585f8dd8 100644
--- a/test/e2e/volume_plugin_test.go
+++ b/test/e2e/volume_plugin_test.go
@@ -6,6 +6,7 @@ import (
"path/filepath"
. "github.com/containers/podman/v4/test/utils"
+ "github.com/containers/storage/pkg/stringid"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
@@ -188,4 +189,71 @@ var _ = Describe("Podman volume plugins", func() {
rmAll.WaitWithDefaultTimeout()
Expect(rmAll).Should(Exit(0))
})
+
+ It("podman volume reload", func() {
+ podmanTest.AddImageToRWStore(volumeTest)
+
+ confFile := filepath.Join(podmanTest.TempDir, "containers.conf")
+ err := os.WriteFile(confFile, []byte(`[engine]
+[engine.volume_plugins]
+testvol5 = "/run/docker/plugins/testvol5.sock"`), 0o644)
+ Expect(err).ToNot(HaveOccurred())
+ os.Setenv("CONTAINERS_CONF", confFile)
+
+ pluginStatePath := filepath.Join(podmanTest.TempDir, "volumes")
+ err = os.Mkdir(pluginStatePath, 0755)
+ Expect(err).ToNot(HaveOccurred())
+
+ // Keep this distinct within tests to avoid multiple tests using the same plugin.
+ pluginName := "testvol5"
+ ctrName := "pluginCtr"
+ plugin := podmanTest.Podman([]string{"run", "--name", ctrName, "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins",
+ "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath})
+ plugin.WaitWithDefaultTimeout()
+ Expect(plugin).Should(Exit(0))
+
+ localvol := "local-" + stringid.GenerateNonCryptoID()
+ // create local volume
+ session := podmanTest.Podman([]string{"volume", "create", localvol})
+ session.WaitWithDefaultTimeout()
+ Expect(session).To(Exit(0))
+
+ vol1 := "vol1-" + stringid.GenerateNonCryptoID()
+ session = podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, vol1})
+ session.WaitWithDefaultTimeout()
+ Expect(session).To(Exit(0))
+
+ // now create volume in plugin without podman
+ vol2 := "vol2-" + stringid.GenerateNonCryptoID()
+ plugin = podmanTest.Podman([]string{"exec", ctrName, "/usr/local/bin/testvol", "--sock-name", pluginName, "create", vol2})
+ plugin.WaitWithDefaultTimeout()
+ Expect(plugin).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"volume", "ls", "-q"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).To(Exit(0))
+ Expect(session.OutputToStringArray()).To(ContainElements(localvol, vol1))
+ Expect(session.ErrorToString()).To(Equal("")) // make sure no errors are shown
+
+ plugin = podmanTest.Podman([]string{"exec", ctrName, "/usr/local/bin/testvol", "--sock-name", pluginName, "remove", vol1})
+ plugin.WaitWithDefaultTimeout()
+ Expect(plugin).Should(Exit(0))
+
+ // now reload volumes from plugins
+ session = podmanTest.Podman([]string{"volume", "reload"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).To(Exit(0))
+ Expect(string(session.Out.Contents())).To(Equal(fmt.Sprintf(`Added:
+%s
+Removed:
+%s
+`, vol2, vol1)))
+ Expect(session.ErrorToString()).To(Equal("")) // make sure no errors are shown
+
+ session = podmanTest.Podman([]string{"volume", "ls", "-q"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).To(Exit(0))
+ Expect(session.OutputToStringArray()).To(ContainElements(localvol, vol2))
+ Expect(session.ErrorToString()).To(Equal("")) // make no errors are shown
+ })
})
diff --git a/test/system/150-login.bats b/test/system/150-login.bats
index 33b8438bf..dc902d5fe 100644
--- a/test/system/150-login.bats
+++ b/test/system/150-login.bats
@@ -314,7 +314,7 @@ function _test_skopeo_credential_sharing() {
fi
# Make sure socket is closed
- if { exec 3<> /dev/tcp/127.0.0.1/${PODMAN_LOGIN_REGISTRY_PORT}; } &>/dev/null; then
+ if ! port_is_free $PODMAN_LOGIN_REGISTRY_PORT; then
die "Socket still seems open"
fi
}
diff --git a/test/system/160-volumes.bats b/test/system/160-volumes.bats
index 797883ec6..da60112a0 100644
--- a/test/system/160-volumes.bats
+++ b/test/system/160-volumes.bats
@@ -64,6 +64,29 @@ function teardown() {
}
+# Filter volumes by name
+@test "podman volume filter --name" {
+ suffix=$(random_string)
+ prefix="volume"
+
+ for i in 1 2; do
+ myvolume=${prefix}_${i}_${suffix}
+ run_podman volume create $myvolume
+ is "$output" "$myvolume" "output from volume create $i"
+ done
+
+ run_podman volume ls --filter name=${prefix}_1.+ --format "{{.Name}}"
+ is "$output" "${prefix}_1_${suffix}" "--filter name=${prefix}_1.+ shows only one volume"
+
+ # The _1* is intentional as asterisk has different meaning in glob and regexp. Make sure this is regexp
+ run_podman volume ls --filter name=${prefix}_1* --format "{{.Name}}"
+ is "$output" "${prefix}_1_${suffix}.*${prefix}_2_${suffix}.*" "--filter name=${prefix}_1* shows ${prefix}_1_${suffix} and ${prefix}_2_${suffix}"
+
+ for i in 1 2; do
+ run_podman volume rm ${prefix}_${i}_${suffix}
+ done
+}
+
# Named volumes
@test "podman volume create / run" {
myvolume=myvol$(random_string)
diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats
index 404ad67ec..f597c0e0a 100644
--- a/test/system/200-pod.bats
+++ b/test/system/200-pod.bats
@@ -472,4 +472,30 @@ spec:
run_podman pod rm $name-pod
}
+@test "pod resource limits" {
+ skip_if_remote "resource limits only implemented on non-remote"
+ if is_rootless; then
+ skip "only meaningful for rootful"
+ fi
+
+ local name1="resources1"
+ run_podman --cgroup-manager=systemd pod create --name=$name1 --cpus=5
+ run_podman --cgroup-manager=systemd pod start $name1
+ run_podman pod inspect --format '{{.CgroupPath}}' $name1
+ local path1="$output"
+ local actual1=$(< /sys/fs/cgroup/$path1/cpu.max)
+ is "$actual1" "500000 100000" "resource limits set properly"
+ run_podman pod --cgroup-manager=systemd rm -f $name1
+
+ local name2="resources2"
+ run_podman --cgroup-manager=cgroupfs pod create --cpus=5 --name=$name2
+ run_podman --cgroup-manager=cgroupfs pod start $name2
+ run_podman pod inspect --format '{{.CgroupPath}}' $name2
+ local path2="$output"
+ local actual2=$(< /sys/fs/cgroup/$path2/cpu.max)
+ is "$actual2" "500000 100000" "resource limits set properly"
+ run_podman --cgroup-manager=cgroupfs pod rm $name2
+
+}
+
# vim: filetype=sh
diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats
index fb785177c..0d724985e 100644
--- a/test/system/500-networking.bats
+++ b/test/system/500-networking.bats
@@ -676,12 +676,12 @@ EOF
@test "podman run port forward range" {
for netmode in bridge slirp4netns:port_handler=slirp4netns slirp4netns:port_handler=rootlesskit; do
- local port=$(random_free_port)
- local end_port=$(( $port + 2 ))
- local range="$port-$end_port:$port-$end_port"
+ local range=$(random_free_port_range 3)
+ local port="${test%-*}"
+ local end_port="${test#-*}"
local random=$(random_string)
- run_podman run --network $netmode -p "$range" -d $IMAGE sleep inf
+ run_podman run --network $netmode -p "$range:$range" -d $IMAGE sleep inf
cid="$output"
for port in $(seq $port $end_port); do
run_podman exec -d $cid nc -l -p $port -e /bin/cat
diff --git a/test/system/helpers.bash b/test/system/helpers.bash
index 74b5ddc4b..273e8d2f5 100644
--- a/test/system/helpers.bash
+++ b/test/system/helpers.bash
@@ -284,7 +284,7 @@ function random_free_port() {
local port
for port in $(shuf -i ${range}); do
- if ! { exec {unused_fd}<> /dev/tcp/127.0.0.1/$port; } &>/dev/null; then
+ if port_is_free $port; then
echo $port
return
fi
@@ -293,6 +293,35 @@ function random_free_port() {
die "Could not find open port in range $range"
}
+function random_free_port_range() {
+ local size=${1?Usage: random_free_port_range SIZE (as in, number of ports)}
+
+ local maxtries=10
+ while [[ $maxtries -gt 0 ]]; do
+ local firstport=$(random_free_port)
+ local all_ports_free=1
+ for i in $(seq 2 $size); do
+ if ! port_is_free $((firstport + $i)); then
+ all_ports_free=
+ break
+ fi
+ done
+ if [[ -n "$all_ports_free" ]]; then
+ echo "$firstport-$((firstport + $size - 1))"
+ return
+ fi
+
+ maxtries=$((maxtries - 1))
+ done
+
+ die "Could not find free port range with size $size"
+}
+
+function port_is_free() {
+ local port=${1?Usage: port_is_free PORT}
+ ! { exec {unused_fd}<> /dev/tcp/127.0.0.1/$port; } &>/dev/null
+}
+
###################
# wait_for_port # Returns once port is available on host
###################
diff --git a/test/testvol/Containerfile b/test/testvol/Containerfile
new file mode 100644
index 000000000..32448f5a9
--- /dev/null
+++ b/test/testvol/Containerfile
@@ -0,0 +1,9 @@
+FROM docker.io/library/golang:1.18-alpine AS build-img
+COPY ./ /go/src/github.com/containers/podman/
+WORKDIR /go/src/github.com/containers/podman
+RUN GO111MODULE=off go build -o /testvol ./test/testvol
+
+FROM alpine
+COPY --from=build-img /testvol /usr/local/bin
+WORKDIR /
+ENTRYPOINT ["/usr/local/bin/testvol", "serve"]
diff --git a/test/testvol/create.go b/test/testvol/create.go
new file mode 100644
index 000000000..d29300f0b
--- /dev/null
+++ b/test/testvol/create.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+ pluginapi "github.com/docker/go-plugins-helpers/volume"
+ "github.com/spf13/cobra"
+)
+
+var createCmd = &cobra.Command{
+ Use: "create NAME",
+ Short: "create a volume",
+ Long: `Create a volume in the volume plugin listening on --sock-name`,
+ Args: cobra.ExactArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return createVol(config.sockName, args[0])
+ },
+}
+
+func createVol(sockName, volName string) error {
+ plugin, err := getPlugin(sockName)
+ if err != nil {
+ return err
+ }
+ createReq := new(pluginapi.CreateRequest)
+ createReq.Name = volName
+ return plugin.CreateVolume(createReq)
+}
diff --git a/test/testvol/list.go b/test/testvol/list.go
new file mode 100644
index 000000000..fea615a70
--- /dev/null
+++ b/test/testvol/list.go
@@ -0,0 +1,32 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+)
+
+var listCmd = &cobra.Command{
+ Use: "list",
+ Short: "list all volumes",
+ Long: `List all volumes from the volume plugin listening on --sock-name`,
+ Args: cobra.NoArgs,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return listVol(config.sockName)
+ },
+}
+
+func listVol(sockName string) error {
+ plugin, err := getPlugin(sockName)
+ if err != nil {
+ return err
+ }
+ vols, err := plugin.ListVolumes()
+ if err != nil {
+ return err
+ }
+ for _, vol := range vols {
+ fmt.Println(vol.Name)
+ }
+ return nil
+}
diff --git a/test/testvol/main.go b/test/testvol/main.go
index 30ab365b3..99c6fb694 100644
--- a/test/testvol/main.go
+++ b/test/testvol/main.go
@@ -14,13 +14,20 @@ import (
)
var rootCmd = &cobra.Command{
- Use: "testvol",
- Short: "testvol - volume plugin for Podman",
+ Use: "testvol",
+ Short: "testvol - volume plugin for Podman testing",
+ PersistentPreRunE: before,
+ SilenceUsage: true,
+}
+
+var serveCmd = &cobra.Command{
+ Use: "serve",
+ Short: "serve the volume plugin on the unix socket",
Long: `Creates simple directory volumes using the Volume Plugin API for testing volume plugin functionality`,
+ Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return startServer(config.sockName)
},
- PersistentPreRunE: before,
}
// Configuration for the volume plugin
@@ -37,9 +44,12 @@ var config = cliConfig{
}
func init() {
- rootCmd.Flags().StringVar(&config.sockName, "sock-name", config.sockName, "Name of unix socket for plugin")
- rootCmd.Flags().StringVar(&config.path, "path", "", "Path to initialize state and mount points")
+ rootCmd.PersistentFlags().StringVar(&config.sockName, "sock-name", config.sockName, "Name of unix socket for plugin")
rootCmd.PersistentFlags().StringVar(&config.logLevel, "log-level", config.logLevel, "Log messages including and over the specified level: debug, info, warn, error, fatal, panic")
+
+ serveCmd.Flags().StringVar(&config.path, "path", "", "Path to initialize state and mount points")
+
+ rootCmd.AddCommand(serveCmd, createCmd, removeCmd, listCmd)
}
func before(cmd *cobra.Command, args []string) error {
@@ -59,11 +69,8 @@ func before(cmd *cobra.Command, args []string) error {
func main() {
if err := rootCmd.Execute(); err != nil {
- logrus.Errorf("Running volume plugin: %v", err)
os.Exit(1)
}
-
- os.Exit(0)
}
// startServer runs the HTTP server and responds to requests
diff --git a/test/testvol/remove.go b/test/testvol/remove.go
new file mode 100644
index 000000000..2839b0b50
--- /dev/null
+++ b/test/testvol/remove.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+ pluginapi "github.com/docker/go-plugins-helpers/volume"
+ "github.com/spf13/cobra"
+)
+
+var removeCmd = &cobra.Command{
+ Use: "remove NAME",
+ Short: "remove a volume",
+ Long: `Remove a volume in the volume plugin listening on --sock-name`,
+ Args: cobra.ExactArgs(1),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return removeVol(config.sockName, args[0])
+ },
+}
+
+func removeVol(sockName, volName string) error {
+ plugin, err := getPlugin(sockName)
+ if err != nil {
+ return err
+ }
+ removeReq := new(pluginapi.RemoveRequest)
+ removeReq.Name = volName
+ return plugin.RemoveVolume(removeReq)
+}
diff --git a/test/testvol/util.go b/test/testvol/util.go
new file mode 100644
index 000000000..7a0aeba86
--- /dev/null
+++ b/test/testvol/util.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+ "path/filepath"
+ "strings"
+
+ "github.com/containers/podman/v4/libpod/plugin"
+)
+
+const pluginSockDir = "/run/docker/plugins"
+
+func getSocketPath(pathOrName string) string {
+ if filepath.IsAbs(pathOrName) {
+ return pathOrName
+ }
+
+ // only a name join it with the default path
+ return filepath.Join(pluginSockDir, pathOrName+".sock")
+}
+
+func getPluginName(pathOrName string) string {
+ return strings.TrimSuffix(filepath.Base(pathOrName), ".sock")
+}
+
+func getPlugin(sockNameOrPath string) (*plugin.VolumePlugin, error) {
+ path := getSocketPath(sockNameOrPath)
+ name := getPluginName(sockNameOrPath)
+ return plugin.GetVolumePlugin(name, path)
+}