Browse code

Remove email address field from login

This removes the email prompt when you use docker login, and also removes the ability to register via the docker cli. Docker login, will strictly be used for logging into a registry server.

Signed-off-by: Ken Cochrane <kencochrane@gmail.com>

Ken Cochrane authored on 2016/03/01 10:51:36
Showing 18 changed files
... ...
@@ -17,7 +17,7 @@ import (
17 17
 	"github.com/docker/engine-api/types"
18 18
 )
19 19
 
20
-// CmdLogin logs in or registers a user to a Docker registry service.
20
+// CmdLogin logs in a user to a Docker registry service.
21 21
 //
22 22
 // If no server is specified, the user will be logged into or registered to the registry's index server.
23 23
 //
... ...
@@ -28,7 +28,9 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
28 28
 
29 29
 	flUser := cmd.String([]string{"u", "-username"}, "", "Username")
30 30
 	flPassword := cmd.String([]string{"p", "-password"}, "", "Password")
31
-	flEmail := cmd.String([]string{"e", "-email"}, "", "Email")
31
+
32
+	// Deprecated in 1.11: Should be removed in docker 1.13
33
+	cmd.String([]string{"#e", "#-email"}, "", "Email")
32 34
 
33 35
 	cmd.ParseFlags(args, true)
34 36
 
... ...
@@ -38,13 +40,15 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
38 38
 	}
39 39
 
40 40
 	var serverAddress string
41
+	var isDefaultRegistry bool
41 42
 	if len(cmd.Args()) > 0 {
42 43
 		serverAddress = cmd.Arg(0)
43 44
 	} else {
44 45
 		serverAddress = cli.electAuthServer()
46
+		isDefaultRegistry = true
45 47
 	}
46 48
 
47
-	authConfig, err := cli.configureAuth(*flUser, *flPassword, *flEmail, serverAddress)
49
+	authConfig, err := cli.configureAuth(*flUser, *flPassword, serverAddress, isDefaultRegistry)
48 50
 	if err != nil {
49 51
 		return err
50 52
 	}
... ...
@@ -77,7 +81,7 @@ func (cli *DockerCli) promptWithDefault(prompt string, configDefault string) {
77 77
 	}
78 78
 }
79 79
 
80
-func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress string) (types.AuthConfig, error) {
80
+func (cli *DockerCli) configureAuth(flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) {
81 81
 	authconfig, err := getCredentials(cli.configFile, serverAddress)
82 82
 	if err != nil {
83 83
 		return authconfig, err
... ...
@@ -86,6 +90,10 @@ func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress s
86 86
 	authconfig.Username = strings.TrimSpace(authconfig.Username)
87 87
 
88 88
 	if flUser = strings.TrimSpace(flUser); flUser == "" {
89
+		if isDefaultRegistry {
90
+			// if this is a defauly registry (docker hub), then display the following message.
91
+			fmt.Fprintln(cli.out, "Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.")
92
+		}
89 93
 		cli.promptWithDefault("Username", authconfig.Username)
90 94
 		flUser = readInput(cli.in, cli.out)
91 95
 		flUser = strings.TrimSpace(flUser)
... ...
@@ -115,29 +123,8 @@ func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress s
115 115
 		}
116 116
 	}
117 117
 
118
-	// Assume that a different username means they may not want to use
119
-	// the email from the config file, so prompt it
120
-	if flUser != authconfig.Username {
121
-		if flEmail == "" {
122
-			cli.promptWithDefault("Email", authconfig.Email)
123
-			flEmail = readInput(cli.in, cli.out)
124
-			if flEmail == "" {
125
-				flEmail = authconfig.Email
126
-			}
127
-		}
128
-	} else {
129
-		// However, if they don't override the username use the
130
-		// email from the cmd line if specified. IOW, allow
131
-		// then to change/override them.  And if not specified, just
132
-		// use what's in the config file
133
-		if flEmail == "" {
134
-			flEmail = authconfig.Email
135
-		}
136
-	}
137
-
138 118
 	authconfig.Username = flUser
139 119
 	authconfig.Password = flPassword
140
-	authconfig.Email = flEmail
141 120
 	authconfig.ServerAddress = serverAddress
142 121
 
143 122
 	return authconfig, nil
... ...
@@ -48,7 +48,7 @@ func (cli *DockerCli) registryAuthenticationPrivilegedFunc(index *registrytypes.
48 48
 	return func() (string, error) {
49 49
 		fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
50 50
 		indexServer := registry.GetAuthConfigKey(index)
51
-		authConfig, err := cli.configureAuth("", "", "", indexServer)
51
+		authConfig, err := cli.configureAuth("", "", indexServer, false)
52 52
 		if err != nil {
53 53
 			return "", err
54 54
 		}
... ...
@@ -88,11 +88,6 @@ func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
88 88
 		if err != nil {
89 89
 			return err
90 90
 		}
91
-		origEmail := strings.Split(arr[1], " = ")
92
-		if len(origEmail) != 2 {
93
-			return fmt.Errorf("Invalid Auth config file")
94
-		}
95
-		authConfig.Email = origEmail[1]
96 91
 		authConfig.ServerAddress = defaultIndexserver
97 92
 		configFile.AuthConfigs[defaultIndexserver] = authConfig
98 93
 	} else {
... ...
@@ -111,12 +111,9 @@ func TestOldInvalidsAuth(t *testing.T) {
111 111
 	invalids := map[string]string{
112 112
 		`username = test`: "The Auth config file is empty",
113 113
 		`username
114
-password
115
-email`: "Invalid Auth config file",
114
+password`: "Invalid Auth config file",
116 115
 		`username = test
117 116
 email`: "Invalid auth configuration file",
118
-		`username = am9lam9lOmhlbGxv
119
-email`: "Invalid Auth config file",
120 117
 	}
121 118
 
122 119
 	tmpHome, err := ioutil.TempDir("", "config-test")
... ...
@@ -164,7 +161,7 @@ func TestOldValidAuth(t *testing.T) {
164 164
 
165 165
 	fn := filepath.Join(tmpHome, oldConfigfile)
166 166
 	js := `username = am9lam9lOmhlbGxv
167
-email = user@example.com`
167
+	email = user@example.com`
168 168
 	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
169 169
 		t.Fatal(err)
170 170
 	}
... ...
@@ -176,15 +173,23 @@ email = user@example.com`
176 176
 
177 177
 	// defaultIndexserver is https://index.docker.io/v1/
178 178
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
179
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
179
+	if ac.Username != "joejoe" || ac.Password != "hello" {
180 180
 		t.Fatalf("Missing data from parsing:\n%q", config)
181 181
 	}
182 182
 
183 183
 	// Now save it and make sure it shows up in new form
184 184
 	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
185 185
 
186
-	if !strings.Contains(configStr, "user@example.com") {
187
-		t.Fatalf("Should have save in new form: %s", configStr)
186
+	expConfStr := `{
187
+	"auths": {
188
+		"https://index.docker.io/v1/": {
189
+			"auth": "am9lam9lOmhlbGxv"
190
+		}
191
+	}
192
+}`
193
+
194
+	if configStr != expConfStr {
195
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
188 196
 	}
189 197
 }
190 198
 
... ...
@@ -239,15 +244,24 @@ func TestOldJson(t *testing.T) {
239 239
 	}
240 240
 
241 241
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
242
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
242
+	if ac.Username != "joejoe" || ac.Password != "hello" {
243 243
 		t.Fatalf("Missing data from parsing:\n%q", config)
244 244
 	}
245 245
 
246 246
 	// Now save it and make sure it shows up in new form
247 247
 	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
248 248
 
249
-	if !strings.Contains(configStr, "user@example.com") {
250
-		t.Fatalf("Should have save in new form: %s", configStr)
249
+	expConfStr := `{
250
+	"auths": {
251
+		"https://index.docker.io/v1/": {
252
+			"auth": "am9lam9lOmhlbGxv",
253
+			"email": "user@example.com"
254
+		}
255
+	}
256
+}`
257
+
258
+	if configStr != expConfStr {
259
+		t.Fatalf("Should have save in new form: \n'%s'\n not \n'%s'\n", configStr, expConfStr)
251 260
 	}
252 261
 }
253 262
 
... ...
@@ -259,7 +273,7 @@ func TestNewJson(t *testing.T) {
259 259
 	defer os.RemoveAll(tmpHome)
260 260
 
261 261
 	fn := filepath.Join(tmpHome, ConfigFileName)
262
-	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
262
+	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }`
263 263
 	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
264 264
 		t.Fatal(err)
265 265
 	}
... ...
@@ -270,15 +284,62 @@ func TestNewJson(t *testing.T) {
270 270
 	}
271 271
 
272 272
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
273
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
273
+	if ac.Username != "joejoe" || ac.Password != "hello" {
274 274
 		t.Fatalf("Missing data from parsing:\n%q", config)
275 275
 	}
276 276
 
277 277
 	// Now save it and make sure it shows up in new form
278 278
 	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
279 279
 
280
-	if !strings.Contains(configStr, "user@example.com") {
281
-		t.Fatalf("Should have save in new form: %s", configStr)
280
+	expConfStr := `{
281
+	"auths": {
282
+		"https://index.docker.io/v1/": {
283
+			"auth": "am9lam9lOmhlbGxv"
284
+		}
285
+	}
286
+}`
287
+
288
+	if configStr != expConfStr {
289
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
290
+	}
291
+}
292
+
293
+func TestNewJsonNoEmail(t *testing.T) {
294
+	tmpHome, err := ioutil.TempDir("", "config-test")
295
+	if err != nil {
296
+		t.Fatal(err)
297
+	}
298
+	defer os.RemoveAll(tmpHome)
299
+
300
+	fn := filepath.Join(tmpHome, ConfigFileName)
301
+	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }`
302
+	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
303
+		t.Fatal(err)
304
+	}
305
+
306
+	config, err := Load(tmpHome)
307
+	if err != nil {
308
+		t.Fatalf("Failed loading on empty json file: %q", err)
309
+	}
310
+
311
+	ac := config.AuthConfigs["https://index.docker.io/v1/"]
312
+	if ac.Username != "joejoe" || ac.Password != "hello" {
313
+		t.Fatalf("Missing data from parsing:\n%q", config)
314
+	}
315
+
316
+	// Now save it and make sure it shows up in new form
317
+	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
318
+
319
+	expConfStr := `{
320
+	"auths": {
321
+		"https://index.docker.io/v1/": {
322
+			"auth": "am9lam9lOmhlbGxv"
323
+		}
324
+	}
325
+}`
326
+
327
+	if configStr != expConfStr {
328
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
282 329
 	}
283 330
 }
284 331
 
... ...
@@ -366,7 +427,7 @@ func TestJsonReaderNoFile(t *testing.T) {
366 366
 	}
367 367
 
368 368
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
369
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
369
+	if ac.Username != "joejoe" || ac.Password != "hello" {
370 370
 		t.Fatalf("Missing data from parsing:\n%q", config)
371 371
 	}
372 372
 
... ...
@@ -381,7 +442,7 @@ func TestOldJsonReaderNoFile(t *testing.T) {
381 381
 	}
382 382
 
383 383
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
384
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
384
+	if ac.Username != "joejoe" || ac.Password != "hello" {
385 385
 		t.Fatalf("Missing data from parsing:\n%q", config)
386 386
 	}
387 387
 }
... ...
@@ -404,7 +465,7 @@ func TestJsonWithPsFormatNoFile(t *testing.T) {
404 404
 
405 405
 func TestJsonSaveWithNoFile(t *testing.T) {
406 406
 	js := `{
407
-		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
407
+		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } },
408 408
 		"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
409 409
 }`
410 410
 	config, err := LoadFromReader(strings.NewReader(js))
... ...
@@ -426,9 +487,16 @@ func TestJsonSaveWithNoFile(t *testing.T) {
426 426
 		t.Fatalf("Failed saving to file: %q", err)
427 427
 	}
428 428
 	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
429
-	if !strings.Contains(string(buf), `"auths":`) ||
430
-		!strings.Contains(string(buf), "user@example.com") {
431
-		t.Fatalf("Should have save in new form: %s", string(buf))
429
+	expConfStr := `{
430
+	"auths": {
431
+		"https://index.docker.io/v1/": {
432
+			"auth": "am9lam9lOmhlbGxv"
433
+		}
434
+	},
435
+	"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
436
+}`
437
+	if string(buf) != expConfStr {
438
+		t.Fatalf("Should have save in new form: \n%s\nnot \n%s", string(buf), expConfStr)
432 439
 	}
433 440
 }
434 441
 
... ...
@@ -454,14 +522,23 @@ func TestLegacyJsonSaveWithNoFile(t *testing.T) {
454 454
 		t.Fatalf("Failed saving to file: %q", err)
455 455
 	}
456 456
 	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
457
-	if !strings.Contains(string(buf), `"auths":`) ||
458
-		!strings.Contains(string(buf), "user@example.com") {
459
-		t.Fatalf("Should have save in new form: %s", string(buf))
457
+
458
+	expConfStr := `{
459
+	"auths": {
460
+		"https://index.docker.io/v1/": {
461
+			"auth": "am9lam9lOmhlbGxv",
462
+			"email": "user@example.com"
463
+		}
464
+	}
465
+}`
466
+
467
+	if string(buf) != expConfStr {
468
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", string(buf), expConfStr)
460 469
 	}
461 470
 }
462 471
 
463 472
 func TestEncodeAuth(t *testing.T) {
464
-	newAuthConfig := &types.AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
473
+	newAuthConfig := &types.AuthConfig{Username: "ken", Password: "test"}
465 474
 	authStr := encodeAuth(newAuthConfig)
466 475
 	decAuthConfig := &types.AuthConfig{}
467 476
 	var err error
... ...
@@ -811,7 +811,7 @@ _docker_daemon() {
811 811
  			return
812 812
  			;;
813 813
  	esac
814
- 
814
+
815 815
  	local key=$(__docker_map_key_of_current_option '--storage-opt')
816 816
  	case "$key" in
817 817
  		dm.@(blkdiscard|override_udev_sync_check|use_deferred_@(removal|deletion)))
... ...
@@ -1205,14 +1205,14 @@ _docker_load() {
1205 1205
 
1206 1206
 _docker_login() {
1207 1207
 	case "$prev" in
1208
-		--email|-e|--password|-p|--username|-u)
1208
+		--password|-p|--username|-u)
1209 1209
 			return
1210 1210
 			;;
1211 1211
 	esac
1212 1212
 
1213 1213
 	case "$cur" in
1214 1214
 		-*)
1215
-			COMPREPLY=( $( compgen -W "--email -e --help --password -p --username -u" -- "$cur" ) )
1215
+			COMPREPLY=( $( compgen -W "--help --password -p --username -u" -- "$cur" ) )
1216 1216
 			;;
1217 1217
 	esac
1218 1218
 }
... ...
@@ -221,8 +221,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from load' -l help -d 'Print
221 221
 complete -c docker -A -f -n '__fish_seen_subcommand_from load' -s i -l input -d 'Read from a tar archive file, instead of STDIN'
222 222
 
223 223
 # login
224
-complete -c docker -f -n '__fish_docker_no_subcommand' -a login -d 'Register or log in to a Docker registry server'
225
-complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s e -l email -d 'Email'
224
+complete -c docker -f -n '__fish_docker_no_subcommand' -a login -d 'Log in to a Docker registry server'
226 225
 complete -c docker -A -f -n '__fish_seen_subcommand_from login' -l help -d 'Print usage'
227 226
 complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s p -l password -d 'Password'
228 227
 complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s u -l username -d 'Username'
... ...
@@ -399,5 +398,3 @@ complete -c docker -f -n '__fish_docker_no_subcommand' -a version -d 'Show the D
399 399
 complete -c docker -f -n '__fish_docker_no_subcommand' -a wait -d 'Block until a container stops, then print its exit code'
400 400
 complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -l help -d 'Print usage'
401 401
 complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -a '(__fish_print_docker_containers running)' -d "Container"
402
-
403
-
... ...
@@ -812,7 +812,6 @@ __docker_subcommand() {
812 812
         (login)
813 813
             _arguments $(__docker_arguments) \
814 814
                 $opts_help \
815
-                "($help -e --email)"{-e=,--email=}"[Email]:email: " \
816 815
                 "($help -p --password)"{-p=,--password=}"[Password]:password: " \
817 816
                 "($help -u --user)"{-u=,--user=}"[Username]:username: " \
818 817
                 "($help -)1:server: " && ret=0
... ...
@@ -14,6 +14,13 @@ weight=80
14 14
 
15 15
 The following list of features are deprecated in Engine.
16 16
 
17
+### `-e` and `--email` flags on `docker login`
18
+**Deprecated In Release: v1.11**
19
+
20
+**Target For Removal In Release: v1.13**
21
+
22
+The docker login command is removing the ability to automatically register for an account with the target registry if the given username doesn't exist. Due to this change, the email flag is no longer required, and will be deprecated.
23
+
17 24
 ### Ambiguous event fields in API
18 25
 **Deprecated In Release: v1.10**
19 26
 
... ...
@@ -12,10 +12,9 @@ parent = "smn_cli"
12 12
 
13 13
     Usage: docker login [OPTIONS] [SERVER]
14 14
 
15
-    Register or log in to a Docker registry server, if no server is
15
+    Log in to a Docker registry server, if no server is
16 16
 	specified "https://index.docker.io/v1/" is the default.
17 17
 
18
-      -e, --email=""       Email
19 18
       --help               Print usage
20 19
       -p, --password=""    Password
21 20
       -u, --username=""    Username
... ...
@@ -27,10 +26,10 @@ adding the server name.
27 27
     $ docker login localhost:8080
28 28
 
29 29
 
30
-`docker login` requires user to use `sudo` or be `root`, except when: 
30
+`docker login` requires user to use `sudo` or be `root`, except when:
31 31
 
32 32
 1.  connecting to a remote daemon, such as a `docker-machine` provisioned `docker engine`.
33
-2.  user is added to the `docker` group.  This will impact the security of your system; the `docker` group is `root` equivalent.  See [Docker Daemon Attack Surface](https://docs.docker.com/security/security/#docker-daemon-attack-surface) for details. 
33
+2.  user is added to the `docker` group.  This will impact the security of your system; the `docker` group is `root` equivalent.  See [Docker Daemon Attack Surface](https://docs.docker.com/security/security/#docker-daemon-attack-surface) for details.
34 34
 
35 35
 You can log into any public or private repository for which you have
36 36
 credentials.  When you log in, the command stores encoded credentials in
... ...
@@ -33,15 +33,11 @@ Docker itself provides access to Docker Hub services via the `docker search`,
33 33
 ### Account creation and login
34 34
 Typically, you'll want to start by creating an account on Docker Hub (if you haven't
35 35
 already) and logging in. You can create your account directly on
36
-[Docker Hub](https://hub.docker.com/account/signup/), or by running:
36
+[Docker Hub](https://hub.docker.com/account/signup/).
37 37
 
38 38
     $ docker login
39 39
 
40
-This will prompt you for a user name, which will become the public namespace for your
41
-public repositories.
42
-If your user name is available, Docker will prompt you to enter a password and your
43
-e-mail address. It will then automatically log you in. You can now commit and
44
-push your own images up to your repos on Docker Hub.
40
+You can now commit and push your own images up to your repos on Docker Hub.
45 41
 
46 42
 > **Note:**
47 43
 > Your authentication credentials will be stored in the `~/.docker/config.json`
... ...
@@ -6548,7 +6548,7 @@ func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *check.C) {
6548 6548
 }
6549 6549
 
6550 6550
 func (s *DockerRegistryAuthSuite) TestBuildFromAuthenticatedRegistry(c *check.C) {
6551
-	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, "-e", s.reg.email, privateRegistryURL)
6551
+	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL)
6552 6552
 
6553 6553
 	baseImage := privateRegistryURL + "/baseimage"
6554 6554
 
... ...
@@ -21,10 +21,24 @@ func (s *DockerSuite) TestLoginWithoutTTY(c *check.C) {
21 21
 
22 22
 func (s *DockerRegistryAuthSuite) TestLoginToPrivateRegistry(c *check.C) {
23 23
 	// wrong credentials
24
+	out, _, err := dockerCmdWithError("login", "-u", s.reg.username, "-p", "WRONGPASSWORD", privateRegistryURL)
25
+	c.Assert(err, checker.NotNil, check.Commentf(out))
26
+	c.Assert(out, checker.Contains, "401 Unauthorized")
27
+
28
+	// now it's fine
29
+	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL)
30
+}
31
+
32
+func (s *DockerRegistryAuthSuite) TestLoginToPrivateRegistryDeprecatedEmailFlag(c *check.C) {
33
+	// Test to make sure login still works with the deprecated -e and --email flags
34
+	// wrong credentials
24 35
 	out, _, err := dockerCmdWithError("login", "-u", s.reg.username, "-p", "WRONGPASSWORD", "-e", s.reg.email, privateRegistryURL)
25 36
 	c.Assert(err, checker.NotNil, check.Commentf(out))
26 37
 	c.Assert(out, checker.Contains, "401 Unauthorized")
27 38
 
28 39
 	// now it's fine
40
+	// -e flag
29 41
 	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, "-e", s.reg.email, privateRegistryURL)
42
+	// --email flag
43
+	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, "--email", s.reg.email, privateRegistryURL)
30 44
 }
... ...
@@ -390,7 +390,6 @@ func (s *DockerRegistryAuthSuite) TestPullWithExternalAuth(c *check.C) {
390 390
 	b, err := ioutil.ReadFile(configPath)
391 391
 	c.Assert(err, checker.IsNil)
392 392
 	c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":")
393
-	c.Assert(string(b), checker.Contains, "email")
394 393
 
395 394
 	dockerCmd(c, "--config", tmp, "tag", "busybox", repoName)
396 395
 	dockerCmd(c, "--config", tmp, "push", repoName)
... ...
@@ -112,7 +112,7 @@ func (s *DockerRegistrySuite) TestV1(c *check.C) {
112 112
 	s.d.Cmd("run", repoName)
113 113
 	c.Assert(v1Repo, check.Not(check.Equals), 1, check.Commentf("Expected v1 repository access after run"))
114 114
 
115
-	s.d.Cmd("login", "-u", "richard", "-p", "testtest", "-e", "testuser@testdomain.com", reg.hostport)
115
+	s.d.Cmd("login", "-u", "richard", "-p", "testtest", reg.hostport)
116 116
 	c.Assert(v1Logins, check.Not(check.Equals), 0, check.Commentf("Expected v1 login attempt"))
117 117
 
118 118
 	s.d.Cmd("tag", "busybox", repoName)
... ...
@@ -2,26 +2,25 @@
2 2
 % Docker Community
3 3
 % JUNE 2014
4 4
 # NAME
5
-docker-login - Register or log in to a Docker registry. 
5
+docker-login - Log in to a Docker registry.
6 6
 
7 7
 # SYNOPSIS
8 8
 **docker login**
9
-[**-e**|**--email**[=*EMAIL*]]
10 9
 [**--help**]
11 10
 [**-p**|**--password**[=*PASSWORD*]]
12 11
 [**-u**|**--username**[=*USERNAME*]]
13 12
 [SERVER]
14 13
 
15 14
 # DESCRIPTION
16
-Register or log in to a Docker Registry located on the specified
15
+Log in to a Docker Registry located on the specified
17 16
 `SERVER`.  You can specify a URL or a `hostname` for the `SERVER` value. If you
18 17
 do not specify a `SERVER`, the command uses Docker's public registry located at
19 18
 `https://registry-1.docker.io/` by default.  To get a username/password for Docker's public registry, create an account on Docker Hub.
20 19
 
21
-`docker login` requires user to use `sudo` or be `root`, except when: 
20
+`docker login` requires user to use `sudo` or be `root`, except when:
22 21
 
23 22
 1.  connecting to  a remote daemon, such as a `docker-machine` provisioned `docker engine`.
24
-2.  user is added to the `docker` group.  This will impact the security of your system; the `docker` group is `root` equivalent.  See [Docker Daemon Attack Surface](https://docs.docker.com/articles/security/#docker-daemon-attack-surface) for details. 
23
+2.  user is added to the `docker` group.  This will impact the security of your system; the `docker` group is `root` equivalent.  See [Docker Daemon Attack Surface](https://docs.docker.com/articles/security/#docker-daemon-attack-surface) for details.
25 24
 
26 25
 You can log into any public or private repository for which you have
27 26
 credentials.  When you log in, the command stores encoded credentials in
... ...
@@ -31,9 +30,6 @@ credentials.  When you log in, the command stores encoded credentials in
31 31
 >
32 32
 
33 33
 # OPTIONS
34
-**-e**, **--email**=""
35
-   Email
36
-
37 34
 **--help**
38 35
   Print usage statement
39 36
 
... ...
@@ -1,7 +1,6 @@
1 1
 package registry
2 2
 
3 3
 import (
4
-	"encoding/json"
5 4
 	"fmt"
6 5
 	"io/ioutil"
7 6
 	"net/http"
... ...
@@ -24,11 +23,8 @@ func Login(authConfig *types.AuthConfig, registryEndpoint *Endpoint) (string, er
24 24
 // loginV1 tries to register/login to the v1 registry server.
25 25
 func loginV1(authConfig *types.AuthConfig, registryEndpoint *Endpoint) (string, error) {
26 26
 	var (
27
-		status         string
28
-		respBody       []byte
29
-		err            error
30
-		respStatusCode = 0
31
-		serverAddress  = authConfig.ServerAddress
27
+		err           error
28
+		serverAddress = authConfig.ServerAddress
32 29
 	)
33 30
 
34 31
 	logrus.Debugf("attempting v1 login to registry endpoint %s", registryEndpoint)
... ...
@@ -39,93 +35,37 @@ func loginV1(authConfig *types.AuthConfig, registryEndpoint *Endpoint) (string,
39 39
 
40 40
 	loginAgainstOfficialIndex := serverAddress == IndexServer
41 41
 
42
-	// to avoid sending the server address to the server it should be removed before being marshaled
43
-	authCopy := *authConfig
44
-	authCopy.ServerAddress = ""
45
-
46
-	jsonBody, err := json.Marshal(authCopy)
47
-	if err != nil {
48
-		return "", fmt.Errorf("Config Error: %s", err)
49
-	}
50
-
51
-	// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
52
-	b := strings.NewReader(string(jsonBody))
53
-	resp1, err := registryEndpoint.client.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
42
+	req, err := http.NewRequest("GET", serverAddress+"users/", nil)
43
+	req.SetBasicAuth(authConfig.Username, authConfig.Password)
44
+	resp, err := registryEndpoint.client.Do(req)
54 45
 	if err != nil {
55
-		return "", fmt.Errorf("Server Error: %s", err)
46
+		return "", err
56 47
 	}
57
-	defer resp1.Body.Close()
58
-	respStatusCode = resp1.StatusCode
59
-	respBody, err = ioutil.ReadAll(resp1.Body)
48
+	defer resp.Body.Close()
49
+	body, err := ioutil.ReadAll(resp.Body)
60 50
 	if err != nil {
61
-		return "", fmt.Errorf("Server Error: [%#v] %s", respStatusCode, err)
51
+		return "", err
62 52
 	}
63
-
64
-	if respStatusCode == 201 {
53
+	if resp.StatusCode == http.StatusOK {
54
+		return "Login Succeeded", nil
55
+	} else if resp.StatusCode == http.StatusUnauthorized {
65 56
 		if loginAgainstOfficialIndex {
66
-			status = "Account created. Please use the confirmation link we sent" +
67
-				" to your e-mail to activate it."
68
-		} else {
69
-			// *TODO: Use registry configuration to determine what this says, if anything?
70
-			status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it."
71
-		}
72
-	} else if respStatusCode == 400 {
73
-		if string(respBody) == "\"Username or email already exists\"" {
74
-			req, err := http.NewRequest("GET", serverAddress+"users/", nil)
75
-			req.SetBasicAuth(authConfig.Username, authConfig.Password)
76
-			resp, err := registryEndpoint.client.Do(req)
77
-			if err != nil {
78
-				return "", err
79
-			}
80
-			defer resp.Body.Close()
81
-			body, err := ioutil.ReadAll(resp.Body)
82
-			if err != nil {
83
-				return "", err
84
-			}
85
-			if resp.StatusCode == 200 {
86
-				return "Login Succeeded", nil
87
-			} else if resp.StatusCode == 401 {
88
-				return "", fmt.Errorf("Wrong login/password, please try again")
89
-			} else if resp.StatusCode == 403 {
90
-				if loginAgainstOfficialIndex {
91
-					return "", fmt.Errorf("Login: Account is not Active. Please check your e-mail for a confirmation link.")
92
-				}
93
-				// *TODO: Use registry configuration to determine what this says, if anything?
94
-				return "", fmt.Errorf("Login: Account is not Active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)
95
-			} else if resp.StatusCode == 500 { // Issue #14326
96
-				logrus.Errorf("%s returned status code %d. Response Body :\n%s", req.URL.String(), resp.StatusCode, body)
97
-				return "", fmt.Errorf("Internal Server Error")
98
-			}
99
-			return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, resp.StatusCode, resp.Header)
100
-		}
101
-		return "", fmt.Errorf("Registration: %s", respBody)
102
-
103
-	} else if respStatusCode == 401 {
104
-		// This case would happen with private registries where /v1/users is
105
-		// protected, so people can use `docker login` as an auth check.
106
-		req, err := http.NewRequest("GET", serverAddress+"users/", nil)
107
-		req.SetBasicAuth(authConfig.Username, authConfig.Password)
108
-		resp, err := registryEndpoint.client.Do(req)
109
-		if err != nil {
110
-			return "", err
57
+			return "", fmt.Errorf("Wrong login/password, please try again. Haven't got a Docker ID? Create one at https://hub.docker.com")
111 58
 		}
112
-		defer resp.Body.Close()
113
-		body, err := ioutil.ReadAll(resp.Body)
114
-		if err != nil {
115
-			return "", err
116
-		}
117
-		if resp.StatusCode == 200 {
118
-			return "Login Succeeded", nil
119
-		} else if resp.StatusCode == 401 {
120
-			return "", fmt.Errorf("Wrong login/password, please try again")
121
-		} else {
122
-			return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
123
-				resp.StatusCode, resp.Header)
59
+		return "", fmt.Errorf("Wrong login/password, please try again")
60
+	} else if resp.StatusCode == http.StatusForbidden {
61
+		if loginAgainstOfficialIndex {
62
+			return "", fmt.Errorf("Login: Account is not active. Please check your e-mail for a confirmation link.")
124 63
 		}
64
+		// *TODO: Use registry configuration to determine what this says, if anything?
65
+		return "", fmt.Errorf("Login: Account is not active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)
66
+	} else if resp.StatusCode == http.StatusInternalServerError { // Issue #14326
67
+		logrus.Errorf("%s returned status code %d. Response Body :\n%s", req.URL.String(), resp.StatusCode, body)
68
+		return "", fmt.Errorf("Internal Server Error")
125 69
 	} else {
126
-		return "", fmt.Errorf("Unexpected status code [%d] : %s", respStatusCode, respBody)
70
+		return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
71
+			resp.StatusCode, resp.Header)
127 72
 	}
128
-	return status, nil
129 73
 }
130 74
 
131 75
 // loginV2 tries to login to the v2 registry server. The given registry endpoint has been
... ...
@@ -14,7 +14,6 @@ func buildAuthConfigs() map[string]types.AuthConfig {
14 14
 		authConfigs[registry] = types.AuthConfig{
15 15
 			Username: "docker-user",
16 16
 			Password: "docker-pass",
17
-			Email:    "docker@docker.io",
18 17
 		}
19 18
 	}
20 19
 
... ...
@@ -30,9 +29,6 @@ func TestSameAuthDataPostSave(t *testing.T) {
30 30
 	if authConfig.Password != "docker-pass" {
31 31
 		t.Fail()
32 32
 	}
33
-	if authConfig.Email != "docker@docker.io" {
34
-		t.Fail()
35
-	}
36 33
 	if authConfig.Auth != "" {
37 34
 		t.Fail()
38 35
 	}
... ...
@@ -62,17 +58,14 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
62 62
 	registryAuth := types.AuthConfig{
63 63
 		Username: "foo-user",
64 64
 		Password: "foo-pass",
65
-		Email:    "foo@example.com",
66 65
 	}
67 66
 	localAuth := types.AuthConfig{
68 67
 		Username: "bar-user",
69 68
 		Password: "bar-pass",
70
-		Email:    "bar@example.com",
71 69
 	}
72 70
 	officialAuth := types.AuthConfig{
73 71
 		Username: "baz-user",
74 72
 		Password: "baz-pass",
75
-		Email:    "baz@example.com",
76 73
 	}
77 74
 	authConfigs[IndexServer] = officialAuth
78 75
 
... ...
@@ -105,7 +98,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
105 105
 
106 106
 	for configKey, registries := range validRegistries {
107 107
 		configured, ok := expectedAuths[configKey]
108
-		if !ok || configured.Email == "" {
108
+		if !ok {
109 109
 			t.Fail()
110 110
 		}
111 111
 		index := &registrytypes.IndexInfo{
... ...
@@ -114,13 +107,13 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
114 114
 		for _, registry := range registries {
115 115
 			authConfigs[registry] = configured
116 116
 			resolved := ResolveAuthConfig(authConfigs, index)
117
-			if resolved.Email != configured.Email {
118
-				t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
117
+			if resolved.Username != configured.Username || resolved.Password != configured.Password {
118
+				t.Errorf("%s -> %v != %v\n", registry, resolved, configured)
119 119
 			}
120 120
 			delete(authConfigs, registry)
121 121
 			resolved = ResolveAuthConfig(authConfigs, index)
122
-			if resolved.Email == configured.Email {
123
-				t.Errorf("%s -> %q == %q\n", registry, resolved.Email, configured.Email)
122
+			if resolved.Username == configured.Username || resolved.Password == configured.Password {
123
+				t.Errorf("%s -> %v == %v\n", registry, resolved, configured)
124 124
 			}
125 125
 		}
126 126
 	}
... ...
@@ -752,7 +752,6 @@ func (r *Session) GetAuthConfig(withPasswd bool) *types.AuthConfig {
752 752
 	return &types.AuthConfig{
753 753
 		Username: r.authConfig.Username,
754 754
 		Password: password,
755
-		Email:    r.authConfig.Email,
756 755
 	}
757 756
 }
758 757