Browse code

added login and push

Victor Vieux authored on 2013/05/06 20:34:31
Showing 5 changed files
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	_ "bytes"
5 5
 	"encoding/json"
6 6
 	"fmt"
7
+	"github.com/dotcloud/docker/auth"
7 8
 	"github.com/gorilla/mux"
8 9
 	"log"
9 10
 	"net"
... ...
@@ -42,6 +43,51 @@ func ListenAndServe(addr string, srv *Server) error {
42 42
 	r := mux.NewRouter()
43 43
 	log.Printf("Listening for HTTP on %s\n", addr)
44 44
 
45
+	r.Path("/auth").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46
+		log.Println(r.Method, r.RequestURI)
47
+		var out auth.AuthConfig
48
+		out.Username = srv.runtime.authConfig.Username
49
+		out.Email = srv.runtime.authConfig.Email
50
+		b, err := json.Marshal(out)
51
+		if err != nil {
52
+			http.Error(w, err.Error(), http.StatusInternalServerError)
53
+		} else {
54
+			w.Write(b)
55
+		}
56
+	})
57
+
58
+	r.Path("/auth").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
59
+		log.Println(r.Method, r.RequestURI)
60
+		var config auth.AuthConfig
61
+		if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
62
+			http.Error(w, err.Error(), http.StatusInternalServerError)
63
+			return
64
+		}
65
+
66
+		if config.Username == srv.runtime.authConfig.Username {
67
+			config.Password = srv.runtime.authConfig.Password
68
+		}
69
+
70
+		newAuthConfig := auth.NewAuthConfig(config.Username, config.Password, config.Email, srv.runtime.root)
71
+		status, err := auth.Login(newAuthConfig)
72
+		if err != nil {
73
+			http.Error(w, err.Error(), http.StatusInternalServerError)
74
+			return
75
+		} else {
76
+			srv.runtime.authConfig = newAuthConfig
77
+		}
78
+		if status != "" {
79
+			b, err := json.Marshal(ApiAuth{status})
80
+			if err != nil {
81
+				http.Error(w, err.Error(), http.StatusInternalServerError)
82
+			} else {
83
+				w.Write(b)
84
+			}
85
+		} else {
86
+			w.WriteHeader(http.StatusOK)
87
+		}
88
+	})
89
+
45 90
 	r.Path("/version").Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46 91
 		log.Println(r.Method, r.RequestURI)
47 92
 		m := srv.DockerVersion()
... ...
@@ -272,6 +318,28 @@ func ListenAndServe(addr string, srv *Server) error {
272 272
 		}
273 273
 	})
274 274
 
275
+	r.Path("/images/{name:*.}/push").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
276
+		log.Println(r.Method, r.RequestURI)
277
+		vars := mux.Vars(r)
278
+		name := vars["name"]
279
+
280
+		file, rwc, err := hijackServer(w)
281
+		if file != nil {
282
+			defer file.Close()
283
+		}
284
+		if rwc != nil {
285
+			defer rwc.Close()
286
+		}
287
+		if err != nil {
288
+			httpError(w, err)
289
+			return
290
+		}
291
+		fmt.Fprintf(file, "HTTP/1.1 200 OK\r\nContent-Type: raw-stream-hijack\r\n\r\n")
292
+		if err := srv.ImagePush(name, file); err != nil {
293
+			fmt.Fprintln(file, "Error: "+err.Error())
294
+		}
295
+	})
296
+
275 297
 	r.Path("/containers").Methods("POST").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
276 298
 		log.Println(r.Method, r.RequestURI)
277 299
 		var config Config
... ...
@@ -54,3 +54,7 @@ type ApiVersion struct {
54 54
 type ApiWait struct {
55 55
 	StatusCode int
56 56
 }
57
+
58
+type ApiAuth struct {
59
+	Status string
60
+}
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"encoding/json"
6 6
 	"flag"
7 7
 	"fmt"
8
+	"github.com/dotcloud/docker/auth"
8 9
 	"github.com/dotcloud/docker/term"
9 10
 	"io"
10 11
 	"io/ioutil"
... ...
@@ -15,8 +16,10 @@ import (
15 15
 	"os"
16 16
 	"path/filepath"
17 17
 	"strconv"
18
+	"strings"
18 19
 	"text/tabwriter"
19 20
 	"time"
21
+	"unicode"
20 22
 )
21 23
 
22 24
 const VERSION = "0.2.2"
... ...
@@ -59,10 +62,12 @@ func ParseCommands(args ...string) error {
59 59
 		"import":  CmdImport,
60 60
 		"history": CmdHistory,
61 61
 		"kill":    CmdKill,
62
+		"login":   CmdLogin,
62 63
 		"logs":    CmdLogs,
63 64
 		"port":    CmdPort,
64 65
 		"ps":      CmdPs,
65 66
 		"pull":    CmdPull,
67
+		"push":    CmdPush,
66 68
 		"restart": CmdRestart,
67 69
 		"rm":      CmdRm,
68 70
 		"rmi":     CmdRmi,
... ...
@@ -98,12 +103,12 @@ func cmdHelp(args ...string) error {
98 98
 		{"info", "Display system-wide information"},
99 99
 		{"inspect", "Return low-level information on a container/image"},
100 100
 		{"kill", "Kill a running container"},
101
-		//		{"login", "Register or Login to the docker registry server"},
101
+		{"login", "Register or Login to the docker registry server"},
102 102
 		{"logs", "Fetch the logs of a container"},
103 103
 		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
104 104
 		{"ps", "List containers"},
105 105
 		{"pull", "Pull an image or a repository from the docker registry server"},
106
-		//		{"push", "Push an image or a repository to the docker registry server"},
106
+		{"push", "Push an image or a repository to the docker registry server"},
107 107
 		{"restart", "Restart a running container"},
108 108
 		{"rm", "Remove a container"},
109 109
 		{"rmi", "Remove an image"},
... ...
@@ -120,17 +125,8 @@ func cmdHelp(args ...string) error {
120 120
 	return nil
121 121
 }
122 122
 
123
-/*
124 123
 // 'docker login': login / register a user to registry service.
125
-func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
126
-	// Read a line on raw terminal with support for simple backspace
127
-	// sequences and echo.
128
-	//
129
-	// This function is necessary because the login command must be done in a
130
-	// raw terminal for two reasons:
131
-	// - we have to read a password (without echoing it);
132
-	// - the rcli "protocol" only supports cannonical and raw modes and you
133
-	//   can't tune it once the command as been started.
124
+func CmdLogin(args ...string) error {
134 125
 	var readStringOnRawTerminal = func(stdin io.Reader, stdout io.Writer, echo bool) string {
135 126
 		char := make([]byte, 1)
136 127
 		buffer := make([]byte, 64)
... ...
@@ -173,52 +169,74 @@ func (srv *Server) CmdLogin(stdin io.ReadCloser, stdout rcli.DockerConn, args ..
173 173
 		return readStringOnRawTerminal(stdin, stdout, false)
174 174
 	}
175 175
 
176
-	stdout.SetOptionRawTerminal()
176
+	oldState, err := SetRawTerminal()
177
+	if err != nil {
178
+		return err
179
+	} else {
180
+		defer RestoreTerminal(oldState)
181
+	}
177 182
 
178
-	cmd := rcli.Subcmd(stdout, "login", "", "Register or Login to the docker registry server")
183
+	cmd := Subcmd("login", "", "Register or Login to the docker registry server")
179 184
 	if err := cmd.Parse(args); err != nil {
180 185
 		return nil
181 186
 	}
182 187
 
188
+	body, _, err := call("GET", "/auth", nil)
189
+	if err != nil {
190
+		return err
191
+	}
192
+
193
+	var out auth.AuthConfig
194
+	err = json.Unmarshal(body, &out)
195
+	if err != nil {
196
+		return err
197
+	}
198
+
183 199
 	var username string
184 200
 	var password string
185 201
 	var email string
186 202
 
187
-	fmt.Fprint(stdout, "Username (", srv.runtime.authConfig.Username, "): ")
188
-	username = readAndEchoString(stdin, stdout)
203
+	fmt.Print("Username (", out.Username, "): ")
204
+	username = readAndEchoString(os.Stdin, os.Stdout)
189 205
 	if username == "" {
190
-		username = srv.runtime.authConfig.Username
206
+		username = out.Username
191 207
 	}
192
-	if username != srv.runtime.authConfig.Username {
193
-		fmt.Fprint(stdout, "Password: ")
194
-		password = readString(stdin, stdout)
208
+	if username != out.Username {
209
+		fmt.Print("Password: ")
210
+		password = readString(os.Stdin, os.Stdout)
195 211
 
196 212
 		if password == "" {
197 213
 			return fmt.Errorf("Error : Password Required")
198 214
 		}
199 215
 
200
-		fmt.Fprint(stdout, "Email (", srv.runtime.authConfig.Email, "): ")
201
-		email = readAndEchoString(stdin, stdout)
216
+		fmt.Print("Email (", out.Email, "): ")
217
+		email = readAndEchoString(os.Stdin, os.Stdout)
202 218
 		if email == "" {
203
-			email = srv.runtime.authConfig.Email
219
+			email = out.Email
204 220
 		}
205 221
 	} else {
206
-		password = srv.runtime.authConfig.Password
207
-		email = srv.runtime.authConfig.Email
222
+		email = out.Email
208 223
 	}
209
-	newAuthConfig := auth.NewAuthConfig(username, password, email, srv.runtime.root)
210
-	status, err := auth.Login(newAuthConfig)
224
+
225
+	out.Username = username
226
+	out.Password = password
227
+	out.Email = email
228
+
229
+	body, _, err = call("POST", "/auth", out)
211 230
 	if err != nil {
212
-		fmt.Fprintf(stdout, "Error: %s\r\n", err)
213
-	} else {
214
-		srv.runtime.authConfig = newAuthConfig
231
+		return err
232
+	}
233
+
234
+	var out2 ApiAuth
235
+	err = json.Unmarshal(body, &out)
236
+	if err != nil {
237
+		return err
215 238
 	}
216
-	if status != "" {
217
-		fmt.Fprint(stdout, status)
239
+	if out2.Status != "" {
240
+		fmt.Print(out2.Status)
218 241
 	}
219 242
 	return nil
220 243
 }
221
-*/
222 244
 
223 245
 // 'docker wait': block until a container stops
224 246
 func CmdWait(args ...string) error {
... ...
@@ -547,66 +565,60 @@ func CmdImport(args ...string) error {
547 547
 	return nil
548 548
 }
549 549
 
550
-/*
551
-func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...string) error {
552
-	cmd := rcli.Subcmd(stdout, "push", "NAME", "Push an image or a repository to the registry")
550
+func CmdPush(args ...string) error {
551
+	cmd := Subcmd("push", "NAME", "Push an image or a repository to the registry")
553 552
 	if err := cmd.Parse(args); err != nil {
554 553
 		return nil
555 554
 	}
556
-	local := cmd.Arg(0)
555
+	name := cmd.Arg(0)
557 556
 
558
-	if local == "" {
557
+	if name == "" {
559 558
 		cmd.Usage()
560 559
 		return nil
561 560
 	}
562 561
 
562
+	body, _, err := call("GET", "/auth", nil)
563
+	if err != nil {
564
+		return err
565
+	}
566
+
567
+	var out auth.AuthConfig
568
+	err = json.Unmarshal(body, &out)
569
+	if err != nil {
570
+		return err
571
+	}
572
+
563 573
 	// If the login failed, abort
564
-	if srv.runtime.authConfig == nil || srv.runtime.authConfig.Username == "" {
565
-		if err := srv.CmdLogin(stdin, stdout, args...); err != nil {
574
+	if out.Username == "" {
575
+		if err := CmdLogin(args...); err != nil {
576
+			return err
577
+		}
578
+
579
+		body, _, err = call("GET", "/auth", nil)
580
+		if err != nil {
581
+			return err
582
+		}
583
+		err = json.Unmarshal(body, &out)
584
+		if err != nil {
566 585
 			return err
567 586
 		}
568
-		if srv.runtime.authConfig == nil || srv.runtime.authConfig.Username == "" {
587
+
588
+		if out.Username == "" {
569 589
 			return fmt.Errorf("Please login prior to push. ('docker login')")
570 590
 		}
571 591
 	}
572 592
 
573
-	var remote string
574
-
575
-	tmp := strings.SplitN(local, "/", 2)
576
-	if len(tmp) == 1 {
593
+	if len(strings.SplitN(name, "/", 2)) == 1 {
577 594
 		return fmt.Errorf(
578 595
 			"Impossible to push a \"root\" repository. Please rename your repository in <user>/<repo> (ex: %s/%s)",
579
-			srv.runtime.authConfig.Username, local)
580
-	} else {
581
-		remote = local
596
+			out.Username, name)
582 597
 	}
583 598
 
584
-	Debugf("Pushing [%s] to [%s]\n", local, remote)
585
-
586
-	// Try to get the image
587
-	// FIXME: Handle lookup
588
-	// FIXME: Also push the tags in case of ./docker push myrepo:mytag
589
-	//	img, err := srv.runtime.LookupImage(cmd.Arg(0))
590
-	img, err := srv.runtime.graph.Get(local)
591
-	if err != nil {
592
-		Debugf("The push refers to a repository [%s] (len: %d)\n", local, len(srv.runtime.repositories.Repositories[local]))
593
-		// If it fails, try to get the repository
594
-		if localRepo, exists := srv.runtime.repositories.Repositories[local]; exists {
595
-			if err := srv.runtime.graph.PushRepository(stdout, remote, localRepo, srv.runtime.authConfig); err != nil {
596
-				return err
597
-			}
598
-			return nil
599
-		}
600
-
601
-		return err
602
-	}
603
-	err = srv.runtime.graph.PushImage(stdout, img, srv.runtime.authConfig)
604
-	if err != nil {
599
+	if err := hijack("POST", "/images"+name+"/pull", false); err != nil {
605 600
 		return err
606 601
 	}
607 602
 	return nil
608 603
 }
609
-*/
610 604
 
611 605
 func CmdPull(args ...string) error {
612 606
 	cmd := Subcmd("pull", "NAME", "Pull an image or a repository from the registry")
... ...
@@ -214,6 +214,27 @@ func (srv *Server) ImagePull(name string, file *os.File) error {
214 214
 	return nil
215 215
 }
216 216
 
217
+func (srv *Server) ImagePush(name string, file *os.File) error {
218
+	img, err := srv.runtime.graph.Get(name)
219
+	if err != nil {
220
+		Debugf("The push refers to a repository [%s] (len: %d)\n", name, len(srv.runtime.repositories.Repositories[name]))
221
+		// If it fails, try to get the repository                                                                                                                      
222
+		if localRepo, exists := srv.runtime.repositories.Repositories[name]; exists {
223
+			if err := srv.runtime.graph.PushRepository(file, name, localRepo, srv.runtime.authConfig); err != nil {
224
+				return err
225
+			}
226
+			return nil
227
+		}
228
+
229
+		return err
230
+	}
231
+	err = srv.runtime.graph.PushImage(file, img, srv.runtime.authConfig)
232
+	if err != nil {
233
+		return err
234
+	}
235
+	return nil
236
+}
237
+
217 238
 func (srv *Server) ImageImport(src, repo, tag string, file *os.File) error {
218 239
 	var archive io.Reader
219 240
 	var resp *http.Response
... ...
@@ -477,3 +477,37 @@ func FindCgroupMountpoint(cgroupType string) (string, error) {
477 477
 
478 478
 	return "", fmt.Errorf("cgroup mountpoint not found for %s", cgroupType)
479 479
 }
480
+
481
+/*
482
+func ReadStringOnTerminal(prompt string) (string, error) {
483
+	fmt.Print(prompt)
484
+	in := bufio.NewReader(os.Stdin);
485
+	return in.ReadString('\n');
486
+}
487
+
488
+func ReadPasswdOnTerminal(prompt string) (string, error) {
489
+	fmt.Print(prompt);
490
+	const stty_arg0  = "/bin/stty";
491
+	stty_argv_e_off := []string{"stty","-echo"};
492
+	stty_argv_e_on  := []string{"stty","echo"};
493
+	const exec_cwdir = "";
494
+	fd := []*os.File{os.Stdin,os.Stdout,os.Stderr};
495
+	pid, err := os.StartProcess(stty_arg0,stty_argv_e_off,nil,exec_cwdir,fd);
496
+	if err != nil {
497
+		return "", err
498
+	}
499
+	rd := bufio.NewReader(os.Stdin);
500
+	os.Wait(pid,0);
501
+	line, err := rd.ReadString('\n');
502
+	if err != nil {
503
+		return "", err
504
+	}
505
+	passwd := strings.TrimSpace(line)
506
+	pid, e := os.StartProcess(stty_arg0,stty_argv_e_on,nil,exec_cwdir,fd);
507
+	if e =! nil {
508
+		return "", err
509
+	}
510
+	os.Wait(pid,0)
511
+	return passwd, err
512
+}
513
+*/