Signed-off-by: Dong Chen <dongluo.chen@docker.com>
Dong Chen authored on 2017/04/11 10:58:31... | ... |
@@ -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 |
} |