{"action":"created","comment":{"url":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/comments/170971475","pull_request_review_id":99741972,"id":170971475,"diff_hunk":"@@ -101,27 +101,139 @@ func (c ipamClient) AutoAssign(ctx context.Context, args AutoAssignArgs) ([]net.\n \treturn v4list, v6list, nil\n }\n \n+// getBlockFromAffinity returns the block referenced by the given affinity, attempting to create it if\n+// it does not exist. getBlockFromAffinity will delete the provided affinity if it does not match the actual\n+// affinity of the block.\n+func (c ipamClient) getBlockFromAffinity(ctx context.Context, aff *model.KVPair) (*model.KVPair, error) {\n+\t// Parse out affinity data.\n+\tcidr := aff.Key.(model.BlockAffinityKey).CIDR\n+\thost := aff.Key.(model.BlockAffinityKey).Host\n+\tstate := aff.Value.(*model.BlockAffinity).State\n+\n+\t// Get the block referenced by this affinity.\n+\tb, err := c.client.Get(ctx, model.BlockKey{CIDR: cidr}, \"\")\n+\tif err != nil {\n+\t\tif _, ok := err.(cerrors.ErrorResourceDoesNotExist); ok {\n+\t\t\t// The block referenced by the affinity doesn't exist. Try to create it.\n+\t\t\taff.Value.(*model.BlockAffinity).State = model.StatePending\n+\t\t\taff, err = c.client.Update(ctx, aff)\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, err\n+\t\t\t}\n+\n+\t\t\tcfg, err := c.GetIPAMConfig(ctx)\n+\t\t\tif err != nil {\n+\t\t\t\tlog.Errorf(\"Error getting IPAM Config: %v\", err)\n+\t\t\t\treturn nil, err\n+\t\t\t}\n+\n+\t\t\t// Claim the block, which will also confirm the affinity.\n+\t\t\tb, err := c.blockReaderWriter.claimBlockAffinity(ctx, aff, *cfg)\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, err\n+\t\t\t}\n+\t\t\treturn b, nil\n+\t\t}\n+\t\treturn nil, err\n+\t}\n+\n+\t// If the block doesn't match the affinity, it means we've got a stale affininty hanging around.\n+\t// We should remove it.\n+\tblockAffinity := b.Value.(*model.AllocationBlock).Affinity\n+\tif blockAffinity == nil || *blockAffinity != fmt.Sprintf(\"host:%s\", host) {\n+\t\tlog.Warn(\"Block does not match the provided affinity, deleting stale affinity\")\n+\t\t_, err := c.client.Delete(ctx, aff.Key, aff.Revision)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\treturn nil, errors.New(\"Affinity is stale\")\n+\t}\n+\n+\t// If the block does match the affinity but the affinity has not been confirmed,\n+\t// try to confirm it.\n+\tif state != model.StateConfirmed {\n+\t\t// Write the affinity as pending.\n+\t\taff.Value.(*model.BlockAffinity).State = model.StatePending\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\n+\t\t// CAS the block to get a new revision and invalidate any other instances\n+\t\t// that might be trying to operate on the block.\n+\t\tb, err = c.client.Update(ctx, b)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\n+\t\t// Confirm the affinity.\n+\t\taff.Value.(*model.BlockAffinity).State = model.StateConfirmed\n+\t\taff, err = c.client.Update(ctx, aff)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t}\n+\treturn b, nil\n+}\n+\n func (c ipamClient) autoAssign(ctx context.Context, num int, handleID *string, attrs map[string]string, pools []net.IPNet, version ipVersion, host string) ([]net.IP, error) {\n \n \t// Start by trying to assign from one of the host-affine blocks.  We\n \t// always do strict checking at this stage, so it doesn't matter whether\n \t// globally we have strict_affinity or not.\n-\tlog.Debugf(\"Looking for addresses in current affine blocks for host '%s'\", host)\n+\tlogCtx := log.WithFields(log.Fields{\"host\": host})\n+\tlogCtx.Debugf(\"Looking for addresses in current affine blocks for host\")\n \taffBlocks, err := c.blockReaderWriter.getAffineBlocks(ctx, host, version, pools)\n \tif err != nil {\n \t\treturn nil, err\n \t}\n-\tlog.Debugf(\"Found %d affine IPv%d blocks for host '%s': %v\", len(affBlocks), version.Number, host, affBlocks)\n+\tlogCtx.Debugf(\"Found %d affine IPv%d blocks for host: %v\", len(affBlocks), version.Number, affBlocks)\n \tips := []net.IP{}\n+\tnewIPs := []net.IP{}\n \tfor len(ips) < num {\n \t\tif len(affBlocks) == 0 {\n-\t\t\tlog.Infof(\"Ran out of existing affine blocks for host '%s'\", host)\n+\t\t\tlogCtx.Infof(\"Ran out of existing affine blocks for host\")\n \t\t\tbreak\n \t\t}\n \t\tcidr := affBlocks[0]\n \t\taffBlocks = affBlocks[1:]\n-\t\tips, _ = c.assignFromExistingBlock(ctx, cidr, num, handleID, attrs, host, true)\n-\t\tlog.Debugf(\"Block '%s' provided addresses: %v\", cidr.String(), ips)\n+\n+\t\t// Try to assign from this block - if we hit a CAS error, we'll try this block again.\n+\t\t// For any other error, we'll break out and try the next affine block.\n+\t\tfor i := 0; i < ipamEtcdRetries; i++ {\n+\t\t\t// Get the affinity.\n+\t\t\taff, err := c.client.Get(ctx, model.BlockAffinityKey{Host: host, CIDR: cidr}, \"\")\n+\t\t\tif err != nil {\n+\t\t\t\tlogCtx.WithError(err).Warnf(\"Error getting affinity\")\n+\t\t\t\tbreak\n+\t\t\t}\n+\n+\t\t\t// Get the block which is referenced by the affinity, creating it if necessary.\n+\t\t\tb, err := c.getBlockFromAffinity(ctx, aff)\n+\t\t\tif err != nil {\n+\t\t\t\t// Couldn't get a block for this affinity.\n+\t\t\t\tlogCtx.WithError(err).Warn(\"Couldn't get block for affinity\")\n+\t\t\t\tif _, ok := err.(cerrors.ErrorResourceUpdateConflict); ok {","path":"lib/ipam/ipam.go","position":120,"original_position":120,"commit_id":"03e8764bd1788d110cd1c9631e7b78fad3ff996b","original_commit_id":"03e8764bd1788d110cd1c9631e7b78fad3ff996b","user":{"login":"caseydavenport","id":1944093,"avatar_url":"https://avatars3.githubusercontent.com/u/1944093?v=4","gravatar_id":"","url":"https://api.github.com/users/caseydavenport","html_url":"https://github.com/caseydavenport","followers_url":"https://api.github.com/users/caseydavenport/followers","following_url":"https://api.github.com/users/caseydavenport/following{/other_user}","gists_url":"https://api.github.com/users/caseydavenport/gists{/gist_id}","starred_url":"https://api.github.com/users/caseydavenport/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caseydavenport/subscriptions","organizations_url":"https://api.github.com/users/caseydavenport/orgs","repos_url":"https://api.github.com/users/caseydavenport/repos","events_url":"https://api.github.com/users/caseydavenport/events{/privacy}","received_events_url":"https://api.github.com/users/caseydavenport/received_events","type":"User","site_admin":false},"body":"Need to debug log in all these branches, adjust warning log so it only occurs when not a resource update conflict. ","created_at":"2018-02-27T15:59:15Z","updated_at":"2018-02-27T15:59:15Z","html_url":"https://github.com/projectcalico/libcalico-go/pull/785#discussion_r170971475","pull_request_url":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/785","author_association":"OWNER","_links":{"self":{"href":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/comments/170971475"},"html":{"href":"https://github.com/projectcalico/libcalico-go/pull/785#discussion_r170971475"},"pull_request":{"href":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/785"}}},"pull_request":{"url":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/785","id":169005176,"html_url":"https://github.com/projectcalico/libcalico-go/pull/785","diff_url":"https://github.com/projectcalico/libcalico-go/pull/785.diff","patch_url":"https://github.com/projectcalico/libcalico-go/pull/785.patch","issue_url":"https://api.github.com/repos/projectcalico/libcalico-go/issues/785","number":785,"state":"open","locked":false,"title":"[WIP] Fix affinity claim race","user":{"login":"caseydavenport","id":1944093,"avatar_url":"https://avatars3.githubusercontent.com/u/1944093?v=4","gravatar_id":"","url":"https://api.github.com/users/caseydavenport","html_url":"https://github.com/caseydavenport","followers_url":"https://api.github.com/users/caseydavenport/followers","following_url":"https://api.github.com/users/caseydavenport/following{/other_user}","gists_url":"https://api.github.com/users/caseydavenport/gists{/gist_id}","starred_url":"https://api.github.com/users/caseydavenport/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caseydavenport/subscriptions","organizations_url":"https://api.github.com/users/caseydavenport/orgs","repos_url":"https://api.github.com/users/caseydavenport/repos","events_url":"https://api.github.com/users/caseydavenport/events{/privacy}","received_events_url":"https://api.github.com/users/caseydavenport/received_events","type":"User","site_admin":false},"body":"## Description\r\n<!-- A few sentences describing the overall goals of the pull request's commits.\r\nPlease include\r\n- the type of fix - (e.g. bug fix, new feature, documentation)\r\n- some details on _why_ this PR should be merged\r\n- the details of the testing you've done on it (both manual and automated)\r\n- which components are affected by this PR\r\n- links to issues that this PR addresses\r\n-->\r\n\r\nFixes #778 \r\n\r\n## Todos\r\n- [ ] Tests\r\n- [ ] Documentation\r\n- [ ] Release note\r\n\r\n## Release Note\r\n<!-- Writing a release note:\r\n- By default, no release note action is required.\r\n- If you're unsure whether or not your PR needs a note, ask your reviewer for guidance.\r\n- If this PR requires a release note, update the block below to include a concise note describing\r\n  the change and any important impacts this PR may have.\r\n-->\r\n\r\n```release-note\r\nNone required\r\n```\r\n","created_at":"2018-02-14T02:00:45Z","updated_at":"2018-02-27T15:59:15Z","closed_at":null,"merged_at":null,"merge_commit_sha":"058e96e4948c030f4b20083f3135141258ffd5b8","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/785/commits","review_comments_url":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/785/comments","review_comment_url":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/comments{/number}","comments_url":"https://api.github.com/repos/projectcalico/libcalico-go/issues/785/comments","statuses_url":"https://api.github.com/repos/projectcalico/libcalico-go/statuses/03e8764bd1788d110cd1c9631e7b78fad3ff996b","head":{"label":"caseydavenport:fix-ipam-race","ref":"fix-ipam-race","sha":"03e8764bd1788d110cd1c9631e7b78fad3ff996b","user":{"login":"caseydavenport","id":1944093,"avatar_url":"https://avatars3.githubusercontent.com/u/1944093?v=4","gravatar_id":"","url":"https://api.github.com/users/caseydavenport","html_url":"https://github.com/caseydavenport","followers_url":"https://api.github.com/users/caseydavenport/followers","following_url":"https://api.github.com/users/caseydavenport/following{/other_user}","gists_url":"https://api.github.com/users/caseydavenport/gists{/gist_id}","starred_url":"https://api.github.com/users/caseydavenport/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caseydavenport/subscriptions","organizations_url":"https://api.github.com/users/caseydavenport/orgs","repos_url":"https://api.github.com/users/caseydavenport/repos","events_url":"https://api.github.com/users/caseydavenport/events{/privacy}","received_events_url":"https://api.github.com/users/caseydavenport/received_events","type":"User","site_admin":false},"repo":{"id":63621502,"name":"libcalico-go","full_name":"caseydavenport/libcalico-go","owner":{"login":"caseydavenport","id":1944093,"avatar_url":"https://avatars3.githubusercontent.com/u/1944093?v=4","gravatar_id":"","url":"https://api.github.com/users/caseydavenport","html_url":"https://github.com/caseydavenport","followers_url":"https://api.github.com/users/caseydavenport/followers","following_url":"https://api.github.com/users/caseydavenport/following{/other_user}","gists_url":"https://api.github.com/users/caseydavenport/gists{/gist_id}","starred_url":"https://api.github.com/users/caseydavenport/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caseydavenport/subscriptions","organizations_url":"https://api.github.com/users/caseydavenport/orgs","repos_url":"https://api.github.com/users/caseydavenport/repos","events_url":"https://api.github.com/users/caseydavenport/events{/privacy}","received_events_url":"https://api.github.com/users/caseydavenport/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/caseydavenport/libcalico-go","description":"Golang Calico library functions:  https://www.projectcalico.org","fork":true,"url":"https://api.github.com/repos/caseydavenport/libcalico-go","forks_url":"https://api.github.com/repos/caseydavenport/libcalico-go/forks","keys_url":"https://api.github.com/repos/caseydavenport/libcalico-go/keys{/key_id}","collaborators_url":"https://api.github.com/repos/caseydavenport/libcalico-go/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/caseydavenport/libcalico-go/teams","hooks_url":"https://api.github.com/repos/caseydavenport/libcalico-go/hooks","issue_events_url":"https://api.github.com/repos/caseydavenport/libcalico-go/issues/events{/number}","events_url":"https://api.github.com/repos/caseydavenport/libcalico-go/events","assignees_url":"https://api.github.com/repos/caseydavenport/libcalico-go/assignees{/user}","branches_url":"https://api.github.com/repos/caseydavenport/libcalico-go/branches{/branch}","tags_url":"https://api.github.com/repos/caseydavenport/libcalico-go/tags","blobs_url":"https://api.github.com/repos/caseydavenport/libcalico-go/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/caseydavenport/libcalico-go/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/caseydavenport/libcalico-go/git/refs{/sha}","trees_url":"https://api.github.com/repos/caseydavenport/libcalico-go/git/trees{/sha}","statuses_url":"https://api.github.com/repos/caseydavenport/libcalico-go/statuses/{sha}","languages_url":"https://api.github.com/repos/caseydavenport/libcalico-go/languages","stargazers_url":"https://api.github.com/repos/caseydavenport/libcalico-go/stargazers","contributors_url":"https://api.github.com/repos/caseydavenport/libcalico-go/contributors","subscribers_url":"https://api.github.com/repos/caseydavenport/libcalico-go/subscribers","subscription_url":"https://api.github.com/repos/caseydavenport/libcalico-go/subscription","commits_url":"https://api.github.com/repos/caseydavenport/libcalico-go/commits{/sha}","git_commits_url":"https://api.github.com/repos/caseydavenport/libcalico-go/git/commits{/sha}","comments_url":"https://api.github.com/repos/caseydavenport/libcalico-go/comments{/number}","issue_comment_url":"https://api.github.com/repos/caseydavenport/libcalico-go/issues/comments{/number}","contents_url":"https://api.github.com/repos/caseydavenport/libcalico-go/contents/{+path}","compare_url":"https://api.github.com/repos/caseydavenport/libcalico-go/compare/{base}...{head}","merges_url":"https://api.github.com/repos/caseydavenport/libcalico-go/merges","archive_url":"https://api.github.com/repos/caseydavenport/libcalico-go/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/caseydavenport/libcalico-go/downloads","issues_url":"https://api.github.com/repos/caseydavenport/libcalico-go/issues{/number}","pulls_url":"https://api.github.com/repos/caseydavenport/libcalico-go/pulls{/number}","milestones_url":"https://api.github.com/repos/caseydavenport/libcalico-go/milestones{/number}","notifications_url":"https://api.github.com/repos/caseydavenport/libcalico-go/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/caseydavenport/libcalico-go/labels{/name}","releases_url":"https://api.github.com/repos/caseydavenport/libcalico-go/releases{/id}","deployments_url":"https://api.github.com/repos/caseydavenport/libcalico-go/deployments","created_at":"2016-07-18T17:02:22Z","updated_at":"2016-07-18T17:02:23Z","pushed_at":"2018-02-26T22:15:15Z","git_url":"git://github.com/caseydavenport/libcalico-go.git","ssh_url":"git@github.com:caseydavenport/libcalico-go.git","clone_url":"https://github.com/caseydavenport/libcalico-go.git","svn_url":"https://github.com/caseydavenport/libcalico-go","homepage":null,"size":2686,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"projectcalico:master","ref":"master","sha":"13d015d6433fd53fb260dc2b3ab50340c34f5749","user":{"login":"projectcalico","id":12304728,"avatar_url":"https://avatars3.githubusercontent.com/u/12304728?v=4","gravatar_id":"","url":"https://api.github.com/users/projectcalico","html_url":"https://github.com/projectcalico","followers_url":"https://api.github.com/users/projectcalico/followers","following_url":"https://api.github.com/users/projectcalico/following{/other_user}","gists_url":"https://api.github.com/users/projectcalico/gists{/gist_id}","starred_url":"https://api.github.com/users/projectcalico/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/projectcalico/subscriptions","organizations_url":"https://api.github.com/users/projectcalico/orgs","repos_url":"https://api.github.com/users/projectcalico/repos","events_url":"https://api.github.com/users/projectcalico/events{/privacy}","received_events_url":"https://api.github.com/users/projectcalico/received_events","type":"Organization","site_admin":false},"repo":{"id":63438319,"name":"libcalico-go","full_name":"projectcalico/libcalico-go","owner":{"login":"projectcalico","id":12304728,"avatar_url":"https://avatars3.githubusercontent.com/u/12304728?v=4","gravatar_id":"","url":"https://api.github.com/users/projectcalico","html_url":"https://github.com/projectcalico","followers_url":"https://api.github.com/users/projectcalico/followers","following_url":"https://api.github.com/users/projectcalico/following{/other_user}","gists_url":"https://api.github.com/users/projectcalico/gists{/gist_id}","starred_url":"https://api.github.com/users/projectcalico/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/projectcalico/subscriptions","organizations_url":"https://api.github.com/users/projectcalico/orgs","repos_url":"https://api.github.com/users/projectcalico/repos","events_url":"https://api.github.com/users/projectcalico/events{/privacy}","received_events_url":"https://api.github.com/users/projectcalico/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/projectcalico/libcalico-go","description":"Golang Calico library functions:  https://www.projectcalico.org","fork":false,"url":"https://api.github.com/repos/projectcalico/libcalico-go","forks_url":"https://api.github.com/repos/projectcalico/libcalico-go/forks","keys_url":"https://api.github.com/repos/projectcalico/libcalico-go/keys{/key_id}","collaborators_url":"https://api.github.com/repos/projectcalico/libcalico-go/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/projectcalico/libcalico-go/teams","hooks_url":"https://api.github.com/repos/projectcalico/libcalico-go/hooks","issue_events_url":"https://api.github.com/repos/projectcalico/libcalico-go/issues/events{/number}","events_url":"https://api.github.com/repos/projectcalico/libcalico-go/events","assignees_url":"https://api.github.com/repos/projectcalico/libcalico-go/assignees{/user}","branches_url":"https://api.github.com/repos/projectcalico/libcalico-go/branches{/branch}","tags_url":"https://api.github.com/repos/projectcalico/libcalico-go/tags","blobs_url":"https://api.github.com/repos/projectcalico/libcalico-go/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/projectcalico/libcalico-go/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/projectcalico/libcalico-go/git/refs{/sha}","trees_url":"https://api.github.com/repos/projectcalico/libcalico-go/git/trees{/sha}","statuses_url":"https://api.github.com/repos/projectcalico/libcalico-go/statuses/{sha}","languages_url":"https://api.github.com/repos/projectcalico/libcalico-go/languages","stargazers_url":"https://api.github.com/repos/projectcalico/libcalico-go/stargazers","contributors_url":"https://api.github.com/repos/projectcalico/libcalico-go/contributors","subscribers_url":"https://api.github.com/repos/projectcalico/libcalico-go/subscribers","subscription_url":"https://api.github.com/repos/projectcalico/libcalico-go/subscription","commits_url":"https://api.github.com/repos/projectcalico/libcalico-go/commits{/sha}","git_commits_url":"https://api.github.com/repos/projectcalico/libcalico-go/git/commits{/sha}","comments_url":"https://api.github.com/repos/projectcalico/libcalico-go/comments{/number}","issue_comment_url":"https://api.github.com/repos/projectcalico/libcalico-go/issues/comments{/number}","contents_url":"https://api.github.com/repos/projectcalico/libcalico-go/contents/{+path}","compare_url":"https://api.github.com/repos/projectcalico/libcalico-go/compare/{base}...{head}","merges_url":"https://api.github.com/repos/projectcalico/libcalico-go/merges","archive_url":"https://api.github.com/repos/projectcalico/libcalico-go/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/projectcalico/libcalico-go/downloads","issues_url":"https://api.github.com/repos/projectcalico/libcalico-go/issues{/number}","pulls_url":"https://api.github.com/repos/projectcalico/libcalico-go/pulls{/number}","milestones_url":"https://api.github.com/repos/projectcalico/libcalico-go/milestones{/number}","notifications_url":"https://api.github.com/repos/projectcalico/libcalico-go/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/projectcalico/libcalico-go/labels{/name}","releases_url":"https://api.github.com/repos/projectcalico/libcalico-go/releases{/id}","deployments_url":"https://api.github.com/repos/projectcalico/libcalico-go/deployments","created_at":"2016-07-15T17:09:40Z","updated_at":"2017-12-21T03:13:50Z","pushed_at":"2018-02-26T22:15:16Z","git_url":"git://github.com/projectcalico/libcalico-go.git","ssh_url":"git@github.com:projectcalico/libcalico-go.git","clone_url":"https://github.com/projectcalico/libcalico-go.git","svn_url":"https://github.com/projectcalico/libcalico-go","homepage":null,"size":2920,"stargazers_count":43,"watchers_count":43,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":54,"mirror_url":null,"archived":false,"open_issues_count":27,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0"},"forks":54,"open_issues":27,"watchers":43,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/785"},"html":{"href":"https://github.com/projectcalico/libcalico-go/pull/785"},"issue":{"href":"https://api.github.com/repos/projectcalico/libcalico-go/issues/785"},"comments":{"href":"https://api.github.com/repos/projectcalico/libcalico-go/issues/785/comments"},"review_comments":{"href":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/785/comments"},"review_comment":{"href":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/projectcalico/libcalico-go/pulls/785/commits"},"statuses":{"href":"https://api.github.com/repos/projectcalico/libcalico-go/statuses/03e8764bd1788d110cd1c9631e7b78fad3ff996b"}},"author_association":"OWNER"}}
{"actor":{"display_login":"caseydavenport"}}