Przeglądaj źródła

Merge pull request #20565 from kencochrane/remove_email_on_login

Remove email address field from login
Aaron Lehmann 9 lat temu
rodzic
commit
2453262e7b

+ 12 - 25
api/client/login.go

@@ -17,7 +17,7 @@ import (
 	"github.com/docker/engine-api/types"
 	"github.com/docker/engine-api/types"
 )
 )
 
 
-// CmdLogin logs in or registers a user to a Docker registry service.
+// CmdLogin logs in a user to a Docker registry service.
 //
 //
 // If no server is specified, the user will be logged into or registered to the registry's index server.
 // If no server is specified, the user will be logged into or registered to the registry's index server.
 //
 //
@@ -28,7 +28,9 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 
 
 	flUser := cmd.String([]string{"u", "-username"}, "", "Username")
 	flUser := cmd.String([]string{"u", "-username"}, "", "Username")
 	flPassword := cmd.String([]string{"p", "-password"}, "", "Password")
 	flPassword := cmd.String([]string{"p", "-password"}, "", "Password")
-	flEmail := cmd.String([]string{"e", "-email"}, "", "Email")
+
+	// Deprecated in 1.11: Should be removed in docker 1.13
+	cmd.String([]string{"#e", "#-email"}, "", "Email")
 
 
 	cmd.ParseFlags(args, true)
 	cmd.ParseFlags(args, true)
 
 
@@ -38,13 +40,15 @@ func (cli *DockerCli) CmdLogin(args ...string) error {
 	}
 	}
 
 
 	var serverAddress string
 	var serverAddress string
+	var isDefaultRegistry bool
 	if len(cmd.Args()) > 0 {
 	if len(cmd.Args()) > 0 {
 		serverAddress = cmd.Arg(0)
 		serverAddress = cmd.Arg(0)
 	} else {
 	} else {
 		serverAddress = cli.electAuthServer()
 		serverAddress = cli.electAuthServer()
+		isDefaultRegistry = true
 	}
 	}
 
 
-	authConfig, err := cli.configureAuth(*flUser, *flPassword, *flEmail, serverAddress)
+	authConfig, err := cli.configureAuth(*flUser, *flPassword, serverAddress, isDefaultRegistry)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -77,7 +81,7 @@ func (cli *DockerCli) promptWithDefault(prompt string, configDefault string) {
 	}
 	}
 }
 }
 
 
-func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress string) (types.AuthConfig, error) {
+func (cli *DockerCli) configureAuth(flUser, flPassword, serverAddress string, isDefaultRegistry bool) (types.AuthConfig, error) {
 	authconfig, err := getCredentials(cli.configFile, serverAddress)
 	authconfig, err := getCredentials(cli.configFile, serverAddress)
 	if err != nil {
 	if err != nil {
 		return authconfig, err
 		return authconfig, err
@@ -86,6 +90,10 @@ func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress s
 	authconfig.Username = strings.TrimSpace(authconfig.Username)
 	authconfig.Username = strings.TrimSpace(authconfig.Username)
 
 
 	if flUser = strings.TrimSpace(flUser); flUser == "" {
 	if flUser = strings.TrimSpace(flUser); flUser == "" {
+		if isDefaultRegistry {
+			// if this is a defauly registry (docker hub), then display the following message.
+			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.")
+		}
 		cli.promptWithDefault("Username", authconfig.Username)
 		cli.promptWithDefault("Username", authconfig.Username)
 		flUser = readInput(cli.in, cli.out)
 		flUser = readInput(cli.in, cli.out)
 		flUser = strings.TrimSpace(flUser)
 		flUser = strings.TrimSpace(flUser)
@@ -115,29 +123,8 @@ func (cli *DockerCli) configureAuth(flUser, flPassword, flEmail, serverAddress s
 		}
 		}
 	}
 	}
 
 
-	// Assume that a different username means they may not want to use
-	// the email from the config file, so prompt it
-	if flUser != authconfig.Username {
-		if flEmail == "" {
-			cli.promptWithDefault("Email", authconfig.Email)
-			flEmail = readInput(cli.in, cli.out)
-			if flEmail == "" {
-				flEmail = authconfig.Email
-			}
-		}
-	} else {
-		// However, if they don't override the username use the
-		// email from the cmd line if specified. IOW, allow
-		// then to change/override them.  And if not specified, just
-		// use what's in the config file
-		if flEmail == "" {
-			flEmail = authconfig.Email
-		}
-	}
-
 	authconfig.Username = flUser
 	authconfig.Username = flUser
 	authconfig.Password = flPassword
 	authconfig.Password = flPassword
-	authconfig.Email = flEmail
 	authconfig.ServerAddress = serverAddress
 	authconfig.ServerAddress = serverAddress
 
 
 	return authconfig, nil
 	return authconfig, nil

+ 1 - 1
api/client/utils.go

@@ -48,7 +48,7 @@ func (cli *DockerCli) registryAuthenticationPrivilegedFunc(index *registrytypes.
 	return func() (string, error) {
 	return func() (string, error) {
 		fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
 		fmt.Fprintf(cli.out, "\nPlease login prior to %s:\n", cmdName)
 		indexServer := registry.GetAuthConfigKey(index)
 		indexServer := registry.GetAuthConfigKey(index)
-		authConfig, err := cli.configureAuth("", "", "", indexServer)
+		authConfig, err := cli.configureAuth("", "", indexServer, false)
 		if err != nil {
 		if err != nil {
 			return "", err
 			return "", err
 		}
 		}

+ 0 - 5
cliconfig/config.go

@@ -88,11 +88,6 @@ func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
-		origEmail := strings.Split(arr[1], " = ")
-		if len(origEmail) != 2 {
-			return fmt.Errorf("Invalid Auth config file")
-		}
-		authConfig.Email = origEmail[1]
 		authConfig.ServerAddress = defaultIndexserver
 		authConfig.ServerAddress = defaultIndexserver
 		configFile.AuthConfigs[defaultIndexserver] = authConfig
 		configFile.AuthConfigs[defaultIndexserver] = authConfig
 	} else {
 	} else {

+ 102 - 25
cliconfig/config_test.go

@@ -111,12 +111,9 @@ func TestOldInvalidsAuth(t *testing.T) {
 	invalids := map[string]string{
 	invalids := map[string]string{
 		`username = test`: "The Auth config file is empty",
 		`username = test`: "The Auth config file is empty",
 		`username
 		`username
-password
-email`: "Invalid Auth config file",
+password`: "Invalid Auth config file",
 		`username = test
 		`username = test
 email`: "Invalid auth configuration file",
 email`: "Invalid auth configuration file",
-		`username = am9lam9lOmhlbGxv
-email`: "Invalid Auth config file",
 	}
 	}
 
 
 	tmpHome, err := ioutil.TempDir("", "config-test")
 	tmpHome, err := ioutil.TempDir("", "config-test")
@@ -164,7 +161,7 @@ func TestOldValidAuth(t *testing.T) {
 
 
 	fn := filepath.Join(tmpHome, oldConfigfile)
 	fn := filepath.Join(tmpHome, oldConfigfile)
 	js := `username = am9lam9lOmhlbGxv
 	js := `username = am9lam9lOmhlbGxv
-email = user@example.com`
+	email = user@example.com`
 	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
 	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -176,15 +173,23 @@ email = user@example.com`
 
 
 	// defaultIndexserver is https://index.docker.io/v1/
 	// defaultIndexserver is https://index.docker.io/v1/
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
+	if ac.Username != "joejoe" || ac.Password != "hello" {
 		t.Fatalf("Missing data from parsing:\n%q", config)
 		t.Fatalf("Missing data from parsing:\n%q", config)
 	}
 	}
 
 
 	// Now save it and make sure it shows up in new form
 	// Now save it and make sure it shows up in new form
 	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
 	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
 
 
-	if !strings.Contains(configStr, "user@example.com") {
-		t.Fatalf("Should have save in new form: %s", configStr)
+	expConfStr := `{
+	"auths": {
+		"https://index.docker.io/v1/": {
+			"auth": "am9lam9lOmhlbGxv"
+		}
+	}
+}`
+
+	if configStr != expConfStr {
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
 	}
 	}
 }
 }
 
 
@@ -239,15 +244,24 @@ func TestOldJson(t *testing.T) {
 	}
 	}
 
 
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
+	if ac.Username != "joejoe" || ac.Password != "hello" {
 		t.Fatalf("Missing data from parsing:\n%q", config)
 		t.Fatalf("Missing data from parsing:\n%q", config)
 	}
 	}
 
 
 	// Now save it and make sure it shows up in new form
 	// Now save it and make sure it shows up in new form
 	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
 	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
 
 
-	if !strings.Contains(configStr, "user@example.com") {
-		t.Fatalf("Should have save in new form: %s", configStr)
+	expConfStr := `{
+	"auths": {
+		"https://index.docker.io/v1/": {
+			"auth": "am9lam9lOmhlbGxv",
+			"email": "user@example.com"
+		}
+	}
+}`
+
+	if configStr != expConfStr {
+		t.Fatalf("Should have save in new form: \n'%s'\n not \n'%s'\n", configStr, expConfStr)
 	}
 	}
 }
 }
 
 
@@ -259,7 +273,7 @@ func TestNewJson(t *testing.T) {
 	defer os.RemoveAll(tmpHome)
 	defer os.RemoveAll(tmpHome)
 
 
 	fn := filepath.Join(tmpHome, ConfigFileName)
 	fn := filepath.Join(tmpHome, ConfigFileName)
-	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }`
+	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }`
 	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
 	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
@@ -270,15 +284,62 @@ func TestNewJson(t *testing.T) {
 	}
 	}
 
 
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
+	if ac.Username != "joejoe" || ac.Password != "hello" {
 		t.Fatalf("Missing data from parsing:\n%q", config)
 		t.Fatalf("Missing data from parsing:\n%q", config)
 	}
 	}
 
 
 	// Now save it and make sure it shows up in new form
 	// Now save it and make sure it shows up in new form
 	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
 	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
 
 
-	if !strings.Contains(configStr, "user@example.com") {
-		t.Fatalf("Should have save in new form: %s", configStr)
+	expConfStr := `{
+	"auths": {
+		"https://index.docker.io/v1/": {
+			"auth": "am9lam9lOmhlbGxv"
+		}
+	}
+}`
+
+	if configStr != expConfStr {
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
+	}
+}
+
+func TestNewJsonNoEmail(t *testing.T) {
+	tmpHome, err := ioutil.TempDir("", "config-test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tmpHome)
+
+	fn := filepath.Join(tmpHome, ConfigFileName)
+	js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } }`
+	if err := ioutil.WriteFile(fn, []byte(js), 0600); err != nil {
+		t.Fatal(err)
+	}
+
+	config, err := Load(tmpHome)
+	if err != nil {
+		t.Fatalf("Failed loading on empty json file: %q", err)
+	}
+
+	ac := config.AuthConfigs["https://index.docker.io/v1/"]
+	if ac.Username != "joejoe" || ac.Password != "hello" {
+		t.Fatalf("Missing data from parsing:\n%q", config)
+	}
+
+	// Now save it and make sure it shows up in new form
+	configStr := saveConfigAndValidateNewFormat(t, config, tmpHome)
+
+	expConfStr := `{
+	"auths": {
+		"https://index.docker.io/v1/": {
+			"auth": "am9lam9lOmhlbGxv"
+		}
+	}
+}`
+
+	if configStr != expConfStr {
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", configStr, expConfStr)
 	}
 	}
 }
 }
 
 
@@ -366,7 +427,7 @@ func TestJsonReaderNoFile(t *testing.T) {
 	}
 	}
 
 
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
+	if ac.Username != "joejoe" || ac.Password != "hello" {
 		t.Fatalf("Missing data from parsing:\n%q", config)
 		t.Fatalf("Missing data from parsing:\n%q", config)
 	}
 	}
 
 
@@ -381,7 +442,7 @@ func TestOldJsonReaderNoFile(t *testing.T) {
 	}
 	}
 
 
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
 	ac := config.AuthConfigs["https://index.docker.io/v1/"]
-	if ac.Email != "user@example.com" || ac.Username != "joejoe" || ac.Password != "hello" {
+	if ac.Username != "joejoe" || ac.Password != "hello" {
 		t.Fatalf("Missing data from parsing:\n%q", config)
 		t.Fatalf("Missing data from parsing:\n%q", config)
 	}
 	}
 }
 }
@@ -404,7 +465,7 @@ func TestJsonWithPsFormatNoFile(t *testing.T) {
 
 
 func TestJsonSaveWithNoFile(t *testing.T) {
 func TestJsonSaveWithNoFile(t *testing.T) {
 	js := `{
 	js := `{
-		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } },
+		"auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } },
 		"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
 		"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
 }`
 }`
 	config, err := LoadFromReader(strings.NewReader(js))
 	config, err := LoadFromReader(strings.NewReader(js))
@@ -426,9 +487,16 @@ func TestJsonSaveWithNoFile(t *testing.T) {
 		t.Fatalf("Failed saving to file: %q", err)
 		t.Fatalf("Failed saving to file: %q", err)
 	}
 	}
 	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
 	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
-	if !strings.Contains(string(buf), `"auths":`) ||
-		!strings.Contains(string(buf), "user@example.com") {
-		t.Fatalf("Should have save in new form: %s", string(buf))
+	expConfStr := `{
+	"auths": {
+		"https://index.docker.io/v1/": {
+			"auth": "am9lam9lOmhlbGxv"
+		}
+	},
+	"psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}"
+}`
+	if string(buf) != expConfStr {
+		t.Fatalf("Should have save in new form: \n%s\nnot \n%s", string(buf), expConfStr)
 	}
 	}
 }
 }
 
 
@@ -454,14 +522,23 @@ func TestLegacyJsonSaveWithNoFile(t *testing.T) {
 		t.Fatalf("Failed saving to file: %q", err)
 		t.Fatalf("Failed saving to file: %q", err)
 	}
 	}
 	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
 	buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName))
-	if !strings.Contains(string(buf), `"auths":`) ||
-		!strings.Contains(string(buf), "user@example.com") {
-		t.Fatalf("Should have save in new form: %s", string(buf))
+
+	expConfStr := `{
+	"auths": {
+		"https://index.docker.io/v1/": {
+			"auth": "am9lam9lOmhlbGxv",
+			"email": "user@example.com"
+		}
+	}
+}`
+
+	if string(buf) != expConfStr {
+		t.Fatalf("Should have save in new form: \n%s\n not \n%s", string(buf), expConfStr)
 	}
 	}
 }
 }
 
 
 func TestEncodeAuth(t *testing.T) {
 func TestEncodeAuth(t *testing.T) {
-	newAuthConfig := &types.AuthConfig{Username: "ken", Password: "test", Email: "test@example.com"}
+	newAuthConfig := &types.AuthConfig{Username: "ken", Password: "test"}
 	authStr := encodeAuth(newAuthConfig)
 	authStr := encodeAuth(newAuthConfig)
 	decAuthConfig := &types.AuthConfig{}
 	decAuthConfig := &types.AuthConfig{}
 	var err error
 	var err error

+ 3 - 3
contrib/completion/bash/docker

@@ -811,7 +811,7 @@ _docker_daemon() {
  			return
  			return
  			;;
  			;;
  	esac
  	esac
- 
+
  	local key=$(__docker_map_key_of_current_option '--storage-opt')
  	local key=$(__docker_map_key_of_current_option '--storage-opt')
  	case "$key" in
  	case "$key" in
  		dm.@(blkdiscard|override_udev_sync_check|use_deferred_@(removal|deletion)))
  		dm.@(blkdiscard|override_udev_sync_check|use_deferred_@(removal|deletion)))
@@ -1205,14 +1205,14 @@ _docker_load() {
 
 
 _docker_login() {
 _docker_login() {
 	case "$prev" in
 	case "$prev" in
-		--email|-e|--password|-p|--username|-u)
+		--password|-p|--username|-u)
 			return
 			return
 			;;
 			;;
 	esac
 	esac
 
 
 	case "$cur" in
 	case "$cur" in
 		-*)
 		-*)
-			COMPREPLY=( $( compgen -W "--email -e --help --password -p --username -u" -- "$cur" ) )
+			COMPREPLY=( $( compgen -W "--help --password -p --username -u" -- "$cur" ) )
 			;;
 			;;
 	esac
 	esac
 }
 }

+ 1 - 4
contrib/completion/fish/docker.fish

@@ -221,8 +221,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from load' -l help -d 'Print
 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'
 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'
 
 
 # login
 # login
-complete -c docker -f -n '__fish_docker_no_subcommand' -a login -d 'Register or log in to a Docker registry server'
-complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s e -l email -d 'Email'
+complete -c docker -f -n '__fish_docker_no_subcommand' -a login -d 'Log in to a Docker registry server'
 complete -c docker -A -f -n '__fish_seen_subcommand_from login' -l help -d 'Print usage'
 complete -c docker -A -f -n '__fish_seen_subcommand_from login' -l help -d 'Print usage'
 complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s p -l password -d 'Password'
 complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s p -l password -d 'Password'
 complete -c docker -A -f -n '__fish_seen_subcommand_from login' -s u -l username -d 'Username'
 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
 complete -c docker -f -n '__fish_docker_no_subcommand' -a wait -d 'Block until a container stops, then print its exit code'
 complete -c docker -f -n '__fish_docker_no_subcommand' -a wait -d 'Block until a container stops, then print its exit code'
 complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -l help -d 'Print usage'
 complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -l help -d 'Print usage'
 complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -a '(__fish_print_docker_containers running)' -d "Container"
 complete -c docker -A -f -n '__fish_seen_subcommand_from wait' -a '(__fish_print_docker_containers running)' -d "Container"
-
-

+ 0 - 1
contrib/completion/zsh/_docker

@@ -812,7 +812,6 @@ __docker_subcommand() {
         (login)
         (login)
             _arguments $(__docker_arguments) \
             _arguments $(__docker_arguments) \
                 $opts_help \
                 $opts_help \
-                "($help -e --email)"{-e=,--email=}"[Email]:email: " \
                 "($help -p --password)"{-p=,--password=}"[Password]:password: " \
                 "($help -p --password)"{-p=,--password=}"[Password]:password: " \
                 "($help -u --user)"{-u=,--user=}"[Username]:username: " \
                 "($help -u --user)"{-u=,--user=}"[Username]:username: " \
                 "($help -)1:server: " && ret=0
                 "($help -)1:server: " && ret=0

+ 7 - 0
docs/deprecated.md

@@ -14,6 +14,13 @@ weight=80
 
 
 The following list of features are deprecated in Engine.
 The following list of features are deprecated in Engine.
 
 
+### `-e` and `--email` flags on `docker login`
+**Deprecated In Release: v1.11**
+
+**Target For Removal In Release: v1.13**
+
+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.
+
 ### Ambiguous event fields in API
 ### Ambiguous event fields in API
 **Deprecated In Release: v1.10**
 **Deprecated In Release: v1.10**
 
 

+ 3 - 4
docs/reference/commandline/login.md

@@ -12,10 +12,9 @@ parent = "smn_cli"
 
 
     Usage: docker login [OPTIONS] [SERVER]
     Usage: docker login [OPTIONS] [SERVER]
 
 
-    Register or log in to a Docker registry server, if no server is
+    Log in to a Docker registry server, if no server is
 	specified "https://index.docker.io/v1/" is the default.
 	specified "https://index.docker.io/v1/" is the default.
 
 
-      -e, --email=""       Email
       --help               Print usage
       --help               Print usage
       -p, --password=""    Password
       -p, --password=""    Password
       -u, --username=""    Username
       -u, --username=""    Username
@@ -27,10 +26,10 @@ adding the server name.
     $ docker login localhost:8080
     $ docker login localhost:8080
 
 
 
 
-`docker login` requires user to use `sudo` or be `root`, except when: 
+`docker login` requires user to use `sudo` or be `root`, except when:
 
 
 1.  connecting to a remote daemon, such as a `docker-machine` provisioned `docker engine`.
 1.  connecting to a remote daemon, such as a `docker-machine` provisioned `docker engine`.
-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. 
+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.
 
 
 You can log into any public or private repository for which you have
 You can log into any public or private repository for which you have
 credentials.  When you log in, the command stores encoded credentials in
 credentials.  When you log in, the command stores encoded credentials in

+ 2 - 6
docs/userguide/containers/dockerrepos.md

@@ -33,15 +33,11 @@ Docker itself provides access to Docker Hub services via the `docker search`,
 ### Account creation and login
 ### Account creation and login
 Typically, you'll want to start by creating an account on Docker Hub (if you haven't
 Typically, you'll want to start by creating an account on Docker Hub (if you haven't
 already) and logging in. You can create your account directly on
 already) and logging in. You can create your account directly on
-[Docker Hub](https://hub.docker.com/account/signup/), or by running:
+[Docker Hub](https://hub.docker.com/account/signup/).
 
 
     $ docker login
     $ docker login
 
 
-This will prompt you for a user name, which will become the public namespace for your
-public repositories.
-If your user name is available, Docker will prompt you to enter a password and your
-e-mail address. It will then automatically log you in. You can now commit and
-push your own images up to your repos on Docker Hub.
+You can now commit and push your own images up to your repos on Docker Hub.
 
 
 > **Note:**
 > **Note:**
 > Your authentication credentials will be stored in the `~/.docker/config.json`
 > Your authentication credentials will be stored in the `~/.docker/config.json`

+ 1 - 1
integration-cli/docker_cli_build_test.go

@@ -6568,7 +6568,7 @@ func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *check.C) {
 }
 }
 
 
 func (s *DockerRegistryAuthSuite) TestBuildFromAuthenticatedRegistry(c *check.C) {
 func (s *DockerRegistryAuthSuite) TestBuildFromAuthenticatedRegistry(c *check.C) {
-	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, "-e", s.reg.email, privateRegistryURL)
+	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL)
 
 
 	baseImage := privateRegistryURL + "/baseimage"
 	baseImage := privateRegistryURL + "/baseimage"
 
 

+ 14 - 0
integration-cli/docker_cli_login_test.go

@@ -20,11 +20,25 @@ func (s *DockerSuite) TestLoginWithoutTTY(c *check.C) {
 }
 }
 
 
 func (s *DockerRegistryAuthSuite) TestLoginToPrivateRegistry(c *check.C) {
 func (s *DockerRegistryAuthSuite) TestLoginToPrivateRegistry(c *check.C) {
+	// wrong credentials
+	out, _, err := dockerCmdWithError("login", "-u", s.reg.username, "-p", "WRONGPASSWORD", privateRegistryURL)
+	c.Assert(err, checker.NotNil, check.Commentf(out))
+	c.Assert(out, checker.Contains, "401 Unauthorized")
+
+	// now it's fine
+	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL)
+}
+
+func (s *DockerRegistryAuthSuite) TestLoginToPrivateRegistryDeprecatedEmailFlag(c *check.C) {
+	// Test to make sure login still works with the deprecated -e and --email flags
 	// wrong credentials
 	// wrong credentials
 	out, _, err := dockerCmdWithError("login", "-u", s.reg.username, "-p", "WRONGPASSWORD", "-e", s.reg.email, privateRegistryURL)
 	out, _, err := dockerCmdWithError("login", "-u", s.reg.username, "-p", "WRONGPASSWORD", "-e", s.reg.email, privateRegistryURL)
 	c.Assert(err, checker.NotNil, check.Commentf(out))
 	c.Assert(err, checker.NotNil, check.Commentf(out))
 	c.Assert(out, checker.Contains, "401 Unauthorized")
 	c.Assert(out, checker.Contains, "401 Unauthorized")
 
 
 	// now it's fine
 	// now it's fine
+	// -e flag
 	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, "-e", s.reg.email, privateRegistryURL)
 	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, "-e", s.reg.email, privateRegistryURL)
+	// --email flag
+	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, "--email", s.reg.email, privateRegistryURL)
 }
 }

+ 0 - 1
integration-cli/docker_cli_pull_local_test.go

@@ -390,7 +390,6 @@ func (s *DockerRegistryAuthSuite) TestPullWithExternalAuth(c *check.C) {
 	b, err := ioutil.ReadFile(configPath)
 	b, err := ioutil.ReadFile(configPath)
 	c.Assert(err, checker.IsNil)
 	c.Assert(err, checker.IsNil)
 	c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":")
 	c.Assert(string(b), checker.Not(checker.Contains), "\"auth\":")
-	c.Assert(string(b), checker.Contains, "email")
 
 
 	dockerCmd(c, "--config", tmp, "tag", "busybox", repoName)
 	dockerCmd(c, "--config", tmp, "tag", "busybox", repoName)
 	dockerCmd(c, "--config", tmp, "push", repoName)
 	dockerCmd(c, "--config", tmp, "push", repoName)

+ 1 - 1
integration-cli/docker_cli_v2_only_test.go

@@ -112,7 +112,7 @@ func (s *DockerRegistrySuite) TestV1(c *check.C) {
 	s.d.Cmd("run", repoName)
 	s.d.Cmd("run", repoName)
 	c.Assert(v1Repo, check.Not(check.Equals), 1, check.Commentf("Expected v1 repository access after run"))
 	c.Assert(v1Repo, check.Not(check.Equals), 1, check.Commentf("Expected v1 repository access after run"))
 
 
-	s.d.Cmd("login", "-u", "richard", "-p", "testtest", "-e", "testuser@testdomain.com", reg.hostport)
+	s.d.Cmd("login", "-u", "richard", "-p", "testtest", reg.hostport)
 	c.Assert(v1Logins, check.Not(check.Equals), 0, check.Commentf("Expected v1 login attempt"))
 	c.Assert(v1Logins, check.Not(check.Equals), 0, check.Commentf("Expected v1 login attempt"))
 
 
 	s.d.Cmd("tag", "busybox", repoName)
 	s.d.Cmd("tag", "busybox", repoName)

+ 4 - 8
man/docker-login.1.md

@@ -2,26 +2,25 @@
 % Docker Community
 % Docker Community
 % JUNE 2014
 % JUNE 2014
 # NAME
 # NAME
-docker-login - Register or log in to a Docker registry. 
+docker-login - Log in to a Docker registry.
 
 
 # SYNOPSIS
 # SYNOPSIS
 **docker login**
 **docker login**
-[**-e**|**--email**[=*EMAIL*]]
 [**--help**]
 [**--help**]
 [**-p**|**--password**[=*PASSWORD*]]
 [**-p**|**--password**[=*PASSWORD*]]
 [**-u**|**--username**[=*USERNAME*]]
 [**-u**|**--username**[=*USERNAME*]]
 [SERVER]
 [SERVER]
 
 
 # DESCRIPTION
 # DESCRIPTION
-Register or log in to a Docker Registry located on the specified
+Log in to a Docker Registry located on the specified
 `SERVER`.  You can specify a URL or a `hostname` for the `SERVER` value. If you
 `SERVER`.  You can specify a URL or a `hostname` for the `SERVER` value. If you
 do not specify a `SERVER`, the command uses Docker's public registry located at
 do not specify a `SERVER`, the command uses Docker's public registry located at
 `https://registry-1.docker.io/` by default.  To get a username/password for Docker's public registry, create an account on Docker Hub.
 `https://registry-1.docker.io/` by default.  To get a username/password for Docker's public registry, create an account on Docker Hub.
 
 
-`docker login` requires user to use `sudo` or be `root`, except when: 
+`docker login` requires user to use `sudo` or be `root`, except when:
 
 
 1.  connecting to  a remote daemon, such as a `docker-machine` provisioned `docker engine`.
 1.  connecting to  a remote daemon, such as a `docker-machine` provisioned `docker engine`.
-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. 
+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.
 
 
 You can log into any public or private repository for which you have
 You can log into any public or private repository for which you have
 credentials.  When you log in, the command stores encoded credentials in
 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
 >
 >
 
 
 # OPTIONS
 # OPTIONS
-**-e**, **--email**=""
-   Email
-
 **--help**
 **--help**
   Print usage statement
   Print usage statement
 
 

+ 24 - 84
registry/auth.go

@@ -1,7 +1,6 @@
 package registry
 package registry
 
 
 import (
 import (
-	"encoding/json"
 	"fmt"
 	"fmt"
 	"io/ioutil"
 	"io/ioutil"
 	"net/http"
 	"net/http"
@@ -24,11 +23,8 @@ func Login(authConfig *types.AuthConfig, registryEndpoint *Endpoint) (string, er
 // loginV1 tries to register/login to the v1 registry server.
 // loginV1 tries to register/login to the v1 registry server.
 func loginV1(authConfig *types.AuthConfig, registryEndpoint *Endpoint) (string, error) {
 func loginV1(authConfig *types.AuthConfig, registryEndpoint *Endpoint) (string, error) {
 	var (
 	var (
-		status         string
-		respBody       []byte
-		err            error
-		respStatusCode = 0
-		serverAddress  = authConfig.ServerAddress
+		err           error
+		serverAddress = authConfig.ServerAddress
 	)
 	)
 
 
 	logrus.Debugf("attempting v1 login to registry endpoint %s", registryEndpoint)
 	logrus.Debugf("attempting v1 login to registry endpoint %s", registryEndpoint)
@@ -39,93 +35,37 @@ func loginV1(authConfig *types.AuthConfig, registryEndpoint *Endpoint) (string,
 
 
 	loginAgainstOfficialIndex := serverAddress == IndexServer
 	loginAgainstOfficialIndex := serverAddress == IndexServer
 
 
-	// to avoid sending the server address to the server it should be removed before being marshaled
-	authCopy := *authConfig
-	authCopy.ServerAddress = ""
-
-	jsonBody, err := json.Marshal(authCopy)
-	if err != nil {
-		return "", fmt.Errorf("Config Error: %s", err)
-	}
-
-	// using `bytes.NewReader(jsonBody)` here causes the server to respond with a 411 status.
-	b := strings.NewReader(string(jsonBody))
-	resp1, err := registryEndpoint.client.Post(serverAddress+"users/", "application/json; charset=utf-8", b)
+	req, err := http.NewRequest("GET", serverAddress+"users/", nil)
+	req.SetBasicAuth(authConfig.Username, authConfig.Password)
+	resp, err := registryEndpoint.client.Do(req)
 	if err != nil {
 	if err != nil {
-		return "", fmt.Errorf("Server Error: %s", err)
+		return "", err
 	}
 	}
-	defer resp1.Body.Close()
-	respStatusCode = resp1.StatusCode
-	respBody, err = ioutil.ReadAll(resp1.Body)
+	defer resp.Body.Close()
+	body, err := ioutil.ReadAll(resp.Body)
 	if err != nil {
 	if err != nil {
-		return "", fmt.Errorf("Server Error: [%#v] %s", respStatusCode, err)
+		return "", err
 	}
 	}
-
-	if respStatusCode == 201 {
+	if resp.StatusCode == http.StatusOK {
+		return "Login Succeeded", nil
+	} else if resp.StatusCode == http.StatusUnauthorized {
 		if loginAgainstOfficialIndex {
 		if loginAgainstOfficialIndex {
-			status = "Account created. Please use the confirmation link we sent" +
-				" to your e-mail to activate it."
-		} else {
-			// *TODO: Use registry configuration to determine what this says, if anything?
-			status = "Account created. Please see the documentation of the registry " + serverAddress + " for instructions how to activate it."
-		}
-	} else if respStatusCode == 400 {
-		if string(respBody) == "\"Username or email already exists\"" {
-			req, err := http.NewRequest("GET", serverAddress+"users/", nil)
-			req.SetBasicAuth(authConfig.Username, authConfig.Password)
-			resp, err := registryEndpoint.client.Do(req)
-			if err != nil {
-				return "", err
-			}
-			defer resp.Body.Close()
-			body, err := ioutil.ReadAll(resp.Body)
-			if err != nil {
-				return "", err
-			}
-			if resp.StatusCode == 200 {
-				return "Login Succeeded", nil
-			} else if resp.StatusCode == 401 {
-				return "", fmt.Errorf("Wrong login/password, please try again")
-			} else if resp.StatusCode == 403 {
-				if loginAgainstOfficialIndex {
-					return "", fmt.Errorf("Login: Account is not Active. Please check your e-mail for a confirmation link.")
-				}
-				// *TODO: Use registry configuration to determine what this says, if anything?
-				return "", fmt.Errorf("Login: Account is not Active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)
-			} else if resp.StatusCode == 500 { // Issue #14326
-				logrus.Errorf("%s returned status code %d. Response Body :\n%s", req.URL.String(), resp.StatusCode, body)
-				return "", fmt.Errorf("Internal Server Error")
-			}
-			return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body, resp.StatusCode, resp.Header)
-		}
-		return "", fmt.Errorf("Registration: %s", respBody)
-
-	} else if respStatusCode == 401 {
-		// This case would happen with private registries where /v1/users is
-		// protected, so people can use `docker login` as an auth check.
-		req, err := http.NewRequest("GET", serverAddress+"users/", nil)
-		req.SetBasicAuth(authConfig.Username, authConfig.Password)
-		resp, err := registryEndpoint.client.Do(req)
-		if err != nil {
-			return "", err
+			return "", fmt.Errorf("Wrong login/password, please try again. Haven't got a Docker ID? Create one at https://hub.docker.com")
 		}
 		}
-		defer resp.Body.Close()
-		body, err := ioutil.ReadAll(resp.Body)
-		if err != nil {
-			return "", err
-		}
-		if resp.StatusCode == 200 {
-			return "Login Succeeded", nil
-		} else if resp.StatusCode == 401 {
-			return "", fmt.Errorf("Wrong login/password, please try again")
-		} else {
-			return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
-				resp.StatusCode, resp.Header)
+		return "", fmt.Errorf("Wrong login/password, please try again")
+	} else if resp.StatusCode == http.StatusForbidden {
+		if loginAgainstOfficialIndex {
+			return "", fmt.Errorf("Login: Account is not active. Please check your e-mail for a confirmation link.")
 		}
 		}
+		// *TODO: Use registry configuration to determine what this says, if anything?
+		return "", fmt.Errorf("Login: Account is not active. Please see the documentation of the registry %s for instructions how to activate it.", serverAddress)
+	} else if resp.StatusCode == http.StatusInternalServerError { // Issue #14326
+		logrus.Errorf("%s returned status code %d. Response Body :\n%s", req.URL.String(), resp.StatusCode, body)
+		return "", fmt.Errorf("Internal Server Error")
 	} else {
 	} else {
-		return "", fmt.Errorf("Unexpected status code [%d] : %s", respStatusCode, respBody)
+		return "", fmt.Errorf("Login: %s (Code: %d; Headers: %s)", body,
+			resp.StatusCode, resp.Header)
 	}
 	}
-	return status, nil
 }
 }
 
 
 // loginV2 tries to login to the v2 registry server. The given registry endpoint has been
 // loginV2 tries to login to the v2 registry server. The given registry endpoint has been

+ 5 - 12
registry/auth_test.go

@@ -14,7 +14,6 @@ func buildAuthConfigs() map[string]types.AuthConfig {
 		authConfigs[registry] = types.AuthConfig{
 		authConfigs[registry] = types.AuthConfig{
 			Username: "docker-user",
 			Username: "docker-user",
 			Password: "docker-pass",
 			Password: "docker-pass",
-			Email:    "docker@docker.io",
 		}
 		}
 	}
 	}
 
 
@@ -30,9 +29,6 @@ func TestSameAuthDataPostSave(t *testing.T) {
 	if authConfig.Password != "docker-pass" {
 	if authConfig.Password != "docker-pass" {
 		t.Fail()
 		t.Fail()
 	}
 	}
-	if authConfig.Email != "docker@docker.io" {
-		t.Fail()
-	}
 	if authConfig.Auth != "" {
 	if authConfig.Auth != "" {
 		t.Fail()
 		t.Fail()
 	}
 	}
@@ -62,17 +58,14 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
 	registryAuth := types.AuthConfig{
 	registryAuth := types.AuthConfig{
 		Username: "foo-user",
 		Username: "foo-user",
 		Password: "foo-pass",
 		Password: "foo-pass",
-		Email:    "foo@example.com",
 	}
 	}
 	localAuth := types.AuthConfig{
 	localAuth := types.AuthConfig{
 		Username: "bar-user",
 		Username: "bar-user",
 		Password: "bar-pass",
 		Password: "bar-pass",
-		Email:    "bar@example.com",
 	}
 	}
 	officialAuth := types.AuthConfig{
 	officialAuth := types.AuthConfig{
 		Username: "baz-user",
 		Username: "baz-user",
 		Password: "baz-pass",
 		Password: "baz-pass",
-		Email:    "baz@example.com",
 	}
 	}
 	authConfigs[IndexServer] = officialAuth
 	authConfigs[IndexServer] = officialAuth
 
 
@@ -105,7 +98,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
 
 
 	for configKey, registries := range validRegistries {
 	for configKey, registries := range validRegistries {
 		configured, ok := expectedAuths[configKey]
 		configured, ok := expectedAuths[configKey]
-		if !ok || configured.Email == "" {
+		if !ok {
 			t.Fail()
 			t.Fail()
 		}
 		}
 		index := &registrytypes.IndexInfo{
 		index := &registrytypes.IndexInfo{
@@ -114,13 +107,13 @@ func TestResolveAuthConfigFullURL(t *testing.T) {
 		for _, registry := range registries {
 		for _, registry := range registries {
 			authConfigs[registry] = configured
 			authConfigs[registry] = configured
 			resolved := ResolveAuthConfig(authConfigs, index)
 			resolved := ResolveAuthConfig(authConfigs, index)
-			if resolved.Email != configured.Email {
-				t.Errorf("%s -> %q != %q\n", registry, resolved.Email, configured.Email)
+			if resolved.Username != configured.Username || resolved.Password != configured.Password {
+				t.Errorf("%s -> %v != %v\n", registry, resolved, configured)
 			}
 			}
 			delete(authConfigs, registry)
 			delete(authConfigs, registry)
 			resolved = ResolveAuthConfig(authConfigs, index)
 			resolved = ResolveAuthConfig(authConfigs, index)
-			if resolved.Email == configured.Email {
-				t.Errorf("%s -> %q == %q\n", registry, resolved.Email, configured.Email)
+			if resolved.Username == configured.Username || resolved.Password == configured.Password {
+				t.Errorf("%s -> %v == %v\n", registry, resolved, configured)
 			}
 			}
 		}
 		}
 	}
 	}

+ 0 - 1
registry/session.go

@@ -752,7 +752,6 @@ func (r *Session) GetAuthConfig(withPasswd bool) *types.AuthConfig {
 	return &types.AuthConfig{
 	return &types.AuthConfig{
 		Username: r.authConfig.Username,
 		Username: r.authConfig.Username,
 		Password: password,
 		Password: password,
-		Email:    r.authConfig.Email,
 	}
 	}
 }
 }