Skip to content

Commit

Permalink
Merge pull request #1395 from weaveworks/1371-proxy-no-dockerclient
Browse files Browse the repository at this point in the history
[proxy] all request modification is done through map[string]interface{}
  • Loading branch information
paulbellamy committed Oct 12, 2015
2 parents a31ccef + 375fd91 commit d5e63b8
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 78 deletions.
1 change: 0 additions & 1 deletion proxy/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
129 changes: 90 additions & 39 deletions proxy/create_container_interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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{}
if err := i.proxy.addWeaveWaitVolume(hostConfig); err != nil {
return err
}
i.proxy.addWeaveWaitVolume(container.HostConfig)
if err := i.setWeaveWaitEntrypoint(container.Config); err != nil {
if err := i.setWeaveWaitEntrypoint(container); 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
}
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
}
}
Expand All @@ -74,50 +78,97 @@ 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 {
return err
}

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
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) {
labels, err := container.Object("Labels")
if err != nil {
return "", err
}
label, err := labels.String(i.proxy.Config.HostnameFromLabel)
if err != nil {
return "", err
}
if label == "" {
return hostname, nil
}

return label, nil
}
12 changes: 8 additions & 4 deletions proxy/create_exec_interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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)
}
Expand Down
8 changes: 4 additions & 4 deletions proxy/inspect_container_interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions proxy/inspect_exec_interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
44 changes: 35 additions & 9 deletions proxy/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -27,12 +28,11 @@ 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) (string, error) {
iface, ok := j[key]
if !ok || iface == nil {
return "", nil
}
Expand All @@ -44,3 +44,29 @@ func lookupString(m map[string]interface{}, key string) (string, error) {

return result, nil
}

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 []string{o}, nil
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, "string or array of strings", iface}
}
}
return result, nil
}

return nil, &UnmarshalWrongTypeError{key, "string or array of strings", iface}
}

0 comments on commit d5e63b8

Please sign in to comment.