Merge pull request #1376 from weaveworks/1199-proxy-kubernetes
[proxy] add --rewrite-inspect flag Fixes #117. Fixes #1199.
This commit is contained in:
commit
32a82f2d0e
11 files changed
+248
-30
No files matched your search
@@ -29,6 +29,7 @@ func main() {
|
||||
mflagext.ListVar(&c.ListenAddrs, []string{"H"}, nil, "addresses on which to listen")
|
||||
mflag.StringVar(&c.HostnameMatch, []string{"-hostname-match"}, "(.*)", "Regexp pattern to apply on container names (e.g. '^aws-[0-9]+-(.*)$')")
|
||||
mflag.StringVar(&c.HostnameReplacement, []string{"-hostname-replacement"}, "$1", "Expression to generate hostnames based on matches from --hostname-match (e.g. 'my-app-$1')")
|
||||
mflag.BoolVar(&c.RewriteInspect, []string{"-rewrite-inspect"}, false, "Rewrite 'inspect' calls to return the weave network settings (if attached)")
|
||||
mflag.BoolVar(&c.NoDefaultIPAM, []string{"#-no-default-ipam", "-no-default-ipalloc"}, false, "do not automatically allocate addresses for containers without a WEAVE_CIDR")
|
||||
mflag.BoolVar(&c.NoRewriteHosts, []string{"-no-rewrite-hosts"}, false, "do not automatically rewrite /etc/hosts. Use if you need the docker IP to remain in /etc/hosts")
|
||||
mflag.StringVar(&c.TLSConfig.CACert, []string{"#tlscacert", "-tlscacert"}, "", "Trust certs signed only by this CA")
|
||||
|
||||
@@ -3,12 +3,15 @@ package proxy
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
. "github.com/weaveworks/weave/common"
|
||||
@@ -37,6 +40,19 @@ func callWeave(args ...string) ([]byte, []byte, error) {
|
||||
return stdout.Bytes(), stderr.Bytes(), err
|
||||
}
|
||||
|
||||
func unmarshalRequestBody(r *http.Request, target interface{}) error {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.Body.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(body))
|
||||
|
||||
return json.Unmarshal(body, &target)
|
||||
}
|
||||
|
||||
func marshalRequestBody(r *http.Request, body interface{}) error {
|
||||
newBody, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
@@ -47,6 +63,32 @@ func marshalRequestBody(r *http.Request, body interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalResponseBody(r *http.Response, target interface{}) error {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.Body.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(body))
|
||||
|
||||
return json.Unmarshal(body, &target)
|
||||
}
|
||||
|
||||
func marshalResponseBody(r *http.Response, body interface{}) error {
|
||||
newBody, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(newBody))
|
||||
r.ContentLength = int64(len(newBody))
|
||||
// Stop it being chunked, because that hangs
|
||||
r.TransferEncoding = nil
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func inspectContainerInPath(client *docker.Client, path string) (*docker.Container, error) {
|
||||
subs := containerIDRegexp.FindStringSubmatch(path)
|
||||
if subs == nil {
|
||||
@@ -62,3 +104,32 @@ func inspectContainerInPath(client *docker.Client, path string) (*docker.Contain
|
||||
}
|
||||
return container, err
|
||||
}
|
||||
|
||||
func weaveContainerIPs(containerID string) (mac string, ips []net.IP, nets []*net.IPNet, err error) {
|
||||
stdout, stderr, err := callWeave("ps", containerID)
|
||||
if err != nil || len(stderr) > 0 {
|
||||
err = errors.New(string(stderr))
|
||||
return
|
||||
}
|
||||
if len(stdout) <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
fields := strings.Fields(string(stdout))
|
||||
if len(fields) < 2 {
|
||||
return
|
||||
}
|
||||
mac = fields[1]
|
||||
|
||||
var ip net.IP
|
||||
var ipnet *net.IPNet
|
||||
for _, cidr := range fields[2:] {
|
||||
ip, ipnet, err = net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ips = append(ips, ip)
|
||||
nets = append(nets, ipnet)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@@ -42,15 +40,8 @@ func (err *ErrNoSuchImage) Error() string {
|
||||
}
|
||||
|
||||
func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Body.Close()
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(body))
|
||||
|
||||
container := createContainerRequestBody{}
|
||||
if err := json.Unmarshal(body, &container); err != nil {
|
||||
if err := unmarshalRequestBody(r, &container); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -74,12 +65,7 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
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 marshalRequestBody(r, container)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -14,15 +11,8 @@ import (
|
||||
type createExecInterceptor struct{ proxy *Proxy }
|
||||
|
||||
func (i *createExecInterceptor) InterceptRequest(r *http.Request) error {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Body.Close()
|
||||
r.Body = ioutil.NopCloser(bytes.NewReader(body))
|
||||
|
||||
options := docker.CreateExecOptions{}
|
||||
if err := json.Unmarshal(body, &options); err != nil {
|
||||
if err := unmarshalRequestBody(r, &options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
. "github.com/weaveworks/weave/common"
|
||||
)
|
||||
|
||||
type inspectContainerInterceptor struct{ proxy *Proxy }
|
||||
|
||||
func (i *inspectContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *inspectContainerInterceptor) InterceptResponse(r *http.Response) error {
|
||||
if !i.proxy.RewriteInspect {
|
||||
return nil
|
||||
}
|
||||
|
||||
container := map[string]interface{}{}
|
||||
if err := unmarshalResponseBody(r, &container); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updateContainerNetworkSettings(container); err != nil {
|
||||
Log.Warningf("Inspecting container %s failed: %s", container["Id"], err)
|
||||
}
|
||||
|
||||
return marshalResponseBody(r, container)
|
||||
}
|
||||
|
||||
func updateContainerNetworkSettings(container map[string]interface{}) error {
|
||||
containerID, err := lookupString(container, "Id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mac, ips, nets, err := weaveContainerIPs(containerID)
|
||||
if err != nil || len(ips) == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
networkSettings, err := lookupObject(container, "NetworkSettings")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
networkSettings["MacAddress"] = mac
|
||||
networkSettings["IPAddress"] = ips[0].String()
|
||||
networkSettings["IPPrefixLen"], _ = nets[0].Mask.Size()
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
. "github.com/weaveworks/weave/common"
|
||||
)
|
||||
|
||||
type inspectExecInterceptor struct{ proxy *Proxy }
|
||||
|
||||
func (i *inspectExecInterceptor) InterceptRequest(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *inspectExecInterceptor) InterceptResponse(r *http.Response) error {
|
||||
if !i.proxy.RewriteInspect {
|
||||
return nil
|
||||
}
|
||||
|
||||
exec := map[string]interface{}{}
|
||||
if err := unmarshalResponseBody(r, &exec); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
container, err := lookupObject(exec, "Container")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := updateContainerNetworkSettings(container); err != nil {
|
||||
Log.Warningf("Inspecting exec %s failed: %s", exec["Id"], err)
|
||||
}
|
||||
|
||||
return marshalResponseBody(r, exec)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type UnmarshalWrongTypeError struct {
|
||||
Field, Expected string
|
||||
Got interface{}
|
||||
}
|
||||
|
||||
func (e *UnmarshalWrongTypeError) Error() string {
|
||||
return fmt.Sprintf("Wrong type for %s field, expected %s, but got %T", e.Field, e.Expected, e.Got)
|
||||
}
|
||||
|
||||
// For parsing json objects. Look up (or create) a child object in the given map.
|
||||
func lookupObject(m map[string]interface{}, key string) (map[string]interface{}, error) {
|
||||
iface, ok := m[key]
|
||||
if !ok || iface == nil {
|
||||
result := map[string]interface{}{}
|
||||
m[key] = result
|
||||
return result, nil
|
||||
}
|
||||
|
||||
result, ok := iface.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, &UnmarshalWrongTypeError{key, "object", iface}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// For parsing json objects. Look up (or create) a string in the given map.
|
||||
func lookupString(m map[string]interface{}, key string) (string, error) {
|
||||
iface, ok := m[key]
|
||||
if !ok || iface == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
result, ok := iface.(string)
|
||||
if !ok {
|
||||
return "", &UnmarshalWrongTypeError{key, "string", iface}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
+14
-3
@@ -24,18 +24,25 @@ 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$")
|
||||
containerCreateRegexp = dockerAPIEndpoint("containers/create")
|
||||
containerStartRegexp = dockerAPIEndpoint("containers/[^/]*/(re)?start")
|
||||
containerInspectRegexp = dockerAPIEndpoint("containers/[^/]*/json")
|
||||
execCreateRegexp = dockerAPIEndpoint("containers/[^/]*/exec")
|
||||
execInspectRegexp = dockerAPIEndpoint("exec/[^/]*/json")
|
||||
|
||||
ErrWeaveCIDRNone = errors.New("the container was created with the '-e WEAVE_CIDR=none' option")
|
||||
ErrNoDefaultIPAM = errors.New("the container was created without specifying an IP address with '-e WEAVE_CIDR=...' and the proxy was started with the '--no-default-ipalloc' option")
|
||||
)
|
||||
|
||||
func dockerAPIEndpoint(endpoint string) *regexp.Regexp {
|
||||
return regexp.MustCompile("^(/v[0-9\\.]*)?/" + endpoint + "$")
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
HostnameMatch string
|
||||
HostnameReplacement string
|
||||
ListenAddrs []string
|
||||
RewriteInspect bool
|
||||
NoDefaultIPAM bool
|
||||
NoRewriteHosts bool
|
||||
TLSConfig TLSConfig
|
||||
@@ -123,8 +130,12 @@ func (proxy *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
i = &createContainerInterceptor{proxy}
|
||||
case containerStartRegexp.MatchString(path):
|
||||
i = &startContainerInterceptor{proxy}
|
||||
case containerInspectRegexp.MatchString(path):
|
||||
i = &inspectContainerInterceptor{proxy}
|
||||
case execCreateRegexp.MatchString(path):
|
||||
i = &createExecInterceptor{proxy}
|
||||
case execInspectRegexp.MatchString(path):
|
||||
i = &inspectExecInterceptor{proxy}
|
||||
default:
|
||||
i = &nullInterceptor{}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,16 @@ 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"`.
|
||||
|
||||
The docker NetworkSettings (including IP address, MacAddress, and
|
||||
IPPrefixLen), will still be returned by `docker inspect`. If you want
|
||||
`docker inspect` to return the weave NetworkSettings instead, then the
|
||||
proxy must be launced with the `--rewrite-inspect` flag. This will
|
||||
only substitute in the weave network settings when the container has a
|
||||
weave IP. If a container has more than one weave IP, the inspect call
|
||||
will only include one of them.
|
||||
|
||||
host1$ weave launch-router && weave launch-proxy --rewrite-inspect
|
||||
|
||||
## <a name="ipam"></a>Automatic IP address assignment
|
||||
|
||||
If [automatic IP address assignment](ipam.html) is enabled in weave,
|
||||
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
start_suite "Check that docker inspect returns the weave IP"
|
||||
|
||||
weave_on $HOST1 launch-router
|
||||
weave_on $HOST1 launch-proxy --rewrite-inspect
|
||||
|
||||
proxy docker_on $HOST1 run -dt --name c1 $SMALL_IMAGE /bin/sh
|
||||
inspect_format="{{.Name}} {{.NetworkSettings.MacAddress}} {{.NetworkSettings.IPAddress}}/{{.NetworkSettings.IPPrefixLen}}"
|
||||
expected="/$(weave_on $HOST1 ps c1)"
|
||||
|
||||
assert "proxy docker_on $HOST1 inspect --format='$inspect_format' c1" "$expected"
|
||||
|
||||
end_suite
|
||||
@@ -56,6 +56,7 @@ weave launch-proxy [-H <endpoint>] [--with-dns | --without-dns]
|
||||
[--no-default-ipalloc] [--no-rewrite-hosts]
|
||||
[--hostname-match <regexp>]
|
||||
[--hostname-replacement <replacement>]
|
||||
[--rewrite-inspect]
|
||||
weave env [--restore]
|
||||
weave config
|
||||
weave connect [--replace] [<peer> ...]
|
||||
|
||||
Reference in new issue
Block a user