Browse code

Initial commit to add links and inject env

Michael Crosby authored on 2013/09/14 07:25:12
Showing 8 changed files
... ...
@@ -957,6 +957,20 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
957 957
 	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
958 958
 }
959 959
 
960
+func getLinksJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
961
+	out := []APILink{}
962
+	name := r.FormValue("name")
963
+
964
+	links := srv.runtime.links.Get(name)
965
+	for _, l := range links {
966
+		out = append(out, APILink{l.To, l.From, l.Addr, l.Alias})
967
+	}
968
+
969
+	w.Header().Add("Content-Type", "application/json")
970
+	writeJSON(w, http.StatusOK, out)
971
+	return nil
972
+}
973
+
960 974
 func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc) http.HandlerFunc {
961 975
 	return func(w http.ResponseWriter, r *http.Request) {
962 976
 		// log the request
... ...
@@ -1012,6 +1026,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
1012 1012
 			"/containers/{name:.*}/json":      getContainersByName,
1013 1013
 			"/containers/{name:.*}/top":       getContainersTop,
1014 1014
 			"/containers/{name:.*}/attach/ws": wsContainersAttach,
1015
+			"/links/json":                     getLinksJSON,
1015 1016
 		},
1016 1017
 		"POST": {
1017 1018
 			"/auth":                         postAuth,
... ...
@@ -120,3 +120,10 @@ type APICopy struct {
120 120
 	Resource string
121 121
 	HostPath string
122 122
 }
123
+
124
+type APILink struct {
125
+	To    string
126
+	From  string
127
+	Addr  string
128
+	Alias string
129
+}
... ...
@@ -1112,6 +1112,37 @@ func (cli *DockerCli) CmdPs(args ...string) error {
1112 1112
 	return nil
1113 1113
 }
1114 1114
 
1115
+func (cli *DockerCli) CmdLink(args ...string) error {
1116
+	cmd := Subcmd("link", "[OPTIONS] CONTAINER", "Get the links for a container")
1117
+
1118
+	if err := cmd.Parse(args); err != nil {
1119
+		return err
1120
+	}
1121
+
1122
+	v := url.Values{}
1123
+	v.Set("name", cmd.Arg(0))
1124
+
1125
+	body, _, err := cli.call("GET", "/links/json?"+v.Encode(), nil)
1126
+	if err != nil {
1127
+		return err
1128
+	}
1129
+
1130
+	var links []APILink
1131
+	if err := json.Unmarshal(body, &links); err != nil {
1132
+		return err
1133
+	}
1134
+	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
1135
+
1136
+	fmt.Fprintf(w, "FROM\tTO\tADDRESS\tALIAS")
1137
+	fmt.Fprintf(w, "\n")
1138
+	for _, l := range links {
1139
+		fmt.Fprintf(w, "%s\t%s\t%s\t%s", l.From, l.To, l.Addr, l.Alias)
1140
+		fmt.Fprintf(w, "\n")
1141
+	}
1142
+	w.Flush()
1143
+	return nil
1144
+}
1145
+
1115 1146
 func (cli *DockerCli) CmdCommit(args ...string) error {
1116 1147
 	cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes")
1117 1148
 	flComment := cmd.String("m", "", "Commit message")
... ...
@@ -91,6 +91,7 @@ type HostConfig struct {
91 91
 	ContainerIDFile string
92 92
 	LxcConf         []KeyValuePair
93 93
 	PortBindings    map[Port][]PortBinding
94
+	Links           []Link
94 95
 }
95 96
 
96 97
 type BindMap struct {
... ...
@@ -179,6 +180,9 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
179 179
 	var flLxcOpts ListOpts
180 180
 	cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
181 181
 
182
+	var flLinks ListOpts
183
+	cmd.Var(&flLinks, "link", "Add link to another container (containerid:port:alias)")
184
+
182 185
 	if err := cmd.Parse(args); err != nil {
183 186
 		return nil, nil, cmd, err
184 187
 	}
... ...
@@ -250,6 +254,22 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
250 250
 		return nil, nil, cmd, err
251 251
 	}
252 252
 
253
+	// Merge in exposed ports to the map of published ports
254
+	for _, e := range flExpose {
255
+		if strings.Contains(e, ":") {
256
+			return nil, nil, cmd, fmt.Errorf("Invalid port format for -expose: %s", e)
257
+		}
258
+		p := NewPort(splitProtoPort(e))
259
+		if _, exists := ports[p]; !exists {
260
+			ports[p] = struct{}{}
261
+		}
262
+	}
263
+
264
+	links, err := parseLinks(flLinks)
265
+	if err != nil {
266
+		return nil, nil, cmd, err
267
+	}
268
+
253 269
 	config := &Config{
254 270
 		Hostname:        *flHostname,
255 271
 		Domainname:      domainname,
... ...
@@ -280,6 +300,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
280 280
 		ContainerIDFile: *flContainerIDFile,
281 281
 		LxcConf:         lxcConf,
282 282
 		PortBindings:    portBindings,
283
+		Links:           links,
283 284
 	}
284 285
 
285 286
 	if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit {
... ...
@@ -805,6 +826,31 @@ func (container *Container) Start(hostConfig *HostConfig) error {
805 805
 		"-e", "container=lxc",
806 806
 		"-e", "HOSTNAME="+container.Config.Hostname,
807 807
 	)
808
+
809
+	if hostConfig != nil && hostConfig.Links != nil {
810
+		runtime := container.runtime
811
+		for _, l := range hostConfig.Links {
812
+			linkedContainer := runtime.Get(l.From)
813
+			if linkedContainer == nil {
814
+				return fmt.Errorf("Cannot locate container for link: %s AS %s", l.From, l.Alias)
815
+			}
816
+			if !linkedContainer.State.Running {
817
+				return fmt.Errorf("Cannot link a non running container: %s AS %s", l.From, l.Alias)
818
+			}
819
+
820
+			// Check for linkedContainer exposed ports
821
+			//
822
+			// Hide ports that are not requested
823
+
824
+			l.To = utils.TruncateID(container.ID)
825
+			l.Addr = fmt.Sprintf("%s:%s", linkedContainer.NetworkSettings.IPAddress, l.Port)
826
+			if err := runtime.links.RegisterLink(l); err != nil {
827
+				return nil
828
+			}
829
+			params = append(params, "-e", l.ToEnv())
830
+		}
831
+	}
832
+
808 833
 	if container.Config.WorkingDir != "" {
809 834
 		workingDir := path.Clean(container.Config.WorkingDir)
810 835
 		utils.Debugf("[working dir] working dir is %s", workingDir)
811 836
new file mode 100644
... ...
@@ -0,0 +1,59 @@
0
+package docker
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/utils"
5
+	"strings"
6
+)
7
+
8
+type Link struct {
9
+	From  string
10
+	To    string
11
+	Addr  string
12
+	Alias string
13
+	Port  string
14
+}
15
+
16
+type LinkRepository struct {
17
+	links map[string]Link
18
+}
19
+
20
+func (l *Link) ToEnv() string {
21
+	return fmt.Sprintf("%s_ADDR=%s", strings.ToUpper(l.Alias), l.Addr)
22
+}
23
+
24
+func NewLinkRepository(root string) (*LinkRepository, error) {
25
+	r := &LinkRepository{make(map[string]Link)}
26
+	return r, nil
27
+}
28
+
29
+// Return all links for a container
30
+func (l *LinkRepository) Get(id string) []Link {
31
+	id = strings.Trim(strings.ToLower(id), "")
32
+	out := []Link{}
33
+	for _, link := range l.links {
34
+		if link.To == id || link.From == id {
35
+			out = append(out, link)
36
+		}
37
+	}
38
+	return out
39
+}
40
+
41
+// Returns the link for a current alias
42
+func (l *LinkRepository) GetByAlias(alias string) (Link, error) {
43
+	link, exists := l.links[alias]
44
+	if !exists {
45
+		return link, fmt.Errorf("Link does not exist for alias: %s", alias)
46
+	}
47
+	return link, nil
48
+}
49
+
50
+// Create a new link with a unique alias
51
+func (l *LinkRepository) RegisterLink(link Link) error {
52
+	if _, exists := l.links[link.Alias]; exists {
53
+		return fmt.Errorf("A link for %s already exists", link.Alias)
54
+	}
55
+	utils.Debugf("Registering link: %v", link)
56
+	l.links[link.Alias] = link
57
+	return nil
58
+}
... ...
@@ -35,6 +35,7 @@ type Runtime struct {
35 35
 	volumes        *Graph
36 36
 	srv            *Server
37 37
 	config         *DaemonConfig
38
+	links          *LinkRepository
38 39
 }
39 40
 
40 41
 var sysInitPath string
... ...
@@ -452,6 +453,11 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) {
452 452
 		}
453 453
 	}
454 454
 	runtime.UpdateCapabilities(false)
455
+	links, err := NewLinkRepository("")
456
+	if err != nil {
457
+		return nil, err
458
+	}
459
+	runtime.links = links
455 460
 	return runtime, nil
456 461
 }
457 462
 
... ...
@@ -281,3 +281,30 @@ func migratePortMappings(config *Config) error {
281 281
 	}
282 282
 	return nil
283 283
 }
284
+
285
+// Links come in the format of
286
+// id:port:alias
287
+func parseLink(rawLink string) (Link, error) {
288
+	parts, err := utils.PartParser("id:port:alias", rawLink)
289
+	if err != nil {
290
+		return Link{}, err
291
+	}
292
+
293
+	return Link{
294
+		From:  parts["id"],
295
+		Alias: parts["alias"],
296
+		Port:  parts["port"],
297
+	}, nil
298
+}
299
+
300
+func parseLinks(rawLinks []string) ([]Link, error) {
301
+	out := make([]Link, len(rawLinks))
302
+	for i, l := range rawLinks {
303
+		link, err := parseLink(l)
304
+		if err != nil {
305
+			return nil, err
306
+		}
307
+		out[i] = link
308
+	}
309
+	return out, nil
310
+}
284 311
new file mode 100644
... ...
@@ -0,0 +1,11 @@
0
+package docker
1
+
2
+// Returns a new virtual container for interfacing with the host interfaces
3
+func NewHostContainer() (*Container, error) {
4
+	return nil, nil
5
+}
6
+
7
+// Returns a new virutal container for interfacing with the docker daemon
8
+func NewDockerContainer() (*Container, error) {
9
+	return nil, nil
10
+}