diff --git a/.gitmodules b/.gitmodules index 918c7914..cb055f87 100644 --- a/.gitmodules +++ b/.gitmodules @@ -124,3 +124,9 @@ [submodule "vendor/github.com/containernetworking/cni"] path = vendor/github.com/containernetworking/cni url = https://github.com/containernetworking/cni +[submodule "vendor/golang.org/x/net"] + path = vendor/golang.org/x/net + url = https://go.googlesource.com/net +[submodule "vendor/google.golang.org/grpc"] + path = vendor/google.golang.org/grpc + url = https://github.com/grpc/grpc-go diff --git a/ipam/http.go b/ipam/http.go index d6e08e51..b024f3ae 100644 --- a/ipam/http.go +++ b/ipam/http.go @@ -198,17 +198,6 @@ func (alloc *Allocator) HandleHTTP(router *mux.Router, defaultSubnet address.CID w.WriteHeader(204) }) - router.Methods("DELETE").Path("/peer").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - alloc.Shutdown() - w.WriteHeader(204) - }) - - router.Methods("DELETE").Path("/peer/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ident := mux.Vars(r)["id"] - transferred := alloc.AdminTakeoverRanges(ident) - fmt.Fprintf(w, "%d IPs taken over from %s\n", transferred, ident) - }) - router.Methods("GET").Path("/ipinfo/tracker").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, tracker) }) 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..43bc8a6a --- /dev/null +++ b/prog/weaver/discovery.go @@ -0,0 +1,104 @@ +package main + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "time" + + "github.com/gorilla/mux" + + "github.com/weaveworks/weave/ipam" +) + +// 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"` + PeerCount int `json:"peercount"` +} + +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: (%v) %v", err, response) + return err +} + +func peerDiscoveryUpdate(discoveryEndpoint, token, peername, nickname string, addresses []string) ([]string, int, error) { + request := PeerUpdateRequest{ + Name: peername, + Nickname: nickname, + Addresses: addresses, + } + var updateResponse PeerUpdateResponse + err := do("POST", discoveryEndpoint, token, request, &updateResponse) + return updateResponse.Addresses, updateResponse.PeerCount, err +} + +func peerDiscoveryDelete(discoveryEndpoint, token, peername string) error { + request := PeerUpdateRequest{Name: peername} + return do("DELETE", discoveryEndpoint, token, request, nil) +} + +func HandleHTTPPeer(router *mux.Router, alloc *ipam.Allocator, discoveryEndpoint, token, peername string) { + router.Methods("DELETE").Path("/peer").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if discoveryEndpoint != "" { + if err := peerDiscoveryDelete(discoveryEndpoint, token, peername); err != nil { + Log.Errorf("Error while deleting self from peer discovery: %s", err) + } + } + if alloc != nil { + alloc.Shutdown() + } + w.WriteHeader(204) + }) + + router.Methods("DELETE").Path("/peer/{id}").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ident := mux.Vars(r)["id"] + if discoveryEndpoint != "" { + // TODO: deal with this being either a peername or a nickname + if err := peerDiscoveryDelete(discoveryEndpoint, token, ident); err != nil { + Log.Errorf("Error while deleting self from peer discovery: %s", err) + } + } + if alloc != nil { + transferred := alloc.AdminTakeoverRanges(ident) + fmt.Fprintf(w, "%d IPs taken over from %s\n", transferred, ident) + } + }) +} diff --git a/prog/weaver/main.go b/prog/weaver/main.go index 9e409021..a5871ec7 100644 --- a/prog/weaver/main.go +++ b/prog/weaver/main.go @@ -53,7 +53,7 @@ type dnsConfig struct { func (c *ipamConfig) Enabled() bool { var ( hasPeerCount = c.PeerCount > 0 - hasMode = len(c.Mode) > 0 + hasMode = c.HasMode() hasRange = c.IPRangeCIDR != "" hasSubnet = c.IPSubnetCIDR != "" ) @@ -75,6 +75,10 @@ func (c *ipamConfig) Enabled() bool { return true } +func (c ipamConfig) HasMode() bool { + return len(c.Mode) > 0 +} + func (c *ipamConfig) parseMode() error { modeAndParam := strings.SplitN(c.Mode, "=", 2) @@ -143,6 +147,9 @@ func main() { trustedSubnetStr string dbPrefix string isAWSVPC bool + discoveryEndpoint string + token string + advertiseAddress string defaultDockerHost = "unix:///var/run/docker.sock" ) @@ -184,6 +191,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/net", "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 +267,24 @@ 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, count, err := peerDiscoveryUpdate(discoveryEndpoint, token, name.String(), nickName, addresses) + checkFatal(err) + if !ipamConfig.HasMode() { + ipamConfig.PeerCount = len(peers) + count + } + peers = append(peers, discoveredPeers...) + } else if peers, err = router.InitialPeers(resume, peers); err != nil { Log.Fatal("Unable to get initial peer set: ", err) } @@ -352,6 +379,7 @@ func main() { } router.HandleHTTP(muxRouter) HandleHTTP(muxRouter, version, router, allocator, defaultSubnet, ns, dnsserver) + HandleHTTPPeer(muxRouter, allocator, discoveryEndpoint, token, name.String()) muxRouter.Methods("GET").Path("/metrics").Handler(metricsHandler(router, allocator, ns, dnsserver)) http.Handle("/", common.LoggingHTTPHandler(muxRouter)) Log.Println("Listening for HTTP control messages on", httpAddr) @@ -376,7 +404,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 diff --git a/site/using-weave.md b/site/using-weave.md index a27ae983..1d6442e9 100644 --- a/site/using-weave.md +++ b/site/using-weave.md @@ -135,5 +135,6 @@ Weave Net supports *any* protocol, and it doesn't have to be over TCP/IP. For ex **See Also** * [Installing Weave Net](/site/installing-weave.md) + * [Using Weave Cloud to get started with Weave Net](/site/using-weave/weave-cloud.md) * [Using Fastdp With Weave Net](/site/using-weave/fastdp.md) * [Using the Weave Net Docker Network Plugin](/site/plugin.md) diff --git a/site/using-weave/weave-cloud-token.png b/site/using-weave/weave-cloud-token.png new file mode 100644 index 00000000..165ea8b8 Binary files /dev/null and b/site/using-weave/weave-cloud-token.png differ diff --git a/site/using-weave/weave-cloud.md b/site/using-weave/weave-cloud.md new file mode 100644 index 00000000..c9a25cac --- /dev/null +++ b/site/using-weave/weave-cloud.md @@ -0,0 +1,126 @@ +--- +title: Using Weave Cloud to get started with Weave Net +menu_order: 10 +--- + +Weave Net provides a simple to deploy networking solution for containerized apps. Here, we describe how to manage a Weave container network using a sample application which consists of two simple `netcat` services deployed to containers on two separate hosts. + +This section contains the following topics: + + * [Signing in to Weave Cloud](#signing-in) + * [Launching Weave Net](#launching) + * [Launching on Additional Hosts](#peer-connections) + * [How Peer Discovery Works](#how-it-works) + +### Signing in to Weave Cloud + +Weave Cloud is a fast and simple way to visualize, manage and monitor +containers and microservices. In order to use Weave Cloud to set up +Weave Net, you must sign up to Weave Cloud and create an "Instance". + +The instance will have a Service Token which is a 32-character string +used to uniquely identify it. Get hold of the Service Token for your +Instance, as shown below: + +![UI to get a Service Token](weave-cloud-token.png) + +For the purpose of these instructions we will use a dummy Service +Token `dk6hz1acdd58aea6b75migoy4axqasdf`. + + +### Launching Weave Net + +Before launching Weave Net and deploying your apps, ensure that Docker +is [installed](https://docs.docker.com/engine/installation/) on both +hosts. + +On `$HOST1` run: + + host1$ weave launch --token=dk6hz1acdd58aea6b75migoy4axqasdf + host1$ eval $(weave env) + host1$ docker run --name a1 -ti weaveworks/ubuntu + +Where, + + * The first line runs Weave Net. + +* The second line configures the Weave Net environment, so that containers launched via the Docker command line are automatically attached to the Weave network, and, + * The third line runs the [application container](/site/faq.md#own-image) using [a Docker command](https://docs.docker.com/engine/reference/run/). + +> **Note** If the first command results in an error like +> ``` +> Cannot connect to the Docker daemon. Is the docker daemon running on this host? +> ``` +> then you likely need to be 'root' in order to connect to the Docker +> daemon. If so, run the above and all subsequent commands in a +> *single* root shell: +> ``` +> host1$ sudo -s +> host1# weave launch --token=dk6hz1acdd58aea6b75migoy4axqasdf +> host1# eval $(weave env) +> host1# docker run --name a1 -ti weaveworks/ubuntu +> ``` +> Do *not* prefix individual commands with `sudo`, since some commands +> modify environment entries and hence they all need to be executed from +> the same shell. + +Weave Net must be launched once per host. The relevant container images will be pulled down from Docker Hub on demand during `weave launch`. + +You can also preload the images by running `weave setup`. Preloaded images are useful for automated deployments, and ensure there are no delays during later operations. + +If you are deploying an application that consists of more than one container to the same host, launch them one after another using `docker run`, as appropriate. + + +### Launching on Additional Hosts + +To launch Weave Net on an additional host and create a peer +connection, run exactly the same commands as on the first host: + + host2$ weave launch --token=dk6hz1acdd58aea6b75migoy4axqasdf + host2$ eval $(weave env) + host2$ docker run --name a2 -ti weaveworks/ubuntu + +Because you are using Weave Cloud, the second peer will discover the +first and automatically create a connection to it. + +>**Note:** If there is a firewall between `$HOST1` and `$HOST2`, you must permit traffic to flow through TCP 6783 and UDP 6783/6784, which are Weave’s control and data ports. + +Now you can proceed to [test container communications](site/using-weave#testing) + +### How Peer Discovery Works + +The Weave Cloud back-end maintains a database of all the peers that +have registered in each instance. You can create as many instances as +you like in the Weave Cloud console: for different projects, or to +separate test and production. + +As each peer starts up, it looks for all network instances that +another peer might be able to contact it on, and registers those with the back-end. +If you want to control which addresses are used, add the `--advertise-address` flag: + + host1$ weave launch --token=dk6hz1acdd58aea6b75migoy4axqasdf --advertise-address=192.0.2.1,198.51.100.4 + +When the new peer registers itself, it also fetches a list of all +other peers in the instance, and begins to contact them. + +If the peer cannot contact the Weave Cloud back-end it will print an +error message in its log and exit. To check the logs run: + + host1$ docker logs weave + +When a Weave Net instance is formed for the first time, the peers must +initialize the [IP Allocation Manager](site/ipam.md). This process, +and the configuration options available, are unchanged when using +Weave Cloud. + +When you shut down a peer for good, use [the `weave reset` +command](site/ipam/stop-remove-peers-ipam.md) and it will be removed +from the Weave Cloud database so future peers will not attempt to +connect to it. + +**See Also** + + * [Installing Weave Net](/site/installing-weave.md) + * [Using Weave Net](/site/using-weave.md) + * [Using Fastdp With Weave Net](/site/using-weave/fastdp.md) + * [Using the Weave Net Docker Network Plugin](/site/plugin.md) diff --git a/vendor/github.com/weaveworks/common b/vendor/github.com/weaveworks/common index 08ae9c83..14d8aacf 160000 --- a/vendor/github.com/weaveworks/common +++ b/vendor/github.com/weaveworks/common @@ -1 +1 @@ -Subproject commit 08ae9c839237b80dd6fd41b8570573d1528cec3b +Subproject commit 14d8aacf4ca7808fa278dca181fb6669a02dabd1 diff --git a/vendor/golang.org/x/net b/vendor/golang.org/x/net new file mode 160000 index 00000000..d379faa2 --- /dev/null +++ b/vendor/golang.org/x/net @@ -0,0 +1 @@ +Subproject commit d379faa25cbdc04d653984913a2ceb43b0bc46d7 diff --git a/vendor/google.golang.org/grpc b/vendor/google.golang.org/grpc new file mode 160000 index 00000000..1dab9337 --- /dev/null +++ b/vendor/google.golang.org/grpc @@ -0,0 +1 @@ +Subproject commit 1dab93372523195731c738b0f0cb4e452228e959