{"action":"created","comment":{"url":"https://api.github.com/repos/weaveworks/weave/pulls/comments/39844381","id":39844381,"diff_hunk":"@@ -0,0 +1,393 @@\n+package router\n+\n+import (\n+\t\"fmt\"\n+\t\"strings\"\n+\t\"sync\"\n+)\n+\n+// OverlaySwitch selects which overlay to use, from a set of\n+// subsidiary overlays.  First, it passes a list of supported overalys\n+// in the connection features, and uses that to determine which\n+// overlays are in common.  Then it tries those common overlays, and\n+// uses the best one that seems to be working.\n+\n+type OverlaySwitch struct {\n+\toverlays     map[string]Overlay\n+\toverlayNames []string\n+}\n+\n+func NewOverlaySwitch() *OverlaySwitch {\n+\treturn &OverlaySwitch{overlays: make(map[string]Overlay)}\n+}\n+\n+func (osw *OverlaySwitch) Add(name string, overlay Overlay) {\n+\t// check for repeated names\n+\tif _, present := osw.overlays[name]; present {\n+\t\tlog.Fatal(\"OverlaySwitch: repeated overlay name\")\n+\t}\n+\n+\tosw.overlays[name] = overlay\n+\tosw.overlayNames = append(osw.overlayNames, name)\n+}\n+\n+func (osw *OverlaySwitch) AddFeaturesTo(features map[string]string) {\n+\tfeatures[\"Overlays\"] = strings.Join(osw.overlayNames, \" \")\n+}\n+\n+func (osw *OverlaySwitch) InvalidateRoutes() {\n+\tfor _, overlay := range osw.overlays {\n+\t\toverlay.InvalidateRoutes()\n+\t}\n+}\n+\n+func (osw *OverlaySwitch) InvalidateShortIDs() {\n+\tfor _, overlay := range osw.overlays {\n+\t\toverlay.InvalidateShortIDs()\n+\t}\n+}\n+\n+func (osw *OverlaySwitch) StartConsumingPackets(localPeer *Peer, peers *Peers,\n+\tconsumer OverlayConsumer) error {\n+\tfor _, overlay := range osw.overlays {\n+\t\tif err := overlay.StartConsumingPackets(localPeer, peers,\n+\t\t\tconsumer); err != nil {\n+\t\t\treturn err\n+\t\t}\n+\t}\n+\treturn nil\n+}\n+\n+type namedOverlay struct {\n+\tOverlay\n+\tname string\n+}\n+\n+// Find the common set of overlays supported by both sides, with the\n+// ordering being the same on both sides too.\n+func (osw *OverlaySwitch) commonOverlays(params ForwarderParams) ([]namedOverlay, error) {\n+\tvar peerOverlays []string\n+\tif overlaysFeature, present := params.Features[\"Overlays\"]; present {\n+\t\tpeerOverlays = strings.Split(overlaysFeature, \" \")\n+\t}\n+\n+\tcommon := make(map[string]Overlay)\n+\tfor _, name := range peerOverlays {\n+\t\tif overlay := osw.overlays[name]; overlay != nil {\n+\t\t\tcommon[name] = overlay\n+\t\t}\n+\t}\n+\n+\tif len(common) == 0 {\n+\t\treturn nil, fmt.Errorf(\"no overlays in common with peer\")\n+\t}\n+\n+\t// we order them according to the connecting node\n+\tordering := osw.overlayNames\n+\tif params.RemoteAddr == nil {\n+\t\t// we are the connectee\n+\t\tordering = peerOverlays\n+\t}\n+\n+\tres := make([]namedOverlay, 0, len(common))\n+\tfor _, name := range ordering {\n+\t\toverlay := common[name]\n+\t\tif overlay != nil {\n+\t\t\tres = append(res, namedOverlay{overlay, name})\n+\t\t}\n+\t}\n+\n+\t// we use bytes to represent forwarder indices in control\n+\t// messages, so just in case:\n+\tif len(res) > 256 {\n+\t\tres = res[:256]\n+\t}\n+\n+\treturn res, nil\n+}\n+\n+type overlaySwitchForwarder struct {\n+\tremotePeer *Peer\n+\n+\tlock sync.Mutex\n+\n+\t// the forwarder to send on\n+\tbest OverlayForwarder\n+\n+\t// the subsidiary forwarders\n+\tforwarders []subForwarder\n+\n+\t// closed to tell the main goroutine to stop\n+\tstopChan chan<- struct{}\n+\n+\talreadyEstablished bool\n+\testablishedChan    chan struct{}\n+\terrorChan          chan error\n+}\n+\n+// A subsidiary forwarder\n+type subForwarder struct {\n+\tfwd         OverlayForwarder\n+\toverlayName string\n+\n+\t// Has the forwarder signalled that it is established?\n+\testablished bool\n+\n+\t// closed to tell the forwarder monitor goroutine to stop\n+\tstopChan chan<- struct{}\n+}\n+\n+// An event from a subsidiary forwarder\n+type subForwarderEvent struct {\n+\t// the index of the forwarder\n+\tindex int\n+\n+\t// is this an \"established\" event?\n+\testablished bool\n+\n+\t// is this an error event?\n+\terr error\n+}\n+\n+func (osw *OverlaySwitch) MakeForwarder(\n+\tparams ForwarderParams) (OverlayForwarder, error) {\n+\toverlays, err := osw.commonOverlays(params)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\t// channel to carry events from the subforwarder monitors to\n+\t// the main goroutine\n+\teventsChan := make(chan subForwarderEvent)\n+\n+\t// channel to stop the main goroutine\n+\tstopChan := make(chan struct{})\n+\n+\tfwd := &overlaySwitchForwarder{\n+\t\tremotePeer: params.RemotePeer,\n+\n+\t\tforwarders: make([]subForwarder, len(overlays)),\n+\t\tstopChan:   stopChan,\n+\n+\t\testablishedChan: make(chan struct{}),\n+\t\terrorChan:       make(chan error, 1),\n+\t}\n+\n+\torigSendControlMessage := params.SendControlMessage\n+\tfor i, overlay := range overlays {\n+\t\t// Prefix control messages to indicate the relevant forwarder\n+\t\tindex := i\n+\t\tparams.SendControlMessage = func(msg []byte) error {\n+\t\t\txmsg := make([]byte, len(msg)+1)\n+\t\t\txmsg[0] = byte(index)\n+\t\t\tcopy(xmsg[1:], msg)\n+\t\t\treturn origSendControlMessage(xmsg)\n+\t\t}\n+\n+\t\tsubFwd, err := overlay.MakeForwarder(params)\n+\t\tif err != nil {\n+\t\t\tfwd.stopFrom(0)\n+\t\t\treturn nil, err\n+\t\t}\n+\n+\t\tsubStopChan := make(chan struct{})\n+\t\tgo monitorForwarder(i, eventsChan, subStopChan, subFwd)\n+\t\tfwd.forwarders[i] = subForwarder{\n+\t\t\tfwd:         subFwd,\n+\t\t\toverlayName: overlay.name,\n+\t\t\tstopChan:    subStopChan,\n+\t\t}\n+\t}\n+\n+\tfwd.chooseBest()\n+\tgo fwd.run(eventsChan, stopChan)\n+\treturn fwd, nil\n+}\n+\n+func monitorForwarder(index int, eventsChan chan<- subForwarderEvent,\n+\tstopChan <-chan struct{}, fwd OverlayForwarder) {\n+\testablishedChan := fwd.EstablishedChannel()\n+loop:\n+\tfor {\n+\t\te := subForwarderEvent{index: index}\n+\n+\t\tselect {\n+\t\tcase <-establishedChan:\n+\t\t\te.established = true\n+\t\t\testablishedChan = nil\n+\n+\t\tcase err := <-fwd.ErrorChannel():\n+\t\t\te.err = err\n+\n+\t\tcase <-stopChan:\n+\t\t\tbreak loop\n+\t\t}\n+\n+\t\tselect {\n+\t\tcase eventsChan <- e:\n+\t\tcase <-stopChan:\n+\t\t\tbreak loop\n+\t\t}\n+\n+\t\tif e.err != nil {\n+\t\t\tbreak loop\n+\t\t}\n+\t}\n+\n+\tfwd.Stop()\n+}\n+\n+func (fwd *overlaySwitchForwarder) run(eventsChan <-chan subForwarderEvent,\n+\tstopChan <-chan struct{}) {\n+loop:\n+\tfor {\n+\t\tselect {\n+\t\tcase <-stopChan:\n+\t\t\tbreak loop\n+\n+\t\tcase e := <-eventsChan:\n+\t\t\tswitch {\n+\t\t\tcase e.established:\n+\t\t\t\tfwd.established(e.index)\n+\t\t\tcase e.err != nil:\n+\t\t\t\tfwd.error(e.index, e.err)\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tfwd.lock.Lock()\n+\tdefer fwd.lock.Unlock()\n+\tfwd.stopFrom(0)\n+}\n+\n+func (fwd *overlaySwitchForwarder) established(index int) {\n+\tfwd.lock.Lock()\n+\tdefer fwd.lock.Unlock()\n+\n+\tfwd.forwarders[index].established = true\n+\n+\t// less preferred forwarders are no longer needed\n+\tfwd.stopFrom(index + 1)\n+\n+\tif !fwd.alreadyEstablished {","path":"router/overlay_switch.go","position":272,"original_position":272,"commit_id":"e78cca3e5fe8f1d6fa858169e351681e53e9f7f6","original_commit_id":"e78cca3e5fe8f1d6fa858169e351681e53e9f7f6","user":{"login":"dpw","id":15074,"avatar_url":"https://avatars.githubusercontent.com/u/15074?v=3","gravatar_id":"","url":"https://api.github.com/users/dpw","html_url":"https://github.com/dpw","followers_url":"https://api.github.com/users/dpw/followers","following_url":"https://api.github.com/users/dpw/following{/other_user}","gists_url":"https://api.github.com/users/dpw/gists{/gist_id}","starred_url":"https://api.github.com/users/dpw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dpw/subscriptions","organizations_url":"https://api.github.com/users/dpw/orgs","repos_url":"https://api.github.com/users/dpw/repos","events_url":"https://api.github.com/users/dpw/events{/privacy}","received_events_url":"https://api.github.com/users/dpw/received_events","type":"User","site_admin":false},"body":"The intent of the Overlay API is that `EstablishedChannel` can be called at any point, returning the same channel.","created_at":"2015-09-18T11:06:29Z","updated_at":"2015-09-18T11:06:29Z","html_url":"https://github.com/weaveworks/weave/pull/1438#discussion_r39844381","pull_request_url":"https://api.github.com/repos/weaveworks/weave/pulls/1438","_links":{"self":{"href":"https://api.github.com/repos/weaveworks/weave/pulls/comments/39844381"},"html":{"href":"https://github.com/weaveworks/weave/pull/1438#discussion_r39844381"},"pull_request":{"href":"https://api.github.com/repos/weaveworks/weave/pulls/1438"}}},"pull_request":{"url":"https://api.github.com/repos/weaveworks/weave/pulls/1438","id":45070865,"html_url":"https://github.com/weaveworks/weave/pull/1438","diff_url":"https://github.com/weaveworks/weave/pull/1438.diff","patch_url":"https://github.com/weaveworks/weave/pull/1438.patch","issue_url":"https://api.github.com/repos/weaveworks/weave/issues/1438","number":1438,"state":"open","locked":false,"title":"Fast datapath","user":{"login":"dpw","id":15074,"avatar_url":"https://avatars.githubusercontent.com/u/15074?v=3","gravatar_id":"","url":"https://api.github.com/users/dpw","html_url":"https://github.com/dpw","followers_url":"https://api.github.com/users/dpw/followers","following_url":"https://api.github.com/users/dpw/following{/other_user}","gists_url":"https://api.github.com/users/dpw/gists{/gist_id}","starred_url":"https://api.github.com/users/dpw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dpw/subscriptions","organizations_url":"https://api.github.com/users/dpw/orgs","repos_url":"https://api.github.com/users/dpw/repos","events_url":"https://api.github.com/users/dpw/events{/privacy}","received_events_url":"https://api.github.com/users/dpw/received_events","type":"User","site_admin":false},"body":"Smoke tests pass (modulo the one that is broken on master), so I think this is fit for a review pass.\r\n\r\nThings still to do:\r\n\r\n* The `weave` script changes are still a bit grotty.  But...\r\n* I think there needs to be a \"disable it all\" option to weave, which disables fastdp, `--net=host`, and the MTU change.\r\n* I haven't modified architecture.txt. To be honest, most of the contents are more like implementation notes that belong as comments in the code.  If the fast datapath code requires additional notes of that nature, I'd be happy to add them as comments. A high-level overview of fast datapath would be a good thing, but I think I'll need to re-write the whole of architecture.txt to match.\r\n* 1.1 compatibility support.  That will be a separate issue.  Short ids is the tricky part here.\r\n* I need to check out a kernel oops I saw when testing with ubuntu 12.04 a few days ago.","created_at":"2015-09-15T16:23:44Z","updated_at":"2015-09-18T11:06:29Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6162af871a2de4dbe177f41c9ff16633086d8858","assignee":{"login":"awh","id":1504438,"avatar_url":"https://avatars.githubusercontent.com/u/1504438?v=3","gravatar_id":"","url":"https://api.github.com/users/awh","html_url":"https://github.com/awh","followers_url":"https://api.github.com/users/awh/followers","following_url":"https://api.github.com/users/awh/following{/other_user}","gists_url":"https://api.github.com/users/awh/gists{/gist_id}","starred_url":"https://api.github.com/users/awh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/awh/subscriptions","organizations_url":"https://api.github.com/users/awh/orgs","repos_url":"https://api.github.com/users/awh/repos","events_url":"https://api.github.com/users/awh/events{/privacy}","received_events_url":"https://api.github.com/users/awh/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/weaveworks/weave/pulls/1438/commits","review_comments_url":"https://api.github.com/repos/weaveworks/weave/pulls/1438/comments","review_comment_url":"https://api.github.com/repos/weaveworks/weave/pulls/comments{/number}","comments_url":"https://api.github.com/repos/weaveworks/weave/issues/1438/comments","statuses_url":"https://api.github.com/repos/weaveworks/weave/statuses/e78cca3e5fe8f1d6fa858169e351681e53e9f7f6","head":{"label":"weaveworks:fdp","ref":"fdp","sha":"e78cca3e5fe8f1d6fa858169e351681e53e9f7f6","user":{"login":"weaveworks","id":9976052,"avatar_url":"https://avatars.githubusercontent.com/u/9976052?v=3","gravatar_id":"","url":"https://api.github.com/users/weaveworks","html_url":"https://github.com/weaveworks","followers_url":"https://api.github.com/users/weaveworks/followers","following_url":"https://api.github.com/users/weaveworks/following{/other_user}","gists_url":"https://api.github.com/users/weaveworks/gists{/gist_id}","starred_url":"https://api.github.com/users/weaveworks/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weaveworks/subscriptions","organizations_url":"https://api.github.com/users/weaveworks/orgs","repos_url":"https://api.github.com/users/weaveworks/repos","events_url":"https://api.github.com/users/weaveworks/events{/privacy}","received_events_url":"https://api.github.com/users/weaveworks/received_events","type":"Organization","site_admin":false},"repo":{"id":23059575,"name":"weave","full_name":"weaveworks/weave","owner":{"login":"weaveworks","id":9976052,"avatar_url":"https://avatars.githubusercontent.com/u/9976052?v=3","gravatar_id":"","url":"https://api.github.com/users/weaveworks","html_url":"https://github.com/weaveworks","followers_url":"https://api.github.com/users/weaveworks/followers","following_url":"https://api.github.com/users/weaveworks/following{/other_user}","gists_url":"https://api.github.com/users/weaveworks/gists{/gist_id}","starred_url":"https://api.github.com/users/weaveworks/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weaveworks/subscriptions","organizations_url":"https://api.github.com/users/weaveworks/orgs","repos_url":"https://api.github.com/users/weaveworks/repos","events_url":"https://api.github.com/users/weaveworks/events{/privacy}","received_events_url":"https://api.github.com/users/weaveworks/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/weaveworks/weave","description":"Weaving Docker containers into applications","fork":false,"url":"https://api.github.com/repos/weaveworks/weave","forks_url":"https://api.github.com/repos/weaveworks/weave/forks","keys_url":"https://api.github.com/repos/weaveworks/weave/keys{/key_id}","collaborators_url":"https://api.github.com/repos/weaveworks/weave/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/weaveworks/weave/teams","hooks_url":"https://api.github.com/repos/weaveworks/weave/hooks","issue_events_url":"https://api.github.com/repos/weaveworks/weave/issues/events{/number}","events_url":"https://api.github.com/repos/weaveworks/weave/events","assignees_url":"https://api.github.com/repos/weaveworks/weave/assignees{/user}","branches_url":"https://api.github.com/repos/weaveworks/weave/branches{/branch}","tags_url":"https://api.github.com/repos/weaveworks/weave/tags","blobs_url":"https://api.github.com/repos/weaveworks/weave/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/weaveworks/weave/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/weaveworks/weave/git/refs{/sha}","trees_url":"https://api.github.com/repos/weaveworks/weave/git/trees{/sha}","statuses_url":"https://api.github.com/repos/weaveworks/weave/statuses/{sha}","languages_url":"https://api.github.com/repos/weaveworks/weave/languages","stargazers_url":"https://api.github.com/repos/weaveworks/weave/stargazers","contributors_url":"https://api.github.com/repos/weaveworks/weave/contributors","subscribers_url":"https://api.github.com/repos/weaveworks/weave/subscribers","subscription_url":"https://api.github.com/repos/weaveworks/weave/subscription","commits_url":"https://api.github.com/repos/weaveworks/weave/commits{/sha}","git_commits_url":"https://api.github.com/repos/weaveworks/weave/git/commits{/sha}","comments_url":"https://api.github.com/repos/weaveworks/weave/comments{/number}","issue_comment_url":"https://api.github.com/repos/weaveworks/weave/issues/comments{/number}","contents_url":"https://api.github.com/repos/weaveworks/weave/contents/{+path}","compare_url":"https://api.github.com/repos/weaveworks/weave/compare/{base}...{head}","merges_url":"https://api.github.com/repos/weaveworks/weave/merges","archive_url":"https://api.github.com/repos/weaveworks/weave/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/weaveworks/weave/downloads","issues_url":"https://api.github.com/repos/weaveworks/weave/issues{/number}","pulls_url":"https://api.github.com/repos/weaveworks/weave/pulls{/number}","milestones_url":"https://api.github.com/repos/weaveworks/weave/milestones{/number}","notifications_url":"https://api.github.com/repos/weaveworks/weave/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/weaveworks/weave/labels{/name}","releases_url":"https://api.github.com/repos/weaveworks/weave/releases{/id}","created_at":"2014-08-18T05:19:07Z","updated_at":"2015-09-18T10:19:49Z","pushed_at":"2015-09-17T13:43:42Z","git_url":"git://github.com/weaveworks/weave.git","ssh_url":"git@github.com:weaveworks/weave.git","clone_url":"https://github.com/weaveworks/weave.git","svn_url":"https://github.com/weaveworks/weave","homepage":"http://weave.works","size":17209,"stargazers_count":3702,"watchers_count":3702,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":227,"mirror_url":null,"open_issues_count":152,"forks":227,"open_issues":152,"watchers":3702,"default_branch":"master"}},"base":{"label":"weaveworks:master","ref":"master","sha":"14dde9ec322dc895b16505b11b50e93e9a8c4bf4","user":{"login":"weaveworks","id":9976052,"avatar_url":"https://avatars.githubusercontent.com/u/9976052?v=3","gravatar_id":"","url":"https://api.github.com/users/weaveworks","html_url":"https://github.com/weaveworks","followers_url":"https://api.github.com/users/weaveworks/followers","following_url":"https://api.github.com/users/weaveworks/following{/other_user}","gists_url":"https://api.github.com/users/weaveworks/gists{/gist_id}","starred_url":"https://api.github.com/users/weaveworks/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weaveworks/subscriptions","organizations_url":"https://api.github.com/users/weaveworks/orgs","repos_url":"https://api.github.com/users/weaveworks/repos","events_url":"https://api.github.com/users/weaveworks/events{/privacy}","received_events_url":"https://api.github.com/users/weaveworks/received_events","type":"Organization","site_admin":false},"repo":{"id":23059575,"name":"weave","full_name":"weaveworks/weave","owner":{"login":"weaveworks","id":9976052,"avatar_url":"https://avatars.githubusercontent.com/u/9976052?v=3","gravatar_id":"","url":"https://api.github.com/users/weaveworks","html_url":"https://github.com/weaveworks","followers_url":"https://api.github.com/users/weaveworks/followers","following_url":"https://api.github.com/users/weaveworks/following{/other_user}","gists_url":"https://api.github.com/users/weaveworks/gists{/gist_id}","starred_url":"https://api.github.com/users/weaveworks/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weaveworks/subscriptions","organizations_url":"https://api.github.com/users/weaveworks/orgs","repos_url":"https://api.github.com/users/weaveworks/repos","events_url":"https://api.github.com/users/weaveworks/events{/privacy}","received_events_url":"https://api.github.com/users/weaveworks/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/weaveworks/weave","description":"Weaving Docker containers into applications","fork":false,"url":"https://api.github.com/repos/weaveworks/weave","forks_url":"https://api.github.com/repos/weaveworks/weave/forks","keys_url":"https://api.github.com/repos/weaveworks/weave/keys{/key_id}","collaborators_url":"https://api.github.com/repos/weaveworks/weave/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/weaveworks/weave/teams","hooks_url":"https://api.github.com/repos/weaveworks/weave/hooks","issue_events_url":"https://api.github.com/repos/weaveworks/weave/issues/events{/number}","events_url":"https://api.github.com/repos/weaveworks/weave/events","assignees_url":"https://api.github.com/repos/weaveworks/weave/assignees{/user}","branches_url":"https://api.github.com/repos/weaveworks/weave/branches{/branch}","tags_url":"https://api.github.com/repos/weaveworks/weave/tags","blobs_url":"https://api.github.com/repos/weaveworks/weave/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/weaveworks/weave/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/weaveworks/weave/git/refs{/sha}","trees_url":"https://api.github.com/repos/weaveworks/weave/git/trees{/sha}","statuses_url":"https://api.github.com/repos/weaveworks/weave/statuses/{sha}","languages_url":"https://api.github.com/repos/weaveworks/weave/languages","stargazers_url":"https://api.github.com/repos/weaveworks/weave/stargazers","contributors_url":"https://api.github.com/repos/weaveworks/weave/contributors","subscribers_url":"https://api.github.com/repos/weaveworks/weave/subscribers","subscription_url":"https://api.github.com/repos/weaveworks/weave/subscription","commits_url":"https://api.github.com/repos/weaveworks/weave/commits{/sha}","git_commits_url":"https://api.github.com/repos/weaveworks/weave/git/commits{/sha}","comments_url":"https://api.github.com/repos/weaveworks/weave/comments{/number}","issue_comment_url":"https://api.github.com/repos/weaveworks/weave/issues/comments{/number}","contents_url":"https://api.github.com/repos/weaveworks/weave/contents/{+path}","compare_url":"https://api.github.com/repos/weaveworks/weave/compare/{base}...{head}","merges_url":"https://api.github.com/repos/weaveworks/weave/merges","archive_url":"https://api.github.com/repos/weaveworks/weave/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/weaveworks/weave/downloads","issues_url":"https://api.github.com/repos/weaveworks/weave/issues{/number}","pulls_url":"https://api.github.com/repos/weaveworks/weave/pulls{/number}","milestones_url":"https://api.github.com/repos/weaveworks/weave/milestones{/number}","notifications_url":"https://api.github.com/repos/weaveworks/weave/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/weaveworks/weave/labels{/name}","releases_url":"https://api.github.com/repos/weaveworks/weave/releases{/id}","created_at":"2014-08-18T05:19:07Z","updated_at":"2015-09-18T10:19:49Z","pushed_at":"2015-09-17T13:43:42Z","git_url":"git://github.com/weaveworks/weave.git","ssh_url":"git@github.com:weaveworks/weave.git","clone_url":"https://github.com/weaveworks/weave.git","svn_url":"https://github.com/weaveworks/weave","homepage":"http://weave.works","size":17209,"stargazers_count":3702,"watchers_count":3702,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":227,"mirror_url":null,"open_issues_count":152,"forks":227,"open_issues":152,"watchers":3702,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/weaveworks/weave/pulls/1438"},"html":{"href":"https://github.com/weaveworks/weave/pull/1438"},"issue":{"href":"https://api.github.com/repos/weaveworks/weave/issues/1438"},"comments":{"href":"https://api.github.com/repos/weaveworks/weave/issues/1438/comments"},"review_comments":{"href":"https://api.github.com/repos/weaveworks/weave/pulls/1438/comments"},"review_comment":{"href":"https://api.github.com/repos/weaveworks/weave/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/weaveworks/weave/pulls/1438/commits"},"statuses":{"href":"https://api.github.com/repos/weaveworks/weave/statuses/e78cca3e5fe8f1d6fa858169e351681e53e9f7f6"}}}}