From 194723f314f505de3d39afb7fd769bc02293fd88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=8D=92=E9=87=8E=E7=84=A1=E7=87=88?= <ttys3@outlook.com>
Date: Tue, 10 Mar 2020 12:28:03 +0800
Subject: [PATCH] force run container detached if container CreateCommand
 missing the detach param
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

the podman generated systemd service file has `Type=forking` service,
so the command after `ExecStart=` should not run in front.
if someone created a container and has the detach(`-d`) param missing
like this
```
podman create --name ngxdemo -P nginxdemos/hello
```
and generate the file with `--new` param:
```
podman generate systemd --name --new ngxdemo
```
because `podman run xxx` has no `-d` param,
so the container is not run in background and nerver exit.
and systemd will fail to start the service:
```
sudo systemctl start container-ngxdemo.service
Job for container-ngxdemo.service failed because a timeout was exceeded.
See "systemctl status container-ngxdemo.service" and "journalctl -xe" for details.
```

Signed-off-by: 荒野無燈 <ttys3@outlook.com>
---
 .../markdown/podman-generate-systemd.1.md     |  1 +
 pkg/systemd/generate/systemdgen.go            | 20 ++++
 pkg/systemd/generate/systemdgen_test.go       | 91 ++++++++++++++++++-
 test/e2e/generate_systemd_test.go             | 28 ++++++
 4 files changed, 139 insertions(+), 1 deletion(-)

diff --git a/docs/source/markdown/podman-generate-systemd.1.md b/docs/source/markdown/podman-generate-systemd.1.md
index 4d3f9ba48d..3199232fa0 100644
--- a/docs/source/markdown/podman-generate-systemd.1.md
+++ b/docs/source/markdown/podman-generate-systemd.1.md
@@ -25,6 +25,7 @@ Use the name of the container for the start, stop, and description in the unit f
 **--new**
 
 Create a new container via podman-run instead of starting an existing one.  This option relies on container configuration files, which may not map directly to podman CLI flags; please review the generated output carefully before placing in production.
+Since we use systemd `Type=forking` service, using this option will force the container run with the detached param `-d`
 
 **--timeout**, **-t**=*value*
 
diff --git a/pkg/systemd/generate/systemdgen.go b/pkg/systemd/generate/systemdgen.go
index 00ddc63f39..f2798819f2 100644
--- a/pkg/systemd/generate/systemdgen.go
+++ b/pkg/systemd/generate/systemdgen.go
@@ -164,6 +164,26 @@ func CreateContainerSystemdUnit(info *ContainerInfo, opts Options) (string, erro
 			"--cidfile", "%t/%n-cid",
 			"--cgroups=no-conmon",
 		}
+
+		// Enforce detaching
+		//
+		// since we use systemd `Type=forking` service
+		// @see https://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=
+		// when we generated systemd service file with the --new param,
+		// `ExecStart` will have `/usr/bin/podman run ...`
+		// if `info.CreateCommand` has no `-d` or `--detach` param,
+		// podman will run the container in default attached mode,
+		// as a result, `systemd start` will wait the `podman run` command exit until failed with timeout error.
+		hasDetachParam := false
+		for _, p := range info.CreateCommand[index:] {
+			if p == "--detach" || p == "-d" {
+				hasDetachParam = true
+			}
+		}
+		if !hasDetachParam {
+			command = append(command, "-d")
+		}
+
 		command = append(command, info.CreateCommand[index:]...)
 		info.RunCommand = strings.Join(command, " ")
 		info.New = true
diff --git a/pkg/systemd/generate/systemdgen_test.go b/pkg/systemd/generate/systemdgen_test.go
index 145296ea9c..3c20dd8b9a 100644
--- a/pkg/systemd/generate/systemdgen_test.go
+++ b/pkg/systemd/generate/systemdgen_test.go
@@ -132,13 +132,57 @@ After=network-online.target
 [Service]
 Restart=always
 ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid
-ExecStart=/usr/bin/podman run --conmon-pidfile %t/%n-pid --cidfile %t/%n-cid --cgroups=no-conmon --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
+ExecStart=/usr/bin/podman run --conmon-pidfile %t/%n-pid --cidfile %t/%n-cid --cgroups=no-conmon -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
 ExecStop=/usr/bin/podman stop --ignore --cidfile %t/%n-cid -t 42
 ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/%n-cid
 PIDFile=%t/%n-pid
 KillMode=none
 Type=forking
 
+[Install]
+WantedBy=multi-user.target`
+
+	goodNameNewDetach := `# jadda-jadda.service
+# autogenerated by Podman CI
+
+[Unit]
+Description=Podman jadda-jadda.service
+Documentation=man:podman-generate-systemd(1)
+Wants=network.target
+After=network-online.target
+
+[Service]
+Restart=always
+ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid
+ExecStart=/usr/bin/podman run --conmon-pidfile %t/%n-pid --cidfile %t/%n-cid --cgroups=no-conmon --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN
+ExecStop=/usr/bin/podman stop --ignore --cidfile %t/%n-cid -t 42
+ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/%n-cid
+PIDFile=%t/%n-pid
+KillMode=none
+Type=forking
+
+[Install]
+WantedBy=multi-user.target`
+
+	goodIdNew := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
+# autogenerated by Podman CI
+
+[Unit]
+Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service
+Documentation=man:podman-generate-systemd(1)
+Wants=network.target
+After=network-online.target
+
+[Service]
+Restart=always
+ExecStartPre=/usr/bin/rm -f %t/%n-pid %t/%n-cid
+ExecStart=/usr/bin/podman run --conmon-pidfile %t/%n-pid --cidfile %t/%n-cid --cgroups=no-conmon -d awesome-image:latest
+ExecStop=/usr/bin/podman stop --ignore --cidfile %t/%n-cid -t 10
+ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/%n-cid
+PIDFile=%t/%n-pid
+KillMode=none
+Type=forking
+
 [Install]
 WantedBy=multi-user.target`
 
@@ -230,6 +274,51 @@ WantedBy=multi-user.target`
 			goodNameNew,
 			false,
 		},
+		{"good with explicit short detach param",
+			ContainerInfo{
+				Executable:    "/usr/bin/podman",
+				ServiceName:   "jadda-jadda",
+				ContainerName: "jadda-jadda",
+				RestartPolicy: "always",
+				PIDFile:       "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
+				StopTimeout:   42,
+				PodmanVersion: "CI",
+				New:           true,
+				CreateCommand: []string{"I'll get stripped", "container", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"},
+			},
+			goodNameNew,
+			false,
+		},
+		{"good with explicit full detach param",
+			ContainerInfo{
+				Executable:    "/usr/bin/podman",
+				ServiceName:   "jadda-jadda",
+				ContainerName: "jadda-jadda",
+				RestartPolicy: "always",
+				PIDFile:       "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
+				StopTimeout:   42,
+				PodmanVersion: "CI",
+				New:           true,
+				CreateCommand: []string{"I'll get stripped", "container", "run", "--detach", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"},
+			},
+			goodNameNewDetach,
+			false,
+		},
+		{"good with id and no param",
+			ContainerInfo{
+				Executable:    "/usr/bin/podman",
+				ServiceName:   "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
+				ContainerName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401",
+				RestartPolicy: "always",
+				PIDFile:       "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid",
+				StopTimeout:   10,
+				PodmanVersion: "CI",
+				New:           true,
+				CreateCommand: []string{"I'll get stripped", "container", "run", "awesome-image:latest"},
+			},
+			goodIdNew,
+			false,
+		},
 	}
 	for _, tt := range tests {
 		test := tt
diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go
index 31131a68be..e5ab0b8548 100644
--- a/test/e2e/generate_systemd_test.go
+++ b/test/e2e/generate_systemd_test.go
@@ -195,6 +195,34 @@ var _ = Describe("Podman generate systemd", func() {
 		Expect(found).To(BeTrue())
 	})
 
+	It("podman generate systemd --new without explicit detaching param", func() {
+		n := podmanTest.Podman([]string{"create", "--name", "foo", "alpine", "top"})
+		n.WaitWithDefaultTimeout()
+		Expect(n.ExitCode()).To(Equal(0))
+
+		session := podmanTest.Podman([]string{"generate", "systemd", "--timeout", "42", "--name", "--new", "foo"})
+		session.WaitWithDefaultTimeout()
+		Expect(session.ExitCode()).To(Equal(0))
+
+		// Grepping the output (in addition to unit tests)
+		found, _ := session.GrepString("--cgroups=no-conmon -d")
+		Expect(found).To(BeTrue())
+	})
+
+	It("podman generate systemd --new with explicit detaching param in middle", func() {
+		n := podmanTest.Podman([]string{"create", "--name", "foo", "-d", "alpine", "top"})
+		n.WaitWithDefaultTimeout()
+		Expect(n.ExitCode()).To(Equal(0))
+
+		session := podmanTest.Podman([]string{"generate", "systemd", "--timeout", "42", "--name", "--new", "foo"})
+		session.WaitWithDefaultTimeout()
+		Expect(session.ExitCode()).To(Equal(0))
+
+		// Grepping the output (in addition to unit tests)
+		found, _ := session.GrepString("--name foo -d alpine top")
+		Expect(found).To(BeTrue())
+	})
+
 	It("podman generate systemd --new pod", func() {
 		n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"})
 		n.WaitWithDefaultTimeout()