GitHub PullRequestReviewCommentEvent at 2014-10-23 21:00:39
Repository: etcd
Actor: unihorn
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/coreos/etcd/pulls/comments/19307358","id":19307358,"diff_hunk":"@@ -19,131 +19,127 @@ package etcdserver\n import (\n \t\"crypto/sha1\"\n \t\"encoding/binary\"\n+\t\"encoding/json\"\n \t\"fmt\"\n-\t\"math/rand\"\n+\t\"log\"\n \t\"net/url\"\n+\t\"path\"\n \t\"sort\"\n \t\"strings\"\n \n+\tetcdErr \"github.com/coreos/etcd/error\"\n \t\"github.com/coreos/etcd/pkg/flags\"\n \t\"github.com/coreos/etcd/pkg/types\"\n+\t\"github.com/coreos/etcd/store\"\n )\n \n+const (\n+\traftAttributesSuffix = \"raftAttributes\"\n+\tattributesSuffix     = \"attributes\"\n+)\n+\n+type ClusterInfo interface {\n+\tID() uint64\n+\tClientURLs() []string\n+\tMembers() map[uint64]*Member\n+\tMember(id uint64) *Member\n+}\n+\n // Cluster is a list of Members that belong to the same raft cluster\n type Cluster struct {\n \tid      uint64\n \tname    string\n \tmembers map[uint64]*Member\n+\t// removed contains the ids of removed members in the cluster.\n+\t// removed id cannot be reused.\n+\tremoved map[uint64]bool\n+\tstore   store.Store\n }\n \n-func NewCluster(clusterName string) *Cluster {\n-\treturn &Cluster{name: clusterName, members: make(map[uint64]*Member)}\n-}\n+// NewClusterFromString returns Cluster through given clusterName and parsing\n+// members from a sets of names to IPs discovery formatted like:\n+// mach0=http://1.1.1.1,mach0=http://2.2.2.2,mach0=http://1.1.1.1,mach1=http://2.2.2.2,mach1=http://3.3.3.3\n+func NewClusterFromString(name string, cluster string) (*Cluster, error) {\n+\tc := newCluster(name)\n \n-func (c Cluster) FindName(name string) *Member {\n-\tfor _, m := range c.members {\n-\t\tif m.Name == name {\n-\t\t\treturn m\n+\tv, err := url.ParseQuery(strings.Replace(cluster, \",\", \"&\", -1))\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\tfor name, urls := range v {\n+\t\tif len(urls) == 0 || urls[0] == \"\" {\n+\t\t\treturn nil, fmt.Errorf(\"Empty URL given for %q\", name)\n \t\t}\n+\t\tm := NewMember(name, types.URLs(*flags.NewURLsValue(strings.Join(urls, \",\"))), c.name, nil)\n+\t\tif _, ok := c.members[m.ID]; ok {\n+\t\t\treturn nil, fmt.Errorf(\"Member exists with identical ID %v\", m)\n+\t\t}\n+\t\tc.members[m.ID] = m\n \t}\n-\treturn nil\n+\tc.genID()\n+\treturn c, nil\n }\n \n-func (c Cluster) FindID(id uint64) *Member {\n-\treturn c.members[id]\n-}\n+func NewClusterFromStore(name string, st store.Store) *Cluster {\n+\tc := newCluster(name)\n+\tc.store = st\n \n-func (c Cluster) Add(m Member) error {\n-\tif c.FindID(m.ID) != nil {\n-\t\treturn fmt.Errorf(\"Member exists with identical ID %v\", m)\n+\te, err := c.store.Get(storeMembersPrefix, true, true)\n+\tif err != nil {\n+\t\tif isKeyNotFound(err) {\n+\t\t\treturn c\n+\t\t}\n+\t\tlog.Panicf(\"get storeMembers should never fail: %v\", err)\n \t}\n-\tc.members[m.ID] = &m\n-\treturn nil\n-}\n-\n-func (c *Cluster) AddSlice(mems []Member) error {\n-\tfor _, m := range mems {\n-\t\terr := c.Add(m)\n+\tfor _, n := range e.Node.Nodes {\n+\t\tm, err := nodeToMember(n)\n \t\tif err != nil {\n-\t\t\treturn err\n+\t\t\tlog.Panicf(\"unexpected nodeToMember error: %v\", err)\n \t\t}\n+\t\tc.members[m.ID] = m\n \t}\n \n-\treturn nil\n-}\n-\n-// Pick chooses a random address from a given Member's addresses, and returns it as\n-// an addressible URI. If the given member does not exist, an empty string is returned.\n-func (c Cluster) Pick(id uint64) string {\n-\tif m := c.FindID(id); m != nil {\n-\t\turls := m.PeerURLs\n-\t\tif len(urls) == 0 {\n-\t\t\treturn \"\"\n+\te, err = c.store.Get(storeRemovedMembersPrefix, true, true)\n+\tif err != nil {\n+\t\tif isKeyNotFound(err) {\n+\t\t\treturn c\n \t\t}\n-\t\treturn urls[rand.Intn(len(urls))]\n+\t\tlog.Panicf(\"get storeRemovedMembers should never fail: %v\", err)\n \t}\n-\n-\treturn \"\"\n-}\n-\n-// SetMembersFromString parses a sets of names to IPs either from the command line or discovery formatted like:\n-// mach0=http://1.1.1.1,mach0=http://2.2.2.2,mach0=http://1.1.1.1,mach1=http://2.2.2.2,mach1=http://3.3.3.3\n-func (c *Cluster) SetMembersFromString(s string) error {\n-\tc.members = make(map[uint64]*Member)\n-\tv, err := url.ParseQuery(strings.Replace(s, \",\", \"&\", -1))\n-\tif err != nil {\n-\t\treturn err\n+\tfor _, n := range e.Node.Nodes {\n+\t\tc.removed[parseMemberID(n.Key)] = true\n \t}\n \n-\tfor name, urls := range v {\n-\t\tif len(urls) == 0 || urls[0] == \"\" {\n-\t\t\treturn fmt.Errorf(\"Empty URL given for %q\", name)\n-\t\t}\n-\n-\t\tm := NewMember(name, types.URLs(*flags.NewURLsValue(strings.Join(urls, \",\"))), c.name, nil)\n-\t\terr := c.Add(*m)\n-\t\tif err != nil {\n-\t\t\treturn err\n-\t\t}\n-\t}\n-\treturn nil\n+\treturn c\n }\n \n-func (c *Cluster) AddMemberFromURLs(name string, urls types.URLs) (*Member, error) {\n-\tm := NewMember(name, urls, c.name, nil)\n-\terr := c.Add(*m)\n-\tif err != nil {\n-\t\treturn nil, err\n+func newCluster(name string) *Cluster {\n+\treturn &Cluster{\n+\t\tname:    name,\n+\t\tmembers: make(map[uint64]*Member),\n+\t\tremoved: make(map[uint64]bool),\n \t}\n-\treturn m, nil\n }\n \n-func (c *Cluster) GenID(salt []byte) {\n-\tmIDs := c.MemberIDs()\n-\tb := make([]byte, 8*len(mIDs))\n-\tfor i, id := range mIDs {\n-\t\tbinary.BigEndian.PutUint64(b[8*i:], id)\n-\t}\n-\tb = append(b, salt...)\n-\thash := sha1.Sum(b)\n-\tc.id = binary.BigEndian.Uint64(hash[:8])\n+func (c Cluster) ID() uint64 { return c.id }\n+\n+func (c Cluster) Members() map[uint64]*Member { return c.members }\n+\n+func (c *Cluster) Member(id uint64) *Member {\n+\treturn c.members[id]\n }\n \n-func (c Cluster) String() string {\n-\tsl := []string{}\n+// MemberByName returns a Member with the given name if exists.\n+// If more than one member has the given name, it will return one randomly.","path":"etcdserver/cluster.go","position":189,"original_position":189,"commit_id":"8eee8c260e3a57af8bc8c0635c2921ddd5f59885","original_commit_id":"9d2f294fb0f889b85f7b340a2847602f71da0c98","user":{"login":"unihorn","id":2976232,"avatar_url":"https://avatars.githubusercontent.com/u/2976232?v=2","gravatar_id":"","url":"https://api.github.com/users/unihorn","html_url":"https://github.com/unihorn","followers_url":"https://api.github.com/users/unihorn/followers","following_url":"https://api.github.com/users/unihorn/following{/other_user}","gists_url":"https://api.github.com/users/unihorn/gists{/gist_id}","starred_url":"https://api.github.com/users/unihorn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unihorn/subscriptions","organizations_url":"https://api.github.com/users/unihorn/orgs","repos_url":"https://api.github.com/users/unihorn/repos","events_url":"https://api.github.com/users/unihorn/events{/privacy}","received_events_url":"https://api.github.com/users/unihorn/received_events","type":"User","site_admin":false},"body":"yes. we could even panic it if it has two members with the given name for safety. ","created_at":"2014-10-23T21:00:39Z","updated_at":"2014-10-23T21:00:39Z","html_url":"https://github.com/coreos/etcd/pull/1361#discussion_r19307358","pull_request_url":"https://api.github.com/repos/coreos/etcd/pulls/1361","_links":{"self":{"href":"https://api.github.com/repos/coreos/etcd/pulls/comments/19307358"},"html":{"href":"https://github.com/coreos/etcd/pull/1361#discussion_r19307358"},"pull_request":{"href":"https://api.github.com/repos/coreos/etcd/pulls/1361"}}},"pull_request":{"url":"https://api.github.com/repos/coreos/etcd/pulls/1361","id":23197251,"html_url":"https://github.com/coreos/etcd/pull/1361","diff_url":"https://github.com/coreos/etcd/pull/1361.diff","patch_url":"https://github.com/coreos/etcd/pull/1361.patch","issue_url":"https://api.github.com/repos/coreos/etcd/issues/1361","number":1361,"state":"open","locked":false,"title":"etcdserver: refactor cluster and clusterStore","user":{"login":"unihorn","id":2976232,"avatar_url":"https://avatars.githubusercontent.com/u/2976232?v=2","gravatar_id":"","url":"https://api.github.com/users/unihorn","html_url":"https://github.com/unihorn","followers_url":"https://api.github.com/users/unihorn/followers","following_url":"https://api.github.com/users/unihorn/following{/other_user}","gists_url":"https://api.github.com/users/unihorn/gists{/gist_id}","starred_url":"https://api.github.com/users/unihorn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unihorn/subscriptions","organizations_url":"https://api.github.com/users/unihorn/orgs","repos_url":"https://api.github.com/users/unihorn/repos","events_url":"https://api.github.com/users/unihorn/events{/privacy}","received_events_url":"https://api.github.com/users/unihorn/received_events","type":"User","site_admin":false},"body":"Integrate clusterStore into cluster, and let cluster become the source of\r\ncluster info.\r\n\r\nfixes #1354 \r\n\r\nearly feedback welcome","created_at":"2014-10-22T23:18:27Z","updated_at":"2014-10-23T21:00:39Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7f079f6cbc6ee2be5a4c859ddc82d8b7979d1def","assignee":null,"milestone":{"url":"https://api.github.com/repos/coreos/etcd/milestones/5","labels_url":"https://api.github.com/repos/coreos/etcd/milestones/5/labels","id":782754,"number":5,"title":"v0.5.0-alpha","description":"v0.5 alpha release","creator":{"login":"jonboulle","id":2601015,"avatar_url":"https://avatars.githubusercontent.com/u/2601015?v=2","gravatar_id":"","url":"https://api.github.com/users/jonboulle","html_url":"https://github.com/jonboulle","followers_url":"https://api.github.com/users/jonboulle/followers","following_url":"https://api.github.com/users/jonboulle/following{/other_user}","gists_url":"https://api.github.com/users/jonboulle/gists{/gist_id}","starred_url":"https://api.github.com/users/jonboulle/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jonboulle/subscriptions","organizations_url":"https://api.github.com/users/jonboulle/orgs","repos_url":"https://api.github.com/users/jonboulle/repos","events_url":"https://api.github.com/users/jonboulle/events{/privacy}","received_events_url":"https://api.github.com/users/jonboulle/received_events","type":"User","site_admin":false},"open_issues":8,"closed_issues":87,"state":"open","created_at":"2014-09-08T21:14:05Z","updated_at":"2014-10-23T20:48:03Z","due_on":"2014-10-24T07:00:00Z"},"commits_url":"https://api.github.com/repos/coreos/etcd/pulls/1361/commits","review_comments_url":"https://api.github.com/repos/coreos/etcd/pulls/1361/comments","review_comment_url":"https://api.github.com/repos/coreos/etcd/pulls/comments/{number}","comments_url":"https://api.github.com/repos/coreos/etcd/issues/1361/comments","statuses_url":"https://api.github.com/repos/coreos/etcd/statuses/8eee8c260e3a57af8bc8c0635c2921ddd5f59885","head":{"label":"unihorn:182","ref":"182","sha":"8eee8c260e3a57af8bc8c0635c2921ddd5f59885","user":{"login":"unihorn","id":2976232,"avatar_url":"https://avatars.githubusercontent.com/u/2976232?v=2","gravatar_id":"","url":"https://api.github.com/users/unihorn","html_url":"https://github.com/unihorn","followers_url":"https://api.github.com/users/unihorn/followers","following_url":"https://api.github.com/users/unihorn/following{/other_user}","gists_url":"https://api.github.com/users/unihorn/gists{/gist_id}","starred_url":"https://api.github.com/users/unihorn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unihorn/subscriptions","organizations_url":"https://api.github.com/users/unihorn/orgs","repos_url":"https://api.github.com/users/unihorn/repos","events_url":"https://api.github.com/users/unihorn/events{/privacy}","received_events_url":"https://api.github.com/users/unihorn/received_events","type":"User","site_admin":false},"repo":{"id":16744058,"name":"etcd","full_name":"unihorn/etcd","owner":{"login":"unihorn","id":2976232,"avatar_url":"https://avatars.githubusercontent.com/u/2976232?v=2","gravatar_id":"","url":"https://api.github.com/users/unihorn","html_url":"https://github.com/unihorn","followers_url":"https://api.github.com/users/unihorn/followers","following_url":"https://api.github.com/users/unihorn/following{/other_user}","gists_url":"https://api.github.com/users/unihorn/gists{/gist_id}","starred_url":"https://api.github.com/users/unihorn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unihorn/subscriptions","organizations_url":"https://api.github.com/users/unihorn/orgs","repos_url":"https://api.github.com/users/unihorn/repos","events_url":"https://api.github.com/users/unihorn/events{/privacy}","received_events_url":"https://api.github.com/users/unihorn/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/unihorn/etcd","description":"A highly-available key value store for shared configuration and service discovery","fork":true,"url":"https://api.github.com/repos/unihorn/etcd","forks_url":"https://api.github.com/repos/unihorn/etcd/forks","keys_url":"https://api.github.com/repos/unihorn/etcd/keys{/key_id}","collaborators_url":"https://api.github.com/repos/unihorn/etcd/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/unihorn/etcd/teams","hooks_url":"https://api.github.com/repos/unihorn/etcd/hooks","issue_events_url":"https://api.github.com/repos/unihorn/etcd/issues/events{/number}","events_url":"https://api.github.com/repos/unihorn/etcd/events","assignees_url":"https://api.github.com/repos/unihorn/etcd/assignees{/user}","branches_url":"https://api.github.com/repos/unihorn/etcd/branches{/branch}","tags_url":"https://api.github.com/repos/unihorn/etcd/tags","blobs_url":"https://api.github.com/repos/unihorn/etcd/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/unihorn/etcd/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/unihorn/etcd/git/refs{/sha}","trees_url":"https://api.github.com/repos/unihorn/etcd/git/trees{/sha}","statuses_url":"https://api.github.com/repos/unihorn/etcd/statuses/{sha}","languages_url":"https://api.github.com/repos/unihorn/etcd/languages","stargazers_url":"https://api.github.com/repos/unihorn/etcd/stargazers","contributors_url":"https://api.github.com/repos/unihorn/etcd/contributors","subscribers_url":"https://api.github.com/repos/unihorn/etcd/subscribers","subscription_url":"https://api.github.com/repos/unihorn/etcd/subscription","commits_url":"https://api.github.com/repos/unihorn/etcd/commits{/sha}","git_commits_url":"https://api.github.com/repos/unihorn/etcd/git/commits{/sha}","comments_url":"https://api.github.com/repos/unihorn/etcd/comments{/number}","issue_comment_url":"https://api.github.com/repos/unihorn/etcd/issues/comments/{number}","contents_url":"https://api.github.com/repos/unihorn/etcd/contents/{+path}","compare_url":"https://api.github.com/repos/unihorn/etcd/compare/{base}...{head}","merges_url":"https://api.github.com/repos/unihorn/etcd/merges","archive_url":"https://api.github.com/repos/unihorn/etcd/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/unihorn/etcd/downloads","issues_url":"https://api.github.com/repos/unihorn/etcd/issues{/number}","pulls_url":"https://api.github.com/repos/unihorn/etcd/pulls{/number}","milestones_url":"https://api.github.com/repos/unihorn/etcd/milestones{/number}","notifications_url":"https://api.github.com/repos/unihorn/etcd/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/unihorn/etcd/labels{/name}","releases_url":"https://api.github.com/repos/unihorn/etcd/releases{/id}","created_at":"2014-02-11T20:18:23Z","updated_at":"2014-09-12T22:41:09Z","pushed_at":"2014-10-23T20:59:04Z","git_url":"git://github.com/unihorn/etcd.git","ssh_url":"git@github.com:unihorn/etcd.git","clone_url":"https://github.com/unihorn/etcd.git","svn_url":"https://github.com/unihorn/etcd","homepage":"","size":16157,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"coreos:master","ref":"master","sha":"16c9970a03ede5895ef732532784d00a3c13f749","user":{"login":"coreos","id":3730757,"avatar_url":"https://avatars.githubusercontent.com/u/3730757?v=2","gravatar_id":"","url":"https://api.github.com/users/coreos","html_url":"https://github.com/coreos","followers_url":"https://api.github.com/users/coreos/followers","following_url":"https://api.github.com/users/coreos/following{/other_user}","gists_url":"https://api.github.com/users/coreos/gists{/gist_id}","starred_url":"https://api.github.com/users/coreos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coreos/subscriptions","organizations_url":"https://api.github.com/users/coreos/orgs","repos_url":"https://api.github.com/users/coreos/repos","events_url":"https://api.github.com/users/coreos/events{/privacy}","received_events_url":"https://api.github.com/users/coreos/received_events","type":"Organization","site_admin":false},"repo":{"id":11225014,"name":"etcd","full_name":"coreos/etcd","owner":{"login":"coreos","id":3730757,"avatar_url":"https://avatars.githubusercontent.com/u/3730757?v=2","gravatar_id":"","url":"https://api.github.com/users/coreos","html_url":"https://github.com/coreos","followers_url":"https://api.github.com/users/coreos/followers","following_url":"https://api.github.com/users/coreos/following{/other_user}","gists_url":"https://api.github.com/users/coreos/gists{/gist_id}","starred_url":"https://api.github.com/users/coreos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coreos/subscriptions","organizations_url":"https://api.github.com/users/coreos/orgs","repos_url":"https://api.github.com/users/coreos/repos","events_url":"https://api.github.com/users/coreos/events{/privacy}","received_events_url":"https://api.github.com/users/coreos/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/coreos/etcd","description":"A highly-available key value store for shared configuration and service discovery","fork":false,"url":"https://api.github.com/repos/coreos/etcd","forks_url":"https://api.github.com/repos/coreos/etcd/forks","keys_url":"https://api.github.com/repos/coreos/etcd/keys{/key_id}","collaborators_url":"https://api.github.com/repos/coreos/etcd/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/coreos/etcd/teams","hooks_url":"https://api.github.com/repos/coreos/etcd/hooks","issue_events_url":"https://api.github.com/repos/coreos/etcd/issues/events{/number}","events_url":"https://api.github.com/repos/coreos/etcd/events","assignees_url":"https://api.github.com/repos/coreos/etcd/assignees{/user}","branches_url":"https://api.github.com/repos/coreos/etcd/branches{/branch}","tags_url":"https://api.github.com/repos/coreos/etcd/tags","blobs_url":"https://api.github.com/repos/coreos/etcd/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/coreos/etcd/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/coreos/etcd/git/refs{/sha}","trees_url":"https://api.github.com/repos/coreos/etcd/git/trees{/sha}","statuses_url":"https://api.github.com/repos/coreos/etcd/statuses/{sha}","languages_url":"https://api.github.com/repos/coreos/etcd/languages","stargazers_url":"https://api.github.com/repos/coreos/etcd/stargazers","contributors_url":"https://api.github.com/repos/coreos/etcd/contributors","subscribers_url":"https://api.github.com/repos/coreos/etcd/subscribers","subscription_url":"https://api.github.com/repos/coreos/etcd/subscription","commits_url":"https://api.github.com/repos/coreos/etcd/commits{/sha}","git_commits_url":"https://api.github.com/repos/coreos/etcd/git/commits{/sha}","comments_url":"https://api.github.com/repos/coreos/etcd/comments{/number}","issue_comment_url":"https://api.github.com/repos/coreos/etcd/issues/comments/{number}","contents_url":"https://api.github.com/repos/coreos/etcd/contents/{+path}","compare_url":"https://api.github.com/repos/coreos/etcd/compare/{base}...{head}","merges_url":"https://api.github.com/repos/coreos/etcd/merges","archive_url":"https://api.github.com/repos/coreos/etcd/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/coreos/etcd/downloads","issues_url":"https://api.github.com/repos/coreos/etcd/issues{/number}","pulls_url":"https://api.github.com/repos/coreos/etcd/pulls{/number}","milestones_url":"https://api.github.com/repos/coreos/etcd/milestones{/number}","notifications_url":"https://api.github.com/repos/coreos/etcd/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/coreos/etcd/labels{/name}","releases_url":"https://api.github.com/repos/coreos/etcd/releases{/id}","created_at":"2013-07-06T21:57:21Z","updated_at":"2014-10-23T16:56:39Z","pushed_at":"2014-10-23T18:30:51Z","git_url":"git://github.com/coreos/etcd.git","ssh_url":"git@github.com:coreos/etcd.git","clone_url":"https://github.com/coreos/etcd.git","svn_url":"https://github.com/coreos/etcd","homepage":"","size":26837,"stargazers_count":4473,"watchers_count":4473,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":450,"mirror_url":null,"open_issues_count":111,"forks":450,"open_issues":111,"watchers":4473,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/coreos/etcd/pulls/1361"},"html":{"href":"https://github.com/coreos/etcd/pull/1361"},"issue":{"href":"https://api.github.com/repos/coreos/etcd/issues/1361"},"comments":{"href":"https://api.github.com/repos/coreos/etcd/issues/1361/comments"},"review_comments":{"href":"https://api.github.com/repos/coreos/etcd/pulls/1361/comments"},"review_comment":{"href":"https://api.github.com/repos/coreos/etcd/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/coreos/etcd/pulls/1361/commits"},"statuses":{"href":"https://api.github.com/repos/coreos/etcd/statuses/8eee8c260e3a57af8bc8c0635c2921ddd5f59885"}}}}