Browse code

Stream the cp operation on the client

Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/01/17 04:02:51
Showing 2 changed files
... ...
@@ -332,7 +332,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
332 332
 	authconfig.ServerAddress = serverAddress
333 333
 	cli.configFile.Configs[serverAddress] = authconfig
334 334
 
335
-	body, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress])
335
+	body, statusCode, err := readBody(cli.call("POST", "/auth", cli.configFile.Configs[serverAddress]))
336 336
 	if statusCode == 401 {
337 337
 		delete(cli.configFile.Configs, serverAddress)
338 338
 		auth.SaveConfig(cli.configFile)
... ...
@@ -397,7 +397,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
397 397
 		fmt.Fprintf(cli.out, "Git commit (client): %s\n", GITCOMMIT)
398 398
 	}
399 399
 
400
-	body, _, err := cli.call("GET", "/version", nil)
400
+	body, _, err := readBody(cli.call("GET", "/version", nil))
401 401
 	if err != nil {
402 402
 		return err
403 403
 	}
... ...
@@ -438,7 +438,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
438 438
 		return nil
439 439
 	}
440 440
 
441
-	body, _, err := cli.call("GET", "/info", nil)
441
+	body, _, err := readBody(cli.call("GET", "/info", nil))
442 442
 	if err != nil {
443 443
 		return err
444 444
 	}
... ...
@@ -518,7 +518,7 @@ func (cli *DockerCli) CmdStop(args ...string) error {
518 518
 
519 519
 	var encounteredError error
520 520
 	for _, name := range cmd.Args() {
521
-		_, _, err := cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil)
521
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil))
522 522
 		if err != nil {
523 523
 			fmt.Fprintf(cli.err, "%s\n", err)
524 524
 			encounteredError = fmt.Errorf("Error: failed to stop one or more containers")
... ...
@@ -545,7 +545,7 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
545 545
 
546 546
 	var encounteredError error
547 547
 	for _, name := range cmd.Args() {
548
-		_, _, err := cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil)
548
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil))
549 549
 		if err != nil {
550 550
 			fmt.Fprintf(cli.err, "%s\n", err)
551 551
 			encounteredError = fmt.Errorf("Error: failed to  restart one or more containers")
... ...
@@ -564,7 +564,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
564 564
 			if s == syscall.SIGCHLD {
565 565
 				continue
566 566
 			}
567
-			if _, _, err := cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%d", cid, s), nil); err != nil {
567
+			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%d", cid, s), nil)); err != nil {
568 568
 				utils.Debugf("Error sending signal: %s", err)
569 569
 			}
570 570
 		}
... ...
@@ -591,7 +591,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
591 591
 			return fmt.Errorf("Impossible to start and attach multiple containers at once.")
592 592
 		}
593 593
 
594
-		body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
594
+		body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil))
595 595
 		if err != nil {
596 596
 			return err
597 597
 		}
... ...
@@ -627,7 +627,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
627 627
 
628 628
 	var encounteredError error
629 629
 	for _, name := range cmd.Args() {
630
-		_, _, err := cli.call("POST", "/containers/"+name+"/start", nil)
630
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil))
631 631
 		if err != nil {
632 632
 			if !*attach || !*openStdin {
633 633
 				fmt.Fprintf(cli.err, "%s\n", err)
... ...
@@ -684,9 +684,9 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
684 684
 	status := 0
685 685
 
686 686
 	for _, name := range cmd.Args() {
687
-		obj, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
687
+		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil))
688 688
 		if err != nil {
689
-			obj, _, err = cli.call("GET", "/images/"+name+"/json", nil)
689
+			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil))
690 690
 			if err != nil {
691 691
 				if strings.Contains(err.Error(), "No such") {
692 692
 					fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
... ...
@@ -752,7 +752,7 @@ func (cli *DockerCli) CmdTop(args ...string) error {
752 752
 		val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
753 753
 	}
754 754
 
755
-	body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil)
755
+	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil))
756 756
 	if err != nil {
757 757
 		return err
758 758
 	}
... ...
@@ -787,7 +787,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
787 787
 		port = parts[0]
788 788
 		proto = parts[1]
789 789
 	}
790
-	body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
790
+	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil))
791 791
 	if err != nil {
792 792
 		return err
793 793
 	}
... ...
@@ -820,7 +820,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
820 820
 
821 821
 	var encounteredError error
822 822
 	for _, name := range cmd.Args() {
823
-		body, _, err := cli.call("DELETE", "/images/"+name, nil)
823
+		body, _, err := readBody(cli.call("DELETE", "/images/"+name, nil))
824 824
 		if err != nil {
825 825
 			fmt.Fprintf(cli.err, "%s\n", err)
826 826
 			encounteredError = fmt.Errorf("Error: failed to remove one or more images")
... ...
@@ -857,7 +857,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
857 857
 		return nil
858 858
 	}
859 859
 
860
-	body, _, err := cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil)
860
+	body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil))
861 861
 	if err != nil {
862 862
 		return err
863 863
 	}
... ...
@@ -923,7 +923,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
923 923
 
924 924
 	var encounteredError error
925 925
 	for _, name := range cmd.Args() {
926
-		_, _, err := cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil)
926
+		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil))
927 927
 		if err != nil {
928 928
 			fmt.Fprintf(cli.err, "%s\n", err)
929 929
 			encounteredError = fmt.Errorf("Error: failed to remove one or more containers")
... ...
@@ -947,7 +947,7 @@ func (cli *DockerCli) CmdKill(args ...string) error {
947 947
 
948 948
 	var encounteredError error
949 949
 	for _, name := range args {
950
-		if _, _, err := cli.call("POST", "/containers/"+name+"/kill", nil); err != nil {
950
+		if _, _, err := readBody(cli.call("POST", "/containers/"+name+"/kill", nil)); err != nil {
951 951
 			fmt.Fprintf(cli.err, "%s\n", err)
952 952
 			encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
953 953
 		} else {
... ...
@@ -1132,7 +1132,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
1132 1132
 	filter := cmd.Arg(0)
1133 1133
 
1134 1134
 	if *flViz || *flTree {
1135
-		body, _, err := cli.call("GET", "/images/json?all=1", nil)
1135
+		body, _, err := readBody(cli.call("GET", "/images/json?all=1", nil))
1136 1136
 		if err != nil {
1137 1137
 			return err
1138 1138
 		}
... ...
@@ -1202,7 +1202,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
1202 1202
 			v.Set("all", "1")
1203 1203
 		}
1204 1204
 
1205
-		body, _, err := cli.call("GET", "/images/json?"+v.Encode(), nil)
1205
+		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil))
1206 1206
 		if err != nil {
1207 1207
 			return err
1208 1208
 		}
... ...
@@ -1353,7 +1353,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1353 1353
 		v.Set("size", "1")
1354 1354
 	}
1355 1355
 
1356
-	body, _, err := cli.call("GET", "/containers/json?"+v.Encode(), nil)
1356
+	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil))
1357 1357
 	if err != nil {
1358 1358
 		return err
1359 1359
 	}
... ...
@@ -1445,7 +1445,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
1445 1445
 			return err
1446 1446
 		}
1447 1447
 	}
1448
-	body, _, err := cli.call("POST", "/commit?"+v.Encode(), config)
1448
+	body, _, err := readBody(cli.call("POST", "/commit?"+v.Encode(), config))
1449 1449
 	if err != nil {
1450 1450
 		return err
1451 1451
 	}
... ...
@@ -1520,7 +1520,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
1520 1520
 		return nil
1521 1521
 	}
1522 1522
 
1523
-	body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil)
1523
+	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil))
1524 1524
 	if err != nil {
1525 1525
 		return err
1526 1526
 	}
... ...
@@ -1555,7 +1555,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
1555 1555
 		return nil
1556 1556
 	}
1557 1557
 	name := cmd.Arg(0)
1558
-	body, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
1558
+	body, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil))
1559 1559
 	if err != nil {
1560 1560
 		return err
1561 1561
 	}
... ...
@@ -1592,7 +1592,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
1592 1592
 		return nil
1593 1593
 	}
1594 1594
 	name := cmd.Arg(0)
1595
-	body, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
1595
+	body, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil))
1596 1596
 	if err != nil {
1597 1597
 		return err
1598 1598
 	}
... ...
@@ -1659,7 +1659,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
1659 1659
 
1660 1660
 	v := url.Values{}
1661 1661
 	v.Set("term", cmd.Arg(0))
1662
-	body, _, err := cli.call("GET", "/images/search?"+v.Encode(), nil)
1662
+	body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil))
1663 1663
 	if err != nil {
1664 1664
 		return err
1665 1665
 	}
... ...
@@ -1724,7 +1724,7 @@ func (cli *DockerCli) CmdTag(args ...string) error {
1724 1724
 		v.Set("force", "1")
1725 1725
 	}
1726 1726
 
1727
-	if _, _, err := cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil); err != nil {
1727
+	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil)); err != nil {
1728 1728
 		return err
1729 1729
 	}
1730 1730
 	return nil
... ...
@@ -1973,7 +1973,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
1973 1973
 	}
1974 1974
 
1975 1975
 	//create the container
1976
-	body, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config)
1976
+	body, statusCode, err := readBody(cli.call("POST", "/containers/create?"+containerValues.Encode(), config))
1977 1977
 	//if image not found try to pull it
1978 1978
 	if statusCode == 404 {
1979 1979
 		_, tag := utils.ParseRepositoryTag(config.Image)
... ...
@@ -2010,7 +2010,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
2010 2010
 		if err = cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.err, map[string][]string{"X-Registry-Auth": registryAuthHeader}); err != nil {
2011 2011
 			return err
2012 2012
 		}
2013
-		if body, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config); err != nil {
2013
+		if body, _, err = readBody(cli.call("POST", "/containers/create?"+containerValues.Encode(), config)); err != nil {
2014 2014
 			return err
2015 2015
 		}
2016 2016
 	} else if err != nil {
... ...
@@ -2111,7 +2111,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
2111 2111
 	}
2112 2112
 
2113 2113
 	//start the container
2114
-	if _, _, err = cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig); err != nil {
2114
+	if _, _, err = readBody(cli.call("POST", "/containers/"+runResult.ID+"/start", hostConfig)); err != nil {
2115 2115
 		return err
2116 2116
 	}
2117 2117
 
... ...
@@ -2141,13 +2141,13 @@ func (cli *DockerCli) CmdRun(args ...string) error {
2141 2141
 	if autoRemove {
2142 2142
 		// Autoremove: wait for the container to finish, retrieve
2143 2143
 		// the exit code and remove the container
2144
-		if _, _, err := cli.call("POST", "/containers/"+runResult.ID+"/wait", nil); err != nil {
2144
+		if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.ID+"/wait", nil)); err != nil {
2145 2145
 			return err
2146 2146
 		}
2147 2147
 		if _, status, err = getExitCode(cli, runResult.ID); err != nil {
2148 2148
 			return err
2149 2149
 		}
2150
-		if _, _, err := cli.call("DELETE", "/containers/"+runResult.ID+"?v=1", nil); err != nil {
2150
+		if _, _, err := readBody(cli.call("DELETE", "/containers/"+runResult.ID+"?v=1", nil)); err != nil {
2151 2151
 			return err
2152 2152
 		}
2153 2153
 	} else {
... ...
@@ -2183,14 +2183,16 @@ func (cli *DockerCli) CmdCp(args ...string) error {
2183 2183
 	copyData.Resource = info[1]
2184 2184
 	copyData.HostPath = cmd.Arg(1)
2185 2185
 
2186
-	data, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData)
2186
+	stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData)
2187
+	if stream != nil {
2188
+		defer stream.Close()
2189
+	}
2187 2190
 	if err != nil {
2188 2191
 		return err
2189 2192
 	}
2190 2193
 
2191 2194
 	if statusCode == 200 {
2192
-		r := bytes.NewReader(data)
2193
-		if err := archive.Untar(r, copyData.HostPath, nil); err != nil {
2195
+		if err := archive.Untar(stream, copyData.HostPath, nil); err != nil {
2194 2196
 			return err
2195 2197
 		}
2196 2198
 	}
... ...
@@ -2232,7 +2234,7 @@ func (cli *DockerCli) CmdLoad(args ...string) error {
2232 2232
 	return nil
2233 2233
 }
2234 2234
 
2235
-func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int, error) {
2235
+func (cli *DockerCli) call(method, path string, data interface{}) (io.ReadCloser, int, error) {
2236 2236
 	var params io.Reader
2237 2237
 	if data != nil {
2238 2238
 		buf, err := json.Marshal(data)
... ...
@@ -2266,26 +2268,20 @@ func (cli *DockerCli) call(method, path string, data interface{}) ([]byte, int,
2266 2266
 	}
2267 2267
 	clientconn := httputil.NewClientConn(dial, nil)
2268 2268
 	resp, err := clientconn.Do(req)
2269
-	defer clientconn.Close()
2270 2269
 	if err != nil {
2270
+		clientconn.Close()
2271 2271
 		if strings.Contains(err.Error(), "connection refused") {
2272 2272
 			return nil, -1, ErrConnectionRefused
2273 2273
 		}
2274 2274
 		return nil, -1, err
2275 2275
 	}
2276
-	defer resp.Body.Close()
2277
-
2278
-	body, err := ioutil.ReadAll(resp.Body)
2279
-	if err != nil {
2280
-		return nil, -1, err
2281
-	}
2282
-	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
2283
-		if len(body) == 0 {
2284
-			return nil, resp.StatusCode, fmt.Errorf("Error: %s", http.StatusText(resp.StatusCode))
2276
+	wrapper := utils.NewReadCloserWrapper(resp.Body, func() error {
2277
+		if resp != nil && resp.Body != nil {
2278
+			resp.Body.Close()
2285 2279
 		}
2286
-		return nil, resp.StatusCode, fmt.Errorf("Error: %s", bytes.TrimSpace(body))
2287
-	}
2288
-	return body, resp.StatusCode, nil
2280
+		return clientconn.Close()
2281
+	})
2282
+	return wrapper, resp.StatusCode, nil
2289 2283
 }
2290 2284
 
2291 2285
 func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
... ...
@@ -2480,7 +2476,7 @@ func (cli *DockerCli) resizeTty(id string) {
2480 2480
 	v := url.Values{}
2481 2481
 	v.Set("h", strconv.Itoa(height))
2482 2482
 	v.Set("w", strconv.Itoa(width))
2483
-	if _, _, err := cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil); err != nil {
2483
+	if _, _, err := readBody(cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil)); err != nil {
2484 2484
 		utils.Errorf("Error resize: %s", err)
2485 2485
 	}
2486 2486
 }
... ...
@@ -2517,7 +2513,7 @@ func (cli *DockerCli) LoadConfigFile() (err error) {
2517 2517
 }
2518 2518
 
2519 2519
 func waitForExit(cli *DockerCli, containerId string) (int, error) {
2520
-	body, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil)
2520
+	body, _, err := readBody(cli.call("POST", "/containers/"+containerId+"/wait", nil))
2521 2521
 	if err != nil {
2522 2522
 		return -1, err
2523 2523
 	}
... ...
@@ -2532,7 +2528,7 @@ func waitForExit(cli *DockerCli, containerId string) (int, error) {
2532 2532
 // getExitCode perform an inspect on the container. It returns
2533 2533
 // the running state and the exit code.
2534 2534
 func getExitCode(cli *DockerCli, containerId string) (bool, int, error) {
2535
-	body, _, err := cli.call("GET", "/containers/"+containerId+"/json", nil)
2535
+	body, _, err := readBody(cli.call("GET", "/containers/"+containerId+"/json", nil))
2536 2536
 	if err != nil {
2537 2537
 		// If we can't connect, then the daemon probably died.
2538 2538
 		if err != ErrConnectionRefused {
... ...
@@ -2547,6 +2543,26 @@ func getExitCode(cli *DockerCli, containerId string) (bool, int, error) {
2547 2547
 	return c.State.IsRunning(), c.State.GetExitCode(), nil
2548 2548
 }
2549 2549
 
2550
+func readBody(stream io.ReadCloser, statusCode int, err error) ([]byte, int, error) {
2551
+	if stream != nil {
2552
+		defer stream.Close()
2553
+	}
2554
+	if err != nil {
2555
+		return nil, statusCode, err
2556
+	}
2557
+	body, err := ioutil.ReadAll(stream)
2558
+	if err != nil {
2559
+		return nil, -1, err
2560
+	}
2561
+	if statusCode < 200 || statusCode >= 400 {
2562
+		if len(body) == 0 {
2563
+			return nil, statusCode, fmt.Errorf("Error: %s", http.StatusText(statusCode))
2564
+		}
2565
+		return nil, statusCode, fmt.Errorf("Error: %s", bytes.TrimSpace(body))
2566
+	}
2567
+	return body, statusCode, nil
2568
+}
2569
+
2550 2570
 func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
2551 2571
 	var (
2552 2572
 		isTerminal = false
... ...
@@ -1142,3 +1142,19 @@ func CopyFile(src, dst string) (int64, error) {
1142 1142
 	defer df.Close()
1143 1143
 	return io.Copy(df, sf)
1144 1144
 }
1145
+
1146
+type readCloserWrapper struct {
1147
+	io.Reader
1148
+	closer func() error
1149
+}
1150
+
1151
+func (r *readCloserWrapper) Close() error {
1152
+	return r.closer()
1153
+}
1154
+
1155
+func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser {
1156
+	return &readCloserWrapper{
1157
+		Reader: r,
1158
+		closer: closer,
1159
+	}
1160
+}