Signed-off-by: Dong Chen <dongluo.chen@docker.com>
| ... | ... |
@@ -499,16 +499,16 @@ definitions: |
| 499 | 499 |
items: |
| 500 | 500 |
type: "string" |
| 501 | 501 |
Interval: |
| 502 |
- description: "The time to wait between checks in nanoseconds. It should be 0 or not less than 1000000000(1s). 0 means inherit." |
|
| 502 |
+ description: "The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." |
|
| 503 | 503 |
type: "integer" |
| 504 | 504 |
Timeout: |
| 505 |
- 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." |
|
| 505 |
+ description: "The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit." |
|
| 506 | 506 |
type: "integer" |
| 507 | 507 |
Retries: |
| 508 | 508 |
description: "The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit." |
| 509 | 509 |
type: "integer" |
| 510 | 510 |
StartPeriod: |
| 511 |
- description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. 0 means inherit." |
|
| 511 |
+ description: "Start period for the container to initialize before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit." |
|
| 512 | 512 |
type: "integer" |
| 513 | 513 |
|
| 514 | 514 |
HostConfig: |
| ... | ... |
@@ -7,6 +7,12 @@ import ( |
| 7 | 7 |
"github.com/docker/go-connections/nat" |
| 8 | 8 |
) |
| 9 | 9 |
|
| 10 |
+// MinimumDuration puts a minimum on user configured duration. |
|
| 11 |
+// This is to prevent API error on time unit. For example, API may |
|
| 12 |
+// set 3 as healthcheck interval with intention of 3 seconds, but |
|
| 13 |
+// Docker interprets it as 3 nanoseconds. |
|
| 14 |
+const MinimumDuration = 1 * time.Millisecond |
|
| 15 |
+ |
|
| 10 | 16 |
// HealthConfig holds configuration settings for the HEALTHCHECK feature. |
| 11 | 17 |
type HealthConfig struct {
|
| 12 | 18 |
// Test is the test to perform to check that the container is healthy. |
| ... | ... |
@@ -497,7 +497,7 @@ func cmd(b *Builder, args []string, attributes map[string]bool, original string) |
| 497 | 497 |
} |
| 498 | 498 |
|
| 499 | 499 |
// parseOptInterval(flag) is the duration of flag.Value, or 0 if |
| 500 |
-// empty. An error is reported if the value is given and less than 1 second. |
|
| 500 |
+// empty. An error is reported if the value is given and less than minimum duration. |
|
| 501 | 501 |
func parseOptInterval(f *Flag) (time.Duration, error) {
|
| 502 | 502 |
s := f.Value |
| 503 | 503 |
if s == "" {
|
| ... | ... |
@@ -507,8 +507,8 @@ func parseOptInterval(f *Flag) (time.Duration, error) {
|
| 507 | 507 |
if err != nil {
|
| 508 | 508 |
return 0, err |
| 509 | 509 |
} |
| 510 |
- if d < time.Duration(time.Second) {
|
|
| 511 |
- return 0, fmt.Errorf("Interval %#v cannot be less than 1 second", f.name)
|
|
| 510 |
+ if d < time.Duration(container.MinimumDuration) {
|
|
| 511 |
+ return 0, fmt.Errorf("Interval %#v cannot be less than %s", f.name, container.MinimumDuration)
|
|
| 512 | 512 |
} |
| 513 | 513 |
return d, nil |
| 514 | 514 |
} |
| ... | ... |
@@ -530,3 +530,21 @@ func TestShell(t *testing.T) {
|
| 530 | 530 |
t.Fatalf("Shell should be set to %s, got %s", expectedShell, b.runConfig.Shell)
|
| 531 | 531 |
} |
| 532 | 532 |
} |
| 533 |
+ |
|
| 534 |
+func TestParseOptInterval(t *testing.T) {
|
|
| 535 |
+ flInterval := &Flag{
|
|
| 536 |
+ name: "interval", |
|
| 537 |
+ flagType: stringType, |
|
| 538 |
+ Value: "50ns", |
|
| 539 |
+ } |
|
| 540 |
+ _, err := parseOptInterval(flInterval) |
|
| 541 |
+ if err == nil {
|
|
| 542 |
+ t.Fatalf("Error should be presented for interval %s", flInterval.Value)
|
|
| 543 |
+ } |
|
| 544 |
+ |
|
| 545 |
+ flInterval.Value = "1ms" |
|
| 546 |
+ _, err = parseOptInterval(flInterval) |
|
| 547 |
+ if err != nil {
|
|
| 548 |
+ t.Fatalf("Unexpected error: %s", err.Error())
|
|
| 549 |
+ } |
|
| 550 |
+} |
| ... | ... |
@@ -244,20 +244,20 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostCon |
| 244 | 244 |
|
| 245 | 245 |
// Validate the healthcheck params of Config |
| 246 | 246 |
if config.Healthcheck != nil {
|
| 247 |
- if config.Healthcheck.Interval != 0 && config.Healthcheck.Interval < time.Millisecond {
|
|
| 248 |
- return nil, fmt.Errorf("Interval in Healthcheck cannot be less than one millisecond")
|
|
| 247 |
+ if config.Healthcheck.Interval != 0 && config.Healthcheck.Interval < containertypes.MinimumDuration {
|
|
| 248 |
+ return nil, fmt.Errorf("Interval in Healthcheck cannot be less than %s", containertypes.MinimumDuration)
|
|
| 249 | 249 |
} |
| 250 | 250 |
|
| 251 |
- if config.Healthcheck.Timeout != 0 && config.Healthcheck.Timeout < time.Millisecond {
|
|
| 252 |
- return nil, fmt.Errorf("Timeout in Healthcheck cannot be less than one millisecond")
|
|
| 251 |
+ if config.Healthcheck.Timeout != 0 && config.Healthcheck.Timeout < containertypes.MinimumDuration {
|
|
| 252 |
+ return nil, fmt.Errorf("Timeout in Healthcheck cannot be less than %s", containertypes.MinimumDuration)
|
|
| 253 | 253 |
} |
| 254 | 254 |
|
| 255 | 255 |
if config.Healthcheck.Retries < 0 {
|
| 256 | 256 |
return nil, fmt.Errorf("Retries in Healthcheck cannot be negative")
|
| 257 | 257 |
} |
| 258 | 258 |
|
| 259 |
- if config.Healthcheck.StartPeriod != 0 && config.Healthcheck.StartPeriod < time.Millisecond {
|
|
| 260 |
- return nil, fmt.Errorf("StartPeriod in Healthcheck cannot be less than one millisecond")
|
|
| 259 |
+ if config.Healthcheck.StartPeriod != 0 && config.Healthcheck.StartPeriod < containertypes.MinimumDuration {
|
|
| 260 |
+ return nil, fmt.Errorf("StartPeriod in Healthcheck cannot be less than %s", containertypes.MinimumDuration)
|
|
| 261 | 261 |
} |
| 262 | 262 |
} |
| 263 | 263 |
} |
| ... | ... |
@@ -285,7 +285,8 @@ Create a container |
| 285 | 285 |
"Test": ["CMD-SHELL", "curl localhost:3000"], |
| 286 | 286 |
"Interval": 1000000000, |
| 287 | 287 |
"Timeout": 10000000000, |
| 288 |
- "Retries": 10 |
|
| 288 |
+ "Retries": 10, |
|
| 289 |
+ "StartPeriod": 60000000000 |
|
| 289 | 290 |
}, |
| 290 | 291 |
"WorkingDir": "", |
| 291 | 292 |
"NetworkDisabled": false, |
| ... | ... |
@@ -397,9 +398,10 @@ Create a container |
| 397 | 397 |
+ `{"NONE"}` disable healthcheck
|
| 398 | 398 |
+ `{"CMD", args...}` exec arguments directly
|
| 399 | 399 |
+ `{"CMD-SHELL", command}` run command with system's default shell
|
| 400 |
- - **Interval** - The time to wait between checks in nanoseconds. It should be 0 or not less than 1000000000(1s). 0 means inherit. |
|
| 401 |
- - **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. |
|
| 400 |
+ - **Interval** - The time to wait between checks in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit. |
|
| 401 |
+ - **Timeout** - The time to wait before considering the check to have hung. It should be 0 or at least 1000000 (1 ms). 0 means inherit. |
|
| 402 | 402 |
- **Retries** - The number of consecutive failures needed to consider a container as unhealthy. 0 means inherit. |
| 403 |
+ - **StartPeriod** - The time to wait for container initialization before starting health-retries countdown in nanoseconds. It should be 0 or at least 1000000 (1 ms). 0 means inherit. |
|
| 403 | 404 |
- **WorkingDir** - A string specifying the working directory for commands to |
| 404 | 405 |
run in. |
| 405 | 406 |
- **NetworkDisabled** - Boolean value, when true disables networking for the |
| ... | ... |
@@ -1,9 +1,11 @@ |
| 1 | 1 |
package main |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 4 | 5 |
"net/http" |
| 5 | 6 |
"time" |
| 6 | 7 |
|
| 8 |
+ "github.com/docker/docker/api/types/container" |
|
| 7 | 9 |
"github.com/docker/docker/integration-cli/checker" |
| 8 | 10 |
"github.com/docker/docker/integration-cli/request" |
| 9 | 11 |
"github.com/go-check/check" |
| ... | ... |
@@ -91,8 +93,8 @@ func (s *DockerSuite) TestAPICreateWithInvalidHealthcheckParams(c *check.C) {
|
| 91 | 91 |
config := map[string]interface{}{
|
| 92 | 92 |
"Image": "busybox", |
| 93 | 93 |
"Healthcheck": map[string]interface{}{
|
| 94 |
- "Interval": time.Duration(-10000000), |
|
| 95 |
- "Timeout": time.Duration(1000000000), |
|
| 94 |
+ "Interval": -10 * time.Millisecond, |
|
| 95 |
+ "Timeout": time.Second, |
|
| 96 | 96 |
"Retries": int(1000), |
| 97 | 97 |
}, |
| 98 | 98 |
} |
| ... | ... |
@@ -100,39 +102,38 @@ func (s *DockerSuite) TestAPICreateWithInvalidHealthcheckParams(c *check.C) {
|
| 100 | 100 |
status, body, err := request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
| 101 | 101 |
c.Assert(err, check.IsNil) |
| 102 | 102 |
c.Assert(status, check.Equals, http.StatusInternalServerError) |
| 103 |
- expected := "Interval in Healthcheck cannot be less than one second" |
|
| 103 |
+ expected := fmt.Sprintf("Interval in Healthcheck cannot be less than %s", container.MinimumDuration)
|
|
| 104 | 104 |
c.Assert(getErrorMessage(c, body), checker.Contains, expected) |
| 105 | 105 |
|
| 106 |
- // test invalid Interval in Healthcheck: larger than 0s but less than 1s |
|
| 106 |
+ // test invalid Interval in Healthcheck: larger than 0s but less than 1ms |
|
| 107 | 107 |
name = "test2" |
| 108 | 108 |
config = map[string]interface{}{
|
| 109 | 109 |
"Image": "busybox", |
| 110 | 110 |
"Healthcheck": map[string]interface{}{
|
| 111 |
- "Interval": time.Duration(500000000), |
|
| 112 |
- "Timeout": time.Duration(1000000000), |
|
| 111 |
+ "Interval": 500 * time.Microsecond, |
|
| 112 |
+ "Timeout": time.Second, |
|
| 113 | 113 |
"Retries": int(1000), |
| 114 | 114 |
}, |
| 115 | 115 |
} |
| 116 | 116 |
status, body, err = request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
| 117 | 117 |
c.Assert(err, check.IsNil) |
| 118 | 118 |
c.Assert(status, check.Equals, http.StatusInternalServerError) |
| 119 |
- expected = "Interval in Healthcheck cannot be less than one second" |
|
| 120 | 119 |
c.Assert(getErrorMessage(c, body), checker.Contains, expected) |
| 121 | 120 |
|
| 122 |
- // test invalid Timeout in Healthcheck: less than 1s |
|
| 121 |
+ // test invalid Timeout in Healthcheck: less than 1ms |
|
| 123 | 122 |
name = "test3" |
| 124 | 123 |
config = map[string]interface{}{
|
| 125 | 124 |
"Image": "busybox", |
| 126 | 125 |
"Healthcheck": map[string]interface{}{
|
| 127 |
- "Interval": time.Duration(1000000000), |
|
| 128 |
- "Timeout": time.Duration(-100000000), |
|
| 126 |
+ "Interval": time.Second, |
|
| 127 |
+ "Timeout": -100 * time.Millisecond, |
|
| 129 | 128 |
"Retries": int(1000), |
| 130 | 129 |
}, |
| 131 | 130 |
} |
| 132 | 131 |
status, body, err = request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
| 133 | 132 |
c.Assert(err, check.IsNil) |
| 134 | 133 |
c.Assert(status, check.Equals, http.StatusInternalServerError) |
| 135 |
- expected = "Timeout in Healthcheck cannot be less than one second" |
|
| 134 |
+ expected = fmt.Sprintf("Timeout in Healthcheck cannot be less than %s", container.MinimumDuration)
|
|
| 136 | 135 |
c.Assert(getErrorMessage(c, body), checker.Contains, expected) |
| 137 | 136 |
|
| 138 | 137 |
// test invalid Retries in Healthcheck: less than 0 |
| ... | ... |
@@ -140,8 +141,8 @@ func (s *DockerSuite) TestAPICreateWithInvalidHealthcheckParams(c *check.C) {
|
| 140 | 140 |
config = map[string]interface{}{
|
| 141 | 141 |
"Image": "busybox", |
| 142 | 142 |
"Healthcheck": map[string]interface{}{
|
| 143 |
- "Interval": time.Duration(1000000000), |
|
| 144 |
- "Timeout": time.Duration(1000000000), |
|
| 143 |
+ "Interval": time.Second, |
|
| 144 |
+ "Timeout": time.Second, |
|
| 145 | 145 |
"Retries": int(-10), |
| 146 | 146 |
}, |
| 147 | 147 |
} |
| ... | ... |
@@ -150,4 +151,21 @@ func (s *DockerSuite) TestAPICreateWithInvalidHealthcheckParams(c *check.C) {
|
| 150 | 150 |
c.Assert(status, check.Equals, http.StatusInternalServerError) |
| 151 | 151 |
expected = "Retries in Healthcheck cannot be negative" |
| 152 | 152 |
c.Assert(getErrorMessage(c, body), checker.Contains, expected) |
| 153 |
+ |
|
| 154 |
+ // test invalid StartPeriod in Healthcheck: not 0 and less than 1ms |
|
| 155 |
+ name = "test3" |
|
| 156 |
+ config = map[string]interface{}{
|
|
| 157 |
+ "Image": "busybox", |
|
| 158 |
+ "Healthcheck": map[string]interface{}{
|
|
| 159 |
+ "Interval": time.Second, |
|
| 160 |
+ "Timeout": time.Second, |
|
| 161 |
+ "Retries": int(1000), |
|
| 162 |
+ "StartPeriod": 100 * time.Microsecond, |
|
| 163 |
+ }, |
|
| 164 |
+ } |
|
| 165 |
+ status, body, err = request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
|
| 166 |
+ c.Assert(err, check.IsNil) |
|
| 167 |
+ c.Assert(status, check.Equals, http.StatusInternalServerError) |
|
| 168 |
+ expected = fmt.Sprintf("StartPeriod in Healthcheck cannot be less than %s", container.MinimumDuration)
|
|
| 169 |
+ c.Assert(getErrorMessage(c, body), checker.Contains, expected) |
|
| 153 | 170 |
} |