diff options
-rw-r--r-- | cmd/podman/common/create_opts.go | 9 | ||||
-rw-r--r-- | cmd/podman/common/specgen.go | 42 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.c | 25 | ||||
-rw-r--r-- | test/apiv2/python/rest_api/test_v2_0_0_container.py | 10 | ||||
-rw-r--r-- | test/e2e/healthcheck_run_test.go | 10 | ||||
-rw-r--r-- | test/e2e/run_test.go | 4 | ||||
-rw-r--r-- | transfer.md | 4 |
7 files changed, 74 insertions, 30 deletions
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 66778f519..42e0efe5d 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -517,7 +517,14 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c cliOpts.OOMKillDisable = *cc.HostConfig.OomKillDisable } if cc.Config.Healthcheck != nil { - cliOpts.HealthCmd = strings.Join(cc.Config.Healthcheck.Test, " ") + finCmd := "" + for _, str := range cc.Config.Healthcheck.Test { + finCmd = finCmd + str + " " + } + if len(finCmd) > 1 { + finCmd = finCmd[:len(finCmd)-1] + } + cliOpts.HealthCmd = finCmd cliOpts.HealthInterval = cc.Config.Healthcheck.Interval.String() cliOpts.HealthRetries = uint(cc.Config.Healthcheck.Retries) cliOpts.HealthStartPeriod = cc.Config.Healthcheck.StartPeriod.String() diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 24b45e479..42f515ace 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -516,7 +516,6 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string if len(con) != 2 { return fmt.Errorf("invalid --security-opt 1: %q", opt) } - switch con[0] { case "apparmor": s.ContainerSecurityConfig.ApparmorProfile = con[1] @@ -664,25 +663,40 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string } func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string) (*manifest.Schema2HealthConfig, error) { + cmdArr := []string{} + isArr := true + err := json.Unmarshal([]byte(inCmd), &cmdArr) // array unmarshalling + if err != nil { + cmdArr = strings.SplitN(inCmd, " ", 2) // default for compat + isArr = false + } // Every healthcheck requires a command - if len(inCmd) == 0 { + if len(cmdArr) == 0 { return nil, errors.New("Must define a healthcheck command for all healthchecks") } - - // first try to parse option value as JSON array of strings... - cmd := []string{} - - if inCmd == "none" { - cmd = []string{"NONE"} - } else { - err := json.Unmarshal([]byte(inCmd), &cmd) - if err != nil { - // ...otherwise pass it to "/bin/sh -c" inside the container - cmd = []string{"CMD-SHELL", inCmd} + concat := "" + if cmdArr[0] == "CMD" || cmdArr[0] == "none" { // this is for compat, we are already split properly for most compat cases + cmdArr = strings.Fields(inCmd) + } else if cmdArr[0] != "CMD-SHELL" { // this is for podman side of things, wont contain the keywords + if isArr && len(cmdArr) > 1 { // an array of consecutive commands + cmdArr = append([]string{"CMD"}, cmdArr...) + } else { // one singular command + if len(cmdArr) == 1 { + concat = cmdArr[0] + } else { + concat = strings.Join(cmdArr[0:], " ") + } + cmdArr = append([]string{"CMD-SHELL"}, concat) } } + + if cmdArr[0] == "none" { // if specified to remove healtcheck + cmdArr = []string{"NONE"} + } + + // healthcheck is by default an array, so we simply pass the user input hc := manifest.Schema2HealthConfig{ - Test: cmd, + Test: cmdArr, } if interval == "disable" { diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index e5f9e88d9..4d8443fcb 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -465,38 +465,43 @@ reexec_in_user_namespace_wait (int pid, int options) static int create_pause_process (const char *pause_pid_file_path, char **argv) { - int r, p[2]; + pid_t pid; + int p[2]; if (pipe (p) < 0) - _exit (EXIT_FAILURE); + return -1; - r = fork (); - if (r < 0) - _exit (EXIT_FAILURE); + pid = fork (); + if (pid < 0) + { + close (p[0]); + close (p[1]); + return -1; + } - if (r) + if (pid) { char b; + int r; close (p[1]); /* Block until we write the pid file. */ r = TEMP_FAILURE_RETRY (read (p[0], &b, 1)); close (p[0]); - reexec_in_user_namespace_wait (r, 0); + reexec_in_user_namespace_wait (pid, 0); return r == 1 && b == '0' ? 0 : -1; } else { - int fd; - pid_t pid; + int r, fd; close (p[0]); setsid (); pid = fork (); - if (r < 0) + if (pid < 0) _exit (EXIT_FAILURE); if (pid) diff --git a/test/apiv2/python/rest_api/test_v2_0_0_container.py b/test/apiv2/python/rest_api/test_v2_0_0_container.py index f252bd401..30d902d8c 100644 --- a/test/apiv2/python/rest_api/test_v2_0_0_container.py +++ b/test/apiv2/python/rest_api/test_v2_0_0_container.py @@ -33,9 +33,10 @@ class ContainerTestCase(APITestCase): self.assertId(r.content) _ = parse(r.json()["Created"]) + r = requests.post( self.podman_url + "/v1.40/containers/create?name=topcontainer", - json={"Cmd": ["top"], "Image": "alpine:latest"}, + json={"Healthcheck": {"Test": ["CMD-SHELL", "exit 0"], "Interval":1000, "Timeout":1000, "Retries": 5}, "Cmd": ["top"], "Image": "alpine:latest"}, ) self.assertEqual(r.status_code, 201, r.text) payload = r.json() @@ -49,6 +50,13 @@ class ContainerTestCase(APITestCase): state = out["State"]["Health"] self.assertIsInstance(state, dict) + r = requests.get(self.uri(f"/containers/{payload['Id']}/json")) + self.assertEqual(r.status_code, 200, r.text) + self.assertId(r.content) + out = r.json() + hc = out["Config"]["Healthcheck"]["Test"] + self.assertListEqual(["CMD-SHELL", "exit 0"], hc) + def test_stats(self): r = requests.get(self.uri(self.resolve_container("/containers/{}/stats?stream=false"))) self.assertIn(r.status_code, (200, 409), r.text) diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go index 28040ecfd..535783dbd 100644 --- a/test/e2e/healthcheck_run_test.go +++ b/test/e2e/healthcheck_run_test.go @@ -174,6 +174,16 @@ var _ = Describe("Podman healthcheck run", func() { Expect(inspect[0].State.Healthcheck.Status).To(Equal("healthy")) }) + It("podman healthcheck unhealthy but valid arguments check", func() { + session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-retries", "2", "--health-cmd", "[\"ls\", \"/foo\"]", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) + hc.WaitWithDefaultTimeout() + Expect(hc.ExitCode()).To(Equal(1)) + }) + It("podman healthcheck single healthy result changes failed to healthy", func() { session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-retries", "2", "--health-cmd", "ls /foo || exit 1", ALPINE, "top"}) session.WaitWithDefaultTimeout() diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index 3bfd59b54..3c65c02d1 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -1213,14 +1213,14 @@ USER mail`, BB) }) It("podman run with bad healthcheck timeout", func() { - session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "[\"foo\"]", "--health-timeout", "0s", ALPINE, "top"}) + session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "foo", "--health-timeout", "0s", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("healthcheck-timeout must be at least 1 second")) }) It("podman run with bad healthcheck start-period", func() { - session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "[\"foo\"]", "--health-start-period", "-1s", ALPINE, "top"}) + session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "foo", "--health-start-period", "-1s", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("healthcheck-start-period must be 0 seconds or greater")) diff --git a/transfer.md b/transfer.md index c37592384..765094dc9 100644 --- a/transfer.md +++ b/transfer.md @@ -141,8 +141,8 @@ The following podman commands do not have a Docker equivalent: * [`podman generate `](./docs/source/markdown/podman-generate.1.md) * [`podman generate kube`](./docs/source/markdown/podman-generate-kube.1.md) * [`podman generate systemd`](./docs/source/markdown/podman-generate-systemd.1.md) -* [`podman healthcheck `](/docs/source/markdown/podmanh-healthcheck.1.md) -* [`podman healthcheck run`](/docs/source/markdown/podmanh-healthcheck-run.1.md) +* [`podman healthcheck `](/docs/source/markdown/podman-healthcheck.1.md) +* [`podman healthcheck run`](/docs/source/markdown/podman-healthcheck-run.1.md) * [`podman image diff`](./docs/source/markdown/podman-image-diff.1.md) * [`podman image exists`](./docs/source/markdown/podman-image-exists.1.md) * [`podman image mount`](./docs/source/markdown/podman-image-mount.1.md) |