[proxy] all request modification is done through map[string]interface{}
That way we are less likely fall victim to go-dockerclient, or the API changing.
This commit is contained in:
1 parent
d323de3c21
commit
fbb2c213a9
9 files changed
+298
-78
No files matched your search
@@ -90,7 +90,6 @@ func marshalResponseBody(r *http.Response, body interface{}) error {
|
||||
// Stop it being chunked, because that hangs
|
||||
r.TransferEncoding = nil
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func inspectContainerInPath(client *docker.Client, path string) (*docker.Container, error) {
|
||||
|
||||
@@ -17,12 +17,6 @@ var (
|
||||
|
||||
type createContainerInterceptor struct{ proxy *Proxy }
|
||||
|
||||
type createContainerRequestBody struct {
|
||||
*docker.Config
|
||||
HostConfig *docker.HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty"`
|
||||
MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty"`
|
||||
}
|
||||
|
||||
// ErrNoSuchImage replaces docker.NoSuchImage, which does not contain the image
|
||||
// name, which in turn breaks docker clients post 1.7.0 since they expect the
|
||||
// image name to be present in errors.
|
||||
@@ -35,35 +29,45 @@ func (err *ErrNoSuchImage) Error() string {
|
||||
}
|
||||
|
||||
func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
container := createContainerRequestBody{}
|
||||
container := jsonObject{}
|
||||
if err := unmarshalRequestBody(r, &container); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cidrs, err := i.proxy.weaveCIDRsFromConfig(container.Config, container.HostConfig); err != nil {
|
||||
hostConfig, err := container.Object("HostConfig")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
networkMode, err := hostConfig.String("NetworkMode")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
env, err := container.StringArray("Env")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cidrs, err := i.proxy.weaveCIDRs(networkMode, env); err != nil {
|
||||
Log.Infof("Leaving container alone because %s", err)
|
||||
} else {
|
||||
Log.Infof("Creating container with WEAVE_CIDR \"%s\"", strings.Join(cidrs, " "))
|
||||
if container.HostConfig == nil {
|
||||
container.HostConfig = &docker.HostConfig{}
|
||||
}
|
||||
if container.Config == nil {
|
||||
container.Config = &docker.Config{}
|
||||
}
|
||||
i.proxy.addWeaveWaitVolume(container.HostConfig)
|
||||
if err := i.setWeaveWaitEntrypoint(container.Config); err != nil {
|
||||
if err := i.proxy.addWeaveWaitVolume(hostConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
hostname := r.URL.Query().Get("name")
|
||||
if i.proxy.Config.HostnameFromLabel != "" {
|
||||
if labelValue, ok := container.Labels[i.proxy.Config.HostnameFromLabel]; ok {
|
||||
hostname = labelValue
|
||||
}
|
||||
if err := i.setWeaveWaitEntrypoint(container); err != nil {
|
||||
return err
|
||||
}
|
||||
hostname, err := i.containerHostname(r, container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hostname = i.proxy.hostnameMatchRegexp.ReplaceAllString(hostname, i.proxy.HostnameReplacement)
|
||||
if dnsDomain := i.proxy.getDNSDomain(); dnsDomain != "" {
|
||||
i.setHostname(&container, hostname, dnsDomain)
|
||||
if err := i.proxy.setWeaveDNS(container.HostConfig, container.Hostname, dnsDomain); err != nil {
|
||||
if err := i.setHostname(container, hostname, dnsDomain); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := i.proxy.setWeaveDNS(hostConfig, hostname, dnsDomain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -74,50 +78,104 @@ func (i *createContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *createContainerInterceptor) setWeaveWaitEntrypoint(container *docker.Config) error {
|
||||
if len(container.Entrypoint) == 0 {
|
||||
image, err := i.proxy.client.InspectImage(container.Image)
|
||||
func (i *createContainerInterceptor) setWeaveWaitEntrypoint(container jsonObject) error {
|
||||
var entrypoint []string
|
||||
entrypoint, err := container.StringArray("Entrypoint")
|
||||
if err != nil {
|
||||
err, ok := err.(*UnmarshalWrongTypeError)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
got, ok := err.Got.(string)
|
||||
if !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
entrypoint = []string{got}
|
||||
}
|
||||
|
||||
cmd, err := container.StringArray("Cmd")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(entrypoint) == 0 {
|
||||
containerImage, err := container.String("Image")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
image, err := i.proxy.client.InspectImage(containerImage)
|
||||
if err == docker.ErrNoSuchImage {
|
||||
return &ErrNoSuchImage{container.Image}
|
||||
return &ErrNoSuchImage{containerImage}
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(container.Cmd) == 0 {
|
||||
container.Cmd = image.Config.Cmd
|
||||
if len(cmd) == 0 {
|
||||
cmd = image.Config.Cmd
|
||||
container["Cmd"] = cmd
|
||||
}
|
||||
|
||||
if container.Entrypoint == nil {
|
||||
container.Entrypoint = image.Config.Entrypoint
|
||||
if entrypoint == nil {
|
||||
entrypoint = image.Config.Entrypoint
|
||||
container["Entrypoint"] = entrypoint
|
||||
}
|
||||
}
|
||||
|
||||
if len(container.Entrypoint) == 0 && len(container.Cmd) == 0 {
|
||||
if len(entrypoint) == 0 && len(cmd) == 0 {
|
||||
return ErrNoCommandSpecified
|
||||
}
|
||||
|
||||
if len(container.Entrypoint) == 0 || container.Entrypoint[0] != weaveWaitEntrypoint[0] {
|
||||
container.Entrypoint = append(weaveWaitEntrypoint, container.Entrypoint...)
|
||||
if len(entrypoint) == 0 || entrypoint[0] != weaveWaitEntrypoint[0] {
|
||||
container["Entrypoint"] = append(weaveWaitEntrypoint, entrypoint...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *createContainerInterceptor) setHostname(container *createContainerRequestBody, name, dnsDomain string) {
|
||||
if container.Hostname == "" && name != "" {
|
||||
func (i *createContainerInterceptor) setHostname(container jsonObject, name, dnsDomain string) error {
|
||||
hostname, err := container.String("Hostname")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hostname == "" && name != "" {
|
||||
// Strip trailing period because it's unusual to see it used on the end of a host name
|
||||
trimmedDNSDomain := strings.TrimSuffix(dnsDomain, ".")
|
||||
if len(name)+1+len(trimmedDNSDomain) > MaxDockerHostname {
|
||||
Log.Warningf("Container name [%s] too long to be used as hostname", name)
|
||||
} else {
|
||||
container.Hostname = name
|
||||
container.Domainname = trimmedDNSDomain
|
||||
hostname = name
|
||||
container["Hostname"] = name
|
||||
container["Domainname"] = trimmedDNSDomain
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *createContainerInterceptor) InterceptResponse(r *http.Response) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *createContainerInterceptor) containerHostname(r *http.Request, container jsonObject) (hostname string, err error) {
|
||||
hostname = r.URL.Query().Get("name")
|
||||
if i.proxy.Config.HostnameFromLabel != "" {
|
||||
hostname, err = i.hostnameFromLabel(hostname, container)
|
||||
}
|
||||
hostname = i.proxy.hostnameMatchRegexp.ReplaceAllString(hostname, i.proxy.HostnameReplacement)
|
||||
return
|
||||
}
|
||||
|
||||
func (i *createContainerInterceptor) hostnameFromLabel(hostname string, container jsonObject) (string, error) {
|
||||
label, err := container.String("Labels", i.proxy.Config.HostnameFromLabel)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if label == "" {
|
||||
return hostname, nil
|
||||
}
|
||||
|
||||
return label, nil
|
||||
}
|
||||
@@ -4,14 +4,13 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/fsouza/go-dockerclient"
|
||||
. "github.com/weaveworks/weave/common"
|
||||
)
|
||||
|
||||
type createExecInterceptor struct{ proxy *Proxy }
|
||||
|
||||
func (i *createExecInterceptor) InterceptRequest(r *http.Request) error {
|
||||
options := docker.CreateExecOptions{}
|
||||
options := jsonObject{}
|
||||
if err := unmarshalRequestBody(r, &options); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -25,14 +24,19 @@ func (i *createExecInterceptor) InterceptRequest(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
cidrs, err := i.proxy.weaveCIDRsFromConfig(container.Config, container.HostConfig)
|
||||
cidrs, err := i.proxy.weaveCIDRs(container.HostConfig.NetworkMode, container.Config.Env)
|
||||
if err != nil {
|
||||
Log.Infof("Leaving container %s alone because %s", container.ID, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd, err := options.StringArray("Cmd")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Log.Infof("Exec in container %s with WEAVE_CIDR \"%s\"", container.ID, strings.Join(cidrs, " "))
|
||||
options.Cmd = append(weaveWaitEntrypoint, options.Cmd...)
|
||||
options["Cmd"] = append(weaveWaitEntrypoint, cmd...)
|
||||
|
||||
return marshalRequestBody(r, options)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ func (i *inspectContainerInterceptor) InterceptResponse(r *http.Response) error
|
||||
return nil
|
||||
}
|
||||
|
||||
container := map[string]interface{}{}
|
||||
container := jsonObject{}
|
||||
if err := unmarshalResponseBody(r, &container); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -29,8 +29,8 @@ func (i *inspectContainerInterceptor) InterceptResponse(r *http.Response) error
|
||||
return marshalResponseBody(r, container)
|
||||
}
|
||||
|
||||
func updateContainerNetworkSettings(container map[string]interface{}) error {
|
||||
containerID, err := lookupString(container, "Id")
|
||||
func updateContainerNetworkSettings(container jsonObject) error {
|
||||
containerID, err := container.String("Id")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func updateContainerNetworkSettings(container map[string]interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
networkSettings, err := lookupObject(container, "NetworkSettings")
|
||||
networkSettings, err := container.Object("NetworkSettings")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -17,12 +17,12 @@ func (i *inspectExecInterceptor) InterceptResponse(r *http.Response) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
exec := map[string]interface{}{}
|
||||
exec := jsonObject{}
|
||||
if err := unmarshalResponseBody(r, &exec); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
container, err := lookupObject(exec, "Container")
|
||||
container, err := exec.Object("Container")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+41
-8
@@ -13,12 +13,13 @@ 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]
|
||||
type jsonObject map[string]interface{}
|
||||
|
||||
func (j jsonObject) Object(key string) (jsonObject, error) {
|
||||
iface, ok := j[key]
|
||||
if !ok || iface == nil {
|
||||
result := map[string]interface{}{}
|
||||
m[key] = result
|
||||
result := jsonObject{}
|
||||
j[key] = result
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -27,12 +28,20 @@ func lookupObject(m map[string]interface{}, key string) (map[string]interface{},
|
||||
return nil, &UnmarshalWrongTypeError{key, "object", iface}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return jsonObject(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]
|
||||
func (j jsonObject) String(key string, ks ...string) (string, error) {
|
||||
if len(ks) > 0 {
|
||||
j, err := j.Object(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return j.String(ks[0], ks[1:]...)
|
||||
}
|
||||
|
||||
iface, ok := j[key]
|
||||
if !ok || iface == nil {
|
||||
return "", nil
|
||||
}
|
||||
@@ -44,3 +53,27 @@ func lookupString(m map[string]interface{}, key string) (string, error) {
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// For parsing json objects. Look up (or create) a string array in the given map.
|
||||
func (j jsonObject) StringArray(key string) ([]string, error) {
|
||||
iface, ok := j[key]
|
||||
if !ok || iface == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch o := iface.(type) {
|
||||
case []string:
|
||||
return o, nil
|
||||
case []interface{}:
|
||||
result := []string{}
|
||||
for _, s := range o {
|
||||
if s, ok := s.(string); ok {
|
||||
result = append(result, s)
|
||||
} else {
|
||||
return nil, &UnmarshalWrongTypeError{key, "array of strings", iface}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
return nil, &UnmarshalWrongTypeError{key, "array of strings", iface}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLookupObject(t *testing.T) {
|
||||
tests := []struct {
|
||||
root jsonObject
|
||||
key string
|
||||
result jsonObject
|
||||
err error
|
||||
}{
|
||||
{
|
||||
jsonObject{},
|
||||
"a",
|
||||
jsonObject{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
jsonObject{"a": map[string]interface{}{"b": int(1)}},
|
||||
"a",
|
||||
jsonObject{"b": int(1)},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
jsonObject{"nonObject": int(1)},
|
||||
"nonObject",
|
||||
nil,
|
||||
&UnmarshalWrongTypeError{Field: "nonObject", Expected: "object", Got: 1},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
gotResult, gotErr := test.root.Object(test.key)
|
||||
msg := fmt.Sprintf("%q.Object(%q) => %q, %q", test.root, test.key, gotResult, gotErr)
|
||||
assert.Equal(t, gotResult, test.result, msg)
|
||||
assert.Equal(t, gotErr, test.err, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupString(t *testing.T) {
|
||||
tests := []struct {
|
||||
root jsonObject
|
||||
path []string
|
||||
result string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
jsonObject{},
|
||||
[]string{"a", "b"},
|
||||
"",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
jsonObject{"nonString": int(1)},
|
||||
[]string{"nonString"},
|
||||
"",
|
||||
&UnmarshalWrongTypeError{Field: "nonString", Expected: "string", Got: 1},
|
||||
},
|
||||
{
|
||||
jsonObject{"nonObject": int(1)},
|
||||
[]string{"nonObject", "b"},
|
||||
"",
|
||||
&UnmarshalWrongTypeError{Field: "nonObject", Expected: "object", Got: 1},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
gotResult, gotErr := test.root.String(test.path[0], test.path[1:]...)
|
||||
msg := fmt.Sprintf("%q.String(%q) => %q, %q", test.root, test.path, gotResult, gotErr)
|
||||
assert.Equal(t, gotResult, test.result, msg)
|
||||
assert.Equal(t, gotErr, test.err, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupStringArray(t *testing.T) {
|
||||
tests := []struct {
|
||||
root jsonObject
|
||||
path string
|
||||
result []string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
jsonObject{},
|
||||
"a",
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
jsonObject{"a": []string{"foo"}},
|
||||
"a",
|
||||
[]string{"foo"},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
jsonObject{"a": []string{}},
|
||||
"a",
|
||||
[]string{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
jsonObject{"string": "foo"},
|
||||
"string",
|
||||
nil,
|
||||
&UnmarshalWrongTypeError{Field: "string", Expected: "array of strings", Got: "foo"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
gotResult, gotErr := test.root.StringArray(test.path)
|
||||
msg := fmt.Sprintf("%q.String(%q) => %q, %q", test.root, test.path, gotResult, gotErr)
|
||||
assert.Equal(t, gotResult, test.result, msg)
|
||||
assert.Equal(t, gotErr, test.err, msg)
|
||||
}
|
||||
}
|
||||
+27
-17
@@ -346,7 +346,7 @@ func (proxy *Proxy) ContainerDied(ident string) {
|
||||
}
|
||||
|
||||
func (proxy *Proxy) attach(container *docker.Container) error {
|
||||
cidrs, err := proxy.weaveCIDRsFromConfig(container.Config, container.HostConfig)
|
||||
cidrs, err := proxy.weaveCIDRs(container.HostConfig.NetworkMode, container.Config.Env)
|
||||
if err != nil {
|
||||
Log.Infof("Leaving container %s alone because %s", container.ID, err)
|
||||
return nil
|
||||
@@ -368,15 +368,11 @@ func (proxy *Proxy) attach(container *docker.Container) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proxy *Proxy) weaveCIDRsFromConfig(config *docker.Config, hostConfig *docker.HostConfig) ([]string, error) {
|
||||
netMode := ""
|
||||
if hostConfig != nil {
|
||||
netMode = hostConfig.NetworkMode
|
||||
func (proxy *Proxy) weaveCIDRs(networkMode string, env []string) ([]string, error) {
|
||||
if networkMode == "host" || strings.HasPrefix(networkMode, "container:") {
|
||||
return nil, fmt.Errorf("the container was created with the '--net=%s'", networkMode)
|
||||
}
|
||||
if netMode == "host" || strings.HasPrefix(netMode, "container:") {
|
||||
return nil, fmt.Errorf("the container was created with the '--net=%s'", netMode)
|
||||
}
|
||||
for _, e := range config.Env {
|
||||
for _, e := range env {
|
||||
if strings.HasPrefix(e, "WEAVE_CIDR=") {
|
||||
if e[11:] == "none" {
|
||||
return nil, ErrWeaveCIDRNone
|
||||
@@ -390,26 +386,40 @@ func (proxy *Proxy) weaveCIDRsFromConfig(config *docker.Config, hostConfig *dock
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (proxy *Proxy) addWeaveWaitVolume(hostConfig *docker.HostConfig) {
|
||||
func (proxy *Proxy) addWeaveWaitVolume(hostConfig jsonObject) error {
|
||||
configBinds, err := hostConfig.StringArray("Binds")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var binds []string
|
||||
for _, bind := range hostConfig.Binds {
|
||||
for _, bind := range configBinds {
|
||||
s := strings.Split(bind, ":")
|
||||
if len(s) >= 2 && s[1] == "/w" {
|
||||
continue
|
||||
}
|
||||
binds = append(binds, bind)
|
||||
}
|
||||
hostConfig.Binds = append(binds, fmt.Sprintf("%s:/w:ro", proxy.weaveWaitVolume))
|
||||
hostConfig["Binds"] = append(binds, fmt.Sprintf("%s:/w:ro", proxy.weaveWaitVolume))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proxy *Proxy) setWeaveDNS(hostConfig *docker.HostConfig, hostname, dnsDomain string) error {
|
||||
hostConfig.DNS = append(hostConfig.DNS, proxy.dockerBridgeIP)
|
||||
func (proxy *Proxy) setWeaveDNS(hostConfig jsonObject, hostname, dnsDomain string) error {
|
||||
dns, err := hostConfig.StringArray("Dns")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hostConfig["Dns"] = append(dns, proxy.dockerBridgeIP)
|
||||
|
||||
if len(hostConfig.DNSSearch) == 0 {
|
||||
dnsSearch, err := hostConfig.StringArray("DnsSearch")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(dnsSearch) == 0 {
|
||||
if hostname == "" {
|
||||
hostConfig.DNSSearch = []string{dnsDomain}
|
||||
hostConfig["DnsSearch"] = []string{dnsDomain}
|
||||
} else {
|
||||
hostConfig.DNSSearch = []string{"."}
|
||||
hostConfig["DnsSearch"] = []string{"."}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ func (i *startContainerInterceptor) InterceptRequest(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
hostConfig := &docker.HostConfig{}
|
||||
if err := unmarshalRequestBody(r, hostConfig); err != nil {
|
||||
hostConfig := map[string]interface{}{}
|
||||
if err := unmarshalRequestBody(r, &hostConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user