Signed-off-by: allencloud <allen.sun@daocloud.io>
| ... | ... |
@@ -742,10 +742,10 @@ definitions: |
| 742 | 742 |
items: |
| 743 | 743 |
type: "string" |
| 744 | 744 |
Interval: |
| 745 |
- description: "The time to wait between checks in nanoseconds. 0 means inherit." |
|
| 745 |
+ description: "The time to wait between checks in nanoseconds. It should be 0 or not less than 1000000000(1s). 0 means inherit." |
|
| 746 | 746 |
type: "integer" |
| 747 | 747 |
Timeout: |
| 748 |
- description: "The time to wait before considering the check to have hung. 0 means inherit." |
|
| 748 |
+ description: "The time to wait before considering the check to have hung. It should be 0 or not less than 1000000000(1s). 0 means inherit." |
|
| 749 | 749 |
type: "integer" |
| 750 | 750 |
Retries: |
| 751 | 751 |
description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." |
| ... | ... |
@@ -511,6 +511,9 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*container.Config, *c |
| 511 | 511 |
if copts.healthTimeout < 0 {
|
| 512 | 512 |
return nil, nil, nil, fmt.Errorf("--health-timeout cannot be negative")
|
| 513 | 513 |
} |
| 514 |
+ if copts.healthRetries < 0 {
|
|
| 515 |
+ return nil, nil, nil, fmt.Errorf("--health-retries cannot be negative")
|
|
| 516 |
+ } |
|
| 514 | 517 |
|
| 515 | 518 |
healthConfig = &container.HealthConfig{
|
| 516 | 519 |
Test: probe, |
| ... | ... |
@@ -231,6 +231,21 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostCon |
| 231 | 231 |
return nil, err |
| 232 | 232 |
} |
| 233 | 233 |
} |
| 234 |
+ |
|
| 235 |
+ // Validate the healthcheck params of Config |
|
| 236 |
+ if config.Healthcheck != nil {
|
|
| 237 |
+ if config.Healthcheck.Interval != 0 && config.Healthcheck.Interval < time.Second {
|
|
| 238 |
+ return nil, fmt.Errorf("Interval in Healthcheck cannot be less than one second")
|
|
| 239 |
+ } |
|
| 240 |
+ |
|
| 241 |
+ if config.Healthcheck.Timeout != 0 && config.Healthcheck.Timeout < time.Second {
|
|
| 242 |
+ return nil, fmt.Errorf("Timeout in Healthcheck cannot be less than one second")
|
|
| 243 |
+ } |
|
| 244 |
+ |
|
| 245 |
+ if config.Healthcheck.Retries < 0 {
|
|
| 246 |
+ return nil, fmt.Errorf("Retries in Healthcheck cannot be negative")
|
|
| 247 |
+ } |
|
| 248 |
+ } |
|
| 234 | 249 |
} |
| 235 | 250 |
|
| 236 | 251 |
if hostConfig == nil {
|
| ... | ... |
@@ -281,6 +281,12 @@ Create a container |
| 281 | 281 |
"Volumes": {
|
| 282 | 282 |
"/volumes/data": {}
|
| 283 | 283 |
}, |
| 284 |
+ "Healthcheck":{
|
|
| 285 |
+ "Test": ["CMD-SHELL", "curl localhost:3000"], |
|
| 286 |
+ "Interval": 1000000000, |
|
| 287 |
+ "Timeout": 10000000000, |
|
| 288 |
+ "Retries": 10 |
|
| 289 |
+ }, |
|
| 284 | 290 |
"WorkingDir": "", |
| 285 | 291 |
"NetworkDisabled": false, |
| 286 | 292 |
"MacAddress": "12:34:56:78:9a:bc", |
| ... | ... |
@@ -385,6 +391,15 @@ Create a container |
| 385 | 385 |
- **Image** - A string specifying the image name to use for the container. |
| 386 | 386 |
- **Volumes** - An object mapping mount point paths (strings) inside the |
| 387 | 387 |
container to empty objects. |
| 388 |
+- **Healthcheck** - A test to perform to check that the container is healthy. |
|
| 389 |
+ - **Test** - The test to perform. Possible values are: |
|
| 390 |
+ + `{}` inherit healthcheck from image or parent image
|
|
| 391 |
+ + `{"NONE"}` disable healthcheck
|
|
| 392 |
+ + `{"CMD", args...}` exec arguments directly
|
|
| 393 |
+ + `{"CMD-SHELL", command}` run command with system's default shell
|
|
| 394 |
+ - **Interval** - The time to wait between checks in nanoseconds. It should be 0 or not less than 1000000000(1s). 0 means inherit. |
|
| 395 |
+ - **Timeout** - The time to wait before considering the check to have hung. It should be 0 or not less than 1000000000(1s). 0 means inherit. |
|
| 396 |
+ - **Retries** - The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit. |
|
| 388 | 397 |
- **WorkingDir** - A string specifying the working directory for commands to |
| 389 | 398 |
run in. |
| 390 | 399 |
- **NetworkDisabled** - Boolean value, when true disables networking for the |
| ... | ... |
@@ -2,6 +2,7 @@ package main |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"net/http" |
| 5 |
+ "time" |
|
| 5 | 6 |
|
| 6 | 7 |
"github.com/docker/docker/integration-cli/checker" |
| 7 | 8 |
"github.com/docker/docker/integration-cli/request" |
| ... | ... |
@@ -83,3 +84,70 @@ func (s *DockerSuite) TestAPICreateEmptyEnv(c *check.C) {
|
| 83 | 83 |
expected = "invalid environment variable: =foo" |
| 84 | 84 |
c.Assert(getErrorMessage(c, body), checker.Contains, expected) |
| 85 | 85 |
} |
| 86 |
+ |
|
| 87 |
+func (s *DockerSuite) TestAPICreateWithInvalidHealthcheckParams(c *check.C) {
|
|
| 88 |
+ // test invalid Interval in Healthcheck: less than 0s |
|
| 89 |
+ name := "test1" |
|
| 90 |
+ config := map[string]interface{}{
|
|
| 91 |
+ "Image": "busybox", |
|
| 92 |
+ "Healthcheck": map[string]interface{}{
|
|
| 93 |
+ "Interval": time.Duration(-10000000), |
|
| 94 |
+ "Timeout": time.Duration(1000000000), |
|
| 95 |
+ "Retries": int(1000), |
|
| 96 |
+ }, |
|
| 97 |
+ } |
|
| 98 |
+ |
|
| 99 |
+ status, body, err := request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
|
| 100 |
+ c.Assert(err, check.IsNil) |
|
| 101 |
+ c.Assert(status, check.Equals, http.StatusInternalServerError) |
|
| 102 |
+ expected := "Interval in Healthcheck cannot be less than one second" |
|
| 103 |
+ c.Assert(getErrorMessage(c, body), checker.Contains, expected) |
|
| 104 |
+ |
|
| 105 |
+ // test invalid Interval in Healthcheck: larger than 0s but less than 1s |
|
| 106 |
+ name = "test2" |
|
| 107 |
+ config = map[string]interface{}{
|
|
| 108 |
+ "Image": "busybox", |
|
| 109 |
+ "Healthcheck": map[string]interface{}{
|
|
| 110 |
+ "Interval": time.Duration(500000000), |
|
| 111 |
+ "Timeout": time.Duration(1000000000), |
|
| 112 |
+ "Retries": int(1000), |
|
| 113 |
+ }, |
|
| 114 |
+ } |
|
| 115 |
+ status, body, err = request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
|
| 116 |
+ c.Assert(err, check.IsNil) |
|
| 117 |
+ c.Assert(status, check.Equals, http.StatusInternalServerError) |
|
| 118 |
+ expected = "Interval in Healthcheck cannot be less than one second" |
|
| 119 |
+ c.Assert(getErrorMessage(c, body), checker.Contains, expected) |
|
| 120 |
+ |
|
| 121 |
+ // test invalid Timeout in Healthcheck: less than 1s |
|
| 122 |
+ name = "test3" |
|
| 123 |
+ config = map[string]interface{}{
|
|
| 124 |
+ "Image": "busybox", |
|
| 125 |
+ "Healthcheck": map[string]interface{}{
|
|
| 126 |
+ "Interval": time.Duration(1000000000), |
|
| 127 |
+ "Timeout": time.Duration(-100000000), |
|
| 128 |
+ "Retries": int(1000), |
|
| 129 |
+ }, |
|
| 130 |
+ } |
|
| 131 |
+ status, body, err = request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
|
| 132 |
+ c.Assert(err, check.IsNil) |
|
| 133 |
+ c.Assert(status, check.Equals, http.StatusInternalServerError) |
|
| 134 |
+ expected = "Timeout in Healthcheck cannot be less than one second" |
|
| 135 |
+ c.Assert(getErrorMessage(c, body), checker.Contains, expected) |
|
| 136 |
+ |
|
| 137 |
+ // test invalid Retries in Healthcheck: less than 0 |
|
| 138 |
+ name = "test4" |
|
| 139 |
+ config = map[string]interface{}{
|
|
| 140 |
+ "Image": "busybox", |
|
| 141 |
+ "Healthcheck": map[string]interface{}{
|
|
| 142 |
+ "Interval": time.Duration(1000000000), |
|
| 143 |
+ "Timeout": time.Duration(1000000000), |
|
| 144 |
+ "Retries": int(-10), |
|
| 145 |
+ }, |
|
| 146 |
+ } |
|
| 147 |
+ status, body, err = request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
|
| 148 |
+ c.Assert(err, check.IsNil) |
|
| 149 |
+ c.Assert(status, check.Equals, http.StatusInternalServerError) |
|
| 150 |
+ expected = "Retries in Healthcheck cannot be negative" |
|
| 151 |
+ c.Assert(getErrorMessage(c, body), checker.Contains, expected) |
|
| 152 |
+} |
| ... | ... |
@@ -95,7 +95,7 @@ func (s *DockerSuite) TestHealth(c *check.C) {
|
| 95 | 95 |
|
| 96 | 96 |
// Enable the checks from the CLI |
| 97 | 97 |
_, _ = dockerCmd(c, "run", "-d", "--name=fatal_healthcheck", |
| 98 |
- "--health-interval=0.5s", |
|
| 98 |
+ "--health-interval=1s", |
|
| 99 | 99 |
"--health-retries=3", |
| 100 | 100 |
"--health-cmd=cat /status", |
| 101 | 101 |
"no_healthcheck") |
| ... | ... |
@@ -121,13 +121,13 @@ func (s *DockerSuite) TestHealth(c *check.C) {
|
| 121 | 121 |
// Note: if the interval is too small, it seems that Docker spends all its time running health |
| 122 | 122 |
// checks and never gets around to killing it. |
| 123 | 123 |
_, _ = dockerCmd(c, "run", "-d", "--name=test", |
| 124 |
- "--health-interval=1s", "--health-cmd=sleep 5m", "--health-timeout=1ms", imageName) |
|
| 124 |
+ "--health-interval=1s", "--health-cmd=sleep 5m", "--health-timeout=1s", imageName) |
|
| 125 | 125 |
waitForHealthStatus(c, "test", "starting", "unhealthy") |
| 126 | 126 |
health = getHealth(c, "test") |
| 127 | 127 |
last = health.Log[len(health.Log)-1] |
| 128 | 128 |
c.Check(health.Status, checker.Equals, "unhealthy") |
| 129 | 129 |
c.Check(last.ExitCode, checker.Equals, -1) |
| 130 |
- c.Check(last.Output, checker.Equals, "Health check exceeded timeout (1ms)") |
|
| 130 |
+ c.Check(last.Output, checker.Equals, "Health check exceeded timeout (1s)") |
|
| 131 | 131 |
dockerCmd(c, "rm", "-f", "test") |
| 132 | 132 |
|
| 133 | 133 |
// Check JSON-format |