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. |