diff --git a/common/docker/client.go b/common/docker/client.go index 1f2ee0df..6a4eaff1 100644 --- a/common/docker/client.go +++ b/common/docker/client.go @@ -7,6 +7,7 @@ import ( // An observer for container events type ContainerObserver interface { + ContainerStarted(ident string) ContainerDied(ident string) } @@ -22,12 +23,26 @@ func NewClient(apiPath string) (*Client, error) { } client := &Client{dc} - env, err := client.Version() + return client, client.checkWorking(apiPath) +} + +func NewVersionedClient(apiPath string, apiVersionString string) (*Client, error) { + dc, err := docker.NewVersionedClient(apiPath, apiVersionString) if err != nil { return nil, err } + client := &Client{dc} + + return client, client.checkWorking(apiPath) +} + +func (c *Client) checkWorking(apiPath string) error { + env, err := c.Version() + if err != nil { + return err + } Log.Infof("[docker] Using Docker API on %s: %v", apiPath, env) - return client, nil + return nil } // AddObserver adds an observer for docker events @@ -41,6 +56,9 @@ func (c *Client) AddObserver(ob ContainerObserver) error { go func() { for event := range events { switch event.Status { + case "start": + id := event.ID + ob.ContainerStarted(id) case "die": id := event.ID ob.ContainerDied(id) diff --git a/ipam/allocator.go b/ipam/allocator.go index 28f2e449..e3765aca 100644 --- a/ipam/allocator.go +++ b/ipam/allocator.go @@ -226,6 +226,8 @@ func (alloc *Allocator) ContainerDied(ident string) { } } +func (alloc *Allocator) ContainerStarted(ident string) {} + // Delete (Sync) - release all IP addresses for container with given name func (alloc *Allocator) Delete(ident string) error { errChan := make(chan error) diff --git a/nameserver/nameserver.go b/nameserver/nameserver.go index dd6bcd6b..356e61e0 100644 --- a/nameserver/nameserver.go +++ b/nameserver/nameserver.go @@ -130,6 +130,8 @@ func (n *Nameserver) ReverseLookup(ip address.Address) (string, error) { return match.Hostname, nil } +func (n *Nameserver) ContainerStarted(ident string) {} + func (n *Nameserver) ContainerDied(ident string) { n.Lock() entries := n.entries.tombstone(n.ourName, func(e *Entry) bool { diff --git a/proxy/proxy.go b/proxy/proxy.go index beb34915..88ec5085 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -11,8 +11,9 @@ import ( "strings" "syscall" - "github.com/fsouza/go-dockerclient" + docker "github.com/fsouza/go-dockerclient" . "github.com/weaveworks/weave/common" + weavedocker "github.com/weaveworks/weave/common/docker" ) const ( @@ -25,7 +26,6 @@ const ( var ( containerCreateRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/create$") - containerStartRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/[^/]*/(re)?start$") execCreateRegexp = regexp.MustCompile("^(/v[0-9\\.]*)?/containers/[^/]*/exec$") ErrWeaveCIDRNone = errors.New("the container was created with the '-e WEAVE_CIDR=none' option") @@ -64,11 +64,11 @@ func NewProxy(c Config) (*Proxy, error) { // to insulate ourselves from breaking changes to the API, as // happened in 1.20 (Docker 1.8.0) when the presentation of // volumes changed in `inspect`. - client, err := docker.NewVersionedClient(dockerSockUnix, "1.15") + client, err := weavedocker.NewVersionedClient(dockerSockUnix, "1.15") if err != nil { return nil, err } - p.client = client + p.client = client.Client if !p.WithoutDNS { dockerBridgeIP, stderr, err := callWeave("docker-bridge-ip") @@ -88,6 +88,8 @@ func NewProxy(c Config) (*Proxy, error) { return nil, err } + client.AddObserver(p) + return p, nil } @@ -121,8 +123,6 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch { case containerCreateRegexp.MatchString(path): i = &createContainerInterceptor{proxy} - case containerStartRegexp.MatchString(path): - i = &startContainerInterceptor{proxy} case execCreateRegexp.MatchString(path): i = &createExecInterceptor{proxy} default: @@ -224,6 +224,45 @@ func (proxy *Proxy) listen(protoAndAddr string) (net.Listener, string, error) { return listener, fmt.Sprintf("%s://%s", proto, addr), nil } +// weavedocker.ContainerObserver interface +func (proxy *Proxy) ContainerStarted(ident string) { + container, err := proxy.client.InspectContainer(ident) + if err != nil { + Log.Warningf("Error inspecting container %s: %v", ident, err) + return + } + // If this was a container we modified the entrypoint for, attach it to the network + if len(container.Config.Entrypoint) > 0 && container.Config.Entrypoint[0] == weaveWaitEntrypoint[0] { + proxy.attach(container) + } +} + +func (proxy *Proxy) ContainerDied(ident string) { +} + +func (proxy *Proxy) attach(container *docker.Container) error { + cidrs, err := proxy.weaveCIDRsFromConfig(container.Config, container.HostConfig) + if err != nil { + Log.Infof("Leaving container %s alone because %s", container.ID, err) + return nil + } + Log.Infof("Attaching container %s with WEAVE_CIDR \"%s\" to weave network", container.ID, strings.Join(cidrs, " ")) + args := []string{"attach"} + args = append(args, cidrs...) + if !proxy.NoRewriteHosts { + args = append(args, "--rewrite-hosts") + } + args = append(args, "--or-die", container.ID) + if _, stderr, err := callWeave(args...); err != nil { + Log.Warningf("Attaching container %s to weave network failed: %s", container.ID, string(stderr)) + return errors.New(string(stderr)) + } else if len(stderr) > 0 { + Log.Warningf("Attaching container %s to weave network: %s", container.ID, string(stderr)) + } + + return nil +} + func (proxy *Proxy) weaveCIDRsFromConfig(config *docker.Config, hostConfig *docker.HostConfig) ([]string, error) { netMode := "" if hostConfig != nil { diff --git a/proxy/start_container_interceptor.go b/proxy/start_container_interceptor.go deleted file mode 100644 index 267f68c4..00000000 --- a/proxy/start_container_interceptor.go +++ /dev/null @@ -1,47 +0,0 @@ -package proxy - -import ( - "errors" - "net/http" - "strings" - - . "github.com/weaveworks/weave/common" -) - -type startContainerInterceptor struct{ proxy *Proxy } - -func (i *startContainerInterceptor) InterceptRequest(r *http.Request) error { - return nil -} - -func (i *startContainerInterceptor) InterceptResponse(r *http.Response) error { - if r.StatusCode < 200 || r.StatusCode >= 300 { // Docker didn't do the start - return nil - } - - container, err := inspectContainerInPath(i.proxy.client, r.Request.URL.Path) - if err != nil { - return err - } - - cidrs, err := i.proxy.weaveCIDRsFromConfig(container.Config, container.HostConfig) - if err != nil { - Log.Infof("Leaving container %s alone because %s", container.ID, err) - return nil - } - Log.Infof("Attaching container %s with WEAVE_CIDR \"%s\" to weave network", container.ID, strings.Join(cidrs, " ")) - args := []string{"attach"} - args = append(args, cidrs...) - if !i.proxy.NoRewriteHosts { - args = append(args, "--rewrite-hosts") - } - args = append(args, "--or-die", container.ID) - if _, stderr, err := callWeave(args...); err != nil { - Log.Warningf("Attaching container %s to weave network failed: %s", container.ID, string(stderr)) - return errors.New(string(stderr)) - } else if len(stderr) > 0 { - Log.Warningf("Attaching container %s to weave network: %s", container.ID, string(stderr)) - } - - return nil -} diff --git a/site/features.md b/site/features.md index 189f9365..84e295fd 100644 --- a/site/features.md +++ b/site/features.md @@ -64,6 +64,10 @@ or [remote API](https://docs.docker.com/reference/api/docker_remote_api/) are attached to the weave network before they begin execution. +Containers started in this way that subsequently restart, either by an +explicit `docker restart` command or by Docker restart policy, are +re-attached to the weave network by the weave Docker API proxy. + ### Address allocation Containers are automatically allocated an IP address that is unique @@ -248,6 +252,9 @@ invocation: host1$ weave detach net:default net:10.2.2.0/24 net:10.2.3.0/24 $C 10.2.1.3 10.2.2.3 10.2.3.1 +Note that addresses added by dynamic attachment are not re-attached +if the container restarts. + ### Security In order to connect containers across untrusted networks, weave peers