There are two problems with obtaining the proxy status via the normal proxy endpoint... 1. when TLS is enabled, this requires using TLS at the client end 2. the status URL is mixed in with the docker remote API, which is rather unclean There is limited value to having some status output from the proxy, since all the output is constant. So instead we log the version and command line args, and in `weave status` only report whether the proxy is running. We still want a way to wait for the proxy to start up, and log a nice error if it dies. We do this by polling the docker logs for a startup message. Fixes #877. Fixes #876. Undoes most of #756, #800, #854.
117 lines
2.6 KiB
Go
117 lines
2.6 KiB
Go
package proxy
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"regexp"
|
|
|
|
"github.com/fsouza/go-dockerclient"
|
|
. "github.com/weaveworks/weave/common"
|
|
)
|
|
|
|
const (
|
|
defaultCaFile = "ca.pem"
|
|
defaultKeyFile = "key.pem"
|
|
defaultCertFile = "cert.pem"
|
|
)
|
|
|
|
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$")
|
|
)
|
|
|
|
type Proxy struct {
|
|
Config
|
|
|
|
dial func() (net.Conn, error)
|
|
client *docker.Client
|
|
dockerBridgeIP string
|
|
}
|
|
|
|
type Config struct {
|
|
DockerAddr string
|
|
ListenAddr string
|
|
TLSConfig TLSConfig
|
|
Version string
|
|
WithDNS bool
|
|
WithIPAM bool
|
|
}
|
|
|
|
func NewProxy(c Config) (*Proxy, error) {
|
|
u, err := url.Parse(c.DockerAddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
targetAddr := ""
|
|
switch u.Scheme {
|
|
case "tcp":
|
|
targetAddr = u.Host
|
|
case "unix":
|
|
targetAddr = u.Path
|
|
}
|
|
|
|
p := &Proxy{
|
|
Config: c,
|
|
dial: func() (net.Conn, error) {
|
|
return net.Dial(u.Scheme, targetAddr)
|
|
},
|
|
}
|
|
|
|
if p.WithDNS {
|
|
dockerBridgeIP, err := callWeave("docker-bridge-ip")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.dockerBridgeIP = string(dockerBridgeIP)
|
|
}
|
|
|
|
if err := p.TLSConfig.loadCerts(); err != nil {
|
|
Error.Fatalf("Could not configure tls for proxy: %s", err)
|
|
}
|
|
|
|
p.client, err = docker.NewClient(p.DockerAddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return p, 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.dockerBridgeIP, proxy.WithDNS, proxy.WithIPAM}, w, r)
|
|
case containerStartRegexp.MatchString(path):
|
|
proxy.serveWithInterceptor(&startContainerInterceptor{proxy.client, proxy.WithDNS, proxy.WithIPAM}, w, r)
|
|
case execCreateRegexp.MatchString(path):
|
|
proxy.serveWithInterceptor(&createExecInterceptor{proxy.client, proxy.WithIPAM}, w, r)
|
|
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)
|
|
}
|
|
|
|
func (proxy *Proxy) ListenAndServe() error {
|
|
listener, err := net.Listen("tcp", proxy.ListenAddr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if proxy.TLSConfig.enabled() {
|
|
listener = tls.NewListener(listener, proxy.TLSConfig.Config)
|
|
}
|
|
|
|
Info.Println("proxy listening")
|
|
|
|
return (&http.Server{Handler: proxy}).Serve(listener)
|
|
}
|