Browse code

Add links for container relationships and introspection

Michael Crosby authored on 2013/10/05 11:25:15
Showing 48 changed files
... ...
@@ -33,15 +33,13 @@ run	apt-get update
33 33
 run	apt-get install -y -q curl
34 34
 run	apt-get install -y -q git
35 35
 run	apt-get install -y -q mercurial
36
-run	apt-get install -y -q build-essential
36
+run apt-get install -y -q build-essential libsqlite3-dev
37 37
 
38
-# Install Go from source (for eventual cross-compiling)
39
-env	CGO_ENABLED 0
40
-run	curl -s https://go.googlecode.com/files/go1.1.2.src.tar.gz | tar -v -C / -xz && mv /go /goroot
41
-run	cd /goroot/src && ./make.bash
42
-env GOROOT	/goroot
43
-env	PATH	$PATH:/goroot/bin
38
+# Install Go
39
+run	curl -s https://go.googlecode.com/files/go1.2rc1.src.tar.gz | tar -v -C /usr/local -xz
40
+env	PATH	/usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
44 41
 env	GOPATH	/go:/go/src/github.com/dotcloud/docker/vendor
42
+run cd /usr/local/go/src && ./make.bash && go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std
45 43
 
46 44
 # Ubuntu stuff
47 45
 run	apt-get install -y -q ruby1.9.3 rubygems libffi-dev
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"encoding/json"
7 7
 	"fmt"
8 8
 	"github.com/dotcloud/docker/auth"
9
+	"github.com/dotcloud/docker/gograph"
9 10
 	"github.com/dotcloud/docker/utils"
10 11
 	"github.com/gorilla/mux"
11 12
 	"io"
... ...
@@ -14,6 +15,7 @@ import (
14 14
 	"mime"
15 15
 	"net"
16 16
 	"net/http"
17
+	"net/url"
17 18
 	"os"
18 19
 	"os/exec"
19 20
 	"regexp"
... ...
@@ -154,7 +156,7 @@ func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *
154 154
 			}
155 155
 		}
156 156
 	}
157
-
157
+	name = decodeName(name)
158 158
 	if err := srv.ContainerKill(name, signal); err != nil {
159 159
 		return err
160 160
 	}
... ...
@@ -167,6 +169,7 @@ func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r
167 167
 		return fmt.Errorf("Missing parameter")
168 168
 	}
169 169
 	name := vars["name"]
170
+	name = decodeName(name)
170 171
 
171 172
 	if err := srv.ContainerExport(name, w); err != nil {
172 173
 		utils.Errorf("%s", err)
... ...
@@ -534,16 +537,19 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
534 534
 		return err
535 535
 	}
536 536
 
537
-	if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
537
+	if !config.NetworkDisabled && len(config.Dns) == 0 && len(srv.runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
538 538
 		out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns))
539 539
 		config.Dns = defaultDns
540 540
 	}
541 541
 
542
-	id, err := srv.ContainerCreate(config)
542
+	id, warnings, err := srv.ContainerCreate(config)
543 543
 	if err != nil {
544 544
 		return err
545 545
 	}
546 546
 	out.ID = id
547
+	for _, warning := range warnings {
548
+		out.Warnings = append(out.Warnings, warning)
549
+	}
547 550
 
548 551
 	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
549 552
 		log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
... ...
@@ -574,6 +580,7 @@ func postContainersRestart(srv *Server, version float64, w http.ResponseWriter,
574 574
 		return fmt.Errorf("Missing parameter")
575 575
 	}
576 576
 	name := vars["name"]
577
+	name = decodeName(name)
577 578
 	if err := srv.ContainerRestart(name, t); err != nil {
578 579
 		return err
579 580
 	}
... ...
@@ -589,12 +596,18 @@ func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *ht
589 589
 		return fmt.Errorf("Missing parameter")
590 590
 	}
591 591
 	name := vars["name"]
592
+	name = decodeName(name)
593
+
592 594
 	removeVolume, err := getBoolParam(r.Form.Get("v"))
593 595
 	if err != nil {
594 596
 		return err
595 597
 	}
598
+	removeLink, err := getBoolParam(r.Form.Get("link"))
599
+	if err != nil {
600
+		return err
601
+	}
596 602
 
597
-	if err := srv.ContainerDestroy(name, removeVolume); err != nil {
603
+	if err := srv.ContainerDestroy(name, removeVolume, removeLink); err != nil {
598 604
 		return err
599 605
 	}
600 606
 	w.WriteHeader(http.StatusNoContent)
... ...
@@ -640,7 +653,12 @@ func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r
640 640
 	if vars == nil {
641 641
 		return fmt.Errorf("Missing parameter")
642 642
 	}
643
+	var err error
643 644
 	name := vars["name"]
645
+	name = decodeName(name)
646
+	if err != nil {
647
+		return err
648
+	}
644 649
 	if err := srv.ContainerStart(name, hostConfig); err != nil {
645 650
 		return err
646 651
 	}
... ...
@@ -661,6 +679,7 @@ func postContainersStop(srv *Server, version float64, w http.ResponseWriter, r *
661 661
 		return fmt.Errorf("Missing parameter")
662 662
 	}
663 663
 	name := vars["name"]
664
+	name = decodeName(name)
664 665
 
665 666
 	if err := srv.ContainerStop(name, t); err != nil {
666 667
 		return err
... ...
@@ -674,6 +693,8 @@ func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *
674 674
 		return fmt.Errorf("Missing parameter")
675 675
 	}
676 676
 	name := vars["name"]
677
+	name = decodeName(name)
678
+
677 679
 	status, err := srv.ContainerWait(name)
678 680
 	if err != nil {
679 681
 		return err
... ...
@@ -733,6 +754,7 @@ func postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r
733 733
 		return fmt.Errorf("Missing parameter")
734 734
 	}
735 735
 	name := vars["name"]
736
+	name = decodeName(name)
736 737
 
737 738
 	c, err := srv.ContainerInspect(name)
738 739
 	if err != nil {
... ...
@@ -805,6 +827,7 @@ func wsContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *
805 805
 		return fmt.Errorf("Missing parameter")
806 806
 	}
807 807
 	name := vars["name"]
808
+	name = decodeName(name)
808 809
 
809 810
 	if _, err := srv.ContainerInspect(name); err != nil {
810 811
 		return err
... ...
@@ -827,6 +850,7 @@ func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r
827 827
 		return fmt.Errorf("Missing parameter")
828 828
 	}
829 829
 	name := vars["name"]
830
+	name = decodeName(name)
830 831
 
831 832
 	container, err := srv.ContainerInspect(name)
832 833
 	if err != nil {
... ...
@@ -994,7 +1018,7 @@ func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute s
994 994
 		if err != nil {
995 995
 			version = APIVERSION
996 996
 		}
997
-		if srv.enableCors {
997
+		if srv.runtime.config.EnableCors {
998 998
 			writeCorsHeaders(w, r)
999 999
 		}
1000 1000
 
... ...
@@ -1010,6 +1034,75 @@ func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute s
1010 1010
 	}
1011 1011
 }
1012 1012
 
1013
+func getContainersLinks(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
1014
+	if err := parseForm(r); err != nil {
1015
+		return err
1016
+	}
1017
+
1018
+	runtime := srv.runtime
1019
+	all, err := getBoolParam(r.Form.Get("all"))
1020
+	if err != nil {
1021
+		return err
1022
+	}
1023
+
1024
+	out := []APILink{}
1025
+	err = runtime.containerGraph.Walk("/", func(p string, e *gograph.Entity) error {
1026
+		if container := runtime.Get(e.ID()); container != nil {
1027
+			if !all && strings.Contains(p, container.ID) {
1028
+				return nil
1029
+			}
1030
+			out = append(out, APILink{
1031
+				Path:        p,
1032
+				ContainerID: container.ID,
1033
+				Image:       runtime.repositories.ImageName(container.Image),
1034
+			})
1035
+		}
1036
+		return nil
1037
+	}, -1)
1038
+
1039
+	if err != nil {
1040
+		return err
1041
+	}
1042
+	return writeJSON(w, http.StatusOK, out)
1043
+}
1044
+
1045
+func postContainerLink(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
1046
+	if vars == nil {
1047
+		return fmt.Errorf("Missing parameter")
1048
+	}
1049
+	values := make(map[string]string)
1050
+	if matchesContentType(r.Header.Get("Content-Type"), "application/json") && r.Body != nil {
1051
+		defer r.Body.Close()
1052
+
1053
+		dec := json.NewDecoder(r.Body)
1054
+		if err := dec.Decode(&values); err != nil {
1055
+			return err
1056
+		}
1057
+	} else {
1058
+		return fmt.Errorf("Invalid json body")
1059
+	}
1060
+	currentName := values["currentName"]
1061
+	newName := values["newName"]
1062
+
1063
+	if currentName == "" {
1064
+		return fmt.Errorf("currentName cannot be empty")
1065
+	}
1066
+	if newName == "" {
1067
+		return fmt.Errorf("newName cannot be empty")
1068
+	}
1069
+
1070
+	if err := srv.runtime.RenameLink(currentName, newName); err != nil {
1071
+		return err
1072
+	}
1073
+
1074
+	return nil
1075
+}
1076
+
1077
+func decodeName(name string) string {
1078
+	s, _ := url.QueryUnescape(name)
1079
+	return s
1080
+}
1081
+
1013 1082
 func createRouter(srv *Server, logging bool) (*mux.Router, error) {
1014 1083
 	r := mux.NewRouter()
1015 1084
 
... ...
@@ -1030,6 +1123,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
1030 1030
 			"/containers/{name:.*}/json":      getContainersByName,
1031 1031
 			"/containers/{name:.*}/top":       getContainersTop,
1032 1032
 			"/containers/{name:.*}/attach/ws": wsContainersAttach,
1033
+			"/containers/links":               getContainersLinks,
1033 1034
 		},
1034 1035
 		"POST": {
1035 1036
 			"/auth":                         postAuth,
... ...
@@ -1048,6 +1142,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
1048 1048
 			"/containers/{name:.*}/resize":  postContainersResize,
1049 1049
 			"/containers/{name:.*}/attach":  postContainersAttach,
1050 1050
 			"/containers/{name:.*}/copy":    postContainersCopy,
1051
+			"/containers/link":              postContainerLink,
1051 1052
 		},
1052 1053
 		"DELETE": {
1053 1054
 			"/containers/{name:.*}": deleteContainers,
... ...
@@ -1,7 +1,5 @@
1 1
 package docker
2 2
 
3
-import "encoding/json"
4
-
5 3
 type APIHistory struct {
6 4
 	ID        string   `json:"Id"`
7 5
 	Tags      []string `json:",omitempty"`
... ...
@@ -52,6 +50,7 @@ type APIContainers struct {
52 52
 	Ports      []APIPort
53 53
 	SizeRw     int64
54 54
 	SizeRootFs int64
55
+	Names      []string
55 56
 }
56 57
 
57 58
 func (self *APIContainers) ToLegacy() APIContainersOld {
... ...
@@ -96,14 +95,7 @@ type APIPort struct {
96 96
 	PrivatePort int64
97 97
 	PublicPort  int64
98 98
 	Type        string
99
-}
100
-
101
-func (port *APIPort) MarshalJSON() ([]byte, error) {
102
-	return json.Marshal(map[string]interface{}{
103
-		"PrivatePort": port.PrivatePort,
104
-		"PublicPort":  port.PublicPort,
105
-		"Type":        port.Type,
106
-	})
99
+	IP          string
107 100
 }
108 101
 
109 102
 type APIVersion struct {
... ...
@@ -129,3 +121,9 @@ type APICopy struct {
129 129
 	Resource string
130 130
 	HostPath string
131 131
 }
132
+
133
+type APILink struct {
134
+	Path        string
135
+	ContainerID string
136
+	Image       string
137
+}
... ...
@@ -349,7 +349,7 @@ func TestGetContainersJSON(t *testing.T) {
349 349
 
350 350
 	beginLen := runtime.containers.Len()
351 351
 
352
-	container, err := runtime.Create(&Config{
352
+	container, _, err := runtime.Create(&Config{
353 353
 		Image: GetTestImage(runtime).ID,
354 354
 		Cmd:   []string{"echo", "test"},
355 355
 	})
... ...
@@ -386,7 +386,7 @@ func TestGetContainersExport(t *testing.T) {
386 386
 	srv := &Server{runtime: runtime}
387 387
 
388 388
 	// Create a container and remove a file
389
-	container, err := runtime.Create(
389
+	container, _, err := runtime.Create(
390 390
 		&Config{
391 391
 			Image: GetTestImage(runtime).ID,
392 392
 			Cmd:   []string{"touch", "/test"},
... ...
@@ -436,7 +436,7 @@ func TestGetContainersChanges(t *testing.T) {
436 436
 	srv := &Server{runtime: runtime}
437 437
 
438 438
 	// Create a container and remove a file
439
-	container, err := runtime.Create(
439
+	container, _, err := runtime.Create(
440 440
 		&Config{
441 441
 			Image: GetTestImage(runtime).ID,
442 442
 			Cmd:   []string{"/bin/rm", "/etc/passwd"},
... ...
@@ -479,7 +479,7 @@ func TestGetContainersTop(t *testing.T) {
479 479
 
480 480
 	srv := &Server{runtime: runtime}
481 481
 
482
-	container, err := runtime.Create(
482
+	container, _, err := runtime.Create(
483 483
 		&Config{
484 484
 			Image:     GetTestImage(runtime).ID,
485 485
 			Cmd:       []string{"/bin/sh", "-c", "cat"},
... ...
@@ -561,7 +561,7 @@ func TestGetContainersByName(t *testing.T) {
561 561
 	srv := &Server{runtime: runtime}
562 562
 
563 563
 	// Create a container and remove a file
564
-	container, err := runtime.Create(
564
+	container, _, err := runtime.Create(
565 565
 		&Config{
566 566
 			Image: GetTestImage(runtime).ID,
567 567
 			Cmd:   []string{"echo", "test"},
... ...
@@ -592,7 +592,7 @@ func TestPostCommit(t *testing.T) {
592 592
 	srv := &Server{runtime: runtime}
593 593
 
594 594
 	// Create a container and remove a file
595
-	container, err := runtime.Create(
595
+	container, _, err := runtime.Create(
596 596
 		&Config{
597 597
 			Image: GetTestImage(runtime).ID,
598 598
 			Cmd:   []string{"touch", "/test"},
... ...
@@ -686,7 +686,7 @@ func TestPostContainersKill(t *testing.T) {
686 686
 
687 687
 	srv := &Server{runtime: runtime}
688 688
 
689
-	container, err := runtime.Create(
689
+	container, _, err := runtime.Create(
690 690
 		&Config{
691 691
 			Image:     GetTestImage(runtime).ID,
692 692
 			Cmd:       []string{"/bin/cat"},
... ...
@@ -728,7 +728,7 @@ func TestPostContainersRestart(t *testing.T) {
728 728
 
729 729
 	srv := &Server{runtime: runtime}
730 730
 
731
-	container, err := runtime.Create(
731
+	container, _, err := runtime.Create(
732 732
 		&Config{
733 733
 			Image:     GetTestImage(runtime).ID,
734 734
 			Cmd:       []string{"/bin/top"},
... ...
@@ -782,7 +782,7 @@ func TestPostContainersStart(t *testing.T) {
782 782
 
783 783
 	srv := &Server{runtime: runtime}
784 784
 
785
-	container, err := runtime.Create(
785
+	container, _, err := runtime.Create(
786 786
 		&Config{
787 787
 			Image:     GetTestImage(runtime).ID,
788 788
 			Cmd:       []string{"/bin/cat"},
... ...
@@ -834,7 +834,7 @@ func TestPostContainersStop(t *testing.T) {
834 834
 
835 835
 	srv := &Server{runtime: runtime}
836 836
 
837
-	container, err := runtime.Create(
837
+	container, _, err := runtime.Create(
838 838
 		&Config{
839 839
 			Image:     GetTestImage(runtime).ID,
840 840
 			Cmd:       []string{"/bin/top"},
... ...
@@ -881,7 +881,7 @@ func TestPostContainersWait(t *testing.T) {
881 881
 
882 882
 	srv := &Server{runtime: runtime}
883 883
 
884
-	container, err := runtime.Create(
884
+	container, _, err := runtime.Create(
885 885
 		&Config{
886 886
 			Image:     GetTestImage(runtime).ID,
887 887
 			Cmd:       []string{"/bin/sleep", "1"},
... ...
@@ -923,7 +923,7 @@ func TestPostContainersAttach(t *testing.T) {
923 923
 
924 924
 	srv := &Server{runtime: runtime}
925 925
 
926
-	container, err := runtime.Create(
926
+	container, _, err := runtime.Create(
927 927
 		&Config{
928 928
 			Image:     GetTestImage(runtime).ID,
929 929
 			Cmd:       []string{"/bin/cat"},
... ...
@@ -1012,7 +1012,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
1012 1012
 
1013 1013
 	srv := &Server{runtime: runtime}
1014 1014
 
1015
-	container, err := runtime.Create(
1015
+	container, _, err := runtime.Create(
1016 1016
 		&Config{
1017 1017
 			Image:     GetTestImage(runtime).ID,
1018 1018
 			Cmd:       []string{"/bin/sh", "-c", "/bin/cat >&2"},
... ...
@@ -1104,7 +1104,7 @@ func TestDeleteContainers(t *testing.T) {
1104 1104
 
1105 1105
 	srv := &Server{runtime: runtime}
1106 1106
 
1107
-	container, err := runtime.Create(&Config{
1107
+	container, _, err := runtime.Create(&Config{
1108 1108
 		Image: GetTestImage(runtime).ID,
1109 1109
 		Cmd:   []string{"touch", "/test"},
1110 1110
 	})
... ...
@@ -1142,7 +1142,8 @@ func TestOptionsRoute(t *testing.T) {
1142 1142
 	runtime := mkRuntime(t)
1143 1143
 	defer nuke(runtime)
1144 1144
 
1145
-	srv := &Server{runtime: runtime, enableCors: true}
1145
+	runtime.config.EnableCors = true
1146
+	srv := &Server{runtime: runtime}
1146 1147
 
1147 1148
 	r := httptest.NewRecorder()
1148 1149
 	router, err := createRouter(srv, false)
... ...
@@ -1165,7 +1166,8 @@ func TestGetEnabledCors(t *testing.T) {
1165 1165
 	runtime := mkRuntime(t)
1166 1166
 	defer nuke(runtime)
1167 1167
 
1168
-	srv := &Server{runtime: runtime, enableCors: true}
1168
+	runtime.config.EnableCors = true
1169
+	srv := &Server{runtime: runtime}
1169 1170
 
1170 1171
 	r := httptest.NewRecorder()
1171 1172
 
... ...
@@ -1292,7 +1294,7 @@ func TestPostContainersCopy(t *testing.T) {
1292 1292
 	srv := &Server{runtime: runtime}
1293 1293
 
1294 1294
 	// Create a container and remove a file
1295
-	container, err := runtime.Create(
1295
+	container, _, err := runtime.Create(
1296 1296
 		&Config{
1297 1297
 			Image: GetTestImage(runtime).ID,
1298 1298
 			Cmd:   []string{"touch", "/test.txt"},
... ...
@@ -187,6 +187,9 @@ func (b *buildFile) CmdCmd(args string) error {
187 187
 }
188 188
 
189 189
 func (b *buildFile) CmdExpose(args string) error {
190
+	if strings.Contains(args, ":") {
191
+		return fmt.Errorf("EXPOSE cannot be used to bind to a host ip or port")
192
+	}
190 193
 	ports := strings.Split(args, " ")
191 194
 	b.config.PortSpecs = append(ports, b.config.PortSpecs...)
192 195
 	return b.commit("", b.config.Cmd, fmt.Sprintf("EXPOSE %v", ports))
... ...
@@ -332,7 +335,7 @@ func (b *buildFile) CmdAdd(args string) error {
332 332
 
333 333
 	b.config.Image = b.image
334 334
 	// Create the container and start it
335
-	container, err := b.runtime.Create(b.config)
335
+	container, _, err := b.runtime.Create(b.config)
336 336
 	if err != nil {
337 337
 		return err
338 338
 	}
... ...
@@ -367,7 +370,7 @@ func (b *buildFile) run() (string, error) {
367 367
 	b.config.Image = b.image
368 368
 
369 369
 	// Create the container and start it
370
-	c, err := b.runtime.Create(b.config)
370
+	c, _, err := b.runtime.Create(b.config)
371 371
 	if err != nil {
372 372
 		return "", err
373 373
 	}
... ...
@@ -430,7 +433,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
430 430
 			}
431 431
 		}
432 432
 
433
-		container, err := b.runtime.Create(b.config)
433
+		container, _, err := b.runtime.Create(b.config)
434 434
 		if err != nil {
435 435
 			return err
436 436
 		}
... ...
@@ -92,6 +92,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
92 92
 		{"kill", "Kill a running container"},
93 93
 		{"login", "Register or Login to the docker registry server"},
94 94
 		{"logs", "Fetch the logs of a container"},
95
+		{"ls", "List links for containers"},
95 96
 		{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
96 97
 		{"ps", "List containers"},
97 98
 		{"pull", "Pull an image or a repository from the docker registry server"},
... ...
@@ -504,7 +505,8 @@ func (cli *DockerCli) CmdStop(args ...string) error {
504 504
 	v.Set("t", strconv.Itoa(*nSeconds))
505 505
 
506 506
 	for _, name := range cmd.Args() {
507
-		_, _, err := cli.call("POST", "/containers/"+name+"/stop?"+v.Encode(), nil)
507
+		encName := cleanName(name)
508
+		_, _, err := cli.call("POST", "/containers/"+encName+"/stop?"+v.Encode(), nil)
508 509
 		if err != nil {
509 510
 			fmt.Fprintf(cli.err, "%s\n", err)
510 511
 		} else {
... ...
@@ -529,7 +531,8 @@ func (cli *DockerCli) CmdRestart(args ...string) error {
529 529
 	v.Set("t", strconv.Itoa(*nSeconds))
530 530
 
531 531
 	for _, name := range cmd.Args() {
532
-		_, _, err := cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil)
532
+		encName := cleanName(name)
533
+		_, _, err := cli.call("POST", "/containers/"+encName+"/restart?"+v.Encode(), nil)
533 534
 		if err != nil {
534 535
 			fmt.Fprintf(cli.err, "%s\n", err)
535 536
 		} else {
... ...
@@ -605,7 +608,8 @@ func (cli *DockerCli) CmdStart(args ...string) error {
605 605
 
606 606
 	var encounteredError error
607 607
 	for _, name := range cmd.Args() {
608
-		_, _, err := cli.call("POST", "/containers/"+name+"/start", nil)
608
+		encName := cleanName(name)
609
+		_, _, err := cli.call("POST", "/containers/"+encName+"/start", nil)
609 610
 		if err != nil {
610 611
 			if !*attach || !*openStdin {
611 612
 				fmt.Fprintf(cli.err, "%s\n", err)
... ...
@@ -811,6 +815,8 @@ func (cli *DockerCli) CmdHistory(args ...string) error {
811 811
 func (cli *DockerCli) CmdRm(args ...string) error {
812 812
 	cmd := Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers")
813 813
 	v := cmd.Bool("v", false, "Remove the volumes associated to the container")
814
+	link := cmd.Bool("link", false, "Remove the specified link and not the underlying container")
815
+
814 816
 	if err := cmd.Parse(args); err != nil {
815 817
 		return nil
816 818
 	}
... ...
@@ -822,8 +828,12 @@ func (cli *DockerCli) CmdRm(args ...string) error {
822 822
 	if *v {
823 823
 		val.Set("v", "1")
824 824
 	}
825
+	if *link {
826
+		val.Set("link", "1")
827
+	}
825 828
 	for _, name := range cmd.Args() {
826
-		_, _, err := cli.call("DELETE", "/containers/"+name+"?"+val.Encode(), nil)
829
+		encName := cleanName(name)
830
+		_, _, err := cli.call("DELETE", "/containers/"+encName+"?"+val.Encode(), nil)
827 831
 		if err != nil {
828 832
 			fmt.Fprintf(cli.err, "%s\n", err)
829 833
 		} else {
... ...
@@ -845,7 +855,8 @@ func (cli *DockerCli) CmdKill(args ...string) error {
845 845
 	}
846 846
 
847 847
 	for _, name := range args {
848
-		_, _, err := cli.call("POST", "/containers/"+name+"/kill", nil)
848
+		encName := cleanName(name)
849
+		_, _, err := cli.call("POST", "/containers/"+encName+"/kill", nil)
849 850
 		if err != nil {
850 851
 			fmt.Fprintf(cli.err, "%s\n", err)
851 852
 		} else {
... ...
@@ -1088,10 +1099,10 @@ func (cli *DockerCli) CmdImages(args ...string) error {
1088 1088
 func displayablePorts(ports []APIPort) string {
1089 1089
 	result := []string{}
1090 1090
 	for _, port := range ports {
1091
-		if port.Type == "tcp" {
1092
-			result = append(result, fmt.Sprintf("%d->%d", port.PublicPort, port.PrivatePort))
1091
+		if port.IP == "" {
1092
+			result = append(result, fmt.Sprintf("%d/%s", port.PublicPort, port.Type))
1093 1093
 		} else {
1094
-			result = append(result, fmt.Sprintf("%d->%d/%s", port.PublicPort, port.PrivatePort, port.Type))
1094
+			result = append(result, fmt.Sprintf("%s:%d->%d/%s", port.IP, port.PublicPort, port.PrivatePort, port.Type))
1095 1095
 		}
1096 1096
 	}
1097 1097
 	sort.Strings(result)
... ...
@@ -1144,7 +1155,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1144 1144
 	}
1145 1145
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1146 1146
 	if !*quiet {
1147
-		fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS")
1147
+		fmt.Fprint(w, "ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
1148 1148
 		if *size {
1149 1149
 			fmt.Fprintln(w, "\tSIZE")
1150 1150
 		} else {
... ...
@@ -1153,11 +1164,16 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1153 1153
 	}
1154 1154
 
1155 1155
 	for _, out := range outs {
1156
+		for i := 0; i < len(out.Names); i++ {
1157
+			out.Names[i] = utils.Trunc(out.Names[i], 10)
1158
+		}
1159
+
1160
+		names := strings.Join(out.Names, ",")
1156 1161
 		if !*quiet {
1157 1162
 			if *noTrunc {
1158
-				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports))
1163
+				fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports), names)
1159 1164
 			} else {
1160
-				fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports))
1165
+				fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\t%s\t", utils.TruncateID(out.ID), out.Image, utils.Trunc(out.Command, 20), utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports), names)
1161 1166
 			}
1162 1167
 			if *size {
1163 1168
 				if out.SizeRootFs > 0 {
... ...
@@ -1183,6 +1199,64 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1183 1183
 	return nil
1184 1184
 }
1185 1185
 
1186
+func (cli *DockerCli) CmdLs(args ...string) error {
1187
+	cmd := Subcmd("ls", "", "List links for containers")
1188
+	flAll := cmd.Bool("a", false, "Show all links")
1189
+
1190
+	if err := cmd.Parse(args); err != nil {
1191
+		return nil
1192
+	}
1193
+	v := url.Values{}
1194
+	if *flAll {
1195
+		v.Set("all", "1")
1196
+	}
1197
+
1198
+	body, _, err := cli.call("GET", "/containers/links?"+v.Encode(), nil)
1199
+	if err != nil {
1200
+		return err
1201
+	}
1202
+	var links []APILink
1203
+	if err := json.Unmarshal(body, &links); err != nil {
1204
+		return err
1205
+	}
1206
+
1207
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1208
+	fmt.Fprintf(w, "NAME\tID\tIMAGE")
1209
+	fmt.Fprintf(w, "\n")
1210
+
1211
+	sortLinks(links, func(i, j APILink) bool {
1212
+		return len(i.Path) < len(j.Path)
1213
+	})
1214
+	for _, link := range links {
1215
+		fmt.Fprintf(w, "%s\t%s\t%s", link.Path, utils.TruncateID(link.ContainerID), link.Image)
1216
+		fmt.Fprintf(w, "\n")
1217
+	}
1218
+	w.Flush()
1219
+
1220
+	return nil
1221
+}
1222
+
1223
+func (cli *DockerCli) CmdLink(args ...string) error {
1224
+	cmd := Subcmd("link", "CURRENT_NAME NEW_NAME", "Link the container with a new name")
1225
+	if err := cmd.Parse(args); err != nil {
1226
+		return nil
1227
+	}
1228
+	if cmd.NArg() != 2 {
1229
+		cmd.Usage()
1230
+		return nil
1231
+	}
1232
+	body := map[string]string{
1233
+		"currentName": cmd.Arg(0),
1234
+		"newName":     cmd.Arg(1),
1235
+	}
1236
+
1237
+	_, _, err := cli.call("POST", "/containers/link", body)
1238
+	if err != nil {
1239
+		return err
1240
+	}
1241
+	return nil
1242
+}
1243
+
1186 1244
 func (cli *DockerCli) CmdCommit(args ...string) error {
1187 1245
 	cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes")
1188 1246
 	flComment := cmd.String("m", "", "Commit message")
... ...
@@ -1300,8 +1374,9 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
1300 1300
 		cmd.Usage()
1301 1301
 		return nil
1302 1302
 	}
1303
+	name := cleanName(cmd.Arg(0))
1303 1304
 
1304
-	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err, nil); err != nil {
1305
+	if err := cli.hijack("POST", "/containers/"+name+"/attach?logs=1&stdout=1&stderr=1", false, nil, cli.out, cli.err, nil); err != nil {
1305 1306
 		return err
1306 1307
 	}
1307 1308
 	return nil
... ...
@@ -1318,8 +1393,9 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
1318 1318
 		cmd.Usage()
1319 1319
 		return nil
1320 1320
 	}
1321
-
1322
-	body, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil)
1321
+	name := cmd.Arg(0)
1322
+	name = cleanName(name)
1323
+	body, _, err := cli.call("GET", "/containers/"+name+"/json", nil)
1323 1324
 	if err != nil {
1324 1325
 		return err
1325 1326
 	}
... ...
@@ -2028,6 +2104,10 @@ func getExitCode(cli *DockerCli, containerId string) (bool, int, error) {
2028 2028
 	return c.State.Running, c.State.ExitCode, nil
2029 2029
 }
2030 2030
 
2031
+func cleanName(name string) string {
2032
+	return strings.Replace(name, "/", "%252F", -1)
2033
+}
2034
+
2031 2035
 func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
2032 2036
 	var (
2033 2037
 		isTerminal = false
2034 2038
new file mode 100644
... ...
@@ -0,0 +1,17 @@
0
+package docker
1
+
2
+import (
3
+	"net"
4
+)
5
+
6
+type DaemonConfig struct {
7
+	Pidfile        string
8
+	GraphPath      string
9
+	ProtoAddresses []string
10
+	AutoRestart    bool
11
+	EnableCors     bool
12
+	Dns            []string
13
+	EnableIptables bool
14
+	BridgeIface    string
15
+	DefaultIp      net.IP
16
+}
... ...
@@ -59,6 +59,8 @@ type Container struct {
59 59
 	// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
60 60
 	// Easier than migrating older container configs :)
61 61
 	VolumesRW map[string]bool
62
+
63
+	activeLinks map[string]*Link
62 64
 }
63 65
 
64 66
 type Config struct {
... ...
@@ -71,7 +73,8 @@ type Config struct {
71 71
 	AttachStdin     bool
72 72
 	AttachStdout    bool
73 73
 	AttachStderr    bool
74
-	PortSpecs       []string
74
+	PortSpecs       []string // Deprecated - Can be in the format of 8080/tcp
75
+	ExposedPorts    map[Port]struct{}
75 76
 	Tty             bool // Attach standard streams to a tty, including stdin if it is not closed.
76 77
 	OpenStdin       bool // Open stdin
77 78
 	StdinOnce       bool // If true, close stdin after the 1 attached client disconnects.
... ...
@@ -91,6 +94,8 @@ type HostConfig struct {
91 91
 	Binds           []string
92 92
 	ContainerIDFile string
93 93
 	LxcConf         []KeyValuePair
94
+	PortBindings    map[Port][]PortBinding
95
+	Links           []string
94 96
 }
95 97
 
96 98
 type BindMap struct {
... ...
@@ -113,6 +118,34 @@ type KeyValuePair struct {
113 113
 	Value string
114 114
 }
115 115
 
116
+type PortBinding struct {
117
+	HostIp   string
118
+	HostPort string
119
+}
120
+
121
+// 80/tcp
122
+type Port string
123
+
124
+func (p Port) Proto() string {
125
+	return strings.Split(string(p), "/")[1]
126
+}
127
+
128
+func (p Port) Port() string {
129
+	return strings.Split(string(p), "/")[0]
130
+}
131
+
132
+func (p Port) Int() int {
133
+	i, err := parsePort(p.Port())
134
+	if err != nil {
135
+		panic(err)
136
+	}
137
+	return i
138
+}
139
+
140
+func NewPort(proto, port string) Port {
141
+	return Port(fmt.Sprintf("%s/%s", port, proto))
142
+}
143
+
116 144
 func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, *flag.FlagSet, error) {
117 145
 	cmd := Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
118 146
 	if os.Getenv("TEST") != "" {
... ...
@@ -142,8 +175,11 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
142 142
 
143 143
 	flCpuShares := cmd.Int64("c", 0, "CPU shares (relative weight)")
144 144
 
145
-	var flPorts ListOpts
146
-	cmd.Var(&flPorts, "p", "Expose a container's port to the host (use 'docker port' to see the actual mapping)")
145
+	var flPublish ListOpts
146
+	cmd.Var(&flPublish, "p", "Publish a container's port to the host (use 'docker port' to see the actual mapping)")
147
+
148
+	var flExpose ListOpts
149
+	cmd.Var(&flExpose, "expose", "Expose a port from the container without publishing it to your host")
147 150
 
148 151
 	var flEnv ListOpts
149 152
 	cmd.Var(&flEnv, "e", "Set environment variables")
... ...
@@ -162,6 +198,9 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
162 162
 	var flLxcOpts ListOpts
163 163
 	cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
164 164
 
165
+	var flLinks ListOpts
166
+	cmd.Var(&flLinks, "link", "Add link to another container (containerid:alias)")
167
+
165 168
 	if err := cmd.Parse(args); err != nil {
166 169
 		return nil, nil, cmd, err
167 170
 	}
... ...
@@ -230,10 +269,28 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
230 230
 		hostname = parts[0]
231 231
 		domainname = parts[1]
232 232
 	}
233
+
234
+	ports, portBindings, err := parsePortSpecs(flPublish)
235
+	if err != nil {
236
+		return nil, nil, cmd, err
237
+	}
238
+
239
+	// Merge in exposed ports to the map of published ports
240
+	for _, e := range flExpose {
241
+		if strings.Contains(e, ":") {
242
+			return nil, nil, cmd, fmt.Errorf("Invalid port format for -expose: %s", e)
243
+		}
244
+		p := NewPort(splitProtoPort(e))
245
+		if _, exists := ports[p]; !exists {
246
+			ports[p] = struct{}{}
247
+		}
248
+	}
249
+
233 250
 	config := &Config{
234
-		Hostname:        hostname,
251
+		Hostname:        *flHostname,
235 252
 		Domainname:      domainname,
236
-		PortSpecs:       flPorts,
253
+		PortSpecs:       nil, // Deprecated
254
+		ExposedPorts:    ports,
237 255
 		User:            *flUser,
238 256
 		Tty:             *flTty,
239 257
 		NetworkDisabled: !*flNetwork,
... ...
@@ -253,10 +310,13 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
253 253
 		Privileged:      *flPrivileged,
254 254
 		WorkingDir:      *flWorkingDir,
255 255
 	}
256
+
256 257
 	hostConfig := &HostConfig{
257 258
 		Binds:           binds,
258 259
 		ContainerIDFile: *flContainerIDFile,
259 260
 		LxcConf:         lxcConf,
261
+		PortBindings:    portBindings,
262
+		Links:           flLinks,
260 263
 	}
261 264
 
262 265
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
... ...
@@ -271,36 +331,38 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
271 271
 	return config, hostConfig, cmd, nil
272 272
 }
273 273
 
274
-type PortMapping map[string]string
274
+type PortMapping map[string]string // Deprecated
275 275
 
276 276
 type NetworkSettings struct {
277 277
 	IPAddress   string
278 278
 	IPPrefixLen int
279 279
 	Gateway     string
280 280
 	Bridge      string
281
-	PortMapping map[string]PortMapping
281
+	PortMapping map[string]PortMapping // Deprecated
282
+	Ports       map[Port][]PortBinding
282 283
 }
283 284
 
284
-// returns a more easy to process description of the port mapping defined in the settings
285 285
 func (settings *NetworkSettings) PortMappingAPI() []APIPort {
286 286
 	var mapping []APIPort
287
-	for private, public := range settings.PortMapping["Tcp"] {
288
-		pubint, _ := strconv.ParseInt(public, 0, 0)
289
-		privint, _ := strconv.ParseInt(private, 0, 0)
290
-		mapping = append(mapping, APIPort{
291
-			PrivatePort: privint,
292
-			PublicPort:  pubint,
293
-			Type:        "tcp",
294
-		})
295
-	}
296
-	for private, public := range settings.PortMapping["Udp"] {
297
-		pubint, _ := strconv.ParseInt(public, 0, 0)
298
-		privint, _ := strconv.ParseInt(private, 0, 0)
299
-		mapping = append(mapping, APIPort{
300
-			PrivatePort: privint,
301
-			PublicPort:  pubint,
302
-			Type:        "udp",
303
-		})
287
+	for port, bindings := range settings.Ports {
288
+		p, _ := parsePort(port.Port())
289
+		if len(bindings) == 0 {
290
+			mapping = append(mapping, APIPort{
291
+				PublicPort: int64(p),
292
+				Type:       port.Proto(),
293
+			})
294
+			continue
295
+		}
296
+		for _, binding := range bindings {
297
+			p, _ := parsePort(port.Port())
298
+			h, _ := parsePort(binding.HostPort)
299
+			mapping = append(mapping, APIPort{
300
+				PrivatePort: int64(p),
301
+				PublicPort:  int64(h),
302
+				Type:        port.Proto(),
303
+				IP:          binding.HostIp,
304
+			})
305
+		}
304 306
 	}
305 307
 	return mapping
306 308
 }
... ...
@@ -602,7 +664,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
602 602
 	if container.runtime.networkManager.disabled {
603 603
 		container.Config.NetworkDisabled = true
604 604
 	} else {
605
-		if err := container.allocateNetwork(); err != nil {
605
+		if err := container.allocateNetwork(hostConfig); err != nil {
606 606
 			return err
607 607
 		}
608 608
 	}
... ...
@@ -792,6 +854,46 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
792 792
 		"-e", "container=lxc",
793 793
 		"-e", "HOSTNAME="+container.Config.Hostname,
794 794
 	)
795
+
796
+	// Init any links between the parent and children
797
+	runtime := container.runtime
798
+
799
+	children, err := runtime.Children(fmt.Sprintf("/%s", container.ID))
800
+	if err != nil {
801
+		return err
802
+	}
803
+
804
+	if len(children) > 0 {
805
+		container.activeLinks = make(map[string]*Link, len(children))
806
+
807
+		// If we encounter an error make sure that we rollback any network
808
+		// config and ip table changes
809
+		rollback := func() {
810
+			for _, link := range container.activeLinks {
811
+				link.Disable()
812
+			}
813
+			container.activeLinks = nil
814
+		}
815
+
816
+		for p, child := range children {
817
+			link, err := NewLink(container, child, p, runtime.networkManager.bridgeIface)
818
+			if err != nil {
819
+				rollback()
820
+				return err
821
+			}
822
+
823
+			container.activeLinks[link.Alias()] = link
824
+			if err := link.Enable(); err != nil {
825
+				rollback()
826
+				return err
827
+			}
828
+
829
+			for _, envVar := range link.ToEnv() {
830
+				params = append(params, "-e", envVar)
831
+			}
832
+		}
833
+	}
834
+
795 835
 	if container.Config.WorkingDir != "" {
796 836
 		workingDir := path.Clean(container.Config.WorkingDir)
797 837
 		utils.Debugf("[working dir] working dir is %s", workingDir)
... ...
@@ -925,7 +1027,7 @@ func (container *Container) StderrPipe() (io.ReadCloser, error) {
925 925
 	return utils.NewBufReader(reader), nil
926 926
 }
927 927
 
928
-func (container *Container) allocateNetwork() error {
928
+func (container *Container) allocateNetwork(hostConfig *HostConfig) error {
929 929
 	if container.Config.NetworkDisabled {
930 930
 		return nil
931 931
 	}
... ...
@@ -952,36 +1054,59 @@ func (container *Container) allocateNetwork() error {
952 952
 		}
953 953
 	}
954 954
 
955
-	var portSpecs []string
955
+	if container.Config.PortSpecs != nil {
956
+		utils.Debugf("Migrating port mappings for container: %s", strings.Join(container.Config.PortSpecs, ", "))
957
+		if err := migratePortMappings(container.Config); err != nil {
958
+			return err
959
+		}
960
+		container.Config.PortSpecs = nil
961
+	}
962
+
963
+	portSpecs := make(map[Port]struct{})
964
+	bindings := make(map[Port][]PortBinding)
965
+
956 966
 	if !container.State.Ghost {
957
-		portSpecs = container.Config.PortSpecs
958
-	} else {
959
-		for backend, frontend := range container.NetworkSettings.PortMapping["Tcp"] {
960
-			portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/tcp", frontend, backend))
967
+		if container.Config.ExposedPorts != nil {
968
+			portSpecs = container.Config.ExposedPorts
961 969
 		}
962
-		for backend, frontend := range container.NetworkSettings.PortMapping["Udp"] {
963
-			portSpecs = append(portSpecs, fmt.Sprintf("%s:%s/udp", frontend, backend))
970
+		if hostConfig.PortBindings != nil {
971
+			bindings = hostConfig.PortBindings
972
+		}
973
+	} else {
974
+		if container.NetworkSettings.Ports != nil {
975
+			for port, binding := range container.NetworkSettings.Ports {
976
+				portSpecs[port] = struct{}{}
977
+				bindings[port] = binding
978
+			}
964 979
 		}
965 980
 	}
966 981
 
967
-	container.NetworkSettings.PortMapping = make(map[string]PortMapping)
968
-	container.NetworkSettings.PortMapping["Tcp"] = make(PortMapping)
969
-	container.NetworkSettings.PortMapping["Udp"] = make(PortMapping)
970
-	for _, spec := range portSpecs {
971
-		nat, err := iface.AllocatePort(spec)
972
-		if err != nil {
973
-			iface.Release()
974
-			return err
982
+	container.NetworkSettings.PortMapping = nil
983
+
984
+	for port := range portSpecs {
985
+		binding := bindings[port]
986
+		for i := 0; i < len(binding); i++ {
987
+			b := binding[i]
988
+			nat, err := iface.AllocatePort(port, b)
989
+			if err != nil {
990
+				iface.Release()
991
+				return err
992
+			}
993
+			utils.Debugf("Allocate port: %s:%s->%s", nat.Binding.HostIp, port, nat.Binding.HostPort)
994
+			binding[i] = nat.Binding
975 995
 		}
976
-		proto := strings.Title(nat.Proto)
977
-		backend, frontend := strconv.Itoa(nat.Backend), strconv.Itoa(nat.Frontend)
978
-		container.NetworkSettings.PortMapping[proto][backend] = frontend
996
+		bindings[port] = binding
979 997
 	}
998
+	container.SaveHostConfig(hostConfig)
999
+
1000
+	container.NetworkSettings.Ports = bindings
980 1001
 	container.network = iface
1002
+
981 1003
 	container.NetworkSettings.Bridge = container.runtime.networkManager.bridgeIface
982 1004
 	container.NetworkSettings.IPAddress = iface.IPNet.IP.String()
983 1005
 	container.NetworkSettings.IPPrefixLen, _ = iface.IPNet.Mask.Size()
984 1006
 	container.NetworkSettings.Gateway = iface.Gateway.String()
1007
+
985 1008
 	return nil
986 1009
 }
987 1010
 
... ...
@@ -1064,6 +1189,14 @@ func (container *Container) monitor(hostConfig *HostConfig) {
1064 1064
 
1065 1065
 func (container *Container) cleanup() {
1066 1066
 	container.releaseNetwork()
1067
+
1068
+	// Disable all active links
1069
+	if container.activeLinks != nil {
1070
+		for _, link := range container.activeLinks {
1071
+			link.Disable()
1072
+		}
1073
+	}
1074
+
1067 1075
 	if container.Config.OpenStdin {
1068 1076
 		if err := container.stdin.Close(); err != nil {
1069 1077
 			utils.Errorf("%s: Error close stdin: %s", container.ID, err)
... ...
@@ -1345,3 +1478,9 @@ func (container *Container) Copy(resource string) (Archive, error) {
1345 1345
 	}
1346 1346
 	return TarFilter(basePath, Uncompressed, filter)
1347 1347
 }
1348
+
1349
+// Returns true if the container exposes a certain port
1350
+func (container *Container) Exposes(p Port) bool {
1351
+	_, exists := container.Config.ExposedPorts[p]
1352
+	return exists
1353
+}
... ...
@@ -18,7 +18,7 @@ import (
18 18
 func TestIDFormat(t *testing.T) {
19 19
 	runtime := mkRuntime(t)
20 20
 	defer nuke(runtime)
21
-	container1, err := runtime.Create(
21
+	container1, _, err := runtime.Create(
22 22
 		&Config{
23 23
 			Image: GetTestImage(runtime).ID,
24 24
 			Cmd:   []string{"/bin/sh", "-c", "echo hello world"},
... ...
@@ -388,7 +388,7 @@ func TestRun(t *testing.T) {
388 388
 func TestOutput(t *testing.T) {
389 389
 	runtime := mkRuntime(t)
390 390
 	defer nuke(runtime)
391
-	container, err := runtime.Create(
391
+	container, _, err := runtime.Create(
392 392
 		&Config{
393 393
 			Image: GetTestImage(runtime).ID,
394 394
 			Cmd:   []string{"echo", "-n", "foobar"},
... ...
@@ -411,7 +411,7 @@ func TestKillDifferentUser(t *testing.T) {
411 411
 	runtime := mkRuntime(t)
412 412
 	defer nuke(runtime)
413 413
 
414
-	container, err := runtime.Create(&Config{
414
+	container, _, err := runtime.Create(&Config{
415 415
 		Image:     GetTestImage(runtime).ID,
416 416
 		Cmd:       []string{"cat"},
417 417
 		OpenStdin: true,
... ...
@@ -471,7 +471,7 @@ func TestCreateVolume(t *testing.T) {
471 471
 	if err != nil {
472 472
 		t.Fatal(err)
473 473
 	}
474
-	c, err := runtime.Create(config)
474
+	c, _, err := runtime.Create(config)
475 475
 	if err != nil {
476 476
 		t.Fatal(err)
477 477
 	}
... ...
@@ -486,7 +486,7 @@ func TestCreateVolume(t *testing.T) {
486 486
 func TestKill(t *testing.T) {
487 487
 	runtime := mkRuntime(t)
488 488
 	defer nuke(runtime)
489
-	container, err := runtime.Create(&Config{
489
+	container, _, err := runtime.Create(&Config{
490 490
 		Image: GetTestImage(runtime).ID,
491 491
 		Cmd:   []string{"sleep", "2"},
492 492
 	},
... ...
@@ -530,7 +530,7 @@ func TestExitCode(t *testing.T) {
530 530
 	runtime := mkRuntime(t)
531 531
 	defer nuke(runtime)
532 532
 
533
-	trueContainer, err := runtime.Create(&Config{
533
+	trueContainer, _, err := runtime.Create(&Config{
534 534
 		Image: GetTestImage(runtime).ID,
535 535
 		Cmd:   []string{"/bin/true", ""},
536 536
 	})
... ...
@@ -545,7 +545,7 @@ func TestExitCode(t *testing.T) {
545 545
 		t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode)
546 546
 	}
547 547
 
548
-	falseContainer, err := runtime.Create(&Config{
548
+	falseContainer, _, err := runtime.Create(&Config{
549 549
 		Image: GetTestImage(runtime).ID,
550 550
 		Cmd:   []string{"/bin/false", ""},
551 551
 	})
... ...
@@ -564,7 +564,7 @@ func TestExitCode(t *testing.T) {
564 564
 func TestRestart(t *testing.T) {
565 565
 	runtime := mkRuntime(t)
566 566
 	defer nuke(runtime)
567
-	container, err := runtime.Create(&Config{
567
+	container, _, err := runtime.Create(&Config{
568 568
 		Image: GetTestImage(runtime).ID,
569 569
 		Cmd:   []string{"echo", "-n", "foobar"},
570 570
 	},
... ...
@@ -594,7 +594,7 @@ func TestRestart(t *testing.T) {
594 594
 func TestRestartStdin(t *testing.T) {
595 595
 	runtime := mkRuntime(t)
596 596
 	defer nuke(runtime)
597
-	container, err := runtime.Create(&Config{
597
+	container, _, err := runtime.Create(&Config{
598 598
 		Image: GetTestImage(runtime).ID,
599 599
 		Cmd:   []string{"cat"},
600 600
 
... ...
@@ -672,7 +672,7 @@ func TestUser(t *testing.T) {
672 672
 	defer nuke(runtime)
673 673
 
674 674
 	// Default user must be root
675
-	container, err := runtime.Create(&Config{
675
+	container, _, err := runtime.Create(&Config{
676 676
 		Image: GetTestImage(runtime).ID,
677 677
 		Cmd:   []string{"id"},
678 678
 	},
... ...
@@ -690,7 +690,7 @@ func TestUser(t *testing.T) {
690 690
 	}
691 691
 
692 692
 	// Set a username
693
-	container, err = runtime.Create(&Config{
693
+	container, _, err = runtime.Create(&Config{
694 694
 		Image: GetTestImage(runtime).ID,
695 695
 		Cmd:   []string{"id"},
696 696
 
... ...
@@ -710,7 +710,7 @@ func TestUser(t *testing.T) {
710 710
 	}
711 711
 
712 712
 	// Set a UID
713
-	container, err = runtime.Create(&Config{
713
+	container, _, err = runtime.Create(&Config{
714 714
 		Image: GetTestImage(runtime).ID,
715 715
 		Cmd:   []string{"id"},
716 716
 
... ...
@@ -730,7 +730,7 @@ func TestUser(t *testing.T) {
730 730
 	}
731 731
 
732 732
 	// Set a different user by uid
733
-	container, err = runtime.Create(&Config{
733
+	container, _, err = runtime.Create(&Config{
734 734
 		Image: GetTestImage(runtime).ID,
735 735
 		Cmd:   []string{"id"},
736 736
 
... ...
@@ -752,7 +752,7 @@ func TestUser(t *testing.T) {
752 752
 	}
753 753
 
754 754
 	// Set a different user by username
755
-	container, err = runtime.Create(&Config{
755
+	container, _, err = runtime.Create(&Config{
756 756
 		Image: GetTestImage(runtime).ID,
757 757
 		Cmd:   []string{"id"},
758 758
 
... ...
@@ -772,7 +772,7 @@ func TestUser(t *testing.T) {
772 772
 	}
773 773
 
774 774
 	// Test an wrong username
775
-	container, err = runtime.Create(&Config{
775
+	container, _, err = runtime.Create(&Config{
776 776
 		Image: GetTestImage(runtime).ID,
777 777
 		Cmd:   []string{"id"},
778 778
 
... ...
@@ -793,7 +793,7 @@ func TestMultipleContainers(t *testing.T) {
793 793
 	runtime := mkRuntime(t)
794 794
 	defer nuke(runtime)
795 795
 
796
-	container1, err := runtime.Create(&Config{
796
+	container1, _, err := runtime.Create(&Config{
797 797
 		Image: GetTestImage(runtime).ID,
798 798
 		Cmd:   []string{"sleep", "2"},
799 799
 	},
... ...
@@ -803,7 +803,7 @@ func TestMultipleContainers(t *testing.T) {
803 803
 	}
804 804
 	defer runtime.Destroy(container1)
805 805
 
806
-	container2, err := runtime.Create(&Config{
806
+	container2, _, err := runtime.Create(&Config{
807 807
 		Image: GetTestImage(runtime).ID,
808 808
 		Cmd:   []string{"sleep", "2"},
809 809
 	},
... ...
@@ -847,7 +847,7 @@ func TestMultipleContainers(t *testing.T) {
847 847
 func TestStdin(t *testing.T) {
848 848
 	runtime := mkRuntime(t)
849 849
 	defer nuke(runtime)
850
-	container, err := runtime.Create(&Config{
850
+	container, _, err := runtime.Create(&Config{
851 851
 		Image: GetTestImage(runtime).ID,
852 852
 		Cmd:   []string{"cat"},
853 853
 
... ...
@@ -892,7 +892,7 @@ func TestStdin(t *testing.T) {
892 892
 func TestTty(t *testing.T) {
893 893
 	runtime := mkRuntime(t)
894 894
 	defer nuke(runtime)
895
-	container, err := runtime.Create(&Config{
895
+	container, _, err := runtime.Create(&Config{
896 896
 		Image: GetTestImage(runtime).ID,
897 897
 		Cmd:   []string{"cat"},
898 898
 
... ...
@@ -937,7 +937,7 @@ func TestTty(t *testing.T) {
937 937
 func TestEnv(t *testing.T) {
938 938
 	runtime := mkRuntime(t)
939 939
 	defer nuke(runtime)
940
-	container, err := runtime.Create(&Config{
940
+	container, _, err := runtime.Create(&Config{
941 941
 		Image: GetTestImage(runtime).ID,
942 942
 		Cmd:   []string{"env"},
943 943
 	},
... ...
@@ -986,7 +986,7 @@ func TestEnv(t *testing.T) {
986 986
 func TestEntrypoint(t *testing.T) {
987 987
 	runtime := mkRuntime(t)
988 988
 	defer nuke(runtime)
989
-	container, err := runtime.Create(
989
+	container, _, err := runtime.Create(
990 990
 		&Config{
991 991
 			Image:      GetTestImage(runtime).ID,
992 992
 			Entrypoint: []string{"/bin/echo"},
... ...
@@ -1009,7 +1009,7 @@ func TestEntrypoint(t *testing.T) {
1009 1009
 func TestEntrypointNoCmd(t *testing.T) {
1010 1010
 	runtime := mkRuntime(t)
1011 1011
 	defer nuke(runtime)
1012
-	container, err := runtime.Create(
1012
+	container, _, err := runtime.Create(
1013 1013
 		&Config{
1014 1014
 			Image:      GetTestImage(runtime).ID,
1015 1015
 			Entrypoint: []string{"/bin/echo", "foobar"},
... ...
@@ -1060,7 +1060,7 @@ func TestLXCConfig(t *testing.T) {
1060 1060
 	cpuMin := 100
1061 1061
 	cpuMax := 10000
1062 1062
 	cpu := cpuMin + rand.Intn(cpuMax-cpuMin)
1063
-	container, err := runtime.Create(&Config{
1063
+	container, _, err := runtime.Create(&Config{
1064 1064
 		Image: GetTestImage(runtime).ID,
1065 1065
 		Cmd:   []string{"/bin/true"},
1066 1066
 
... ...
@@ -1084,7 +1084,7 @@ func TestLXCConfig(t *testing.T) {
1084 1084
 func TestCustomLxcConfig(t *testing.T) {
1085 1085
 	runtime := mkRuntime(t)
1086 1086
 	defer nuke(runtime)
1087
-	container, err := runtime.Create(&Config{
1087
+	container, _, err := runtime.Create(&Config{
1088 1088
 		Image: GetTestImage(runtime).ID,
1089 1089
 		Cmd:   []string{"/bin/true"},
1090 1090
 
... ...
@@ -1115,7 +1115,7 @@ func BenchmarkRunSequencial(b *testing.B) {
1115 1115
 	runtime := mkRuntime(b)
1116 1116
 	defer nuke(runtime)
1117 1117
 	for i := 0; i < b.N; i++ {
1118
-		container, err := runtime.Create(&Config{
1118
+		container, _, err := runtime.Create(&Config{
1119 1119
 			Image: GetTestImage(runtime).ID,
1120 1120
 			Cmd:   []string{"echo", "-n", "foo"},
1121 1121
 		},
... ...
@@ -1147,7 +1147,7 @@ func BenchmarkRunParallel(b *testing.B) {
1147 1147
 		complete := make(chan error)
1148 1148
 		tasks = append(tasks, complete)
1149 1149
 		go func(i int, complete chan error) {
1150
-			container, err := runtime.Create(&Config{
1150
+			container, _, err := runtime.Create(&Config{
1151 1151
 				Image: GetTestImage(runtime).ID,
1152 1152
 				Cmd:   []string{"echo", "-n", "foo"},
1153 1153
 			},
... ...
@@ -1297,7 +1297,7 @@ func TestBindMounts(t *testing.T) {
1297 1297
 func TestVolumesFromReadonlyMount(t *testing.T) {
1298 1298
 	runtime := mkRuntime(t)
1299 1299
 	defer nuke(runtime)
1300
-	container, err := runtime.Create(
1300
+	container, _, err := runtime.Create(
1301 1301
 		&Config{
1302 1302
 			Image:   GetTestImage(runtime).ID,
1303 1303
 			Cmd:     []string{"/bin/echo", "-n", "foobar"},
... ...
@@ -1316,7 +1316,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
1316 1316
 		t.Fail()
1317 1317
 	}
1318 1318
 
1319
-	container2, err := runtime.Create(
1319
+	container2, _, err := runtime.Create(
1320 1320
 		&Config{
1321 1321
 			Image:       GetTestImage(runtime).ID,
1322 1322
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
... ...
@@ -1352,7 +1352,7 @@ func TestRestartWithVolumes(t *testing.T) {
1352 1352
 	runtime := mkRuntime(t)
1353 1353
 	defer nuke(runtime)
1354 1354
 
1355
-	container, err := runtime.Create(&Config{
1355
+	container, _, err := runtime.Create(&Config{
1356 1356
 		Image:   GetTestImage(runtime).ID,
1357 1357
 		Cmd:     []string{"echo", "-n", "foobar"},
1358 1358
 		Volumes: map[string]struct{}{"/test": {}},
... ...
@@ -1395,7 +1395,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
1395 1395
 	runtime := mkRuntime(t)
1396 1396
 	defer nuke(runtime)
1397 1397
 
1398
-	container, err := runtime.Create(&Config{
1398
+	container, _, err := runtime.Create(&Config{
1399 1399
 		Image:   GetTestImage(runtime).ID,
1400 1400
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
1401 1401
 		Volumes: map[string]struct{}{"/test": {}},
... ...
@@ -1422,7 +1422,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
1422 1422
 		t.Fail()
1423 1423
 	}
1424 1424
 
1425
-	container2, err := runtime.Create(
1425
+	container2, _, err := runtime.Create(
1426 1426
 		&Config{
1427 1427
 			Image:       GetTestImage(runtime).ID,
1428 1428
 			Cmd:         []string{"cat", "/test/foo"},
... ...
@@ -1463,7 +1463,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
1463 1463
 	if err != nil {
1464 1464
 		t.Fatal(err)
1465 1465
 	}
1466
-	c, err := runtime.Create(config)
1466
+	c, _, err := runtime.Create(config)
1467 1467
 	if err != nil {
1468 1468
 		t.Fatal(err)
1469 1469
 	}
... ...
@@ -1529,7 +1529,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
1529 1529
 	runtime := mkRuntime(t)
1530 1530
 	defer nuke(runtime)
1531 1531
 
1532
-	container, err := runtime.Create(&Config{
1532
+	container, _, err := runtime.Create(&Config{
1533 1533
 		Image:   GetTestImage(runtime).ID,
1534 1534
 		Cmd:     []string{"sh", "-c", "echo -n bar > /test/foo"},
1535 1535
 		Volumes: map[string]struct{}{"/test": {}},
... ...
@@ -1556,7 +1556,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
1556 1556
 		t.Fail()
1557 1557
 	}
1558 1558
 
1559
-	container2, err := runtime.Create(
1559
+	container2, _, err := runtime.Create(
1560 1560
 		&Config{
1561 1561
 			Image:   GetTestImage(runtime).ID,
1562 1562
 			Cmd:     []string{"sh", "-c", "echo -n bar > /other/foo"},
... ...
@@ -1577,7 +1577,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
1577 1577
 		t.Fatal(err)
1578 1578
 	}
1579 1579
 
1580
-	container3, err := runtime.Create(
1580
+	container3, _, err := runtime.Create(
1581 1581
 		&Config{
1582 1582
 			Image:       GetTestImage(runtime).ID,
1583 1583
 			Cmd:         []string{"/bin/echo", "-n", "foobar"},
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"github.com/dotcloud/docker/utils"
8 8
 	"io/ioutil"
9 9
 	"log"
10
+	"net"
10 11
 	"os"
11 12
 	"os/signal"
12 13
 	"strconv"
... ...
@@ -37,7 +38,11 @@ func main() {
37 37
 	flDns := flag.String("dns", "", "Set custom dns servers")
38 38
 	flHosts := docker.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)}
39 39
 	flag.Var(&flHosts, "H", "tcp://host:port to bind/connect to or unix://path/to/socket to use")
40
+	flEnableIptables := flag.Bool("iptables", true, "Disable iptables within docker")
41
+	flDefaultIp := flag.String("ip", "0.0.0.0", "Default ip address to use when binding a containers ports")
42
+
40 43
 	flag.Parse()
44
+
41 45
 	if *flVersion {
42 46
 		showVersion()
43 47
 		return
... ...
@@ -54,10 +59,9 @@ func main() {
54 54
 		}
55 55
 	}
56 56
 
57
+	bridge := docker.DefaultNetworkBridge
57 58
 	if *bridgeName != "" {
58
-		docker.NetworkBridgeIface = *bridgeName
59
-	} else {
60
-		docker.NetworkBridgeIface = docker.DefaultNetworkBridge
59
+		bridge = *bridgeName
61 60
 	}
62 61
 	if *flDebug {
63 62
 		os.Setenv("DEBUG", "1")
... ...
@@ -69,7 +73,25 @@ func main() {
69 69
 			flag.Usage()
70 70
 			return
71 71
 		}
72
-		if err := daemon(*pidfile, *flGraphPath, flHosts, *flAutoRestart, *flEnableCors, *flDns); err != nil {
72
+		var dns []string
73
+		if *flDns != "" {
74
+			dns = []string{*flDns}
75
+		}
76
+
77
+		ip := net.ParseIP(*flDefaultIp)
78
+
79
+		config := &docker.DaemonConfig{
80
+			Pidfile:        *pidfile,
81
+			GraphPath:      *flGraphPath,
82
+			AutoRestart:    *flAutoRestart,
83
+			EnableCors:     *flEnableCors,
84
+			Dns:            dns,
85
+			EnableIptables: *flEnableIptables,
86
+			BridgeIface:    bridge,
87
+			ProtoAddresses: flHosts,
88
+			DefaultIp:      ip,
89
+		}
90
+		if err := daemon(config); err != nil {
73 91
 			log.Fatal(err)
74 92
 		}
75 93
 	} else {
... ...
@@ -117,30 +139,26 @@ func removePidFile(pidfile string) {
117 117
 	}
118 118
 }
119 119
 
120
-func daemon(pidfile string, flGraphPath string, protoAddrs []string, autoRestart, enableCors bool, flDns string) error {
121
-	if err := createPidFile(pidfile); err != nil {
120
+func daemon(config *docker.DaemonConfig) error {
121
+	if err := createPidFile(config.Pidfile); err != nil {
122 122
 		log.Fatal(err)
123 123
 	}
124
-	defer removePidFile(pidfile)
124
+	defer removePidFile(config.Pidfile)
125 125
 
126 126
 	c := make(chan os.Signal, 1)
127 127
 	signal.Notify(c, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM))
128 128
 	go func() {
129 129
 		sig := <-c
130 130
 		log.Printf("Received signal '%v', exiting\n", sig)
131
-		removePidFile(pidfile)
131
+		removePidFile(config.Pidfile)
132 132
 		os.Exit(0)
133 133
 	}()
134
-	var dns []string
135
-	if flDns != "" {
136
-		dns = []string{flDns}
137
-	}
138
-	server, err := docker.NewServer(flGraphPath, autoRestart, enableCors, dns)
134
+	server, err := docker.NewServer(config)
139 135
 	if err != nil {
140 136
 		return err
141 137
 	}
142
-	chErrors := make(chan error, len(protoAddrs))
143
-	for _, protoAddr := range protoAddrs {
138
+	chErrors := make(chan error, len(config.ProtoAddresses))
139
+	for _, protoAddr := range config.ProtoAddresses {
144 140
 		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
145 141
 		if protoAddrParts[0] == "unix" {
146 142
 			syscall.Unlink(protoAddrParts[1])
... ...
@@ -155,7 +173,7 @@ func daemon(pidfile string, flGraphPath string, protoAddrs []string, autoRestart
155 155
 			chErrors <- docker.ListenAndServe(protoAddrParts[0], protoAddrParts[1], server, true)
156 156
 		}()
157 157
 	}
158
-	for i := 0; i < len(protoAddrs); i += 1 {
158
+	for i := 0; i < len(config.ProtoAddresses); i += 1 {
159 159
 		err := <-chErrors
160 160
 		if err != nil {
161 161
 			return err
... ...
@@ -96,8 +96,8 @@ Examples:
96 96
 
97 97
 .. _cli_build_examples:
98 98
 
99
-Examples
100
-~~~~~~~~
99
+Examples:
100
+~~~~~~~~~
101 101
 
102 102
 .. code-block:: bash
103 103
 
... ...
@@ -403,6 +403,33 @@ Insert file from github
403 403
 
404 404
     Kill a running container
405 405
 
406
+.. _cli_link:
407
+
408
+``link``
409
+--------
410
+
411
+::
412
+
413
+    Usage: docker link CURRENT_NAME NEW_NAME
414
+
415
+    Link a container to a new name.
416
+
417
+
418
+Examples:
419
+~~~~~~~~~
420
+
421
+.. code-block:: bash
422
+
423
+    $ docker link /59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc /redis
424
+    $ docker ls
425
+    NAME                                                                      ID                                                                 IMAGE
426
+    /redis                                                                    59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc   crosbymichael/redis:latest
427
+    /59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc         59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc   crosbymichael/redis:latest
428
+
429
+
430
+This will create a new link for the existing name ``/59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc`` 
431
+with the new name ``/redis`` so that we can new reference the same container under the new name ``/redis``.
432
+
406 433
 .. _cli_login:
407 434
 
408 435
 ``login``
... ...
@@ -430,7 +457,6 @@ Insert file from github
430 430
 ``logs``
431 431
 --------
432 432
 
433
-
434 433
 ::
435 434
 
436 435
     Usage: docker logs [OPTIONS] CONTAINER
... ...
@@ -510,6 +536,29 @@ Insert file from github
510 510
     Usage: docker rm [OPTIONS] CONTAINER
511 511
 
512 512
     Remove one or more containers
513
+        -link="": Remove the link instead of the actual container
514
+ 
515
+
516
+Examples:
517
+~~~~~~~~~
518
+
519
+.. code-block:: bash
520
+
521
+    $ docker rm /redis
522
+    /redis
523
+
524
+
525
+This will remove the container referenced under the link ``/redis``.
526
+
527
+
528
+.. code-block:: bash
529
+
530
+    $ docker rm -link /webapp/redis
531
+    /webapp/redis
532
+
533
+
534
+This will remove the underlying link between ``/webapp`` and the ``/redis`` containers removing all
535
+network communication.
513 536
 
514 537
 .. _cli_rmi:
515 538
 
... ...
@@ -533,7 +582,7 @@ Insert file from github
533 533
 
534 534
     Run a command in a new container
535 535
 
536
-      -a=map[]: Attach to stdin, stdout or stderr.
536
+      -a=map[]: Attach to stdin, stdout or stderr
537 537
       -c=0: CPU shares (relative weight)
538 538
       -cidfile="": Write the container ID to the file
539 539
       -d=false: Detached mode: Run container in the background, print new container id
... ...
@@ -549,14 +598,16 @@ Insert file from github
549 549
       -u="": Username or UID
550 550
       -dns=[]: Set custom dns servers for the container
551 551
       -v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro]. If "container-dir" is missing, then docker creates a new volume.
552
-      -volumes-from="": Mount all volumes from the given container.
553
-      -entrypoint="": Overwrite the default entrypoint set by the image.
552
+      -volumes-from="": Mount all volumes from the given container
553
+      -entrypoint="": Overwrite the default entrypoint set by the image
554 554
       -w="": Working directory inside the container
555 555
       -lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
556 556
       -sig-proxy=false: Proxify all received signal to the process (even in non-tty mode)
557
+      -expose=[]: Expose a port from the container without publishing it to your host
558
+      -link="": Add link to another container (containerid:alias)
557 559
 
558 560
 Examples
559
-~~~~~~~~
561
+--------
560 562
 
561 563
 .. code-block:: bash
562 564
 
... ...
@@ -604,6 +655,38 @@ working directory, by changing into the directory to the value
604 604
 returned by ``pwd``. So this combination executes the command
605 605
 using the container, but inside the current working directory.
606 606
 
607
+.. code-block:: bash
608
+
609
+    docker run -p 127.0.0.0::80 ubuntu bash
610
+
611
+This the ``-p`` flag now allows you to bind a port to a specific
612
+interface of the host machine.  In this example port ``80`` of the 
613
+container will have a dynamically allocated port bound to 127.0.0.1 
614
+of the host.
615
+
616
+.. code-block:: bash
617
+
618
+    docker run -p 127.0.0.1:80:80 ubuntu bash
619
+
620
+This will bind port ``80`` of the container to port ``80`` on 127.0.0.1 of your
621
+host machine.
622
+
623
+.. code-block:: bash
624
+
625
+    docker run -expose 80 ubuntu bash
626
+
627
+This will expose port ``80`` of the container for use within a link
628
+without publishing the port to the host system's interfaces.  
629
+
630
+.. code-block:: bash
631
+
632
+    docker run -link /redis:redis ubuntu bash
633
+
634
+The ``-link`` flag will link the container named ``/redis`` into the 
635
+newly created container with the alias ``redis``.  The new container
636
+can access the network and environment of the redis container via
637
+environment variables.
638
+
607 639
 .. _cli_search:
608 640
 
609 641
 ``search``
... ...
@@ -1,6 +1,6 @@
1 1
 :title: Docker Examples
2 2
 :description: Examples on how to use Docker
3
-:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql
3
+:keywords: docker, hello world, node, nodejs, python, couch, couchdb, redis, ssh, sshd, examples, postgresql, link
4 4
 
5 5
 
6 6
 .. _example_list:
... ...
@@ -24,3 +24,4 @@ to more substantial services like you might find in production.
24 24
    postgresql_service
25 25
    mongodb
26 26
    running_riak_service
27
+   linking_into_redis
27 28
new file mode 100644
... ...
@@ -0,0 +1,146 @@
0
+:title: Linking to an Redis container
1
+:description: Running redis linked into your web app
2
+:keywords: docker, example, networking, redis, link
3
+
4
+.. _linking_redis:
5
+
6
+Linking Redis
7
+=============
8
+
9
+.. include:: example_header.inc
10
+
11
+Building a redis container to link as a child of our web application.
12
+
13
+Building the redis container
14
+----------------------------
15
+
16
+We will use a pre-build version of redis from the index under 
17
+the name ``crosbymichael/redis``.  If you are interested in the 
18
+Dockerfile that was used to build this container here it is.
19
+
20
+.. code-block:: bash
21
+    
22
+    # Build redis from source
23
+    # Make sure you have the redis source code checked out in
24
+    # the same directory as this Dockerfile
25
+    FROM ubuntu
26
+
27
+    RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
28
+    RUN apt-get update
29
+    RUN apt-get upgrade -y
30
+
31
+    RUN apt-get install -y gcc make g++ build-essential libc6-dev tcl
32
+
33
+    ADD . /redis
34
+
35
+    RUN (cd /redis && make)
36
+    RUN (cd /redis && make test)
37
+
38
+    RUN mkdir -p /redis-data
39
+    VOLUME ["/redis-data"]
40
+    EXPOSE 6379
41
+
42
+    ENTRYPOINT ["/redis/src/redis-server"]
43
+    CMD ["--dir", "/redis-data"]
44
+
45
+
46
+We need to ``EXPOSE`` the default port of 6379 so that our link knows what ports 
47
+to connect to our redis container on.  If you do not expose any ports for the
48
+image then docker will not be able to establish the link between containers.
49
+
50
+
51
+Run the redis container
52
+-----------------------
53
+
54
+.. code-block:: bash
55
+    
56
+    docker run -d -e PASSWORD=docker crosbymichael/redis --requirepass=docker
57
+ 
58
+This will run our redis container using the default port of 6379 and using
59
+as password to secure our service. Next we will link the redis container to 
60
+a new name using ``docker link`` and ``docker ls``.
61
+
62
+
63
+Linking an existing container
64
+-----------------------------
65
+
66
+.. code-block:: bash
67
+
68
+    docker ls
69
+
70
+    NAME                                                                      ID                                                                 IMAGE
71
+    /39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89         39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89   crosbymichael/redis:latest
72
+
73
+
74
+Docker will automatically create an initial link with the container's id but
75
+because the is long and not very user friendly we can link the container with
76
+a new name.
77
+
78
+.. code-block:: bash
79
+
80
+    docker link /39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89 /redis
81
+
82
+    docker ls 
83
+
84
+    NAME                                                                      ID                                                                 IMAGE
85
+    /redis                                                                    39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89   crosbymichael/redis:latest
86
+    /39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89         39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89   crosbymichael/redis:latest
87
+
88
+Now we can reference our running redis service using the friendly name ``/redis``.  
89
+We can issue all the commands that you would expect; start, stop, attach, using the new name.
90
+
91
+Linking redis as a child
92
+------------------------
93
+
94
+Next we can start a new web application that has a dependency on redis and apply a link 
95
+to connect both containers.  If you noticed when running our redis service we did not use
96
+the ``-p`` option to publish the redis port to the host system.  Redis exposed port 6379
97
+but we did not publish the port.  This allows docker to prevent all network traffic to
98
+the redis container except when explicitly specified within a link.  This is a big win
99
+for security.  
100
+
101
+
102
+Now lets start our web application with a link into redis.
103
+
104
+.. code-block:: bash
105
+   
106
+    docker run -t -i -link /redis:db ubuntu bash
107
+
108
+    root@4c01db0b339c:/# env
109
+
110
+    HOSTNAME=4c01db0b339c
111
+    DB_NAME=/4c01db0b339cf19958731255a796ee072040a652f51652a4ade190ab8c27006f/db
112
+    TERM=xterm
113
+    DB_PORT=tcp://172.17.0.8:6379
114
+    DB_PORT_6379_TCP=tcp://172.17.0.8:6379
115
+    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
116
+    PWD=/
117
+    DB_ENV_PASSWORD=dockerpass
118
+    SHLVL=1
119
+    HOME=/
120
+    container=lxc
121
+    _=/usr/bin/env
122
+    root@4c01db0b339c:/#
123
+
124
+
125
+When we inspect the environment of the linked container we can see a few extra environment 
126
+variables have been added.  When you specified ``-link /redis:db`` you are telling docker
127
+to link the container named ``/redis`` into this new container with the alias ``db``.  
128
+Environment variables are prefixed with the alias so that the parent container can access
129
+network and environment information from the child.
130
+
131
+.. code-block:: bash
132
+
133
+    # The name of the child container
134
+    DB_NAME=/4c01db0b339cf19958731255a796ee072040a652f51652a4ade190ab8c27006f/db
135
+    # The default protocol, ip, and port of the service running in the container
136
+    DB_PORT=tcp://172.17.0.8:6379
137
+    # A specific protocol, ip, and port of various services
138
+    DB_PORT_6379_TCP=tcp://172.17.0.8:6379
139
+    # Get environment variables of the container 
140
+    DB_ENV_PASSWORD=dockerpass
141
+
142
+
143
+Accessing the network information along with the environment of the child container allows
144
+us to easily connect to the redis service on the specific ip and port and use the password
145
+specified in the environment.  
0 146
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
0 1
new file mode 100644
... ...
@@ -0,0 +1,455 @@
0
+package gograph
1
+
2
+import (
3
+	_ "code.google.com/p/gosqlite/sqlite3"
4
+	"database/sql"
5
+	"fmt"
6
+	"os"
7
+	"path"
8
+)
9
+
10
+const (
11
+	createEntityTable = `
12
+    CREATE TABLE IF NOT EXISTS entity (
13
+        id text NOT NULL PRIMARY KEY
14
+    );`
15
+
16
+	createEdgeTable = `
17
+    CREATE TABLE IF NOT EXISTS edge (
18
+        "entity_id" text NOT NULL,
19
+        "parent_id" text NULL,
20
+        "name" text NOT NULL,
21
+        CONSTRAINT "parent_fk" FOREIGN KEY ("parent_id") REFERENCES "entity" ("id"),
22
+        CONSTRAINT "entity_fk" FOREIGN KEY ("entity_id") REFERENCES "entity" ("id")
23
+        );
24
+
25
+    CREATE UNIQUE INDEX "name_parent_ix" ON "edge" (parent_id, name);
26
+    `
27
+)
28
+
29
+// Entity with a unique id
30
+type Entity struct {
31
+	id string
32
+}
33
+
34
+// An Edge connects two entities together
35
+type Edge struct {
36
+	EntityID string
37
+	Name     string
38
+	ParentID string
39
+}
40
+
41
+type Entities map[string]*Entity
42
+type Edges []*Edge
43
+
44
+type WalkFunc func(fullPath string, entity *Entity) error
45
+
46
+// Graph database for storing entities and their relationships
47
+type Database struct {
48
+	dbPath string
49
+}
50
+
51
+// Create a new graph database initialized with a root entity
52
+func NewDatabase(dbPath string) (*Database, error) {
53
+	db := &Database{dbPath}
54
+	if _, err := os.Stat(dbPath); err == nil {
55
+		return db, nil
56
+	}
57
+	conn, err := db.openConn()
58
+	if err != nil {
59
+		return nil, err
60
+	}
61
+	defer conn.Close()
62
+
63
+	if _, err := conn.Exec(createEntityTable); err != nil {
64
+		return nil, err
65
+	}
66
+	if _, err := conn.Exec(createEdgeTable); err != nil {
67
+		return nil, err
68
+	}
69
+
70
+	rollback := func() {
71
+		conn.Exec("ROLLBACK")
72
+	}
73
+
74
+	// Create root entities
75
+	if _, err := conn.Exec("BEGIN"); err != nil {
76
+		return nil, err
77
+	}
78
+	if _, err := conn.Exec("INSERT INTO entity (id) VALUES (?);", "0"); err != nil {
79
+		rollback()
80
+		return nil, err
81
+	}
82
+
83
+	if _, err := conn.Exec("INSERT INTO edge (entity_id, name) VALUES(?,?);", "0", "/"); err != nil {
84
+		rollback()
85
+		return nil, err
86
+	}
87
+
88
+	if _, err := conn.Exec("COMMIT"); err != nil {
89
+		return nil, err
90
+	}
91
+	return db, nil
92
+}
93
+
94
+// Set the entity id for a given path
95
+func (db *Database) Set(fullPath, id string) (*Entity, error) {
96
+	conn, err := db.openConn()
97
+	if err != nil {
98
+		return nil, err
99
+	}
100
+	defer conn.Close()
101
+	rollback := func() {
102
+		conn.Exec("ROLLBACK")
103
+	}
104
+	if _, err := conn.Exec("BEGIN"); err != nil {
105
+		return nil, err
106
+	}
107
+	var entityId string
108
+	if err := conn.QueryRow("SELECT id FROM entity WHERE id = ?;", id).Scan(&entityId); err != nil {
109
+		if err == sql.ErrNoRows {
110
+			if _, err := conn.Exec("INSERT INTO entity (id) VALUES(?);", id); err != nil {
111
+				rollback()
112
+				return nil, err
113
+			}
114
+		} else {
115
+			rollback()
116
+			return nil, err
117
+		}
118
+	}
119
+	e := &Entity{id}
120
+
121
+	parentPath, name := splitPath(fullPath)
122
+	if err := db.setEdge(conn, parentPath, name, e); err != nil {
123
+		rollback()
124
+		return nil, err
125
+	}
126
+
127
+	if _, err := conn.Exec("COMMIT"); err != nil {
128
+		return nil, err
129
+	}
130
+	return e, nil
131
+}
132
+
133
+func (db *Database) setEdge(conn *sql.DB, parentPath, name string, e *Entity) error {
134
+	parent, err := db.get(conn, parentPath)
135
+	if err != nil {
136
+		return err
137
+	}
138
+	if parent.id == e.id {
139
+		return fmt.Errorf("Cannot set self as child")
140
+	}
141
+
142
+	if _, err := conn.Exec("INSERT INTO edge (parent_id, name, entity_id) VALUES (?,?,?);", parent.id, name, e.id); err != nil {
143
+		return err
144
+	}
145
+	return nil
146
+}
147
+
148
+// Return the root "/" entity for the database
149
+func (db *Database) RootEntity() *Entity {
150
+	return &Entity{
151
+		id: "0",
152
+	}
153
+}
154
+
155
+// Return the entity for a given path
156
+func (db *Database) Get(name string) *Entity {
157
+	conn, err := db.openConn()
158
+	if err != nil {
159
+		return nil
160
+	}
161
+	e, err := db.get(conn, name)
162
+	if err != nil {
163
+		return nil
164
+	}
165
+	return e
166
+}
167
+
168
+func (db *Database) get(conn *sql.DB, name string) (*Entity, error) {
169
+	e := db.RootEntity()
170
+	// We always know the root name so return it if
171
+	// it is requested
172
+	if name == "/" {
173
+		return e, nil
174
+	}
175
+
176
+	parts := split(name)
177
+	for i := 1; i < len(parts); i++ {
178
+		p := parts[i]
179
+
180
+		next := db.child(conn, e, p)
181
+		if next == nil {
182
+			return nil, fmt.Errorf("Cannot find child")
183
+		}
184
+		e = next
185
+	}
186
+	return e, nil
187
+
188
+}
189
+
190
+// List all entities by from the name
191
+// The key will be the full path of the entity
192
+func (db *Database) List(name string, depth int) Entities {
193
+	out := Entities{}
194
+	conn, err := db.openConn()
195
+	if err != nil {
196
+		return out
197
+	}
198
+	defer conn.Close()
199
+
200
+	for c := range db.children(conn, name, depth) {
201
+		out[c.FullPath] = c.Entity
202
+	}
203
+	return out
204
+}
205
+
206
+func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
207
+	conn, err := db.openConn()
208
+	if err != nil {
209
+		return err
210
+	}
211
+	defer conn.Close()
212
+
213
+	for c := range db.children(conn, name, depth) {
214
+		if err := walkFunc(c.FullPath, c.Entity); err != nil {
215
+			return err
216
+		}
217
+	}
218
+	return nil
219
+}
220
+
221
+// Return the refrence count for a specified id
222
+func (db *Database) Refs(id string) int {
223
+	conn, err := db.openConn()
224
+	if err != nil {
225
+		return -1
226
+	}
227
+	defer conn.Close()
228
+
229
+	var count int
230
+	if err := conn.QueryRow("SELECT COUNT(*) FROM edge WHERE entity_id = ?;", id).Scan(&count); err != nil {
231
+		return 0
232
+	}
233
+	return count
234
+}
235
+
236
+// Return all the id's path references
237
+func (db *Database) RefPaths(id string) Edges {
238
+	refs := Edges{}
239
+	conn, err := db.openConn()
240
+	if err != nil {
241
+		return refs
242
+	}
243
+	defer conn.Close()
244
+
245
+	rows, err := conn.Query("SELECT name, parent_id FROM edge WHERE entity_id = ?;", id)
246
+	if err != nil {
247
+		return refs
248
+	}
249
+	defer rows.Close()
250
+
251
+	for rows.Next() {
252
+		var name string
253
+		var parentId string
254
+		if err := rows.Scan(&name, &parentId); err != nil {
255
+			return refs
256
+		}
257
+		refs = append(refs, &Edge{
258
+			EntityID: id,
259
+			Name:     name,
260
+			ParentID: parentId,
261
+		})
262
+	}
263
+	return refs
264
+}
265
+
266
+// Delete the reference to an entity at a given path
267
+func (db *Database) Delete(name string) error {
268
+	if name == "/" {
269
+		return fmt.Errorf("Cannot delete root entity")
270
+	}
271
+	conn, err := db.openConn()
272
+	if err != nil {
273
+		return err
274
+	}
275
+	defer conn.Close()
276
+
277
+	parentPath, n := splitPath(name)
278
+	parent, err := db.get(conn, parentPath)
279
+	if err != nil {
280
+		return err
281
+	}
282
+
283
+	if _, err := conn.Exec("DELETE FROM edge WHERE parent_id = ? AND name = ?;", parent.id, n); err != nil {
284
+		return err
285
+	}
286
+	return nil
287
+}
288
+
289
+// Remove the entity with the specified id
290
+// Walk the graph to make sure all references to the entity
291
+// are removed and return the number of references removed
292
+func (db *Database) Purge(id string) (int, error) {
293
+	conn, err := db.openConn()
294
+	if err != nil {
295
+		return -1, err
296
+	}
297
+	defer conn.Close()
298
+
299
+	rollback := func() {
300
+		conn.Exec("ROLLBACK")
301
+	}
302
+
303
+	if _, err := conn.Exec("BEGIN"); err != nil {
304
+		return -1, err
305
+	}
306
+
307
+	// Delete all edges
308
+	rows, err := conn.Exec("DELETE FROM edge WHERE entity_id = ?;", id)
309
+	if err != nil {
310
+		rollback()
311
+		return -1, err
312
+	}
313
+
314
+	changes, err := rows.RowsAffected()
315
+	if err != nil {
316
+		return -1, err
317
+	}
318
+
319
+	// Delete entity
320
+	if _, err := conn.Exec("DELETE FROM entity where id = ?;", id); err != nil {
321
+		rollback()
322
+		return -1, err
323
+	}
324
+
325
+	if _, err := conn.Exec("COMMIT"); err != nil {
326
+		return -1, err
327
+	}
328
+	return int(changes), nil
329
+}
330
+
331
+// Rename an edge for a given path
332
+func (db *Database) Rename(currentName, newName string) error {
333
+	parentPath, name := splitPath(currentName)
334
+	newParentPath, newEdgeName := splitPath(newName)
335
+
336
+	if parentPath != newParentPath {
337
+		return fmt.Errorf("Cannot rename when root paths do not match %s != %s", parentPath, newParentPath)
338
+	}
339
+
340
+	conn, err := db.openConn()
341
+	if err != nil {
342
+		return err
343
+	}
344
+	defer conn.Close()
345
+
346
+	parent, err := db.get(conn, parentPath)
347
+	if err != nil {
348
+		return err
349
+	}
350
+
351
+	rows, err := conn.Exec("UPDATE edge SET name = ? WHERE parent_id = ? AND name = ?;", newEdgeName, parent.id, name)
352
+	if err != nil {
353
+		return err
354
+	}
355
+	i, err := rows.RowsAffected()
356
+	if err != nil {
357
+		return err
358
+	}
359
+	if i == 0 {
360
+		return fmt.Errorf("Cannot locate edge for %s %s", parent.id, name)
361
+	}
362
+	return nil
363
+}
364
+
365
+type WalkMeta struct {
366
+	Parent   *Entity
367
+	Entity   *Entity
368
+	FullPath string
369
+	Edge     *Edge
370
+}
371
+
372
+func (db *Database) children(conn *sql.DB, name string, depth int) <-chan WalkMeta {
373
+	out := make(chan WalkMeta)
374
+	e, err := db.get(conn, name)
375
+	if err != nil {
376
+		close(out)
377
+		return out
378
+	}
379
+
380
+	go func() {
381
+		rows, err := conn.Query("SELECT entity_id, name FROM edge where parent_id = ?;", e.id)
382
+		if err != nil {
383
+			close(out)
384
+		}
385
+		defer rows.Close()
386
+
387
+		for rows.Next() {
388
+			var entityId, entityName string
389
+			if err := rows.Scan(&entityId, &entityName); err != nil {
390
+				// Log error
391
+				continue
392
+			}
393
+			child := &Entity{entityId}
394
+			edge := &Edge{
395
+				ParentID: e.id,
396
+				Name:     entityName,
397
+				EntityID: child.id,
398
+			}
399
+
400
+			meta := WalkMeta{
401
+				Parent:   e,
402
+				Entity:   child,
403
+				FullPath: path.Join(name, edge.Name),
404
+				Edge:     edge,
405
+			}
406
+
407
+			out <- meta
408
+			if depth == 0 {
409
+				continue
410
+			}
411
+			nDepth := depth
412
+			if depth != -1 {
413
+				nDepth -= 1
414
+			}
415
+			sc := db.children(conn, meta.FullPath, nDepth)
416
+			for c := range sc {
417
+				out <- c
418
+			}
419
+		}
420
+		close(out)
421
+	}()
422
+	return out
423
+}
424
+
425
+// Return the entity based on the parent path and name
426
+func (db *Database) child(conn *sql.DB, parent *Entity, name string) *Entity {
427
+	var id string
428
+	if err := conn.QueryRow("SELECT entity_id FROM edge WHERE parent_id = ? AND name = ?;", parent.id, name).Scan(&id); err != nil {
429
+		return nil
430
+	}
431
+	return &Entity{id}
432
+}
433
+
434
+func (db *Database) openConn() (*sql.DB, error) {
435
+	return sql.Open("sqlite3", db.dbPath)
436
+}
437
+
438
+// Return the id used to reference this entity
439
+func (e *Entity) ID() string {
440
+	return e.id
441
+}
442
+
443
+// Return the paths sorted by depth
444
+func (e Entities) Paths() []string {
445
+	out := make([]string, len(e))
446
+	var i int
447
+	for k := range e {
448
+		out[i] = k
449
+		i++
450
+	}
451
+	sortByDepth(out)
452
+
453
+	return out
454
+}
0 455
new file mode 100644
... ...
@@ -0,0 +1,452 @@
0
+package gograph
1
+
2
+import (
3
+	"os"
4
+	"path"
5
+	"strconv"
6
+	"testing"
7
+)
8
+
9
+func newTestDb(t *testing.T) *Database {
10
+	db, err := NewDatabase(path.Join(os.TempDir(), "sqlite.db"))
11
+	if err != nil {
12
+		t.Fatal(err)
13
+	}
14
+	return db
15
+}
16
+
17
+func destroyTestDb(db *Database) {
18
+	os.Remove(db.dbPath)
19
+}
20
+
21
+func TestNewDatabase(t *testing.T) {
22
+	db := newTestDb(t)
23
+	if db == nil {
24
+		t.Fatal("Datbase should not be nil")
25
+	}
26
+	defer destroyTestDb(db)
27
+}
28
+
29
+func TestCreateRootEnity(t *testing.T) {
30
+	db := newTestDb(t)
31
+	defer destroyTestDb(db)
32
+	root := db.RootEntity()
33
+	if root == nil {
34
+		t.Fatal("Root entity should not be nil")
35
+	}
36
+}
37
+
38
+func TestGetRootEntity(t *testing.T) {
39
+	db := newTestDb(t)
40
+	defer destroyTestDb(db)
41
+
42
+	e := db.Get("/")
43
+	if e == nil {
44
+		t.Fatal("Entity should not be nil")
45
+	}
46
+	if e.ID() != "0" {
47
+		t.Fatalf("Enity id should be 0, got %s", e.ID())
48
+	}
49
+}
50
+
51
+func TestSetEntityWithDifferentName(t *testing.T) {
52
+	db := newTestDb(t)
53
+	defer destroyTestDb(db)
54
+
55
+	db.Set("/test", "1")
56
+	if _, err := db.Set("/other", "1"); err != nil {
57
+		t.Fatal(err)
58
+	}
59
+}
60
+
61
+func TestCreateChild(t *testing.T) {
62
+	db := newTestDb(t)
63
+	defer destroyTestDb(db)
64
+
65
+	child, err := db.Set("/db", "1")
66
+	if err != nil {
67
+		t.Fatal(err)
68
+	}
69
+	if child == nil {
70
+		t.Fatal("Child should not be nil")
71
+	}
72
+	if child.ID() != "1" {
73
+		t.Fail()
74
+	}
75
+}
76
+
77
+func TestListAllRootChildren(t *testing.T) {
78
+	db := newTestDb(t)
79
+	defer destroyTestDb(db)
80
+
81
+	for i := 1; i < 6; i++ {
82
+		a := strconv.Itoa(i)
83
+		if _, err := db.Set("/"+a, a); err != nil {
84
+			t.Fatal(err)
85
+		}
86
+	}
87
+	entries := db.List("/", -1)
88
+	if len(entries) != 5 {
89
+		t.Fatalf("Expect 5 entries for / got %d", len(entries))
90
+	}
91
+}
92
+
93
+func TestListAllSubChildren(t *testing.T) {
94
+	db := newTestDb(t)
95
+	defer destroyTestDb(db)
96
+
97
+	_, err := db.Set("/webapp", "1")
98
+	if err != nil {
99
+		t.Fatal(err)
100
+	}
101
+	child2, err := db.Set("/db", "2")
102
+	if err != nil {
103
+		t.Fatal(err)
104
+	}
105
+	child4, err := db.Set("/logs", "4")
106
+	if err != nil {
107
+		t.Fatal(err)
108
+	}
109
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
110
+		t.Fatal(err)
111
+	}
112
+
113
+	child3, err := db.Set("/sentry", "3")
114
+	if err != nil {
115
+		t.Fatal(err)
116
+	}
117
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
118
+		t.Fatal(err)
119
+	}
120
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
121
+		t.Fatal(err)
122
+	}
123
+
124
+	entries := db.List("/webapp", 1)
125
+	if len(entries) != 3 {
126
+		t.Fatalf("Expect 3 entries for / got %d", len(entries))
127
+	}
128
+
129
+	entries = db.List("/webapp", 0)
130
+	if len(entries) != 2 {
131
+		t.Fatalf("Expect 2 entries for / got %d", len(entries))
132
+	}
133
+}
134
+
135
+func TestAddSelfAsChild(t *testing.T) {
136
+	db := newTestDb(t)
137
+	defer destroyTestDb(db)
138
+
139
+	child, err := db.Set("/test", "1")
140
+	if err != nil {
141
+		t.Fatal(err)
142
+	}
143
+	if _, err := db.Set("/test/other", child.ID()); err == nil {
144
+		t.Fatal("Error should not be nil")
145
+	}
146
+}
147
+
148
+func TestAddChildToNonExistantRoot(t *testing.T) {
149
+	db := newTestDb(t)
150
+	defer destroyTestDb(db)
151
+
152
+	if _, err := db.Set("/myapp", "1"); err != nil {
153
+		t.Fatal(err)
154
+	}
155
+
156
+	if _, err := db.Set("/myapp/proxy/db", "2"); err == nil {
157
+		t.Fatal("Error should not be nil")
158
+	}
159
+}
160
+
161
+func TestWalkAll(t *testing.T) {
162
+	db := newTestDb(t)
163
+	defer destroyTestDb(db)
164
+	_, err := db.Set("/webapp", "1")
165
+	if err != nil {
166
+		t.Fatal(err)
167
+	}
168
+	child2, err := db.Set("/db", "2")
169
+	if err != nil {
170
+		t.Fatal(err)
171
+	}
172
+	child4, err := db.Set("/db/logs", "4")
173
+	if err != nil {
174
+		t.Fatal(err)
175
+	}
176
+	if _, err := db.Set("/webapp/logs", child4.ID()); err != nil {
177
+		t.Fatal(err)
178
+	}
179
+
180
+	child3, err := db.Set("/sentry", "3")
181
+	if err != nil {
182
+		t.Fatal(err)
183
+	}
184
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
185
+		t.Fatal(err)
186
+	}
187
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
188
+		t.Fatal(err)
189
+	}
190
+
191
+	child5, err := db.Set("/gograph", "5")
192
+	if err != nil {
193
+		t.Fatal(err)
194
+	}
195
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
196
+		t.Fatal(err)
197
+	}
198
+
199
+	if err := db.Walk("/", func(p string, e *Entity) error {
200
+		t.Logf("Path: %s Entity: %s", p, e.ID())
201
+		return nil
202
+	}, -1); err != nil {
203
+		t.Fatal(err)
204
+	}
205
+}
206
+
207
+func TestGetEntityByPath(t *testing.T) {
208
+	db := newTestDb(t)
209
+	defer destroyTestDb(db)
210
+	_, err := db.Set("/webapp", "1")
211
+	if err != nil {
212
+		t.Fatal(err)
213
+	}
214
+	child2, err := db.Set("/db", "2")
215
+	if err != nil {
216
+		t.Fatal(err)
217
+	}
218
+	child4, err := db.Set("/logs", "4")
219
+	if err != nil {
220
+		t.Fatal(err)
221
+	}
222
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
223
+		t.Fatal(err)
224
+	}
225
+
226
+	child3, err := db.Set("/sentry", "3")
227
+	if err != nil {
228
+		t.Fatal(err)
229
+	}
230
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
231
+		t.Fatal(err)
232
+	}
233
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
234
+		t.Fatal(err)
235
+	}
236
+
237
+	child5, err := db.Set("/gograph", "5")
238
+	if err != nil {
239
+		t.Fatal(err)
240
+	}
241
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
242
+		t.Fatal(err)
243
+	}
244
+
245
+	entity := db.Get("/webapp/db/logs")
246
+	if entity == nil {
247
+		t.Fatal("Entity should not be nil")
248
+	}
249
+	if entity.ID() != "4" {
250
+		t.Fatalf("Expected to get entity with id 4, got %s", entity.ID())
251
+	}
252
+}
253
+
254
+func TestEnitiesPaths(t *testing.T) {
255
+	db := newTestDb(t)
256
+	defer destroyTestDb(db)
257
+	_, err := db.Set("/webapp", "1")
258
+	if err != nil {
259
+		t.Fatal(err)
260
+	}
261
+	child2, err := db.Set("/db", "2")
262
+	if err != nil {
263
+		t.Fatal(err)
264
+	}
265
+	child4, err := db.Set("/logs", "4")
266
+	if err != nil {
267
+		t.Fatal(err)
268
+	}
269
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
270
+		t.Fatal(err)
271
+	}
272
+
273
+	child3, err := db.Set("/sentry", "3")
274
+	if err != nil {
275
+		t.Fatal(err)
276
+	}
277
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
278
+		t.Fatal(err)
279
+	}
280
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
281
+		t.Fatal(err)
282
+	}
283
+
284
+	child5, err := db.Set("/gograph", "5")
285
+	if err != nil {
286
+		t.Fatal(err)
287
+	}
288
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
289
+		t.Fatal(err)
290
+	}
291
+
292
+	out := db.List("/", -1)
293
+	for _, p := range out.Paths() {
294
+		t.Log(p)
295
+	}
296
+}
297
+
298
+func TestDeleteRootEntity(t *testing.T) {
299
+	db := newTestDb(t)
300
+	defer destroyTestDb(db)
301
+
302
+	if err := db.Delete("/"); err == nil {
303
+		t.Fatal("Error should not be nil")
304
+	}
305
+}
306
+
307
+func TestDeleteEntity(t *testing.T) {
308
+	db := newTestDb(t)
309
+	defer destroyTestDb(db)
310
+	_, err := db.Set("/webapp", "1")
311
+	if err != nil {
312
+		t.Fatal(err)
313
+	}
314
+	child2, err := db.Set("/db", "2")
315
+	if err != nil {
316
+		t.Fatal(err)
317
+	}
318
+	child4, err := db.Set("/logs", "4")
319
+	if err != nil {
320
+		t.Fatal(err)
321
+	}
322
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
323
+		t.Fatal(err)
324
+	}
325
+
326
+	child3, err := db.Set("/sentry", "3")
327
+	if err != nil {
328
+		t.Fatal(err)
329
+	}
330
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
331
+		t.Fatal(err)
332
+	}
333
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
334
+		t.Fatal(err)
335
+	}
336
+
337
+	child5, err := db.Set("/gograph", "5")
338
+	if err != nil {
339
+		t.Fatal(err)
340
+	}
341
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
342
+		t.Fatal(err)
343
+	}
344
+
345
+	if err := db.Delete("/webapp/sentry"); err != nil {
346
+		t.Fatal(err)
347
+	}
348
+	entity := db.Get("/webapp/sentry")
349
+	if entity != nil {
350
+		t.Fatal("Entity /webapp/sentry should be nil")
351
+	}
352
+}
353
+
354
+func TestCountRefs(t *testing.T) {
355
+	db := newTestDb(t)
356
+	defer destroyTestDb(db)
357
+
358
+	db.Set("/webapp", "1")
359
+
360
+	if db.Refs("1") != 1 {
361
+		t.Fatal("Expect reference count to be 1")
362
+	}
363
+
364
+	db.Set("/db", "2")
365
+	db.Set("/webapp/db", "2")
366
+	if db.Refs("2") != 2 {
367
+		t.Fatal("Expect reference count to be 2")
368
+	}
369
+}
370
+
371
+func TestPurgeId(t *testing.T) {
372
+	db := newTestDb(t)
373
+	defer destroyTestDb(db)
374
+
375
+	db.Set("/webapp", "1")
376
+
377
+	if db.Refs("1") != 1 {
378
+		t.Fatal("Expect reference count to be 1")
379
+	}
380
+
381
+	db.Set("/db", "2")
382
+	db.Set("/webapp/db", "2")
383
+
384
+	count, err := db.Purge("2")
385
+	if err != nil {
386
+		t.Fatal(err)
387
+	}
388
+	if count != 2 {
389
+		t.Fatal("Expected 2 references to be removed")
390
+	}
391
+}
392
+
393
+func TestRename(t *testing.T) {
394
+	db := newTestDb(t)
395
+	defer destroyTestDb(db)
396
+
397
+	db.Set("/webapp", "1")
398
+
399
+	if db.Refs("1") != 1 {
400
+		t.Fatal("Expect reference count to be 1")
401
+	}
402
+
403
+	db.Set("/db", "2")
404
+	db.Set("/webapp/db", "2")
405
+
406
+	if db.Get("/webapp/db") == nil {
407
+		t.Fatal("Cannot find entity at path /webapp/db")
408
+	}
409
+
410
+	if err := db.Rename("/webapp/db", "/webapp/newdb"); err != nil {
411
+		t.Fatal(err)
412
+	}
413
+	if db.Get("/webapp/db") != nil {
414
+		t.Fatal("Entity should not exist at /webapp/db")
415
+	}
416
+	if db.Get("/webapp/newdb") == nil {
417
+		t.Fatal("Cannot find entity at path /webapp/newdb")
418
+	}
419
+
420
+}
421
+
422
+func TestCreateMultipleNames(t *testing.T) {
423
+	db := newTestDb(t)
424
+	defer destroyTestDb(db)
425
+
426
+	db.Set("/db", "1")
427
+	if _, err := db.Set("/myapp", "1"); err != nil {
428
+		t.Fatal(err)
429
+	}
430
+
431
+	db.Walk("/", func(p string, e *Entity) error {
432
+		t.Logf("%s\n", p)
433
+		return nil
434
+	}, -1)
435
+}
436
+
437
+func TestRefPaths(t *testing.T) {
438
+	db := newTestDb(t)
439
+	defer destroyTestDb(db)
440
+
441
+	db.Set("/webapp", "1")
442
+
443
+	db.Set("/db", "2")
444
+	db.Set("/webapp/db", "2")
445
+
446
+	refs := db.RefPaths("2")
447
+	if len(refs) != 2 {
448
+		t.Fatalf("Expected reference count to be 2, got %d", len(refs))
449
+	}
450
+
451
+}
0 452
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+package gograph
1
+
2
+import "sort"
3
+
4
+type pathSorter struct {
5
+	paths []string
6
+	by    func(i, j string) bool
7
+}
8
+
9
+func sortByDepth(paths []string) {
10
+	s := &pathSorter{paths, func(i, j string) bool {
11
+		return pathDepth(i) > pathDepth(j)
12
+	}}
13
+	sort.Sort(s)
14
+}
15
+
16
+func (s *pathSorter) Len() int {
17
+	return len(s.paths)
18
+}
19
+
20
+func (s *pathSorter) Swap(i, j int) {
21
+	s.paths[i], s.paths[j] = s.paths[j], s.paths[i]
22
+}
23
+
24
+func (s *pathSorter) Less(i, j int) bool {
25
+	return s.by(s.paths[i], s.paths[j])
26
+}
0 27
new file mode 100644
... ...
@@ -0,0 +1,29 @@
0
+package gograph
1
+
2
+import (
3
+	"testing"
4
+)
5
+
6
+func TestSort(t *testing.T) {
7
+	paths := []string{
8
+		"/",
9
+		"/myreallylongname",
10
+		"/app/db",
11
+	}
12
+
13
+	sortByDepth(paths)
14
+
15
+	if len(paths) != 3 {
16
+		t.Fatalf("Expected 3 parts got %d", len(paths))
17
+	}
18
+
19
+	if paths[0] != "/app/db" {
20
+		t.Fatalf("Expected /app/db got %s", paths[0])
21
+	}
22
+	if paths[1] != "/myreallylongname" {
23
+		t.Fatalf("Expected /myreallylongname got %s", paths[1])
24
+	}
25
+	if paths[2] != "/" {
26
+		t.Fatalf("Expected / got %s", paths[2])
27
+	}
28
+}
0 29
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+package gograph
1
+
2
+import (
3
+	"path"
4
+	"strings"
5
+)
6
+
7
+// Split p on /
8
+func split(p string) []string {
9
+	return strings.Split(p, "/")
10
+}
11
+
12
+// Returns the depth or number of / in a given path
13
+func pathDepth(p string) int {
14
+	parts := split(p)
15
+	if len(parts) == 2 && parts[1] == "" {
16
+		return 1
17
+	}
18
+	return len(parts)
19
+}
20
+
21
+func splitPath(p string) (parent, name string) {
22
+	if p[0] != '/' {
23
+		p = "/" + p
24
+	}
25
+	parent, name = path.Split(p)
26
+	l := len(parent)
27
+	if parent[l-1] == '/' {
28
+		parent = parent[:l-1]
29
+	}
30
+	return
31
+}
... ...
@@ -45,7 +45,8 @@ if [ -n "$(git status --porcelain)" ]; then
45 45
 fi
46 46
 
47 47
 # Use these flags when compiling the tests and final binary
48
-LDFLAGS="-X main.GITCOMMIT $GITCOMMIT -X main.VERSION $VERSION -d -w"
48
+LDFLAGS='-X main.GITCOMMIT "'$GITCOMMIT'" -X main.VERSION "'$VERSION'" -w -linkmode external -extldflags "-lpthread -static -Wl,--unresolved-symbols=ignore-in-object-files"'
49
+BUILDFLAGS='-tags netgo'
49 50
 
50 51
 
51 52
 bundle() {
... ...
@@ -2,6 +2,6 @@
2 2
 
3 3
 DEST=$1
4 4
 
5
-if go build -o $DEST/docker-$VERSION -ldflags "$LDFLAGS" ./docker; then
6
-	echo "Created binary: $DEST/docker-$VERSION"
7
-fi
5
+go build -o $DEST/docker-$VERSION -ldflags "$LDFLAGS" $BUILDFLAGS ./docker
6
+
7
+echo "Created binary: $DEST/docker-$VERSION"
... ...
@@ -15,7 +15,7 @@ bundle_test() {
15 15
 			set -x
16 16
 			cd $test_dir
17 17
 			go test -i
18
-			go test -v -ldflags "$LDFLAGS" $TESTFLAGS
18
+			go test -v -ldflags "$LDFLAGS" $BUILDFLAGS $TESTFLAGS
19 19
 		)  done
20 20
 	} 2>&1 | tee $DEST/test.log
21 21
 }
22 22
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
0 1
new file mode 100644
... ...
@@ -0,0 +1,105 @@
0
+package iptables
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"net"
6
+	"os/exec"
7
+	"strconv"
8
+	"strings"
9
+)
10
+
11
+type Action string
12
+
13
+const (
14
+	Add    Action = "-A"
15
+	Delete Action = "-D"
16
+)
17
+
18
+var (
19
+	ErrIptablesNotFound = errors.New("Iptables not found")
20
+	nat                 = []string{"-t", "nat"}
21
+)
22
+
23
+type Chain struct {
24
+	Name   string
25
+	Bridge string
26
+}
27
+
28
+func NewChain(name, bridge string) (*Chain, error) {
29
+	if err := Raw("-t", "nat", "-N", name); err != nil {
30
+		return nil, err
31
+	}
32
+	chain := &Chain{
33
+		Name:   name,
34
+		Bridge: bridge,
35
+	}
36
+
37
+	if err := chain.Prerouting(Add, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil {
38
+		return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
39
+	}
40
+	if err := chain.Output(Add, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil {
41
+		return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
42
+	}
43
+	return chain, nil
44
+}
45
+
46
+func RemoveExistingChain(name string) error {
47
+	chain := &Chain{
48
+		Name: name,
49
+	}
50
+	return chain.Remove()
51
+}
52
+
53
+func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr string, dest_port int) error {
54
+	return Raw("-t", "nat", fmt.Sprint(action), c.Name,
55
+		"-p", proto,
56
+		"-d", ip.String(),
57
+		"--dport", strconv.Itoa(port),
58
+		"!", "-i", c.Bridge,
59
+		"-j", "DNAT",
60
+		"--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port)))
61
+}
62
+
63
+func (c *Chain) Prerouting(action Action, args ...string) error {
64
+	a := append(nat, fmt.Sprint(action), "PREROUTING")
65
+	if len(args) > 0 {
66
+		a = append(a, args...)
67
+	}
68
+	return Raw(append(a, "-j", c.Name)...)
69
+}
70
+
71
+func (c *Chain) Output(action Action, args ...string) error {
72
+	a := append(nat, fmt.Sprint(action), "OUTPUT")
73
+	if len(args) > 0 {
74
+		a = append(a, args...)
75
+	}
76
+	return Raw(append(a, "-j", c.Name)...)
77
+}
78
+
79
+func (c *Chain) Remove() error {
80
+	// Ignore errors - This could mean the chains were never set up
81
+	c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
82
+	c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
83
+	c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6
84
+
85
+	c.Prerouting(Delete)
86
+	c.Output(Delete)
87
+
88
+	Raw("-t", "nat", "-F", c.Name)
89
+	Raw("-t", "nat", "-X", c.Name)
90
+
91
+	return nil
92
+}
93
+
94
+func Raw(args ...string) error {
95
+	path, err := exec.LookPath("iptables")
96
+	if err != nil {
97
+		return ErrIptablesNotFound
98
+	}
99
+	if err := exec.Command(path, args...).Run(); err != nil {
100
+		return fmt.Errorf("iptables failed: iptables %v", strings.Join(args, " "))
101
+	}
102
+	return nil
103
+
104
+}
0 105
new file mode 100644
... ...
@@ -0,0 +1,18 @@
0
+package iptables
1
+
2
+import (
3
+	"os"
4
+	"testing"
5
+)
6
+
7
+func TestIptables(t *testing.T) {
8
+	if err := Raw("-L"); err != nil {
9
+		t.Fatal(err)
10
+	}
11
+	path := os.Getenv("PATH")
12
+	os.Setenv("PATH", "")
13
+	defer os.Setenv("PATH", path)
14
+	if err := Raw("-L"); err == nil {
15
+		t.Fatal("Not finding iptables in the PATH should cause an error")
16
+	}
17
+}
0 18
new file mode 100644
... ...
@@ -0,0 +1,141 @@
0
+package docker
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/iptables"
5
+	"path"
6
+	"strings"
7
+)
8
+
9
+type Link struct {
10
+	ParentIP         string
11
+	ChildIP          string
12
+	Name             string
13
+	BridgeInterface  string
14
+	ChildEnvironment []string
15
+	Ports            []Port
16
+	IsEnabled        bool
17
+}
18
+
19
+func NewLink(parent, child *Container, name, bridgeInterface string) (*Link, error) {
20
+	if parent.ID == child.ID {
21
+		return nil, fmt.Errorf("Cannot link to self: %s == %s", parent.ID, child.ID)
22
+	}
23
+	if !child.State.Running {
24
+		return nil, fmt.Errorf("Cannot link to a non running container: %s AS %s", child.ID, name)
25
+	}
26
+
27
+	ports := make([]Port, len(child.Config.ExposedPorts))
28
+	var i int
29
+	for p := range child.Config.ExposedPorts {
30
+		ports[i] = p
31
+		i++
32
+	}
33
+
34
+	l := &Link{
35
+		BridgeInterface:  bridgeInterface,
36
+		Name:             name,
37
+		ChildIP:          child.NetworkSettings.IPAddress,
38
+		ParentIP:         parent.NetworkSettings.IPAddress,
39
+		ChildEnvironment: child.Config.Env,
40
+		Ports:            ports,
41
+	}
42
+	return l, nil
43
+
44
+}
45
+
46
+func (l *Link) Alias() string {
47
+	_, alias := path.Split(l.Name)
48
+	return alias
49
+}
50
+
51
+func (l *Link) ToEnv() []string {
52
+	env := []string{}
53
+	alias := strings.ToUpper(l.Alias())
54
+
55
+	if p := l.getDefaultPort(); p != nil {
56
+		env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
57
+	}
58
+
59
+	// Load exposed ports into the environment
60
+	for _, p := range l.Ports {
61
+		env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
62
+	}
63
+
64
+	// Load the linked container's name into the environment
65
+	env = append(env, fmt.Sprintf("%s_NAME=%s", alias, l.Name))
66
+
67
+	if l.ChildEnvironment != nil {
68
+		for _, v := range l.ChildEnvironment {
69
+			parts := strings.Split(v, "=")
70
+			if len(parts) != 2 {
71
+				continue
72
+			}
73
+			// Ignore a few variables that are added during docker build
74
+			if parts[0] == "HOME" || parts[0] == "PATH" {
75
+				continue
76
+			}
77
+			env = append(env, fmt.Sprintf("%s_ENV_%s=%s", alias, parts[0], parts[1]))
78
+		}
79
+	}
80
+	return env
81
+}
82
+
83
+// Default port rules
84
+func (l *Link) getDefaultPort() *Port {
85
+	var p Port
86
+	i := len(l.Ports)
87
+
88
+	if i == 0 {
89
+		return nil
90
+	} else if i > 1 {
91
+		sortPorts(l.Ports, func(ip, jp Port) bool {
92
+			// If the two ports have the same number, tcp takes priority
93
+			// Sort in desc order
94
+			return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
95
+		})
96
+	}
97
+	p = l.Ports[0]
98
+	return &p
99
+}
100
+
101
+func (l *Link) Enable() error {
102
+	if err := l.toggle("-I", false); err != nil {
103
+		return err
104
+	}
105
+	l.IsEnabled = true
106
+	return nil
107
+}
108
+
109
+func (l *Link) Disable() {
110
+	// We do not care about errors here because the link may not
111
+	// exist in iptables
112
+	l.toggle("-D", true)
113
+
114
+	l.IsEnabled = false
115
+}
116
+
117
+func (l *Link) toggle(action string, ignoreErrors bool) error {
118
+	for _, p := range l.Ports {
119
+		if err := iptables.Raw(action, "FORWARD",
120
+			"-i", l.BridgeInterface, "-o", l.BridgeInterface,
121
+			"-p", p.Proto(),
122
+			"-s", l.ParentIP,
123
+			"--dport", p.Port(),
124
+			"-d", l.ChildIP,
125
+			"-j", "ACCEPT"); !ignoreErrors && err != nil {
126
+			return err
127
+		}
128
+
129
+		if err := iptables.Raw(action, "FORWARD",
130
+			"-i", l.BridgeInterface, "-o", l.BridgeInterface,
131
+			"-p", p.Proto(),
132
+			"-s", l.ChildIP,
133
+			"--sport", p.Port(),
134
+			"-d", l.ParentIP,
135
+			"-j", "ACCEPT"); !ignoreErrors && err != nil {
136
+			return err
137
+		}
138
+	}
139
+	return nil
140
+}
0 141
new file mode 100644
... ...
@@ -0,0 +1,104 @@
0
+package docker
1
+
2
+import (
3
+	"strings"
4
+	"testing"
5
+)
6
+
7
+func newMockLinkContainer(id string, ip string) *Container {
8
+	return &Container{
9
+		Config: &Config{},
10
+		ID:     id,
11
+		NetworkSettings: &NetworkSettings{
12
+			IPAddress: ip,
13
+		},
14
+	}
15
+}
16
+
17
+func TestLinkNew(t *testing.T) {
18
+	toID := GenerateID()
19
+	fromID := GenerateID()
20
+
21
+	from := newMockLinkContainer(fromID, "172.0.17.2")
22
+	from.Config.Env = []string{}
23
+	from.State = State{Running: true}
24
+	ports := make(map[Port]struct{})
25
+
26
+	ports[Port("6379/tcp")] = struct{}{}
27
+
28
+	from.Config.ExposedPorts = ports
29
+
30
+	to := newMockLinkContainer(toID, "172.0.17.3")
31
+
32
+	link, err := NewLink(to, from, "/db/docker", "172.0.17.1")
33
+	if err != nil {
34
+		t.Fatal(err)
35
+	}
36
+
37
+	if link == nil {
38
+		t.FailNow()
39
+	}
40
+	if link.Name != "/db/docker" {
41
+		t.Fail()
42
+	}
43
+	if link.Alias() != "docker" {
44
+		t.Fail()
45
+	}
46
+	if link.ParentIP != "172.0.17.3" {
47
+		t.Fail()
48
+	}
49
+	if link.ChildIP != "172.0.17.2" {
50
+		t.Fail()
51
+	}
52
+	if link.BridgeInterface != "172.0.17.1" {
53
+		t.Fail()
54
+	}
55
+	for _, p := range link.Ports {
56
+		if p != Port("6379/tcp") {
57
+			t.Fail()
58
+		}
59
+	}
60
+}
61
+
62
+func TestLinkEnv(t *testing.T) {
63
+	toID := GenerateID()
64
+	fromID := GenerateID()
65
+
66
+	from := newMockLinkContainer(fromID, "172.0.17.2")
67
+	from.Config.Env = []string{"PASSWORD=gordon"}
68
+	from.State = State{Running: true}
69
+	ports := make(map[Port]struct{})
70
+
71
+	ports[Port("6379/tcp")] = struct{}{}
72
+
73
+	from.Config.ExposedPorts = ports
74
+
75
+	to := newMockLinkContainer(toID, "172.0.17.3")
76
+
77
+	link, err := NewLink(to, from, "/db/docker", "172.0.17.1")
78
+	if err != nil {
79
+		t.Fatal(err)
80
+	}
81
+
82
+	rawEnv := link.ToEnv()
83
+	env := make(map[string]string, len(rawEnv))
84
+	for _, e := range rawEnv {
85
+		parts := strings.Split(e, "=")
86
+		if len(parts) != 2 {
87
+			t.FailNow()
88
+		}
89
+		env[parts[0]] = parts[1]
90
+	}
91
+	if env["DOCKER_PORT"] != "tcp://172.0.17.2:6379" {
92
+		t.Fatalf("Expected tcp://172.0.17.2:6379, got %s", env["DOCKER_PORT"])
93
+	}
94
+	if env["DOCKER_PORT_6379_TCP"] != "tcp://172.0.17.2:6379" {
95
+		t.Fatalf("Expected tcp://172.0.17.2:6379, got %s", env["DOCKER_PORT_6379_TCP"])
96
+	}
97
+	if env["DOCKER_NAME"] != "/db/docker" {
98
+		t.Fatalf("Expected /db/docker, got %s", env["DOCKER_NAME"])
99
+	}
100
+	if env["DOCKER_ENV_PASSWORD"] != "gordon" {
101
+		t.Fatalf("Expected gordon, got %s", env["DOCKER_ENV_PASSWORD"])
102
+	}
103
+}
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"encoding/binary"
5 5
 	"errors"
6 6
 	"fmt"
7
+	"github.com/dotcloud/docker/iptables"
8
+	"github.com/dotcloud/docker/proxy"
7 9
 	"github.com/dotcloud/docker/utils"
8 10
 	"log"
9 11
 	"net"
... ...
@@ -13,8 +15,6 @@ import (
13 13
 	"sync"
14 14
 )
15 15
 
16
-var NetworkBridgeIface string
17
-
18 16
 const (
19 17
 	DefaultNetworkBridge = "docker0"
20 18
 	DisableNetworkBridge = "none"
... ...
@@ -81,18 +81,6 @@ func ip(args ...string) (string, error) {
81 81
 	return string(output), nil
82 82
 }
83 83
 
84
-// Wrapper around the iptables command
85
-func iptables(args ...string) error {
86
-	path, err := exec.LookPath("iptables")
87
-	if err != nil {
88
-		return fmt.Errorf("command not found: iptables")
89
-	}
90
-	if err := exec.Command(path, args...).Run(); err != nil {
91
-		return fmt.Errorf("iptables failed: iptables %v", strings.Join(args, " "))
92
-	}
93
-	return nil
94
-}
95
-
96 84
 func checkRouteOverlaps(routes string, dockerNetwork *net.IPNet) error {
97 85
 	utils.Debugf("Routes:\n\n%s", routes)
98 86
 	for _, line := range strings.Split(routes, "\n") {
... ...
@@ -124,7 +112,7 @@ func checkRouteOverlaps(routes string, dockerNetwork *net.IPNet) error {
124 124
 // CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`,
125 125
 // and attempts to configure it with an address which doesn't conflict with any other interface on the host.
126 126
 // If it can't find an address which doesn't conflict, it will return an error.
127
-func CreateBridgeIface(ifaceName string) error {
127
+func CreateBridgeIface(config *DaemonConfig) error {
128 128
 	addrs := []string{
129 129
 		// Here we don't follow the convention of using the 1st IP of the range for the gateway.
130 130
 		// This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges.
... ...
@@ -163,23 +151,29 @@ func CreateBridgeIface(ifaceName string) error {
163 163
 		}
164 164
 	}
165 165
 	if ifaceAddr == "" {
166
-		return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", ifaceName, ifaceName)
166
+		return fmt.Errorf("Could not find a free IP address range for interface '%s'. Please configure its address manually and run 'docker -b %s'", config.BridgeIface, config.BridgeIface)
167 167
 	}
168
-	utils.Debugf("Creating bridge %s with network %s", ifaceName, ifaceAddr)
168
+	utils.Debugf("Creating bridge %s with network %s", config.BridgeIface, ifaceAddr)
169 169
 
170
-	if output, err := ip("link", "add", ifaceName, "type", "bridge"); err != nil {
170
+	if output, err := ip("link", "add", config.BridgeIface, "type", "bridge"); err != nil {
171 171
 		return fmt.Errorf("Error creating bridge: %s (output: %s)", err, output)
172 172
 	}
173 173
 
174
-	if output, err := ip("addr", "add", ifaceAddr, "dev", ifaceName); err != nil {
174
+	if output, err := ip("addr", "add", ifaceAddr, "dev", config.BridgeIface); err != nil {
175 175
 		return fmt.Errorf("Unable to add private network: %s (%s)", err, output)
176 176
 	}
177
-	if output, err := ip("link", "set", ifaceName, "up"); err != nil {
177
+	if output, err := ip("link", "set", config.BridgeIface, "up"); err != nil {
178 178
 		return fmt.Errorf("Unable to start network bridge: %s (%s)", err, output)
179 179
 	}
180
-	if err := iptables("-t", "nat", "-A", "POSTROUTING", "-s", ifaceAddr,
181
-		"!", "-d", ifaceAddr, "-j", "MASQUERADE"); err != nil {
182
-		return fmt.Errorf("Unable to enable network bridge NAT: %s", err)
180
+	if config.EnableIptables {
181
+		if err := iptables.Raw("-t", "nat", "-A", "POSTROUTING", "-s", ifaceAddr,
182
+			"!", "-d", ifaceAddr, "-j", "MASQUERADE"); err != nil {
183
+			return fmt.Errorf("Unable to enable network bridge NAT: %s", err)
184
+		}
185
+		// Prevent inter-container communication by default
186
+		if err := iptables.Raw("-A", "FORWARD", "-i", config.BridgeIface, "-o", config.BridgeIface, "-j", "DROP"); err != nil {
187
+			return fmt.Errorf("Unable to prevent intercontainer communication: %s", err)
188
+		}
183 189
 	}
184 190
 	return nil
185 191
 }
... ...
@@ -216,58 +210,27 @@ func getIfaceAddr(name string) (net.Addr, error) {
216 216
 // It keeps track of all mappings and is able to unmap at will
217 217
 type PortMapper struct {
218 218
 	tcpMapping map[int]*net.TCPAddr
219
-	tcpProxies map[int]Proxy
219
+	tcpProxies map[int]proxy.Proxy
220 220
 	udpMapping map[int]*net.UDPAddr
221
-	udpProxies map[int]Proxy
222
-}
223
-
224
-func (mapper *PortMapper) cleanup() error {
225
-	// Ignore errors - This could mean the chains were never set up
226
-	iptables("-t", "nat", "-D", "PREROUTING", "-m", "addrtype", "--dst-type", "LOCAL", "-j", "DOCKER")
227
-	iptables("-t", "nat", "-D", "OUTPUT", "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8", "-j", "DOCKER")
228
-	iptables("-t", "nat", "-D", "OUTPUT", "-m", "addrtype", "--dst-type", "LOCAL", "-j", "DOCKER") // Created in versions <= 0.1.6
229
-	// Also cleanup rules created by older versions, or -X might fail.
230
-	iptables("-t", "nat", "-D", "PREROUTING", "-j", "DOCKER")
231
-	iptables("-t", "nat", "-D", "OUTPUT", "-j", "DOCKER")
232
-	iptables("-t", "nat", "-F", "DOCKER")
233
-	iptables("-t", "nat", "-X", "DOCKER")
234
-	mapper.tcpMapping = make(map[int]*net.TCPAddr)
235
-	mapper.tcpProxies = make(map[int]Proxy)
236
-	mapper.udpMapping = make(map[int]*net.UDPAddr)
237
-	mapper.udpProxies = make(map[int]Proxy)
238
-	return nil
239
-}
240
-
241
-func (mapper *PortMapper) setup() error {
242
-	if err := iptables("-t", "nat", "-N", "DOCKER"); err != nil {
243
-		return fmt.Errorf("Failed to create DOCKER chain: %s", err)
244
-	}
245
-	if err := iptables("-t", "nat", "-A", "PREROUTING", "-m", "addrtype", "--dst-type", "LOCAL", "-j", "DOCKER"); err != nil {
246
-		return fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
247
-	}
248
-	if err := iptables("-t", "nat", "-A", "OUTPUT", "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8", "-j", "DOCKER"); err != nil {
249
-		return fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
250
-	}
251
-	return nil
252
-}
221
+	udpProxies map[int]proxy.Proxy
253 222
 
254
-func (mapper *PortMapper) iptablesForward(rule string, port int, proto string, dest_addr string, dest_port int) error {
255
-	return iptables("-t", "nat", rule, "DOCKER", "-p", proto, "--dport", strconv.Itoa(port),
256
-		"!", "-i", NetworkBridgeIface,
257
-		"-j", "DNAT", "--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port)))
223
+	iptables  *iptables.Chain
224
+	defaultIp net.IP
258 225
 }
259 226
 
260
-func (mapper *PortMapper) Map(port int, backendAddr net.Addr) error {
227
+func (mapper *PortMapper) Map(ip net.IP, port int, backendAddr net.Addr) error {
261 228
 	if _, isTCP := backendAddr.(*net.TCPAddr); isTCP {
262 229
 		backendPort := backendAddr.(*net.TCPAddr).Port
263 230
 		backendIP := backendAddr.(*net.TCPAddr).IP
264
-		if err := mapper.iptablesForward("-A", port, "tcp", backendIP.String(), backendPort); err != nil {
265
-			return err
231
+		if mapper.iptables != nil {
232
+			if err := mapper.iptables.Forward(iptables.Add, ip, port, "tcp", backendIP.String(), backendPort); err != nil {
233
+				return err
234
+			}
266 235
 		}
267 236
 		mapper.tcpMapping[port] = backendAddr.(*net.TCPAddr)
268
-		proxy, err := NewProxy(&net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: port}, backendAddr)
237
+		proxy, err := proxy.NewProxy(&net.TCPAddr{IP: ip, Port: port}, backendAddr)
269 238
 		if err != nil {
270
-			mapper.Unmap(port, "tcp")
239
+			mapper.Unmap(ip, port, "tcp")
271 240
 			return err
272 241
 		}
273 242
 		mapper.tcpProxies[port] = proxy
... ...
@@ -275,13 +238,15 @@ func (mapper *PortMapper) Map(port int, backendAddr net.Addr) error {
275 275
 	} else {
276 276
 		backendPort := backendAddr.(*net.UDPAddr).Port
277 277
 		backendIP := backendAddr.(*net.UDPAddr).IP
278
-		if err := mapper.iptablesForward("-A", port, "udp", backendIP.String(), backendPort); err != nil {
279
-			return err
278
+		if mapper.iptables != nil {
279
+			if err := mapper.iptables.Forward(iptables.Add, ip, port, "udp", backendIP.String(), backendPort); err != nil {
280
+				return err
281
+			}
280 282
 		}
281 283
 		mapper.udpMapping[port] = backendAddr.(*net.UDPAddr)
282
-		proxy, err := NewProxy(&net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: port}, backendAddr)
284
+		proxy, err := proxy.NewProxy(&net.UDPAddr{IP: ip, Port: port}, backendAddr)
283 285
 		if err != nil {
284
-			mapper.Unmap(port, "udp")
286
+			mapper.Unmap(ip, port, "udp")
285 287
 			return err
286 288
 		}
287 289
 		mapper.udpProxies[port] = proxy
... ...
@@ -290,7 +255,7 @@ func (mapper *PortMapper) Map(port int, backendAddr net.Addr) error {
290 290
 	return nil
291 291
 }
292 292
 
293
-func (mapper *PortMapper) Unmap(port int, proto string) error {
293
+func (mapper *PortMapper) Unmap(ip net.IP, port int, proto string) error {
294 294
 	if proto == "tcp" {
295 295
 		backendAddr, ok := mapper.tcpMapping[port]
296 296
 		if !ok {
... ...
@@ -300,8 +265,10 @@ func (mapper *PortMapper) Unmap(port int, proto string) error {
300 300
 			proxy.Close()
301 301
 			delete(mapper.tcpProxies, port)
302 302
 		}
303
-		if err := mapper.iptablesForward("-D", port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
304
-			return err
303
+		if mapper.iptables != nil {
304
+			if err := mapper.iptables.Forward(iptables.Delete, ip, port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
305
+				return err
306
+			}
305 307
 		}
306 308
 		delete(mapper.tcpMapping, port)
307 309
 	} else {
... ...
@@ -313,21 +280,37 @@ func (mapper *PortMapper) Unmap(port int, proto string) error {
313 313
 			proxy.Close()
314 314
 			delete(mapper.udpProxies, port)
315 315
 		}
316
-		if err := mapper.iptablesForward("-D", port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
317
-			return err
316
+		if mapper.iptables != nil {
317
+			if err := mapper.iptables.Forward(iptables.Delete, ip, port, proto, backendAddr.IP.String(), backendAddr.Port); err != nil {
318
+				return err
319
+			}
318 320
 		}
319 321
 		delete(mapper.udpMapping, port)
320 322
 	}
321 323
 	return nil
322 324
 }
323 325
 
324
-func newPortMapper() (*PortMapper, error) {
325
-	mapper := &PortMapper{}
326
-	if err := mapper.cleanup(); err != nil {
326
+func newPortMapper(config *DaemonConfig) (*PortMapper, error) {
327
+	// We can always try removing the iptables
328
+	if err := iptables.RemoveExistingChain("DOCKER"); err != nil {
327 329
 		return nil, err
328 330
 	}
329
-	if err := mapper.setup(); err != nil {
330
-		return nil, err
331
+	var chain *iptables.Chain
332
+	if config.EnableIptables {
333
+		var err error
334
+		chain, err = iptables.NewChain("DOCKER", config.BridgeIface)
335
+		if err != nil {
336
+			return nil, fmt.Errorf("Failed to create DOCKER chain: %s", err)
337
+		}
338
+	}
339
+
340
+	mapper := &PortMapper{
341
+		tcpMapping: make(map[int]*net.TCPAddr),
342
+		tcpProxies: make(map[int]proxy.Proxy),
343
+		udpMapping: make(map[int]*net.UDPAddr),
344
+		udpProxies: make(map[int]proxy.Proxy),
345
+		iptables:   chain,
346
+		defaultIp:  config.DefaultIp,
331 347
 	}
332 348
 	return mapper, nil
333 349
 }
... ...
@@ -519,40 +502,56 @@ type NetworkInterface struct {
519 519
 	disabled bool
520 520
 }
521 521
 
522
-// Allocate an external TCP port and map it to the interface
523
-func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) {
522
+// Allocate an external port and map it to the interface
523
+func (iface *NetworkInterface) AllocatePort(port Port, binding PortBinding) (*Nat, error) {
524 524
 
525 525
 	if iface.disabled {
526 526
 		return nil, fmt.Errorf("Trying to allocate port for interface %v, which is disabled", iface) // FIXME
527 527
 	}
528 528
 
529
-	nat, err := parseNat(spec)
529
+	ip := iface.manager.portMapper.defaultIp
530
+
531
+	if binding.HostIp != "" {
532
+		ip = net.ParseIP(binding.HostIp)
533
+	} else {
534
+		binding.HostIp = ip.String()
535
+	}
536
+
537
+	nat := &Nat{
538
+		Port:    port,
539
+		Binding: binding,
540
+	}
541
+
542
+	containerPort, err := parsePort(port.Port())
530 543
 	if err != nil {
531 544
 		return nil, err
532 545
 	}
533 546
 
534
-	if nat.Proto == "tcp" {
535
-		extPort, err := iface.manager.tcpPortAllocator.Acquire(nat.Frontend)
547
+	hostPort, _ := parsePort(nat.Binding.HostPort)
548
+
549
+	if nat.Port.Proto() == "tcp" {
550
+		extPort, err := iface.manager.tcpPortAllocator.Acquire(hostPort)
536 551
 		if err != nil {
537 552
 			return nil, err
538 553
 		}
539
-		backend := &net.TCPAddr{IP: iface.IPNet.IP, Port: nat.Backend}
540
-		if err := iface.manager.portMapper.Map(extPort, backend); err != nil {
554
+
555
+		backend := &net.TCPAddr{IP: iface.IPNet.IP, Port: containerPort}
556
+		if err := iface.manager.portMapper.Map(ip, extPort, backend); err != nil {
541 557
 			iface.manager.tcpPortAllocator.Release(extPort)
542 558
 			return nil, err
543 559
 		}
544
-		nat.Frontend = extPort
560
+		nat.Binding.HostPort = strconv.Itoa(extPort)
545 561
 	} else {
546
-		extPort, err := iface.manager.udpPortAllocator.Acquire(nat.Frontend)
562
+		extPort, err := iface.manager.udpPortAllocator.Acquire(hostPort)
547 563
 		if err != nil {
548 564
 			return nil, err
549 565
 		}
550
-		backend := &net.UDPAddr{IP: iface.IPNet.IP, Port: nat.Backend}
551
-		if err := iface.manager.portMapper.Map(extPort, backend); err != nil {
566
+		backend := &net.UDPAddr{IP: iface.IPNet.IP, Port: containerPort}
567
+		if err := iface.manager.portMapper.Map(ip, extPort, backend); err != nil {
552 568
 			iface.manager.udpPortAllocator.Release(extPort)
553 569
 			return nil, err
554 570
 		}
555
-		nat.Frontend = extPort
571
+		nat.Binding.HostPort = strconv.Itoa(extPort)
556 572
 	}
557 573
 	iface.extPorts = append(iface.extPorts, nat)
558 574
 
... ...
@@ -560,83 +559,37 @@ func (iface *NetworkInterface) AllocatePort(spec string) (*Nat, error) {
560 560
 }
561 561
 
562 562
 type Nat struct {
563
-	Proto    string
564
-	Frontend int
565
-	Backend  int
563
+	Port    Port
564
+	Binding PortBinding
566 565
 }
567 566
 
568
-func parseNat(spec string) (*Nat, error) {
569
-	var nat Nat
570
-
571
-	if strings.Contains(spec, "/") {
572
-		specParts := strings.Split(spec, "/")
573
-		if len(specParts) != 2 {
574
-			return nil, fmt.Errorf("Invalid port format.")
575
-		}
576
-		proto := specParts[1]
577
-		spec = specParts[0]
578
-		if proto != "tcp" && proto != "udp" {
579
-			return nil, fmt.Errorf("Invalid port format: unknown protocol %v.", proto)
580
-		}
581
-		nat.Proto = proto
582
-	} else {
583
-		nat.Proto = "tcp"
584
-	}
585
-
586
-	if strings.Contains(spec, ":") {
587
-		specParts := strings.Split(spec, ":")
588
-		if len(specParts) != 2 {
589
-			return nil, fmt.Errorf("Invalid port format.")
590
-		}
591
-		// If spec starts with ':', external and internal ports must be the same.
592
-		// This might fail if the requested external port is not available.
593
-		var sameFrontend bool
594
-		if len(specParts[0]) == 0 {
595
-			sameFrontend = true
596
-		} else {
597
-			front, err := strconv.ParseUint(specParts[0], 10, 16)
598
-			if err != nil {
599
-				return nil, err
600
-			}
601
-			nat.Frontend = int(front)
602
-		}
603
-		back, err := strconv.ParseUint(specParts[1], 10, 16)
604
-		if err != nil {
605
-			return nil, err
606
-		}
607
-		nat.Backend = int(back)
608
-		if sameFrontend {
609
-			nat.Frontend = nat.Backend
610
-		}
611
-	} else {
612
-		port, err := strconv.ParseUint(spec, 10, 16)
613
-		if err != nil {
614
-			return nil, err
615
-		}
616
-		nat.Backend = int(port)
617
-	}
618
-
619
-	return &nat, nil
567
+func (n *Nat) String() string {
568
+	return fmt.Sprintf("%s:%d:%d/%s", n.Binding.HostIp, n.Binding.HostPort, n.Port.Port(), n.Port.Proto())
620 569
 }
621 570
 
622 571
 // Release: Network cleanup - release all resources
623 572
 func (iface *NetworkInterface) Release() {
624
-
625 573
 	if iface.disabled {
626 574
 		return
627 575
 	}
628 576
 
629 577
 	for _, nat := range iface.extPorts {
630
-		utils.Debugf("Unmaping %v/%v", nat.Proto, nat.Frontend)
631
-		if err := iface.manager.portMapper.Unmap(nat.Frontend, nat.Proto); err != nil {
632
-			log.Printf("Unable to unmap port %v/%v: %v", nat.Proto, nat.Frontend, err)
578
+		hostPort, err := parsePort(nat.Binding.HostPort)
579
+		if err != nil {
580
+			log.Printf("Unable to get host port: %s", err)
581
+			continue
582
+		}
583
+		ip := net.ParseIP(nat.Binding.HostIp)
584
+		utils.Debugf("Unmaping %s/%s", nat.Port.Proto, nat.Binding.HostPort)
585
+		if err := iface.manager.portMapper.Unmap(ip, hostPort, nat.Port.Proto()); err != nil {
586
+			log.Printf("Unable to unmap port %s: %s", nat, err)
633 587
 		}
634
-		if nat.Proto == "tcp" {
635
-			if err := iface.manager.tcpPortAllocator.Release(nat.Frontend); err != nil {
636
-				log.Printf("Unable to release port tcp/%v: %v", nat.Frontend, err)
588
+		if nat.Port.Proto() == "tcp" {
589
+			if err := iface.manager.tcpPortAllocator.Release(hostPort); err != nil {
590
+				log.Printf("Unable to release port %s", nat)
637 591
 			}
638
-		} else if err := iface.manager.udpPortAllocator.Release(nat.Frontend); err != nil {
639
-			log.Printf("Unable to release port udp/%v: %v", nat.Frontend, err)
592
+		} else if err := iface.manager.udpPortAllocator.Release(hostPort); err != nil {
593
+			log.Printf("Unable to release port %s: %s", nat, err)
640 594
 		}
641 595
 	}
642 596
 
... ...
@@ -704,22 +657,21 @@ func (manager *NetworkManager) Close() error {
704 704
 	return err3
705 705
 }
706 706
 
707
-func newNetworkManager(bridgeIface string) (*NetworkManager, error) {
708
-
709
-	if bridgeIface == DisableNetworkBridge {
707
+func newNetworkManager(config *DaemonConfig) (*NetworkManager, error) {
708
+	if config.BridgeIface == DisableNetworkBridge {
710 709
 		manager := &NetworkManager{
711 710
 			disabled: true,
712 711
 		}
713 712
 		return manager, nil
714 713
 	}
715 714
 
716
-	addr, err := getIfaceAddr(bridgeIface)
715
+	addr, err := getIfaceAddr(config.BridgeIface)
717 716
 	if err != nil {
718 717
 		// If the iface is not found, try to create it
719
-		if err := CreateBridgeIface(bridgeIface); err != nil {
718
+		if err := CreateBridgeIface(config); err != nil {
720 719
 			return nil, err
721 720
 		}
722
-		addr, err = getIfaceAddr(bridgeIface)
721
+		addr, err = getIfaceAddr(config.BridgeIface)
723 722
 		if err != nil {
724 723
 			return nil, err
725 724
 		}
... ...
@@ -737,13 +689,13 @@ func newNetworkManager(bridgeIface string) (*NetworkManager, error) {
737 737
 		return nil, err
738 738
 	}
739 739
 
740
-	portMapper, err := newPortMapper()
740
+	portMapper, err := newPortMapper(config)
741 741
 	if err != nil {
742 742
 		return nil, err
743 743
 	}
744 744
 
745 745
 	manager := &NetworkManager{
746
-		bridgeIface:      bridgeIface,
746
+		bridgeIface:      config.BridgeIface,
747 747
 		bridgeNetwork:    network,
748 748
 		ipAllocator:      ipAllocator,
749 749
 		tcpPortAllocator: tcpPortAllocator,
750 750
deleted file mode 100644
... ...
@@ -1,266 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"encoding/binary"
5
-	"fmt"
6
-	"github.com/dotcloud/docker/utils"
7
-	"io"
8
-	"log"
9
-	"net"
10
-	"sync"
11
-	"syscall"
12
-	"time"
13
-)
14
-
15
-const (
16
-	UDPConnTrackTimeout = 90 * time.Second
17
-	UDPBufSize          = 2048
18
-)
19
-
20
-type Proxy interface {
21
-	// Start forwarding traffic back and forth the front and back-end
22
-	// addresses.
23
-	Run()
24
-	// Stop forwarding traffic and close both ends of the Proxy.
25
-	Close()
26
-	// Return the address on which the proxy is listening.
27
-	FrontendAddr() net.Addr
28
-	// Return the proxied address.
29
-	BackendAddr() net.Addr
30
-}
31
-
32
-type TCPProxy struct {
33
-	listener     *net.TCPListener
34
-	frontendAddr *net.TCPAddr
35
-	backendAddr  *net.TCPAddr
36
-}
37
-
38
-func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
39
-	listener, err := net.ListenTCP("tcp", frontendAddr)
40
-	if err != nil {
41
-		return nil, err
42
-	}
43
-	// If the port in frontendAddr was 0 then ListenTCP will have a picked
44
-	// a port to listen on, hence the call to Addr to get that actual port:
45
-	return &TCPProxy{
46
-		listener:     listener,
47
-		frontendAddr: listener.Addr().(*net.TCPAddr),
48
-		backendAddr:  backendAddr,
49
-	}, nil
50
-}
51
-
52
-func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
53
-	backend, err := net.DialTCP("tcp", nil, proxy.backendAddr)
54
-	if err != nil {
55
-		log.Printf("Can't forward traffic to backend tcp/%v: %v\n", proxy.backendAddr, err.Error())
56
-		client.Close()
57
-		return
58
-	}
59
-
60
-	event := make(chan int64)
61
-	var broker = func(to, from *net.TCPConn) {
62
-		written, err := io.Copy(to, from)
63
-		if err != nil {
64
-			// If the socket we are writing to is shutdown with
65
-			// SHUT_WR, forward it to the other end of the pipe:
66
-			if err, ok := err.(*net.OpError); ok && err.Err == syscall.EPIPE {
67
-				from.CloseWrite()
68
-			}
69
-		}
70
-		to.CloseRead()
71
-		event <- written
72
-	}
73
-	utils.Debugf("Forwarding traffic between tcp/%v and tcp/%v", client.RemoteAddr(), backend.RemoteAddr())
74
-	go broker(client, backend)
75
-	go broker(backend, client)
76
-
77
-	var transferred int64 = 0
78
-	for i := 0; i < 2; i++ {
79
-		select {
80
-		case written := <-event:
81
-			transferred += written
82
-		case <-quit:
83
-			// Interrupt the two brokers and "join" them.
84
-			client.Close()
85
-			backend.Close()
86
-			for ; i < 2; i++ {
87
-				transferred += <-event
88
-			}
89
-			goto done
90
-		}
91
-	}
92
-	client.Close()
93
-	backend.Close()
94
-done:
95
-	utils.Debugf("%v bytes transferred between tcp/%v and tcp/%v", transferred, client.RemoteAddr(), backend.RemoteAddr())
96
-}
97
-
98
-func (proxy *TCPProxy) Run() {
99
-	quit := make(chan bool)
100
-	defer close(quit)
101
-
102
-	utils.Debugf("Starting proxy on tcp/%v for tcp/%v", proxy.frontendAddr, proxy.backendAddr)
103
-	for {
104
-		client, err := proxy.listener.Accept()
105
-		if err != nil {
106
-			if utils.IsClosedError(err) {
107
-				utils.Debugf("Stopping proxy on tcp/%v for tcp/%v (socket was closed)", proxy.frontendAddr, proxy.backendAddr)
108
-			} else {
109
-				utils.Errorf("Stopping proxy on tcp/%v for tcp/%v (%v)", proxy.frontendAddr, proxy.backendAddr, err.Error())
110
-			}
111
-			return
112
-		}
113
-		go proxy.clientLoop(client.(*net.TCPConn), quit)
114
-	}
115
-}
116
-
117
-func (proxy *TCPProxy) Close()                 { proxy.listener.Close() }
118
-func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
119
-func (proxy *TCPProxy) BackendAddr() net.Addr  { return proxy.backendAddr }
120
-
121
-// A net.Addr where the IP is split into two fields so you can use it as a key
122
-// in a map:
123
-type connTrackKey struct {
124
-	IPHigh uint64
125
-	IPLow  uint64
126
-	Port   int
127
-}
128
-
129
-func newConnTrackKey(addr *net.UDPAddr) *connTrackKey {
130
-	if len(addr.IP) == net.IPv4len {
131
-		return &connTrackKey{
132
-			IPHigh: 0,
133
-			IPLow:  uint64(binary.BigEndian.Uint32(addr.IP)),
134
-			Port:   addr.Port,
135
-		}
136
-	}
137
-	return &connTrackKey{
138
-		IPHigh: binary.BigEndian.Uint64(addr.IP[:8]),
139
-		IPLow:  binary.BigEndian.Uint64(addr.IP[8:]),
140
-		Port:   addr.Port,
141
-	}
142
-}
143
-
144
-type connTrackMap map[connTrackKey]*net.UDPConn
145
-
146
-type UDPProxy struct {
147
-	listener       *net.UDPConn
148
-	frontendAddr   *net.UDPAddr
149
-	backendAddr    *net.UDPAddr
150
-	connTrackTable connTrackMap
151
-	connTrackLock  sync.Mutex
152
-}
153
-
154
-func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
155
-	listener, err := net.ListenUDP("udp", frontendAddr)
156
-	if err != nil {
157
-		return nil, err
158
-	}
159
-	return &UDPProxy{
160
-		listener:       listener,
161
-		frontendAddr:   listener.LocalAddr().(*net.UDPAddr),
162
-		backendAddr:    backendAddr,
163
-		connTrackTable: make(connTrackMap),
164
-	}, nil
165
-}
166
-
167
-func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) {
168
-	defer func() {
169
-		proxy.connTrackLock.Lock()
170
-		delete(proxy.connTrackTable, *clientKey)
171
-		proxy.connTrackLock.Unlock()
172
-		utils.Debugf("Done proxying between udp/%v and udp/%v", clientAddr.String(), proxy.backendAddr.String())
173
-		proxyConn.Close()
174
-	}()
175
-
176
-	readBuf := make([]byte, UDPBufSize)
177
-	for {
178
-		proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout))
179
-	again:
180
-		read, err := proxyConn.Read(readBuf)
181
-		if err != nil {
182
-			if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED {
183
-				// This will happen if the last write failed
184
-				// (e.g: nothing is actually listening on the
185
-				// proxied port on the container), ignore it
186
-				// and continue until UDPConnTrackTimeout
187
-				// expires:
188
-				goto again
189
-			}
190
-			return
191
-		}
192
-		for i := 0; i != read; {
193
-			written, err := proxy.listener.WriteToUDP(readBuf[i:read], clientAddr)
194
-			if err != nil {
195
-				return
196
-			}
197
-			i += written
198
-			utils.Debugf("Forwarded %v/%v bytes to udp/%v", i, read, clientAddr.String())
199
-		}
200
-	}
201
-}
202
-
203
-func (proxy *UDPProxy) Run() {
204
-	readBuf := make([]byte, UDPBufSize)
205
-	utils.Debugf("Starting proxy on udp/%v for udp/%v", proxy.frontendAddr, proxy.backendAddr)
206
-	for {
207
-		read, from, err := proxy.listener.ReadFromUDP(readBuf)
208
-		if err != nil {
209
-			// NOTE: Apparently ReadFrom doesn't return
210
-			// ECONNREFUSED like Read do (see comment in
211
-			// UDPProxy.replyLoop)
212
-			if utils.IsClosedError(err) {
213
-				utils.Debugf("Stopping proxy on udp/%v for udp/%v (socket was closed)", proxy.frontendAddr, proxy.backendAddr)
214
-			} else {
215
-				utils.Errorf("Stopping proxy on udp/%v for udp/%v (%v)", proxy.frontendAddr, proxy.backendAddr, err.Error())
216
-			}
217
-			break
218
-		}
219
-
220
-		fromKey := newConnTrackKey(from)
221
-		proxy.connTrackLock.Lock()
222
-		proxyConn, hit := proxy.connTrackTable[*fromKey]
223
-		if !hit {
224
-			proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr)
225
-			if err != nil {
226
-				log.Printf("Can't proxy a datagram to udp/%s: %v\n", proxy.backendAddr.String(), err)
227
-				continue
228
-			}
229
-			proxy.connTrackTable[*fromKey] = proxyConn
230
-			go proxy.replyLoop(proxyConn, from, fromKey)
231
-		}
232
-		proxy.connTrackLock.Unlock()
233
-		for i := 0; i != read; {
234
-			written, err := proxyConn.Write(readBuf[i:read])
235
-			if err != nil {
236
-				log.Printf("Can't proxy a datagram to udp/%s: %v\n", proxy.backendAddr.String(), err)
237
-				break
238
-			}
239
-			i += written
240
-			utils.Debugf("Forwarded %v/%v bytes to udp/%v", i, read, proxy.backendAddr.String())
241
-		}
242
-	}
243
-}
244
-
245
-func (proxy *UDPProxy) Close() {
246
-	proxy.listener.Close()
247
-	proxy.connTrackLock.Lock()
248
-	defer proxy.connTrackLock.Unlock()
249
-	for _, conn := range proxy.connTrackTable {
250
-		conn.Close()
251
-	}
252
-}
253
-
254
-func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
255
-func (proxy *UDPProxy) BackendAddr() net.Addr  { return proxy.backendAddr }
256
-
257
-func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
258
-	switch frontendAddr.(type) {
259
-	case *net.UDPAddr:
260
-		return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr))
261
-	case *net.TCPAddr:
262
-		return NewTCPProxy(frontendAddr.(*net.TCPAddr), backendAddr.(*net.TCPAddr))
263
-	default:
264
-		panic(fmt.Errorf("Unsupported protocol"))
265
-	}
266
-}
267 1
deleted file mode 100644
... ...
@@ -1,216 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"bytes"
5
-	"fmt"
6
-	"io"
7
-	"net"
8
-	"strings"
9
-	"testing"
10
-	"time"
11
-)
12
-
13
-var testBuf = []byte("Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo")
14
-var testBufSize = len(testBuf)
15
-
16
-type EchoServer interface {
17
-	Run()
18
-	Close()
19
-	LocalAddr() net.Addr
20
-}
21
-
22
-type TCPEchoServer struct {
23
-	listener net.Listener
24
-	testCtx  *testing.T
25
-}
26
-
27
-type UDPEchoServer struct {
28
-	conn    net.PacketConn
29
-	testCtx *testing.T
30
-}
31
-
32
-func NewEchoServer(t *testing.T, proto, address string) EchoServer {
33
-	var server EchoServer
34
-	if strings.HasPrefix(proto, "tcp") {
35
-		listener, err := net.Listen(proto, address)
36
-		if err != nil {
37
-			t.Fatal(err)
38
-		}
39
-		server = &TCPEchoServer{listener: listener, testCtx: t}
40
-	} else {
41
-		socket, err := net.ListenPacket(proto, address)
42
-		if err != nil {
43
-			t.Fatal(err)
44
-		}
45
-		server = &UDPEchoServer{conn: socket, testCtx: t}
46
-	}
47
-	return server
48
-}
49
-
50
-func (server *TCPEchoServer) Run() {
51
-	go func() {
52
-		for {
53
-			client, err := server.listener.Accept()
54
-			if err != nil {
55
-				return
56
-			}
57
-			go func(client net.Conn) {
58
-				if _, err := io.Copy(client, client); err != nil {
59
-					server.testCtx.Logf("can't echo to the client: %v\n", err.Error())
60
-				}
61
-				client.Close()
62
-			}(client)
63
-		}
64
-	}()
65
-}
66
-
67
-func (server *TCPEchoServer) LocalAddr() net.Addr { return server.listener.Addr() }
68
-func (server *TCPEchoServer) Close()              { server.listener.Addr() }
69
-
70
-func (server *UDPEchoServer) Run() {
71
-	go func() {
72
-		readBuf := make([]byte, 1024)
73
-		for {
74
-			read, from, err := server.conn.ReadFrom(readBuf)
75
-			if err != nil {
76
-				return
77
-			}
78
-			for i := 0; i != read; {
79
-				written, err := server.conn.WriteTo(readBuf[i:read], from)
80
-				if err != nil {
81
-					break
82
-				}
83
-				i += written
84
-			}
85
-		}
86
-	}()
87
-}
88
-
89
-func (server *UDPEchoServer) LocalAddr() net.Addr { return server.conn.LocalAddr() }
90
-func (server *UDPEchoServer) Close()              { server.conn.Close() }
91
-
92
-func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string) {
93
-	defer proxy.Close()
94
-	go proxy.Run()
95
-	client, err := net.Dial(proto, addr)
96
-	if err != nil {
97
-		t.Fatalf("Can't connect to the proxy: %v", err)
98
-	}
99
-	defer client.Close()
100
-	client.SetDeadline(time.Now().Add(10 * time.Second))
101
-	if _, err = client.Write(testBuf); err != nil {
102
-		t.Fatal(err)
103
-	}
104
-	recvBuf := make([]byte, testBufSize)
105
-	if _, err = client.Read(recvBuf); err != nil {
106
-		t.Fatal(err)
107
-	}
108
-	if !bytes.Equal(testBuf, recvBuf) {
109
-		t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf))
110
-	}
111
-}
112
-
113
-func testProxy(t *testing.T, proto string, proxy Proxy) {
114
-	testProxyAt(t, proto, proxy, proxy.FrontendAddr().String())
115
-}
116
-
117
-func TestTCP4Proxy(t *testing.T) {
118
-	backend := NewEchoServer(t, "tcp", "127.0.0.1:0")
119
-	defer backend.Close()
120
-	backend.Run()
121
-	frontendAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
122
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
123
-	if err != nil {
124
-		t.Fatal(err)
125
-	}
126
-	testProxy(t, "tcp", proxy)
127
-}
128
-
129
-func TestTCP6Proxy(t *testing.T) {
130
-	backend := NewEchoServer(t, "tcp", "[::1]:0")
131
-	defer backend.Close()
132
-	backend.Run()
133
-	frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
134
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
135
-	if err != nil {
136
-		t.Fatal(err)
137
-	}
138
-	testProxy(t, "tcp", proxy)
139
-}
140
-
141
-func TestTCPDualStackProxy(t *testing.T) {
142
-	// If I understand `godoc -src net favoriteAddrFamily` (used by the
143
-	// net.Listen* functions) correctly this should work, but it doesn't.
144
-	t.Skip("No support for dual stack yet")
145
-	backend := NewEchoServer(t, "tcp", "[::1]:0")
146
-	defer backend.Close()
147
-	backend.Run()
148
-	frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
149
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
150
-	if err != nil {
151
-		t.Fatal(err)
152
-	}
153
-	ipv4ProxyAddr := &net.TCPAddr{
154
-		IP:   net.IPv4(127, 0, 0, 1),
155
-		Port: proxy.FrontendAddr().(*net.TCPAddr).Port,
156
-	}
157
-	testProxyAt(t, "tcp", proxy, ipv4ProxyAddr.String())
158
-}
159
-
160
-func TestUDP4Proxy(t *testing.T) {
161
-	backend := NewEchoServer(t, "udp", "127.0.0.1:0")
162
-	defer backend.Close()
163
-	backend.Run()
164
-	frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
165
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
166
-	if err != nil {
167
-		t.Fatal(err)
168
-	}
169
-	testProxy(t, "udp", proxy)
170
-}
171
-
172
-func TestUDP6Proxy(t *testing.T) {
173
-	backend := NewEchoServer(t, "udp", "[::1]:0")
174
-	defer backend.Close()
175
-	backend.Run()
176
-	frontendAddr := &net.UDPAddr{IP: net.IPv6loopback, Port: 0}
177
-	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
178
-	if err != nil {
179
-		t.Fatal(err)
180
-	}
181
-	testProxy(t, "udp", proxy)
182
-}
183
-
184
-func TestUDPWriteError(t *testing.T) {
185
-	frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
186
-	// Hopefully, this port will be free: */
187
-	backendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 25587}
188
-	proxy, err := NewProxy(frontendAddr, backendAddr)
189
-	if err != nil {
190
-		t.Fatal(err)
191
-	}
192
-	defer proxy.Close()
193
-	go proxy.Run()
194
-	client, err := net.Dial("udp", "127.0.0.1:25587")
195
-	if err != nil {
196
-		t.Fatalf("Can't connect to the proxy: %v", err)
197
-	}
198
-	defer client.Close()
199
-	// Make sure the proxy doesn't stop when there is no actual backend:
200
-	client.Write(testBuf)
201
-	client.Write(testBuf)
202
-	backend := NewEchoServer(t, "udp", "127.0.0.1:25587")
203
-	defer backend.Close()
204
-	backend.Run()
205
-	client.SetDeadline(time.Now().Add(10 * time.Second))
206
-	if _, err = client.Write(testBuf); err != nil {
207
-		t.Fatal(err)
208
-	}
209
-	recvBuf := make([]byte, testBufSize)
210
-	if _, err = client.Read(recvBuf); err != nil {
211
-		t.Fatal(err)
212
-	}
213
-	if !bytes.Equal(testBuf, recvBuf) {
214
-		t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf))
215
-	}
216
-}
... ...
@@ -2,117 +2,9 @@ package docker
2 2
 
3 3
 import (
4 4
 	"net"
5
-	"os"
6 5
 	"testing"
7 6
 )
8 7
 
9
-func TestIptables(t *testing.T) {
10
-	if err := iptables("-L"); err != nil {
11
-		t.Fatal(err)
12
-	}
13
-	path := os.Getenv("PATH")
14
-	os.Setenv("PATH", "")
15
-	defer os.Setenv("PATH", path)
16
-	if err := iptables("-L"); err == nil {
17
-		t.Fatal("Not finding iptables in the PATH should cause an error")
18
-	}
19
-}
20
-
21
-func TestParseNat(t *testing.T) {
22
-	if nat, err := parseNat("4500"); err == nil {
23
-		if nat.Frontend != 0 || nat.Backend != 4500 || nat.Proto != "tcp" {
24
-			t.Errorf("-p 4500 should produce 0->4500/tcp, got %d->%d/%s",
25
-				nat.Frontend, nat.Backend, nat.Proto)
26
-		}
27
-	} else {
28
-		t.Fatal(err)
29
-	}
30
-
31
-	if nat, err := parseNat(":4501"); err == nil {
32
-		if nat.Frontend != 4501 || nat.Backend != 4501 || nat.Proto != "tcp" {
33
-			t.Errorf("-p :4501 should produce 4501->4501/tcp, got %d->%d/%s",
34
-				nat.Frontend, nat.Backend, nat.Proto)
35
-		}
36
-	} else {
37
-		t.Fatal(err)
38
-	}
39
-
40
-	if nat, err := parseNat("4502:4503"); err == nil {
41
-		if nat.Frontend != 4502 || nat.Backend != 4503 || nat.Proto != "tcp" {
42
-			t.Errorf("-p 4502:4503 should produce 4502->4503/tcp, got %d->%d/%s",
43
-				nat.Frontend, nat.Backend, nat.Proto)
44
-		}
45
-	} else {
46
-		t.Fatal(err)
47
-	}
48
-
49
-	if nat, err := parseNat("4502:4503/tcp"); err == nil {
50
-		if nat.Frontend != 4502 || nat.Backend != 4503 || nat.Proto != "tcp" {
51
-			t.Errorf("-p 4502:4503/tcp should produce 4502->4503/tcp, got %d->%d/%s",
52
-				nat.Frontend, nat.Backend, nat.Proto)
53
-		}
54
-	} else {
55
-		t.Fatal(err)
56
-	}
57
-
58
-	if nat, err := parseNat("4502:4503/udp"); err == nil {
59
-		if nat.Frontend != 4502 || nat.Backend != 4503 || nat.Proto != "udp" {
60
-			t.Errorf("-p 4502:4503/udp should produce 4502->4503/udp, got %d->%d/%s",
61
-				nat.Frontend, nat.Backend, nat.Proto)
62
-		}
63
-	} else {
64
-		t.Fatal(err)
65
-	}
66
-
67
-	if nat, err := parseNat(":4503/udp"); err == nil {
68
-		if nat.Frontend != 4503 || nat.Backend != 4503 || nat.Proto != "udp" {
69
-			t.Errorf("-p :4503/udp should produce 4503->4503/udp, got %d->%d/%s",
70
-				nat.Frontend, nat.Backend, nat.Proto)
71
-		}
72
-	} else {
73
-		t.Fatal(err)
74
-	}
75
-
76
-	if nat, err := parseNat(":4503/tcp"); err == nil {
77
-		if nat.Frontend != 4503 || nat.Backend != 4503 || nat.Proto != "tcp" {
78
-			t.Errorf("-p :4503/tcp should produce 4503->4503/tcp, got %d->%d/%s",
79
-				nat.Frontend, nat.Backend, nat.Proto)
80
-		}
81
-	} else {
82
-		t.Fatal(err)
83
-	}
84
-
85
-	if nat, err := parseNat("4503/tcp"); err == nil {
86
-		if nat.Frontend != 0 || nat.Backend != 4503 || nat.Proto != "tcp" {
87
-			t.Errorf("-p 4503/tcp should produce 0->4503/tcp, got %d->%d/%s",
88
-				nat.Frontend, nat.Backend, nat.Proto)
89
-		}
90
-	} else {
91
-		t.Fatal(err)
92
-	}
93
-
94
-	if nat, err := parseNat("4503/udp"); err == nil {
95
-		if nat.Frontend != 0 || nat.Backend != 4503 || nat.Proto != "udp" {
96
-			t.Errorf("-p 4503/udp should produce 0->4503/udp, got %d->%d/%s",
97
-				nat.Frontend, nat.Backend, nat.Proto)
98
-		}
99
-	} else {
100
-		t.Fatal(err)
101
-	}
102
-
103
-	if _, err := parseNat("4503/tcpgarbage"); err == nil {
104
-		t.Fatal(err)
105
-	}
106
-
107
-	if _, err := parseNat("4503/tcp/udp"); err == nil {
108
-		t.Fatal(err)
109
-	}
110
-
111
-	if _, err := parseNat("4503/"); err == nil {
112
-		t.Fatal(err)
113
-	}
114
-}
115
-
116 8
 func TestPortAllocation(t *testing.T) {
117 9
 	allocator, err := newPortAllocator()
118 10
 	if err != nil {
119 11
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
0 1
new file mode 100644
... ...
@@ -0,0 +1,216 @@
0
+package proxy
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"io"
6
+	"net"
7
+	"strings"
8
+	"testing"
9
+	"time"
10
+)
11
+
12
+var testBuf = []byte("Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo")
13
+var testBufSize = len(testBuf)
14
+
15
+type EchoServer interface {
16
+	Run()
17
+	Close()
18
+	LocalAddr() net.Addr
19
+}
20
+
21
+type TCPEchoServer struct {
22
+	listener net.Listener
23
+	testCtx  *testing.T
24
+}
25
+
26
+type UDPEchoServer struct {
27
+	conn    net.PacketConn
28
+	testCtx *testing.T
29
+}
30
+
31
+func NewEchoServer(t *testing.T, proto, address string) EchoServer {
32
+	var server EchoServer
33
+	if strings.HasPrefix(proto, "tcp") {
34
+		listener, err := net.Listen(proto, address)
35
+		if err != nil {
36
+			t.Fatal(err)
37
+		}
38
+		server = &TCPEchoServer{listener: listener, testCtx: t}
39
+	} else {
40
+		socket, err := net.ListenPacket(proto, address)
41
+		if err != nil {
42
+			t.Fatal(err)
43
+		}
44
+		server = &UDPEchoServer{conn: socket, testCtx: t}
45
+	}
46
+	return server
47
+}
48
+
49
+func (server *TCPEchoServer) Run() {
50
+	go func() {
51
+		for {
52
+			client, err := server.listener.Accept()
53
+			if err != nil {
54
+				return
55
+			}
56
+			go func(client net.Conn) {
57
+				if _, err := io.Copy(client, client); err != nil {
58
+					server.testCtx.Logf("can't echo to the client: %v\n", err.Error())
59
+				}
60
+				client.Close()
61
+			}(client)
62
+		}
63
+	}()
64
+}
65
+
66
+func (server *TCPEchoServer) LocalAddr() net.Addr { return server.listener.Addr() }
67
+func (server *TCPEchoServer) Close()              { server.listener.Addr() }
68
+
69
+func (server *UDPEchoServer) Run() {
70
+	go func() {
71
+		readBuf := make([]byte, 1024)
72
+		for {
73
+			read, from, err := server.conn.ReadFrom(readBuf)
74
+			if err != nil {
75
+				return
76
+			}
77
+			for i := 0; i != read; {
78
+				written, err := server.conn.WriteTo(readBuf[i:read], from)
79
+				if err != nil {
80
+					break
81
+				}
82
+				i += written
83
+			}
84
+		}
85
+	}()
86
+}
87
+
88
+func (server *UDPEchoServer) LocalAddr() net.Addr { return server.conn.LocalAddr() }
89
+func (server *UDPEchoServer) Close()              { server.conn.Close() }
90
+
91
+func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string) {
92
+	defer proxy.Close()
93
+	go proxy.Run()
94
+	client, err := net.Dial(proto, addr)
95
+	if err != nil {
96
+		t.Fatalf("Can't connect to the proxy: %v", err)
97
+	}
98
+	defer client.Close()
99
+	client.SetDeadline(time.Now().Add(10 * time.Second))
100
+	if _, err = client.Write(testBuf); err != nil {
101
+		t.Fatal(err)
102
+	}
103
+	recvBuf := make([]byte, testBufSize)
104
+	if _, err = client.Read(recvBuf); err != nil {
105
+		t.Fatal(err)
106
+	}
107
+	if !bytes.Equal(testBuf, recvBuf) {
108
+		t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf))
109
+	}
110
+}
111
+
112
+func testProxy(t *testing.T, proto string, proxy Proxy) {
113
+	testProxyAt(t, proto, proxy, proxy.FrontendAddr().String())
114
+}
115
+
116
+func TestTCP4Proxy(t *testing.T) {
117
+	backend := NewEchoServer(t, "tcp", "127.0.0.1:0")
118
+	defer backend.Close()
119
+	backend.Run()
120
+	frontendAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
121
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
122
+	if err != nil {
123
+		t.Fatal(err)
124
+	}
125
+	testProxy(t, "tcp", proxy)
126
+}
127
+
128
+func TestTCP6Proxy(t *testing.T) {
129
+	backend := NewEchoServer(t, "tcp", "[::1]:0")
130
+	defer backend.Close()
131
+	backend.Run()
132
+	frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
133
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
134
+	if err != nil {
135
+		t.Fatal(err)
136
+	}
137
+	testProxy(t, "tcp", proxy)
138
+}
139
+
140
+func TestTCPDualStackProxy(t *testing.T) {
141
+	// If I understand `godoc -src net favoriteAddrFamily` (used by the
142
+	// net.Listen* functions) correctly this should work, but it doesn't.
143
+	t.Skip("No support for dual stack yet")
144
+	backend := NewEchoServer(t, "tcp", "[::1]:0")
145
+	defer backend.Close()
146
+	backend.Run()
147
+	frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0}
148
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
149
+	if err != nil {
150
+		t.Fatal(err)
151
+	}
152
+	ipv4ProxyAddr := &net.TCPAddr{
153
+		IP:   net.IPv4(127, 0, 0, 1),
154
+		Port: proxy.FrontendAddr().(*net.TCPAddr).Port,
155
+	}
156
+	testProxyAt(t, "tcp", proxy, ipv4ProxyAddr.String())
157
+}
158
+
159
+func TestUDP4Proxy(t *testing.T) {
160
+	backend := NewEchoServer(t, "udp", "127.0.0.1:0")
161
+	defer backend.Close()
162
+	backend.Run()
163
+	frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
164
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
165
+	if err != nil {
166
+		t.Fatal(err)
167
+	}
168
+	testProxy(t, "udp", proxy)
169
+}
170
+
171
+func TestUDP6Proxy(t *testing.T) {
172
+	backend := NewEchoServer(t, "udp", "[::1]:0")
173
+	defer backend.Close()
174
+	backend.Run()
175
+	frontendAddr := &net.UDPAddr{IP: net.IPv6loopback, Port: 0}
176
+	proxy, err := NewProxy(frontendAddr, backend.LocalAddr())
177
+	if err != nil {
178
+		t.Fatal(err)
179
+	}
180
+	testProxy(t, "udp", proxy)
181
+}
182
+
183
+func TestUDPWriteError(t *testing.T) {
184
+	frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}
185
+	// Hopefully, this port will be free: */
186
+	backendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 25587}
187
+	proxy, err := NewProxy(frontendAddr, backendAddr)
188
+	if err != nil {
189
+		t.Fatal(err)
190
+	}
191
+	defer proxy.Close()
192
+	go proxy.Run()
193
+	client, err := net.Dial("udp", "127.0.0.1:25587")
194
+	if err != nil {
195
+		t.Fatalf("Can't connect to the proxy: %v", err)
196
+	}
197
+	defer client.Close()
198
+	// Make sure the proxy doesn't stop when there is no actual backend:
199
+	client.Write(testBuf)
200
+	client.Write(testBuf)
201
+	backend := NewEchoServer(t, "udp", "127.0.0.1:25587")
202
+	defer backend.Close()
203
+	backend.Run()
204
+	client.SetDeadline(time.Now().Add(10 * time.Second))
205
+	if _, err = client.Write(testBuf); err != nil {
206
+		t.Fatal(err)
207
+	}
208
+	recvBuf := make([]byte, testBufSize)
209
+	if _, err = client.Read(recvBuf); err != nil {
210
+		t.Fatal(err)
211
+	}
212
+	if !bytes.Equal(testBuf, recvBuf) {
213
+		t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf))
214
+	}
215
+}
0 216
new file mode 100644
... ...
@@ -0,0 +1,29 @@
0
+package proxy
1
+
2
+import (
3
+	"fmt"
4
+	"net"
5
+)
6
+
7
+type Proxy interface {
8
+	// Start forwarding traffic back and forth the front and back-end
9
+	// addresses.
10
+	Run()
11
+	// Stop forwarding traffic and close both ends of the Proxy.
12
+	Close()
13
+	// Return the address on which the proxy is listening.
14
+	FrontendAddr() net.Addr
15
+	// Return the proxied address.
16
+	BackendAddr() net.Addr
17
+}
18
+
19
+func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) {
20
+	switch frontendAddr.(type) {
21
+	case *net.UDPAddr:
22
+		return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr))
23
+	case *net.TCPAddr:
24
+		return NewTCPProxy(frontendAddr.(*net.TCPAddr), backendAddr.(*net.TCPAddr))
25
+	default:
26
+		panic(fmt.Errorf("Unsupported protocol"))
27
+	}
28
+}
0 29
new file mode 100644
... ...
@@ -0,0 +1,93 @@
0
+package proxy
1
+
2
+import (
3
+	"github.com/dotcloud/docker/utils"
4
+	"io"
5
+	"log"
6
+	"net"
7
+	"syscall"
8
+)
9
+
10
+type TCPProxy struct {
11
+	listener     *net.TCPListener
12
+	frontendAddr *net.TCPAddr
13
+	backendAddr  *net.TCPAddr
14
+}
15
+
16
+func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr) (*TCPProxy, error) {
17
+	listener, err := net.ListenTCP("tcp", frontendAddr)
18
+	if err != nil {
19
+		return nil, err
20
+	}
21
+	// If the port in frontendAddr was 0 then ListenTCP will have a picked
22
+	// a port to listen on, hence the call to Addr to get that actual port:
23
+	return &TCPProxy{
24
+		listener:     listener,
25
+		frontendAddr: listener.Addr().(*net.TCPAddr),
26
+		backendAddr:  backendAddr,
27
+	}, nil
28
+}
29
+
30
+func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) {
31
+	backend, err := net.DialTCP("tcp", nil, proxy.backendAddr)
32
+	if err != nil {
33
+		log.Printf("Can't forward traffic to backend tcp/%v: %v\n", proxy.backendAddr, err.Error())
34
+		client.Close()
35
+		return
36
+	}
37
+
38
+	event := make(chan int64)
39
+	var broker = func(to, from *net.TCPConn) {
40
+		written, err := io.Copy(to, from)
41
+		if err != nil {
42
+			// If the socket we are writing to is shutdown with
43
+			// SHUT_WR, forward it to the other end of the pipe:
44
+			if err, ok := err.(*net.OpError); ok && err.Err == syscall.EPIPE {
45
+				from.CloseWrite()
46
+			}
47
+		}
48
+		to.CloseRead()
49
+		event <- written
50
+	}
51
+	utils.Debugf("Forwarding traffic between tcp/%v and tcp/%v", client.RemoteAddr(), backend.RemoteAddr())
52
+	go broker(client, backend)
53
+	go broker(backend, client)
54
+
55
+	var transferred int64 = 0
56
+	for i := 0; i < 2; i++ {
57
+		select {
58
+		case written := <-event:
59
+			transferred += written
60
+		case <-quit:
61
+			// Interrupt the two brokers and "join" them.
62
+			client.Close()
63
+			backend.Close()
64
+			for ; i < 2; i++ {
65
+				transferred += <-event
66
+			}
67
+			goto done
68
+		}
69
+	}
70
+	client.Close()
71
+	backend.Close()
72
+done:
73
+	utils.Debugf("%v bytes transferred between tcp/%v and tcp/%v", transferred, client.RemoteAddr(), backend.RemoteAddr())
74
+}
75
+
76
+func (proxy *TCPProxy) Run() {
77
+	quit := make(chan bool)
78
+	defer close(quit)
79
+	utils.Debugf("Starting proxy on tcp/%v for tcp/%v", proxy.frontendAddr, proxy.backendAddr)
80
+	for {
81
+		client, err := proxy.listener.Accept()
82
+		if err != nil {
83
+			utils.Debugf("Stopping proxy on tcp/%v for tcp/%v (%v)", proxy.frontendAddr, proxy.backendAddr, err.Error())
84
+			return
85
+		}
86
+		go proxy.clientLoop(client.(*net.TCPConn), quit)
87
+	}
88
+}
89
+
90
+func (proxy *TCPProxy) Close()                 { proxy.listener.Close() }
91
+func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
92
+func (proxy *TCPProxy) BackendAddr() net.Addr  { return proxy.backendAddr }
0 93
new file mode 100644
... ...
@@ -0,0 +1,152 @@
0
+package proxy
1
+
2
+import (
3
+	"encoding/binary"
4
+	"github.com/dotcloud/docker/utils"
5
+	"log"
6
+	"net"
7
+	"sync"
8
+	"syscall"
9
+	"time"
10
+)
11
+
12
+const (
13
+	UDPConnTrackTimeout = 90 * time.Second
14
+	UDPBufSize          = 2048
15
+)
16
+
17
+// A net.Addr where the IP is split into two fields so you can use it as a key
18
+// in a map:
19
+type connTrackKey struct {
20
+	IPHigh uint64
21
+	IPLow  uint64
22
+	Port   int
23
+}
24
+
25
+func newConnTrackKey(addr *net.UDPAddr) *connTrackKey {
26
+	if len(addr.IP) == net.IPv4len {
27
+		return &connTrackKey{
28
+			IPHigh: 0,
29
+			IPLow:  uint64(binary.BigEndian.Uint32(addr.IP)),
30
+			Port:   addr.Port,
31
+		}
32
+	}
33
+	return &connTrackKey{
34
+		IPHigh: binary.BigEndian.Uint64(addr.IP[:8]),
35
+		IPLow:  binary.BigEndian.Uint64(addr.IP[8:]),
36
+		Port:   addr.Port,
37
+	}
38
+}
39
+
40
+type connTrackMap map[connTrackKey]*net.UDPConn
41
+
42
+type UDPProxy struct {
43
+	listener       *net.UDPConn
44
+	frontendAddr   *net.UDPAddr
45
+	backendAddr    *net.UDPAddr
46
+	connTrackTable connTrackMap
47
+	connTrackLock  sync.Mutex
48
+}
49
+
50
+func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr) (*UDPProxy, error) {
51
+	listener, err := net.ListenUDP("udp", frontendAddr)
52
+	if err != nil {
53
+		return nil, err
54
+	}
55
+	return &UDPProxy{
56
+		listener:       listener,
57
+		frontendAddr:   listener.LocalAddr().(*net.UDPAddr),
58
+		backendAddr:    backendAddr,
59
+		connTrackTable: make(connTrackMap),
60
+	}, nil
61
+}
62
+
63
+func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) {
64
+	defer func() {
65
+		proxy.connTrackLock.Lock()
66
+		delete(proxy.connTrackTable, *clientKey)
67
+		proxy.connTrackLock.Unlock()
68
+		utils.Debugf("Done proxying between udp/%v and udp/%v", clientAddr.String(), proxy.backendAddr.String())
69
+		proxyConn.Close()
70
+	}()
71
+
72
+	readBuf := make([]byte, UDPBufSize)
73
+	for {
74
+		proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout))
75
+	again:
76
+		read, err := proxyConn.Read(readBuf)
77
+		if err != nil {
78
+			if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED {
79
+				// This will happen if the last write failed
80
+				// (e.g: nothing is actually listening on the
81
+				// proxied port on the container), ignore it
82
+				// and continue until UDPConnTrackTimeout
83
+				// expires:
84
+				goto again
85
+			}
86
+			return
87
+		}
88
+		for i := 0; i != read; {
89
+			written, err := proxy.listener.WriteToUDP(readBuf[i:read], clientAddr)
90
+			if err != nil {
91
+				return
92
+			}
93
+			i += written
94
+			utils.Debugf("Forwarded %v/%v bytes to udp/%v", i, read, clientAddr.String())
95
+		}
96
+	}
97
+}
98
+
99
+func (proxy *UDPProxy) Run() {
100
+	readBuf := make([]byte, UDPBufSize)
101
+	utils.Debugf("Starting proxy on udp/%v for udp/%v", proxy.frontendAddr, proxy.backendAddr)
102
+	for {
103
+		read, from, err := proxy.listener.ReadFromUDP(readBuf)
104
+		if err != nil {
105
+			// NOTE: Apparently ReadFrom doesn't return
106
+			// ECONNREFUSED like Read do (see comment in
107
+			// UDPProxy.replyLoop)
108
+			if utils.IsClosedError(err) {
109
+				utils.Debugf("Stopping proxy on udp/%v for udp/%v (socket was closed)", proxy.frontendAddr, proxy.backendAddr)
110
+			} else {
111
+				utils.Errorf("Stopping proxy on udp/%v for udp/%v (%v)", proxy.frontendAddr, proxy.backendAddr, err.Error())
112
+			}
113
+			break
114
+		}
115
+
116
+		fromKey := newConnTrackKey(from)
117
+		proxy.connTrackLock.Lock()
118
+		proxyConn, hit := proxy.connTrackTable[*fromKey]
119
+		if !hit {
120
+			proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr)
121
+			if err != nil {
122
+				log.Printf("Can't proxy a datagram to udp/%s: %v\n", proxy.backendAddr.String(), err)
123
+				continue
124
+			}
125
+			proxy.connTrackTable[*fromKey] = proxyConn
126
+			go proxy.replyLoop(proxyConn, from, fromKey)
127
+		}
128
+		proxy.connTrackLock.Unlock()
129
+		for i := 0; i != read; {
130
+			written, err := proxyConn.Write(readBuf[i:read])
131
+			if err != nil {
132
+				log.Printf("Can't proxy a datagram to udp/%s: %v\n", proxy.backendAddr.String(), err)
133
+				break
134
+			}
135
+			i += written
136
+			utils.Debugf("Forwarded %v/%v bytes to udp/%v", i, read, proxy.backendAddr.String())
137
+		}
138
+	}
139
+}
140
+
141
+func (proxy *UDPProxy) Close() {
142
+	proxy.listener.Close()
143
+	proxy.connTrackLock.Lock()
144
+	defer proxy.connTrackLock.Unlock()
145
+	for _, conn := range proxy.connTrackTable {
146
+		conn.Close()
147
+	}
148
+}
149
+
150
+func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr }
151
+func (proxy *UDPProxy) BackendAddr() net.Addr  { return proxy.backendAddr }
... ...
@@ -3,6 +3,7 @@ package docker
3 3
 import (
4 4
 	"container/list"
5 5
 	"fmt"
6
+	"github.com/dotcloud/docker/gograph"
6 7
 	"github.com/dotcloud/docker/utils"
7 8
 	"io"
8 9
 	"io/ioutil"
... ...
@@ -24,7 +25,6 @@ type Capabilities struct {
24 24
 }
25 25
 
26 26
 type Runtime struct {
27
-	root           string
28 27
 	repository     string
29 28
 	containers     *list.List
30 29
 	networkManager *NetworkManager
... ...
@@ -32,10 +32,10 @@ type Runtime struct {
32 32
 	repositories   *TagStore
33 33
 	idIndex        *utils.TruncIndex
34 34
 	capabilities   *Capabilities
35
-	autoRestart    bool
36 35
 	volumes        *Graph
37 36
 	srv            *Server
38
-	Dns            []string
37
+	config         *DaemonConfig
38
+	containerGraph *gograph.Database
39 39
 }
40 40
 
41 41
 var sysInitPath string
... ...
@@ -66,10 +66,15 @@ func (runtime *Runtime) getContainerElement(id string) *list.Element {
66 66
 // Get looks for a container by the specified ID or name, and returns it.
67 67
 // If the container is not found, or if an error occurs, nil is returned.
68 68
 func (runtime *Runtime) Get(name string) *Container {
69
+	if c, _ := runtime.GetByName(name); c != nil {
70
+		return c
71
+	}
72
+
69 73
 	id, err := runtime.idIndex.Get(name)
70 74
 	if err != nil {
71 75
 		return nil
72 76
 	}
77
+
73 78
 	e := runtime.getContainerElement(id)
74 79
 	if e == nil {
75 80
 		return nil
... ...
@@ -87,10 +92,9 @@ func (runtime *Runtime) containerRoot(id string) string {
87 87
 	return path.Join(runtime.repository, id)
88 88
 }
89 89
 
90
-// Load reads the contents of a container from disk and registers
91
-// it with Register.
90
+// Load reads the contents of a container from disk
92 91
 // This is typically done at startup.
93
-func (runtime *Runtime) Load(id string) (*Container, error) {
92
+func (runtime *Runtime) load(id string) (*Container, error) {
94 93
 	container := &Container{root: runtime.containerRoot(id)}
95 94
 	if err := container.FromDisk(); err != nil {
96 95
 		return nil, err
... ...
@@ -101,9 +105,6 @@ func (runtime *Runtime) Load(id string) (*Container, error) {
101 101
 	if container.State.Running {
102 102
 		container.State.Ghost = true
103 103
 	}
104
-	if err := runtime.Register(container); err != nil {
105
-		return nil, err
106
-	}
107 104
 	return container, nil
108 105
 }
109 106
 
... ...
@@ -148,11 +149,11 @@ func (runtime *Runtime) Register(container *Container) error {
148 148
 		}
149 149
 		if !strings.Contains(string(output), "RUNNING") {
150 150
 			utils.Debugf("Container %s was supposed to be running be is not.", container.ID)
151
-			if runtime.autoRestart {
151
+			if runtime.config.AutoRestart {
152 152
 				utils.Debugf("Restarting")
153 153
 				container.State.Ghost = false
154 154
 				container.State.setStopped(0)
155
-				hostConfig := &HostConfig{}
155
+				hostConfig, _ := container.ReadHostConfig()
156 156
 				if err := container.Start(hostConfig); err != nil {
157 157
 					return err
158 158
 				}
... ...
@@ -172,9 +173,9 @@ func (runtime *Runtime) Register(container *Container) error {
172 172
 	if !container.State.Running {
173 173
 		close(container.waitLock)
174 174
 	} else if !nomonitor {
175
-		container.allocateNetwork()
176
-		// hostConfig isn't needed here and can be nil
177
-		go container.monitor(nil)
175
+		hostConfig, _ := container.ReadHostConfig()
176
+		container.allocateNetwork(hostConfig)
177
+		go container.monitor(hostConfig)
178 178
 	}
179 179
 	return nil
180 180
 }
... ...
@@ -202,6 +203,7 @@ func (runtime *Runtime) Destroy(container *Container) error {
202 202
 	if err := container.Stop(3); err != nil {
203 203
 		return err
204 204
 	}
205
+
205 206
 	if mounted, err := container.Mounted(); err != nil {
206 207
 		return err
207 208
 	} else if mounted {
... ...
@@ -209,6 +211,11 @@ func (runtime *Runtime) Destroy(container *Container) error {
209 209
 			return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
210 210
 		}
211 211
 	}
212
+
213
+	if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
214
+		utils.Debugf("Unable to remove container from link graph: %s", err)
215
+	}
216
+
212 217
 	// Deregister the container before removing its directory, to avoid race conditions
213 218
 	runtime.idIndex.Delete(container.ID)
214 219
 	runtime.containers.Remove(element)
... ...
@@ -227,9 +234,10 @@ func (runtime *Runtime) restore() error {
227 227
 	if err != nil {
228 228
 		return err
229 229
 	}
230
+	containers := []*Container{}
230 231
 	for i, v := range dir {
231 232
 		id := v.Name()
232
-		container, err := runtime.Load(id)
233
+		container, err := runtime.load(id)
233 234
 		if i%21 == 0 && os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
234 235
 			fmt.Printf("\b%c", wheel[i%4])
235 236
 		}
... ...
@@ -238,10 +246,30 @@ func (runtime *Runtime) restore() error {
238 238
 			continue
239 239
 		}
240 240
 		utils.Debugf("Loaded container %v", container.ID)
241
+		containers = append(containers, container)
242
+	}
243
+	sortContainers(containers, func(i, j *Container) bool {
244
+		ic, _ := i.ReadHostConfig()
245
+		jc, _ := j.ReadHostConfig()
246
+
247
+		if ic == nil || ic.Links == nil {
248
+			return true
249
+		}
250
+		if jc == nil || jc.Links == nil {
251
+			return false
252
+		}
253
+		return len(ic.Links) < len(jc.Links)
254
+	})
255
+	for _, container := range containers {
256
+		if err := runtime.Register(container); err != nil {
257
+			utils.Debugf("Failed to register container %s: %s", container.ID, err)
258
+			continue
259
+		}
241 260
 	}
242 261
 	if os.Getenv("DEBUG") == "" && os.Getenv("TEST") == "" {
243 262
 		fmt.Printf("\bdone.\n")
244 263
 	}
264
+
245 265
 	return nil
246 266
 }
247 267
 
... ...
@@ -274,27 +302,45 @@ func (runtime *Runtime) UpdateCapabilities(quiet bool) {
274 274
 }
275 275
 
276 276
 // Create creates a new container from the given configuration.
277
-func (runtime *Runtime) Create(config *Config) (*Container, error) {
277
+func (runtime *Runtime) Create(config *Config) (*Container, []string, error) {
278 278
 	// Lookup image
279 279
 	img, err := runtime.repositories.LookupImage(config.Image)
280 280
 	if err != nil {
281
-		return nil, err
281
+		return nil, nil, err
282 282
 	}
283 283
 
284
+	warnings := []string{}
284 285
 	if img.Config != nil {
286
+		if img.Config.PortSpecs != nil && warnings != nil {
287
+			for _, p := range img.Config.PortSpecs {
288
+				if strings.Contains(p, ":") {
289
+					warnings = append(warnings, "This image expects private ports to be mapped to public ports on your host. "+
290
+						"This has been deprecated and the public mappings will not be honored."+
291
+						"Use -p to publish the ports.")
292
+					break
293
+				}
294
+			}
295
+		}
285 296
 		if err := MergeConfig(config, img.Config); err != nil {
286
-			return nil, err
297
+			return nil, nil, err
287 298
 		}
299
+
288 300
 	}
289 301
 
290 302
 	if len(config.Entrypoint) != 0 && config.Cmd == nil {
291 303
 		config.Cmd = []string{}
292 304
 	} else if config.Cmd == nil || len(config.Cmd) == 0 {
293
-		return nil, fmt.Errorf("No command specified")
305
+		return nil, nil, fmt.Errorf("No command specified")
294 306
 	}
295 307
 
296 308
 	// Generate id
297 309
 	id := GenerateID()
310
+
311
+	// Set the default enitity in the graph
312
+	if _, err := runtime.containerGraph.Set(fmt.Sprintf("/%s", id), id); err != nil {
313
+		return nil, nil, err
314
+	}
315
+
298 316
 	// Generate default hostname
299 317
 	// FIXME: the lxc template no longer needs to set a default hostname
300 318
 	if config.Hostname == "" {
... ...
@@ -328,36 +374,36 @@ func (runtime *Runtime) Create(config *Config) (*Container, error) {
328 328
 	// Step 1: create the container directory.
329 329
 	// This doubles as a barrier to avoid race conditions.
330 330
 	if err := os.Mkdir(container.root, 0700); err != nil {
331
-		return nil, err
331
+		return nil, nil, err
332 332
 	}
333 333
 
334 334
 	resolvConf, err := utils.GetResolvConf()
335 335
 	if err != nil {
336
-		return nil, err
336
+		return nil, nil, err
337 337
 	}
338 338
 
339
-	if len(config.Dns) == 0 && len(runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
339
+	if len(config.Dns) == 0 && len(runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
340 340
 		//"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns
341
-		runtime.Dns = defaultDns
341
+		runtime.config.Dns = defaultDns
342 342
 	}
343 343
 
344 344
 	// If custom dns exists, then create a resolv.conf for the container
345
-	if len(config.Dns) > 0 || len(runtime.Dns) > 0 {
345
+	if len(config.Dns) > 0 || len(runtime.config.Dns) > 0 {
346 346
 		var dns []string
347 347
 		if len(config.Dns) > 0 {
348 348
 			dns = config.Dns
349 349
 		} else {
350
-			dns = runtime.Dns
350
+			dns = runtime.config.Dns
351 351
 		}
352 352
 		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
353 353
 		f, err := os.Create(container.ResolvConfPath)
354 354
 		if err != nil {
355
-			return nil, err
355
+			return nil, nil, err
356 356
 		}
357 357
 		defer f.Close()
358 358
 		for _, dns := range dns {
359 359
 			if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
360
-				return nil, err
360
+				return nil, nil, err
361 361
 			}
362 362
 		}
363 363
 	} else {
... ...
@@ -366,7 +412,7 @@ func (runtime *Runtime) Create(config *Config) (*Container, error) {
366 366
 
367 367
 	// Step 2: save the container json
368 368
 	if err := container.ToDisk(); err != nil {
369
-		return nil, err
369
+		return nil, nil, err
370 370
 	}
371 371
 
372 372
 	// Step 3: if hostname, build hostname and hosts files
... ...
@@ -396,9 +442,9 @@ ff02::2		ip6-allrouters
396 396
 
397 397
 	// Step 4: register the container
398 398
 	if err := runtime.Register(container); err != nil {
399
-		return nil, err
399
+		return nil, nil, err
400 400
 	}
401
-	return container, nil
401
+	return container, warnings, nil
402 402
 }
403 403
 
404 404
 // Commit creates a new filesystem image from the current state of a container.
... ...
@@ -428,13 +474,85 @@ func (runtime *Runtime) Commit(container *Container, repository, tag, comment, a
428 428
 	return img, nil
429 429
 }
430 430
 
431
+func (runtime *Runtime) GetByName(name string) (*Container, error) {
432
+	if id, err := runtime.idIndex.Get(name); err == nil {
433
+		name = id
434
+	}
435
+
436
+	entity := runtime.containerGraph.Get(name)
437
+	if entity == nil {
438
+		return nil, fmt.Errorf("Could not find entity for %s", name)
439
+	}
440
+	e := runtime.getContainerElement(entity.ID())
441
+	if e == nil {
442
+		return nil, fmt.Errorf("Could not find container for entity id %s", entity.ID())
443
+	}
444
+	return e.Value.(*Container), nil
445
+}
446
+
447
+func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
448
+	children := make(map[string]*Container)
449
+
450
+	err := runtime.containerGraph.Walk(name, func(p string, e *gograph.Entity) error {
451
+		c := runtime.Get(e.ID())
452
+		if c == nil {
453
+			return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
454
+		}
455
+		children[p] = c
456
+		return nil
457
+	}, 0)
458
+
459
+	if err != nil {
460
+		return nil, err
461
+	}
462
+	return children, nil
463
+}
464
+
465
+func (runtime *Runtime) RenameLink(oldName, newName string) error {
466
+	if id, err := runtime.idIndex.Get(oldName); err == nil {
467
+		oldName = id
468
+	}
469
+	entity := runtime.containerGraph.Get(oldName)
470
+	if entity == nil {
471
+		return fmt.Errorf("Could not find entity for %s", oldName)
472
+	}
473
+
474
+	// This is not rename but adding a new link for the default name
475
+	// Strip the leading '/'
476
+	if entity.ID() == oldName[1:] {
477
+		_, err := runtime.containerGraph.Set(newName, entity.ID())
478
+		return err
479
+	}
480
+	return runtime.containerGraph.Rename(oldName, newName)
481
+}
482
+
483
+func (runtime *Runtime) Link(parentName, childName, alias string) error {
484
+	if id, err := runtime.idIndex.Get(parentName); err == nil {
485
+		parentName = id
486
+	}
487
+	parent := runtime.containerGraph.Get(parentName)
488
+	if parent == nil {
489
+		return fmt.Errorf("Could not get container for %s", parentName)
490
+	}
491
+	if id, err := runtime.idIndex.Get(childName); err == nil {
492
+		childName = id
493
+	}
494
+	child := runtime.containerGraph.Get(childName)
495
+	if child == nil {
496
+		return fmt.Errorf("Could not get container for %s", childName)
497
+	}
498
+	cc := runtime.Get(child.ID())
499
+
500
+	_, err := runtime.containerGraph.Set(path.Join(parentName, alias), cc.ID)
501
+	return err
502
+}
503
+
431 504
 // FIXME: harmonize with NewGraph()
432
-func NewRuntime(flGraphPath string, autoRestart bool, dns []string) (*Runtime, error) {
433
-	runtime, err := NewRuntimeFromDirectory(flGraphPath, autoRestart)
505
+func NewRuntime(config *DaemonConfig) (*Runtime, error) {
506
+	runtime, err := NewRuntimeFromDirectory(config)
434 507
 	if err != nil {
435 508
 		return nil, err
436 509
 	}
437
-	runtime.Dns = dns
438 510
 
439 511
 	if k, err := utils.GetKernelVersion(); err != nil {
440 512
 		log.Printf("WARNING: %s\n", err)
... ...
@@ -447,34 +565,39 @@ func NewRuntime(flGraphPath string, autoRestart bool, dns []string) (*Runtime, e
447 447
 	return runtime, nil
448 448
 }
449 449
 
450
-func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
451
-	runtimeRepo := path.Join(root, "containers")
450
+func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
451
+	runtimeRepo := path.Join(config.GraphPath, "containers")
452 452
 
453 453
 	if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
454 454
 		return nil, err
455 455
 	}
456 456
 
457
-	g, err := NewGraph(path.Join(root, "graph"))
457
+	g, err := NewGraph(path.Join(config.GraphPath, "graph"))
458 458
 	if err != nil {
459 459
 		return nil, err
460 460
 	}
461
-	volumes, err := NewGraph(path.Join(root, "volumes"))
461
+	volumes, err := NewGraph(path.Join(config.GraphPath, "volumes"))
462 462
 	if err != nil {
463 463
 		return nil, err
464 464
 	}
465
-	repositories, err := NewTagStore(path.Join(root, "repositories"), g)
465
+	repositories, err := NewTagStore(path.Join(config.GraphPath, "repositories"), g)
466 466
 	if err != nil {
467 467
 		return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
468 468
 	}
469
-	if NetworkBridgeIface == "" {
470
-		NetworkBridgeIface = DefaultNetworkBridge
469
+	if config.BridgeIface == "" {
470
+		config.BridgeIface = DefaultNetworkBridge
471
+	}
472
+	netManager, err := newNetworkManager(config)
473
+	if err != nil {
474
+		return nil, err
471 475
 	}
472
-	netManager, err := newNetworkManager(NetworkBridgeIface)
476
+
477
+	graph, err := gograph.NewDatabase(path.Join(config.GraphPath, "linkgraph.db"))
473 478
 	if err != nil {
474 479
 		return nil, err
475 480
 	}
481
+
476 482
 	runtime := &Runtime{
477
-		root:           root,
478 483
 		repository:     runtimeRepo,
479 484
 		containers:     list.New(),
480 485
 		networkManager: netManager,
... ...
@@ -482,8 +605,9 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) {
482 482
 		repositories:   repositories,
483 483
 		idIndex:        utils.NewTruncIndex(),
484 484
 		capabilities:   &Capabilities{},
485
-		autoRestart:    autoRestart,
486 485
 		volumes:        volumes,
486
+		config:         config,
487
+		containerGraph: graph,
487 488
 	}
488 489
 
489 490
 	if err := runtime.restore(); err != nil {
... ...
@@ -43,7 +43,7 @@ func nuke(runtime *Runtime) error {
43 43
 	}
44 44
 	wg.Wait()
45 45
 	runtime.networkManager.Close()
46
-	return os.RemoveAll(runtime.root)
46
+	return os.RemoveAll(runtime.config.GraphPath)
47 47
 }
48 48
 
49 49
 func cleanup(runtime *Runtime) error {
... ...
@@ -85,8 +85,6 @@ func init() {
85 85
 		log.Fatal("docker tests need to be run as root")
86 86
 	}
87 87
 
88
-	NetworkBridgeIface = unitTestNetworkBridge
89
-
90 88
 	// Setup the base runtime, which will be duplicated for each test.
91 89
 	// (no tests are run directly in the base)
92 90
 	setupBaseImage()
... ...
@@ -98,7 +96,12 @@ func init() {
98 98
 
99 99
 
100 100
 func setupBaseImage() {
101
-	runtime, err := NewRuntimeFromDirectory(unitTestStoreBase, false)
101
+	config := &DaemonConfig{
102
+		GraphPath:   unitTestStoreBase,
103
+		AutoRestart: false,
104
+		BridgeIface: unitTestNetworkBridge,
105
+	}
106
+	runtime, err := NewRuntimeFromDirectory(config)
102 107
 	if err != nil {
103 108
 		log.Fatalf("Unable to create a runtime for tests:", err)
104 109
 	}
... ...
@@ -106,7 +109,6 @@ func setupBaseImage() {
106 106
 	// Create the "Server"
107 107
 	srv := &Server{
108 108
 		runtime:     runtime,
109
-		enableCors:  false,
110 109
 		pullingPool: make(map[string]struct{}),
111 110
 		pushingPool: make(map[string]struct{}),
112 111
 	}
... ...
@@ -129,7 +131,6 @@ func spawnGlobalDaemon() {
129 129
 	globalRuntime = mkRuntime(log.New(os.Stderr, "", 0))
130 130
 	srv := &Server{
131 131
 		runtime:     globalRuntime,
132
-		enableCors:  false,
133 132
 		pullingPool: make(map[string]struct{}),
134 133
 		pushingPool: make(map[string]struct{}),
135 134
 	}
... ...
@@ -171,7 +172,7 @@ func TestRuntimeCreate(t *testing.T) {
171 171
 		t.Errorf("Expected 0 containers, %v found", len(runtime.List()))
172 172
 	}
173 173
 
174
-	container, err := runtime.Create(&Config{
174
+	container, _, err := runtime.Create(&Config{
175 175
 		Image: GetTestImage(runtime).ID,
176 176
 		Cmd:   []string{"ls", "-al"},
177 177
 	},
... ...
@@ -211,12 +212,12 @@ func TestRuntimeCreate(t *testing.T) {
211 211
 		t.Errorf("Exists() returned false for a newly created container")
212 212
 	}
213 213
 
214
-	// Make sure crete with bad parameters returns an error
215
-	if _, err = runtime.Create(&Config{Image: GetTestImage(runtime).ID}); err == nil {
214
+	// Make sure create with bad parameters returns an error
215
+	if _, _, err = runtime.Create(&Config{Image: GetTestImage(runtime).ID}); err == nil {
216 216
 		t.Fatal("Builder.Create should throw an error when Cmd is missing")
217 217
 	}
218 218
 
219
-	if _, err := runtime.Create(
219
+	if _, _, err := runtime.Create(
220 220
 		&Config{
221 221
 			Image: GetTestImage(runtime).ID,
222 222
 			Cmd:   []string{},
... ...
@@ -230,30 +231,19 @@ func TestRuntimeCreate(t *testing.T) {
230 230
 		Cmd:       []string{"/bin/ls"},
231 231
 		PortSpecs: []string{"80"},
232 232
 	}
233
-	container, err = runtime.Create(config)
233
+	container, _, err = runtime.Create(config)
234 234
 
235
-	image, err := runtime.Commit(container, "testrepo", "testtag", "", "", config)
235
+	_, err = runtime.Commit(container, "testrepo", "testtag", "", "", config)
236 236
 	if err != nil {
237 237
 		t.Error(err)
238 238
 	}
239
-
240
-	_, err = runtime.Create(
241
-		&Config{
242
-			Image:     image.ID,
243
-			PortSpecs: []string{"80000:80"},
244
-		},
245
-	)
246
-	if err == nil {
247
-		t.Fatal("Builder.Create should throw an error when PortSpecs is invalid")
248
-	}
249
-
250 239
 }
251 240
 
252 241
 func TestDestroy(t *testing.T) {
253 242
 	runtime := mkRuntime(t)
254 243
 	defer nuke(runtime)
255 244
 
256
-	container, err := runtime.Create(&Config{
245
+	container, _, err := runtime.Create(&Config{
257 246
 		Image: GetTestImage(runtime).ID,
258 247
 		Cmd:   []string{"ls", "-al"},
259 248
 	})
... ...
@@ -327,6 +317,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container,
327 327
 		strPort   string
328 328
 		runtime   = mkRuntime(t)
329 329
 		port      = 5554
330
+		p	  Port
330 331
 	)
331 332
 
332 333
 	for {
... ...
@@ -340,22 +331,34 @@ func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container,
340 340
 		} else {
341 341
 			t.Fatal(fmt.Errorf("Unknown protocol %v", proto))
342 342
 		}
343
-		container, err = runtime.Create(&Config{
344
-			Image:     GetTestImage(runtime).ID,
345
-			Cmd:       []string{"sh", "-c", cmd},
346
-			PortSpecs: []string{fmt.Sprintf("%s/%s", strPort, proto)},
343
+		ep := make(map[Port]struct{}, 1)
344
+		p = Port(fmt.Sprintf("%s/%s", strPort, proto))
345
+		ep[p] = struct{}{}
346
+
347
+		container, _, err = runtime.Create(&Config{
348
+			Image:        GetTestImage(runtime).ID,
349
+			Cmd:          []string{"sh", "-c", cmd},
350
+			PortSpecs:    []string{fmt.Sprintf("%s/%s", strPort, proto)},
351
+			ExposedPorts: ep,
347 352
 		})
348
-		if container != nil {
349
-			break
350
-		}
351 353
 		if err != nil {
352 354
 			nuke(runtime)
353 355
 			t.Fatal(err)
354 356
 		}
357
+
358
+		if container != nil {
359
+			break
360
+		}
355 361
 		t.Logf("Port %v already in use, trying another one", strPort)
356 362
 	}
357 363
 
358
-	if err := container.Start(&HostConfig{}); err != nil {
364
+	hostConfig := &HostConfig{
365
+		PortBindings: make(map[Port][]PortBinding),
366
+	}
367
+	hostConfig.PortBindings[p] = []PortBinding{
368
+		{},
369
+	}
370
+	if err := container.Start(hostConfig); err != nil {
359 371
 		nuke(runtime)
360 372
 		t.Fatal(err)
361 373
 	}
... ...
@@ -369,7 +372,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container,
369 369
 	// Even if the state is running, lets give some time to lxc to spawn the process
370 370
 	container.WaitTimeout(500 * time.Millisecond)
371 371
 
372
-	strPort = container.NetworkSettings.PortMapping[strings.Title(proto)][strPort]
372
+	strPort = container.NetworkSettings.Ports[p][0].HostPort
373 373
 	return runtime, container, strPort
374 374
 }
375 375
 
... ...
@@ -501,7 +504,8 @@ func TestRestore(t *testing.T) {
501 501
 
502 502
 	// Here are are simulating a docker restart - that is, reloading all containers
503 503
 	// from scratch
504
-	runtime2, err := NewRuntimeFromDirectory(runtime1.root, false)
504
+	runtime1.config.AutoRestart = false
505
+	runtime2, err := NewRuntimeFromDirectory(runtime1.config)
505 506
 	if err != nil {
506 507
 		t.Fatal(err)
507 508
 	}
... ...
@@ -528,3 +532,271 @@ func TestRestore(t *testing.T) {
528 528
 	}
529 529
 	container2.State.Running = false
530 530
 }
531
+
532
+func TestReloadContainerLinks(t *testing.T) {
533
+	runtime1 := mkRuntime(t)
534
+	defer nuke(runtime1)
535
+	// Create a container with one instance of docker
536
+	container1, _, _ := mkContainer(runtime1, []string{"_", "ls", "-al"}, t)
537
+	defer runtime1.Destroy(container1)
538
+
539
+	// Create a second container meant to be killed
540
+	container2, _, _ := mkContainer(runtime1, []string{"-i", "_", "/bin/cat"}, t)
541
+	defer runtime1.Destroy(container2)
542
+
543
+	// Start the container non blocking
544
+	hostConfig := &HostConfig{}
545
+	if err := container2.Start(hostConfig); err != nil {
546
+		t.Fatal(err)
547
+	}
548
+	h1 := &HostConfig{}
549
+	// Add a link to container 2
550
+	h1.Links = []string{utils.TruncateID(container2.ID) + ":first"}
551
+	if err := container1.Start(h1); err != nil {
552
+		t.Fatal(err)
553
+	}
554
+
555
+	if !container2.State.Running {
556
+		t.Fatalf("Container %v should appear as running but isn't", container2.ID)
557
+	}
558
+
559
+	if !container1.State.Running {
560
+		t.Fatalf("Container %s should appear as running bu isn't", container1.ID)
561
+	}
562
+
563
+	if len(runtime1.List()) != 2 {
564
+		t.Errorf("Expected 2 container, %v found", len(runtime1.List()))
565
+	}
566
+
567
+	if !container2.State.Running {
568
+		t.Fatalf("Container %v should appear as running but isn't", container2.ID)
569
+	}
570
+
571
+	// Here are are simulating a docker restart - that is, reloading all containers
572
+	// from scratch
573
+	runtime1.config.AutoRestart = true
574
+	runtime2, err := NewRuntimeFromDirectory(runtime1.config)
575
+	if err != nil {
576
+		t.Fatal(err)
577
+	}
578
+	defer nuke(runtime2)
579
+	if len(runtime2.List()) != 2 {
580
+		t.Errorf("Expected 2 container, %v found", len(runtime2.List()))
581
+	}
582
+	runningCount := 0
583
+	for _, c := range runtime2.List() {
584
+		if c.State.Running {
585
+			t.Logf("Running container found: %v (%v)", c.ID, c.Path)
586
+			runningCount++
587
+		}
588
+	}
589
+	if runningCount != 2 {
590
+		t.Fatalf("Expected 2 container alive, %d found", runningCount)
591
+	}
592
+
593
+	// Make sure container 2 ( the child of container 1 ) was registered and started first
594
+	// with the runtime
595
+	first := runtime2.containers.Front()
596
+	if first.Value.(*Container).ID != container2.ID {
597
+		t.Fatalf("Container 2 %s should be registered first in the runtime", container2.ID)
598
+	}
599
+
600
+	t.Logf("Number of links: %d", runtime2.containerGraph.Refs("engine"))
601
+	// Verify that the link is still registered in the runtime
602
+	entity := runtime2.containerGraph.Get(fmt.Sprintf("/%s", container1.ID))
603
+	if entity == nil {
604
+		t.Fatal("Entity should not be nil")
605
+	}
606
+}
607
+
608
+func TestDefaultContainerName(t *testing.T) {
609
+	runtime := mkRuntime(t)
610
+	defer nuke(runtime)
611
+	srv := &Server{runtime: runtime}
612
+
613
+	config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
614
+	if err != nil {
615
+		t.Fatal(err)
616
+	}
617
+
618
+	shortId, _, err := srv.ContainerCreate(config)
619
+	if err != nil {
620
+		t.Fatal(err)
621
+	}
622
+	container := runtime.Get(shortId)
623
+	containerID := container.ID
624
+
625
+	paths := runtime.containerGraph.RefPaths(containerID)
626
+	if paths == nil || len(paths) == 0 {
627
+		t.Fatalf("Could not find edges for %s", containerID)
628
+	}
629
+	edge := paths[0]
630
+	if edge.ParentID != "0" {
631
+		t.Fatalf("Expected engine got %s", edge.ParentID)
632
+	}
633
+	if edge.EntityID != containerID {
634
+		t.Fatalf("Expected %s got %s", containerID, edge.EntityID)
635
+	}
636
+	if edge.Name != containerID {
637
+		t.Fatalf("Expected %s got %s", containerID, edge.Name)
638
+	}
639
+}
640
+
641
+func TestDefaultContainerRename(t *testing.T) {
642
+	runtime := mkRuntime(t)
643
+	defer nuke(runtime)
644
+	srv := &Server{runtime: runtime}
645
+
646
+	config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
647
+	if err != nil {
648
+		t.Fatal(err)
649
+	}
650
+
651
+	shortId, _, err := srv.ContainerCreate(config)
652
+	if err != nil {
653
+		t.Fatal(err)
654
+	}
655
+	container := runtime.Get(shortId)
656
+	containerID := container.ID
657
+
658
+	if err := runtime.RenameLink(fmt.Sprintf("/%s", containerID), "/webapp"); err != nil {
659
+		t.Fatal(err)
660
+	}
661
+
662
+	webapp, err := runtime.GetByName("/webapp")
663
+	if err != nil {
664
+		t.Fatal(err)
665
+	}
666
+
667
+	if webapp.ID != container.ID {
668
+		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
669
+	}
670
+}
671
+
672
+func TestLinkChildContainer(t *testing.T) {
673
+	runtime := mkRuntime(t)
674
+	defer nuke(runtime)
675
+	srv := &Server{runtime: runtime}
676
+
677
+	config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
678
+	if err != nil {
679
+		t.Fatal(err)
680
+	}
681
+
682
+	shortId, _, err := srv.ContainerCreate(config)
683
+	if err != nil {
684
+		t.Fatal(err)
685
+	}
686
+	container := runtime.Get(shortId)
687
+
688
+	if err := runtime.RenameLink(fmt.Sprintf("/%s", container.ID), "/webapp"); err != nil {
689
+		t.Fatal(err)
690
+	}
691
+
692
+	webapp, err := runtime.GetByName("/webapp")
693
+	if err != nil {
694
+		t.Fatal(err)
695
+	}
696
+
697
+	if webapp.ID != container.ID {
698
+		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
699
+	}
700
+
701
+	config, _, _, err = ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
702
+	if err != nil {
703
+		t.Fatal(err)
704
+	}
705
+
706
+	shortId, _, err = srv.ContainerCreate(config)
707
+	if err != nil {
708
+		t.Fatal(err)
709
+	}
710
+
711
+	childContainer := runtime.Get(shortId)
712
+	if err := runtime.RenameLink(fmt.Sprintf("/%s", childContainer.ID), "/db"); err != nil {
713
+		t.Fatal(err)
714
+	}
715
+
716
+	if err := runtime.Link("/webapp", "/db", "db"); err != nil {
717
+		t.Fatal(err)
718
+	}
719
+
720
+	// Get the child by it's new name
721
+	db, err := runtime.GetByName("/webapp/db")
722
+	if err != nil {
723
+		t.Fatal(err)
724
+	}
725
+	if db.ID != childContainer.ID {
726
+		t.Fatalf("Expect db id to match container id: %s != %s", db.ID, childContainer.ID)
727
+	}
728
+}
729
+
730
+func TestGetAllChildren(t *testing.T) {
731
+	runtime := mkRuntime(t)
732
+	defer nuke(runtime)
733
+	srv := &Server{runtime: runtime}
734
+
735
+	config, _, _, err := ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
736
+	if err != nil {
737
+		t.Fatal(err)
738
+	}
739
+
740
+	shortId, _, err := srv.ContainerCreate(config)
741
+	if err != nil {
742
+		t.Fatal(err)
743
+	}
744
+	container := runtime.Get(shortId)
745
+
746
+	if err := runtime.RenameLink(fmt.Sprintf("/%s", container.ID), "/webapp"); err != nil {
747
+		t.Fatal(err)
748
+	}
749
+
750
+	webapp, err := runtime.GetByName("/webapp")
751
+	if err != nil {
752
+		t.Fatal(err)
753
+	}
754
+
755
+	if webapp.ID != container.ID {
756
+		t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
757
+	}
758
+
759
+	config, _, _, err = ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
760
+	if err != nil {
761
+		t.Fatal(err)
762
+	}
763
+
764
+	shortId, _, err = srv.ContainerCreate(config)
765
+	if err != nil {
766
+		t.Fatal(err)
767
+	}
768
+
769
+	childContainer := runtime.Get(shortId)
770
+	if err := runtime.RenameLink(fmt.Sprintf("/%s", childContainer.ID), "/db"); err != nil {
771
+		t.Fatal(err)
772
+	}
773
+
774
+	if err := runtime.Link("/webapp", "/db", "db"); err != nil {
775
+		t.Fatal(err)
776
+	}
777
+
778
+	children, err := runtime.Children("/webapp")
779
+	if err != nil {
780
+		t.Fatal(err)
781
+	}
782
+
783
+	if children == nil {
784
+		t.Fatal("Children should not be nil")
785
+	}
786
+	if len(children) == 0 {
787
+		t.Fatal("Children should not be empty")
788
+	}
789
+
790
+	for key, value := range children {
791
+		if key != "/webapp/db" {
792
+			t.Fatalf("Expected /webapp/db got %s", key)
793
+		}
794
+		if value.ID != childContainer.ID {
795
+			t.Fatalf("Expected id %s got %s", childContainer.ID, value.ID)
796
+		}
797
+	}
798
+}
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"errors"
7 7
 	"fmt"
8 8
 	"github.com/dotcloud/docker/auth"
9
+	"github.com/dotcloud/docker/gograph"
9 10
 	"github.com/dotcloud/docker/registry"
10 11
 	"github.com/dotcloud/docker/utils"
11 12
 	"io"
... ...
@@ -114,7 +115,7 @@ func (srv *Server) ContainerExport(name string, out io.Writer) error {
114 114
 }
115 115
 
116 116
 func (srv *Server) ImagesSearch(term string) ([]APISearch, error) {
117
-	r, err := registry.NewRegistry(srv.runtime.root, nil, srv.HTTPRequestFactory(nil))
117
+	r, err := registry.NewRegistry(srv.runtime.config.GraphPath, nil, srv.HTTPRequestFactory(nil))
118 118
 	if err != nil {
119 119
 		return nil, err
120 120
 	}
... ...
@@ -151,7 +152,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
151 151
 		return "", err
152 152
 	}
153 153
 
154
-	c, err := srv.runtime.Create(config)
154
+	c, _, err := srv.runtime.Create(config)
155 155
 	if err != nil {
156 156
 		return "", err
157 157
 	}
... ...
@@ -369,7 +370,7 @@ func (srv *Server) ContainerChanges(name string) ([]Change, error) {
369 369
 func (srv *Server) Containers(all, size bool, n int, since, before string) []APIContainers {
370 370
 	var foundBefore bool
371 371
 	var displayed int
372
-	retContainers := []APIContainers{}
372
+	out := []APIContainers{}
373 373
 
374 374
 	for _, container := range srv.runtime.List() {
375 375
 		if !container.State.Running && !all && n == -1 && since == "" && before == "" {
... ...
@@ -391,23 +392,35 @@ func (srv *Server) Containers(all, size bool, n int, since, before string) []API
391 391
 			break
392 392
 		}
393 393
 		displayed++
394
-
395
-		c := APIContainers{
396
-			ID: container.ID,
397
-		}
398
-		c.Image = srv.runtime.repositories.ImageName(container.Image)
399
-		c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
400
-		c.Created = container.Created.Unix()
401
-		c.Status = container.State.String()
402
-		c.Ports = container.NetworkSettings.PortMappingAPI()
403
-		if size {
404
-			c.SizeRw, c.SizeRootFs = container.GetSize()
405
-		}
406
-		retContainers = append(retContainers, c)
394
+		c := createAPIContainer(container, size, srv.runtime)
395
+		out = append(out, c)
407 396
 	}
408
-	return retContainers
397
+	return out
409 398
 }
410 399
 
400
+func createAPIContainer(container *Container, size bool, runtime *Runtime) APIContainers {
401
+	c := APIContainers{
402
+		ID: container.ID,
403
+	}
404
+	names := []string{}
405
+	runtime.containerGraph.Walk("/", func(p string, e *gograph.Entity) error {
406
+		if e.ID() == container.ID {
407
+			names = append(names, p)
408
+		}
409
+		return nil
410
+	}, -1)
411
+	c.Names = names
412
+
413
+	c.Image = runtime.repositories.ImageName(container.Image)
414
+	c.Command = fmt.Sprintf("%s %s", container.Path, strings.Join(container.Args, " "))
415
+	c.Created = container.Created.Unix()
416
+	c.Status = container.State.String()
417
+	c.Ports = container.NetworkSettings.PortMappingAPI()
418
+	if size {
419
+		c.SizeRw, c.SizeRootFs = container.GetSize()
420
+	}
421
+	return c
422
+}
411 423
 func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, config *Config) (string, error) {
412 424
 	container := srv.runtime.Get(name)
413 425
 	if container == nil {
... ...
@@ -646,7 +659,7 @@ func (srv *Server) poolRemove(kind, key string) error {
646 646
 }
647 647
 
648 648
 func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig, metaHeaders map[string][]string, parallel bool) error {
649
-	r, err := registry.NewRegistry(srv.runtime.root, authConfig, srv.HTTPRequestFactory(metaHeaders))
649
+	r, err := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders))
650 650
 	if err != nil {
651 651
 		return err
652 652
 	}
... ...
@@ -855,7 +868,7 @@ func (srv *Server) ImagePush(localName string, out io.Writer, sf *utils.StreamFo
855 855
 
856 856
 	out = utils.NewWriteFlusher(out)
857 857
 	img, err := srv.runtime.graph.Get(localName)
858
-	r, err2 := registry.NewRegistry(srv.runtime.root, authConfig, srv.HTTPRequestFactory(metaHeaders))
858
+	r, err2 := registry.NewRegistry(srv.runtime.config.GraphPath, authConfig, srv.HTTPRequestFactory(metaHeaders))
859 859
 	if err2 != nil {
860 860
 		return err2
861 861
 	}
... ...
@@ -920,10 +933,9 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
920 920
 	return nil
921 921
 }
922 922
 
923
-func (srv *Server) ContainerCreate(config *Config) (string, error) {
924
-
923
+func (srv *Server) ContainerCreate(config *Config) (string, []string, error) {
925 924
 	if config.Memory != 0 && config.Memory < 524288 {
926
-		return "", fmt.Errorf("Memory limit must be given in bytes (minimum 524288 bytes)")
925
+		return "", nil, fmt.Errorf("Memory limit must be given in bytes (minimum 524288 bytes)")
927 926
 	}
928 927
 
929 928
 	if config.Memory > 0 && !srv.runtime.capabilities.MemoryLimit {
... ...
@@ -933,7 +945,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) {
933 933
 	if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit {
934 934
 		config.MemorySwap = -1
935 935
 	}
936
-	container, err := srv.runtime.Create(config)
936
+	container, buildWarnings, err := srv.runtime.Create(config)
937 937
 	if err != nil {
938 938
 		if srv.runtime.graph.IsNotExist(err) {
939 939
 
... ...
@@ -942,12 +954,12 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) {
942 942
 				tag = DEFAULTTAG
943 943
 			}
944 944
 
945
-			return "", fmt.Errorf("No such image: %s (tag: %s)", config.Image, tag)
945
+			return "", nil, fmt.Errorf("No such image: %s (tag: %s)", config.Image, tag)
946 946
 		}
947
-		return "", err
947
+		return "", nil, err
948 948
 	}
949 949
 	srv.LogEvent("create", container.ShortID(), srv.runtime.repositories.ImageName(container.Image))
950
-	return container.ShortID(), nil
950
+	return container.ShortID(), buildWarnings, nil
951 951
 }
952 952
 
953 953
 func (srv *Server) ContainerRestart(name string, t int) error {
... ...
@@ -962,7 +974,34 @@ func (srv *Server) ContainerRestart(name string, t int) error {
962 962
 	return nil
963 963
 }
964 964
 
965
-func (srv *Server) ContainerDestroy(name string, removeVolume bool) error {
965
+func (srv *Server) ContainerDestroy(name string, removeVolume, removeLink bool) error {
966
+	if removeLink {
967
+		p := name
968
+		if p[0] != '/' {
969
+			p = "/" + p
970
+		}
971
+		parent, n := path.Split(p)
972
+		l := len(parent)
973
+		if parent[l-1] == '/' {
974
+			parent = parent[:l-1]
975
+		}
976
+
977
+		pe := srv.runtime.containerGraph.Get(parent)
978
+		parentContainer := srv.runtime.Get(pe.ID())
979
+
980
+		if parentContainer != nil && parentContainer.activeLinks != nil {
981
+			if link, exists := parentContainer.activeLinks[n]; exists {
982
+				link.Disable()
983
+			} else {
984
+				utils.Debugf("Could not find active link for %s", name)
985
+			}
986
+		}
987
+
988
+		if err := srv.runtime.containerGraph.Delete(name); err != nil {
989
+			return err
990
+		}
991
+		return nil
992
+	}
966 993
 	if container := srv.runtime.Get(name); container != nil {
967 994
 		if container.State.Running {
968 995
 			return fmt.Errorf("Impossible to remove a running container, please stop it first")
... ...
@@ -1162,14 +1201,32 @@ func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error)
1162 1162
 }
1163 1163
 
1164 1164
 func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error {
1165
-	if container := srv.runtime.Get(name); container != nil {
1166
-		if err := container.Start(hostConfig); err != nil {
1167
-			return fmt.Errorf("Error starting container %s: %s", name, err)
1168
-		}
1169
-		srv.LogEvent("start", container.ShortID(), srv.runtime.repositories.ImageName(container.Image))
1170
-	} else {
1165
+	runtime := srv.runtime
1166
+	container := runtime.Get(name)
1167
+	if container == nil {
1171 1168
 		return fmt.Errorf("No such container: %s", name)
1172 1169
 	}
1170
+
1171
+	// Register links
1172
+	if hostConfig != nil && hostConfig.Links != nil {
1173
+		for _, l := range hostConfig.Links {
1174
+			parts, err := parseLink(l)
1175
+			if err != nil {
1176
+				return err
1177
+			}
1178
+
1179
+			childName := parts["name"]
1180
+			if err := runtime.Link(fmt.Sprintf("/%s", container.ID), childName, parts["alias"]); err != nil {
1181
+				return err
1182
+			}
1183
+		}
1184
+	}
1185
+
1186
+	if err := container.Start(hostConfig); err != nil {
1187
+		return fmt.Errorf("Error starting container %s: %s", name, err)
1188
+	}
1189
+	srv.LogEvent("start", container.ShortID(), runtime.repositories.ImageName(container.Image))
1190
+
1173 1191
 	return nil
1174 1192
 }
1175 1193
 
... ...
@@ -1321,17 +1378,16 @@ func (srv *Server) ContainerCopy(name string, resource string, out io.Writer) er
1321 1321
 
1322 1322
 }
1323 1323
 
1324
-func NewServer(flGraphPath string, autoRestart, enableCors bool, dns ListOpts) (*Server, error) {
1324
+func NewServer(config *DaemonConfig) (*Server, error) {
1325 1325
 	if runtime.GOARCH != "amd64" {
1326 1326
 		log.Fatalf("The docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH)
1327 1327
 	}
1328
-	runtime, err := NewRuntime(flGraphPath, autoRestart, dns)
1328
+	runtime, err := NewRuntime(config)
1329 1329
 	if err != nil {
1330 1330
 		return nil, err
1331 1331
 	}
1332 1332
 	srv := &Server{
1333 1333
 		runtime:     runtime,
1334
-		enableCors:  enableCors,
1335 1334
 		pullingPool: make(map[string]struct{}),
1336 1335
 		pushingPool: make(map[string]struct{}),
1337 1336
 		events:      make([]utils.JSONMessage, 0, 64), //only keeps the 64 last events
... ...
@@ -1369,7 +1425,6 @@ func (srv *Server) LogEvent(action, id, from string) {
1369 1369
 type Server struct {
1370 1370
 	sync.Mutex
1371 1371
 	runtime     *Runtime
1372
-	enableCors  bool
1373 1372
 	pullingPool map[string]struct{}
1374 1373
 	pushingPool map[string]struct{}
1375 1374
 	events      []utils.JSONMessage
... ...
@@ -89,7 +89,7 @@ func TestCreateRm(t *testing.T) {
89 89
 		t.Fatal(err)
90 90
 	}
91 91
 
92
-	id, err := srv.ContainerCreate(config)
92
+	id, _, err := srv.ContainerCreate(config)
93 93
 	if err != nil {
94 94
 		t.Fatal(err)
95 95
 	}
... ...
@@ -98,7 +98,7 @@ func TestCreateRm(t *testing.T) {
98 98
 		t.Errorf("Expected 1 container, %v found", len(runtime.List()))
99 99
 	}
100 100
 
101
-	if err = srv.ContainerDestroy(id, true); err != nil {
101
+	if err = srv.ContainerDestroy(id, true, false); err != nil {
102 102
 		t.Fatal(err)
103 103
 	}
104 104
 
... ...
@@ -119,7 +119,7 @@ func TestCreateRmVolumes(t *testing.T) {
119 119
 		t.Fatal(err)
120 120
 	}
121 121
 
122
-	id, err := srv.ContainerCreate(config)
122
+	id, _, err := srv.ContainerCreate(config)
123 123
 	if err != nil {
124 124
 		t.Fatal(err)
125 125
 	}
... ...
@@ -138,7 +138,7 @@ func TestCreateRmVolumes(t *testing.T) {
138 138
 		t.Fatal(err)
139 139
 	}
140 140
 
141
-	if err = srv.ContainerDestroy(id, true); err != nil {
141
+	if err = srv.ContainerDestroy(id, true, false); err != nil {
142 142
 		t.Fatal(err)
143 143
 	}
144 144
 
... ...
@@ -158,7 +158,7 @@ func TestCommit(t *testing.T) {
158 158
 		t.Fatal(err)
159 159
 	}
160 160
 
161
-	id, err := srv.ContainerCreate(config)
161
+	id, _, err := srv.ContainerCreate(config)
162 162
 	if err != nil {
163 163
 		t.Fatal(err)
164 164
 	}
... ...
@@ -179,7 +179,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
179 179
 		t.Fatal(err)
180 180
 	}
181 181
 
182
-	id, err := srv.ContainerCreate(config)
182
+	id, _, err := srv.ContainerCreate(config)
183 183
 	if err != nil {
184 184
 		t.Fatal(err)
185 185
 	}
... ...
@@ -209,7 +209,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
209 209
 	}
210 210
 
211 211
 	// FIXME: this failed once with a race condition ("Unable to remove filesystem for xxx: directory not empty")
212
-	if err := srv.ContainerDestroy(id, true); err != nil {
212
+	if err := srv.ContainerDestroy(id, true, false); err != nil {
213 213
 		t.Fatal(err)
214 214
 	}
215 215
 
... ...
@@ -224,7 +224,7 @@ func TestRunWithTooLowMemoryLimit(t *testing.T) {
224 224
 	defer nuke(runtime)
225 225
 
226 226
 	// Try to create a container with a memory limit of 1 byte less than the minimum allowed limit.
227
-	if _, err := (*Server).ContainerCreate(&Server{runtime: runtime},
227
+	if _, _, err := (*Server).ContainerCreate(&Server{runtime: runtime},
228 228
 		&Config{
229 229
 			Image:     GetTestImage(runtime).ID,
230 230
 			Memory:    524287,
... ...
@@ -397,7 +397,7 @@ func TestRmi(t *testing.T) {
397 397
 		t.Fatal(err)
398 398
 	}
399 399
 
400
-	containerID, err := srv.ContainerCreate(config)
400
+	containerID, _, err := srv.ContainerCreate(config)
401 401
 	if err != nil {
402 402
 		t.Fatal(err)
403 403
 	}
... ...
@@ -418,7 +418,7 @@ func TestRmi(t *testing.T) {
418 418
 		t.Fatal(err)
419 419
 	}
420 420
 
421
-	containerID, err = srv.ContainerCreate(config)
421
+	containerID, _, err = srv.ContainerCreate(config)
422 422
 	if err != nil {
423 423
 		t.Fatal(err)
424 424
 	}
... ...
@@ -34,3 +34,72 @@ func sortImagesByCreationAndTag(images []APIImages) {
34 34
 
35 35
 	sort.Sort(sorter)
36 36
 }
37
+
38
+type portSorter struct {
39
+	ports []Port
40
+	by    func(i, j Port) bool
41
+}
42
+
43
+func (s *portSorter) Len() int {
44
+	return len(s.ports)
45
+}
46
+
47
+func (s *portSorter) Swap(i, j int) {
48
+	s.ports[i], s.ports[j] = s.ports[j], s.ports[i]
49
+}
50
+
51
+func (s *portSorter) Less(i, j int) bool {
52
+	ip := s.ports[i]
53
+	jp := s.ports[j]
54
+
55
+	return s.by(ip, jp)
56
+}
57
+
58
+func sortPorts(ports []Port, predicate func(i, j Port) bool) {
59
+	s := &portSorter{ports, predicate}
60
+	sort.Sort(s)
61
+}
62
+
63
+type containerSorter struct {
64
+	containers []*Container
65
+	by         func(i, j *Container) bool
66
+}
67
+
68
+func (s *containerSorter) Len() int {
69
+	return len(s.containers)
70
+}
71
+
72
+func (s *containerSorter) Swap(i, j int) {
73
+	s.containers[i], s.containers[j] = s.containers[j], s.containers[i]
74
+}
75
+
76
+func (s *containerSorter) Less(i, j int) bool {
77
+	return s.by(s.containers[i], s.containers[j])
78
+}
79
+
80
+func sortContainers(containers []*Container, predicate func(i, j *Container) bool) {
81
+	s := &containerSorter{containers, predicate}
82
+	sort.Sort(s)
83
+}
84
+
85
+type apiLinkSorter struct {
86
+	links []APILink
87
+	by    func(i, j APILink) bool
88
+}
89
+
90
+func (s *apiLinkSorter) Len() int {
91
+	return len(s.links)
92
+}
93
+
94
+func (s *apiLinkSorter) Swap(i, j int) {
95
+	s.links[i], s.links[j] = s.links[j], s.links[i]
96
+}
97
+
98
+func (s *apiLinkSorter) Less(i, j int) bool {
99
+	return s.by(s.links[i], s.links[j])
100
+}
101
+
102
+func sortLinks(links []APILink, predicate func(i, j APILink) bool) {
103
+	s := &apiLinkSorter{links, predicate}
104
+	sort.Sort(s)
105
+}
... ...
@@ -1,6 +1,7 @@
1 1
 package docker
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"testing"
5 6
 )
6 7
 
... ...
@@ -55,3 +56,38 @@ func TestServerListOrderedImagesByCreationDateAndTag(t *testing.T) {
55 55
 		t.Error("Expected []APIImges to be ordered by most recent creation date and tag name.")
56 56
 	}
57 57
 }
58
+
59
+func TestSortUniquePorts(t *testing.T) {
60
+	ports := []Port{
61
+		Port("6379/tcp"),
62
+		Port("22/tcp"),
63
+	}
64
+
65
+	sortPorts(ports, func(ip, jp Port) bool {
66
+		return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
67
+	})
68
+
69
+	first := ports[0]
70
+	if fmt.Sprint(first) != "22/tcp" {
71
+		t.Log(fmt.Sprint(first))
72
+		t.Fail()
73
+	}
74
+}
75
+
76
+func TestSortSamePortWithDifferentProto(t *testing.T) {
77
+	ports := []Port{
78
+		Port("8888/tcp"),
79
+		Port("8888/udp"),
80
+		Port("6379/tcp"),
81
+		Port("6379/udp"),
82
+	}
83
+
84
+	sortPorts(ports, func(ip, jp Port) bool {
85
+		return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp")
86
+	})
87
+
88
+	first := ports[0]
89
+	if fmt.Sprint(first) != "6379/tcp" {
90
+		t.Fail()
91
+	}
92
+}
... ...
@@ -2,6 +2,8 @@ package docker
2 2
 
3 3
 import (
4 4
 	"fmt"
5
+	"github.com/dotcloud/docker/utils"
6
+	"strconv"
5 7
 	"strings"
6 8
 )
7 9
 
... ...
@@ -27,6 +29,7 @@ func CompareConfig(a, b *Config) bool {
27 27
 		len(a.Dns) != len(b.Dns) ||
28 28
 		len(a.Env) != len(b.Env) ||
29 29
 		len(a.PortSpecs) != len(b.PortSpecs) ||
30
+		len(a.ExposedPorts) != len(b.ExposedPorts) ||
30 31
 		len(a.Entrypoint) != len(b.Entrypoint) ||
31 32
 		len(a.Volumes) != len(b.Volumes) {
32 33
 		return false
... ...
@@ -52,6 +55,11 @@ func CompareConfig(a, b *Config) bool {
52 52
 			return false
53 53
 		}
54 54
 	}
55
+	for k := range a.ExposedPorts {
56
+		if _, exists := b.ExposedPorts[k]; !exists {
57
+			return false
58
+		}
59
+	}
55 60
 	for i := 0; i < len(a.Entrypoint); i++ {
56 61
 		if a.Entrypoint[i] != b.Entrypoint[i] {
57 62
 			return false
... ...
@@ -78,26 +86,38 @@ func MergeConfig(userConf, imageConf *Config) error {
78 78
 	if userConf.CpuShares == 0 {
79 79
 		userConf.CpuShares = imageConf.CpuShares
80 80
 	}
81
-	if userConf.PortSpecs == nil || len(userConf.PortSpecs) == 0 {
82
-		userConf.PortSpecs = imageConf.PortSpecs
83
-	} else {
84
-		for _, imagePortSpec := range imageConf.PortSpecs {
85
-			found := false
86
-			imageNat, err := parseNat(imagePortSpec)
87
-			if err != nil {
88
-				return err
89
-			}
90
-			for _, userPortSpec := range userConf.PortSpecs {
91
-				userNat, err := parseNat(userPortSpec)
92
-				if err != nil {
93
-					return err
94
-				}
95
-				if imageNat.Proto == userNat.Proto && imageNat.Backend == userNat.Backend {
96
-					found = true
97
-				}
81
+	if userConf.ExposedPorts == nil || len(userConf.ExposedPorts) == 0 {
82
+		userConf.ExposedPorts = imageConf.ExposedPorts
83
+	}
84
+
85
+	if userConf.PortSpecs != nil && len(userConf.PortSpecs) > 0 {
86
+		if userConf.ExposedPorts == nil {
87
+			userConf.ExposedPorts = make(map[Port]struct{})
88
+		}
89
+		ports, _, err := parsePortSpecs(userConf.PortSpecs)
90
+		if err != nil {
91
+			return err
92
+		}
93
+		for port := range ports {
94
+			if _, exists := userConf.ExposedPorts[port]; !exists {
95
+				userConf.ExposedPorts[port] = struct{}{}
98 96
 			}
99
-			if !found {
100
-				userConf.PortSpecs = append(userConf.PortSpecs, imagePortSpec)
97
+		}
98
+		userConf.PortSpecs = nil
99
+	}
100
+	if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
101
+		utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
102
+		if userConf.ExposedPorts == nil {
103
+			userConf.ExposedPorts = make(map[Port]struct{})
104
+		}
105
+
106
+		ports, _, err := parsePortSpecs(imageConf.PortSpecs)
107
+		if err != nil {
108
+			return err
109
+		}
110
+		for port := range ports {
111
+			if _, exists := userConf.ExposedPorts[port]; !exists {
112
+				userConf.ExposedPorts[port] = struct{}{}
101 113
 			}
102 114
 		}
103 115
 	}
... ...
@@ -174,3 +194,98 @@ func parseLxcOpt(opt string) (string, string, error) {
174 174
 	}
175 175
 	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
176 176
 }
177
+
178
+// We will receive port specs in the format of ip:public:private/proto and these need to be
179
+// parsed in the internal types
180
+func parsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) {
181
+	exposedPorts := make(map[Port]struct{}, len(ports))
182
+	bindings := make(map[Port][]PortBinding)
183
+
184
+	for _, rawPort := range ports {
185
+		proto := "tcp"
186
+		if i := strings.LastIndex(rawPort, "/"); i != -1 {
187
+			proto = rawPort[i+1:]
188
+			rawPort = rawPort[:i]
189
+		}
190
+		if !strings.Contains(rawPort, ":") {
191
+			rawPort = fmt.Sprintf("::%s", rawPort)
192
+		} else if len(strings.Split(rawPort, ":")) == 2 {
193
+			rawPort = fmt.Sprintf(":%s", rawPort)
194
+		}
195
+
196
+		parts, err := utils.PartParser("ip:hostPort:containerPort", rawPort)
197
+		if err != nil {
198
+			return nil, nil, err
199
+		}
200
+		containerPort := parts["containerPort"]
201
+		rawIp := parts["ip"]
202
+		hostPort := parts["hostPort"]
203
+
204
+		if containerPort == "" {
205
+			return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
206
+		}
207
+
208
+		port := NewPort(proto, containerPort)
209
+		if _, exists := exposedPorts[port]; !exists {
210
+			exposedPorts[port] = struct{}{}
211
+		}
212
+
213
+		binding := PortBinding{
214
+			HostIp:   rawIp,
215
+			HostPort: hostPort,
216
+		}
217
+		bslice, exists := bindings[port]
218
+		if !exists {
219
+			bslice = []PortBinding{}
220
+		}
221
+		bindings[port] = append(bslice, binding)
222
+	}
223
+	return exposedPorts, bindings, nil
224
+}
225
+
226
+// Splits a port in the format of port/proto
227
+func splitProtoPort(rawPort string) (string, string) {
228
+	parts := strings.Split(rawPort, "/")
229
+	l := len(parts)
230
+	if l == 0 {
231
+		return "", ""
232
+	}
233
+	if l == 1 {
234
+		return "tcp", rawPort
235
+	}
236
+	return parts[0], parts[1]
237
+}
238
+
239
+func parsePort(rawPort string) (int, error) {
240
+	port, err := strconv.ParseUint(rawPort, 10, 16)
241
+	if err != nil {
242
+		return 0, err
243
+	}
244
+	return int(port), nil
245
+}
246
+
247
+func migratePortMappings(config *Config) error {
248
+	if config.PortSpecs != nil {
249
+		// We don't have to worry about migrating the bindings to the host
250
+		// This is our breaking change
251
+		ports, _, err := parsePortSpecs(config.PortSpecs)
252
+		if err != nil {
253
+			return err
254
+		}
255
+		config.PortSpecs = nil
256
+
257
+		if config.ExposedPorts == nil {
258
+			config.ExposedPorts = make(map[Port]struct{}, len(ports))
259
+		}
260
+		for k, v := range ports {
261
+			config.ExposedPorts[k] = v
262
+		}
263
+	}
264
+	return nil
265
+}
266
+
267
+// Links come in the format of
268
+// name:alias
269
+func parseLink(rawLink string) (map[string]string, error) {
270
+	return utils.PartParser("name:alias", rawLink)
271
+}
... ...
@@ -1044,3 +1044,22 @@ func IsClosedError(err error) bool {
1044 1044
 	 */
1045 1045
 	return strings.HasSuffix(err.Error(), "use of closed network connection")
1046 1046
 }
1047
+
1048
+func PartParser(template, data string) (map[string]string, error) {
1049
+	// ip:public:private
1050
+	templateParts := strings.Split(template, ":")
1051
+	parts := strings.Split(data, ":")
1052
+	if len(parts) != len(templateParts) {
1053
+		return nil, fmt.Errorf("Invalid format to parse.  %s should match template %s", data, template)
1054
+	}
1055
+	out := make(map[string]string, len(templateParts))
1056
+
1057
+	for i, t := range templateParts {
1058
+		value := ""
1059
+		if len(parts) > i {
1060
+			value = parts[i]
1061
+		}
1062
+		out[t] = value
1063
+	}
1064
+	return out, nil
1065
+}
... ...
@@ -424,3 +424,23 @@ func TestDependencyGraph(t *testing.T) {
424 424
 		t.Fatalf("Expected [d], found %v instead", res[2])
425 425
 	}
426 426
 }
427
+
428
+func TestParsePortMapping(t *testing.T) {
429
+	data, err := PartParser("ip:public:private", "192.168.1.1:80:8080")
430
+	if err != nil {
431
+		t.Fatal(err)
432
+	}
433
+
434
+	if len(data) != 3 {
435
+		t.FailNow()
436
+	}
437
+	if data["ip"] != "192.168.1.1" {
438
+		t.Fail()
439
+	}
440
+	if data["public"] != "80" {
441
+		t.Fail()
442
+	}
443
+	if data["private"] != "8080" {
444
+		t.Fail()
445
+	}
446
+}
... ...
@@ -66,7 +66,11 @@ func newTestRuntime(prefix string) (runtime *Runtime, err error) {
66 66
 		return nil, err
67 67
 	}
68 68
 
69
-	runtime, err = NewRuntimeFromDirectory(root, false)
69
+	config := &DaemonConfig{
70
+		GraphPath:   root,
71
+		AutoRestart: false,
72
+	}
73
+	runtime, err = NewRuntimeFromDirectory(config)
70 74
 	if err != nil {
71 75
 		return nil, err
72 76
 	}
... ...
@@ -125,7 +129,7 @@ func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConf
125 125
 	if config.Image == "_" {
126 126
 		config.Image = GetTestImage(r).ID
127 127
 	}
128
-	c, err := r.Create(config)
128
+	c, _, err := r.Create(config)
129 129
 	if err != nil {
130 130
 		return nil, nil, err
131 131
 	}
... ...
@@ -253,12 +257,12 @@ func TestMergeConfig(t *testing.T) {
253 253
 		}
254 254
 	}
255 255
 
256
-	if len(configUser.PortSpecs) != 3 {
257
-		t.Fatalf("Expected 3 portSpecs, 1111:1111, 3333:2222 and 3333:3333, found %d", len(configUser.PortSpecs))
256
+	if len(configUser.ExposedPorts) != 3 {
257
+		t.Fatalf("Expected 3 portSpecs, 1111, 2222 and 3333, found %d", len(configUser.PortSpecs))
258 258
 	}
259
-	for _, portSpecs := range configUser.PortSpecs {
260
-		if portSpecs != "1111:1111" && portSpecs != "3333:2222" && portSpecs != "3333:3333" {
261
-			t.Fatalf("Expected 1111:1111 or 3333:2222 or 3333:3333, found %s", portSpecs)
259
+	for portSpecs := range configUser.ExposedPorts {
260
+		if portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
261
+			t.Fatalf("Expected 1111 or 2222 or 3333, found %s", portSpecs)
262 262
 		}
263 263
 	}
264 264
 	if len(configUser.Env) != 3 {
... ...
@@ -284,60 +288,144 @@ func TestMergeConfig(t *testing.T) {
284 284
 	}
285 285
 }
286 286
 
287
-func TestMergeConfigPublicPortNotHonored(t *testing.T) {
288
-	volumesImage := make(map[string]struct{})
289
-	volumesImage["/test1"] = struct{}{}
290
-	volumesImage["/test2"] = struct{}{}
291
-	configImage := &Config{
292
-		Dns:       []string{"1.1.1.1", "2.2.2.2"},
293
-		PortSpecs: []string{"1111", "2222"},
294
-		Env:       []string{"VAR1=1", "VAR2=2"},
295
-		Volumes:   volumesImage,
296
-	}
287
+func TestParseLxcConfOpt(t *testing.T) {
288
+	opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
297 289
 
298
-	volumesUser := make(map[string]struct{})
299
-	volumesUser["/test3"] = struct{}{}
300
-	configUser := &Config{
301
-		Dns:       []string{"3.3.3.3"},
302
-		PortSpecs: []string{"1111:3333"},
303
-		Env:       []string{"VAR2=3", "VAR3=3"},
304
-		Volumes:   volumesUser,
290
+	for _, o := range opts {
291
+		k, v, err := parseLxcOpt(o)
292
+		if err != nil {
293
+			t.FailNow()
294
+		}
295
+		if k != "lxc.utsname" {
296
+			t.Fail()
297
+		}
298
+		if v != "docker" {
299
+			t.Fail()
300
+		}
305 301
 	}
302
+}
306 303
 
307
-	MergeConfig(configUser, configImage)
308
-
309
-	contains := func(a []string, expect string) bool {
310
-		for _, p := range a {
311
-			if p == expect {
312
-				return true
313
-			}
304
+func TestParseNetworkOptsPrivateOnly(t *testing.T) {
305
+	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::80"})
306
+	if err != nil {
307
+		t.Fatal(err)
308
+	}
309
+	if len(ports) != 1 {
310
+		t.Logf("Expected 1 got %d", len(ports))
311
+		t.FailNow()
312
+	}
313
+	if len(bindings) != 1 {
314
+		t.Logf("Expected 1 got %d", len(bindings))
315
+		t.FailNow()
316
+	}
317
+	for k := range ports {
318
+		if k.Proto() != "tcp" {
319
+			t.Logf("Expected tcp got %s", k.Proto())
320
+			t.Fail()
321
+		}
322
+		if k.Port() != "80" {
323
+			t.Logf("Expected 80 got %s", k.Port())
324
+			t.Fail()
325
+		}
326
+		b, exists := bindings[k]
327
+		if !exists {
328
+			t.Log("Binding does not exist")
329
+			t.FailNow()
330
+		}
331
+		if len(b) != 1 {
332
+			t.Logf("Expected 1 got %d", len(b))
333
+			t.FailNow()
334
+		}
335
+		s := b[0]
336
+		if s.HostPort != "" {
337
+			t.Logf("Expected \"\" got %s", s.HostPort)
338
+			t.Fail()
339
+		}
340
+		if s.HostIp != "192.168.1.100" {
341
+			t.Fail()
314 342
 		}
315
-		return false
316 343
 	}
344
+}
317 345
 
318
-	if !contains(configUser.PortSpecs, "2222") {
319
-		t.Logf("Expected '2222' Ports: %v", configUser.PortSpecs)
320
-		t.Fail()
346
+func TestParseNetworkOptsPublic(t *testing.T) {
347
+	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100:8080:80"})
348
+	if err != nil {
349
+		t.Fatal(err)
321 350
 	}
322
-
323
-	if !contains(configUser.PortSpecs, "1111:3333") {
324
-		t.Logf("Expected '1111:3333' Ports: %v", configUser.PortSpecs)
325
-		t.Fail()
351
+	if len(ports) != 1 {
352
+		t.Logf("Expected 1 got %d", len(ports))
353
+		t.FailNow()
354
+	}
355
+	if len(bindings) != 1 {
356
+		t.Logf("Expected 1 got %d", len(bindings))
357
+		t.FailNow()
358
+	}
359
+	for k := range ports {
360
+		if k.Proto() != "tcp" {
361
+			t.Logf("Expected tcp got %s", k.Proto())
362
+			t.Fail()
363
+		}
364
+		if k.Port() != "80" {
365
+			t.Logf("Expected 80 got %s", k.Port())
366
+			t.Fail()
367
+		}
368
+		b, exists := bindings[k]
369
+		if !exists {
370
+			t.Log("Binding does not exist")
371
+			t.FailNow()
372
+		}
373
+		if len(b) != 1 {
374
+			t.Logf("Expected 1 got %d", len(b))
375
+			t.FailNow()
376
+		}
377
+		s := b[0]
378
+		if s.HostPort != "8080" {
379
+			t.Logf("Expected 8080 got %s", s.HostPort)
380
+			t.Fail()
381
+		}
382
+		if s.HostIp != "192.168.1.100" {
383
+			t.Fail()
384
+		}
326 385
 	}
327 386
 }
328 387
 
329
-func TestParseLxcConfOpt(t *testing.T) {
330
-	opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
331
-
332
-	for _, o := range opts {
333
-		k, v, err := parseLxcOpt(o)
334
-		if err != nil {
388
+func TestParseNetworkOptsUdp(t *testing.T) {
389
+	ports, bindings, err := parsePortSpecs([]string{"192.168.1.100::6000/udp"})
390
+	if err != nil {
391
+		t.Fatal(err)
392
+	}
393
+	if len(ports) != 1 {
394
+		t.Logf("Expected 1 got %d", len(ports))
395
+		t.FailNow()
396
+	}
397
+	if len(bindings) != 1 {
398
+		t.Logf("Expected 1 got %d", len(bindings))
399
+		t.FailNow()
400
+	}
401
+	for k := range ports {
402
+		if k.Proto() != "udp" {
403
+			t.Logf("Expected udp got %s", k.Proto())
404
+			t.Fail()
405
+		}
406
+		if k.Port() != "6000" {
407
+			t.Logf("Expected 6000 got %s", k.Port())
408
+			t.Fail()
409
+		}
410
+		b, exists := bindings[k]
411
+		if !exists {
412
+			t.Log("Binding does not exist")
335 413
 			t.FailNow()
336 414
 		}
337
-		if k != "lxc.utsname" {
415
+		if len(b) != 1 {
416
+			t.Logf("Expected 1 got %d", len(b))
417
+			t.FailNow()
418
+		}
419
+		s := b[0]
420
+		if s.HostPort != "" {
421
+			t.Logf("Expected \"\" got %s", s.HostPort)
338 422
 			t.Fail()
339 423
 		}
340
-		if v != "docker" {
424
+		if s.HostIp != "192.168.1.100" {
341 425
 			t.Fail()
342 426
 		}
343 427
 	}
344 428
new file mode 100644
... ...
@@ -0,0 +1,404 @@
0
+// Copyright 2010 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// Package sqlite provides access to the SQLite library, version 3.
5
+package sqlite
6
+
7
+/*
8
+#cgo LDFLAGS: -lsqlite3
9
+
10
+#include <sqlite3.h>
11
+#include <stdlib.h>
12
+
13
+// These wrappers are necessary because SQLITE_TRANSIENT
14
+// is a pointer constant, and cgo doesn't translate them correctly.
15
+// The definition in sqlite3.h is:
16
+//
17
+// typedef void (*sqlite3_destructor_type)(void*);
18
+// #define SQLITE_STATIC      ((sqlite3_destructor_type)0)
19
+// #define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)
20
+
21
+static int my_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
22
+	return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
23
+}
24
+static int my_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
25
+	return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
26
+}
27
+
28
+*/
29
+import "C"
30
+
31
+import (
32
+	"errors"
33
+	"fmt"
34
+	"reflect"
35
+	"strconv"
36
+	"time"
37
+	"unsafe"
38
+)
39
+
40
+type Errno int
41
+
42
+func (e Errno) Error() string {
43
+	s := errText[e]
44
+	if s == "" {
45
+		return fmt.Sprintf("errno %d", int(e))
46
+	}
47
+	return s
48
+}
49
+
50
+var (
51
+	ErrError      error = Errno(1)   //    /* SQL error or missing database */
52
+	ErrInternal   error = Errno(2)   //    /* Internal logic error in SQLite */
53
+	ErrPerm       error = Errno(3)   //    /* Access permission denied */
54
+	ErrAbort      error = Errno(4)   //    /* Callback routine requested an abort */
55
+	ErrBusy       error = Errno(5)   //    /* The database file is locked */
56
+	ErrLocked     error = Errno(6)   //    /* A table in the database is locked */
57
+	ErrNoMem      error = Errno(7)   //    /* A malloc() failed */
58
+	ErrReadOnly   error = Errno(8)   //    /* Attempt to write a readonly database */
59
+	ErrInterrupt  error = Errno(9)   //    /* Operation terminated by sqlite3_interrupt()*/
60
+	ErrIOErr      error = Errno(10)  //    /* Some kind of disk I/O error occurred */
61
+	ErrCorrupt    error = Errno(11)  //    /* The database disk image is malformed */
62
+	ErrFull       error = Errno(13)  //    /* Insertion failed because database is full */
63
+	ErrCantOpen   error = Errno(14)  //    /* Unable to open the database file */
64
+	ErrEmpty      error = Errno(16)  //    /* Database is empty */
65
+	ErrSchema     error = Errno(17)  //    /* The database schema changed */
66
+	ErrTooBig     error = Errno(18)  //    /* String or BLOB exceeds size limit */
67
+	ErrConstraint error = Errno(19)  //    /* Abort due to constraint violation */
68
+	ErrMismatch   error = Errno(20)  //    /* Data type mismatch */
69
+	ErrMisuse     error = Errno(21)  //    /* Library used incorrectly */
70
+	ErrNolfs      error = Errno(22)  //    /* Uses OS features not supported on host */
71
+	ErrAuth       error = Errno(23)  //    /* Authorization denied */
72
+	ErrFormat     error = Errno(24)  //    /* Auxiliary database format error */
73
+	ErrRange      error = Errno(25)  //    /* 2nd parameter to sqlite3_bind out of range */
74
+	ErrNotDB      error = Errno(26)  //    /* File opened that is not a database file */
75
+	Row                 = Errno(100) //   /* sqlite3_step() has another row ready */
76
+	Done                = Errno(101) //   /* sqlite3_step() has finished executing */
77
+)
78
+
79
+var errText = map[Errno]string{
80
+	1:   "SQL error or missing database",
81
+	2:   "Internal logic error in SQLite",
82
+	3:   "Access permission denied",
83
+	4:   "Callback routine requested an abort",
84
+	5:   "The database file is locked",
85
+	6:   "A table in the database is locked",
86
+	7:   "A malloc() failed",
87
+	8:   "Attempt to write a readonly database",
88
+	9:   "Operation terminated by sqlite3_interrupt()*/",
89
+	10:  "Some kind of disk I/O error occurred",
90
+	11:  "The database disk image is malformed",
91
+	12:  "NOT USED. Table or record not found",
92
+	13:  "Insertion failed because database is full",
93
+	14:  "Unable to open the database file",
94
+	15:  "NOT USED. Database lock protocol error",
95
+	16:  "Database is empty",
96
+	17:  "The database schema changed",
97
+	18:  "String or BLOB exceeds size limit",
98
+	19:  "Abort due to constraint violation",
99
+	20:  "Data type mismatch",
100
+	21:  "Library used incorrectly",
101
+	22:  "Uses OS features not supported on host",
102
+	23:  "Authorization denied",
103
+	24:  "Auxiliary database format error",
104
+	25:  "2nd parameter to sqlite3_bind out of range",
105
+	26:  "File opened that is not a database file",
106
+	100: "sqlite3_step() has another row ready",
107
+	101: "sqlite3_step() has finished executing",
108
+}
109
+
110
+func (c *Conn) error(rv C.int) error {
111
+	if c == nil || c.db == nil {
112
+		return errors.New("nil sqlite database")
113
+	}
114
+	if rv == 0 {
115
+		return nil
116
+	}
117
+	if rv == 21 { // misuse
118
+		return Errno(rv)
119
+	}
120
+	return errors.New(Errno(rv).Error() + ": " + C.GoString(C.sqlite3_errmsg(c.db)))
121
+}
122
+
123
+type Conn struct {
124
+	db *C.sqlite3
125
+}
126
+
127
+func Version() string {
128
+	p := C.sqlite3_libversion()
129
+	return C.GoString(p)
130
+}
131
+
132
+func Open(filename string) (*Conn, error) {
133
+	if C.sqlite3_threadsafe() == 0 {
134
+		return nil, errors.New("sqlite library was not compiled for thread-safe operation")
135
+	}
136
+
137
+	var db *C.sqlite3
138
+	name := C.CString(filename)
139
+	defer C.free(unsafe.Pointer(name))
140
+	rv := C.sqlite3_open_v2(name, &db,
141
+		C.SQLITE_OPEN_FULLMUTEX|
142
+			C.SQLITE_OPEN_READWRITE|
143
+			C.SQLITE_OPEN_CREATE,
144
+		nil)
145
+	if rv != 0 {
146
+		return nil, Errno(rv)
147
+	}
148
+	if db == nil {
149
+		return nil, errors.New("sqlite succeeded without returning a database")
150
+	}
151
+	return &Conn{db}, nil
152
+}
153
+
154
+func NewBackup(dst *Conn, dstTable string, src *Conn, srcTable string) (*Backup, error) {
155
+	dname := C.CString(dstTable)
156
+	sname := C.CString(srcTable)
157
+	defer C.free(unsafe.Pointer(dname))
158
+	defer C.free(unsafe.Pointer(sname))
159
+
160
+	sb := C.sqlite3_backup_init(dst.db, dname, src.db, sname)
161
+	if sb == nil {
162
+		return nil, dst.error(C.sqlite3_errcode(dst.db))
163
+	}
164
+	return &Backup{sb, dst, src}, nil
165
+}
166
+
167
+type Backup struct {
168
+	sb       *C.sqlite3_backup
169
+	dst, src *Conn
170
+}
171
+
172
+func (b *Backup) Step(npage int) error {
173
+	rv := C.sqlite3_backup_step(b.sb, C.int(npage))
174
+	if rv == 0 || Errno(rv) == ErrBusy || Errno(rv) == ErrLocked {
175
+		return nil
176
+	}
177
+	return Errno(rv)
178
+}
179
+
180
+type BackupStatus struct {
181
+	Remaining int
182
+	PageCount int
183
+}
184
+
185
+func (b *Backup) Status() BackupStatus {
186
+	return BackupStatus{int(C.sqlite3_backup_remaining(b.sb)), int(C.sqlite3_backup_pagecount(b.sb))}
187
+}
188
+
189
+func (b *Backup) Run(npage int, period time.Duration, c chan<- BackupStatus) error {
190
+	var err error
191
+	for {
192
+		err = b.Step(npage)
193
+		if err != nil {
194
+			break
195
+		}
196
+		if c != nil {
197
+			c <- b.Status()
198
+		}
199
+		time.Sleep(period)
200
+	}
201
+	return b.dst.error(C.sqlite3_errcode(b.dst.db))
202
+}
203
+
204
+func (b *Backup) Close() error {
205
+	if b.sb == nil {
206
+		return errors.New("backup already closed")
207
+	}
208
+	C.sqlite3_backup_finish(b.sb)
209
+	b.sb = nil
210
+	return nil
211
+}
212
+
213
+func (c *Conn) BusyTimeout(ms int) error {
214
+	rv := C.sqlite3_busy_timeout(c.db, C.int(ms))
215
+	if rv == 0 {
216
+		return nil
217
+	}
218
+	return Errno(rv)
219
+}
220
+
221
+func (c *Conn) Exec(cmd string, args ...interface{}) error {
222
+	s, err := c.Prepare(cmd)
223
+	if err != nil {
224
+		return err
225
+	}
226
+	defer s.Finalize()
227
+	err = s.Exec(args...)
228
+	if err != nil {
229
+		return err
230
+	}
231
+	rv := C.sqlite3_step(s.stmt)
232
+	if Errno(rv) != Done {
233
+		return c.error(rv)
234
+	}
235
+	return nil
236
+}
237
+
238
+type Stmt struct {
239
+	c    *Conn
240
+	stmt *C.sqlite3_stmt
241
+	err  error
242
+	t0   time.Time
243
+	sql  string
244
+	args string
245
+}
246
+
247
+func (c *Conn) Prepare(cmd string) (*Stmt, error) {
248
+	if c == nil || c.db == nil {
249
+		return nil, errors.New("nil sqlite database")
250
+	}
251
+	cmdstr := C.CString(cmd)
252
+	defer C.free(unsafe.Pointer(cmdstr))
253
+	var stmt *C.sqlite3_stmt
254
+	var tail *C.char
255
+	rv := C.sqlite3_prepare_v2(c.db, cmdstr, C.int(len(cmd)+1), &stmt, &tail)
256
+	if rv != 0 {
257
+		return nil, c.error(rv)
258
+	}
259
+	return &Stmt{c: c, stmt: stmt, sql: cmd, t0: time.Now()}, nil
260
+}
261
+
262
+func (s *Stmt) Exec(args ...interface{}) error {
263
+	s.args = fmt.Sprintf(" %v", []interface{}(args))
264
+	rv := C.sqlite3_reset(s.stmt)
265
+	if rv != 0 {
266
+		return s.c.error(rv)
267
+	}
268
+
269
+	n := int(C.sqlite3_bind_parameter_count(s.stmt))
270
+	if n != len(args) {
271
+		return errors.New(fmt.Sprintf("incorrect argument count for Stmt.Exec: have %d want %d", len(args), n))
272
+	}
273
+
274
+	for i, v := range args {
275
+		var str string
276
+		switch v := v.(type) {
277
+		case []byte:
278
+			var p *byte
279
+			if len(v) > 0 {
280
+				p = &v[0]
281
+			}
282
+			if rv := C.my_bind_blob(s.stmt, C.int(i+1), unsafe.Pointer(p), C.int(len(v))); rv != 0 {
283
+				return s.c.error(rv)
284
+			}
285
+			continue
286
+
287
+		case bool:
288
+			if v {
289
+				str = "1"
290
+			} else {
291
+				str = "0"
292
+			}
293
+
294
+		default:
295
+			str = fmt.Sprint(v)
296
+		}
297
+
298
+		cstr := C.CString(str)
299
+		rv := C.my_bind_text(s.stmt, C.int(i+1), cstr, C.int(len(str)))
300
+		C.free(unsafe.Pointer(cstr))
301
+		if rv != 0 {
302
+			return s.c.error(rv)
303
+		}
304
+	}
305
+	return nil
306
+}
307
+
308
+func (s *Stmt) Error() error {
309
+	return s.err
310
+}
311
+
312
+func (s *Stmt) Next() bool {
313
+	rv := C.sqlite3_step(s.stmt)
314
+	err := Errno(rv)
315
+	if err == Row {
316
+		return true
317
+	}
318
+	if err != Done {
319
+		s.err = s.c.error(rv)
320
+	}
321
+	return false
322
+}
323
+
324
+func (s *Stmt) Reset() error {
325
+	C.sqlite3_reset(s.stmt)
326
+	return nil
327
+}
328
+
329
+func (s *Stmt) Scan(args ...interface{}) error {
330
+	n := int(C.sqlite3_column_count(s.stmt))
331
+	if n != len(args) {
332
+		return errors.New(fmt.Sprintf("incorrect argument count for Stmt.Scan: have %d want %d", len(args), n))
333
+	}
334
+
335
+	for i, v := range args {
336
+		n := C.sqlite3_column_bytes(s.stmt, C.int(i))
337
+		p := C.sqlite3_column_blob(s.stmt, C.int(i))
338
+		if p == nil && n > 0 {
339
+			return errors.New("got nil blob")
340
+		}
341
+		var data []byte
342
+		if n > 0 {
343
+			data = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]
344
+		}
345
+		switch v := v.(type) {
346
+		case *[]byte:
347
+			*v = data
348
+		case *string:
349
+			*v = string(data)
350
+		case *bool:
351
+			*v = string(data) == "1"
352
+		case *int:
353
+			x, err := strconv.Atoi(string(data))
354
+			if err != nil {
355
+				return errors.New("arg " + strconv.Itoa(i) + " as int: " + err.Error())
356
+			}
357
+			*v = x
358
+		case *int64:
359
+			x, err := strconv.ParseInt(string(data), 10, 64)
360
+			if err != nil {
361
+				return errors.New("arg " + strconv.Itoa(i) + " as int64: " + err.Error())
362
+			}
363
+			*v = x
364
+		case *float64:
365
+			x, err := strconv.ParseFloat(string(data), 64)
366
+			if err != nil {
367
+				return errors.New("arg " + strconv.Itoa(i) + " as float64: " + err.Error())
368
+			}
369
+			*v = x
370
+		default:
371
+			return errors.New("unsupported type in Scan: " + reflect.TypeOf(v).String())
372
+		}
373
+	}
374
+	return nil
375
+}
376
+
377
+func (s *Stmt) SQL() string {
378
+	return s.sql + s.args
379
+}
380
+
381
+func (s *Stmt) Nanoseconds() int64 {
382
+	return time.Now().Sub(s.t0).Nanoseconds()
383
+}
384
+
385
+func (s *Stmt) Finalize() error {
386
+	rv := C.sqlite3_finalize(s.stmt)
387
+	if rv != 0 {
388
+		return s.c.error(rv)
389
+	}
390
+	return nil
391
+}
392
+
393
+func (c *Conn) Close() error {
394
+	if c == nil || c.db == nil {
395
+		return errors.New("nil sqlite database")
396
+	}
397
+	rv := C.sqlite3_close(c.db)
398
+	if rv != 0 {
399
+		return c.error(rv)
400
+	}
401
+	c.db = nil
402
+	return nil
403
+}
0 404
new file mode 100644
... ...
@@ -0,0 +1,498 @@
0
+// Copyright 2010 The Go Authors. All rights reserved.
1
+// Use of this source code is governed by a BSD-style
2
+// license that can be found in the LICENSE file.
3
+
4
+// Package sqlite3 provides access to the SQLite library, version 3.
5
+//
6
+// The package has no exported API.
7
+// It registers a driver for the standard Go database/sql package.
8
+//
9
+//	import _ "code.google.com/p/gosqlite/sqlite3"
10
+//
11
+// (For an alternate, earlier API, see the code.google.com/p/gosqlite/sqlite package.)
12
+package sqlite
13
+
14
+/*
15
+#cgo LDFLAGS: -lsqlite3
16
+
17
+#include <sqlite3.h>
18
+#include <stdlib.h>
19
+
20
+// These wrappers are necessary because SQLITE_TRANSIENT
21
+// is a pointer constant, and cgo doesn't translate them correctly.
22
+// The definition in sqlite3.h is:
23
+//
24
+// typedef void (*sqlite3_destructor_type)(void*);
25
+// #define SQLITE_STATIC      ((sqlite3_destructor_type)0)
26
+// #define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)
27
+
28
+static int my_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
29
+	return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
30
+}
31
+static int my_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
32
+	return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
33
+}
34
+
35
+*/
36
+import "C"
37
+
38
+import (
39
+	"database/sql"
40
+	"database/sql/driver"
41
+	"errors"
42
+	"fmt"
43
+	"io"
44
+	"strings"
45
+	"time"
46
+	"unsafe"
47
+)
48
+
49
+func init() {
50
+	sql.Register("sqlite3", impl{})
51
+}
52
+
53
+type errno int
54
+
55
+func (e errno) Error() string {
56
+	s := errText[e]
57
+	if s == "" {
58
+		return fmt.Sprintf("errno %d", int(e))
59
+	}
60
+	return s
61
+}
62
+
63
+var (
64
+	errError      error = errno(1)   //    /* SQL error or missing database */
65
+	errInternal   error = errno(2)   //    /* Internal logic error in SQLite */
66
+	errPerm       error = errno(3)   //    /* Access permission denied */
67
+	errAbort      error = errno(4)   //    /* Callback routine requested an abort */
68
+	errBusy       error = errno(5)   //    /* The database file is locked */
69
+	errLocked     error = errno(6)   //    /* A table in the database is locked */
70
+	errNoMem      error = errno(7)   //    /* A malloc() failed */
71
+	errReadOnly   error = errno(8)   //    /* Attempt to write a readonly database */
72
+	errInterrupt  error = errno(9)   //    /* Operation terminated by sqlite3_interrupt()*/
73
+	errIOErr      error = errno(10)  //    /* Some kind of disk I/O error occurred */
74
+	errCorrupt    error = errno(11)  //    /* The database disk image is malformed */
75
+	errFull       error = errno(13)  //    /* Insertion failed because database is full */
76
+	errCantOpen   error = errno(14)  //    /* Unable to open the database file */
77
+	errEmpty      error = errno(16)  //    /* Database is empty */
78
+	errSchema     error = errno(17)  //    /* The database schema changed */
79
+	errTooBig     error = errno(18)  //    /* String or BLOB exceeds size limit */
80
+	errConstraint error = errno(19)  //    /* Abort due to constraint violation */
81
+	errMismatch   error = errno(20)  //    /* Data type mismatch */
82
+	errMisuse     error = errno(21)  //    /* Library used incorrectly */
83
+	errNolfs      error = errno(22)  //    /* Uses OS features not supported on host */
84
+	errAuth       error = errno(23)  //    /* Authorization denied */
85
+	errFormat     error = errno(24)  //    /* Auxiliary database format error */
86
+	errRange      error = errno(25)  //    /* 2nd parameter to sqlite3_bind out of range */
87
+	errNotDB      error = errno(26)  //    /* File opened that is not a database file */
88
+	stepRow             = errno(100) //   /* sqlite3_step() has another row ready */
89
+	stepDone            = errno(101) //   /* sqlite3_step() has finished executing */
90
+)
91
+
92
+var errText = map[errno]string{
93
+	1:   "SQL error or missing database",
94
+	2:   "Internal logic error in SQLite",
95
+	3:   "Access permission denied",
96
+	4:   "Callback routine requested an abort",
97
+	5:   "The database file is locked",
98
+	6:   "A table in the database is locked",
99
+	7:   "A malloc() failed",
100
+	8:   "Attempt to write a readonly database",
101
+	9:   "Operation terminated by sqlite3_interrupt()*/",
102
+	10:  "Some kind of disk I/O error occurred",
103
+	11:  "The database disk image is malformed",
104
+	12:  "NOT USED. Table or record not found",
105
+	13:  "Insertion failed because database is full",
106
+	14:  "Unable to open the database file",
107
+	15:  "NOT USED. Database lock protocol error",
108
+	16:  "Database is empty",
109
+	17:  "The database schema changed",
110
+	18:  "String or BLOB exceeds size limit",
111
+	19:  "Abort due to constraint violation",
112
+	20:  "Data type mismatch",
113
+	21:  "Library used incorrectly",
114
+	22:  "Uses OS features not supported on host",
115
+	23:  "Authorization denied",
116
+	24:  "Auxiliary database format error",
117
+	25:  "2nd parameter to sqlite3_bind out of range",
118
+	26:  "File opened that is not a database file",
119
+	100: "sqlite3_step() has another row ready",
120
+	101: "sqlite3_step() has finished executing",
121
+}
122
+
123
+type impl struct{}
124
+
125
+func (impl) Open(name string) (driver.Conn, error) {
126
+	if C.sqlite3_threadsafe() == 0 {
127
+		return nil, errors.New("sqlite library was not compiled for thread-safe operation")
128
+	}
129
+
130
+	var db *C.sqlite3
131
+	cname := C.CString(name)
132
+	defer C.free(unsafe.Pointer(cname))
133
+	rv := C.sqlite3_open_v2(cname, &db,
134
+		C.SQLITE_OPEN_FULLMUTEX|
135
+			C.SQLITE_OPEN_READWRITE|
136
+			C.SQLITE_OPEN_CREATE,
137
+		nil)
138
+	if rv != 0 {
139
+		return nil, errno(rv)
140
+	}
141
+	if db == nil {
142
+		return nil, errors.New("sqlite succeeded without returning a database")
143
+	}
144
+	return &conn{db: db}, nil
145
+}
146
+
147
+type conn struct {
148
+	db     *C.sqlite3
149
+	closed bool
150
+	tx     bool
151
+}
152
+
153
+func (c *conn) error(rv C.int) error {
154
+	if rv == 0 {
155
+		return nil
156
+	}
157
+	if rv == 21 || c.closed {
158
+		return errno(rv)
159
+	}
160
+	return errors.New(errno(rv).Error() + ": " + C.GoString(C.sqlite3_errmsg(c.db)))
161
+}
162
+
163
+func (c *conn) Prepare(cmd string) (driver.Stmt, error) {
164
+	if c.closed {
165
+		panic("database/sql/driver: misuse of sqlite driver: Prepare after Close")
166
+	}
167
+	cmdstr := C.CString(cmd)
168
+	defer C.free(unsafe.Pointer(cmdstr))
169
+	var s *C.sqlite3_stmt
170
+	var tail *C.char
171
+	rv := C.sqlite3_prepare_v2(c.db, cmdstr, C.int(len(cmd)+1), &s, &tail)
172
+	if rv != 0 {
173
+		return nil, c.error(rv)
174
+	}
175
+	return &stmt{c: c, stmt: s, sql: cmd, t0: time.Now()}, nil
176
+}
177
+
178
+func (c *conn) Close() error {
179
+	if c.closed {
180
+		panic("database/sql/driver: misuse of sqlite driver: multiple Close")
181
+	}
182
+	c.closed = true
183
+	rv := C.sqlite3_close(c.db)
184
+	c.db = nil
185
+	return c.error(rv)
186
+}
187
+
188
+func (c *conn) exec(cmd string) error {
189
+	cstring := C.CString(cmd)
190
+	defer C.free(unsafe.Pointer(cstring))
191
+	rv := C.sqlite3_exec(c.db, cstring, nil, nil, nil)
192
+	return c.error(rv)
193
+}
194
+
195
+func (c *conn) Begin() (driver.Tx, error) {
196
+	if c.tx {
197
+		panic("database/sql/driver: misuse of sqlite driver: multiple Tx")
198
+	}
199
+	if err := c.exec("BEGIN TRANSACTION"); err != nil {
200
+		return nil, err
201
+	}
202
+	c.tx = true
203
+	return &tx{c}, nil
204
+}
205
+
206
+type tx struct {
207
+	c *conn
208
+}
209
+
210
+func (t *tx) Commit() error {
211
+	if t.c == nil || !t.c.tx {
212
+		panic("database/sql/driver: misuse of sqlite driver: extra Commit")
213
+	}
214
+	t.c.tx = false
215
+	err := t.c.exec("COMMIT TRANSACTION")
216
+	t.c = nil
217
+	return err
218
+}
219
+
220
+func (t *tx) Rollback() error {
221
+	if t.c == nil || !t.c.tx {
222
+		panic("database/sql/driver: misuse of sqlite driver: extra Rollback")
223
+	}
224
+	t.c.tx = false
225
+	err := t.c.exec("ROLLBACK")
226
+	t.c = nil
227
+	return err
228
+}
229
+
230
+type stmt struct {
231
+	c        *conn
232
+	stmt     *C.sqlite3_stmt
233
+	err      error
234
+	t0       time.Time
235
+	sql      string
236
+	args     string
237
+	closed   bool
238
+	rows     bool
239
+	colnames []string
240
+	coltypes []string
241
+}
242
+
243
+func (s *stmt) Close() error {
244
+	if s.rows {
245
+		panic("database/sql/driver: misuse of sqlite driver: Close with active Rows")
246
+	}
247
+	if s.closed {
248
+		panic("database/sql/driver: misuse of sqlite driver: double Close of Stmt")
249
+	}
250
+	s.closed = true
251
+	rv := C.sqlite3_finalize(s.stmt)
252
+	if rv != 0 {
253
+		return s.c.error(rv)
254
+	}
255
+	return nil
256
+}
257
+
258
+func (s *stmt) NumInput() int {
259
+	if s.closed {
260
+		panic("database/sql/driver: misuse of sqlite driver: NumInput after Close")
261
+	}
262
+	return int(C.sqlite3_bind_parameter_count(s.stmt))
263
+}
264
+
265
+func (s *stmt) reset() error {
266
+	return s.c.error(C.sqlite3_reset(s.stmt))
267
+}
268
+
269
+func (s *stmt) start(args []driver.Value) error {
270
+	if err := s.reset(); err != nil {
271
+		return err
272
+	}
273
+
274
+	n := int(C.sqlite3_bind_parameter_count(s.stmt))
275
+	if n != len(args) {
276
+		return fmt.Errorf("incorrect argument count for command: have %d want %d", len(args), n)
277
+	}
278
+
279
+	for i, v := range args {
280
+		var str string
281
+		switch v := v.(type) {
282
+		case nil:
283
+			if rv := C.sqlite3_bind_null(s.stmt, C.int(i+1)); rv != 0 {
284
+				return s.c.error(rv)
285
+			}
286
+			continue
287
+
288
+		case float64:
289
+			if rv := C.sqlite3_bind_double(s.stmt, C.int(i+1), C.double(v)); rv != 0 {
290
+				return s.c.error(rv)
291
+			}
292
+			continue
293
+
294
+		case int64:
295
+			if rv := C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(v)); rv != 0 {
296
+				return s.c.error(rv)
297
+			}
298
+			continue
299
+
300
+		case []byte:
301
+			var p *byte
302
+			if len(v) > 0 {
303
+				p = &v[0]
304
+			}
305
+			if rv := C.my_bind_blob(s.stmt, C.int(i+1), unsafe.Pointer(p), C.int(len(v))); rv != 0 {
306
+				return s.c.error(rv)
307
+			}
308
+			continue
309
+
310
+		case bool:
311
+			var vi int64
312
+			if v {
313
+				vi = 1
314
+			}
315
+			if rv := C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(vi)); rv != 0 {
316
+				return s.c.error(rv)
317
+			}
318
+			continue
319
+
320
+		case time.Time:
321
+			str = v.UTC().Format(timefmt[0])
322
+
323
+		case string:
324
+			str = v
325
+
326
+		default:
327
+			str = fmt.Sprint(v)
328
+		}
329
+
330
+		cstr := C.CString(str)
331
+		rv := C.my_bind_text(s.stmt, C.int(i+1), cstr, C.int(len(str)))
332
+		C.free(unsafe.Pointer(cstr))
333
+		if rv != 0 {
334
+			return s.c.error(rv)
335
+		}
336
+	}
337
+
338
+	return nil
339
+}
340
+
341
+func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
342
+	if s.closed {
343
+		panic("database/sql/driver: misuse of sqlite driver: Exec after Close")
344
+	}
345
+	if s.rows {
346
+		panic("database/sql/driver: misuse of sqlite driver: Exec with active Rows")
347
+	}
348
+
349
+	err := s.start(args)
350
+	if err != nil {
351
+		return nil, err
352
+	}
353
+
354
+	rv := C.sqlite3_step(s.stmt)
355
+	if errno(rv) != stepDone {
356
+		if rv == 0 {
357
+			rv = 21 // errMisuse
358
+		}
359
+		return nil, s.c.error(rv)
360
+	}
361
+
362
+	id := int64(C.sqlite3_last_insert_rowid(s.c.db))
363
+	rows := int64(C.sqlite3_changes(s.c.db))
364
+	return &result{id, rows}, nil
365
+}
366
+
367
+func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
368
+	if s.closed {
369
+		panic("database/sql/driver: misuse of sqlite driver: Query after Close")
370
+	}
371
+	if s.rows {
372
+		panic("database/sql/driver: misuse of sqlite driver: Query with active Rows")
373
+	}
374
+
375
+	err := s.start(args)
376
+	if err != nil {
377
+		return nil, err
378
+	}
379
+
380
+	s.rows = true
381
+	if s.colnames == nil {
382
+		n := int64(C.sqlite3_column_count(s.stmt))
383
+		s.colnames = make([]string, n)
384
+		s.coltypes = make([]string, n)
385
+		for i := range s.colnames {
386
+			s.colnames[i] = C.GoString(C.sqlite3_column_name(s.stmt, C.int(i)))
387
+			s.coltypes[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(s.stmt, C.int(i))))
388
+		}
389
+	}
390
+	return &rows{s}, nil
391
+}
392
+
393
+type rows struct {
394
+	s *stmt
395
+}
396
+
397
+func (r *rows) Columns() []string {
398
+	if r.s == nil {
399
+		panic("database/sql/driver: misuse of sqlite driver: Columns of closed Rows")
400
+	}
401
+	return r.s.colnames
402
+}
403
+
404
+const maxslice = 1<<31 - 1
405
+
406
+var timefmt = []string{
407
+	"2006-01-02 15:04:05.999999999",
408
+	"2006-01-02T15:04:05.999999999",
409
+	"2006-01-02 15:04:05",
410
+	"2006-01-02T15:04:05",
411
+	"2006-01-02 15:04",
412
+	"2006-01-02T15:04",
413
+	"2006-01-02",
414
+}
415
+
416
+func (r *rows) Next(dst []driver.Value) error {
417
+	if r.s == nil {
418
+		panic("database/sql/driver: misuse of sqlite driver: Next of closed Rows")
419
+	}
420
+
421
+	rv := C.sqlite3_step(r.s.stmt)
422
+	if errno(rv) != stepRow {
423
+		if errno(rv) == stepDone {
424
+			return io.EOF
425
+		}
426
+		if rv == 0 {
427
+			rv = 21
428
+		}
429
+		return r.s.c.error(rv)
430
+	}
431
+
432
+	for i := range dst {
433
+		switch typ := C.sqlite3_column_type(r.s.stmt, C.int(i)); typ {
434
+		default:
435
+			return fmt.Errorf("unexpected sqlite3 column type %d", typ)
436
+		case C.SQLITE_INTEGER:
437
+			val := int64(C.sqlite3_column_int64(r.s.stmt, C.int(i)))
438
+			switch r.s.coltypes[i] {
439
+			case "timestamp", "datetime":
440
+				dst[i] = time.Unix(val, 0).UTC()
441
+			case "boolean":
442
+				dst[i] = val > 0
443
+			default:
444
+				dst[i] = val
445
+			}
446
+
447
+		case C.SQLITE_FLOAT:
448
+			dst[i] = float64(C.sqlite3_column_double(r.s.stmt, C.int(i)))
449
+
450
+		case C.SQLITE_BLOB, C.SQLITE_TEXT:
451
+			n := int(C.sqlite3_column_bytes(r.s.stmt, C.int(i)))
452
+			var b []byte
453
+			if n > 0 {
454
+				p := C.sqlite3_column_blob(r.s.stmt, C.int(i))
455
+				b = (*[maxslice]byte)(unsafe.Pointer(p))[:n]
456
+			}
457
+			dst[i] = b
458
+			switch r.s.coltypes[i] {
459
+			case "timestamp", "datetime":
460
+				dst[i] = time.Time{}
461
+				s := string(b)
462
+				for _, f := range timefmt {
463
+					if t, err := time.Parse(f, s); err == nil {
464
+						dst[i] = t
465
+						break
466
+					}
467
+				}
468
+			}
469
+
470
+		case C.SQLITE_NULL:
471
+			dst[i] = nil
472
+		}
473
+	}
474
+	return nil
475
+}
476
+
477
+func (r *rows) Close() error {
478
+	if r.s == nil {
479
+		panic("database/sql/driver: misuse of sqlite driver: Close of closed Rows")
480
+	}
481
+	r.s.rows = false
482
+	r.s = nil
483
+	return nil
484
+}
485
+
486
+type result struct {
487
+	id   int64
488
+	rows int64
489
+}
490
+
491
+func (r *result) LastInsertId() (int64, error) {
492
+	return r.id, nil
493
+}
494
+
495
+func (r *result) RowsAffected() (int64, error) {
496
+	return r.rows, nil
497
+}