diff --git a/net/if.go b/net/if.go index d4972cad..55445e87 100644 --- a/net/if.go +++ b/net/if.go @@ -82,3 +82,25 @@ func findInterface(ifaceName string) (iface *net.Interface, err error) { } return } + +func LocalAddresses() ([]*net.IPNet, error) { + links, err := netlink.LinkList() + if err != nil { + return nil, err + } + + retval := []*net.IPNet{} + for _, link := range links { + if _, isBridge := link.(*netlink.Bridge); isBridge || (link.Attrs().Flags&net.FlagLoopback) != 0 { + continue + } + addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) + if err != nil { + return nil, err + } + for _, addr := range addrs { + retval = append(retval, addr.IPNet) + } + } + return retval, nil +} diff --git a/prog/weaver/discovery.go b/prog/weaver/discovery.go new file mode 100644 index 00000000..107a27ee --- /dev/null +++ b/prog/weaver/discovery.go @@ -0,0 +1,66 @@ +package main + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "time" +) + +// TODO: move these definitions somewhere more shareable +type PeerUpdateRequest struct { + Name string `json:"peername"` + Nickname string `json:"nickname"` // optional + Addresses []string `json:"addresses"` // can be empty +} + +type PeerUpdateResponse struct { + Addresses []string `json:"addresses"` +} + +var httpClient = &http.Client{Timeout: 30 * time.Second} + +func do(verb string, discoveryEndpoint, token string, request interface{}, response interface{}) error { + body := new(bytes.Buffer) + err := json.NewEncoder(body).Encode(request) + if err != nil { + return err + } + req, err := http.NewRequest(verb, discoveryEndpoint+"/peer", body) + if err != nil { + return err + } + req.Header.Set("Authorization", fmt.Sprintf("Scope-Probe token=%s", token)) + req.Header.Set("X-Weave-Net-Version", version) + req.Header.Set("Content-Type", "application/json") + Log.Printf("Calling peer discovery %s with %s", req.URL, request) + resp, err := httpClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + rbody, _ := ioutil.ReadAll(resp.Body) + return errors.New(resp.Status + ": " + string(rbody)) + } + if response == nil { + return nil + } + err = json.NewDecoder(resp.Body).Decode(response) + Log.Printf("peer discovery result: (%s) %v", err, response) + return err +} + +func peerDiscoveryUpdate(discoveryEndpoint, token, peername, nickname string, addresses []string) ([]string, error) { + request := PeerUpdateRequest{ + Name: peername, + Nickname: nickname, + Addresses: addresses, + } + var updateResponse PeerUpdateResponse + err := do("POST", discoveryEndpoint, token, request, &updateResponse) + return updateResponse.Addresses, err +} diff --git a/prog/weaver/main.go b/prog/weaver/main.go index 9e409021..f6090f37 100644 --- a/prog/weaver/main.go +++ b/prog/weaver/main.go @@ -143,6 +143,9 @@ func main() { trustedSubnetStr string dbPrefix string isAWSVPC bool + discoveryEndpoint string + token string + advertiseAddress string defaultDockerHost = "unix:///var/run/docker.sock" ) @@ -184,6 +187,9 @@ func main() { mflag.StringVar(&trustedSubnetStr, []string{"-trusted-subnets"}, "", "comma-separated list of trusted subnets in CIDR notation") mflag.StringVar(&dbPrefix, []string{"-db-prefix"}, "/weavedb/weave", "pathname/prefix of filename to store data") mflag.BoolVar(&isAWSVPC, []string{"-awsvpc"}, false, "use AWS VPC for routing") + mflag.StringVar(&discoveryEndpoint, []string{"-peer-discovery-url"}, "https://cloud.weave.works/api/weavenet", "url for peer discovery") + mflag.StringVar(&token, []string{"-token"}, "", "token for peer discovery") + mflag.StringVar(&advertiseAddress, []string{"-advertise-address"}, "", "address to advertise for peer discovery") // crude way of detecting that we probably have been started in a // container, with `weave launch` --> suppress misleading paths in @@ -257,7 +263,21 @@ func main() { router := weave.NewNetworkRouter(config, networkConfig, name, nickName, overlay, db) Log.Println("Our name is", router.Ourself) - if peers, err = router.InitialPeers(resume, peers); err != nil { + if token != "" { + var addresses []string + if advertiseAddress == "" { + localAddrs, err := weavenet.LocalAddresses() + checkFatal(err) + for _, addr := range localAddrs { + addresses = append(addresses, addr.IP.String()) + } + } else { + addresses = strings.Split(advertiseAddress, ",") + } + discoveredPeers, err := peerDiscoveryUpdate(discoveryEndpoint, token, name.String(), nickName, addresses) + checkFatal(err) // TODO: what if it is a transient error, and we are restarting? + peers = append(peers, discoveredPeers...) + } else if peers, err = router.InitialPeers(resume, peers); err != nil { Log.Fatal("Unable to get initial peer set: ", err) } @@ -376,7 +396,7 @@ func options() map[string]string { mflag.Visit(func(f *mflag.Flag) { value := f.Value.String() name := canonicalName(f) - if name == "password" { + if name == "password" || name == "token" { value = "" } options[name] = value