{"action":"created","comment":{"url":"https://api.github.com/repos/segmentio/consul-go/pulls/comments/116366359","pull_request_review_id":37986849,"id":116366359,"diff_hunk":"@@ -0,0 +1,344 @@\n+package consul\n+\n+import (\n+\t\"context\"\n+\t\"errors\"\n+\t\"fmt\"\n+\t\"reflect\"\n+\t\"sort\"\n+\t\"sync\"\n+\t\"sync/atomic\"\n+\t\"time\"\n+)\n+\n+// A Locker exposes methods for acquiring locks on keys of the consul key/value\n+// store.\n+type Locker struct {\n+\t// The client used to send requests to the consul agent. If nil, the default\n+\t// client is used instead.\n+\tClient *Client\n+\n+\t// LockDelay is the amount of time that a lock will stay held if it hasn't\n+\t// been released and the session that was attached to it expired.\n+\tLockDelay time.Duration\n+}\n+\n+// Lock acquires locks on the given keys. The method blocks until the locks were\n+// acquired, or the context was camceled. The returned context will be canceled\n+// when the locks are released (by calling the cancellation function), or if the\n+// ownership was lost, in this case the contexts' Err method returns Unlocked.\n+func (l *Locker) Lock(ctx context.Context, keys ...string) (context.Context, context.CancelFunc) {\n+\tretryInterval := 1 * time.Second\n+\tsessionCtx, sessionCancel := l.withSession(ctx, \"lock: %v\", keys)\n+\n+\tdeadline, ok := ctx.Deadline()\n+\tif ok {\n+\t\tretryInterval = deadline.Sub(time.Now()) / 10\n+\t}\n+\n+\tlocks := make([]ctxCancel, 0, len(keys))\n+\tsortedKeys := sortedKeys(keys)\n+\n+\tfor {\n+\t\tfor _, key := range sortedKeys {\n+\t\t\tlockCtx, lockCancel, _ := l.tryLock(sessionCtx, key)\n+\t\t\tif lockCtx == nil {\n+\t\t\t\tbreak\n+\t\t\t}\n+\t\t\tlocks = append(locks, ctxCancel{lockCtx, lockCancel})\n+\t\t}\n+\n+\t\tif len(locks) == len(keys) {\n+\t\t\tmulti := newMultiCtx(sortedKeys, locks)\n+\t\t\treturn multi, func() { multi.cancel(); sessionCancel() }\n+\t\t}\n+\n+\t\tfor _, lock := range locks {\n+\t\t\tlock.cancel()\n+\t\t}\n+\t\tlocks = locks[:0]\n+\n+\t\ttimer := time.NewTicker(retryInterval)\n+\t\tselect {\n+\t\tcase <-timer.C:\n+\t\t\ttimer.Stop()\n+\t\tcase <-ctx.Done():\n+\t\t\ttimer.Stop()\n+\t\t\tsessionCancel()\n+\t\t\treturn errorContext(ctx, ctx.Err())\n+\t\t}\n+\t}\n+}\n+\n+// TryLock attempts to acquire a lock on one of the given keys. The returned\n+// context will be canceled when the lock is released (by calling the\n+// cancellation function), or if the ownership was lost, in this case the\n+// context's Err method returns Unlocked.\n+// The method never blocks, if it fails to acquire any of the locks it returns\n+// a canceled context.\n+func (l *Locker) TryLock(ctx context.Context, keys ...string) (context.Context, context.CancelFunc) {","path":"locker.go","position":79,"original_position":79,"commit_id":"123df3fae7196e0f84f56705b4b545885a087fa4","original_commit_id":"123df3fae7196e0f84f56705b4b545885a087fa4","user":{"login":"yields","id":1661587,"avatar_url":"https://avatars1.githubusercontent.com/u/1661587?v=3","gravatar_id":"","url":"https://api.github.com/users/yields","html_url":"https://github.com/yields","followers_url":"https://api.github.com/users/yields/followers","following_url":"https://api.github.com/users/yields/following{/other_user}","gists_url":"https://api.github.com/users/yields/gists{/gist_id}","starred_url":"https://api.github.com/users/yields/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yields/subscriptions","organizations_url":"https://api.github.com/users/yields/orgs","repos_url":"https://api.github.com/users/yields/repos","events_url":"https://api.github.com/users/yields/events{/privacy}","received_events_url":"https://api.github.com/users/yields/received_events","type":"User","site_admin":false},"body":"Do you think we should rename this to `OneOf` or `One`?","created_at":"2017-05-13T20:02:57Z","updated_at":"2017-05-13T20:10:07Z","html_url":"https://github.com/segmentio/consul-go/pull/9#discussion_r116366359","pull_request_url":"https://api.github.com/repos/segmentio/consul-go/pulls/9","_links":{"self":{"href":"https://api.github.com/repos/segmentio/consul-go/pulls/comments/116366359"},"html":{"href":"https://github.com/segmentio/consul-go/pull/9#discussion_r116366359"},"pull_request":{"href":"https://api.github.com/repos/segmentio/consul-go/pulls/9"}}},"pull_request":{"url":"https://api.github.com/repos/segmentio/consul-go/pulls/9","id":120442821,"html_url":"https://github.com/segmentio/consul-go/pull/9","diff_url":"https://github.com/segmentio/consul-go/pull/9.diff","patch_url":"https://github.com/segmentio/consul-go/pull/9.patch","issue_url":"https://api.github.com/repos/segmentio/consul-go/issues/9","number":9,"state":"open","locked":false,"title":"Sessions and Locks","user":{"login":"achille-roussel","id":865510,"avatar_url":"https://avatars0.githubusercontent.com/u/865510?v=3","gravatar_id":"","url":"https://api.github.com/users/achille-roussel","html_url":"https://github.com/achille-roussel","followers_url":"https://api.github.com/users/achille-roussel/followers","following_url":"https://api.github.com/users/achille-roussel/following{/other_user}","gists_url":"https://api.github.com/users/achille-roussel/gists{/gist_id}","starred_url":"https://api.github.com/users/achille-roussel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/achille-roussel/subscriptions","organizations_url":"https://api.github.com/users/achille-roussel/orgs","repos_url":"https://api.github.com/users/achille-roussel/repos","events_url":"https://api.github.com/users/achille-roussel/events{/privacy}","received_events_url":"https://api.github.com/users/achille-roussel/received_events","type":"User","site_admin":false},"body":"This PR adds tools to deal with locking mechanisms on top of Consul. There are two components to how locks are managed in Consul, the Sessions and the Key/Value store. One can acquire a lock by attaching sessions to keys, then the lock may be explicitly released, or it is released when the session expires.\r\n\r\nSessions and Locks have lifetimes, which translates nicely into the Go Context concept. Both the APIs presented in this PR abstract sessions and locks as contexts, which makes it possible to inject dependencies on Consul Sessions and Locks into any context-aware code.\r\n\r\nThe synchronization mechanisms come in two algorithms named Lock and TryLock.\r\nLock takes a list of keys and blocks until it was able to acquire all of them, the algorithm is designed to prevent deadlocks (by sorting the list of keys and acquiring the locks sequentially).\r\nTryLock takes a list of keys and attempts to acquire a lock on one of the keys, which may fail if all keys were already locked, which means it never blocks.\r\n\r\nHere are a couple of examples:\r\n## Creating Sessions\r\n```go\r\n// Creates a session in Consul and returns a context associated to it.\r\n// The session is automatically renewed, and destroyed when cancel is called.\r\n//\r\n// If the session gets expired or removed for some reason the context is\r\n// asynchronously canceled.\r\nctx, cancel := consul.WithSession(context.Background(), consul.Session{\r\n  Name: \"my session\",\r\n})\r\n```\r\n## Acquiring Locks\r\n```go\r\n// A session is automatically created and attached to the keys, if the session\r\n// expires it also releases the locks which means the returned context would\r\n// get asynchronously canceled. This is great to build algorithms that depend\r\n// on the lock being held and need to abort their execution if they detect that\r\n// they lost ownership of the keys.\r\nctx, cancel := consul.Lock(context.Background(), \"key-1\", \"key-A\")\r\n```\r\n## Chaining dependencies\r\n```go\r\n// This context is canceled after 10 seconds, it's the parent context of the\r\n// session which means it expires the session after 10 seconds.\r\ndeadline, cancel := context.WithTimeout(context.Background(), 10*time.Second)\r\n// ...\r\nsession, destroy := consul.WithSession(deadline, consul.Session{\r\n  Name: \"my session\",\r\n})\r\n// By passing the session as parent context we can attach the session to\r\n// multiple locks, if it expires, all those locks are released and their\r\n// contexts are canceled.\r\nlock1, release1 := consul.Lock(session, \"key-1\")\r\nlock2, release2 := consul.TryLock(session, \"key-2\")\r\n```\r\nPlease take a look and let me know if anything should be changed.","created_at":"2017-05-13T10:04:29Z","updated_at":"2017-05-13T20:10:07Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f03bedd5097d1589d7ae0409b19c47ca2fe6ec35","assignee":null,"assignees":[],"requested_reviewers":[{"login":"dfuentes","id":388569,"avatar_url":"https://avatars1.githubusercontent.com/u/388569?v=3","gravatar_id":"","url":"https://api.github.com/users/dfuentes","html_url":"https://github.com/dfuentes","followers_url":"https://api.github.com/users/dfuentes/followers","following_url":"https://api.github.com/users/dfuentes/following{/other_user}","gists_url":"https://api.github.com/users/dfuentes/gists{/gist_id}","starred_url":"https://api.github.com/users/dfuentes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dfuentes/subscriptions","organizations_url":"https://api.github.com/users/dfuentes/orgs","repos_url":"https://api.github.com/users/dfuentes/repos","events_url":"https://api.github.com/users/dfuentes/events{/privacy}","received_events_url":"https://api.github.com/users/dfuentes/received_events","type":"User","site_admin":false},{"login":"alberts","id":409689,"avatar_url":"https://avatars3.githubusercontent.com/u/409689?v=3","gravatar_id":"","url":"https://api.github.com/users/alberts","html_url":"https://github.com/alberts","followers_url":"https://api.github.com/users/alberts/followers","following_url":"https://api.github.com/users/alberts/following{/other_user}","gists_url":"https://api.github.com/users/alberts/gists{/gist_id}","starred_url":"https://api.github.com/users/alberts/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alberts/subscriptions","organizations_url":"https://api.github.com/users/alberts/orgs","repos_url":"https://api.github.com/users/alberts/repos","events_url":"https://api.github.com/users/alberts/events{/privacy}","received_events_url":"https://api.github.com/users/alberts/received_events","type":"User","site_admin":false},{"login":"calvinfo","id":487539,"avatar_url":"https://avatars3.githubusercontent.com/u/487539?v=3","gravatar_id":"","url":"https://api.github.com/users/calvinfo","html_url":"https://github.com/calvinfo","followers_url":"https://api.github.com/users/calvinfo/followers","following_url":"https://api.github.com/users/calvinfo/following{/other_user}","gists_url":"https://api.github.com/users/calvinfo/gists{/gist_id}","starred_url":"https://api.github.com/users/calvinfo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/calvinfo/subscriptions","organizations_url":"https://api.github.com/users/calvinfo/orgs","repos_url":"https://api.github.com/users/calvinfo/repos","events_url":"https://api.github.com/users/calvinfo/events{/privacy}","received_events_url":"https://api.github.com/users/calvinfo/received_events","type":"User","site_admin":false},{"login":"Pryz","id":971835,"avatar_url":"https://avatars0.githubusercontent.com/u/971835?v=3","gravatar_id":"","url":"https://api.github.com/users/Pryz","html_url":"https://github.com/Pryz","followers_url":"https://api.github.com/users/Pryz/followers","following_url":"https://api.github.com/users/Pryz/following{/other_user}","gists_url":"https://api.github.com/users/Pryz/gists{/gist_id}","starred_url":"https://api.github.com/users/Pryz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Pryz/subscriptions","organizations_url":"https://api.github.com/users/Pryz/orgs","repos_url":"https://api.github.com/users/Pryz/repos","events_url":"https://api.github.com/users/Pryz/events{/privacy}","received_events_url":"https://api.github.com/users/Pryz/received_events","type":"User","site_admin":false},{"login":"ejcx","id":2739058,"avatar_url":"https://avatars0.githubusercontent.com/u/2739058?v=3","gravatar_id":"","url":"https://api.github.com/users/ejcx","html_url":"https://github.com/ejcx","followers_url":"https://api.github.com/users/ejcx/followers","following_url":"https://api.github.com/users/ejcx/following{/other_user}","gists_url":"https://api.github.com/users/ejcx/gists{/gist_id}","starred_url":"https://api.github.com/users/ejcx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ejcx/subscriptions","organizations_url":"https://api.github.com/users/ejcx/orgs","repos_url":"https://api.github.com/users/ejcx/repos","events_url":"https://api.github.com/users/ejcx/events{/privacy}","received_events_url":"https://api.github.com/users/ejcx/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/segmentio/consul-go/pulls/9/commits","review_comments_url":"https://api.github.com/repos/segmentio/consul-go/pulls/9/comments","review_comment_url":"https://api.github.com/repos/segmentio/consul-go/pulls/comments{/number}","comments_url":"https://api.github.com/repos/segmentio/consul-go/issues/9/comments","statuses_url":"https://api.github.com/repos/segmentio/consul-go/statuses/123df3fae7196e0f84f56705b4b545885a087fa4","head":{"label":"segmentio:lock","ref":"lock","sha":"123df3fae7196e0f84f56705b4b545885a087fa4","user":{"login":"segmentio","id":819518,"avatar_url":"https://avatars3.githubusercontent.com/u/819518?v=3","gravatar_id":"","url":"https://api.github.com/users/segmentio","html_url":"https://github.com/segmentio","followers_url":"https://api.github.com/users/segmentio/followers","following_url":"https://api.github.com/users/segmentio/following{/other_user}","gists_url":"https://api.github.com/users/segmentio/gists{/gist_id}","starred_url":"https://api.github.com/users/segmentio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/segmentio/subscriptions","organizations_url":"https://api.github.com/users/segmentio/orgs","repos_url":"https://api.github.com/users/segmentio/repos","events_url":"https://api.github.com/users/segmentio/events{/privacy}","received_events_url":"https://api.github.com/users/segmentio/received_events","type":"Organization","site_admin":false},"repo":{"id":90301546,"name":"consul-go","full_name":"segmentio/consul-go","owner":{"login":"segmentio","id":819518,"avatar_url":"https://avatars3.githubusercontent.com/u/819518?v=3","gravatar_id":"","url":"https://api.github.com/users/segmentio","html_url":"https://github.com/segmentio","followers_url":"https://api.github.com/users/segmentio/followers","following_url":"https://api.github.com/users/segmentio/following{/other_user}","gists_url":"https://api.github.com/users/segmentio/gists{/gist_id}","starred_url":"https://api.github.com/users/segmentio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/segmentio/subscriptions","organizations_url":"https://api.github.com/users/segmentio/orgs","repos_url":"https://api.github.com/users/segmentio/repos","events_url":"https://api.github.com/users/segmentio/events{/privacy}","received_events_url":"https://api.github.com/users/segmentio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/segmentio/consul-go","description":"Go package providing building blocks for interacting with Consul.","fork":false,"url":"https://api.github.com/repos/segmentio/consul-go","forks_url":"https://api.github.com/repos/segmentio/consul-go/forks","keys_url":"https://api.github.com/repos/segmentio/consul-go/keys{/key_id}","collaborators_url":"https://api.github.com/repos/segmentio/consul-go/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/segmentio/consul-go/teams","hooks_url":"https://api.github.com/repos/segmentio/consul-go/hooks","issue_events_url":"https://api.github.com/repos/segmentio/consul-go/issues/events{/number}","events_url":"https://api.github.com/repos/segmentio/consul-go/events","assignees_url":"https://api.github.com/repos/segmentio/consul-go/assignees{/user}","branches_url":"https://api.github.com/repos/segmentio/consul-go/branches{/branch}","tags_url":"https://api.github.com/repos/segmentio/consul-go/tags","blobs_url":"https://api.github.com/repos/segmentio/consul-go/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/segmentio/consul-go/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/segmentio/consul-go/git/refs{/sha}","trees_url":"https://api.github.com/repos/segmentio/consul-go/git/trees{/sha}","statuses_url":"https://api.github.com/repos/segmentio/consul-go/statuses/{sha}","languages_url":"https://api.github.com/repos/segmentio/consul-go/languages","stargazers_url":"https://api.github.com/repos/segmentio/consul-go/stargazers","contributors_url":"https://api.github.com/repos/segmentio/consul-go/contributors","subscribers_url":"https://api.github.com/repos/segmentio/consul-go/subscribers","subscription_url":"https://api.github.com/repos/segmentio/consul-go/subscription","commits_url":"https://api.github.com/repos/segmentio/consul-go/commits{/sha}","git_commits_url":"https://api.github.com/repos/segmentio/consul-go/git/commits{/sha}","comments_url":"https://api.github.com/repos/segmentio/consul-go/comments{/number}","issue_comment_url":"https://api.github.com/repos/segmentio/consul-go/issues/comments{/number}","contents_url":"https://api.github.com/repos/segmentio/consul-go/contents/{+path}","compare_url":"https://api.github.com/repos/segmentio/consul-go/compare/{base}...{head}","merges_url":"https://api.github.com/repos/segmentio/consul-go/merges","archive_url":"https://api.github.com/repos/segmentio/consul-go/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/segmentio/consul-go/downloads","issues_url":"https://api.github.com/repos/segmentio/consul-go/issues{/number}","pulls_url":"https://api.github.com/repos/segmentio/consul-go/pulls{/number}","milestones_url":"https://api.github.com/repos/segmentio/consul-go/milestones{/number}","notifications_url":"https://api.github.com/repos/segmentio/consul-go/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/segmentio/consul-go/labels{/name}","releases_url":"https://api.github.com/repos/segmentio/consul-go/releases{/id}","deployments_url":"https://api.github.com/repos/segmentio/consul-go/deployments","created_at":"2017-05-04T19:21:57Z","updated_at":"2017-05-12T21:49:49Z","pushed_at":"2017-05-13T10:19:44Z","git_url":"git://github.com/segmentio/consul-go.git","ssh_url":"git@github.com:segmentio/consul-go.git","clone_url":"https://github.com/segmentio/consul-go.git","svn_url":"https://github.com/segmentio/consul-go","homepage":null,"size":88,"stargazers_count":3,"watchers_count":3,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":3,"default_branch":"master"}},"base":{"label":"segmentio:master","ref":"master","sha":"6f4d5dbc49ce3aa5702c972bd015d3f87ce08ec3","user":{"login":"segmentio","id":819518,"avatar_url":"https://avatars3.githubusercontent.com/u/819518?v=3","gravatar_id":"","url":"https://api.github.com/users/segmentio","html_url":"https://github.com/segmentio","followers_url":"https://api.github.com/users/segmentio/followers","following_url":"https://api.github.com/users/segmentio/following{/other_user}","gists_url":"https://api.github.com/users/segmentio/gists{/gist_id}","starred_url":"https://api.github.com/users/segmentio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/segmentio/subscriptions","organizations_url":"https://api.github.com/users/segmentio/orgs","repos_url":"https://api.github.com/users/segmentio/repos","events_url":"https://api.github.com/users/segmentio/events{/privacy}","received_events_url":"https://api.github.com/users/segmentio/received_events","type":"Organization","site_admin":false},"repo":{"id":90301546,"name":"consul-go","full_name":"segmentio/consul-go","owner":{"login":"segmentio","id":819518,"avatar_url":"https://avatars3.githubusercontent.com/u/819518?v=3","gravatar_id":"","url":"https://api.github.com/users/segmentio","html_url":"https://github.com/segmentio","followers_url":"https://api.github.com/users/segmentio/followers","following_url":"https://api.github.com/users/segmentio/following{/other_user}","gists_url":"https://api.github.com/users/segmentio/gists{/gist_id}","starred_url":"https://api.github.com/users/segmentio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/segmentio/subscriptions","organizations_url":"https://api.github.com/users/segmentio/orgs","repos_url":"https://api.github.com/users/segmentio/repos","events_url":"https://api.github.com/users/segmentio/events{/privacy}","received_events_url":"https://api.github.com/users/segmentio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/segmentio/consul-go","description":"Go package providing building blocks for interacting with Consul.","fork":false,"url":"https://api.github.com/repos/segmentio/consul-go","forks_url":"https://api.github.com/repos/segmentio/consul-go/forks","keys_url":"https://api.github.com/repos/segmentio/consul-go/keys{/key_id}","collaborators_url":"https://api.github.com/repos/segmentio/consul-go/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/segmentio/consul-go/teams","hooks_url":"https://api.github.com/repos/segmentio/consul-go/hooks","issue_events_url":"https://api.github.com/repos/segmentio/consul-go/issues/events{/number}","events_url":"https://api.github.com/repos/segmentio/consul-go/events","assignees_url":"https://api.github.com/repos/segmentio/consul-go/assignees{/user}","branches_url":"https://api.github.com/repos/segmentio/consul-go/branches{/branch}","tags_url":"https://api.github.com/repos/segmentio/consul-go/tags","blobs_url":"https://api.github.com/repos/segmentio/consul-go/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/segmentio/consul-go/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/segmentio/consul-go/git/refs{/sha}","trees_url":"https://api.github.com/repos/segmentio/consul-go/git/trees{/sha}","statuses_url":"https://api.github.com/repos/segmentio/consul-go/statuses/{sha}","languages_url":"https://api.github.com/repos/segmentio/consul-go/languages","stargazers_url":"https://api.github.com/repos/segmentio/consul-go/stargazers","contributors_url":"https://api.github.com/repos/segmentio/consul-go/contributors","subscribers_url":"https://api.github.com/repos/segmentio/consul-go/subscribers","subscription_url":"https://api.github.com/repos/segmentio/consul-go/subscription","commits_url":"https://api.github.com/repos/segmentio/consul-go/commits{/sha}","git_commits_url":"https://api.github.com/repos/segmentio/consul-go/git/commits{/sha}","comments_url":"https://api.github.com/repos/segmentio/consul-go/comments{/number}","issue_comment_url":"https://api.github.com/repos/segmentio/consul-go/issues/comments{/number}","contents_url":"https://api.github.com/repos/segmentio/consul-go/contents/{+path}","compare_url":"https://api.github.com/repos/segmentio/consul-go/compare/{base}...{head}","merges_url":"https://api.github.com/repos/segmentio/consul-go/merges","archive_url":"https://api.github.com/repos/segmentio/consul-go/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/segmentio/consul-go/downloads","issues_url":"https://api.github.com/repos/segmentio/consul-go/issues{/number}","pulls_url":"https://api.github.com/repos/segmentio/consul-go/pulls{/number}","milestones_url":"https://api.github.com/repos/segmentio/consul-go/milestones{/number}","notifications_url":"https://api.github.com/repos/segmentio/consul-go/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/segmentio/consul-go/labels{/name}","releases_url":"https://api.github.com/repos/segmentio/consul-go/releases{/id}","deployments_url":"https://api.github.com/repos/segmentio/consul-go/deployments","created_at":"2017-05-04T19:21:57Z","updated_at":"2017-05-12T21:49:49Z","pushed_at":"2017-05-13T10:19:44Z","git_url":"git://github.com/segmentio/consul-go.git","ssh_url":"git@github.com:segmentio/consul-go.git","clone_url":"https://github.com/segmentio/consul-go.git","svn_url":"https://github.com/segmentio/consul-go","homepage":null,"size":88,"stargazers_count":3,"watchers_count":3,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/segmentio/consul-go/pulls/9"},"html":{"href":"https://github.com/segmentio/consul-go/pull/9"},"issue":{"href":"https://api.github.com/repos/segmentio/consul-go/issues/9"},"comments":{"href":"https://api.github.com/repos/segmentio/consul-go/issues/9/comments"},"review_comments":{"href":"https://api.github.com/repos/segmentio/consul-go/pulls/9/comments"},"review_comment":{"href":"https://api.github.com/repos/segmentio/consul-go/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/segmentio/consul-go/pulls/9/commits"},"statuses":{"href":"https://api.github.com/repos/segmentio/consul-go/statuses/123df3fae7196e0f84f56705b4b545885a087fa4"}}}}
{"actor":{"display_login":"yields"}}