package integration import ( "errors" "io/fs" "net" "net/url" "os" "os/exec" "path/filepath" "strconv" "syscall" "time" testUtils "github.com/containers/podman/v5/test/utils" podmanUtils "github.com/containers/podman/v5/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" ) var _ = Describe("Systemd activate", func() { var activate string BeforeEach(func() { SkipIfRemote("Testing stopped service requires both podman and podman-remote binaries") activate, err = exec.LookPath("systemd-socket-activate") if err != nil { activate = "/usr/bin/systemd-socket-activate" } stat, err := os.Stat(activate) switch { case errors.Is(err, fs.ErrNotExist): Skip(activate + " required for systemd activation tests") case stat.Mode()&0111 == 0: Skip("Unable to execute " + activate) case err != nil: Skip(err.Error()) } }) It("stop podman.service", func() { // systemd-socket-activate does not support DNS lookups host := "127.0.0.1" port, err := podmanUtils.GetRandomPort() Expect(err).ToNot(HaveOccurred()) addr := net.JoinHostPort(host, strconv.Itoa(port)) podmanOptions := podmanTest.makeOptions(nil, false, false) systemdArgs := []string{ "-E", "http_proxy", "-E", "https_proxy", "-E", "no_proxy", "-E", "HTTP_PROXY", "-E", "HTTPS_PROXY", "-E", "NO_PROXY", "-E", "XDG_RUNTIME_DIR", "-E", "CI_DESIRED_DATABASE", "--listen", addr, podmanTest.PodmanBinary} systemdArgs = append(systemdArgs, podmanOptions...) systemdArgs = append(systemdArgs, "system", "service", "--time=0") activateSession := testUtils.StartSystemExec(activate, systemdArgs) Expect(activateSession.Exited).ShouldNot(Receive(), "Failed to start podman service") WaitForService(url.URL{Scheme: "tcp", Host: addr}) defer activateSession.Signal(syscall.SIGTERM) // Create custom functions for running podman and // podman-remote. This test is a rare exception where both // binaries need to be run in parallel. Usually, the remote // and non-remote details are hidden. Yet we use the // `podmanOptions` above to make sure all settings (root, // runroot, events, tmpdir, etc.) are used as in other e2e // tests. podmanRemote := func(args ...string) *testUtils.PodmanSession { args = append([]string{"--url", "tcp://" + addr}, args...) return testUtils.SystemExec(podmanTest.RemotePodmanBinary, args) } podman := func(args ...string) *testUtils.PodmanSession { args = append(podmanOptions, args...) return testUtils.SystemExec(podmanTest.PodmanBinary, args) } containerName := "top_" + testUtils.RandomString(8) apiSession := podmanRemote( "create", "--tty", "--name", containerName, "--entrypoint", "top", ALPINE, ) Expect(apiSession).Should(testUtils.ExitCleanly()) defer podman("rm", "-f", containerName) apiSession = podmanRemote("start", containerName) Expect(apiSession).Should(testUtils.ExitCleanly()) apiSession = podmanRemote("inspect", "--format={{.State.Running}}", containerName) Expect(apiSession).Should(testUtils.ExitCleanly()) Expect(apiSession.OutputToString()).To(Equal("true")) // Emulate 'systemd stop podman.service' activateSession.Signal(syscall.SIGTERM) time.Sleep(100 * time.Millisecond) Eventually(activateSession).Should(Exit(0)) abiSession := podman("inspect", "--format={{.State.Running}}", containerName) Expect(abiSession).To(testUtils.ExitCleanly()) Expect(abiSession.OutputToString()).To(Equal("true")) }) It("invalid systemd file descriptor", func() { host := "127.0.0.1" port, err := podmanUtils.GetRandomPort() Expect(err).ToNot(HaveOccurred()) addr := net.JoinHostPort(host, strconv.Itoa(port)) // start systemd activation with datagram socket activateSession := testUtils.StartSystemExec(activate, []string{ "--datagram", "--listen", addr, "-E", "CI_DESIRED_DATABASE", podmanTest.PodmanBinary, "--root=" + filepath.Join(tempdir, "server_root"), "system", "service", "--time=0", }) Expect(activateSession.Exited).ShouldNot(Receive(), "Failed to start podman service") // we have to wait for systemd-socket-activate to become ready time.Sleep(1 * time.Second) // now dial the socket to start podman conn, err := net.Dial("udp", addr) Expect(err).ToNot(HaveOccurred()) defer conn.Close() _, err = conn.Write([]byte("test")) Expect(err).ToNot(HaveOccurred()) // wait for podman to exit activateSession.Wait(10) Expect(activateSession).To(Exit(125)) Expect(activateSession.ErrorToString()).To(ContainSubstring("Error: unexpected fd received from systemd: cannot listen on it")) }) })