github.com/containers/podman/v4@v4.9.4/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/v4/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 logs := SystemExec("journalctl", []string{dashWhat, "-n", "20", "-u", serviceName}) 68 Expect(logs).Should(ExitCleanly()) 69 70 status := SystemExec("systemctl", []string{dashWhat, "status", serviceName}) 71 Expect(status.OutputToString()).To(ContainSubstring("active (running)")) 72 }) 73 74 It("podman run container with systemd PID1", func() { 75 ctrName := "testSystemd" 76 run := podmanTest.Podman([]string{"run", "--name", ctrName, "-t", "-i", "-d", SYSTEMD_IMAGE, "/sbin/init"}) 77 run.WaitWithDefaultTimeout() 78 Expect(run).Should(Exit(0)) 79 80 logs := podmanTest.Podman([]string{"logs", ctrName}) 81 logs.WaitWithDefaultTimeout() 82 Expect(logs).Should(ExitCleanly()) 83 84 // Give container 10 seconds to start 85 started := podmanTest.WaitContainerReady(ctrName, "Reached target multi-user.target - Multi-User System.", 30, 1) 86 Expect(started).To(BeTrue(), "Reached multi-user.target") 87 88 systemctl := podmanTest.Podman([]string{"exec", ctrName, "systemctl", "status", "--no-pager"}) 89 systemctl.WaitWithDefaultTimeout() 90 Expect(systemctl).Should(ExitCleanly()) 91 Expect(systemctl.OutputToString()).To(ContainSubstring("State:")) 92 93 result := podmanTest.Podman([]string{"inspect", ctrName}) 94 result.WaitWithDefaultTimeout() 95 Expect(result).Should(ExitCleanly()) 96 conData := result.InspectContainerToJSON() 97 Expect(conData).To(HaveLen(1)) 98 Expect(conData[0].Config).To(HaveField("SystemdMode", true)) 99 100 // stats not supported w/ CGv1 rootless or containerized 101 if isCgroupsV1() && (isRootless() || isContainerized()) { 102 return 103 } 104 stats := podmanTest.Podman([]string{"stats", "--no-stream", ctrName}) 105 stats.WaitWithDefaultTimeout() 106 Expect(stats).Should(ExitCleanly()) 107 108 cgroupPath := podmanTest.Podman([]string{"inspect", "--format='{{.State.CgroupPath}}'", ctrName}) 109 cgroupPath.WaitWithDefaultTimeout() 110 Expect(cgroupPath).Should(ExitCleanly()) 111 Expect(cgroupPath.OutputToString()).To(Not(ContainSubstring("init.scope"))) 112 }) 113 114 It("podman create container with systemd entrypoint triggers systemd mode", func() { 115 ctrName := "testCtr" 116 run := podmanTest.Podman([]string{"create", "--name", ctrName, "--entrypoint", "/sbin/init", SYSTEMD_IMAGE}) 117 run.WaitWithDefaultTimeout() 118 Expect(run).Should(ExitCleanly()) 119 120 result := podmanTest.Podman([]string{"inspect", ctrName}) 121 result.WaitWithDefaultTimeout() 122 Expect(result).Should(ExitCleanly()) 123 conData := result.InspectContainerToJSON() 124 Expect(conData).To(HaveLen(1)) 125 Expect(conData[0].Config).To(HaveField("SystemdMode", true)) 126 }) 127 128 It("podman systemd in command triggers systemd mode", func() { 129 containerfile := fmt.Sprintf(`FROM %s 130 RUN mkdir -p /usr/lib/systemd/; touch /usr/lib/systemd/systemd 131 CMD /usr/lib/systemd/systemd`, ALPINE) 132 133 containerfilePath := filepath.Join(podmanTest.TempDir, "Containerfile") 134 err := os.WriteFile(containerfilePath, []byte(containerfile), 0755) 135 Expect(err).ToNot(HaveOccurred()) 136 session := podmanTest.Podman([]string{"build", "-t", "systemd", "--file", containerfilePath, podmanTest.TempDir}) 137 session.WaitWithDefaultTimeout() 138 Expect(session).Should(ExitCleanly()) 139 140 ctrName := "testCtr" 141 run := podmanTest.Podman([]string{"create", "--name", ctrName, "systemd"}) 142 run.WaitWithDefaultTimeout() 143 Expect(run).Should(ExitCleanly()) 144 145 result := podmanTest.Podman([]string{"inspect", ctrName}) 146 result.WaitWithDefaultTimeout() 147 Expect(result).Should(ExitCleanly()) 148 conData := result.InspectContainerToJSON() 149 Expect(conData).To(HaveLen(1)) 150 Expect(conData[0].Config).To(HaveField("SystemdMode", true)) 151 }) 152 153 It("podman create container with --uidmap and conmon PidFile accessible", func() { 154 ctrName := "testCtrUidMap" 155 run := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:1:1000", "--name", ctrName, ALPINE, "top"}) 156 run.WaitWithDefaultTimeout() 157 Expect(run).Should(ExitCleanly()) 158 159 session := podmanTest.Podman([]string{"inspect", "--format", "{{.ConmonPidFile}}", ctrName}) 160 session.WaitWithDefaultTimeout() 161 Expect(session).Should(ExitCleanly()) 162 163 pidFile := strings.TrimSuffix(session.OutputToString(), "\n") 164 _, err := os.ReadFile(pidFile) 165 Expect(err).ToNot(HaveOccurred()) 166 }) 167 168 It("podman create container with systemd=always triggers systemd mode", func() { 169 ctrName := "testCtr" 170 run := podmanTest.Podman([]string{"create", "--name", ctrName, "--systemd", "always", ALPINE}) 171 run.WaitWithDefaultTimeout() 172 Expect(run).Should(ExitCleanly()) 173 174 result := podmanTest.Podman([]string{"inspect", ctrName}) 175 result.WaitWithDefaultTimeout() 176 Expect(result).Should(ExitCleanly()) 177 conData := result.InspectContainerToJSON() 178 Expect(conData).To(HaveLen(1)) 179 Expect(conData[0].Config).To(HaveField("SystemdMode", true)) 180 }) 181 182 It("podman run --systemd container should NOT mount /run noexec", func() { 183 session := podmanTest.Podman([]string{"run", "--systemd", "always", ALPINE, "sh", "-c", "mount | grep \"/run \""}) 184 session.WaitWithDefaultTimeout() 185 Expect(session).Should(ExitCleanly()) 186 187 Expect(session.OutputToString()).To(Not(ContainSubstring("noexec"))) 188 }) 189 190 It("podman run --systemd arg is case insensitive", func() { 191 session := podmanTest.Podman([]string{"run", "--rm", "--systemd", "Always", ALPINE, "echo", "test"}) 192 session.WaitWithDefaultTimeout() 193 Expect(session).Should(ExitCleanly()) 194 Expect(session.OutputToString()).Should(Equal("test")) 195 196 session = podmanTest.Podman([]string{"run", "--rm", "--systemd", "True", ALPINE, "echo", "test"}) 197 session.WaitWithDefaultTimeout() 198 Expect(session).Should(ExitCleanly()) 199 Expect(session.OutputToString()).Should(Equal("test")) 200 201 session = podmanTest.Podman([]string{"run", "--rm", "--systemd", "False", ALPINE, "echo", "test"}) 202 session.WaitWithDefaultTimeout() 203 Expect(session).Should(ExitCleanly()) 204 Expect(session.OutputToString()).Should(Equal("test")) 205 }) 206 })