Browse code

Check for no `Cmd` on exec create endpoint

Fixes #9414

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2014/12/02 02:16:49
Showing 7 changed files
... ...
@@ -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.