Fixes #9414
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
... | ... |
@@ -118,7 +118,10 @@ func (d *Daemon) ContainerExecCreate(job *engine.Job) engine.Status { |
118 | 118 |
return job.Error(err) |
119 | 119 |
} |
120 | 120 |
|
121 |
- config := runconfig.ExecConfigFromJob(job) |
|
121 |
+ config, err := runconfig.ExecConfigFromJob(job) |
|
122 |
+ if err != nil { |
|
123 |
+ return job.Error(err) |
|
124 |
+ } |
|
122 | 125 |
|
123 | 126 |
entrypoint, args := d.getEntrypointAndArgs(nil, config.Cmd) |
124 | 127 |
|
... | ... |
@@ -23,7 +23,7 @@ func TestContainerApiGetAll(t *testing.T) { |
23 | 23 |
t.Fatalf("Error on container creation: %v, output: %q", err, out) |
24 | 24 |
} |
25 | 25 |
|
26 |
- body, err := sockRequest("GET", "/containers/json?all=1") |
|
26 |
+ body, err := sockRequest("GET", "/containers/json?all=1", nil) |
|
27 | 27 |
if err != nil { |
28 | 28 |
t.Fatalf("GET all containers sockRequest failed: %v", err) |
29 | 29 |
} |
... | ... |
@@ -56,7 +56,7 @@ func TestContainerApiGetExport(t *testing.T) { |
56 | 56 |
t.Fatalf("Error on container creation: %v, output: %q", err, out) |
57 | 57 |
} |
58 | 58 |
|
59 |
- body, err := sockRequest("GET", "/containers/"+name+"/export") |
|
59 |
+ body, err := sockRequest("GET", "/containers/"+name+"/export", nil) |
|
60 | 60 |
if err != nil { |
61 | 61 |
t.Fatalf("GET containers/export sockRequest failed: %v", err) |
62 | 62 |
} |
... | ... |
@@ -92,7 +92,7 @@ func TestContainerApiGetChanges(t *testing.T) { |
92 | 92 |
t.Fatalf("Error on container creation: %v, output: %q", err, out) |
93 | 93 |
} |
94 | 94 |
|
95 |
- body, err := sockRequest("GET", "/containers/"+name+"/changes") |
|
95 |
+ body, err := sockRequest("GET", "/containers/"+name+"/changes", nil) |
|
96 | 96 |
if err != nil { |
97 | 97 |
t.Fatalf("GET containers/changes sockRequest failed: %v", err) |
98 | 98 |
} |
99 | 99 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,25 @@ |
0 |
+package main |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "bytes" |
|
4 |
+ "fmt" |
|
5 |
+ "os/exec" |
|
6 |
+ "testing" |
|
7 |
+) |
|
8 |
+ |
|
9 |
+// Regression test for #9414 |
|
10 |
+func TestExecApiCreateNoCmd(t *testing.T) { |
|
11 |
+ defer deleteAllContainers() |
|
12 |
+ name := "exec_test" |
|
13 |
+ runCmd := exec.Command(dockerBinary, "run", "-d", "-t", "--name", name, "busybox", "/bin/sh") |
|
14 |
+ if out, _, err := runCommandWithOutput(runCmd); err != nil { |
|
15 |
+ t.Fatal(out, err) |
|
16 |
+ } |
|
17 |
+ |
|
18 |
+ body, err := sockRequest("POST", fmt.Sprintf("/containers/%s/exec", name), map[string]interface{}{"Cmd": nil}) |
|
19 |
+ if err == nil || !bytes.Contains(body, []byte("No exec command specified")) { |
|
20 |
+ t.Fatalf("Expected error when creating exec command with no Cmd specified: %q", err) |
|
21 |
+ } |
|
22 |
+ |
|
23 |
+ logDone("exec create API - returns error when missing Cmd") |
|
24 |
+} |
... | ... |
@@ -24,7 +24,7 @@ func TestInspectApiContainerResponse(t *testing.T) { |
24 | 24 |
if testVersion != "latest" { |
25 | 25 |
endpoint = "/" + testVersion + endpoint |
26 | 26 |
} |
27 |
- body, err := sockRequest("GET", endpoint) |
|
27 |
+ body, err := sockRequest("GET", endpoint, nil) |
|
28 | 28 |
if err != nil { |
29 | 29 |
t.Fatalf("sockRequest failed for %s version: %v", testVersion, err) |
30 | 30 |
} |
... | ... |
@@ -16,7 +16,7 @@ func TestResizeApiResponse(t *testing.T) { |
16 | 16 |
cleanedContainerID := stripTrailingCharacters(out) |
17 | 17 |
|
18 | 18 |
endpoint := "/containers/" + cleanedContainerID + "/resize?h=40&w=40" |
19 |
- _, err = sockRequest("POST", endpoint) |
|
19 |
+ _, err = sockRequest("POST", endpoint, nil) |
|
20 | 20 |
if err != nil { |
21 | 21 |
t.Fatalf("resize Request failed %v", err) |
22 | 22 |
} |
... | ... |
@@ -41,7 +41,7 @@ func TestResizeApiResponseWhenContainerNotStarted(t *testing.T) { |
41 | 41 |
} |
42 | 42 |
|
43 | 43 |
endpoint := "/containers/" + cleanedContainerID + "/resize?h=40&w=40" |
44 |
- body, err := sockRequest("POST", endpoint) |
|
44 |
+ body, err := sockRequest("POST", endpoint, nil) |
|
45 | 45 |
if err == nil { |
46 | 46 |
t.Fatalf("resize should fail when container is not started") |
47 | 47 |
} |
... | ... |
@@ -1,6 +1,8 @@ |
1 | 1 |
package main |
2 | 2 |
|
3 | 3 |
import ( |
4 |
+ "bytes" |
|
5 |
+ "encoding/json" |
|
4 | 6 |
"errors" |
5 | 7 |
"fmt" |
6 | 8 |
"io" |
... | ... |
@@ -249,7 +251,7 @@ func (d *Daemon) Cmd(name string, arg ...string) (string, error) { |
249 | 249 |
return string(b), err |
250 | 250 |
} |
251 | 251 |
|
252 |
-func sockRequest(method, endpoint string) ([]byte, error) { |
|
252 |
+func sockRequest(method, endpoint string, data interface{}) ([]byte, error) { |
|
253 | 253 |
// FIX: the path to sock should not be hardcoded |
254 | 254 |
sock := filepath.Join("/", "var", "run", "docker.sock") |
255 | 255 |
c, err := net.DialTimeout("unix", sock, time.Duration(10*time.Second)) |
... | ... |
@@ -260,7 +262,12 @@ func sockRequest(method, endpoint string) ([]byte, error) { |
260 | 260 |
client := httputil.NewClientConn(c, nil) |
261 | 261 |
defer client.Close() |
262 | 262 |
|
263 |
- req, err := http.NewRequest(method, endpoint, nil) |
|
263 |
+ jsonData := bytes.NewBuffer(nil) |
|
264 |
+ if err := json.NewEncoder(jsonData).Encode(data); err != nil { |
|
265 |
+ return nil, err |
|
266 |
+ } |
|
267 |
+ |
|
268 |
+ req, err := http.NewRequest(method, endpoint, jsonData) |
|
264 | 269 |
req.Header.Set("Content-Type", "application/json") |
265 | 270 |
if err != nil { |
266 | 271 |
return nil, fmt.Errorf("could not create new request: %v", err) |
... | ... |
@@ -1,6 +1,8 @@ |
1 | 1 |
package runconfig |
2 | 2 |
|
3 | 3 |
import ( |
4 |
+ "fmt" |
|
5 |
+ |
|
4 | 6 |
"github.com/docker/docker/engine" |
5 | 7 |
flag "github.com/docker/docker/pkg/mflag" |
6 | 8 |
) |
... | ... |
@@ -17,7 +19,7 @@ type ExecConfig struct { |
17 | 17 |
Cmd []string |
18 | 18 |
} |
19 | 19 |
|
20 |
-func ExecConfigFromJob(job *engine.Job) *ExecConfig { |
|
20 |
+func ExecConfigFromJob(job *engine.Job) (*ExecConfig, error) { |
|
21 | 21 |
execConfig := &ExecConfig{ |
22 | 22 |
// TODO(vishh): Expose 'User' once it is supported. |
23 | 23 |
//User: job.Getenv("User"), |
... | ... |
@@ -28,11 +30,14 @@ func ExecConfigFromJob(job *engine.Job) *ExecConfig { |
28 | 28 |
AttachStderr: job.GetenvBool("AttachStderr"), |
29 | 29 |
AttachStdout: job.GetenvBool("AttachStdout"), |
30 | 30 |
} |
31 |
- if cmd := job.GetenvList("Cmd"); cmd != nil { |
|
32 |
- execConfig.Cmd = cmd |
|
31 |
+ cmd := job.GetenvList("Cmd") |
|
32 |
+ if len(cmd) == 0 { |
|
33 |
+ return nil, fmt.Errorf("No exec command specified") |
|
33 | 34 |
} |
34 | 35 |
|
35 |
- return execConfig |
|
36 |
+ execConfig.Cmd = cmd |
|
37 |
+ |
|
38 |
+ return execConfig, nil |
|
36 | 39 |
} |
37 | 40 |
|
38 | 41 |
func ParseExec(cmd *flag.FlagSet, args []string) (*ExecConfig, error) { |
... | ... |
@@ -47,10 +52,11 @@ func ParseExec(cmd *flag.FlagSet, args []string) (*ExecConfig, error) { |
47 | 47 |
return nil, err |
48 | 48 |
} |
49 | 49 |
parsedArgs := cmd.Args() |
50 |
- if len(parsedArgs) > 1 { |
|
51 |
- container = cmd.Arg(0) |
|
52 |
- execCmd = parsedArgs[1:] |
|
50 |
+ if len(parsedArgs) < 2 { |
|
51 |
+ return nil, fmt.Errorf("not enough arguments to create exec command") |
|
53 | 52 |
} |
53 |
+ container = cmd.Arg(0) |
|
54 |
+ execCmd = parsedArgs[1:] |
|
54 | 55 |
|
55 | 56 |
execConfig := &ExecConfig{ |
56 | 57 |
// TODO(vishh): Expose '-u' flag once it is supported. |