From 30e4a8e524a54fdd64ab4da053948b1b9393788d Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Fri, 13 Sep 2013 22:25:12 +0000 Subject: [PATCH] Initial commit to add links and inject env --- api.go | 15 +++++++++++ api_params.go | 7 +++++ commands.go | 31 +++++++++++++++++++++++ container.go | 46 +++++++++++++++++++++++++++++++++ links.go | 59 +++++++++++++++++++++++++++++++++++++++++++ runtime.go | 6 +++++ utils.go | 27 ++++++++++++++++++++ virtual-containers.go | 11 ++++++++ 8 files changed, 202 insertions(+) create mode 100644 links.go create mode 100644 virtual-containers.go diff --git a/api.go b/api.go index f7587fc83..ee7fe3d89 100644 --- a/api.go +++ b/api.go @@ -957,6 +957,20 @@ func writeCorsHeaders(w http.ResponseWriter, r *http.Request) { w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS") } +func getLinksJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error { + out := []APILink{} + name := r.FormValue("name") + + links := srv.runtime.links.Get(name) + for _, l := range links { + out = append(out, APILink{l.To, l.From, l.Addr, l.Alias}) + } + + w.Header().Add("Content-Type", "application/json") + writeJSON(w, http.StatusOK, out) + return nil +} + func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // log the request @@ -1012,6 +1026,7 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) { "/containers/{name:.*}/json": getContainersByName, "/containers/{name:.*}/top": getContainersTop, "/containers/{name:.*}/attach/ws": wsContainersAttach, + "/links/json": getLinksJSON, }, "POST": { "/auth": postAuth, diff --git a/api_params.go b/api_params.go index 3e5acf5b5..b3540ba70 100644 --- a/api_params.go +++ b/api_params.go @@ -120,3 +120,10 @@ type APICopy struct { Resource string HostPath string } + +type APILink struct { + To string + From string + Addr string + Alias string +} diff --git a/commands.go b/commands.go index 768501364..6f527a99f 100644 --- a/commands.go +++ b/commands.go @@ -1112,6 +1112,37 @@ func (cli *DockerCli) CmdPs(args ...string) error { return nil } +func (cli *DockerCli) CmdLink(args ...string) error { + cmd := Subcmd("link", "[OPTIONS] CONTAINER", "Get the links for a container") + + if err := cmd.Parse(args); err != nil { + return err + } + + v := url.Values{} + v.Set("name", cmd.Arg(0)) + + body, _, err := cli.call("GET", "/links/json?"+v.Encode(), nil) + if err != nil { + return err + } + + var links []APILink + if err := json.Unmarshal(body, &links); err != nil { + return err + } + w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) + + fmt.Fprintf(w, "FROM\tTO\tADDRESS\tALIAS") + fmt.Fprintf(w, "\n") + for _, l := range links { + fmt.Fprintf(w, "%s\t%s\t%s\t%s", l.From, l.To, l.Addr, l.Alias) + fmt.Fprintf(w, "\n") + } + w.Flush() + return nil +} + func (cli *DockerCli) CmdCommit(args ...string) error { cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes") flComment := cmd.String("m", "", "Commit message") diff --git a/container.go b/container.go index c5d37a655..6e85bf006 100644 --- a/container.go +++ b/container.go @@ -91,6 +91,7 @@ type HostConfig struct { ContainerIDFile string LxcConf []KeyValuePair PortBindings map[Port][]PortBinding + Links []Link } type BindMap struct { @@ -179,6 +180,9 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, var flLxcOpts ListOpts cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"") + var flLinks ListOpts + cmd.Var(&flLinks, "link", "Add link to another container (containerid:port:alias)") + if err := cmd.Parse(args); err != nil { return nil, nil, cmd, err } @@ -250,6 +254,22 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, return nil, nil, cmd, err } + // Merge in exposed ports to the map of published ports + for _, e := range flExpose { + if strings.Contains(e, ":") { + return nil, nil, cmd, fmt.Errorf("Invalid port format for -expose: %s", e) + } + p := NewPort(splitProtoPort(e)) + if _, exists := ports[p]; !exists { + ports[p] = struct{}{} + } + } + + links, err := parseLinks(flLinks) + if err != nil { + return nil, nil, cmd, err + } + config := &Config{ Hostname: *flHostname, Domainname: domainname, @@ -280,6 +300,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig, ContainerIDFile: *flContainerIDFile, LxcConf: lxcConf, PortBindings: portBindings, + Links: links, } if capabilities != nil && *flMemory > 0 && !capabilities.SwapLimit { @@ -805,6 +826,31 @@ func (container *Container) Start(hostConfig *HostConfig) error { "-e", "container=lxc", "-e", "HOSTNAME="+container.Config.Hostname, ) + + if hostConfig != nil && hostConfig.Links != nil { + runtime := container.runtime + for _, l := range hostConfig.Links { + linkedContainer := runtime.Get(l.From) + if linkedContainer == nil { + return fmt.Errorf("Cannot locate container for link: %s AS %s", l.From, l.Alias) + } + if !linkedContainer.State.Running { + return fmt.Errorf("Cannot link a non running container: %s AS %s", l.From, l.Alias) + } + + // Check for linkedContainer exposed ports + // + // Hide ports that are not requested + + l.To = utils.TruncateID(container.ID) + l.Addr = fmt.Sprintf("%s:%s", linkedContainer.NetworkSettings.IPAddress, l.Port) + if err := runtime.links.RegisterLink(l); err != nil { + return nil + } + params = append(params, "-e", l.ToEnv()) + } + } + if container.Config.WorkingDir != "" { workingDir := path.Clean(container.Config.WorkingDir) utils.Debugf("[working dir] working dir is %s", workingDir) diff --git a/links.go b/links.go new file mode 100644 index 000000000..cf716d6ae --- /dev/null +++ b/links.go @@ -0,0 +1,59 @@ +package docker + +import ( + "fmt" + "github.com/dotcloud/docker/utils" + "strings" +) + +type Link struct { + From string + To string + Addr string + Alias string + Port string +} + +type LinkRepository struct { + links map[string]Link +} + +func (l *Link) ToEnv() string { + return fmt.Sprintf("%s_ADDR=%s", strings.ToUpper(l.Alias), l.Addr) +} + +func NewLinkRepository(root string) (*LinkRepository, error) { + r := &LinkRepository{make(map[string]Link)} + return r, nil +} + +// Return all links for a container +func (l *LinkRepository) Get(id string) []Link { + id = strings.Trim(strings.ToLower(id), "") + out := []Link{} + for _, link := range l.links { + if link.To == id || link.From == id { + out = append(out, link) + } + } + return out +} + +// Returns the link for a current alias +func (l *LinkRepository) GetByAlias(alias string) (Link, error) { + link, exists := l.links[alias] + if !exists { + return link, fmt.Errorf("Link does not exist for alias: %s", alias) + } + return link, nil +} + +// Create a new link with a unique alias +func (l *LinkRepository) RegisterLink(link Link) error { + if _, exists := l.links[link.Alias]; exists { + return fmt.Errorf("A link for %s already exists", link.Alias) + } + utils.Debugf("Registering link: %v", link) + l.links[link.Alias] = link + return nil +} diff --git a/runtime.go b/runtime.go index 0e04248ca..0225a5bd2 100644 --- a/runtime.go +++ b/runtime.go @@ -35,6 +35,7 @@ type Runtime struct { volumes *Graph srv *Server config *DaemonConfig + links *LinkRepository } var sysInitPath string @@ -452,6 +453,11 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) { } } runtime.UpdateCapabilities(false) + links, err := NewLinkRepository("") + if err != nil { + return nil, err + } + runtime.links = links return runtime, nil } diff --git a/utils.go b/utils.go index c42718000..c0548e45b 100644 --- a/utils.go +++ b/utils.go @@ -281,3 +281,30 @@ func migratePortMappings(config *Config) error { } return nil } + +// Links come in the format of +// id:port:alias +func parseLink(rawLink string) (Link, error) { + parts, err := utils.PartParser("id:port:alias", rawLink) + if err != nil { + return Link{}, err + } + + return Link{ + From: parts["id"], + Alias: parts["alias"], + Port: parts["port"], + }, nil +} + +func parseLinks(rawLinks []string) ([]Link, error) { + out := make([]Link, len(rawLinks)) + for i, l := range rawLinks { + link, err := parseLink(l) + if err != nil { + return nil, err + } + out[i] = link + } + return out, nil +} diff --git a/virtual-containers.go b/virtual-containers.go new file mode 100644 index 000000000..3b5b7d781 --- /dev/null +++ b/virtual-containers.go @@ -0,0 +1,11 @@ +package docker + +// Returns a new virtual container for interfacing with the host interfaces +func NewHostContainer() (*Container, error) { + return nil, nil +} + +// Returns a new virutal container for interfacing with the docker daemon +func NewDockerContainer() (*Container, error) { + return nil, nil +}