{"action":"created","comment":{"url":"https://api.github.com/repos/rancher/sparse-tools/pulls/comments/71103798","id":71103798,"diff_hunk":"@@ -51,207 +47,184 @@ func syncFile(localPath string, addr TCPEndPoint, remotePath string, timeout int\n \t}\n \tif err != nil {\n \t\tlog.Error(\"Failed to open local source file:\", localPath)\n-\t\treturn nil, err\n+\t\treturn err\n \t}\n \tdefer fileIo.Close()\n \n-\tconn := connect(addr.Host, strconv.Itoa(int(addr.Port)), timeout)\n-\tif nil == conn {\n-\t\terr = fmt.Errorf(\"Failed to connect to %v\", addr)\n-\t\tlog.Error(err)\n-\t\treturn nil, err\n-\t}\n-\tdefer conn.Close()\n+\tclient := &syncClient{remote, timeout, localPath, fileSize, fileIo}\n \n-\tencoder := gob.NewEncoder(conn)\n-\tdecoder := gob.NewDecoder(conn)\n-\n-\tsession := &SyncSessionClient{encoder, decoder}\n-\terr = session.sendSyncRequest(remotePath, fileSize)\n-\tif err != nil {\n-\t\tlog.Errorf(\"Sync request to %s failed, error: %s\", remotePath, err)\n-\t\treturn nil, err\n-\t}\n+\tdefer client.closeServer() // kill the server no matter success or not, best effort\n \n-\terr = session.syncFileContent(fileIo, fileSize)\n+\terr = client.syncFileContent(fileIo, fileSize)\n \tif err != nil {\n-\t\tlog.Error(\"syncFileContent failed: \", err)\n-\t\treturn nil, err\n+\t\tlog.Errorf(\"syncFileContent failed: %s\", err)\n+\t\treturn err\n \t}\n \n-\treturn nil, err\n+\treturn err\n }\n \n-func connect(host, port string, timeout int) net.Conn {\n-\t// connect to this socket\n-\tendpoint := host + \":\" + port\n-\traddr, err := net.ResolveTCPAddr(\"tcp\", endpoint)\n+func (client *syncClient) syncFileContent(file FileIoProcessor, fileSize int64) error {\n+\texts, err := GetFiemapExtents(file)\n \tif err != nil {\n-\t\tlog.Fatal(\"Connection address resolution error:\", err)\n-\t}\n-\ttimeStart := time.Now()\n-\ttimeStop := timeStart.Add(time.Duration(timeout) * time.Second)\n-\tfor timeNow := timeStart; timeNow.Before(timeStop); timeNow = time.Now() {\n-\t\tconn, err := net.DialTCP(\"tcp\", nil, raddr)\n-\t\tif err == nil {\n-\t\t\tlog.Info(\"connected to server\")\n-\t\t\treturn conn\n-\t\t}\n-\t\tlog.Warn(\"Failed connection to\", endpoint, \"Retrying...\")\n-\t\tif timeNow != timeStart {\n-\t\t\t// only sleep after the second attempt to speedup tests\n-\t\t\ttime.Sleep(1 * time.Second)\n-\t\t}\n+\t\treturn err\n \t}\n-\treturn nil\n-}\n-\n-type SyncSessionClient struct {\n-\tencoder *gob.Encoder\n-\tdecoder *gob.Decoder\n-}\n-\n-func (session *SyncSessionClient) send(e interface{}) error {\n-\treturn session.encoder.Encode(e)\n-}\n-\n-func (session *SyncSessionClient) receive(e interface{}) error {\n-\treturn session.decoder.Decode(e)\n-}\n \n-func (session *SyncSessionClient) syncFileContent(file FileIoProcessor, fileSize int64) error {\n-\texts, err := GetFiemapExtents(file)\n+\terr = client.openServer()\n \tif err != nil {\n-\t\treturn err\n+\t\treturn fmt.Errorf(\"openServer failed, err: %s\", err)\n \t}\n \n \tvar lastIntervalEnd int64\n \tvar holeInterval Interval\n \n-\t// Process each extent\n \tfor _, e := range exts {\n \t\tinterval := Interval{int64(e.Logical), int64(e.Logical + e.Length)}\n-\t\tlog.Debugf(\"Extent: %s, %x\", interval, e.Flags)\n \n+\t\t// report hole\n \t\tif lastIntervalEnd < interval.Begin {\n-\t\t\t// report hole\n \t\t\tholeInterval = Interval{lastIntervalEnd, interval.Begin}\n-\t\t\tlog.Debugf(\"Here is a hole: %s\", holeInterval)\n-\n-\t\t\t// syncing hole interval\n-\t\t\terr := session.SyncHoleInterval(holeInterval)\n+\t\t\terr := client.syncHoleInterval(holeInterval)\n \t\t\tif err != nil {\n-\t\t\t\tlog.Debugf(\"SyncHoleInterval failed: %s\", holeInterval)\n-\t\t\t\treturn err\n+\t\t\t\treturn fmt.Errorf(\"syncHoleInterval %s failed, err: %s\", holeInterval, err)\n \t\t\t}\n \t\t}\n-\t\t// report data\n-\t\tlog.Debugf(\"Here is a data: %s\", interval)\n \t\tlastIntervalEnd = interval.End\n \n-\t\t// syncing data interval\n-\t\terr := session.SyncDataInterval(file, interval)\n+\t\t// report data\n+\t\terr := client.syncDataInterval(file, interval)\n \t\tif err != nil {\n-\t\t\tlog.Debugf(\"SyncDataInterval failed: %s\", interval)\n-\t\t\treturn err\n+\t\t\treturn fmt.Errorf(\"syncDataInterval %s failed, err: %s\", interval, err)\n \t\t}\n+\n \t\tif e.Flags&FIEMAP_EXTENT_LAST != 0 {\n+\n \t\t\t// report last hole\n \t\t\tif lastIntervalEnd < fileSize {\n \t\t\t\tholeInterval := Interval{lastIntervalEnd, fileSize}\n-\t\t\t\tlog.Debugf(\"Here is a hole: %s\", holeInterval)\n \n \t\t\t\t// syncing hole interval\n-\t\t\t\terr = session.SyncHoleInterval(holeInterval)\n+\t\t\t\terr = client.syncHoleInterval(holeInterval)\n \t\t\t\tif err != nil {\n-\t\t\t\t\tlog.Debugf(\"SyncHoleInterval failed: %s\", holeInterval)\n-\t\t\t\t\treturn err\n+\t\t\t\t\treturn fmt.Errorf(\"syncHoleInterval %s failed, err: %s\", holeInterval, err)\n \t\t\t\t}\n \t\t\t}\n \t\t}\n \t}\n \n \t// special case, the whole file is a hole\n \tif len(exts) == 0 && fileSize != 0 {\n-\t\t// report hole\n \t\tholeInterval = Interval{0, fileSize}\n-\t\tlog.Debugf(\"The file is a hole: %s\", holeInterval)\n+\t\tlog.Infof(\"The file is a hole: %s\", holeInterval)\n \n \t\t// syncing hole interval\n-\t\terr := session.SyncHoleInterval(holeInterval)\n+\t\terr := client.syncHoleInterval(holeInterval)\n \t\tif err != nil {\n-\t\t\tlog.Debugf(\"SyncHoleInterval failed: %s\", holeInterval)\n-\t\t\treturn err\n+\t\t\treturn fmt.Errorf(\"syncHoleInterval %s failed, err: %s\", holeInterval, err)\n \t\t}\n \t}\n \n-\treturn session.send(requestHeader{requestMagic, syncDone})\n+\treturn nil\n }\n \n-func (session *SyncSessionClient) sendSyncRequest(path string, size int64) error {\n-\terr := session.send(requestHeader{requestMagic, syncRequestCode})\n-\tif err != nil {\n-\t\tlog.Debug(\"send requestHeader failed: \", err)\n-\t\treturn err\n-\t}\n-\terr = session.send(path)\n-\tif err != nil {\n-\t\tlog.Debug(\"send path failed: \", err)\n-\t\treturn err\n+func (client *syncClient) sendHTTPRequest(method string, action string, interval Interval, data []byte) (*http.Response, error) {\n+\thttpClient := &http.Client{Timeout: time.Duration(httpClientTimeout * time.Second)}\n+\n+\turl := fmt.Sprintf(\"http://%s/v1-ssync/%s\", client.remote, action)\n+\n+\tvar req *http.Request\n+\tif data != nil {\n+\t\treq, _ = http.NewRequest(method, url, bytes.NewBuffer(data))\n+\t} else {\n+\t\treq, _ = http.NewRequest(method, url, nil)","path":"sparse/client.go","position":253,"original_position":253,"commit_id":"575c3cc76d18d917b2c69396b5644146b2510d9e","original_commit_id":"575c3cc76d18d917b2c69396b5644146b2510d9e","user":{"login":"jimengliu","id":17127678,"avatar_url":"https://avatars.githubusercontent.com/u/17127678?v=3","gravatar_id":"","url":"https://api.github.com/users/jimengliu","html_url":"https://github.com/jimengliu","followers_url":"https://api.github.com/users/jimengliu/followers","following_url":"https://api.github.com/users/jimengliu/following{/other_user}","gists_url":"https://api.github.com/users/jimengliu/gists{/gist_id}","starred_url":"https://api.github.com/users/jimengliu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jimengliu/subscriptions","organizations_url":"https://api.github.com/users/jimengliu/orgs","repos_url":"https://api.github.com/users/jimengliu/repos","events_url":"https://api.github.com/users/jimengliu/events{/privacy}","received_events_url":"https://api.github.com/users/jimengliu/received_events","type":"User","site_admin":false},"body":"this is the same","created_at":"2016-07-18T06:30:36Z","updated_at":"2016-07-18T06:30:36Z","html_url":"https://github.com/rancher/sparse-tools/pull/48#discussion_r71103798","pull_request_url":"https://api.github.com/repos/rancher/sparse-tools/pulls/48","_links":{"self":{"href":"https://api.github.com/repos/rancher/sparse-tools/pulls/comments/71103798"},"html":{"href":"https://github.com/rancher/sparse-tools/pull/48#discussion_r71103798"},"pull_request":{"href":"https://api.github.com/repos/rancher/sparse-tools/pulls/48"}}},"pull_request":{"url":"https://api.github.com/repos/rancher/sparse-tools/pulls/48","id":77628420,"html_url":"https://github.com/rancher/sparse-tools/pull/48","diff_url":"https://github.com/rancher/sparse-tools/pull/48.diff","patch_url":"https://github.com/rancher/sparse-tools/pull/48.patch","issue_url":"https://api.github.com/repos/rancher/sparse-tools/issues/48","number":48,"state":"open","locked":false,"title":"change gob to rest calls","user":{"login":"jimengliu","id":17127678,"avatar_url":"https://avatars.githubusercontent.com/u/17127678?v=3","gravatar_id":"","url":"https://api.github.com/users/jimengliu","html_url":"https://github.com/jimengliu","followers_url":"https://api.github.com/users/jimengliu/followers","following_url":"https://api.github.com/users/jimengliu/following{/other_user}","gists_url":"https://api.github.com/users/jimengliu/gists{/gist_id}","starred_url":"https://api.github.com/users/jimengliu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jimengliu/subscriptions","organizations_url":"https://api.github.com/users/jimengliu/orgs","repos_url":"https://api.github.com/users/jimengliu/repos","events_url":"https://api.github.com/users/jimengliu/events{/privacy}","received_events_url":"https://api.github.com/users/jimengliu/received_events","type":"User","site_admin":false},"body":"- make client and server communicate using http rest calls instead of GOB\r\n- re-organize files and folders\r\n\r\n@yasker @ibuildthecloud ","created_at":"2016-07-15T14:54:06Z","updated_at":"2016-07-18T06:30:36Z","closed_at":null,"merged_at":null,"merge_commit_sha":"84d9296d0e82c5753b3109dea6bb96674a039922","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/rancher/sparse-tools/pulls/48/commits","review_comments_url":"https://api.github.com/repos/rancher/sparse-tools/pulls/48/comments","review_comment_url":"https://api.github.com/repos/rancher/sparse-tools/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rancher/sparse-tools/issues/48/comments","statuses_url":"https://api.github.com/repos/rancher/sparse-tools/statuses/575c3cc76d18d917b2c69396b5644146b2510d9e","head":{"label":"jimengliu:fixLog","ref":"fixLog","sha":"575c3cc76d18d917b2c69396b5644146b2510d9e","user":{"login":"jimengliu","id":17127678,"avatar_url":"https://avatars.githubusercontent.com/u/17127678?v=3","gravatar_id":"","url":"https://api.github.com/users/jimengliu","html_url":"https://github.com/jimengliu","followers_url":"https://api.github.com/users/jimengliu/followers","following_url":"https://api.github.com/users/jimengliu/following{/other_user}","gists_url":"https://api.github.com/users/jimengliu/gists{/gist_id}","starred_url":"https://api.github.com/users/jimengliu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jimengliu/subscriptions","organizations_url":"https://api.github.com/users/jimengliu/orgs","repos_url":"https://api.github.com/users/jimengliu/repos","events_url":"https://api.github.com/users/jimengliu/events{/privacy}","received_events_url":"https://api.github.com/users/jimengliu/received_events","type":"User","site_admin":false},"repo":{"id":61582848,"name":"sparse-tools","full_name":"jimengliu/sparse-tools","owner":{"login":"jimengliu","id":17127678,"avatar_url":"https://avatars.githubusercontent.com/u/17127678?v=3","gravatar_id":"","url":"https://api.github.com/users/jimengliu","html_url":"https://github.com/jimengliu","followers_url":"https://api.github.com/users/jimengliu/followers","following_url":"https://api.github.com/users/jimengliu/following{/other_user}","gists_url":"https://api.github.com/users/jimengliu/gists{/gist_id}","starred_url":"https://api.github.com/users/jimengliu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jimengliu/subscriptions","organizations_url":"https://api.github.com/users/jimengliu/orgs","repos_url":"https://api.github.com/users/jimengliu/repos","events_url":"https://api.github.com/users/jimengliu/events{/privacy}","received_events_url":"https://api.github.com/users/jimengliu/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jimengliu/sparse-tools","description":"sparse storage slab synchronization, etc.","fork":true,"url":"https://api.github.com/repos/jimengliu/sparse-tools","forks_url":"https://api.github.com/repos/jimengliu/sparse-tools/forks","keys_url":"https://api.github.com/repos/jimengliu/sparse-tools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jimengliu/sparse-tools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jimengliu/sparse-tools/teams","hooks_url":"https://api.github.com/repos/jimengliu/sparse-tools/hooks","issue_events_url":"https://api.github.com/repos/jimengliu/sparse-tools/issues/events{/number}","events_url":"https://api.github.com/repos/jimengliu/sparse-tools/events","assignees_url":"https://api.github.com/repos/jimengliu/sparse-tools/assignees{/user}","branches_url":"https://api.github.com/repos/jimengliu/sparse-tools/branches{/branch}","tags_url":"https://api.github.com/repos/jimengliu/sparse-tools/tags","blobs_url":"https://api.github.com/repos/jimengliu/sparse-tools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jimengliu/sparse-tools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jimengliu/sparse-tools/git/refs{/sha}","trees_url":"https://api.github.com/repos/jimengliu/sparse-tools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jimengliu/sparse-tools/statuses/{sha}","languages_url":"https://api.github.com/repos/jimengliu/sparse-tools/languages","stargazers_url":"https://api.github.com/repos/jimengliu/sparse-tools/stargazers","contributors_url":"https://api.github.com/repos/jimengliu/sparse-tools/contributors","subscribers_url":"https://api.github.com/repos/jimengliu/sparse-tools/subscribers","subscription_url":"https://api.github.com/repos/jimengliu/sparse-tools/subscription","commits_url":"https://api.github.com/repos/jimengliu/sparse-tools/commits{/sha}","git_commits_url":"https://api.github.com/repos/jimengliu/sparse-tools/git/commits{/sha}","comments_url":"https://api.github.com/repos/jimengliu/sparse-tools/comments{/number}","issue_comment_url":"https://api.github.com/repos/jimengliu/sparse-tools/issues/comments{/number}","contents_url":"https://api.github.com/repos/jimengliu/sparse-tools/contents/{+path}","compare_url":"https://api.github.com/repos/jimengliu/sparse-tools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jimengliu/sparse-tools/merges","archive_url":"https://api.github.com/repos/jimengliu/sparse-tools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jimengliu/sparse-tools/downloads","issues_url":"https://api.github.com/repos/jimengliu/sparse-tools/issues{/number}","pulls_url":"https://api.github.com/repos/jimengliu/sparse-tools/pulls{/number}","milestones_url":"https://api.github.com/repos/jimengliu/sparse-tools/milestones{/number}","notifications_url":"https://api.github.com/repos/jimengliu/sparse-tools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jimengliu/sparse-tools/labels{/name}","releases_url":"https://api.github.com/repos/jimengliu/sparse-tools/releases{/id}","deployments_url":"https://api.github.com/repos/jimengliu/sparse-tools/deployments","created_at":"2016-06-20T21:52:53Z","updated_at":"2016-06-20T21:52:54Z","pushed_at":"2016-07-17T07:35:10Z","git_url":"git://github.com/jimengliu/sparse-tools.git","ssh_url":"git@github.com:jimengliu/sparse-tools.git","clone_url":"https://github.com/jimengliu/sparse-tools.git","svn_url":"https://github.com/jimengliu/sparse-tools","homepage":null,"size":1694,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":true,"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":"rancher:master","ref":"master","sha":"2ff044e474e8a863a337ebbee10a3c1413fb1dcb","user":{"login":"rancher","id":9343010,"avatar_url":"https://avatars.githubusercontent.com/u/9343010?v=3","gravatar_id":"","url":"https://api.github.com/users/rancher","html_url":"https://github.com/rancher","followers_url":"https://api.github.com/users/rancher/followers","following_url":"https://api.github.com/users/rancher/following{/other_user}","gists_url":"https://api.github.com/users/rancher/gists{/gist_id}","starred_url":"https://api.github.com/users/rancher/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rancher/subscriptions","organizations_url":"https://api.github.com/users/rancher/orgs","repos_url":"https://api.github.com/users/rancher/repos","events_url":"https://api.github.com/users/rancher/events{/privacy}","received_events_url":"https://api.github.com/users/rancher/received_events","type":"Organization","site_admin":false},"repo":{"id":56107772,"name":"sparse-tools","full_name":"rancher/sparse-tools","owner":{"login":"rancher","id":9343010,"avatar_url":"https://avatars.githubusercontent.com/u/9343010?v=3","gravatar_id":"","url":"https://api.github.com/users/rancher","html_url":"https://github.com/rancher","followers_url":"https://api.github.com/users/rancher/followers","following_url":"https://api.github.com/users/rancher/following{/other_user}","gists_url":"https://api.github.com/users/rancher/gists{/gist_id}","starred_url":"https://api.github.com/users/rancher/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rancher/subscriptions","organizations_url":"https://api.github.com/users/rancher/orgs","repos_url":"https://api.github.com/users/rancher/repos","events_url":"https://api.github.com/users/rancher/events{/privacy}","received_events_url":"https://api.github.com/users/rancher/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rancher/sparse-tools","description":"sparse storage slab synchronization, etc.","fork":false,"url":"https://api.github.com/repos/rancher/sparse-tools","forks_url":"https://api.github.com/repos/rancher/sparse-tools/forks","keys_url":"https://api.github.com/repos/rancher/sparse-tools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rancher/sparse-tools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rancher/sparse-tools/teams","hooks_url":"https://api.github.com/repos/rancher/sparse-tools/hooks","issue_events_url":"https://api.github.com/repos/rancher/sparse-tools/issues/events{/number}","events_url":"https://api.github.com/repos/rancher/sparse-tools/events","assignees_url":"https://api.github.com/repos/rancher/sparse-tools/assignees{/user}","branches_url":"https://api.github.com/repos/rancher/sparse-tools/branches{/branch}","tags_url":"https://api.github.com/repos/rancher/sparse-tools/tags","blobs_url":"https://api.github.com/repos/rancher/sparse-tools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rancher/sparse-tools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rancher/sparse-tools/git/refs{/sha}","trees_url":"https://api.github.com/repos/rancher/sparse-tools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rancher/sparse-tools/statuses/{sha}","languages_url":"https://api.github.com/repos/rancher/sparse-tools/languages","stargazers_url":"https://api.github.com/repos/rancher/sparse-tools/stargazers","contributors_url":"https://api.github.com/repos/rancher/sparse-tools/contributors","subscribers_url":"https://api.github.com/repos/rancher/sparse-tools/subscribers","subscription_url":"https://api.github.com/repos/rancher/sparse-tools/subscription","commits_url":"https://api.github.com/repos/rancher/sparse-tools/commits{/sha}","git_commits_url":"https://api.github.com/repos/rancher/sparse-tools/git/commits{/sha}","comments_url":"https://api.github.com/repos/rancher/sparse-tools/comments{/number}","issue_comment_url":"https://api.github.com/repos/rancher/sparse-tools/issues/comments{/number}","contents_url":"https://api.github.com/repos/rancher/sparse-tools/contents/{+path}","compare_url":"https://api.github.com/repos/rancher/sparse-tools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rancher/sparse-tools/merges","archive_url":"https://api.github.com/repos/rancher/sparse-tools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rancher/sparse-tools/downloads","issues_url":"https://api.github.com/repos/rancher/sparse-tools/issues{/number}","pulls_url":"https://api.github.com/repos/rancher/sparse-tools/pulls{/number}","milestones_url":"https://api.github.com/repos/rancher/sparse-tools/milestones{/number}","notifications_url":"https://api.github.com/repos/rancher/sparse-tools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rancher/sparse-tools/labels{/name}","releases_url":"https://api.github.com/repos/rancher/sparse-tools/releases{/id}","deployments_url":"https://api.github.com/repos/rancher/sparse-tools/deployments","created_at":"2016-04-13T00:26:40Z","updated_at":"2016-04-24T04:32:29Z","pushed_at":"2016-07-17T07:35:10Z","git_url":"git://github.com/rancher/sparse-tools.git","ssh_url":"git@github.com:rancher/sparse-tools.git","clone_url":"https://github.com/rancher/sparse-tools.git","svn_url":"https://github.com/rancher/sparse-tools","homepage":null,"size":1665,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4,"mirror_url":null,"open_issues_count":1,"forks":4,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rancher/sparse-tools/pulls/48"},"html":{"href":"https://github.com/rancher/sparse-tools/pull/48"},"issue":{"href":"https://api.github.com/repos/rancher/sparse-tools/issues/48"},"comments":{"href":"https://api.github.com/repos/rancher/sparse-tools/issues/48/comments"},"review_comments":{"href":"https://api.github.com/repos/rancher/sparse-tools/pulls/48/comments"},"review_comment":{"href":"https://api.github.com/repos/rancher/sparse-tools/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rancher/sparse-tools/pulls/48/commits"},"statuses":{"href":"https://api.github.com/repos/rancher/sparse-tools/statuses/575c3cc76d18d917b2c69396b5644146b2510d9e"}}}}
{"actor":{"display_login":"jimengliu"}}