From e101dde62e1373f523b1194f8f6a812669170be7 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 26 Nov 2015 16:36:19 +0000 Subject: [PATCH] Extract 'api' package --- Makefile | 2 +- api/dns.go | 18 ++++++++++ api/ipam.go | 43 +++++++++++++++++++++++ api/weaveapi.go | 59 +++++++++++++++++++++++++++++++ plugin/dns.go | 55 ----------------------------- plugin/ipam.go | 89 ----------------------------------------------- plugin/watcher.go | 22 +++++++++--- 7 files changed, 139 insertions(+), 149 deletions(-) create mode 100644 api/dns.go create mode 100644 api/ipam.go create mode 100644 api/weaveapi.go delete mode 100644 plugin/dns.go delete mode 100644 plugin/ipam.go diff --git a/Makefile b/Makefile index d802efc0..d2adf329 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ $(NETCHECK_EXE): prog/netcheck/netcheck.go $(SIGPROXY_EXE): prog/sigproxy/main.go $(WEAVEWAIT_EXE): prog/weavewait/*.go net/*.go $(DOCKERTLSARGS_EXE): prog/docker_tls_args/*.go -$(DOCKERPLUGIN_EXE): prog/plugin/*.go plugin/*.go plugin/skel/*.go +$(DOCKERPLUGIN_EXE): prog/plugin/*.go plugin/*.go plugin/skel/*.go api/*.go common/docker/*.go $(WEAVEWAIT_EXE) $(SIGPROXY_EXE) $(DOCKERTLSARGS_EXE) $(DOCKERPLUGIN_EXE): go get -tags netgo ./$(@D) diff --git a/api/dns.go b/api/dns.go new file mode 100644 index 00000000..750c34f2 --- /dev/null +++ b/api/dns.go @@ -0,0 +1,18 @@ +package api + +import ( + "fmt" + "net/url" +) + +func (client *Client) RegisterWithDNS(ID string, fqdn string, ip string) error { + data := url.Values{} + data.Add("fqdn", fqdn) + _, err := httpVerb("PUT", fmt.Sprintf("%s/name/%s/%s", client.baseUrl, ID, ip), data) + return err +} + +func (client *Client) DeregisterWithDNS(ID string, ip string) error { + _, err := httpVerb("DELETE", fmt.Sprintf("%s/name/%s/%s", client.baseUrl, ID, ip), nil) + return err +} diff --git a/api/ipam.go b/api/ipam.go new file mode 100644 index 00000000..91a3053a --- /dev/null +++ b/api/ipam.go @@ -0,0 +1,43 @@ +package api + +import ( + "fmt" + "net" + + . "github.com/weaveworks/weave/common" +) + +func (client *Client) ipamOp(ID string, op string) (*net.IPNet, error) { + Log.Debugf("IPAM operation %s for %s", op, ID) + ip, err := httpVerb(op, fmt.Sprintf("%s/ip/%s", client.baseUrl, ID), nil) + if err != nil { + return nil, err + } + return parseIP(ip) +} + +// returns an IP for the ID given, allocating a fresh one if necessary +func (client *Client) AllocateIP(ID string) (*net.IPNet, error) { + return client.ipamOp(ID, "POST") +} + +// returns an IP for the ID given, or nil if one has not been +// allocated +func (client *Client) LookupIP(ID string) (*net.IPNet, error) { + return client.ipamOp(ID, "GET") +} + +// release an IP which is no longer needed +func (client *Client) ReleaseIP(ID string) error { + _, err := client.ipamOp(ID, "DELETE") + return err +} + +func parseIP(body string) (*net.IPNet, error) { + ip, ipnet, err := net.ParseCIDR(string(body)) + if err != nil { + return nil, err + } + ipnet.IP = ip + return ipnet, nil +} diff --git a/api/weaveapi.go b/api/weaveapi.go new file mode 100644 index 00000000..d75394bd --- /dev/null +++ b/api/weaveapi.go @@ -0,0 +1,59 @@ +package api + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + + . "github.com/weaveworks/weave/common" +) + +const ( + WeaveHttpPort = 6784 +) + +type Client struct { + baseUrl string +} + +func httpVerb(verb string, url string, values url.Values) (string, error) { + Log.Debugf("weave %s to %s with %v", verb, url, values) + var body io.Reader + if values != nil { + body = strings.NewReader(values.Encode()) + } + req, err := http.NewRequest(verb, url, body) + if err != nil { + return "", err + } + if values != nil { + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + } + if resp, err := http.DefaultClient.Do(req); err != nil { + return "", err + } else { + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNoContent { + return string(body), nil + } else { + return "", errors.New(resp.Status + ": " + string(body)) + } + } +} + +func NewClient(addr string) *Client { + return &Client{baseUrl: fmt.Sprintf("http://%s:%d", addr, WeaveHttpPort)} +} + +func (client *Client) Connect(remote string) error { + _, err := httpVerb("POST", client.baseUrl+"/connect", url.Values{"peer": {remote}}) + return err +} diff --git a/plugin/dns.go b/plugin/dns.go deleted file mode 100644 index 526d46bd..00000000 --- a/plugin/dns.go +++ /dev/null @@ -1,55 +0,0 @@ -package plugin - -import ( - "fmt" - "net/http" - "net/url" - "strings" -) - -func (d *dockerer) registerWithDNS(ID string, fqdn string, ip string) error { - dnsip, err := d.client.GetContainerBridgeIP(WeaveContainer) - if err != nil { - return fmt.Errorf("nameserver not available: %s", err) - } - data := url.Values{} - data.Add("fqdn", fqdn) - - req, err := http.NewRequest("PUT", fmt.Sprintf("http://%s:6785/name/%s/%s", dnsip, ID, ip), strings.NewReader(data.Encode())) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - cl := &http.Client{} - res, err := cl.Do(req) - if err != nil { - return err - } - if res.StatusCode != http.StatusOK { - return fmt.Errorf("non-OK status from nameserver: %d", res.StatusCode) - } - return nil -} - -func (d *dockerer) deregisterWithDNS(ID string, ip string) error { - dnsip, err := d.client.GetContainerBridgeIP(WeaveContainer) - if err != nil { - return fmt.Errorf("nameserver not available: %s", err) - } - - req, err := http.NewRequest("DELETE", fmt.Sprintf("http://%s:6785/name/%s/%s", dnsip, ID, ip), nil) - if err != nil { - return err - } - - cl := &http.Client{} - res, err := cl.Do(req) - if err != nil { - return err - } - if res.StatusCode != http.StatusOK { - return fmt.Errorf("non-OK status from nameserver: %d", res.StatusCode) - } - return nil -} diff --git a/plugin/ipam.go b/plugin/ipam.go deleted file mode 100644 index bbc28aaf..00000000 --- a/plugin/ipam.go +++ /dev/null @@ -1,89 +0,0 @@ -package plugin - -import ( - "fmt" - "io/ioutil" - "net" - "net/http" - - . "github.com/weaveworks/weave/common" -) - -func (d *dockerer) ipamOp(ID string, op string) (*net.IPNet, error) { - weaveip, err := d.client.GetContainerBridgeIP(WeaveContainer) - Log.Debugf("IPAM operation %s for %s", op, ID) - if err != nil { - return nil, err - } - - var res *http.Response - - url := fmt.Sprintf("http://%s:6784/ip/%s", weaveip, ipamID(ID)) - Log.Debugf("Attempting to %s to %s", op, url) - if op == "POST" { - res, err = http.Post(url, "", nil) - } else if op == "GET" { - res, err = http.Get(url) - } - - if err != nil { - return nil, err - } - if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("received status %d from IPAM", res.StatusCode) - } - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return nil, err - } - return parseIP(string(body)) -} - -// returns an IP for the ID given, allocating a fresh one if necessary -func (d *dockerer) allocateIP(ID string) (*net.IPNet, error) { - return d.ipamOp(ID, "POST") -} - -// returns an IP for the ID given, or nil if one has not been -// allocated -func (d *dockerer) lookupIP(ID string) (*net.IPNet, error) { - return d.ipamOp(ID, "GET") -} - -// release an IP which is no longer needed -func (d *dockerer) releaseIP(ID string) error { - weaveip, err := d.client.GetContainerBridgeIP(WeaveContainer) - if err != nil { - return err - } - req, err := http.NewRequest("DELETE", fmt.Sprintf("http://%s:6784/ip/%s", weaveip, ipamID(ID)), nil) - if err != nil { - return err - } - cl := &http.Client{} - res, err := cl.Do(req) - if err != nil { - return err - } - if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusNoContent { - return fmt.Errorf("unexpected HTTP status code from IP release: %d", res.StatusCode) - } - return nil -} - -func parseIP(body string) (*net.IPNet, error) { - ip, ipnet, err := net.ParseCIDR(string(body)) - if err != nil { - return nil, err - } - ipnet.IP = ip - return ipnet, nil -} - -// If something looking like a container ID is supplied to IPAM, it -// will try to check that it's running, and return nothing (and empty -// string) if it's not. So I make sure it doesn't look like a -// container ID by including non-hex characters. -func ipamID(ID string) string { - return "endpoint:" + ID -} diff --git a/plugin/watcher.go b/plugin/watcher.go index 1bfafd03..3dbbe504 100644 --- a/plugin/watcher.go +++ b/plugin/watcher.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + weaveapi "github.com/weaveworks/weave/api" . "github.com/weaveworks/weave/common" "github.com/weaveworks/weave/common/docker" ) @@ -14,6 +15,7 @@ const ( type watcher struct { dockerer + weave *weaveapi.Client } type Watcher interface { @@ -33,6 +35,18 @@ func NewWatcher(client *docker.Client) (Watcher, error) { return w, nil } +func (w *watcher) haveWeaveClient() bool { + if w.weave == nil { + dnsip, err := w.client.GetContainerBridgeIP(WeaveContainer) + if err != nil { + Log.Warningf("nameserver not available: %s", err) + return false + } + w.weave = weaveapi.NewClient(dnsip) + } + return true +} + func (w *watcher) ContainerStarted(id string) { Log.Debugf("Container started %s", id) info, err := w.client.InspectContainer(id) @@ -41,11 +55,11 @@ func (w *watcher) ContainerStarted(id string) { return } // FIXME: check that it's on our network; but, the docker client lib doesn't know about .NetworkID - if isSubdomain(info.Config.Domainname, WeaveDomain) { + if isSubdomain(info.Config.Domainname, WeaveDomain) && w.haveWeaveClient() { // one of ours ip := info.NetworkSettings.IPAddress fqdn := fmt.Sprintf("%s.%s", info.Config.Hostname, info.Config.Domainname) - if err := w.registerWithDNS(id, fqdn, ip); err != nil { + if err := w.weave.RegisterWithDNS(id, fqdn, ip); err != nil { Log.Warningf("unable to register with weaveDNS: %s", err) } } @@ -58,9 +72,9 @@ func (w *watcher) ContainerDied(id string) { Log.Warningf("error inspecting container: %s", err) return } - if isSubdomain(info.Config.Domainname, WeaveDomain) { + if isSubdomain(info.Config.Domainname, WeaveDomain) && w.haveWeaveClient() { ip := info.NetworkSettings.IPAddress - if err := w.deregisterWithDNS(id, ip); err != nil { + if err := w.weave.DeregisterWithDNS(id, ip); err != nil { Log.Warningf("unable to deregister with weaveDNS: %s", err) } }