{"action":"created","comment":{"url":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/comments/214130515","pull_request_review_id":151119283,"id":214130515,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxNDEzMDUxNQ==","diff_hunk":"@@ -0,0 +1,266 @@\n+package uploadproxy\n+\n+import (\n+\t\"crypto/rand\"\n+\t\"crypto/rsa\"\n+\t\"crypto/tls\"\n+\t\"crypto/x509\"\n+\t\"fmt\"\n+\t\"io\"\n+\t\"io/ioutil\"\n+\t\"net/http\"\n+\t\"path/filepath\"\n+\n+\t\"github.com/golang/glog\"\n+\n+\t\"github.com/pkg/errors\"\n+\n+\t\"k8s.io/client-go/kubernetes\"\n+\t\"k8s.io/client-go/util/cert\"\n+\t\"k8s.io/client-go/util/cert/triple\"\n+\n+\tapiserver \"kubevirt.io/containerized-data-importer/pkg/apiserver\"\n+\t. \"kubevirt.io/containerized-data-importer/pkg/common\"\n+\tcontroller \"kubevirt.io/containerized-data-importer/pkg/controller\"\n+\tuploadserver \"kubevirt.io/containerized-data-importer/pkg/uploadserver\"\n+)\n+\n+const (\n+\t// selfsigned cert secret name\n+\tapiCertSecretName = \"cdi-api-certs\"\n+\n+\tapiServiceName = \"cdi-api\"\n+\n+\tcertBytesValue = \"cert-bytes\"\n+\tkeyBytesValue  = \"key-bytes\"\n+\n+\tuploadPath = \"/v1alpha1/upload\"\n+)\n+\n+type UploadProxyServer interface {\n+\tStart() error\n+}\n+\n+type uploadProxyApp struct {\n+\tbindAddress string\n+\tbindPort    uint\n+\n+\tclient kubernetes.Interface\n+\n+\tcertsDirectory string\n+\n+\tcertBytes []byte\n+\tkeyBytes  []byte\n+\n+\t// Used to decrypt token.\n+\tuploadProxyPrivateKey *rsa.PrivateKey\n+\n+\t// Used to verify token came from our apiserver.\n+\tapiServerPublicKey *rsa.PublicKey\n+\n+\tuploadServerClient *http.Client\n+}\n+\n+func NewUploadProxy(bindAddress string, bindPort uint, client kubernetes.Interface) (UploadProxyServer, error) {\n+\tvar err error\n+\tapp := &uploadProxyApp{\n+\t\tbindAddress: bindAddress,\n+\t\tbindPort:    bindPort,\n+\t\tclient:      client,\n+\t}\n+\tapp.certsDirectory, err = ioutil.TempDir(\"\", \"certsdir\")\n+\tif err != nil {\n+\t\treturn nil, errors.Errorf(\"Unable to create certs temporary directory: %v\\n\", errors.WithStack(err))\n+\t}\n+\n+\t// generate/retrieve RSA key used to decrypt tokens\n+\terr = app.generateKeys()\n+\tif err != nil {\n+\t\treturn nil, errors.Errorf(\"Unable to generate and retrieve rsa keys: %v\", errors.WithStack(err))\n+\t}\n+\n+\t// retrieve RSA key used by apiserver to sign tokens\n+\terr = app.getSigningKey()\n+\tif err != nil {\n+\t\treturn nil, errors.Errorf(\"Unable to retrieve apiserver signing key: %v\", errors.WithStack(err))\n+\t}\n+\n+\t// generate self signed cert\n+\terr = app.generateSelfSignedCert()\n+\tif err != nil {\n+\t\treturn nil, errors.Errorf(\"Unable to create self signed certs for upload proxy: %v\\n\", errors.WithStack(err))\n+\n+\t}\n+\n+\t// get upload server http client\n+\terr = app.getUploadServerClient()\n+\tif err != nil {\n+\t\treturn nil, errors.Errorf(\"Unable to create upload server client: %v\\n\", errors.WithStack(err))\n+\t}\n+\n+\treturn app, nil\n+}\n+\n+func (app *uploadProxyApp) getUploadServerClient() error {\n+\tclientCert, err := tls.LoadX509KeyPair(\"/etc/tls/uploadproxy/tls.cert\", \"/etc/tls/uploadproxy/tls.key\")\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\n+\tcaCertBytes, err := ioutil.ReadFile(\"/etc/tls/uploadproxy/ca.cert\")\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\n+\tcaCertPool := x509.NewCertPool()\n+\tcaCertPool.AppendCertsFromPEM(caCertBytes)\n+\n+\ttlsConfig := &tls.Config{\n+\t\tCertificates: []tls.Certificate{clientCert},\n+\t\tRootCAs:      caCertPool,\n+\t}\n+\ttlsConfig.BuildNameToCertificate()\n+\n+\ttransport := &http.Transport{TLSClientConfig: tlsConfig}\n+\tclient := &http.Client{Transport: transport}\n+\n+\tapp.uploadServerClient = client\n+\n+\treturn nil\n+}\n+\n+func (app *uploadProxyApp) handleUploadRequest(w http.ResponseWriter, r *http.Request) {\n+\n+\tencryptedTokenData := r.Header.Get(\"UPLOAD_TOKEN\")\n+\tif encryptedTokenData == \"\" {\n+\t\tw.WriteHeader(http.StatusBadRequest)\n+\t\treturn\n+\t}\n+\n+\ttokenData, err := apiserver.DecryptToken(encryptedTokenData, app.uploadProxyPrivateKey, app.apiServerPublicKey)\n+\n+\tif err != nil {\n+\t\tw.WriteHeader(http.StatusBadRequest)\n+\t\treturn\n+\t}\n+\n+\tglog.V(Vuser).Infof(\"Received valid token: pvc: %s, namespace: %s\", tokenData.PvcName, tokenData.Namespace)\n+\n+\tapp.proxyUploadRequest(tokenData.Namespace, tokenData.PvcName, w, r)\n+}\n+\n+func (app *uploadProxyApp) proxyUploadRequest(namespace, pvc string, w http.ResponseWriter, r *http.Request) {\n+\turl := fmt.Sprintf(\"https://%s.%s.svc%s\", controller.GetUploadResourceName(pvc), namespace, uploadserver.GetUploadPath(pvc))","path":"pkg/uploadproxy/uploadproxy.go","position":153,"original_position":153,"commit_id":"008b9b9707c3c69d269752beb979dc58fb36e784","original_commit_id":"008b9b9707c3c69d269752beb979dc58fb36e784","user":{"login":"mhenriks","id":7395935,"node_id":"MDQ6VXNlcjczOTU5MzU=","avatar_url":"https://avatars1.githubusercontent.com/u/7395935?v=4","gravatar_id":"","url":"https://api.github.com/users/mhenriks","html_url":"https://github.com/mhenriks","followers_url":"https://api.github.com/users/mhenriks/followers","following_url":"https://api.github.com/users/mhenriks/following{/other_user}","gists_url":"https://api.github.com/users/mhenriks/gists{/gist_id}","starred_url":"https://api.github.com/users/mhenriks/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhenriks/subscriptions","organizations_url":"https://api.github.com/users/mhenriks/orgs","repos_url":"https://api.github.com/users/mhenriks/repos","events_url":"https://api.github.com/users/mhenriks/events{/privacy}","received_events_url":"https://api.github.com/users/mhenriks/received_events","type":"User","site_admin":false},"body":"Just had another idea.  I was going to modify the apiserver to only return UploadTokens for PVCs with the `cdi.kubevirt.io/storage.upload.target` annotation.  Can also make sure `cdi.kubevirt.io/storage.upload.pod.phase` is `Running`.","created_at":"2018-08-30T18:13:20Z","updated_at":"2018-08-30T18:13:20Z","html_url":"https://github.com/kubevirt/containerized-data-importer/pull/381#discussion_r214130515","pull_request_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/381","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/comments/214130515"},"html":{"href":"https://github.com/kubevirt/containerized-data-importer/pull/381#discussion_r214130515"},"pull_request":{"href":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/381"}},"in_reply_to_id":214082417},"pull_request":{"url":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/381","id":211600355,"node_id":"MDExOlB1bGxSZXF1ZXN0MjExNjAwMzU1","html_url":"https://github.com/kubevirt/containerized-data-importer/pull/381","diff_url":"https://github.com/kubevirt/containerized-data-importer/pull/381.diff","patch_url":"https://github.com/kubevirt/containerized-data-importer/pull/381.patch","issue_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/issues/381","number":381,"state":"open","locked":false,"title":"[WIP] Upload end to end","user":{"login":"mhenriks","id":7395935,"node_id":"MDQ6VXNlcjczOTU5MzU=","avatar_url":"https://avatars1.githubusercontent.com/u/7395935?v=4","gravatar_id":"","url":"https://api.github.com/users/mhenriks","html_url":"https://github.com/mhenriks","followers_url":"https://api.github.com/users/mhenriks/followers","following_url":"https://api.github.com/users/mhenriks/following{/other_user}","gists_url":"https://api.github.com/users/mhenriks/gists{/gist_id}","starred_url":"https://api.github.com/users/mhenriks/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhenriks/subscriptions","organizations_url":"https://api.github.com/users/mhenriks/orgs","repos_url":"https://api.github.com/users/mhenriks/repos","events_url":"https://api.github.com/users/mhenriks/events{/privacy}","received_events_url":"https://api.github.com/users/mhenriks/received_events","type":"User","site_admin":false},"body":"This is a continuation of #307.  So you should probably be familiar with that.  And mostly focus on the most recent commits by me.  Goal is to provide end to end implementation of upload feature.  What is included here:\r\n\r\nUpload Controller:  This controller looks for PVCs with the annotation \"cdi.kubevirt.io/storage.upload.target\" (that can be changed obviously).  If it sees one, it will create an instance of an Upload Server pod and a corresponding kubernetes service.  When the Upload Server pod gets into \"Completed\" state, the service is destroyed.  If the annotation gets removed from a PVC, the pod and service are destroyed.  That's basically all it does.  But in order for the Upload Proxy to securely communicate with an Upload Service pod, the Upload Controller manages a couple CAs.  One for Upload Servers and one for Upload Server clients (Upload Proxy).  Each Server gets a unique TLS cert.  Currently, all the clients share a client cert.  Certs are stored a kubernetes secrets and mounted as volumes in pods.\r\n\r\nUpload Server:  Basic http server that handles file uploads by passing bodies of http posts of to the dataStream object (which handles decompression and format conversion if necessary).  Once a file has been successfully uploaded, the Upload Server exits.  This is in order to free up the PVC for whoever needs it next.  As alluded to earlier, the Upload Server only accepts TLS connects from clients with certificates signed by the Upload Controller.\r\n\r\nUpload Proxy forwarding:  The Upload Proxy is wired up to forward requests to an Upload Server pod via an service.  So, if we are uploading to PVC 'foo' in namespace 'bar', the Upload Proxy will connect to 'https://cdi-upload-foo.bar.svc'.\r\n\r\nLot of stuff here, but still a lot to go:\r\n\r\n- [x] Upload Controller\r\n- [x] Upload Server\r\n- [x] Upload Proxy forwarding to Upload Server\r\n- [ ] Refactor Upload Token to be JWE (in progress)\r\n- [ ] Unit tests\r\n- [ ] Documentation\r\n- [ ] Functional Tests","created_at":"2018-08-29T01:08:39Z","updated_at":"2018-08-30T18:13:20Z","closed_at":null,"merged_at":null,"merge_commit_sha":"1114f5296f54f18d61cadca5a29478d6ebe0af7a","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/381/commits","review_comments_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/381/comments","review_comment_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/comments{/number}","comments_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/issues/381/comments","statuses_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/statuses/008b9b9707c3c69d269752beb979dc58fb36e784","head":{"label":"mhenriks:upload-e2e-v3","ref":"upload-e2e-v3","sha":"008b9b9707c3c69d269752beb979dc58fb36e784","user":{"login":"mhenriks","id":7395935,"node_id":"MDQ6VXNlcjczOTU5MzU=","avatar_url":"https://avatars1.githubusercontent.com/u/7395935?v=4","gravatar_id":"","url":"https://api.github.com/users/mhenriks","html_url":"https://github.com/mhenriks","followers_url":"https://api.github.com/users/mhenriks/followers","following_url":"https://api.github.com/users/mhenriks/following{/other_user}","gists_url":"https://api.github.com/users/mhenriks/gists{/gist_id}","starred_url":"https://api.github.com/users/mhenriks/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhenriks/subscriptions","organizations_url":"https://api.github.com/users/mhenriks/orgs","repos_url":"https://api.github.com/users/mhenriks/repos","events_url":"https://api.github.com/users/mhenriks/events{/privacy}","received_events_url":"https://api.github.com/users/mhenriks/received_events","type":"User","site_admin":false},"repo":{"id":142875652,"node_id":"MDEwOlJlcG9zaXRvcnkxNDI4NzU2NTI=","name":"containerized-data-importer","full_name":"mhenriks/containerized-data-importer","owner":{"login":"mhenriks","id":7395935,"node_id":"MDQ6VXNlcjczOTU5MzU=","avatar_url":"https://avatars1.githubusercontent.com/u/7395935?v=4","gravatar_id":"","url":"https://api.github.com/users/mhenriks","html_url":"https://github.com/mhenriks","followers_url":"https://api.github.com/users/mhenriks/followers","following_url":"https://api.github.com/users/mhenriks/following{/other_user}","gists_url":"https://api.github.com/users/mhenriks/gists{/gist_id}","starred_url":"https://api.github.com/users/mhenriks/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhenriks/subscriptions","organizations_url":"https://api.github.com/users/mhenriks/orgs","repos_url":"https://api.github.com/users/mhenriks/repos","events_url":"https://api.github.com/users/mhenriks/events{/privacy}","received_events_url":"https://api.github.com/users/mhenriks/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mhenriks/containerized-data-importer","description":"Data Import Service for kubernetes, designed with kubevirt in mind.","fork":true,"url":"https://api.github.com/repos/mhenriks/containerized-data-importer","forks_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/forks","keys_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/teams","hooks_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/hooks","issue_events_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/issues/events{/number}","events_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/events","assignees_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/assignees{/user}","branches_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/branches{/branch}","tags_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/tags","blobs_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/git/refs{/sha}","trees_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/statuses/{sha}","languages_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/languages","stargazers_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/stargazers","contributors_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/contributors","subscribers_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/subscribers","subscription_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/subscription","commits_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/commits{/sha}","git_commits_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/git/commits{/sha}","comments_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/comments{/number}","issue_comment_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/issues/comments{/number}","contents_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/contents/{+path}","compare_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/merges","archive_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/downloads","issues_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/issues{/number}","pulls_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/pulls{/number}","milestones_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/milestones{/number}","notifications_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/labels{/name}","releases_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/releases{/id}","deployments_url":"https://api.github.com/repos/mhenriks/containerized-data-importer/deployments","created_at":"2018-07-30T12:56:21Z","updated_at":"2018-08-15T18:45:59Z","pushed_at":"2018-08-29T01:11:42Z","git_url":"git://github.com/mhenriks/containerized-data-importer.git","ssh_url":"git@github.com:mhenriks/containerized-data-importer.git","clone_url":"https://github.com/mhenriks/containerized-data-importer.git","svn_url":"https://github.com/mhenriks/containerized-data-importer","homepage":"","size":48127,"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":"kubevirt:master","ref":"master","sha":"35bf52f19f8606660cb29b29a0e297bc2dc9111e","user":{"login":"kubevirt","id":18700703,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE4NzAwNzAz","avatar_url":"https://avatars1.githubusercontent.com/u/18700703?v=4","gravatar_id":"","url":"https://api.github.com/users/kubevirt","html_url":"https://github.com/kubevirt","followers_url":"https://api.github.com/users/kubevirt/followers","following_url":"https://api.github.com/users/kubevirt/following{/other_user}","gists_url":"https://api.github.com/users/kubevirt/gists{/gist_id}","starred_url":"https://api.github.com/users/kubevirt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kubevirt/subscriptions","organizations_url":"https://api.github.com/users/kubevirt/orgs","repos_url":"https://api.github.com/users/kubevirt/repos","events_url":"https://api.github.com/users/kubevirt/events{/privacy}","received_events_url":"https://api.github.com/users/kubevirt/received_events","type":"Organization","site_admin":false},"repo":{"id":117756215,"node_id":"MDEwOlJlcG9zaXRvcnkxMTc3NTYyMTU=","name":"containerized-data-importer","full_name":"kubevirt/containerized-data-importer","owner":{"login":"kubevirt","id":18700703,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE4NzAwNzAz","avatar_url":"https://avatars1.githubusercontent.com/u/18700703?v=4","gravatar_id":"","url":"https://api.github.com/users/kubevirt","html_url":"https://github.com/kubevirt","followers_url":"https://api.github.com/users/kubevirt/followers","following_url":"https://api.github.com/users/kubevirt/following{/other_user}","gists_url":"https://api.github.com/users/kubevirt/gists{/gist_id}","starred_url":"https://api.github.com/users/kubevirt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kubevirt/subscriptions","organizations_url":"https://api.github.com/users/kubevirt/orgs","repos_url":"https://api.github.com/users/kubevirt/repos","events_url":"https://api.github.com/users/kubevirt/events{/privacy}","received_events_url":"https://api.github.com/users/kubevirt/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/kubevirt/containerized-data-importer","description":"Data Import Service for kubernetes, designed with kubevirt in mind.","fork":false,"url":"https://api.github.com/repos/kubevirt/containerized-data-importer","forks_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/forks","keys_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/teams","hooks_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/hooks","issue_events_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/issues/events{/number}","events_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/events","assignees_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/assignees{/user}","branches_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/branches{/branch}","tags_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/tags","blobs_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/git/refs{/sha}","trees_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/statuses/{sha}","languages_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/languages","stargazers_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/stargazers","contributors_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/contributors","subscribers_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/subscribers","subscription_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/subscription","commits_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/commits{/sha}","git_commits_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/git/commits{/sha}","comments_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/comments{/number}","issue_comment_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/issues/comments{/number}","contents_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/contents/{+path}","compare_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/merges","archive_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/downloads","issues_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/issues{/number}","pulls_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls{/number}","milestones_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/milestones{/number}","notifications_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/labels{/name}","releases_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/releases{/id}","deployments_url":"https://api.github.com/repos/kubevirt/containerized-data-importer/deployments","created_at":"2018-01-16T23:35:48Z","updated_at":"2018-08-30T17:21:27Z","pushed_at":"2018-08-30T18:12:48Z","git_url":"git://github.com/kubevirt/containerized-data-importer.git","ssh_url":"git@github.com:kubevirt/containerized-data-importer.git","clone_url":"https://github.com/kubevirt/containerized-data-importer.git","svn_url":"https://github.com/kubevirt/containerized-data-importer","homepage":"","size":47904,"stargazers_count":64,"watchers_count":64,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":28,"mirror_url":null,"archived":false,"open_issues_count":34,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":28,"open_issues":34,"watchers":64,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/381"},"html":{"href":"https://github.com/kubevirt/containerized-data-importer/pull/381"},"issue":{"href":"https://api.github.com/repos/kubevirt/containerized-data-importer/issues/381"},"comments":{"href":"https://api.github.com/repos/kubevirt/containerized-data-importer/issues/381/comments"},"review_comments":{"href":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/381/comments"},"review_comment":{"href":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/kubevirt/containerized-data-importer/pulls/381/commits"},"statuses":{"href":"https://api.github.com/repos/kubevirt/containerized-data-importer/statuses/008b9b9707c3c69d269752beb979dc58fb36e784"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"mhenriks"}}