Merge pull request #655 from paulbellamy/proxy-minimal
Minimal implementation of the proxy closes #47, closes #230, closes #251, closes #400.
This commit is contained in:
commit
8b66b8ae47
22 files changed
+787
-7
No files matched your search
@@ -32,7 +32,9 @@ coverage.html
|
||||
# Project specific
|
||||
weaver/weaver
|
||||
weavedns/weavedns
|
||||
weaveproxy/weaveproxy
|
||||
sigproxy/sigproxy
|
||||
weavewait/weavewait
|
||||
tools/bin
|
||||
tools/build
|
||||
releases
|
||||
@@ -40,6 +42,8 @@ weave.tar
|
||||
weavedns.tar
|
||||
weaveexec.tar
|
||||
weaveexec/weave
|
||||
weaveexec/weaveproxy
|
||||
weaveexec/weavewait
|
||||
weaveexec/sigproxy
|
||||
weaveexec/docker*.tgz
|
||||
Vagrantfile.local
|
||||
@@ -10,7 +10,9 @@ DOCKERHUB_USER=weaveworks
|
||||
WEAVE_VERSION=git-$(shell git rev-parse --short=12 HEAD)
|
||||
WEAVER_EXE=weaver/weaver
|
||||
WEAVEDNS_EXE=weavedns/weavedns
|
||||
WEAVEPROXY_EXE=weaveproxy/weaveproxy
|
||||
SIGPROXY_EXE=sigproxy/sigproxy
|
||||
WEAVEWAIT_EXE=weavewait/weavewait
|
||||
WEAVER_IMAGE=$(DOCKERHUB_USER)/weave
|
||||
WEAVEDNS_IMAGE=$(DOCKERHUB_USER)/weavedns
|
||||
WEAVEEXEC_IMAGE=$(DOCKERHUB_USER)/weaveexec
|
||||
@@ -27,9 +29,9 @@ all: $(WEAVER_EXPORT) $(WEAVEDNS_EXPORT) $(WEAVEEXEC_EXPORT)
|
||||
travis: $(WEAVER_EXE) $(WEAVEDNS_EXE)
|
||||
|
||||
update:
|
||||
go get -u -f -v -tags -netgo ./$(dir $(WEAVER_EXE)) ./$(dir $(WEAVEDNS_EXE)) ./$(dir $(SIGPROXY_EXE))
|
||||
go get -u -f -v -tags -netgo ./$(dir $(WEAVER_EXE)) ./$(dir $(WEAVEDNS_EXE)) ./$(dir $(SIGPROXY_EXE)) ./$(dir $(WEAVEPROXY_EXE))
|
||||
|
||||
$(WEAVER_EXE) $(WEAVEDNS_EXE): common/*.go
|
||||
$(WEAVER_EXE) $(WEAVEDNS_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE): common/*.go
|
||||
go get -tags netgo ./$(@D)
|
||||
go build -ldflags "-extldflags \"-static\" -X main.version $(WEAVE_VERSION)" -tags netgo -o $@ ./$(@D)
|
||||
@strings $@ | grep cgo_stub\\\.go >/dev/null || { \
|
||||
@@ -43,6 +45,8 @@ $(WEAVER_EXE) $(WEAVEDNS_EXE): common/*.go
|
||||
|
||||
$(WEAVER_EXE): router/*.go ipam/*.go ipam/*/*.go weaver/main.go
|
||||
$(WEAVEDNS_EXE): nameserver/*.go weavedns/main.go
|
||||
$(WEAVEPROXY_EXE): proxy/*.go weaveproxy/main.go
|
||||
$(WEAVEWAIT_EXE): weavewait/*.go weavewait/main.go
|
||||
|
||||
# Sigproxy needs separate rule as it fails the netgo check in the main
|
||||
# build stanza due to not importing net package
|
||||
@@ -57,9 +61,11 @@ $(WEAVEDNS_EXPORT): weavedns/Dockerfile $(WEAVEDNS_EXE)
|
||||
$(SUDO) docker build -t $(WEAVEDNS_IMAGE) weavedns
|
||||
$(SUDO) docker save $(WEAVEDNS_IMAGE):latest > $@
|
||||
|
||||
$(WEAVEEXEC_EXPORT): weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE)
|
||||
$(WEAVEEXEC_EXPORT): weaveexec/Dockerfile $(DOCKER_DISTRIB) weave $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE)
|
||||
cp weave weaveexec/weave
|
||||
cp sigproxy/sigproxy weaveexec/sigproxy
|
||||
cp $(SIGPROXY_EXE) weaveexec/sigproxy
|
||||
cp $(WEAVEWAIT_EXE) weaveexec/weavewait
|
||||
cp $(WEAVEPROXY_EXE) weaveexec/weaveproxy
|
||||
cp $(DOCKER_DISTRIB) weaveexec/docker.tgz
|
||||
$(SUDO) docker build -t $(WEAVEEXEC_IMAGE) weaveexec
|
||||
$(SUDO) docker save $(WEAVEEXEC_IMAGE):latest > $@
|
||||
@@ -92,7 +98,7 @@ publish: $(PUBLISH)
|
||||
|
||||
clean:
|
||||
-$(SUDO) docker rmi $(WEAVER_IMAGE) $(WEAVEDNS_IMAGE) $(WEAVEEXEC_IMAGE)
|
||||
rm -f $(WEAVER_EXE) $(WEAVEDNS_EXE) $(SIGPROXY_EXE) $(WEAVER_EXPORT) $(WEAVEDNS_EXPORT) $(WEAVEEXEC_EXPORT)
|
||||
rm -f $(WEAVER_EXE) $(WEAVEDNS_EXE) $(SIGPROXY_EXE) $(WEAVEPROXY_EXE) $(WEAVEWAIT_EXE) $(WEAVER_EXPORT) $(WEAVEDNS_EXPORT) $(WEAVEEXEC_EXPORT)
|
||||
|
||||
build:
|
||||
$(SUDO) go clean -i net
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
. "github.com/weaveworks/weave/common"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
interceptor
|
||||
Dial func() (net.Conn, error)
|
||||
}
|
||||
|
||||
func newClient(dial func() (net.Conn, error), i interceptor) *client {
|
||||
return &client{
|
||||
interceptor: i,
|
||||
Dial: dial,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
err := c.InterceptRequest(r)
|
||||
if err == docker.ErrNoSuchImage {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to intercept request", http.StatusInternalServerError)
|
||||
Warning.Print("Error intercepting request: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := c.Dial()
|
||||
if err != nil {
|
||||
http.Error(w, "Could not connect to target", http.StatusInternalServerError)
|
||||
Warning.Print(err)
|
||||
return
|
||||
}
|
||||
client := httputil.NewClientConn(conn, nil)
|
||||
defer client.Close()
|
||||
|
||||
resp, err := client.Do(r)
|
||||
if err != nil && err != httputil.ErrPersistEOF {
|
||||
http.Error(w, fmt.Sprintf("Could not make request to target: %v", err), http.StatusInternalServerError)
|
||||
Warning.Print("Error forwarding request: ", err)
|
||||
return
|
||||
}
|
||||
err = c.InterceptResponse(resp)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to intercept response", http.StatusInternalServerError)
|
||||
Warning.Print("Error intercepting response: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
hdr := w.Header()
|
||||
for k, vs := range resp.Header {
|
||||
for _, v := range vs {
|
||||
hdr.Add(k, v)
|
||||
}
|
||||
}
|
||||
Debug.Printf("Response from target: %s %v", resp.Status, w.Header())
|
||||
|
||||
if resp.Header.Get("Content-Type") == "application/vnd.docker.raw-stream" {
|
||||
doRawStream(w, resp, client)
|
||||
} else if resp.TransferEncoding != nil && resp.TransferEncoding[0] == "chunked" {
|
||||
doChunkedResponse(w, resp, client)
|
||||
} else {
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
if _, err := io.Copy(w, resp.Body); err != nil {
|
||||
Warning.Print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func doRawStream(w http.ResponseWriter, resp *http.Response, client *httputil.ClientConn) {
|
||||
down, downBuf, up, rem, err := hijack(w, client)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to hijack connection for raw stream mode", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer down.Close()
|
||||
defer up.Close()
|
||||
|
||||
if _, err := down.Write([]byte("HTTP/1.1 200 OK\n")); err != nil {
|
||||
Warning.Print(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := resp.Header.Write(down); err != nil {
|
||||
Warning.Print(err)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := down.Write([]byte("\n")); err != nil {
|
||||
Warning.Print(err)
|
||||
return
|
||||
}
|
||||
|
||||
upDone := make(chan struct{})
|
||||
downDone := make(chan struct{})
|
||||
go copyStream(down, io.MultiReader(rem, up), upDone)
|
||||
go copyStream(up, downBuf, downDone)
|
||||
<-upDone
|
||||
<-downDone
|
||||
}
|
||||
|
||||
func copyStream(dst io.Writer, src io.Reader, done chan struct{}) {
|
||||
defer close(done)
|
||||
if _, err := io.Copy(dst, src); err != nil {
|
||||
Warning.Print(err)
|
||||
}
|
||||
if c, ok := dst.(interface {
|
||||
CloseWrite() error
|
||||
}); ok {
|
||||
if err := c.CloseWrite(); err != nil {
|
||||
Warning.Printf("Error closing connection: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func doChunkedResponse(w http.ResponseWriter, resp *http.Response, client *httputil.ClientConn) {
|
||||
// Because we can't go back to request/response after we
|
||||
// hijack the connection, we need to close it and make the
|
||||
// client open another.
|
||||
w.Header().Add("Connection", "close")
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
|
||||
down, _, up, rem, err := hijack(w, client)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to hijack response stream for chunked response", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer up.Close()
|
||||
defer down.Close()
|
||||
// Copy the chunked response body to downstream,
|
||||
// stopping at the end of the chunked section.
|
||||
rawResponseBody := io.MultiReader(rem, up)
|
||||
if _, err := io.Copy(ioutil.Discard, httputil.NewChunkedReader(io.TeeReader(rawResponseBody, down))); err != nil {
|
||||
http.Error(w, "Error copying chunked response body", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
resp.Trailer.Write(down)
|
||||
// a chunked response ends with a CRLF
|
||||
down.Write([]byte("\r\n"))
|
||||
}
|
||||
|
||||
func hijack(w http.ResponseWriter, client *httputil.ClientConn) (down net.Conn, downBuf *bufio.ReadWriter, up net.Conn, rem io.Reader, err error) {
|
||||
hj, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
err = errors.New("Unable to cast to Hijack")
|
||||
return
|
||||
}
|
||||
down, downBuf, err = hj.Hijack()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
up, rem = client.Hijack()
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
. "github.com/weaveworks/weave/common"
|
||||
)
|
||||
|
||||
func callWeave(args ...string) ([]byte, error) {
|
||||
args = append([]string{"--local"}, args...)
|
||||
Debug.Print("Calling weave", args)
|
||||
cmd := exec.Command("./weave", args...)
|
||||
cmd.Env = []string{"PROCFS=/hostproc", "PATH=/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}
|
||||
out, err := cmd.CombinedOutput()
|
||||
return out, err
|
||||
}
|
||||
|
||||
func weaveCIDRsFromConfig(config *docker.Config) ([]string, bool) {
|
||||
for _, e := range config.Env {
|
||||
if strings.HasPrefix(e, "WEAVE_CIDR=") {
|
||||
result := strings.Trim(e[11:], " ")
|
||||
return strings.Split(strings.TrimSpace(result), " "), result != ""
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
type createContainerInterceptor struct {
|
||||
client *docker.Client
|
||||
withDNS bool
|
||||
dockerBridgeIP string
|
||||
}
|
||||
|
||||
type createContainerRequestBody struct {
|
||||
*docker.Config
|
||||
HostConfig *docker.HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty"`
|
||||
MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty"`
|
||||
}
|
||||
|
||||
func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Body.Close()
|
||||
|
||||
container := createContainerRequestBody{}
|
||||
if err := json.Unmarshal(body, &container); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := weaveCIDRsFromConfig(container.Config); ok {
|
||||
container.HostConfig.VolumesFrom = append(container.HostConfig.VolumesFrom, "weaveproxy")
|
||||
if err := i.setWeaveWaitEntrypoint(container.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := i.setWeaveDNS(&container); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
newBody, err := json.Marshal(container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(newBody))
|
||||
r.ContentLength = int64(len(newBody))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *createContainerInterceptor) setWeaveWaitEntrypoint(container *docker.Config) error {
|
||||
var image *docker.Config
|
||||
if !configHasEntrypoint(container) || !configHasCmd(container) {
|
||||
imageInfo, err := i.client.InspectImage(container.Image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
image = imageInfo.Config
|
||||
}
|
||||
|
||||
var entry, command []string
|
||||
if configHasEntrypoint(container) {
|
||||
entry = container.Entrypoint
|
||||
} else if configHasEntrypoint(image) {
|
||||
entry = image.Entrypoint
|
||||
}
|
||||
if configHasCmd(container) {
|
||||
command = container.Cmd
|
||||
} else if configHasCmd(image) {
|
||||
command = image.Cmd
|
||||
}
|
||||
|
||||
container.Entrypoint = []string{"/home/weavewait/weavewait"}
|
||||
container.Cmd = append(entry, command...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func configHasEntrypoint(c *docker.Config) bool {
|
||||
return c != nil && len(c.Entrypoint) > 0
|
||||
}
|
||||
func configHasCmd(c *docker.Config) bool {
|
||||
return c != nil && len(c.Cmd) > 0
|
||||
}
|
||||
|
||||
func (i *createContainerInterceptor) setWeaveDNS(container *createContainerRequestBody) error {
|
||||
if !i.withDNS {
|
||||
return nil
|
||||
}
|
||||
|
||||
container.HostConfig.DNS = append(container.HostConfig.DNS, i.dockerBridgeIP)
|
||||
|
||||
if len(container.HostConfig.DNSSearch) == 0 {
|
||||
container.HostConfig.DNSSearch = []string{"."}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *createContainerInterceptor) InterceptResponse(r *http.Response) error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package proxy
|
||||
|
||||
import "net/http"
|
||||
|
||||
type interceptor interface {
|
||||
InterceptRequest(*http.Request) error
|
||||
InterceptResponse(*http.Response) error
|
||||
}
|
||||
|
||||
type nullInterceptor struct {
|
||||
}
|
||||
|
||||
func (i nullInterceptor) InterceptRequest(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i nullInterceptor) InterceptResponse(r *http.Response) error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
. "github.com/weaveworks/weave/common"
|
||||
)
|
||||
|
||||
var (
|
||||
containerCreateRegexp = regexp.MustCompile("/v[0-9\\.]*/containers/create")
|
||||
containerStartRegexp = regexp.MustCompile("^/v[0-9\\.]*/containers/[^/]*/(re)?start$")
|
||||
)
|
||||
|
||||
type Proxy struct {
|
||||
Dial func() (net.Conn, error)
|
||||
client *docker.Client
|
||||
withDNS bool
|
||||
dockerBridgeIP string
|
||||
}
|
||||
|
||||
func NewProxy(targetURL string, withDNS bool) (*Proxy, error) {
|
||||
u, err := url.Parse(targetURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dockerBridgeIP []byte
|
||||
if withDNS {
|
||||
dockerBridgeIP, err = callWeave("docker-bridge-ip")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
client, err := docker.NewClient(targetURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
targetAddr := ""
|
||||
switch u.Scheme {
|
||||
case "tcp":
|
||||
targetAddr = u.Host
|
||||
case "unix":
|
||||
targetAddr = u.Path
|
||||
}
|
||||
|
||||
return &Proxy{
|
||||
Dial: func() (net.Conn, error) {
|
||||
return net.Dial(u.Scheme, targetAddr)
|
||||
},
|
||||
client: client,
|
||||
withDNS: withDNS,
|
||||
dockerBridgeIP: string(dockerBridgeIP),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
Info.Printf("%s %s", r.Method, r.URL)
|
||||
path := r.URL.Path
|
||||
switch {
|
||||
case containerCreateRegexp.MatchString(path):
|
||||
proxy.serveWithInterceptor(&createContainerInterceptor{proxy.client, proxy.withDNS, proxy.dockerBridgeIP}, w, r)
|
||||
case containerStartRegexp.MatchString(path):
|
||||
proxy.serveWithInterceptor(&startContainerInterceptor{proxy.client, proxy.withDNS}, w, r)
|
||||
case strings.HasPrefix(path, "/weave"):
|
||||
w.WriteHeader(http.StatusOK)
|
||||
default:
|
||||
proxy.serveWithInterceptor(&nullInterceptor{}, w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func (proxy *Proxy) serveWithInterceptor(i interceptor, w http.ResponseWriter, r *http.Request) {
|
||||
newClient(proxy.Dial, i).ServeHTTP(w, r)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
. "github.com/weaveworks/weave/common"
|
||||
)
|
||||
|
||||
var containerIDRegexp = regexp.MustCompile("^/v[0-9\\.]*/containers/([^/]*)/.*")
|
||||
|
||||
type startContainerInterceptor struct {
|
||||
client *docker.Client
|
||||
withDNS bool
|
||||
}
|
||||
|
||||
func (i *startContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *startContainerInterceptor) InterceptResponse(r *http.Response) error {
|
||||
if subs := containerIDRegexp.FindStringSubmatch(r.Request.URL.Path); subs == nil {
|
||||
Warning.Printf("No container id found in request with path %s", r.Request.URL.Path)
|
||||
return nil
|
||||
}
|
||||
containerID := subs[1]
|
||||
|
||||
container, err := i.client.InspectContainer(containerID)
|
||||
if err != nil {
|
||||
Warning.Printf("Error inspecting container %s: %v", containerID, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
cidrs, ok := weaveCIDRsFromConfig(container.Config)
|
||||
if !ok {
|
||||
Debug.Print("No Weave CIDR, ignoring")
|
||||
return nil
|
||||
}
|
||||
Info.Printf("Container %s was started with CIDR \"%s\"", containerID, strings.Join(cidrs, " "))
|
||||
args := []string{"attach"}
|
||||
args = append(args, cidrs...)
|
||||
args = append(args, containerID)
|
||||
if _, err := callWeave(args...); err != nil {
|
||||
Warning.Printf("Attaching container %s to weave failed: %v", containerID, err)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+9
-1
@@ -24,6 +24,7 @@ example](https://github.com/weaveworks/weave#example):
|
||||
* [Fault tolerance](#fault-tolerance)
|
||||
* [Automatic IP address management](#ipam)
|
||||
* [Automatic discovery with WeaveDNS](#dns)
|
||||
* [Proxy](#proxy)
|
||||
|
||||
### <a name="virtual-ethernet-switch"></a>Virtual Ethernet Switch
|
||||
|
||||
@@ -403,4 +404,11 @@ to enable and use this feature.
|
||||
|
||||
WeaveDNS is a distributed DNS service for weave networks, enabling
|
||||
containers to address each other by name rather than IP address. Find
|
||||
out more about weaveDNS from its [documentation](weavedns.html).
|
||||
out more about WeaveDNS from its [documentation](weavedns.html).
|
||||
|
||||
### <a name="proxy"></a>Proxy
|
||||
|
||||
The weave proxy enables you to use the docker command-line utility, allowing
|
||||
weave-enabled containers to be started in the foreground, and to wait for the
|
||||
weave network interface before running. Find out more about the weave proxy
|
||||
from its [documentation](proxy.html).
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: Weave Proxy
|
||||
layout: default
|
||||
---
|
||||
|
||||
# <a name="weave-proxy"></a>Weave Proxy
|
||||
|
||||
Instead of the weave command-line utility, you may prefer to use the standard
|
||||
docker command-line utility to manage containers. This will allow containers to
|
||||
be started in foreground mode. It will also ensure that the weave network
|
||||
interface is available before running your container.
|
||||
|
||||
## Setup
|
||||
|
||||
To do this, while still connecting containers to the weave network, you can use
|
||||
the weave proxy. We can start the proxy with
|
||||
|
||||
host1$ weave launch-proxy
|
||||
|
||||
By default, the proxy will connect to docker at `unix:///var/run/docker.sock`,
|
||||
and listen on port 12375. However, we can adjust the connection to docker via
|
||||
the `-H` argument. All docker commands can be run via the proxy, so it is safe
|
||||
to globally adjust your `DOCKER_HOST`.
|
||||
|
||||
host1$ export DOCKER_HOST="tcp://host1:12375"
|
||||
host1$ docker ps
|
||||
|
||||
## Usage
|
||||
|
||||
When containers are created via the weave proxy, their entrypoint will be
|
||||
modified to wait for the weave network interface to become available. When they are
|
||||
started via the weave proxy, any IP addresses and networks specified in the
|
||||
`WEAVE_CIDR` environment variable will be attached. We can create and start a
|
||||
container via the weave proxy with
|
||||
|
||||
host1$ docker run -e WEAVE_CIDR=10.2.1.1/24 -ti ubuntu /bin/sh
|
||||
|
||||
or, equivalently with
|
||||
|
||||
host1$ docker create -e WEAVE_CIDR=10.2.1.1/24 -ti ubuntu /bin/sh
|
||||
5ef831df61d50a1a49272357155a976595e7268e590f0a2c75693337b14e1382
|
||||
host1$ docker start 5ef831df61d50a1a49272357155a976595e7268e590f0a2c75693337b14e1382
|
||||
|
||||
Multiple IP addresses and networks can be supplied in the WEAVE_CIDR variable by space-separating them, as in `WEAVE_CIDR=10.2.1.1/24 10.2.2.1/24`.
|
||||
|
||||
## Usage with WeaveDNS
|
||||
|
||||
Containers started via the proxy can be automatically configured to use WeaveDNS for name resolution. To accomplish this we need to launch the proxy with the `--with-dns` option
|
||||
|
||||
host1$ weave launch
|
||||
host1$ weave launch-dns 10.2.254.1/24
|
||||
host1$ weave launch-proxy --with-dns
|
||||
|
||||
With this done, any containers launched through the proxy will use weaveDNS for name resolution. WeaveDNS is used in addition to any dns servers specified via the `--dns` option. More details on weaveDNS can be found in the [weaveDNS documentation](weavedns.html).
|
||||
|
||||
## Limitations
|
||||
|
||||
* The proxy does not currently support TLS.
|
||||
* If you have a firewall, you will need to make sure port 12375 is open.
|
||||
@@ -10,7 +10,7 @@ run_on $HOST1 sudo ./weave --local launch -iprange 10.2.5.0/24
|
||||
assert_raises "docker_on $HOST1 ps | grep weave"
|
||||
|
||||
run_on $HOST1 sudo ./weave --local run 10.2.6.5/24 -ti --name=c1 gliderlabs/alpine /bin/sh
|
||||
assert_raises "exec_on $HOST1 c1 ifconfig | grep ethwe"
|
||||
assert_raises "exec_on $HOST1 c1 ip link show ethwe"
|
||||
|
||||
run_on $HOST1 sudo ./weave --local run -ti --name=c2 gliderlabs/alpine /bin/sh
|
||||
assert_raises "exec_on $HOST1 c2 ifconfig | grep ethwe"
|
||||
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
start_suite "Run docker-py test suite against the proxy"
|
||||
|
||||
weave_on $HOST1 launch-proxy
|
||||
assert_raises "docker_on $HOST1 ps | grep weaveproxy"
|
||||
assert_raises "docker_on $HOST1 run \
|
||||
-e NOT_ON_HOST=true \
|
||||
-e DOCKER_HOST=tcp://172.17.42.1:12375 \
|
||||
-v /tmp:/tmp \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
joffrey/docker-py python tests/integration_test.py"
|
||||
|
||||
end_suite
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
start_suite "Proxy waits for weave to be ready before running container commands"
|
||||
weave_on $HOST1 launch-proxy
|
||||
BASE_IMAGE=busybox
|
||||
# Ensure the base image does not exist, so that it will be pulled
|
||||
if (docker_on $HOST1 images $BASE_IMAGE | grep $BASE_IMAGE); then
|
||||
docker_on $HOST1 rmi $BASE_IMAGE
|
||||
fi
|
||||
|
||||
assert_raises "docker_proxy_on $HOST1 run -e 'WEAVE_CIDR=10.2.1.1/24' $BASE_IMAGE ip link show ethwe | grep 'state UP'"
|
||||
|
||||
end_suite
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
start_suite "Proxy uses entrypoint from the image with weavewait"
|
||||
weave_on $HOST1 launch-proxy
|
||||
docker_on $HOST1 build -t inspect-ethwe - <<- EOF
|
||||
FROM gliderlabs/alpine
|
||||
ENTRYPOINT ["ip", "link", "show", "ethwe"]
|
||||
EOF
|
||||
|
||||
assert_raises "docker_proxy_on $HOST1 run -e 'WEAVE_CIDR=10.2.1.1/24' inspect-ethwe | grep 'state UP'"
|
||||
|
||||
end_suite
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
C1=10.2.0.78
|
||||
C2=10.2.0.34
|
||||
NAME=seetwo.weave.local
|
||||
|
||||
start_suite "Proxy registers containers with dns"
|
||||
|
||||
weave_on $HOST1 launch-dns 10.2.254.1/24
|
||||
weave_on $HOST1 launch-proxy --with-dns
|
||||
docker_proxy_on $HOST1 run -e WEAVE_CIDR=$C2/24 -dt --name=c2 -h $NAME gliderlabs/alpine /bin/sh
|
||||
docker_proxy_on $HOST1 run -e WEAVE_CIDR=$C1/24 -dt --name=c1 aanand/docker-dnsutils /bin/sh
|
||||
|
||||
assert_dns_record $HOST1 c1 $NAME $C2
|
||||
|
||||
end_suite
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
C1=10.2.0.78
|
||||
C2=10.2.0.34
|
||||
NAME=seetwo.weave.local
|
||||
|
||||
start_suite "Proxy restart reattaches networking to containers"
|
||||
|
||||
weave_on $HOST1 launch-dns 10.2.254.1/24
|
||||
weave_on $HOST1 launch-proxy --with-dns
|
||||
docker_proxy_on $HOST1 run -e WEAVE_CIDR=$C2/24 -dt --name=c2 -h $NAME gliderlabs/alpine /bin/sh
|
||||
docker_proxy_on $HOST1 run -e WEAVE_CIDR=$C1/24 -dt --name=c1 aanand/docker-dnsutils /bin/sh
|
||||
|
||||
docker_proxy_on $HOST1 restart c2
|
||||
assert_raises "exec_on $HOST1 c2 ip link show ethwe | grep 'state UP'"
|
||||
assert_dns_record $HOST1 c1 $NAME $C2
|
||||
|
||||
end_suite
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
start_suite "Proxy allows overridden entrypoint from the container with weavewait"
|
||||
weave_on $HOST1 launch-proxy
|
||||
docker_on $HOST1 build -t false - <<- EOF
|
||||
FROM gliderlabs/alpine
|
||||
ENTRYPOINT ["/bin/false"]
|
||||
EOF
|
||||
|
||||
assert_raises "docker_proxy_on $HOST1 run -e 'WEAVE_CIDR=10.2.1.1/24' --entrypoint '/sbin/ip' false link show ethwe | grep 'state UP'"
|
||||
|
||||
end_suite
|
||||
@@ -76,6 +76,13 @@ docker_on() {
|
||||
docker -H tcp://$host:2375 "$@"
|
||||
}
|
||||
|
||||
docker_proxy_on() {
|
||||
host=$1
|
||||
shift 1
|
||||
greyly echo "Docker (with proxy) on $host: $@" >&2
|
||||
docker -H tcp://$host:12375 "$@"
|
||||
}
|
||||
|
||||
weave_on() {
|
||||
host=$1
|
||||
shift 1
|
||||
|
||||
@@ -27,6 +27,7 @@ usage() {
|
||||
echo "weave setup"
|
||||
echo "weave launch [-password <password>] [-nickname <nickname>] [-iprange <cidr>] <peer> ..."
|
||||
echo "weave launch-dns <cidr>"
|
||||
echo "weave launch-proxy [-H <docker_endpoint>] [--with-dns]"
|
||||
echo "weave connect <peer>"
|
||||
echo "weave forget <peer>"
|
||||
echo "weave run [--with-dns] [<cidr> ...] <docker run args> ..."
|
||||
@@ -679,6 +680,63 @@ get_ipam_cidr() {
|
||||
fi
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# weave proxy helpers
|
||||
######################################################################
|
||||
|
||||
PROXY_PORT=12375
|
||||
PROXY_CONTAINER_NAME=weaveproxy
|
||||
|
||||
proxy_host() {
|
||||
case $DOCKER_HOST in
|
||||
tcp://*)
|
||||
PROXY_HOST=$(echo $DOCKER_HOST | sed -e "s|tcp://\([^:]*\):[0-9]*|\1|")
|
||||
;;
|
||||
*)
|
||||
PROXY_HOST="localhost"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
wait_for_proxy_to_boot() {
|
||||
proxy_host
|
||||
for n in `seq 1 10`; do
|
||||
if curl --max-time 1 --connect-timeout 1 -s -X GET http://$PROXY_HOST:$PROXY_PORT/weave ; then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "Proxy did not start within 10 seconds" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
ensure_proxy_running() {
|
||||
case $(docker inspect --format '{{.State.Running}} {{.Config.Image}}' $PROXY_CONTAINER_NAME 2>/dev/null) in
|
||||
"true $EXEC_IMAGE")
|
||||
return 0
|
||||
;;
|
||||
true*)
|
||||
echo "Proxy already running with unknown image" >&2
|
||||
exit 1
|
||||
;;
|
||||
false*)
|
||||
docker rm -f $PROXY_CONTAINER_NAME >/dev/null
|
||||
;;
|
||||
esac
|
||||
# Set WEAVEPROXY_DOCKER_ARGS in the environment in order to supply
|
||||
# additional parameters, such as resource limits, to docker
|
||||
# when launching the weave proxy container.
|
||||
PROXY_CONTAINER=$(docker run --privileged --net=host -d \
|
||||
--name=$PROXY_CONTAINER_NAME \
|
||||
-p $PROXY_PORT:$PROXY_PORT/tcp -v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v /proc:/hostproc -e PROCFS=/hostproc \
|
||||
--entrypoint=/home/weave/weaveproxy \
|
||||
$WEAVE_DOCKER_ARGS $WEAVEPROXY_DOCKER_ARGS \
|
||||
$EXEC_IMAGE "$@")
|
||||
|
||||
wait_for_proxy_to_boot
|
||||
}
|
||||
|
||||
##########################################################################################
|
||||
|
||||
[ $(id -u) = 0 ] || {
|
||||
@@ -807,6 +865,10 @@ case "$COMMAND" in
|
||||
populate_dns
|
||||
echo $DNS_CONTAINER
|
||||
;;
|
||||
launch-proxy)
|
||||
ensure_proxy_running "$@"
|
||||
echo $PROXY_CONTAINER
|
||||
;;
|
||||
connect)
|
||||
[ $# -eq 1 ] || usage
|
||||
http_call $CONTAINER_NAME $HTTP_PORT POST /connect -d "peer=$1"
|
||||
@@ -859,6 +921,10 @@ case "$COMMAND" in
|
||||
dns_args "$@"
|
||||
echo -n $DNS_ARGS
|
||||
;;
|
||||
docker-bridge-ip)
|
||||
docker_bridge_ip
|
||||
echo -n $DOCKER_BRIDGE_IP
|
||||
;;
|
||||
start)
|
||||
collect_cidr_args "$@"
|
||||
shift $CIDR_COUNT
|
||||
|
||||
@@ -15,6 +15,10 @@ WORKDIR /home/weave
|
||||
|
||||
ADD ./weave /home/weave/
|
||||
ADD ./sigproxy /home/weave/
|
||||
ADD ./weaveproxy /home/weave/
|
||||
ADD ./weavewait /home/weavewait/weavewait
|
||||
ADD ./docker.tgz /
|
||||
|
||||
VOLUME /home/weavewait
|
||||
|
||||
ENTRYPOINT ["/home/weave/sigproxy", "/home/weave/weave"]
|
||||
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"code.google.com/p/getopt"
|
||||
. "github.com/weaveworks/weave/common"
|
||||
"github.com/weaveworks/weave/proxy"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultTarget = "unix:///var/run/docker.sock"
|
||||
defaultListen = ":12375"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var target, listen string
|
||||
var withDNS, debug bool
|
||||
|
||||
getopt.BoolVarLong(&debug, "debug", 'd', "log debugging information")
|
||||
getopt.StringVar(&target, 'H', fmt.Sprintf("docker daemon URL to proxy (default %s)", defaultTarget))
|
||||
getopt.StringVar(&listen, 'L', fmt.Sprintf("address on which to listen (default %s)", defaultListen))
|
||||
getopt.BoolVarLong(&withDNS, "with-dns", 'w', "instruct created containers to use weaveDNS as their nameserver")
|
||||
getopt.Parse()
|
||||
|
||||
if target == "" {
|
||||
target = defaultTarget
|
||||
}
|
||||
|
||||
if listen == "" {
|
||||
listen = defaultListen
|
||||
}
|
||||
|
||||
if debug {
|
||||
InitDefaultLogging(true)
|
||||
}
|
||||
|
||||
p, err := proxy.NewProxy(target, withDNS)
|
||||
if err != nil {
|
||||
Error.Fatalf("Could not start proxy: %s", err)
|
||||
}
|
||||
s := &http.Server{
|
||||
Addr: listen,
|
||||
Handler: p,
|
||||
}
|
||||
|
||||
Info.Printf("Listening on %s", listen)
|
||||
Info.Printf("Proxying %s", target)
|
||||
|
||||
if err := s.ListenAndServe(); err != nil {
|
||||
Error.Fatalf("Could not listen on %s: %s", listen, err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/weaveworks/weave/net"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if _, err := net.EnsureInterface("ethwe", 20); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
|
||||
if len(os.Args) <= 1 {
|
||||
os.Exit(0)
|
||||
}
|
||||
cmd := exec.Command(os.Args[1], os.Args[2:]...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user