Move restart-policy validation from client to daemon.
| ... | ... |
@@ -255,6 +255,23 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostCon |
| 255 | 255 |
} |
| 256 | 256 |
} |
| 257 | 257 |
|
| 258 |
+ p := hostConfig.RestartPolicy |
|
| 259 |
+ |
|
| 260 |
+ switch p.Name {
|
|
| 261 |
+ case "always", "unless-stopped", "no": |
|
| 262 |
+ if p.MaximumRetryCount != 0 {
|
|
| 263 |
+ return nil, fmt.Errorf("maximum restart count not valid with restart policy of '%s'", p.Name)
|
|
| 264 |
+ } |
|
| 265 |
+ case "on-failure": |
|
| 266 |
+ if p.MaximumRetryCount < 1 {
|
|
| 267 |
+ return nil, fmt.Errorf("maximum restart count must be a positive integer")
|
|
| 268 |
+ } |
|
| 269 |
+ case "": |
|
| 270 |
+ // do nothing |
|
| 271 |
+ default: |
|
| 272 |
+ return nil, fmt.Errorf("invalid restart policy '%s'", p.Name)
|
|
| 273 |
+ } |
|
| 274 |
+ |
|
| 258 | 275 |
// Now do platform-specific verification |
| 259 | 276 |
return verifyPlatformContainerSettings(daemon, hostConfig, config, update) |
| 260 | 277 |
} |
| ... | ... |
@@ -703,6 +703,66 @@ func (s *DockerSuite) TestContainerApiInvalidPortSyntax(c *check.C) {
|
| 703 | 703 |
c.Assert(string(b[:]), checker.Contains, "invalid port") |
| 704 | 704 |
} |
| 705 | 705 |
|
| 706 |
+func (s *DockerSuite) TestContainerApiInvalidRestartPolicyName(c *check.C) {
|
|
| 707 |
+ config := `{
|
|
| 708 |
+ "Image": "busybox", |
|
| 709 |
+ "HostConfig": {
|
|
| 710 |
+ "RestartPolicy": {
|
|
| 711 |
+ "Name": "something", |
|
| 712 |
+ "MaximumRetryCount": 0 |
|
| 713 |
+ } |
|
| 714 |
+ } |
|
| 715 |
+ }` |
|
| 716 |
+ |
|
| 717 |
+ res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
|
|
| 718 |
+ c.Assert(err, checker.IsNil) |
|
| 719 |
+ c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) |
|
| 720 |
+ |
|
| 721 |
+ b, err := readBody(body) |
|
| 722 |
+ c.Assert(err, checker.IsNil) |
|
| 723 |
+ c.Assert(string(b[:]), checker.Contains, "invalid restart policy") |
|
| 724 |
+} |
|
| 725 |
+ |
|
| 726 |
+func (s *DockerSuite) TestContainerApiInvalidRestartPolicyRetryMismatch(c *check.C) {
|
|
| 727 |
+ config := `{
|
|
| 728 |
+ "Image": "busybox", |
|
| 729 |
+ "HostConfig": {
|
|
| 730 |
+ "RestartPolicy": {
|
|
| 731 |
+ "Name": "always", |
|
| 732 |
+ "MaximumRetryCount": 2 |
|
| 733 |
+ } |
|
| 734 |
+ } |
|
| 735 |
+ }` |
|
| 736 |
+ |
|
| 737 |
+ res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
|
|
| 738 |
+ c.Assert(err, checker.IsNil) |
|
| 739 |
+ c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) |
|
| 740 |
+ |
|
| 741 |
+ b, err := readBody(body) |
|
| 742 |
+ c.Assert(err, checker.IsNil) |
|
| 743 |
+ c.Assert(string(b[:]), checker.Contains, "maximum restart count not valid with restart policy") |
|
| 744 |
+} |
|
| 745 |
+ |
|
| 746 |
+func (s *DockerSuite) TestContainerApiInvalidRestartPolicyPositiveRetryCount(c *check.C) {
|
|
| 747 |
+ config := `{
|
|
| 748 |
+ "Image": "busybox", |
|
| 749 |
+ "HostConfig": {
|
|
| 750 |
+ "RestartPolicy": {
|
|
| 751 |
+ "Name": "on-failure", |
|
| 752 |
+ "MaximumRetryCount": -2 |
|
| 753 |
+ } |
|
| 754 |
+ } |
|
| 755 |
+ }` |
|
| 756 |
+ |
|
| 757 |
+ res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
|
|
| 758 |
+ c.Assert(err, checker.IsNil) |
|
| 759 |
+ c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError) |
|
| 760 |
+ |
|
| 761 |
+ b, err := readBody(body) |
|
| 762 |
+ c.Assert(err, checker.IsNil) |
|
| 763 |
+ c.Assert(string(b[:]), checker.Contains, "maximum restart count must be a positive integer") |
|
| 764 |
+} |
|
| 765 |
+ |
|
| 706 | 766 |
// Issue 7941 - test to make sure a "null" in JSON is just ignored. |
| 707 | 767 |
// W/o this fix a null in JSON would be parsed into a string var as "null" |
| 708 | 768 |
func (s *DockerSuite) TestContainerApiPostCreateNull(c *check.C) {
|
| ... | ... |
@@ -729,35 +729,22 @@ func ParseRestartPolicy(policy string) (container.RestartPolicy, error) {
|
| 729 | 729 |
return p, nil |
| 730 | 730 |
} |
| 731 | 731 |
|
| 732 |
- var ( |
|
| 733 |
- parts = strings.Split(policy, ":") |
|
| 734 |
- name = parts[0] |
|
| 735 |
- ) |
|
| 732 |
+ parts := strings.Split(policy, ":") |
|
| 736 | 733 |
|
| 737 |
- p.Name = name |
|
| 738 |
- switch name {
|
|
| 739 |
- case "always", "unless-stopped": |
|
| 740 |
- if len(parts) > 1 {
|
|
| 741 |
- return p, fmt.Errorf("maximum restart count not valid with restart policy of \"%s\"", name)
|
|
| 742 |
- } |
|
| 743 |
- case "no": |
|
| 744 |
- // do nothing |
|
| 745 |
- case "on-failure": |
|
| 746 |
- if len(parts) > 2 {
|
|
| 747 |
- return p, fmt.Errorf("restart count format is not valid, usage: 'on-failure:N' or 'on-failure'")
|
|
| 734 |
+ if len(parts) > 2 {
|
|
| 735 |
+ return p, fmt.Errorf("invalid restart policy format")
|
|
| 736 |
+ } |
|
| 737 |
+ if len(parts) == 2 {
|
|
| 738 |
+ count, err := strconv.Atoi(parts[1]) |
|
| 739 |
+ if err != nil {
|
|
| 740 |
+ return p, fmt.Errorf("maximum retry count must be an integer")
|
|
| 748 | 741 |
} |
| 749 |
- if len(parts) == 2 {
|
|
| 750 |
- count, err := strconv.Atoi(parts[1]) |
|
| 751 |
- if err != nil {
|
|
| 752 |
- return p, err |
|
| 753 |
- } |
|
| 754 | 742 |
|
| 755 |
- p.MaximumRetryCount = count |
|
| 756 |
- } |
|
| 757 |
- default: |
|
| 758 |
- return p, fmt.Errorf("invalid restart policy %s", name)
|
|
| 743 |
+ p.MaximumRetryCount = count |
|
| 759 | 744 |
} |
| 760 | 745 |
|
| 746 |
+ p.Name = parts[0] |
|
| 747 |
+ |
|
| 761 | 748 |
return p, nil |
| 762 | 749 |
} |
| 763 | 750 |
|
| ... | ... |
@@ -556,11 +556,8 @@ func TestParseModes(t *testing.T) {
|
| 556 | 556 |
|
| 557 | 557 |
func TestParseRestartPolicy(t *testing.T) {
|
| 558 | 558 |
invalids := map[string]string{
|
| 559 |
- "something": "invalid restart policy something", |
|
| 560 |
- "always:2": "maximum restart count not valid with restart policy of \"always\"", |
|
| 561 |
- "always:2:3": "maximum restart count not valid with restart policy of \"always\"", |
|
| 562 |
- "on-failure:invalid": `strconv.ParseInt: parsing "invalid": invalid syntax`, |
|
| 563 |
- "on-failure:2:5": "restart count format is not valid, usage: 'on-failure:N' or 'on-failure'", |
|
| 559 |
+ "always:2:3": "invalid restart policy format", |
|
| 560 |
+ "on-failure:invalid": "maximum retry count must be an integer", |
|
| 564 | 561 |
} |
| 565 | 562 |
valids := map[string]container.RestartPolicy{
|
| 566 | 563 |
"": {},
|