8
8
"fmt"
9
9
"net/http"
10
10
"strings"
11
+ "time"
11
12
12
13
"github.com/docker/docker/pkg/integration/checker"
13
14
"github.com/go-check/check"
@@ -66,33 +67,23 @@ func (s *DockerSuite) TestExecAPIStart(c *check.C) {
66
67
testRequires (c , DaemonIsLinux ) // Uses pause/unpause but bits may be salvagable to Windows to Windows CI
67
68
dockerCmd (c , "run" , "-d" , "--name" , "test" , "busybox" , "top" )
68
69
69
- startExec := func (id string , code int ) {
70
- resp , body , err := sockRequestRaw ("POST" , fmt .Sprintf ("/exec/%s/start" , id ), strings .NewReader (`{"Detach": true}` ), "application/json" )
71
- c .Assert (err , checker .IsNil )
72
-
73
- b , err := readBody (body )
74
- comment := check .Commentf ("response body: %s" , b )
75
- c .Assert (err , checker .IsNil , comment )
76
- c .Assert (resp .StatusCode , checker .Equals , code , comment )
77
- }
78
-
79
70
id := createExec (c , "test" )
80
- startExec (id , http .StatusOK )
71
+ startExec (c , id , http .StatusOK )
81
72
82
73
id = createExec (c , "test" )
83
74
dockerCmd (c , "stop" , "test" )
84
75
85
- startExec (id , http .StatusNotFound )
76
+ startExec (c , id , http .StatusNotFound )
86
77
87
78
dockerCmd (c , "start" , "test" )
88
- startExec (id , http .StatusNotFound )
79
+ startExec (c , id , http .StatusNotFound )
89
80
90
81
// make sure exec is created before pausing
91
82
id = createExec (c , "test" )
92
83
dockerCmd (c , "pause" , "test" )
93
- startExec (id , http .StatusConflict )
84
+ startExec (c , id , http .StatusConflict )
94
85
dockerCmd (c , "unpause" , "test" )
95
- startExec (id , http .StatusOK )
86
+ startExec (c , id , http .StatusOK )
96
87
}
97
88
98
89
func (s * DockerSuite ) TestExecAPIStartBackwardsCompatible (c * check.C ) {
@@ -108,6 +99,30 @@ func (s *DockerSuite) TestExecAPIStartBackwardsCompatible(c *check.C) {
108
99
c .Assert (resp .StatusCode , checker .Equals , http .StatusOK , comment )
109
100
}
110
101
102
+ // #19362
103
+ func (s * DockerSuite ) TestExecAPIStartMultipleTimesError (c * check.C ) {
104
+ dockerCmd (c , "run" , "-d" , "--name" , "test" , "busybox" , "top" )
105
+ execID := createExec (c , "test" )
106
+ startExec (c , execID , http .StatusOK )
107
+
108
+ timeout := time .After (10 * time .Second )
109
+ var execJSON struct { Running bool }
110
+ for {
111
+ select {
112
+ case <- timeout :
113
+ c .Fatal ("timeout waiting for exec to start" )
114
+ default :
115
+ }
116
+
117
+ inspectExec (c , execID , & execJSON )
118
+ if ! execJSON .Running {
119
+ break
120
+ }
121
+ }
122
+
123
+ startExec (c , execID , http .StatusConflict )
124
+ }
125
+
111
126
func createExec (c * check.C , name string ) string {
112
127
_ , b , err := sockRequest ("POST" , fmt .Sprintf ("/containers/%s/exec" , name ), map [string ]interface {}{"Cmd" : []string {"true" }})
113
128
c .Assert (err , checker .IsNil , check .Commentf (string (b )))
@@ -118,3 +133,22 @@ func createExec(c *check.C, name string) string {
118
133
c .Assert (json .Unmarshal (b , & createResp ), checker .IsNil , check .Commentf (string (b )))
119
134
return createResp .ID
120
135
}
136
+
137
+ func startExec (c * check.C , id string , code int ) {
138
+ resp , body , err := sockRequestRaw ("POST" , fmt .Sprintf ("/exec/%s/start" , id ), strings .NewReader (`{"Detach": true}` ), "application/json" )
139
+ c .Assert (err , checker .IsNil )
140
+
141
+ b , err := readBody (body )
142
+ comment := check .Commentf ("response body: %s" , b )
143
+ c .Assert (err , checker .IsNil , comment )
144
+ c .Assert (resp .StatusCode , checker .Equals , code , comment )
145
+ }
146
+
147
+ func inspectExec (c * check.C , id string , out interface {}) {
148
+ resp , body , err := sockRequestRaw ("GET" , fmt .Sprintf ("/exec/%s/json" , id ), nil , "" )
149
+ c .Assert (err , checker .IsNil )
150
+ defer body .Close ()
151
+ c .Assert (resp .StatusCode , checker .Equals , http .StatusOK )
152
+ err = json .NewDecoder (body ).Decode (out )
153
+ c .Assert (err , checker .IsNil )
154
+ }
0 commit comments