github.com/containers/podman/v5@v5.1.0-rc1/test/e2e/systemd_test.go (about) 1 package integration 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 9 . "github.com/containers/podman/v5/test/utils" 10 . "github.com/onsi/ginkgo/v2" 11 . "github.com/onsi/gomega" 12 . "github.com/onsi/gomega/gexec" 13 ) 14 15 var _ = Describe("Podman systemd", func() { 16 17 var systemdUnitFile string 18 19 BeforeEach(func() { 20 podmanCmd := fmt.Sprintf("%s %s", podmanTest.PodmanBinary, strings.Join(podmanTest.MakeOptions(nil, false, false), " ")) 21 systemdUnitFile = fmt.Sprintf(`[Unit] 22 Description=redis container 23 [Service] 24 Restart=always 25 ExecStart=%s start -a redis 26 ExecStop=%s stop -t 10 redis 27 KillMode=process 28 [Install] 29 WantedBy=default.target 30 `, podmanCmd, podmanCmd) 31 }) 32 33 It("podman start container by systemd", func() { 34 SkipIfRemote("cannot create unit file on remote host") 35 SkipIfContainerized("test does not have systemd as pid 1") 36 37 dashWhat := "--system" 38 unitDir := "/run/systemd/system" 39 if isRootless() { 40 dashWhat = "--user" 41 unitDir = fmt.Sprintf("%s/systemd/user", os.Getenv("XDG_RUNTIME_DIR")) 42 } 43 err := os.MkdirAll(unitDir, 0700) 44 Expect(err).ToNot(HaveOccurred()) 45 46 serviceName := "redis-" + RandomString(10) 47 sysFilePath := filepath.Join(unitDir, serviceName+".service") 48 sysFile := os.WriteFile(sysFilePath, []byte(systemdUnitFile), 0644) 49 Expect(sysFile).ToNot(HaveOccurred()) 50 defer func() { 51 stop := SystemExec("systemctl", []string{dashWhat, "stop", serviceName}) 52 os.Remove(sysFilePath) 53 SystemExec("systemctl", []string{dashWhat, "daemon-reload"}) 54 Expect(stop).Should(ExitCleanly()) 55 }() 56 57 create := podmanTest.Podman([]string{"create", "--name", "redis", REDIS_IMAGE}) 58 create.WaitWithDefaultTimeout() 59 Expect(create).Should(ExitCleanly()) 60 61 enable := SystemExec("systemctl", []string{dashWhat, "daemon-reload"}) 62 Expect(enable).Should(ExitCleanly()) 63 64 start := SystemExec("systemctl", []string{dashWhat, "start", serviceName}) 65 Expect(start).Should(ExitCleanly()) 66 67 checkAvailableJournald() 68 if !journald.journaldSkip { 69 // "-q" needed on fc40+ because something creates /run/log/journal/XXX 2750 70 logs := SystemExec("journalctl", []string{dashWhat, "-q", "-n", "20", "-u", serviceName}) 71 Expect(logs).Should(ExitCleanly()) 72 } 73 74 status := SystemExec("systemctl", []string{dashWhat, "status", serviceName}) 75 Expect(status.OutputToString()).To(ContainSubstring("active (running)")) 76 }) 77 78 It("podman run container with systemd PID1", func() { 79 ctrName := "testSystemd" 80 run := podmanTest.Podman([]string{"run", "--name", ctrName, "-t", "-i", "-d", SYSTEMD_IMAGE, "/sbin/init"}) 81 run.WaitWithDefaultTimeout() 82 Expect(run).Should(Exit(0)) 83 84 logs := podmanTest.Podman([]string{"logs", ctrName}) 85 logs.WaitWithDefaultTimeout() 86 Expect(logs).Should(ExitCleanly()) 87 88 // Give container 10 seconds to start 89 started := podmanTest.WaitContainerReady(ctrName, "Reached target multi-user.target - Multi-User System.", 30, 1) 90 Expect(started).To(BeTrue(), "Reached multi-user.target") 91 92 systemctl := podmanTest.Podman([]string{"exec", ctrName, "systemctl", "status", "--no-pager"}) 93 systemctl.WaitWithDefaultTimeout() 94 Expect(systemctl).Should(ExitCleanly()) 95 Expect(systemctl.OutputToString()).To(ContainSubstring("State:")) 96 97 result := podmanTest.Podman([]string{"inspect", ctrName}) 98 result.WaitWithDefaultTimeout() 99 Expect(result).Should(ExitCleanly()) 100 conData := result.InspectContainerToJSON() 101 Expect(conData).To(HaveLen(1)) 102 Expect(conData[0].Config).To(HaveField("SystemdMode", true)) 103 104 // stats not supported w/ CGv1 rootless or containerized 105 if isCgroupsV1() && (isRootless() || isContainerized()) { 106 return 107 } 108 stats := podmanTest.Podman([]string{"stats", "--no-stream", ctrName}) 109 stats.WaitWithDefaultTimeout() 110 Expect(stats).Should(ExitCleanly()) 111 112 cgroupPath := podmanTest.Podman([]string{"inspect", "--format='{{.State.CgroupPath}}'", ctrName}) 113 cgroupPath.WaitWithDefaultTimeout() 114 Expect(cgroupPath).Should(ExitCleanly()) 115 Expect(cgroupPath.OutputToString()).To(Not(ContainSubstring("init.scope"))) 116 }) 117 118 It("podman create container with systemd entrypoint triggers systemd mode", func() { 119 ctrName := "testCtr" 120 run := podmanTest.Podman([]string{"create", "--name", ctrName, "--entrypoint", "/sbin/init", SYSTEMD_IMAGE}) 121 run.WaitWithDefaultTimeout() 122 Expect(run).Should(ExitCleanly()) 123 124 result := podmanTest.Podman([]string{"inspect", ctrName}) 125 result.WaitWithDefaultTimeout() 126 Expect(result).Should(ExitCleanly()) 127 conData := result.InspectContainerToJSON() 128 Expect(conData).To(HaveLen(1)) 129 Expect(conData[0].Config).To(HaveField("SystemdMode", true)) 130 }) 131 132 It("podman systemd in command triggers systemd mode", func() { 133 containerfile := fmt.Sprintf(`FROM %s 134 RUN mkdir -p /usr/lib/systemd/; touch /usr/lib/systemd/systemd 135 CMD /usr/lib/systemd/systemd`, ALPINE) 136 137 containerfilePath := filepath.Join(podmanTest.TempDir, "Containerfile") 138 err := os.WriteFile(containerfilePath, []byte(containerfile), 0755) 139 Expect(err).ToNot(HaveOccurred()) 140 session := podmanTest.Podman([]string{"build", "-t", "systemd", "--file", containerfilePath, podmanTest.TempDir}) 141 session.WaitWithDefaultTimeout() 142 Expect(session).Should(ExitCleanly()) 143 144 ctrName := "testCtr" 145 run := podmanTest.Podman([]string{"create", "--name", ctrName, "systemd"}) 146 run.WaitWithDefaultTimeout() 147 Expect(run).Should(ExitCleanly()) 148 149 result := podmanTest.Podman([]string{"inspect", ctrName}) 150 result.WaitWithDefaultTimeout() 151 Expect(result).Should(ExitCleanly()) 152 conData := result.InspectContainerToJSON() 153 Expect(conData).To(HaveLen(1)) 154 Expect(conData[0].Config).To(HaveField("SystemdMode", true)) 155 }) 156 157 It("podman create container with --uidmap and conmon PidFile accessible", func() { 158 ctrName := "testCtrUidMap" 159 run := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:1:1000", "--name", ctrName, ALPINE, "top"}) 160 run.WaitWithDefaultTimeout() 161 Expect(run).Should(ExitCleanly()) 162 163 session := podmanTest.Podman([]string{"inspect", "--format", "{{.ConmonPidFile}}", ctrName}) 164 session.WaitWithDefaultTimeout() 165 Expect(session).Should(ExitCleanly()) 166 167 pidFile := strings.TrimSuffix(session.OutputToString(), "\n") 168 _, err := os.ReadFile(pidFile) 169 Expect(err).ToNot(HaveOccurred()) 170 }) 171 172 It("podman create container with systemd=always triggers systemd mode", func() { 173 ctrName := "testCtr" 174 run := podmanTest.Podman([]string{"create", "--name", ctrName, "--systemd", "always", ALPINE}) 175 run.WaitWithDefaultTimeout() 176 Expect(run).Should(ExitCleanly()) 177 178 result := podmanTest.Podman([]string{"inspect", ctrName}) 179 result.WaitWithDefaultTimeout() 180 Expect(result).Should(ExitCleanly()) 181 conData := result.InspectContainerToJSON() 182 Expect(conData).To(HaveLen(1)) 183 Expect(conData[0].Config).To(HaveField("SystemdMode", true)) 184 }) 185 186 It("podman run --systemd container should NOT mount /run noexec", func() { 187 session := podmanTest.Podman([]string{"run", "--systemd", "always", ALPINE, "sh", "-c", "mount | grep \"/run \""}) 188 session.WaitWithDefaultTimeout() 189 Expect(session).Should(ExitCleanly()) 190 191 Expect(session.OutputToString()).To(Not(ContainSubstring("noexec"))) 192 }) 193 194 It("podman run --systemd arg is case insensitive", func() { 195 session := podmanTest.Podman([]string{"run", "--rm", "--systemd", "Always", ALPINE, "echo", "test"}) 196 session.WaitWithDefaultTimeout() 197 Expect(session).Should(ExitCleanly()) 198 Expect(session.OutputToString()).Should(Equal("test")) 199 200 session = podmanTest.Podman([]string{"run", "--rm", "--systemd", "True", ALPINE, "echo", "test"}) 201 session.WaitWithDefaultTimeout() 202 Expect(session).Should(ExitCleanly()) 203 Expect(session.OutputToString()).Should(Equal("test")) 204 205 session = podmanTest.Podman([]string{"run", "--rm", "--systemd", "False", ALPINE, "echo", "test"}) 206 session.WaitWithDefaultTimeout() 207 Expect(session).Should(ExitCleanly()) 208 Expect(session.OutputToString()).Should(Equal("test")) 209 }) 210 })