Browse code

Fix for issue 9922: private registry search with auth returns 401

Signed-off-by: Don Kjer <don.kjer@gmail.com>
(cherry picked from commit 6b2eeaf8965bac07022752c411b1f8a0f35f9571)

Docker-DCO-1.1-Signed-off-by: Jessie Frazelle <princess@docker.com> (github: jfrazelle)

Docker-DCO-1.1-Signed-off-by: Jessie Frazelle <hugs@docker.com> (github: jfrazelle)

Don Kjer authored on 2015/01/13 04:56:01
Showing 5 changed files
... ...
@@ -441,7 +441,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
441 441
 	authconfig.ServerAddress = serverAddress
442 442
 	cli.configFile.Configs[serverAddress] = authconfig
443 443
 
444
-	stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
444
+	stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], nil)
445 445
 	if statusCode == 401 {
446 446
 		delete(cli.configFile.Configs, serverAddress)
447 447
 		registry.SaveConfig(cli.configFile)
... ...
@@ -527,7 +527,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
527 527
 	}
528 528
 	fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH)
529 529
 
530
-	body, _, err := readBody(cli.call("GET", "/version", nil, false))
530
+	body, _, err := readBody(cli.call("GET", "/version", nil, nil))
531 531
 	if err != nil {
532 532
 		return err
533 533
 	}
... ...
@@ -559,7 +559,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
559 559
 	cmd.Require(flag.Exact, 0)
560 560
 	utils.ParseFlags(cmd, args, false)
561 561
 
562
-	body, _, err := readBody(cli.call("GET", "/info", nil, false))
562
+	body, _, err := readBody(cli.call("GET", "/info", nil, nil))
563 563
 	if err != nil {
564 564
 		return err
565 565
 	}
... ...
@@ -696,7 +696,7 @@ func (cli *DockerCli) CmdStop(args ...string) error {
696 696
 
697 697
 	var encounteredError error
698 698
 	for _, name := range cmd.Args() {
699
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false))
699
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, nil))
700 700
 		if err != nil {
701 701
 			fmt.Fprintf(cli.err, "%s\n", err)
702 702
 			encounteredError = fmt.Errorf("Error: failed to stop one or more containers")
... ...
@@ -719,7 +719,7 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
719 719
 
720 720
 	var encounteredError error
721 721
 	for _, name := range cmd.Args() {
722
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
722
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, nil))
723 723
 		if err != nil {
724 724
 			fmt.Fprintf(cli.err, "%s\n", err)
725 725
 			encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
... ...
@@ -748,7 +748,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
748 748
 			if sig == "" {
749 749
 				log.Errorf("Unsupported signal: %v. Discarding.", s)
750 750
 			}
751
-			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
751
+			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, nil)); err != nil {
752 752
 				log.Debugf("Error sending signal: %s", err)
753 753
 			}
754 754
 		}
... ...
@@ -774,7 +774,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
774 774
 			return fmt.Errorf("You cannot start and attach multiple containers at once.")
775 775
 		}
776 776
 
777
-		stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
777
+		stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
778 778
 		if err != nil {
779 779
 			return err
780 780
 		}
... ...
@@ -834,7 +834,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
834 834
 
835 835
 	var encounteredError error
836 836
 	for _, name := range cmd.Args() {
837
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false))
837
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, nil))
838 838
 		if err != nil {
839 839
 			if !*attach && !*openStdin {
840 840
 				// attach and openStdin is false means it could be starting multiple containers
... ...
@@ -882,7 +882,7 @@ func (cli *DockerCli) CmdUnpause(args ...string) error {
882 882
 
883 883
 	var encounteredError error
884 884
 	for _, name := range cmd.Args() {
885
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, false)); err != nil {
885
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, nil)); err != nil {
886 886
 			fmt.Fprintf(cli.err, "%s\n", err)
887 887
 			encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name)
888 888
 		} else {
... ...
@@ -899,7 +899,7 @@ func (cli *DockerCli) CmdPause(args ...string) error {
899 899
 
900 900
 	var encounteredError error
901 901
 	for _, name := range cmd.Args() {
902
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, false)); err != nil {
902
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, nil)); err != nil {
903 903
 			fmt.Fprintf(cli.err, "%s\n", err)
904 904
 			encounteredError = fmt.Errorf("Error: failed to pause container named %s", name)
905 905
 		} else {
... ...
@@ -922,7 +922,7 @@ func (cli *DockerCli) CmdRename(args ...string) error {
922 922
 	old_name := cmd.Arg(0)
923 923
 	new_name := cmd.Arg(1)
924 924
 
925
-	if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", old_name, new_name), nil, false)); err != nil {
925
+	if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", old_name, new_name), nil, nil)); err != nil {
926 926
 		fmt.Fprintf(cli.err, "%s\n", err)
927 927
 		return fmt.Errorf("Error: failed to rename container named %s", old_name)
928 928
 	}
... ...
@@ -951,7 +951,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
951 951
 	status := 0
952 952
 
953 953
 	for _, name := range cmd.Args() {
954
-		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
954
+		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, nil))
955 955
 		if err != nil {
956 956
 			if strings.Contains(err.Error(), "Too many") {
957 957
 				fmt.Fprintf(cli.err, "Error: %v", err)
... ...
@@ -959,7 +959,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
959 959
 				continue
960 960
 			}
961 961
 
962
-			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
962
+			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
963 963
 			if err != nil {
964 964
 				if strings.Contains(err.Error(), "No such") {
965 965
 					fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
... ...
@@ -1022,7 +1022,7 @@ func (cli *DockerCli) CmdTop(args ...string) error {
1022 1022
 		val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
1023 1023
 	}
1024 1024
 
1025
-	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false)
1025
+	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, nil)
1026 1026
 	if err != nil {
1027 1027
 		return err
1028 1028
 	}
... ...
@@ -1048,7 +1048,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
1048 1048
 	cmd.Require(flag.Min, 1)
1049 1049
 	utils.ParseFlags(cmd, args, true)
1050 1050
 
1051
-	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
1051
+	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
1052 1052
 	if err != nil {
1053 1053
 		return err
1054 1054
 	}
... ...
@@ -1113,7 +1113,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
1113 1113
 
1114 1114
 	var encounteredError error
1115 1115
 	for _, name := range cmd.Args() {
1116
-		body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
1116
+		body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, nil))
1117 1117
 		if err != nil {
1118 1118
 			fmt.Fprintf(cli.err, "%s\n", err)
1119 1119
 			encounteredError = fmt.Errorf("Error: failed to remove one or more images")
... ...
@@ -1144,7 +1144,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
1144 1144
 
1145 1145
 	utils.ParseFlags(cmd, args, true)
1146 1146
 
1147
-	body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false))
1147
+	body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, nil))
1148 1148
 	if err != nil {
1149 1149
 		return err
1150 1150
 	}
... ...
@@ -1211,7 +1211,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
1211 1211
 
1212 1212
 	var encounteredError error
1213 1213
 	for _, name := range cmd.Args() {
1214
-		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false))
1214
+		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, nil))
1215 1215
 		if err != nil {
1216 1216
 			fmt.Fprintf(cli.err, "%s\n", err)
1217 1217
 			encounteredError = fmt.Errorf("Error: failed to remove one or more containers")
... ...
@@ -1232,7 +1232,7 @@ func (cli *DockerCli) CmdKill(args ...string) error {
1232 1232
 
1233 1233
 	var encounteredError error
1234 1234
 	for _, name := range cmd.Args() {
1235
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil {
1235
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, nil)); err != nil {
1236 1236
 			fmt.Fprintf(cli.err, "%s\n", err)
1237 1237
 			encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
1238 1238
 		} else {
... ...
@@ -1317,32 +1317,8 @@ func (cli *DockerCli) CmdPush(args ...string) error {
1317 1317
 	v := url.Values{}
1318 1318
 	v.Set("tag", tag)
1319 1319
 
1320
-	push := func(authConfig registry.AuthConfig) error {
1321
-		buf, err := json.Marshal(authConfig)
1322
-		if err != nil {
1323
-			return err
1324
-		}
1325
-		registryAuthHeader := []string{
1326
-			base64.URLEncoding.EncodeToString(buf),
1327
-		}
1328
-
1329
-		return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
1330
-			"X-Registry-Auth": registryAuthHeader,
1331
-		})
1332
-	}
1333
-
1334
-	if err := push(authConfig); err != nil {
1335
-		if strings.Contains(err.Error(), "Status 401") {
1336
-			fmt.Fprintln(cli.out, "\nPlease login prior to push:")
1337
-			if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
1338
-				return err
1339
-			}
1340
-			authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
1341
-			return push(authConfig)
1342
-		}
1343
-		return err
1344
-	}
1345
-	return nil
1320
+	_, _, err = cli.clientRequestAttemptLogin("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, repoInfo.Index, "push")
1321
+	return err
1346 1322
 }
1347 1323
 
1348 1324
 func (cli *DockerCli) CmdPull(args ...string) error {
... ...
@@ -1375,36 +1351,8 @@ func (cli *DockerCli) CmdPull(args ...string) error {
1375 1375
 
1376 1376
 	cli.LoadConfigFile()
1377 1377
 
1378
-	// Resolve the Auth config relevant for this server
1379
-	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
1380
-
1381
-	pull := func(authConfig registry.AuthConfig) error {
1382
-		buf, err := json.Marshal(authConfig)
1383
-		if err != nil {
1384
-			return err
1385
-		}
1386
-		registryAuthHeader := []string{
1387
-			base64.URLEncoding.EncodeToString(buf),
1388
-		}
1389
-
1390
-		return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
1391
-			"X-Registry-Auth": registryAuthHeader,
1392
-		})
1393
-	}
1394
-
1395
-	if err := pull(authConfig); err != nil {
1396
-		if strings.Contains(err.Error(), "Status 401") {
1397
-			fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
1398
-			if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
1399
-				return err
1400
-			}
1401
-			authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
1402
-			return pull(authConfig)
1403
-		}
1404
-		return err
1405
-	}
1406
-
1407
-	return nil
1378
+	_, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull")
1379
+	return err
1408 1380
 }
1409 1381
 
1410 1382
 func (cli *DockerCli) CmdImages(args ...string) error {
... ...
@@ -1448,7 +1396,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
1448 1448
 			v.Set("filters", filterJson)
1449 1449
 		}
1450 1450
 
1451
-		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
1451
+		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil))
1452 1452
 		if err != nil {
1453 1453
 			return err
1454 1454
 		}
... ...
@@ -1526,7 +1474,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
1526 1526
 			v.Set("all", "1")
1527 1527
 		}
1528 1528
 
1529
-		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
1529
+		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil))
1530 1530
 
1531 1531
 		if err != nil {
1532 1532
 			return err
... ...
@@ -1724,7 +1672,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1724 1724
 		v.Set("filters", filterJson)
1725 1725
 	}
1726 1726
 
1727
-	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false))
1727
+	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, nil))
1728 1728
 	if err != nil {
1729 1729
 		return err
1730 1730
 	}
... ...
@@ -1865,7 +1813,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
1865 1865
 			return err
1866 1866
 		}
1867 1867
 	}
1868
-	stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false)
1868
+	stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, nil)
1869 1869
 	if err != nil {
1870 1870
 		return err
1871 1871
 	}
... ...
@@ -1976,7 +1924,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
1976 1976
 
1977 1977
 	utils.ParseFlags(cmd, args, true)
1978 1978
 
1979
-	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false))
1979
+	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, nil))
1980 1980
 
1981 1981
 	if err != nil {
1982 1982
 		return err
... ...
@@ -2014,7 +1962,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
2014 2014
 
2015 2015
 	name := cmd.Arg(0)
2016 2016
 
2017
-	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
2017
+	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, nil)
2018 2018
 	if err != nil {
2019 2019
 		return err
2020 2020
 	}
... ...
@@ -2055,7 +2003,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
2055 2055
 	utils.ParseFlags(cmd, args, true)
2056 2056
 	name := cmd.Arg(0)
2057 2057
 
2058
-	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
2058
+	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, nil)
2059 2059
 	if err != nil {
2060 2060
 		return err
2061 2061
 	}
... ...
@@ -2126,16 +2074,27 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
2126 2126
 
2127 2127
 	utils.ParseFlags(cmd, args, true)
2128 2128
 
2129
+	name := cmd.Arg(0)
2129 2130
 	v := url.Values{}
2130
-	v.Set("term", cmd.Arg(0))
2131
+	v.Set("term", name)
2132
+
2133
+	// Resolve the Repository name from fqn to hostname + name
2134
+	taglessRemote, _ := parsers.ParseRepositoryTag(name)
2135
+	repoInfo, err := registry.ParseRepositoryInfo(taglessRemote)
2136
+	if err != nil {
2137
+		return err
2138
+	}
2131 2139
 
2132
-	body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true))
2140
+	cli.LoadConfigFile()
2133 2141
 
2142
+	body, statusCode, errReq := cli.clientRequestAttemptLogin("GET", "/images/search?"+v.Encode(), nil, nil, repoInfo.Index, "search")
2143
+	rawBody, _, err := readBody(body, statusCode, errReq)
2134 2144
 	if err != nil {
2135 2145
 		return err
2136 2146
 	}
2147
+
2137 2148
 	outs := engine.NewTable("star_count", 0)
2138
-	if _, err := outs.ReadListFrom(body); err != nil {
2149
+	if _, err := outs.ReadListFrom(rawBody); err != nil {
2139 2150
 		return err
2140 2151
 	}
2141 2152
 	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
... ...
@@ -2190,7 +2149,7 @@ func (cli *DockerCli) CmdTag(args ...string) error {
2190 2190
 		v.Set("force", "1")
2191 2191
 	}
2192 2192
 
2193
-	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil {
2193
+	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, nil)); err != nil {
2194 2194
 		return err
2195 2195
 	}
2196 2196
 	return nil
... ...
@@ -2292,7 +2251,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
2292 2292
 	}
2293 2293
 
2294 2294
 	//create the container
2295
-	stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false)
2295
+	stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil)
2296 2296
 	//if image not found try to pull it
2297 2297
 	if statusCode == 404 {
2298 2298
 		repo, tag := parsers.ParseRepositoryTag(config.Image)
... ...
@@ -2306,7 +2265,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
2306 2306
 			return nil, err
2307 2307
 		}
2308 2308
 		// Retry
2309
-		if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil {
2309
+		if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil); err != nil {
2310 2310
 			return nil, err
2311 2311
 		}
2312 2312
 	} else if err != nil {
... ...
@@ -2496,7 +2455,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
2496 2496
 	}
2497 2497
 
2498 2498
 	//start the container
2499
-	if _, _, err = readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, false)); err != nil {
2499
+	if _, _, err = readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, nil)); err != nil {
2500 2500
 		return err
2501 2501
 	}
2502 2502
 
... ...
@@ -2526,13 +2485,13 @@ func (cli *DockerCli) CmdRun(args ...string) error {
2526 2526
 	if *flAutoRemove {
2527 2527
 		// Autoremove: wait for the container to finish, retrieve
2528 2528
 		// the exit code and remove the container
2529
-		if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, false)); err != nil {
2529
+		if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, nil)); err != nil {
2530 2530
 			return err
2531 2531
 		}
2532 2532
 		if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
2533 2533
 			return err
2534 2534
 		}
2535
-		if _, _, err := readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, false)); err != nil {
2535
+		if _, _, err := readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, nil)); err != nil {
2536 2536
 			return err
2537 2537
 		}
2538 2538
 	} else {
... ...
@@ -2572,7 +2531,7 @@ func (cli *DockerCli) CmdCp(args ...string) error {
2572 2572
 	copyData.Set("Resource", info[1])
2573 2573
 	copyData.Set("HostPath", cmd.Arg(1))
2574 2574
 
2575
-	stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false)
2575
+	stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, nil)
2576 2576
 	if stream != nil {
2577 2577
 		defer stream.Close()
2578 2578
 	}
... ...
@@ -2667,7 +2626,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
2667 2667
 		return &utils.StatusError{StatusCode: 1}
2668 2668
 	}
2669 2669
 
2670
-	stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, false)
2670
+	stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, nil)
2671 2671
 	if err != nil {
2672 2672
 		return err
2673 2673
 	}
... ...
@@ -2689,7 +2648,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
2689 2689
 			return err
2690 2690
 		}
2691 2691
 	} else {
2692
-		if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil {
2692
+		if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, nil)); err != nil {
2693 2693
 			return err
2694 2694
 		}
2695 2695
 		// For now don't print this - wait for when we support exec wait()
... ...
@@ -2781,7 +2740,7 @@ type containerStats struct {
2781 2781
 }
2782 2782
 
2783 2783
 func (s *containerStats) Collect(cli *DockerCli) {
2784
-	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, false)
2784
+	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, nil)
2785 2785
 	if err != nil {
2786 2786
 		s.err = err
2787 2787
 		return
... ...
@@ -54,124 +54,144 @@ func (cli *DockerCli) encodeData(data interface{}) (*bytes.Buffer, error) {
54 54
 	return params, nil
55 55
 }
56 56
 
57
-func (cli *DockerCli) call(method, path string, data interface{}, passAuthInfo bool) (io.ReadCloser, int, error) {
58
-	params, err := cli.encodeData(data)
59
-	if err != nil {
60
-		return nil, -1, err
57
+func (cli *DockerCli) clientRequest(method, path string, in io.Reader, headers map[string][]string) (io.ReadCloser, string, int, error) {
58
+	expectedPayload := (method == "POST" || method == "PUT")
59
+	if expectedPayload && in == nil {
60
+		in = bytes.NewReader([]byte{})
61 61
 	}
62
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), params)
62
+	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
63 63
 	if err != nil {
64
-		return nil, -1, err
65
-	}
66
-	if passAuthInfo {
67
-		cli.LoadConfigFile()
68
-		// Resolve the Auth config relevant for this server
69
-		authConfig := cli.configFile.Configs[registry.IndexServerAddress()]
70
-		getHeaders := func(authConfig registry.AuthConfig) (map[string][]string, error) {
71
-			buf, err := json.Marshal(authConfig)
72
-			if err != nil {
73
-				return nil, err
74
-			}
75
-			registryAuthHeader := []string{
76
-				base64.URLEncoding.EncodeToString(buf),
77
-			}
78
-			return map[string][]string{"X-Registry-Auth": registryAuthHeader}, nil
79
-		}
80
-		if headers, err := getHeaders(authConfig); err == nil && headers != nil {
81
-			for k, v := range headers {
82
-				req.Header[k] = v
83
-			}
84
-		}
64
+		return nil, "", -1, err
85 65
 	}
86 66
 	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
87 67
 	req.URL.Host = cli.addr
88 68
 	req.URL.Scheme = cli.scheme
89
-	if data != nil {
90
-		req.Header.Set("Content-Type", "application/json")
91
-	} else if method == "POST" {
69
+	if headers != nil {
70
+		for k, v := range headers {
71
+			req.Header[k] = v
72
+		}
73
+	}
74
+	if expectedPayload && req.Header.Get("Content-Type") == "" {
92 75
 		req.Header.Set("Content-Type", "text/plain")
93 76
 	}
77
+
94 78
 	resp, err := cli.HTTPClient().Do(req)
79
+	statusCode := -1
80
+	if resp != nil {
81
+		statusCode = resp.StatusCode
82
+	}
95 83
 	if err != nil {
96 84
 		if strings.Contains(err.Error(), "connection refused") {
97
-			return nil, -1, ErrConnectionRefused
85
+			return nil, "", statusCode, ErrConnectionRefused
98 86
 		}
99 87
 
100 88
 		if cli.tlsConfig == nil {
101
-			return nil, -1, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
89
+			return nil, "", statusCode, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
102 90
 		}
103
-		return nil, -1, fmt.Errorf("An error occurred trying to connect: %v", err)
104 91
 
92
+		return nil, "", statusCode, fmt.Errorf("An error occurred trying to connect: %v", err)
105 93
 	}
106 94
 
107
-	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
95
+	if statusCode < 200 || statusCode >= 400 {
108 96
 		body, err := ioutil.ReadAll(resp.Body)
109 97
 		if err != nil {
110
-			return nil, -1, err
98
+			return nil, "", statusCode, err
111 99
 		}
112 100
 		if len(body) == 0 {
113
-			return nil, resp.StatusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(resp.StatusCode), req.URL)
101
+			return nil, "", statusCode, fmt.Errorf("Error: request returned %s for API route and version %s, check if the server supports the requested API version", http.StatusText(statusCode), req.URL)
114 102
 		}
115
-		return nil, resp.StatusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
103
+		return nil, "", statusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
116 104
 	}
117 105
 
118
-	return resp.Body, resp.StatusCode, nil
106
+	return resp.Body, resp.Header.Get("Content-Type"), statusCode, nil
119 107
 }
120 108
 
121
-func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
122
-	return cli.streamHelper(method, path, true, in, out, nil, headers)
123
-}
109
+func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reader, out io.Writer, index *registry.IndexInfo, cmdName string) (io.ReadCloser, int, error) {
110
+	cmdAttempt := func(authConfig registry.AuthConfig) (io.ReadCloser, int, error) {
111
+		buf, err := json.Marshal(authConfig)
112
+		if err != nil {
113
+			return nil, -1, err
114
+		}
115
+		registryAuthHeader := []string{
116
+			base64.URLEncoding.EncodeToString(buf),
117
+		}
124 118
 
125
-func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error {
126
-	if (method == "POST" || method == "PUT") && in == nil {
127
-		in = bytes.NewReader([]byte{})
119
+		// begin the request
120
+		body, contentType, statusCode, err := cli.clientRequest(method, path, in, map[string][]string{
121
+			"X-Registry-Auth": registryAuthHeader,
122
+		})
123
+		if err == nil && out != nil {
124
+			// If we are streaming output, complete the stream since
125
+			// errors may not appear until later.
126
+			err = cli.streamBody(body, contentType, true, out, nil)
127
+		}
128
+		if err != nil {
129
+			// Since errors in a stream appear after status 200 has been written,
130
+			// we may need to change the status code.
131
+			if strings.Contains(err.Error(), "Authentication is required") ||
132
+				strings.Contains(err.Error(), "Status 401") ||
133
+				strings.Contains(err.Error(), "status code 401") {
134
+				statusCode = http.StatusUnauthorized
135
+			}
136
+		}
137
+		return body, statusCode, err
128 138
 	}
129 139
 
130
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
131
-	if err != nil {
132
-		return err
140
+	// Resolve the Auth config relevant for this server
141
+	authConfig := cli.configFile.ResolveAuthConfig(index)
142
+	body, statusCode, err := cmdAttempt(authConfig)
143
+	if statusCode == http.StatusUnauthorized {
144
+		fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
145
+		if err = cli.CmdLogin(index.GetAuthConfigKey()); err != nil {
146
+			return nil, -1, err
147
+		}
148
+		authConfig = cli.configFile.ResolveAuthConfig(index)
149
+		return cmdAttempt(authConfig)
133 150
 	}
134
-	req.Header.Set("User-Agent", "Docker-Client/"+dockerversion.VERSION)
135
-	req.URL.Host = cli.addr
136
-	req.URL.Scheme = cli.scheme
137
-	if method == "POST" {
138
-		req.Header.Set("Content-Type", "text/plain")
151
+	return body, statusCode, err
152
+}
153
+
154
+func (cli *DockerCli) call(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
155
+	params, err := cli.encodeData(data)
156
+	if err != nil {
157
+		return nil, -1, err
139 158
 	}
140 159
 
141
-	if headers != nil {
142
-		for k, v := range headers {
143
-			req.Header[k] = v
160
+	if data != nil {
161
+		if headers == nil {
162
+			headers = make(map[string][]string)
144 163
 		}
164
+		headers["Content-Type"] = []string{"application/json"}
145 165
 	}
146
-	resp, err := cli.HTTPClient().Do(req)
166
+
167
+	body, _, statusCode, err := cli.clientRequest(method, path, params, headers)
168
+	return body, statusCode, err
169
+}
170
+func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
171
+	return cli.streamHelper(method, path, true, in, out, nil, headers)
172
+}
173
+
174
+func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error {
175
+	body, contentType, _, err := cli.clientRequest(method, path, in, headers)
147 176
 	if err != nil {
148
-		if strings.Contains(err.Error(), "connection refused") {
149
-			return fmt.Errorf("Cannot connect to the Docker daemon. Is 'docker -d' running on this host?")
150
-		}
151 177
 		return err
152 178
 	}
153
-	defer resp.Body.Close()
179
+	return cli.streamBody(body, contentType, setRawTerminal, stdout, stderr)
180
+}
154 181
 
155
-	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
156
-		body, err := ioutil.ReadAll(resp.Body)
157
-		if err != nil {
158
-			return err
159
-		}
160
-		if len(body) == 0 {
161
-			return fmt.Errorf("Error :%s", http.StatusText(resp.StatusCode))
162
-		}
163
-		return fmt.Errorf("Error: %s", bytes.TrimSpace(body))
164
-	}
182
+func (cli *DockerCli) streamBody(body io.ReadCloser, contentType string, setRawTerminal bool, stdout, stderr io.Writer) error {
183
+	defer body.Close()
165 184
 
166
-	if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
167
-		return utils.DisplayJSONMessagesStream(resp.Body, stdout, cli.outFd, cli.isTerminalOut)
185
+	if api.MatchesContentType(contentType, "application/json") {
186
+		return utils.DisplayJSONMessagesStream(body, stdout, cli.outFd, cli.isTerminalOut)
168 187
 	}
169 188
 	if stdout != nil || stderr != nil {
170 189
 		// When TTY is ON, use regular copy
190
+		var err error
171 191
 		if setRawTerminal {
172
-			_, err = io.Copy(stdout, resp.Body)
192
+			_, err = io.Copy(stdout, body)
173 193
 		} else {
174
-			_, err = stdcopy.StdCopy(stdout, stderr, resp.Body)
194
+			_, err = stdcopy.StdCopy(stdout, stderr, body)
175 195
 		}
176 196
 		log.Debugf("[stream] End of stdout")
177 197
 		return err
... ...
@@ -195,13 +215,13 @@ func (cli *DockerCli) resizeTty(id string, isExec bool) {
195 195
 		path = "/exec/" + id + "/resize?"
196 196
 	}
197 197
 
198
-	if _, _, err := readBody(cli.call("POST", path+v.Encode(), nil, false)); err != nil {
198
+	if _, _, err := readBody(cli.call("POST", path+v.Encode(), nil, nil)); err != nil {
199 199
 		log.Debugf("Error resize: %s", err)
200 200
 	}
201 201
 }
202 202
 
203
-func waitForExit(cli *DockerCli, containerId string) (int, error) {
204
-	stream, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil, false)
203
+func waitForExit(cli *DockerCli, containerID string) (int, error) {
204
+	stream, _, err := cli.call("POST", "/containers/"+containerID+"/wait", nil, nil)
205 205
 	if err != nil {
206 206
 		return -1, err
207 207
 	}
... ...
@@ -215,8 +235,8 @@ func waitForExit(cli *DockerCli, containerId string) (int, error) {
215 215
 
216 216
 // getExitCode perform an inspect on the container. It returns
217 217
 // the running state and the exit code.
218
-func getExitCode(cli *DockerCli, containerId string) (bool, int, error) {
219
-	stream, _, err := cli.call("GET", "/containers/"+containerId+"/json", nil, false)
218
+func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
219
+	stream, _, err := cli.call("GET", "/containers/"+containerID+"/json", nil, nil)
220 220
 	if err != nil {
221 221
 		// If we can't connect, then the daemon probably died.
222 222
 		if err != ErrConnectionRefused {
... ...
@@ -236,8 +256,8 @@ func getExitCode(cli *DockerCli, containerId string) (bool, int, error) {
236 236
 
237 237
 // getExecExitCode perform an inspect on the exec command. It returns
238 238
 // the running state and the exit code.
239
-func getExecExitCode(cli *DockerCli, execId string) (bool, int, error) {
240
-	stream, _, err := cli.call("GET", "/exec/"+execId+"/json", nil, false)
239
+func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
240
+	stream, _, err := cli.call("GET", "/exec/"+execID+"/json", nil, nil)
241 241
 	if err != nil {
242 242
 		// If we can't connect, then the daemon probably died.
243 243
 		if err != ErrConnectionRefused {
... ...
@@ -1,7 +1,6 @@
1 1
 package registry
2 2
 
3 3
 import (
4
-	"crypto/tls"
5 4
 	"encoding/base64"
6 5
 	"encoding/json"
7 6
 	"errors"
... ...
@@ -71,21 +70,7 @@ func (auth *RequestAuthorization) getToken() (string, error) {
71 71
 		return auth.tokenCache, nil
72 72
 	}
73 73
 
74
-	tlsConfig := tls.Config{
75
-		MinVersion: tls.VersionTLS10,
76
-	}
77
-	if !auth.registryEndpoint.IsSecure {
78
-		tlsConfig.InsecureSkipVerify = true
79
-	}
80
-
81
-	client := &http.Client{
82
-		Transport: &http.Transport{
83
-			DisableKeepAlives: true,
84
-			Proxy:             http.ProxyFromEnvironment,
85
-			TLSClientConfig:   &tlsConfig,
86
-		},
87
-		CheckRedirect: AddRequiredHeadersToRedirectedRequests,
88
-	}
74
+	client := auth.registryEndpoint.HTTPClient()
89 75
 	factory := HTTPRequestFactory(nil)
90 76
 
91 77
 	for _, challenge := range auth.registryEndpoint.AuthChallenges {
... ...
@@ -252,16 +237,10 @@ func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HT
252 252
 // loginV1 tries to register/login to the v1 registry server.
253 253
 func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
254 254
 	var (
255
-		status  string
256
-		reqBody []byte
257
-		err     error
258
-		client  = &http.Client{
259
-			Transport: &http.Transport{
260
-				DisableKeepAlives: true,
261
-				Proxy:             http.ProxyFromEnvironment,
262
-			},
263
-			CheckRedirect: AddRequiredHeadersToRedirectedRequests,
264
-		}
255
+		status        string
256
+		reqBody       []byte
257
+		err           error
258
+		client        = registryEndpoint.HTTPClient()
265 259
 		reqStatusCode = 0
266 260
 		serverAddress = authConfig.ServerAddress
267 261
 	)
... ...
@@ -285,7 +264,7 @@ func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.
285 285
 
286 286
 	// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
287 287
 	b := strings.NewReader(string(jsonBody))
288
-	req1, err := http.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
288
+	req1, err := client.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
289 289
 	if err != nil {
290 290
 		return "", fmt.Errorf("Server Error: %s", err)
291 291
 	}
... ...
@@ -371,26 +350,10 @@ func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.
371 371
 // is to be determined.
372 372
 func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
373 373
 	log.Debugf("attempting v2 login to registry endpoint %s", registryEndpoint)
374
-
375
-	tlsConfig := tls.Config{
376
-		MinVersion: tls.VersionTLS10,
377
-	}
378
-	if !registryEndpoint.IsSecure {
379
-		tlsConfig.InsecureSkipVerify = true
380
-	}
381
-
382
-	client := &http.Client{
383
-		Transport: &http.Transport{
384
-			DisableKeepAlives: true,
385
-			Proxy:             http.ProxyFromEnvironment,
386
-			TLSClientConfig:   &tlsConfig,
387
-		},
388
-		CheckRedirect: AddRequiredHeadersToRedirectedRequests,
389
-	}
390
-
391 374
 	var (
392 375
 		err       error
393 376
 		allErrors []error
377
+		client    = registryEndpoint.HTTPClient()
394 378
 	)
395 379
 
396 380
 	for _, challenge := range registryEndpoint.AuthChallenges {
... ...
@@ -1,6 +1,7 @@
1 1
 package registry
2 2
 
3 3
 import (
4
+	"crypto/tls"
4 5
 	"encoding/json"
5 6
 	"fmt"
6 7
 	"io/ioutil"
... ...
@@ -262,3 +263,20 @@ HeaderLoop:
262 262
 
263 263
 	return RegistryInfo{}, fmt.Errorf("v2 registry endpoint returned status %d: %q", resp.StatusCode, http.StatusText(resp.StatusCode))
264 264
 }
265
+
266
+func (e *Endpoint) HTTPClient() *http.Client {
267
+	tlsConfig := tls.Config{
268
+		MinVersion: tls.VersionTLS10,
269
+	}
270
+	if !e.IsSecure {
271
+		tlsConfig.InsecureSkipVerify = true
272
+	}
273
+	return &http.Client{
274
+		Transport: &http.Transport{
275
+			DisableKeepAlives: true,
276
+			Proxy:             http.ProxyFromEnvironment,
277
+			TLSClientConfig:   &tlsConfig,
278
+		},
279
+		CheckRedirect: AddRequiredHeadersToRedirectedRequests,
280
+	}
281
+}
... ...
@@ -511,6 +511,10 @@ func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate
511 511
 	}
512 512
 	defer res.Body.Close()
513 513
 
514
+	if res.StatusCode == 401 {
515
+		return nil, errLoginRequired
516
+	}
517
+
514 518
 	var tokens, endpoints []string
515 519
 	if !validate {
516 520
 		if res.StatusCode != 200 && res.StatusCode != 201 {