summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/apiv2/25-containersMore.at4
-rw-r--r--test/e2e/generate_kube_test.go70
-rw-r--r--test/e2e/inspect_test.go15
-rw-r--r--test/e2e/network_test.go8
-rw-r--r--test/e2e/run_networking_test.go63
-rw-r--r--test/e2e/run_privileged_test.go12
-rw-r--r--test/e2e/run_test.go15
-rw-r--r--test/e2e/stats_test.go10
-rw-r--r--test/system/015-help.bats13
-rw-r--r--test/system/070-build.bats42
-rw-r--r--test/system/500-networking.bats64
-rw-r--r--test/system/600-completion.bats272
-rw-r--r--test/system/helpers.bash10
-rw-r--r--test/utils/utils.go14
14 files changed, 597 insertions, 15 deletions
diff --git a/test/apiv2/25-containersMore.at b/test/apiv2/25-containersMore.at
index 9d774ef27..b88c798eb 100644
--- a/test/apiv2/25-containersMore.at
+++ b/test/apiv2/25-containersMore.at
@@ -65,13 +65,13 @@ t GET libpod/containers/json?last=1 200 \
cid=$(jq -r '.[0].Id' <<<"$output")
-t GET libpod/generate/$cid/kube 200
+t GET libpod/generate/kube?names=$cid 200
like "$output" ".*apiVersion:.*" "Check generated kube yaml - apiVersion"
like "$output" ".*kind:\\sPod.*" "Check generated kube yaml - kind: Pod"
like "$output" ".*metadata:.*" "Check generated kube yaml - metadata"
like "$output" ".*spec:.*" "Check generated kube yaml - spec"
-t GET libpod/generate/$cid/kube?service=true 200
+t GET "libpod/generate/kube?service=true&names=$cid" 200
like "$output" ".*apiVersion:.*" "Check generated kube yaml(service=true) - apiVersion"
like "$output" ".*kind:\\sPod.*" "Check generated kube yaml(service=true) - kind: Pod"
like "$output" ".*metadata:.*" "Check generated kube yaml(service=true) - metadata"
diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go
index c8782c743..0950a9321 100644
--- a/test/e2e/generate_kube_test.go
+++ b/test/e2e/generate_kube_test.go
@@ -469,4 +469,74 @@ var _ = Describe("Podman generate kube", func() {
Expect(inspect.ExitCode()).To(Equal(0))
Expect(inspect.OutputToString()).To(ContainSubstring(`"pid"`))
})
+
+ It("podman generate kube multiple pods should fail", func() {
+ pod1 := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:pod1", ALPINE, "top"})
+ pod1.WaitWithDefaultTimeout()
+ Expect(pod1.ExitCode()).To(Equal(0))
+
+ pod2 := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:pod2", ALPINE, "top"})
+ pod2.WaitWithDefaultTimeout()
+ Expect(pod2.ExitCode()).To(Equal(0))
+
+ kube := podmanTest.Podman([]string{"generate", "kube", "pod1", "pod2"})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).ToNot(Equal(0))
+ })
+
+ It("podman generate kube with pods and containers should fail", func() {
+ pod1 := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:pod1", ALPINE, "top"})
+ pod1.WaitWithDefaultTimeout()
+ Expect(pod1.ExitCode()).To(Equal(0))
+
+ pod2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top", ALPINE, "top"})
+ pod2.WaitWithDefaultTimeout()
+ Expect(pod2.ExitCode()).To(Equal(0))
+
+ kube := podmanTest.Podman([]string{"generate", "kube", "pod1", "top"})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).ToNot(Equal(0))
+ })
+
+ It("podman generate kube with containers in a pod should fail", func() {
+ pod1 := podmanTest.Podman([]string{"pod", "create", "--name", "pod1"})
+ pod1.WaitWithDefaultTimeout()
+ Expect(pod1.ExitCode()).To(Equal(0))
+
+ con := podmanTest.Podman([]string{"run", "-dt", "--pod", "pod1", "--name", "top", ALPINE, "top"})
+ con.WaitWithDefaultTimeout()
+ Expect(con.ExitCode()).To(Equal(0))
+
+ kube := podmanTest.Podman([]string{"generate", "kube", "top"})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).ToNot(Equal(0))
+ })
+
+ It("podman generate kube with multiple containers", func() {
+ con1 := podmanTest.Podman([]string{"run", "-dt", "--name", "con1", ALPINE, "top"})
+ con1.WaitWithDefaultTimeout()
+ Expect(con1.ExitCode()).To(Equal(0))
+
+ con2 := podmanTest.Podman([]string{"run", "-dt", "--name", "con2", ALPINE, "top"})
+ con2.WaitWithDefaultTimeout()
+ Expect(con2.ExitCode()).To(Equal(0))
+
+ kube := podmanTest.Podman([]string{"generate", "kube", "con1", "con2"})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+ })
+
+ It("podman generate kube with containers in a pod should fail", func() {
+ pod1 := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:pod1", "--name", "top1", ALPINE, "top"})
+ pod1.WaitWithDefaultTimeout()
+ Expect(pod1.ExitCode()).To(Equal(0))
+
+ pod2 := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:pod2", "--name", "top2", ALPINE, "top"})
+ pod2.WaitWithDefaultTimeout()
+ Expect(pod2.ExitCode()).To(Equal(0))
+
+ kube := podmanTest.Podman([]string{"generate", "kube", "pod1", "pod2"})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).ToNot(Equal(0))
+ })
})
diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go
index c2e0f4407..97f77414e 100644
--- a/test/e2e/inspect_test.go
+++ b/test/e2e/inspect_test.go
@@ -7,6 +7,7 @@ import (
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
+ . "github.com/onsi/gomega/gexec"
"github.com/opencontainers/selinux/go-selinux"
)
@@ -428,4 +429,18 @@ var _ = Describe("Podman inspect", func() {
Expect(inspect).To(ExitWithError())
})
+ // Fixes https://github.com/containers/podman/issues/8444
+ It("podman inspect --format json .NetworkSettings.Ports", func() {
+ ctnrName := "Ctnr_" + RandomString(25)
+
+ create := podmanTest.Podman([]string{"create", "--name", ctnrName, "-p", "8080:80", ALPINE})
+ create.WaitWithDefaultTimeout()
+ Expect(create).Should(Exit(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", `--format="{{json .NetworkSettings.Ports}}"`, ctnrName})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect).Should(Exit(0))
+ Expect(inspect.OutputToString()).To(Equal(`"{"80/tcp":[{"HostIp":"","HostPort":"8080"}]}"`))
+ })
+
})
diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go
index ffc914bc2..4e8ab5ad5 100644
--- a/test/e2e/network_test.go
+++ b/test/e2e/network_test.go
@@ -119,7 +119,13 @@ var _ = Describe("Podman network", func() {
})
It("podman network list --filter invalid value", func() {
- session := podmanTest.Podman([]string{"network", "ls", "--filter", "namr=ab"})
+ net := "net" + stringid.GenerateNonCryptoID()
+ session := podmanTest.Podman([]string{"network", "create", net})
+ session.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(net)
+ Expect(session.ExitCode()).To(BeZero())
+
+ session = podmanTest.Podman([]string{"network", "ls", "--filter", "namr=ab"})
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError())
Expect(session.ErrorToString()).To(ContainSubstring(`invalid filter "namr"`))
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index 3fb00a28b..b8e14530c 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -97,6 +97,69 @@ var _ = Describe("Podman run networking", func() {
Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
})
+ It("podman run -p 80-82 -p 8080:8080", func() {
+ name := "testctr"
+ session := podmanTest.Podman([]string{"create", "-t", "-p", "80-82", "-p", "8080:8080", "--name", name, ALPINE, "/bin/sh"})
+ session.WaitWithDefaultTimeout()
+ inspectOut := podmanTest.InspectContainer(name)
+ Expect(len(inspectOut)).To(Equal(1))
+ Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(4))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Not(Equal("80")))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["81/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["81/tcp"][0].HostPort).To(Not(Equal("81")))
+ Expect(inspectOut[0].NetworkSettings.Ports["81/tcp"][0].HostIP).To(Equal(""))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["82/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["82/tcp"][0].HostPort).To(Not(Equal("82")))
+ Expect(inspectOut[0].NetworkSettings.Ports["82/tcp"][0].HostIP).To(Equal(""))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["8080/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostPort).To(Equal("8080"))
+ Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostIP).To(Equal(""))
+ })
+
+ It("podman run -p 80-81 -p 8080-8081", func() {
+ name := "testctr"
+ session := podmanTest.Podman([]string{"create", "-t", "-p", "80-81", "-p", "8080-8081", "--name", name, ALPINE, "/bin/sh"})
+ session.WaitWithDefaultTimeout()
+ inspectOut := podmanTest.InspectContainer(name)
+ Expect(len(inspectOut)).To(Equal(1))
+ Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(4))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Not(Equal("80")))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["81/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["81/tcp"][0].HostPort).To(Not(Equal("81")))
+ Expect(inspectOut[0].NetworkSettings.Ports["81/tcp"][0].HostIP).To(Equal(""))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["8080/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostPort).To(Not(Equal("8080")))
+ Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostIP).To(Equal(""))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["8081/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["8081/tcp"][0].HostPort).To(Not(Equal("8081")))
+ Expect(inspectOut[0].NetworkSettings.Ports["8081/tcp"][0].HostIP).To(Equal(""))
+ })
+
+ It("podman run -p 80 -p 8080-8082:8080-8082", func() {
+ name := "testctr"
+ session := podmanTest.Podman([]string{"create", "-t", "-p", "80", "-p", "8080-8082:8080-8082", "--name", name, ALPINE, "/bin/sh"})
+ session.WaitWithDefaultTimeout()
+ inspectOut := podmanTest.InspectContainer(name)
+ Expect(len(inspectOut)).To(Equal(1))
+ Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(4))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Not(Equal("80")))
+ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["8080/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostPort).To(Equal("8080"))
+ Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostIP).To(Equal(""))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["8081/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["8081/tcp"][0].HostPort).To(Equal("8081"))
+ Expect(inspectOut[0].NetworkSettings.Ports["8081/tcp"][0].HostIP).To(Equal(""))
+ Expect(len(inspectOut[0].NetworkSettings.Ports["8082/tcp"])).To(Equal(1))
+ Expect(inspectOut[0].NetworkSettings.Ports["8082/tcp"][0].HostPort).To(Equal("8082"))
+ Expect(inspectOut[0].NetworkSettings.Ports["8082/tcp"][0].HostIP).To(Equal(""))
+ })
+
It("podman run -p 8080:80", func() {
name := "testctr"
session := podmanTest.Podman([]string{"create", "-t", "-p", "8080:80", "--name", name, ALPINE, "/bin/sh"})
diff --git a/test/e2e/run_privileged_test.go b/test/e2e/run_privileged_test.go
index ab11128ba..760de55b6 100644
--- a/test/e2e/run_privileged_test.go
+++ b/test/e2e/run_privileged_test.go
@@ -90,6 +90,18 @@ var _ = Describe("Podman privileged container tests", func() {
containerCapMatchesHost(session.OutputToString(), host_cap.OutputToString())
})
+ It("podman cap-add CapEff with --user", func() {
+ // Get caps of current process
+ host_cap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"})
+ Expect(host_cap.ExitCode()).To(Equal(0))
+
+ session := podmanTest.Podman([]string{"run", "--user=bin", "--cap-add", "all", "busybox", "awk", "/^CapEff/ { print $2 }", "/proc/self/status"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ containerCapMatchesHost(session.OutputToString(), host_cap.OutputToString())
+ })
+
It("podman cap-drop CapEff", func() {
session := podmanTest.Podman([]string{"run", "--cap-drop", "all", "busybox", "grep", "CapEff", "/proc/self/status"})
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 58ef9a647..dbdd6a072 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -264,6 +264,21 @@ var _ = Describe("Podman run", func() {
session.WaitWithDefaultTimeout()
Expect(session.OutputToString()).To(BeEmpty())
Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr4", "--security-opt", "systempaths=unconfined", ALPINE, "sleep", "200"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"exec", "maskCtr4", "ls", "/sys/firmware"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.OutputToString()).To(Not(BeEmpty()))
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr5", "--security-opt", "systempaths=unconfined", ALPINE, "grep", "/proc", "/proc/self/mounts"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ stdoutLines := session.OutputToStringArray()
+ Expect(stdoutLines).Should(HaveLen(1))
+
})
It("podman run seccomp test", func() {
diff --git a/test/e2e/stats_test.go b/test/e2e/stats_test.go
index 5e8a7a3d0..ab117a2a0 100644
--- a/test/e2e/stats_test.go
+++ b/test/e2e/stats_test.go
@@ -128,6 +128,16 @@ var _ = Describe("Podman stats", func() {
Expect(session.ExitCode()).To(Equal(0))
})
+ It("podman stats on container with forced slirp4netns", func() {
+ // This will force the slirp4netns net mode to be tested as root
+ session := podmanTest.Podman([]string{"run", "-d", "--net", "slirp4netns", ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"stats", "--no-stream", "-a"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
+
// Regression test for #8265
It("podman stats with custom memory limits", func() {
// Run thre containers. One with a memory limit. Make sure
diff --git a/test/system/015-help.bats b/test/system/015-help.bats
index 22db8be8a..5f38c34a1 100644
--- a/test/system/015-help.bats
+++ b/test/system/015-help.bats
@@ -12,22 +12,11 @@
#
load helpers
-# run 'podman help', parse the output looking for 'Available Commands';
-# return that list.
-function podman_commands() {
- dprint "$@"
- run_podman help "$@" |\
- awk '/^Available Commands:/{ok=1;next}/^Options:/{ok=0}ok { print $1 }' |\
- grep .
- "$output"
-}
-
-
function check_help() {
local count=0
local -A found
- for cmd in $(podman_commands "$@"); do
+ for cmd in $(_podman_commands "$@"); do
# Human-readable podman command string, with multiple spaces collapsed
command_string="podman $* $cmd"
command_string=${command_string// / } # 'podman x' -> 'podman x'
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index 59da503a6..8e9a2d613 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -381,6 +381,48 @@ a${random3}z"
run_podman rmi -f build_test
}
+@test "podman build --layers test" {
+ rand_content=$(random_string 50)
+ tmpdir=$PODMAN_TMPDIR/build-test
+ run mkdir -p $tmpdir
+ containerfile=$tmpdir/Containerfile
+ cat >$containerfile <<EOF
+FROM $IMAGE
+RUN echo $rand_content
+EOF
+
+ # Build twice to make sure second time uses cache
+ run_podman build -t build_test $tmpdir
+ if [[ "$output" =~ "Using cache" ]]; then
+ is "$output" "[no instance of 'Using cache']" "no cache used"
+ fi
+
+ run_podman build -t build_test $tmpdir
+ is "$output" ".*cache" "used cache"
+
+ run_podman build -t build_test --layers=true $tmpdir
+ is "$output" ".*cache" "used cache"
+
+ run_podman build -t build_test --layers=false $tmpdir
+ if [[ "$output" =~ "Using cache" ]]; then
+ is "$output" "[no instance of 'Using cache']" "no cache used"
+ fi
+
+ BUILDAH_LAYERS=false run_podman build -t build_test $tmpdir
+ if [[ "$output" =~ "Using cache" ]]; then
+ is "$output" "[no instance of 'Using cache']" "no cache used"
+ fi
+
+ BUILDAH_LAYERS=false run_podman build -t build_test --layers=1 $tmpdir
+ is "$output" ".*cache" "used cache"
+
+ BUILDAH_LAYERS=1 run_podman build -t build_test --layers=false $tmpdir
+ if [[ "$output" =~ "Using cache" ]]; then
+ is "$output" "[no instance of 'Using cache']" "no cache used"
+ fi
+
+ run_podman rmi -a --force
+}
function teardown() {
# A timeout or other error in 'build' can leave behind stale images
diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats
index 44cc731cf..a824ebcd7 100644
--- a/test/system/500-networking.bats
+++ b/test/system/500-networking.bats
@@ -116,4 +116,68 @@ load helpers
fi
}
+@test "podman network reload" {
+ skip_if_remote "podman network reload does not have remote support"
+ skip_if_rootless "podman network reload does not work rootless"
+
+ random_1=$(random_string 30)
+ HOST_PORT=12345
+ SERVER=http://127.0.0.1:$HOST_PORT
+
+ # Create a test file with random content
+ INDEX1=$PODMAN_TMPDIR/hello.txt
+ echo $random_1 > $INDEX1
+
+ # Bind-mount this file with a different name to a container running httpd
+ run_podman run -d --name myweb -p "$HOST_PORT:80" \
+ -v $INDEX1:/var/www/index.txt \
+ -w /var/www \
+ $IMAGE /bin/busybox-extras httpd -f -p 80
+ cid=$output
+
+ run_podman inspect $cid --format "{{.NetworkSettings.IPAddress}}"
+ ip="$output"
+ run_podman inspect $cid --format "{{.NetworkSettings.MacAddress}}"
+ mac="$output"
+
+ # Verify http contents: curl from localhost
+ run curl -s $SERVER/index.txt
+ is "$output" "$random_1" "curl 127.0.0.1:/index.txt"
+
+ # flush the CNI iptables here
+ run iptables -t nat -F CNI-HOSTPORT-DNAT
+
+ # check that we cannot curl (timeout after 5 sec)
+ run timeout 5 curl -s $SERVER/index.txt
+ if [ "$status" -ne 124 ]; then
+ die "curl did not timeout, status code: $status"
+ fi
+
+ # reload the network to recreate the iptables rules
+ run_podman network reload $cid
+ is "$output" "$cid" "Output does not match container ID"
+
+ # check that we still have the same mac and ip
+ run_podman inspect $cid --format "{{.NetworkSettings.IPAddress}}"
+ is "$output" "$ip" "IP address changed after podman network reload"
+ run_podman inspect $cid --format "{{.NetworkSettings.MacAddress}}"
+ is "$output" "$mac" "MAC address changed after podman network reload"
+
+ # check that we can still curl
+ run curl -s $SERVER/index.txt
+ is "$output" "$random_1" "curl 127.0.0.1:/index.txt"
+
+ # make sure --all is working and that this
+ # cmd also works if the iptables still exists
+ run_podman network reload --all
+ is "$output" "$cid" "Output does not match container ID"
+
+ # check that we can still curl
+ run curl -s $SERVER/index.txt
+ is "$output" "$random_1" "curl 127.0.0.1:/index.txt"
+
+ # cleanup the container
+ run_podman rm -f $cid
+}
+
# vim: filetype=sh
diff --git a/test/system/600-completion.bats b/test/system/600-completion.bats
new file mode 100644
index 000000000..1e43cdc41
--- /dev/null
+++ b/test/system/600-completion.bats
@@ -0,0 +1,272 @@
+#!/usr/bin/env bats -*- bats -*-
+#
+# Test podman shell completion
+#
+# Shell completion is provided via the cobra library
+# It is implement by calling a hidden subcommand called "__complete"
+#
+
+load helpers
+
+function check_shell_completion() {
+ local count=0
+
+ # Newline character; used for confirming string output
+ local nl="
+"
+
+ for cmd in $(_podman_commands "$@"); do
+ # Human-readable podman command string, with multiple spaces collapsed
+ name="podman"
+ if is_remote; then
+ name="podman-remote"
+ fi
+ command_string="$name $* $cmd"
+ command_string=${command_string// / } # 'podman x' -> 'podman x'
+
+ run_podman "$@" $cmd --help
+ local full_help="$output"
+
+ # The line immediately after 'Usage:' gives us a 1-line synopsis
+ usage=$(echo "$full_help" | grep -A1 '^Usage:' | tail -1)
+ [ -n "$usage" ] || die "podman $cmd: no Usage message found"
+
+ # If usage ends in '[command]', recurse into subcommands
+ if expr "$usage" : '.*\[command\]$' >/dev/null; then
+ check_shell_completion "$@" $cmd
+ continue
+ fi
+
+ # Trim to command path so we only have the args
+ args="${usage/$command_string/}"
+ # Trim leading whitespaces
+ args="${args#"${args%%[![:space:]]*}"}"
+
+ # Extra args is used to match the correct argument number for the command
+ # This is important because some commands provide different suggestions based
+ # on the number of arguments.
+ extra_args=()
+
+ for arg in $args; do
+
+ match=false
+ i=0
+ while true; do
+
+ case $arg in
+
+ # If we have options than we need to check if we are getting flag completion
+ "[options]")
+ # skip this for remote it fails if a command only has the latest flag e.g podman top
+ if ! is_remote; then
+ run_completion "$@" $cmd "--"
+ # If this fails there is most likely a problem with the cobra library
+ is "${lines[0]}" "--.*" "Found flag in suggestions"
+ [ ${#lines[@]} -gt 2 ] || die "No flag suggestions"
+ _check_completion_end NoFileComp
+ fi
+ # continue the outer for args loop
+ continue 2
+ ;;
+
+ *CONTAINER*)
+ run_completion "$@" $cmd "${extra_args[@]}" ""
+ is "$output" ".*-$random_container_name${nl}" "Found expected container in suggestions"
+
+ match=true
+ # resume
+ ;;&
+
+ *POD*)
+ run_completion "$@" $cmd "${extra_args[@]}" ""
+ is "$output" ".*-$random_pod_name${nl}" "Found expected pod in suggestions"
+ _check_completion_end NoFileComp
+
+ match=true
+ # resume
+ ;;&
+
+ *IMAGE*)
+ run_completion "$@" $cmd "${extra_args[@]}" ""
+ is "$output" ".*localhost/$random_image_name:$random_image_tag${nl}" "Found expected image in suggestions"
+
+ # check that we complete the image with and without tag after at least one char is typed
+ run_completion "$@" $cmd "${extra_args[@]}" "${random_image_name:0:1}"
+ is "$output" ".*$random_image_name:$random_image_tag${nl}" "Found expected image with tag in suggestions"
+ is "$output" ".*$random_image_name${nl}" "Found expected image without tag in suggestions"
+
+ # check that we complete the image id after at least two chars are typed
+ run_completion "$@" $cmd "${extra_args[@]}" "${random_image_id:0:2}"
+ is "$output" ".*$random_image_id${nl}" "Found expected image id in suggestions"
+
+ match=true
+ # resume
+ ;;&
+
+ *NETWORK*)
+ run_completion "$@" $cmd "${extra_args[@]}" ""
+ is "$output" ".*$random_network_name${nl}" "Found network in suggestions"
+ _check_completion_end NoFileComp
+
+ match=true
+ # resume
+ ;;&
+
+ *VOLUME*)
+ run_completion "$@" $cmd "${extra_args[@]}" ""
+ is "$output" ".*$random_volume_name${nl}" "Found volume in suggestions"
+ _check_completion_end NoFileComp
+
+ match=true
+ # resume
+ ;;&
+
+ *REGISTRY*)
+ run_completion "$@" $cmd "${extra_args[@]}" ""
+ ### FIXME how can we get the configured registries?
+ _check_completion_end NoFileComp
+ ### FIXME this fails if no registries are configured
+ [[ ${#lines[@]} -gt 2 ]] || die "No registries found in suggestions"
+
+ match=true
+ # resume
+ ;;&
+
+ *PATH* | *CONTEXT* | *KUBEFILE* | *COMMAND* | *ARG...* | *URI*)
+ # default shell completion should be done for everthing which accepts a path
+ run_completion "$@" $cmd "${extra_args[@]}" ""
+
+ # cp is a special case it returns ShellCompDirectiveNoSpace
+ if [[ "$cmd" == "cp" ]]; then
+ _check_completion_end NoSpace
+ else
+ _check_completion_end Default
+ [[ ${#lines[@]} -eq 2 ]] || die "Suggestions are in the output"
+ fi
+ ;;
+
+ *)
+ if [[ "$match" == "false" ]]; then
+ dprint "UNKNOWN arg: $arg for $command_string ${extra_args[*]}"
+ fi
+ ;;
+
+ esac
+
+ # Increment the argument array
+ extra_args+=("arg")
+
+ i=$(($i + 1))
+ # If the argument ends with ...] than we accept 0...n args
+ # Loop three times to make sure we are not only completing the first arg
+ if [[ ! ${arg} =~ "..." ]] || [[ i -gt 3 ]]; then
+ break
+ fi
+
+ done
+
+ done
+
+ # If the command takes no more parameters make sure we are getting no completion
+ if [[ ! ${args##* } =~ "..." ]]; then
+ run_completion "$@" $cmd "${extra_args[@]}" ""
+ _check_completion_end NoFileComp
+ if [ ${#lines[@]} -gt 2 ]; then
+ # checking for line count is not enough since we may inlcude additional debug output
+ # lines starting with [Debug] are allowed
+ i=0
+ length=$(( ${#lines[@]} - 2 ))
+ while [[ i -lt length ]]; do
+ [[ "${lines[$i]:0:7}" == "[Debug]" ]] || die "Suggestions are in the output"
+ i=$(( i + 1 ))
+ done
+ fi
+ fi
+
+ done
+
+}
+
+# run the completion cmd
+function run_completion() {
+ PODMAN="$PODMAN_COMPLETION" run_podman "$@"
+}
+
+# check for the given ShellCompDirective (always last line)
+function _check_completion_end() {
+ is "${lines[-1]}" "Completion ended with directive: ShellCompDirective$1" "Completion has wrong ShellCompDirective set"
+}
+
+
+@test "podman shell completion test" {
+
+ random_container_name=$(random_string 30)
+ random_pod_name=$(random_string 30)
+ random_image_name=$(random_string 30)
+ random_image_name=${random_image_name,,} # name must be lowercase
+ random_image_tag=$(random_string 5)
+ random_network_name=$(random_string 30)
+ random_volume_name=$(random_string 30)
+
+ # create a container for each state since some commands are only suggesting running container for example
+ run_podman create --name created-$random_container_name $IMAGE
+ run_podman run --name running-$random_container_name -d $IMAGE top
+ run_podman run --name pause-$random_container_name -d $IMAGE top
+ run_podman pause pause-$random_container_name
+ run_podman run --name exited-$random_container_name -d $IMAGE echo exited
+
+ # create pods for each state
+ run_podman pod create --name created-$random_pod_name
+ run_podman pod create --name running-$random_pod_name
+ run_podman run -d --name running-$random_pod_name-con --pod running-$random_pod_name $IMAGE top
+ run_podman pod create --name degraded-$random_pod_name
+ run_podman run -d --name degraded-$random_pod_name-con --pod degraded-$random_pod_name $IMAGE echo degraded
+ run_podman pod create --name exited-$random_pod_name
+ run_podman run -d --name exited-$random_pod_name-con --pod exited-$random_pod_name $IMAGE echo exited
+ run_podman pod stop exited-$random_pod_name
+
+ # create image name (just tag with new names no need to pull)
+ run_podman image tag $IMAGE $random_image_name:$random_image_tag
+ run_podman image list --format '{{.ID}}' --filter reference=$random_image_name
+ random_image_id="${lines[0]}"
+
+ # create network
+ run_podman network create $random_network_name
+
+ # create volume
+ run_podman volume create $random_volume_name
+
+
+ # $PODMAN may be a space-separated string, e.g. if we include a --url.
+ local -a podman_as_array=($PODMAN)
+ # __completeNoDesc must be the first arg if we running the completion cmd
+ PODMAN_COMPLETION="${podman_as_array[0]} __completeNoDesc ${podman_as_array[@]:1}"
+
+ # Called with no args -- start with 'podman --help'. check_shell_completion() will
+ # recurse for any subcommands.
+ check_shell_completion
+
+ # cleanup
+ run_podman volume rm $random_volume_name
+
+ run_podman network rm $random_network_name
+
+ run_podman image untag $IMAGE $random_image_name:$random_image_tag
+
+ for state in created running degraded exited; do
+ run_podman pod rm --force $state-$random_pod_name
+ done
+
+ for state in created running pause exited; do
+ run_podman rm --force $state-$random_container_name
+ done
+
+ # Clean up the pod pause image
+ run_podman image list --format '{{.ID}} {{.Repository}}'
+ while read id name; do
+ if [[ "$name" =~ /pause ]]; then
+ run_podman rmi $id
+ fi
+ done <<<"$output"
+
+}
diff --git a/test/system/helpers.bash b/test/system/helpers.bash
index 2cced10c2..6a7c6cc42 100644
--- a/test/system/helpers.bash
+++ b/test/system/helpers.bash
@@ -521,5 +521,15 @@ function remove_same_dev_warning() {
output=$(printf '%s\n' "${lines[@]}")
}
+# run 'podman help', parse the output looking for 'Available Commands';
+# return that list.
+function _podman_commands() {
+ dprint "$@"
+ run_podman help "$@" |
+ awk '/^Available Commands:/{ok=1;next}/^Options:/{ok=0}ok { print $1 }' |
+ grep .
+ "$output"
+}
+
# END miscellaneous tools
###############################################################################
diff --git a/test/utils/utils.go b/test/utils/utils.go
index d08939678..027e96427 100644
--- a/test/utils/utils.go
+++ b/test/utils/utils.go
@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
+ "math/rand"
"os"
"os/exec"
"runtime"
@@ -465,3 +466,16 @@ func Containerized() bool {
}
return false
}
+
+var randomLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+
+// RandomString returns a string of given length composed of random characters
+func RandomString(n int) string {
+ rand.Seed(GinkgoRandomSeed())
+
+ b := make([]rune, n)
+ for i := range b {
+ b[i] = randomLetters[rand.Intn(len(randomLetters))]
+ }
+ return string(b)
+}