Browse code

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

Signed-off-by: Don Kjer <don.kjer@gmail.com>

Don Kjer authored on 2015/01/13 04:56:01
Showing 35 changed files
... ...
@@ -26,7 +26,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
26 26
 	utils.ParseFlags(cmd, args, true)
27 27
 	name := cmd.Arg(0)
28 28
 
29
-	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
29
+	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, nil)
30 30
 	if err != nil {
31 31
 		return err
32 32
 	}
... ...
@@ -66,7 +66,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
66 66
 			return err
67 67
 		}
68 68
 	}
69
-	stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, false)
69
+	stream, _, err := cli.call("POST", "/commit?"+v.Encode(), config, nil)
70 70
 	if err != nil {
71 71
 		return err
72 72
 	}
... ...
@@ -32,7 +32,7 @@ func (cli *DockerCli) CmdCp(args ...string) error {
32 32
 	copyData.Set("Resource", info[1])
33 33
 	copyData.Set("HostPath", cmd.Arg(1))
34 34
 
35
-	stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, false)
35
+	stream, statusCode, err := cli.call("POST", "/containers/"+info[0]+"/copy", copyData, nil)
36 36
 	if stream != nil {
37 37
 		defer stream.Close()
38 38
 	}
... ...
@@ -92,7 +92,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
92 92
 	}
93 93
 
94 94
 	//create the container
95
-	stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false)
95
+	stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil)
96 96
 	//if image not found try to pull it
97 97
 	if statusCode == 404 {
98 98
 		repo, tag := parsers.ParseRepositoryTag(config.Image)
... ...
@@ -106,7 +106,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
106 106
 			return nil, err
107 107
 		}
108 108
 		// Retry
109
-		if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, false); err != nil {
109
+		if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), mergedConfig, nil); err != nil {
110 110
 			return nil, err
111 111
 		}
112 112
 	} else if err != nil {
... ...
@@ -20,7 +20,7 @@ func (cli *DockerCli) CmdDiff(args ...string) error {
20 20
 
21 21
 	utils.ParseFlags(cmd, args, true)
22 22
 
23
-	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, false))
23
+	body, _, err := readBody(cli.call("GET", "/containers/"+cmd.Arg(0)+"/changes", nil, nil))
24 24
 
25 25
 	if err != nil {
26 26
 		return err
... ...
@@ -24,7 +24,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
24 24
 		return &utils.StatusError{StatusCode: 1}
25 25
 	}
26 26
 
27
-	stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, false)
27
+	stream, _, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, nil)
28 28
 	if err != nil {
29 29
 		return err
30 30
 	}
... ...
@@ -49,7 +49,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
49 49
 			return err
50 50
 		}
51 51
 	} else {
52
-		if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, false)); err != nil {
52
+		if _, _, err := readBody(cli.call("POST", "/exec/"+execID+"/start", execConfig, nil)); err != nil {
53 53
 			return err
54 54
 		}
55 55
 		// For now don't print this - wait for when we support exec wait()
... ...
@@ -23,7 +23,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
23 23
 
24 24
 	utils.ParseFlags(cmd, args, true)
25 25
 
26
-	body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, false))
26
+	body, _, err := readBody(cli.call("GET", "/images/"+cmd.Arg(0)+"/history", nil, nil))
27 27
 	if err != nil {
28 28
 		return err
29 29
 	}
... ...
@@ -129,7 +129,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
129 129
 			v.Set("filters", filterJSON)
130 130
 		}
131 131
 
132
-		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
132
+		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil))
133 133
 		if err != nil {
134 134
 			return err
135 135
 		}
... ...
@@ -207,7 +207,7 @@ func (cli *DockerCli) CmdImages(args ...string) error {
207 207
 			v.Set("all", "1")
208 208
 		}
209 209
 
210
-		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, false))
210
+		body, _, err := readBody(cli.call("GET", "/images/json?"+v.Encode(), nil, nil))
211 211
 
212 212
 		if err != nil {
213 213
 			return err
... ...
@@ -20,7 +20,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
20 20
 	cmd.Require(flag.Exact, 0)
21 21
 	utils.ParseFlags(cmd, args, false)
22 22
 
23
-	body, _, err := readBody(cli.call("GET", "/info", nil, false))
23
+	body, _, err := readBody(cli.call("GET", "/info", nil, nil))
24 24
 	if err != nil {
25 25
 		return err
26 26
 	}
... ...
@@ -37,7 +37,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
37 37
 	status := 0
38 38
 
39 39
 	for _, name := range cmd.Args() {
40
-		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, false))
40
+		obj, _, err := readBody(cli.call("GET", "/containers/"+name+"/json", nil, nil))
41 41
 		if err != nil {
42 42
 			if strings.Contains(err.Error(), "Too many") {
43 43
 				fmt.Fprintf(cli.err, "Error: %v", err)
... ...
@@ -45,7 +45,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
45 45
 				continue
46 46
 			}
47 47
 
48
-			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, false))
48
+			obj, _, err = readBody(cli.call("GET", "/images/"+name+"/json", nil, nil))
49 49
 			if err != nil {
50 50
 				if strings.Contains(err.Error(), "No such") {
51 51
 					fmt.Fprintf(cli.err, "Error: No such image or container: %s\n", name)
... ...
@@ -19,7 +19,7 @@ func (cli *DockerCli) CmdKill(args ...string) error {
19 19
 
20 20
 	var encounteredError error
21 21
 	for _, name := range cmd.Args() {
22
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, false)); err != nil {
22
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", name, *signal), nil, nil)); err != nil {
23 23
 			fmt.Fprintf(cli.err, "%s\n", err)
24 24
 			encounteredError = fmt.Errorf("Error: failed to kill one or more containers")
25 25
 		} else {
... ...
@@ -116,7 +116,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
116 116
 	authconfig.ServerAddress = serverAddress
117 117
 	cli.configFile.Configs[serverAddress] = authconfig
118 118
 
119
-	stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], false)
119
+	stream, statusCode, err := cli.call("POST", "/auth", cli.configFile.Configs[serverAddress], nil)
120 120
 	if statusCode == 401 {
121 121
 		delete(cli.configFile.Configs, serverAddress)
122 122
 		registry.SaveConfig(cli.configFile)
... ...
@@ -25,7 +25,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
25 25
 
26 26
 	name := cmd.Arg(0)
27 27
 
28
-	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, false)
28
+	stream, _, err := cli.call("GET", "/containers/"+name+"/json", nil, nil)
29 29
 	if err != nil {
30 30
 		return err
31 31
 	}
... ...
@@ -17,7 +17,7 @@ func (cli *DockerCli) CmdPause(args ...string) error {
17 17
 
18 18
 	var encounteredError error
19 19
 	for _, name := range cmd.Args() {
20
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, false)); err != nil {
20
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/pause", name), nil, nil)); err != nil {
21 21
 			fmt.Fprintf(cli.err, "%s\n", err)
22 22
 			encounteredError = fmt.Errorf("Error: failed to pause container named %s", name)
23 23
 		} else {
... ...
@@ -19,7 +19,7 @@ func (cli *DockerCli) CmdPort(args ...string) error {
19 19
 	cmd.Require(flag.Min, 1)
20 20
 	utils.ParseFlags(cmd, args, true)
21 21
 
22
-	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
22
+	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
23 23
 	if err != nil {
24 24
 		return err
25 25
 	}
... ...
@@ -85,7 +85,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
85 85
 		v.Set("filters", filterJSON)
86 86
 	}
87 87
 
88
-	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, false))
88
+	body, _, err := readBody(cli.call("GET", "/containers/json?"+v.Encode(), nil, nil))
89 89
 	if err != nil {
90 90
 		return err
91 91
 	}
... ...
@@ -1,11 +1,8 @@
1 1
 package client
2 2
 
3 3
 import (
4
-	"encoding/base64"
5
-	"encoding/json"
6 4
 	"fmt"
7 5
 	"net/url"
8
-	"strings"
9 6
 
10 7
 	"github.com/docker/docker/graph"
11 8
 	flag "github.com/docker/docker/pkg/mflag"
... ...
@@ -47,34 +44,6 @@ func (cli *DockerCli) CmdPull(args ...string) error {
47 47
 
48 48
 	cli.LoadConfigFile()
49 49
 
50
-	// Resolve the Auth config relevant for this server
51
-	authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
52
-
53
-	pull := func(authConfig registry.AuthConfig) error {
54
-		buf, err := json.Marshal(authConfig)
55
-		if err != nil {
56
-			return err
57
-		}
58
-		registryAuthHeader := []string{
59
-			base64.URLEncoding.EncodeToString(buf),
60
-		}
61
-
62
-		return cli.stream("POST", "/images/create?"+v.Encode(), nil, cli.out, map[string][]string{
63
-			"X-Registry-Auth": registryAuthHeader,
64
-		})
65
-	}
66
-
67
-	if err := pull(authConfig); err != nil {
68
-		if strings.Contains(err.Error(), "Status 401") {
69
-			fmt.Fprintln(cli.out, "\nPlease login prior to pull:")
70
-			if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
71
-				return err
72
-			}
73
-			authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
74
-			return pull(authConfig)
75
-		}
76
-		return err
77
-	}
78
-
79
-	return nil
50
+	_, _, err = cli.clientRequestAttemptLogin("POST", "/images/create?"+v.Encode(), nil, cli.out, repoInfo.Index, "pull")
51
+	return err
80 52
 }
... ...
@@ -1,11 +1,8 @@
1 1
 package client
2 2
 
3 3
 import (
4
-	"encoding/base64"
5
-	"encoding/json"
6 4
 	"fmt"
7 5
 	"net/url"
8
-	"strings"
9 6
 
10 7
 	flag "github.com/docker/docker/pkg/mflag"
11 8
 	"github.com/docker/docker/pkg/parsers"
... ...
@@ -50,30 +47,6 @@ func (cli *DockerCli) CmdPush(args ...string) error {
50 50
 	v := url.Values{}
51 51
 	v.Set("tag", tag)
52 52
 
53
-	push := func(authConfig registry.AuthConfig) error {
54
-		buf, err := json.Marshal(authConfig)
55
-		if err != nil {
56
-			return err
57
-		}
58
-		registryAuthHeader := []string{
59
-			base64.URLEncoding.EncodeToString(buf),
60
-		}
61
-
62
-		return cli.stream("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, map[string][]string{
63
-			"X-Registry-Auth": registryAuthHeader,
64
-		})
65
-	}
66
-
67
-	if err := push(authConfig); err != nil {
68
-		if strings.Contains(err.Error(), "Status 401") {
69
-			fmt.Fprintln(cli.out, "\nPlease login prior to push:")
70
-			if err := cli.CmdLogin(repoInfo.Index.GetAuthConfigKey()); err != nil {
71
-				return err
72
-			}
73
-			authConfig := cli.configFile.ResolveAuthConfig(repoInfo.Index)
74
-			return push(authConfig)
75
-		}
76
-		return err
77
-	}
78
-	return nil
53
+	_, _, err = cli.clientRequestAttemptLogin("POST", "/images/"+remote+"/push?"+v.Encode(), nil, cli.out, repoInfo.Index, "push")
54
+	return err
79 55
 }
... ...
@@ -18,7 +18,7 @@ func (cli *DockerCli) CmdRename(args ...string) error {
18 18
 	oldName := cmd.Arg(0)
19 19
 	newName := cmd.Arg(1)
20 20
 
21
-	if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", oldName, newName), nil, false)); err != nil {
21
+	if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/rename?name=%s", oldName, newName), nil, nil)); err != nil {
22 22
 		fmt.Fprintf(cli.err, "%s\n", err)
23 23
 		return fmt.Errorf("Error: failed to rename container named %s", oldName)
24 24
 	}
... ...
@@ -24,7 +24,7 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
24 24
 
25 25
 	var encounteredError error
26 26
 	for _, name := range cmd.Args() {
27
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))
27
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, nil))
28 28
 		if err != nil {
29 29
 			fmt.Fprintf(cli.err, "%s\n", err)
30 30
 			encounteredError = fmt.Errorf("Error: failed to restart one or more containers")
... ...
@@ -31,7 +31,7 @@ func (cli *DockerCli) CmdRm(args ...string) error {
31 31
 
32 32
 	var encounteredError error
33 33
 	for _, name := range cmd.Args() {
34
-		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, false))
34
+		_, _, err := readBody(cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil, nil))
35 35
 		if err != nil {
36 36
 			fmt.Fprintf(cli.err, "%s\n", err)
37 37
 			encounteredError = fmt.Errorf("Error: failed to remove one or more containers")
... ...
@@ -32,7 +32,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error {
32 32
 
33 33
 	var encounteredError error
34 34
 	for _, name := range cmd.Args() {
35
-		body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, false))
35
+		body, _, err := readBody(cli.call("DELETE", "/images/"+name+"?"+v.Encode(), nil, nil))
36 36
 		if err != nil {
37 37
 			fmt.Fprintf(cli.err, "%s\n", err)
38 38
 			encounteredError = fmt.Errorf("Error: failed to remove one or more images")
... ...
@@ -183,14 +183,14 @@ func (cli *DockerCli) CmdRun(args ...string) error {
183 183
 
184 184
 	defer func() {
185 185
 		if *flAutoRemove {
186
-			if _, _, err = readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, false)); err != nil {
186
+			if _, _, err = readBody(cli.call("DELETE", "/containers/"+createResponse.ID+"?v=1", nil, nil)); err != nil {
187 187
 				log.Errorf("Error deleting container: %s", err)
188 188
 			}
189 189
 		}
190 190
 	}()
191 191
 
192 192
 	//start the container
193
-	if _, _, err = readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, false)); err != nil {
193
+	if _, _, err = readBody(cli.call("POST", "/containers/"+createResponse.ID+"/start", nil, nil)); err != nil {
194 194
 		return err
195 195
 	}
196 196
 
... ...
@@ -220,7 +220,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
220 220
 	if *flAutoRemove {
221 221
 		// Autoremove: wait for the container to finish, retrieve
222 222
 		// the exit code and remove the container
223
-		if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, false)); err != nil {
223
+		if _, _, err := readBody(cli.call("POST", "/containers/"+createResponse.ID+"/wait", nil, nil)); err != nil {
224 224
 			return err
225 225
 		}
226 226
 		if _, status, err = getExitCode(cli, createResponse.ID); err != nil {
... ...
@@ -8,6 +8,8 @@ import (
8 8
 
9 9
 	"github.com/docker/docker/engine"
10 10
 	flag "github.com/docker/docker/pkg/mflag"
11
+	"github.com/docker/docker/pkg/parsers"
12
+	"github.com/docker/docker/registry"
11 13
 	"github.com/docker/docker/utils"
12 14
 )
13 15
 
... ...
@@ -24,16 +26,27 @@ func (cli *DockerCli) CmdSearch(args ...string) error {
24 24
 
25 25
 	utils.ParseFlags(cmd, args, true)
26 26
 
27
+	name := cmd.Arg(0)
27 28
 	v := url.Values{}
28
-	v.Set("term", cmd.Arg(0))
29
+	v.Set("term", name)
29 30
 
30
-	body, _, err := readBody(cli.call("GET", "/images/search?"+v.Encode(), nil, true))
31
+	// Resolve the Repository name from fqn to hostname + name
32
+	taglessRemote, _ := parsers.ParseRepositoryTag(name)
33
+	repoInfo, err := registry.ParseRepositoryInfo(taglessRemote)
34
+	if err != nil {
35
+		return err
36
+	}
31 37
 
38
+	cli.LoadConfigFile()
39
+
40
+	body, statusCode, errReq := cli.clientRequestAttemptLogin("GET", "/images/search?"+v.Encode(), nil, nil, repoInfo.Index, "search")
41
+	rawBody, _, err := readBody(body, statusCode, errReq)
32 42
 	if err != nil {
33 43
 		return err
34 44
 	}
45
+
35 46
 	outs := engine.NewTable("star_count", 0)
36
-	if _, err := outs.ReadListFrom(body); err != nil {
47
+	if _, err := outs.ReadListFrom(rawBody); err != nil {
37 48
 		return err
38 49
 	}
39 50
 	w := tabwriter.NewWriter(cli.out, 10, 1, 3, ' ', 0)
... ...
@@ -32,7 +32,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
32 32
 			if sig == "" {
33 33
 				log.Errorf("Unsupported signal: %v. Discarding.", s)
34 34
 			}
35
-			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, false)); err != nil {
35
+			if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, nil)); err != nil {
36 36
 				log.Debugf("Error sending signal: %s", err)
37 37
 			}
38 38
 		}
... ...
@@ -61,7 +61,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
61 61
 			return fmt.Errorf("You cannot start and attach multiple containers at once.")
62 62
 		}
63 63
 
64
-		stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false)
64
+		stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
65 65
 		if err != nil {
66 66
 			return err
67 67
 		}
... ...
@@ -121,7 +121,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
121 121
 
122 122
 	var encounteredError error
123 123
 	for _, name := range cmd.Args() {
124
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false))
124
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, nil))
125 125
 		if err != nil {
126 126
 			if !*attach && !*openStdin {
127 127
 				// attach and openStdin is false means it could be starting multiple containers
... ...
@@ -29,7 +29,7 @@ type containerStats struct {
29 29
 }
30 30
 
31 31
 func (s *containerStats) Collect(cli *DockerCli) {
32
-	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, false)
32
+	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, nil)
33 33
 	if err != nil {
34 34
 		s.err = err
35 35
 		return
... ...
@@ -26,7 +26,7 @@ func (cli *DockerCli) CmdStop(args ...string) error {
26 26
 
27 27
 	var encounteredError error
28 28
 	for _, name := range cmd.Args() {
29
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, false))
29
+		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil, nil))
30 30
 		if err != nil {
31 31
 			fmt.Fprintf(cli.err, "%s\n", err)
32 32
 			encounteredError = fmt.Errorf("Error: failed to stop one or more containers")
... ...
@@ -35,7 +35,7 @@ func (cli *DockerCli) CmdTag(args ...string) error {
35 35
 		v.Set("force", "1")
36 36
 	}
37 37
 
38
-	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, false)); err != nil {
38
+	if _, _, err := readBody(cli.call("POST", "/images/"+cmd.Arg(0)+"/tag?"+v.Encode(), nil, nil)); err != nil {
39 39
 		return err
40 40
 	}
41 41
 	return nil
... ...
@@ -25,7 +25,7 @@ func (cli *DockerCli) CmdTop(args ...string) error {
25 25
 		val.Set("ps_args", strings.Join(cmd.Args()[1:], " "))
26 26
 	}
27 27
 
28
-	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, false)
28
+	stream, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/top?"+val.Encode(), nil, nil)
29 29
 	if err != nil {
30 30
 		return err
31 31
 	}
... ...
@@ -17,7 +17,7 @@ func (cli *DockerCli) CmdUnpause(args ...string) error {
17 17
 
18 18
 	var encounteredError error
19 19
 	for _, name := range cmd.Args() {
20
-		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, false)); err != nil {
20
+		if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/unpause", name), nil, nil)); err != nil {
21 21
 			fmt.Fprintf(cli.err, "%s\n", err)
22 22
 			encounteredError = fmt.Errorf("Error: failed to unpause container named %s", name)
23 23
 		} else {
... ...
@@ -54,124 +54,146 @@ 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
+
70
+	if headers != nil {
71
+		for k, v := range headers {
72
+			req.Header[k] = v
73
+		}
74
+	}
75
+
76
+	if expectedPayload && req.Header.Get("Content-Type") == "" {
92 77
 		req.Header.Set("Content-Type", "text/plain")
93 78
 	}
79
+
94 80
 	resp, err := cli.HTTPClient().Do(req)
81
+	statusCode := -1
82
+	if resp != nil {
83
+		statusCode = resp.StatusCode
84
+	}
95 85
 	if err != nil {
96 86
 		if strings.Contains(err.Error(), "connection refused") {
97
-			return nil, -1, ErrConnectionRefused
87
+			return nil, "", statusCode, ErrConnectionRefused
98 88
 		}
99 89
 
100 90
 		if cli.tlsConfig == nil {
101
-			return nil, -1, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
91
+			return nil, "", statusCode, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
102 92
 		}
103
-		return nil, -1, fmt.Errorf("An error occurred trying to connect: %v", err)
104
-
93
+		return nil, "", statusCode, fmt.Errorf("An error occurred trying to connect: %v", err)
105 94
 	}
106 95
 
107
-	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
96
+	if statusCode < 200 || statusCode >= 400 {
108 97
 		body, err := ioutil.ReadAll(resp.Body)
109 98
 		if err != nil {
110
-			return nil, -1, err
99
+			return nil, "", statusCode, err
111 100
 		}
112 101
 		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)
102
+			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 103
 		}
115
-		return nil, resp.StatusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
104
+		return nil, "", statusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
116 105
 	}
117 106
 
118
-	return resp.Body, resp.StatusCode, nil
107
+	return resp.Body, resp.Header.Get("Content-Type"), statusCode, nil
119 108
 }
120 109
 
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
-}
110
+func (cli *DockerCli) clientRequestAttemptLogin(method, path string, in io.Reader, out io.Writer, index *registry.IndexInfo, cmdName string) (io.ReadCloser, int, error) {
111
+	cmdAttempt := func(authConfig registry.AuthConfig) (io.ReadCloser, int, error) {
112
+		buf, err := json.Marshal(authConfig)
113
+		if err != nil {
114
+			return nil, -1, err
115
+		}
116
+		registryAuthHeader := []string{
117
+			base64.URLEncoding.EncodeToString(buf),
118
+		}
124 119
 
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{})
120
+		// begin the request
121
+		body, contentType, statusCode, err := cli.clientRequest(method, path, in, map[string][]string{
122
+			"X-Registry-Auth": registryAuthHeader,
123
+		})
124
+		if err == nil && out != nil {
125
+			// If we are streaming output, complete the stream since
126
+			// errors may not appear until later.
127
+			err = cli.streamBody(body, contentType, true, out, nil)
128
+		}
129
+		if err != nil {
130
+			// Since errors in a stream appear after status 200 has been written,
131
+			// we may need to change the status code.
132
+			if strings.Contains(err.Error(), "Authentication is required") ||
133
+				strings.Contains(err.Error(), "Status 401") ||
134
+				strings.Contains(err.Error(), "status code 401") {
135
+				statusCode = http.StatusUnauthorized
136
+			}
137
+		}
138
+		return body, statusCode, err
128 139
 	}
129 140
 
130
-	req, err := http.NewRequest(method, fmt.Sprintf("/v%s%s", api.APIVERSION, path), in)
131
-	if err != nil {
132
-		return err
141
+	// Resolve the Auth config relevant for this server
142
+	authConfig := cli.configFile.ResolveAuthConfig(index)
143
+	body, statusCode, err := cmdAttempt(authConfig)
144
+	if statusCode == http.StatusUnauthorized {
145
+		fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
146
+		if err = cli.CmdLogin(index.GetAuthConfigKey()); err != nil {
147
+			return nil, -1, err
148
+		}
149
+		authConfig = cli.configFile.ResolveAuthConfig(index)
150
+		return cmdAttempt(authConfig)
133 151
 	}
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")
152
+	return body, statusCode, err
153
+}
154
+
155
+func (cli *DockerCli) call(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
156
+	params, err := cli.encodeData(data)
157
+	if err != nil {
158
+		return nil, -1, err
139 159
 	}
140 160
 
141
-	if headers != nil {
142
-		for k, v := range headers {
143
-			req.Header[k] = v
161
+	if data != nil {
162
+		if headers == nil {
163
+			headers = make(map[string][]string)
144 164
 		}
165
+		headers["Content-Type"] = []string{"application/json"}
145 166
 	}
146
-	resp, err := cli.HTTPClient().Do(req)
167
+
168
+	body, _, statusCode, err := cli.clientRequest(method, path, params, headers)
169
+	return body, statusCode, err
170
+}
171
+
172
+func (cli *DockerCli) stream(method, path string, in io.Reader, out io.Writer, headers map[string][]string) error {
173
+	return cli.streamHelper(method, path, true, in, out, nil, headers)
174
+}
175
+
176
+func (cli *DockerCli) streamHelper(method, path string, setRawTerminal bool, in io.Reader, stdout, stderr io.Writer, headers map[string][]string) error {
177
+	body, contentType, _, err := cli.clientRequest(method, path, in, headers)
147 178
 	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 179
 		return err
152 180
 	}
153
-	defer resp.Body.Close()
181
+	return cli.streamBody(body, contentType, setRawTerminal, stdout, stderr)
182
+}
154 183
 
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
-	}
184
+func (cli *DockerCli) streamBody(body io.ReadCloser, contentType string, setRawTerminal bool, stdout, stderr io.Writer) error {
185
+	defer body.Close()
165 186
 
166
-	if api.MatchesContentType(resp.Header.Get("Content-Type"), "application/json") {
167
-		return jsonmessage.DisplayJSONMessagesStream(resp.Body, stdout, cli.outFd, cli.isTerminalOut)
187
+	if api.MatchesContentType(contentType, "application/json") {
188
+		return jsonmessage.DisplayJSONMessagesStream(body, stdout, cli.outFd, cli.isTerminalOut)
168 189
 	}
169 190
 	if stdout != nil || stderr != nil {
170 191
 		// When TTY is ON, use regular copy
192
+		var err error
171 193
 		if setRawTerminal {
172
-			_, err = io.Copy(stdout, resp.Body)
194
+			_, err = io.Copy(stdout, body)
173 195
 		} else {
174
-			_, err = stdcopy.StdCopy(stdout, stderr, resp.Body)
196
+			_, err = stdcopy.StdCopy(stdout, stderr, body)
175 197
 		}
176 198
 		log.Debugf("[stream] End of stdout")
177 199
 		return err
... ...
@@ -195,13 +217,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 203
 func waitForExit(cli *DockerCli, containerID string) (int, error) {
204
-	stream, _, err := cli.call("POST", "/containers/"+containerID+"/wait", nil, false)
204
+	stream, _, err := cli.call("POST", "/containers/"+containerID+"/wait", nil, nil)
205 205
 	if err != nil {
206 206
 		return -1, err
207 207
 	}
... ...
@@ -216,7 +238,7 @@ func waitForExit(cli *DockerCli, containerID string) (int, error) {
216 216
 // getExitCode perform an inspect on the container. It returns
217 217
 // the running state and the exit code.
218 218
 func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
219
-	stream, _, err := cli.call("GET", "/containers/"+containerID+"/json", nil, false)
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 {
... ...
@@ -237,7 +259,7 @@ func getExitCode(cli *DockerCli, containerID string) (bool, int, error) {
237 237
 // getExecExitCode perform an inspect on the exec command. It returns
238 238
 // the running state and the exit code.
239 239
 func getExecExitCode(cli *DockerCli, execID string) (bool, int, error) {
240
-	stream, _, err := cli.call("GET", "/exec/"+execID+"/json", nil, false)
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 {
... ...
@@ -33,7 +33,7 @@ func (cli *DockerCli) CmdVersion(args ...string) error {
33 33
 	}
34 34
 	fmt.Fprintf(cli.out, "OS/Arch (client): %s/%s\n", runtime.GOOS, runtime.GOARCH)
35 35
 
36
-	body, _, err := readBody(cli.call("GET", "/version", nil, false))
36
+	body, _, err := readBody(cli.call("GET", "/version", nil, nil))
37 37
 	if err != nil {
38 38
 		return err
39 39
 	}
... ...
@@ -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 {