... | ... |
@@ -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, |
... | ... |
@@ -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 |
+} |