Add dns support for the proxy

In the proxy, I swapped the 'flag' library for
'code.google.com/p/getopt', which uses double-dash flags.

proxy lets 'weave attach' do dns registration on container start

proxy needs to set DNSSearch if not specified
This commit is contained in:
Paul Bellamy committed 2015-05-19 10:34:45 +01:00
1 parent 444b1482fa
commit 96a69d2303
11 files changed
+229 -102

No files matched your search

+1
View File
@@ -10,6 +10,7 @@ import (
"net/http"
"net/http/httputil"
"github.com/fsouza/go-dockerclient"
. "github.com/weaveworks/weave/common"
)
+28
View File
@@ -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
}
+71 -25
View File
@@ -3,20 +3,17 @@ package proxy
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"github.com/fsouza/go-dockerclient"
)
type createContainerInterceptor struct {
client *docker.Client
}
func CreateContainerInterceptor(client *docker.Client) *createContainerInterceptor {
return &createContainerInterceptor{
client: client,
}
client *docker.Client
withDNS bool
}
type createContainerRequestBody struct {
@@ -37,14 +34,14 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) (*http.Re
return nil, err
}
if _, ok := weaveAddrFromConfig(container.Config); ok {
image, err := i.client.InspectImage(container.Config.Image)
if err != nil {
if _, ok := weaveCIDRsFromConfig(container.Config); ok {
i.addToVolumesFrom(container.HostConfig, "weaveproxy")
if err := i.setWeaveWaitEntrypoint(container.Config); err != nil {
return nil, err
}
if err := i.setWeaveDNS(&container); err != nil {
return nil, err
}
addToVolumesFrom(container.HostConfig, "weaveproxy")
setWeaveWaitEntrypoint(image.Config, container.Config)
}
newBody, err := json.Marshal(container)
@@ -57,30 +54,79 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) (*http.Re
return r, nil
}
func addToVolumesFrom(config *docker.HostConfig, mounts ...string) error {
func (i *createContainerInterceptor) addToVolumesFrom(config *docker.HostConfig, mounts ...string) {
config.VolumesFrom = append(config.VolumesFrom, mounts...)
return nil
}
func setWeaveWaitEntrypoint(image, container *docker.Config) {
container.Cmd = combineEntrypoints(image, container)
container.Entrypoint = []string{"/home/weavewait/weavewait"}
}
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
}
func combineEntrypoints(image, container *docker.Config) []string {
var entry, command []string
if len(container.Entrypoint) > 0 {
if configHasEntrypoint(container) {
entry = container.Entrypoint
} else if len(image.Entrypoint) > 0 {
} else if configHasEntrypoint(image) {
entry = image.Entrypoint
}
if len(container.Cmd) > 0 {
if configHasCmd(container) {
command = container.Cmd
} else if len(image.Cmd) > 0 {
} else if configHasCmd(image) {
command = image.Cmd
}
return append(entry, command...)
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
}
dockerBridgeIP, err := getDockerBridgeIP()
if err != nil {
return err
}
container.HostConfig.DNS = append(container.HostConfig.DNS, dockerBridgeIP)
if len(container.HostConfig.DNSSearch) == 0 {
container.HostConfig.DNSSearch = []string{"."}
}
return nil
}
func getDockerBridgeIP() (string, error) {
out, err := callWeave("dns-args")
if err != nil {
return "", fmt.Errorf("Error fetching weave dns-args: %s", err)
}
var dockerBridgeIP string
segments := strings.Split(string(out), " ")
for i := 0; i < len(segments)-1; i++ {
if segments[i] == "--dns" {
dockerBridgeIP = segments[i+1]
}
}
if dockerBridgeIP == "" {
return "", fmt.Errorf("Docker bridge IP not found in weave output: %s", string(out))
}
return dockerBridgeIP, nil
}
func (i *createContainerInterceptor) InterceptResponse(res *http.Response) (*http.Response, error) {
+33 -35
View File
@@ -11,13 +11,28 @@ import (
. "github.com/weaveworks/weave/common"
)
const (
RAW_STREAM = "application/vnd.docker.raw-stream"
)
type Proxy struct {
Dial func() (net.Conn, error)
client *docker.Client
withDNS bool
}
type proxy struct {
Dial func() (net.Conn, error)
client *docker.Client
func NewProxy(targetURL string, withDNS bool) (*Proxy, error) {
u, err := url.Parse(targetURL)
if err != nil {
return nil, err
}
client, err := docker.NewClient(targetURL)
if err != nil {
return nil, err
}
return &Proxy{
Dial: func() (net.Conn, error) {
return net.Dial(targetNetwork(u), targetAddress(u))
},
client: client,
withDNS: withDNS,
}, nil
}
func targetNetwork(u *url.URL) string {
@@ -34,23 +49,6 @@ func targetAddress(u *url.URL) (addr string) {
return
}
func NewProxy(targetUrl string) (*proxy, error) {
u, err := url.Parse(targetUrl)
if err != nil {
return nil, err
}
client, err := docker.NewClient(targetUrl)
if err != nil {
return nil, err
}
return &proxy{
Dial: func() (net.Conn, error) {
return net.Dial(targetNetwork(u), targetAddress(u))
},
client: client,
}, nil
}
func isWeaveStatus(r *http.Request) bool {
return strings.HasPrefix(r.URL.Path, "/weave")
}
@@ -65,32 +63,32 @@ func isStartContainer(r *http.Request) bool {
return err == nil && ok
}
func (proxy *proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
Info.Printf("%s %s", r.Method, r.URL)
switch {
case isCreateContainer(r):
proxy.CreateContainer(w, r)
proxy.createContainer(w, r)
case isStartContainer(r):
proxy.StartContainer(w, r)
proxy.startContainer(w, r)
case isWeaveStatus(r):
proxy.WeaveStatus(w, r)
proxy.weaveStatus(w, r)
default:
proxy.ProxyRequest(w, r)
proxy.proxyRequest(w, r)
}
}
func (proxy *proxy) WeaveStatus(w http.ResponseWriter, r *http.Request) {
func (proxy *Proxy) weaveStatus(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
func (proxy *proxy) CreateContainer(w http.ResponseWriter, r *http.Request) {
NewClient(proxy.Dial, CreateContainerInterceptor(proxy.client)).ServeHTTP(w, r)
func (proxy *Proxy) createContainer(w http.ResponseWriter, r *http.Request) {
newClient(proxy.Dial, &createContainerInterceptor{proxy.client, proxy.withDNS}).ServeHTTP(w, r)
}
func (proxy *proxy) StartContainer(w http.ResponseWriter, r *http.Request) {
NewClient(proxy.Dial, StartContainerInterceptor(proxy.client)).ServeHTTP(w, r)
func (proxy *Proxy) startContainer(w http.ResponseWriter, r *http.Request) {
newClient(proxy.Dial, &startContainerInterceptor{proxy.client, proxy.withDNS}).ServeHTTP(w, r)
}
func (proxy *proxy) ProxyRequest(w http.ResponseWriter, r *http.Request) {
NewClient(proxy.Dial, NullInterceptor).ServeHTTP(w, r)
func (proxy *Proxy) proxyRequest(w http.ResponseWriter, r *http.Request) {
newClient(proxy.Dial, &nullInterceptor{}).ServeHTTP(w, r)
}
+15 -34
View File
@@ -2,7 +2,6 @@ package proxy
import (
"net/http"
"os/exec"
"regexp"
"strings"
@@ -11,13 +10,8 @@ import (
)
type startContainerInterceptor struct {
client *docker.Client
}
func StartContainerInterceptor(client *docker.Client) Interceptor {
return &startContainerInterceptor{
client: client,
}
client *docker.Client
withDNS bool
}
func (i *startContainerInterceptor) InterceptRequest(r *http.Request) (*http.Request, error) {
@@ -25,21 +19,19 @@ func (i *startContainerInterceptor) InterceptRequest(r *http.Request) (*http.Req
}
func (i *startContainerInterceptor) InterceptResponse(res *http.Response) (*http.Response, error) {
containerId := containerFromPath(res.Request.URL.Path)
containerID := containerFromPath(res.Request.URL.Path)
container, err := i.client.InspectContainer(containerId)
container, err := i.client.InspectContainer(containerID)
if err != nil {
Warning.Print("Error Inspecting Container: ", err)
return res, nil
}
if cidr, ok := weaveAddrFromConfig(container.Config); ok {
Info.Printf("Container %s was started with CIDR \"%s\"", containerId, cidr)
args := []string{"attach"}
args = append(args, strings.Split(strings.TrimSpace(cidr), " ")...)
args = append(args, containerId)
if out, err := callWeave(args...); err != nil {
Warning.Print("Calling weave failed: ", err, string(out))
if cidrs, ok := weaveCIDRsFromConfig(container.Config); ok {
Info.Printf("Container %s was started with CIDR \"%s\"", containerID, strings.Join(cidrs, " "))
if err := i.attachContainerToWeave(containerID, cidrs); err != nil {
Warning.Print("Attaching container to weave failed: ", err)
return res, nil
}
} else {
Debug.Print("No Weave CIDR, ignoring")
@@ -55,21 +47,10 @@ func containerFromPath(path string) string {
return ""
}
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 weaveAddrFromConfig(config *docker.Config) (string, bool) {
for _, e := range config.Env {
if strings.HasPrefix(e, "WEAVE_CIDR=") {
result := strings.Trim(e[11:], " ")
return result, result != ""
}
}
return "", false
func (i *startContainerInterceptor) attachContainerToWeave(containerID string, cidrs []string) error {
args := []string{"attach"}
args = append(args, cidrs...)
args = append(args, containerID)
_, err := callWeave(args...)
return err
}
+12
View File
@@ -47,6 +47,18 @@ or, equivalently with
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-readme.html).
More details on weaveDNS can be found in the [weaveDNS documentation](weavedns-readme.html).
#### Limitations
* The proxy does not currently support TLS.
+14
View File
@@ -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
+18
View File
@@ -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
+14
View File
@@ -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
+1 -1
View File
@@ -27,7 +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>]"
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> ..."
+22 -7
View File
@@ -1,27 +1,42 @@
package main
import (
"flag"
"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 debug bool
var withDNS, debug bool
flag.StringVar(&target, "H", "unix:///var/run/docker.sock", "docker daemon URL to proxy")
flag.StringVar(&listen, "L", ":12375", "address on which to listen")
flag.BoolVar(&debug, "debug", false, "log debugging information")
flag.Parse()
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', "register connected containers with weaveDNS")
getopt.Parse()
if target == "" {
target = defaultTarget
}
if listen == "" {
listen = defaultListen
}
if debug {
InitDefaultLogging(true)
}
p, err := proxy.NewProxy(target)
p, err := proxy.NewProxy(target, withDNS)
if err != nil {
Error.Fatalf("Could not start proxy: %s", err)
}