{"action":"created","comment":{"url":"https://api.github.com/repos/openshift/origin/pulls/comments/89975778","pull_request_review_id":10501253,"id":89975778,"diff_hunk":"@@ -0,0 +1,179 @@\n+package index\n+\n+import (\n+\t\"time\"\n+\n+\t\"github.com/docker/distribution/digest\"\n+\t\"github.com/golang/glog\"\n+\t\"github.com/openshift/origin/pkg/client\"\n+\timageapi \"github.com/openshift/origin/pkg/image/api\"\n+\t\"k8s.io/kubernetes/pkg/api\"\n+\t\"k8s.io/kubernetes/pkg/client/cache\"\n+\t\"k8s.io/kubernetes/pkg/controller/framework\"\n+\t\"k8s.io/kubernetes/pkg/runtime\"\n+\t\"k8s.io/kubernetes/pkg/watch\"\n+)\n+\n+type ImageIndex struct {\n+\tclient client.ImageInterface\n+\n+\timageController  *framework.Controller\n+\timageIndex       cache.Indexer\n+\timageStoreSynced func() bool\n+}\n+\n+var (\n+\tChainIndexName     = \"chain\"\n+\tLastChainIndexName = \"last-chain\"\n+\t// TODO: Re-listing images might be expensive on large clusters, this should be\n+\t// configurable?\n+\tResyncInterval = 10 * time.Minute\n+)\n+\n+// NewImageIndex returns a new image index.\n+func NewImageIndex(client client.ImageInterface, stopChan <-chan struct{}) *ImageIndex {\n+\tc := &ImageIndex{\n+\t\tclient: client,\n+\t}\n+\tc.imageIndex, c.imageController = framework.NewIndexerInformer(\n+\t\t&cache.ListWatch{\n+\t\t\tListFunc: func(options api.ListOptions) (runtime.Object, error) {\n+\t\t\t\treturn c.client.List(options)\n+\t\t\t},\n+\t\t\tWatchFunc: func(options api.ListOptions) (watch.Interface, error) {\n+\t\t\t\treturn c.client.Watch(options)\n+\t\t\t},\n+\t\t},\n+\t\t&imageapi.Image{},\n+\t\tResyncInterval,\n+\t\tframework.ResourceEventHandlerFuncs{\n+\t\t\tAddFunc:    c.addImage,\n+\t\t\tUpdateFunc: c.updateImage,\n+\t\t\tDeleteFunc: c.deleteImage,\n+\t\t},\n+\t\tcache.Indexers{\n+\t\t\tChainIndexName:     chainIndexFunc,\n+\t\t\tLastChainIndexName: lastChainIndexFunc,\n+\t\t},\n+\t)\n+\n+\tgo c.imageController.Run(stopChan)\n+\treturn c\n+}\n+\n+func (c *ImageIndex) WaitForSyncedStores() {\n+\tfor !c.imageController.HasSynced() {\n+\t\ttime.Sleep(100 * time.Millisecond)\n+\t}\n+}\n+\n+// Descendants provides a list of images that the provided image is based on.\n+// Note that if a base image is tagged multiple times, this will return all the matching\n+// images.\n+func (c *ImageIndex) Descendants(image *imageapi.Image) ([]*imageapi.Image, error) {\n+\timageChains := getImageLayerChain(image)\n+\tresult := &uniqueImageList{}\n+\tfor _, chain := range imageChains {\n+\t\tobjects, err := c.imageIndex.ByIndex(LastChainIndexName, chain)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\tfor _, obj := range objects {\n+\t\t\tif i := obj.(*imageapi.Image); i.Name != image.Name {\n+\t\t\t\tresult.Append(i)\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn result.Images, nil\n+}\n+\n+// Ancestors provides a list of images that are based in the provideed image.\n+func (c *ImageIndex) Ancestors(image *imageapi.Image) ([]*imageapi.Image, error) {\n+\tchains := getImageLayerChain(image)\n+\tobjects, err := c.imageIndex.ByIndex(ChainIndexName, chains[len(chains)-1])\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\tresult := &uniqueImageList{}\n+\tfor _, obj := range objects {\n+\t\ti := obj.(*imageapi.Image)\n+\t\timageChain := getImageLayerChain(i)\n+\t\t// Remove the image itself from the result\n+\t\tif imageChain[len(imageChain)-1] != chains[len(chains)-1] {\n+\t\t\tresult.Append(i)\n+\t\t}\n+\t}\n+\treturn result.Images, nil\n+}\n+\n+func (c *ImageIndex) addImage(obj interface{}) {\n+\tif err := c.imageIndex.Add(obj); err != nil {\n+\t\tglog.Errorf(\"failed to add image %+v to index: %v\", obj, err)\n+\t}\n+}\n+\n+func (c *ImageIndex) updateImage(_, newObj interface{}) {\n+\tif err := c.imageIndex.Update(newObj); err != nil {\n+\t\tglog.Errorf(\"failed to update image %+v in index: %v\", newObj, err)\n+\t}\n+}\n+\n+func (c *ImageIndex) deleteImage(obj interface{}) {\n+\ti, ok := obj.(*imageapi.Image)\n+\tif !ok {\n+\t\ttombstone, ok := obj.(cache.DeletedFinalStateUnknown)\n+\t\tif !ok {\n+\t\t\tglog.Errorf(\"couldn't get object from tombstone %#v\", obj)\n+\t\t\treturn\n+\t\t}\n+\t\ti, ok = tombstone.Obj.(*imageapi.Image)\n+\t\tif !ok {\n+\t\t\tglog.Errorf(\"tombstone contained object that is not a Deployment %#v\", obj)\n+\t\t\treturn\n+\t\t}\n+\t}\n+\tc.imageIndex.Delete(i)\n+}\n+\n+type uniqueImageList struct {\n+\tImages []*imageapi.Image\n+\tindex  map[string]struct{}\n+}\n+\n+func (l *uniqueImageList) Append(image *imageapi.Image) {\n+\tif l.index == nil {\n+\t\tl.index = map[string]struct{}{}\n+\t}\n+\tif _, exists := l.index[image.Name]; exists {\n+\t\treturn\n+\t}\n+\tl.Images = append(l.Images, image)\n+\tl.index[image.Name] = struct{}{}\n+}\n+\n+// getImageLayerChain recalculates the digest from each layer and return a sequence that\n+// represents the image.\n+func getImageLayerChain(image *imageapi.Image) []string {\n+\tchain := []string{}\n+\tfor i, layer := range image.DockerImageLayers {","path":"pkg/image/index/index.go","position":158,"original_position":158,"commit_id":"565c7420207157d9fd514de8d884da1531086b55","original_commit_id":"fb44e9c103dc62719ea47594e93c299b2c02ef82","user":{"login":"miminar","id":1478803,"avatar_url":"https://avatars.githubusercontent.com/u/1478803?v=3","gravatar_id":"","url":"https://api.github.com/users/miminar","html_url":"https://github.com/miminar","followers_url":"https://api.github.com/users/miminar/followers","following_url":"https://api.github.com/users/miminar/following{/other_user}","gists_url":"https://api.github.com/users/miminar/gists{/gist_id}","starred_url":"https://api.github.com/users/miminar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/miminar/subscriptions","organizations_url":"https://api.github.com/users/miminar/orgs","repos_url":"https://api.github.com/users/miminar/repos","events_url":"https://api.github.com/users/miminar/events{/privacy}","received_events_url":"https://api.github.com/users/miminar/received_events","type":"User","site_admin":false},"body":"A conclusion from private discussion is that empty layers will be omitted because they don't serve any purpose and have been removed from manifest schema v2.","created_at":"2016-11-29T10:02:00Z","updated_at":"2016-11-29T10:02:00Z","html_url":"https://github.com/openshift/origin/pull/12032#discussion_r89975778","pull_request_url":"https://api.github.com/repos/openshift/origin/pulls/12032","_links":{"self":{"href":"https://api.github.com/repos/openshift/origin/pulls/comments/89975778"},"html":{"href":"https://github.com/openshift/origin/pull/12032#discussion_r89975778"},"pull_request":{"href":"https://api.github.com/repos/openshift/origin/pulls/12032"}}},"pull_request":{"url":"https://api.github.com/repos/openshift/origin/pulls/12032","id":95348331,"html_url":"https://github.com/openshift/origin/pull/12032","diff_url":"https://github.com/openshift/origin/pull/12032.diff","patch_url":"https://github.com/openshift/origin/pull/12032.patch","issue_url":"https://api.github.com/repos/openshift/origin/issues/12032","number":12032,"state":"open","locked":false,"title":"WIP: [DO_NOT_MERGE]: Image Index","user":{"login":"mfojtik","id":44136,"avatar_url":"https://avatars.githubusercontent.com/u/44136?v=3","gravatar_id":"","url":"https://api.github.com/users/mfojtik","html_url":"https://github.com/mfojtik","followers_url":"https://api.github.com/users/mfojtik/followers","following_url":"https://api.github.com/users/mfojtik/following{/other_user}","gists_url":"https://api.github.com/users/mfojtik/gists{/gist_id}","starred_url":"https://api.github.com/users/mfojtik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mfojtik/subscriptions","organizations_url":"https://api.github.com/users/mfojtik/orgs","repos_url":"https://api.github.com/users/mfojtik/repos","events_url":"https://api.github.com/users/mfojtik/events{/privacy}","received_events_url":"https://api.github.com/users/mfojtik/received_events","type":"User","site_admin":false},"body":"This code provides a fast access index for listing image ancestors and descentants.\r\n@legionus will provide util package to access the image \"chains\" with explanation how they works.\r\n\r\nCaveats:\r\n\r\n* Since a single image can be tagged multiple times, but having the same layers it means ancestors and descentants might return all these images. \r\n\r\n","created_at":"2016-11-25T21:22:07Z","updated_at":"2016-11-29T10:02:00Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ef6e57a552456266cd24eb5afc41a262852a156a","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/openshift/origin/pulls/12032/commits","review_comments_url":"https://api.github.com/repos/openshift/origin/pulls/12032/comments","review_comment_url":"https://api.github.com/repos/openshift/origin/pulls/comments{/number}","comments_url":"https://api.github.com/repos/openshift/origin/issues/12032/comments","statuses_url":"https://api.github.com/repos/openshift/origin/statuses/565c7420207157d9fd514de8d884da1531086b55","head":{"label":"mfojtik:image-index","ref":"image-index","sha":"565c7420207157d9fd514de8d884da1531086b55","user":{"login":"mfojtik","id":44136,"avatar_url":"https://avatars.githubusercontent.com/u/44136?v=3","gravatar_id":"","url":"https://api.github.com/users/mfojtik","html_url":"https://github.com/mfojtik","followers_url":"https://api.github.com/users/mfojtik/followers","following_url":"https://api.github.com/users/mfojtik/following{/other_user}","gists_url":"https://api.github.com/users/mfojtik/gists{/gist_id}","starred_url":"https://api.github.com/users/mfojtik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mfojtik/subscriptions","organizations_url":"https://api.github.com/users/mfojtik/orgs","repos_url":"https://api.github.com/users/mfojtik/repos","events_url":"https://api.github.com/users/mfojtik/events{/privacy}","received_events_url":"https://api.github.com/users/mfojtik/received_events","type":"User","site_admin":false},"repo":{"id":22954249,"name":"origin","full_name":"mfojtik/origin","owner":{"login":"mfojtik","id":44136,"avatar_url":"https://avatars.githubusercontent.com/u/44136?v=3","gravatar_id":"","url":"https://api.github.com/users/mfojtik","html_url":"https://github.com/mfojtik","followers_url":"https://api.github.com/users/mfojtik/followers","following_url":"https://api.github.com/users/mfojtik/following{/other_user}","gists_url":"https://api.github.com/users/mfojtik/gists{/gist_id}","starred_url":"https://api.github.com/users/mfojtik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mfojtik/subscriptions","organizations_url":"https://api.github.com/users/mfojtik/orgs","repos_url":"https://api.github.com/users/mfojtik/repos","events_url":"https://api.github.com/users/mfojtik/events{/privacy}","received_events_url":"https://api.github.com/users/mfojtik/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mfojtik/origin","description":"OpenShift 3 - build, deploy, and manage your applications with Docker and Kubernetes","fork":true,"url":"https://api.github.com/repos/mfojtik/origin","forks_url":"https://api.github.com/repos/mfojtik/origin/forks","keys_url":"https://api.github.com/repos/mfojtik/origin/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mfojtik/origin/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mfojtik/origin/teams","hooks_url":"https://api.github.com/repos/mfojtik/origin/hooks","issue_events_url":"https://api.github.com/repos/mfojtik/origin/issues/events{/number}","events_url":"https://api.github.com/repos/mfojtik/origin/events","assignees_url":"https://api.github.com/repos/mfojtik/origin/assignees{/user}","branches_url":"https://api.github.com/repos/mfojtik/origin/branches{/branch}","tags_url":"https://api.github.com/repos/mfojtik/origin/tags","blobs_url":"https://api.github.com/repos/mfojtik/origin/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mfojtik/origin/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mfojtik/origin/git/refs{/sha}","trees_url":"https://api.github.com/repos/mfojtik/origin/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mfojtik/origin/statuses/{sha}","languages_url":"https://api.github.com/repos/mfojtik/origin/languages","stargazers_url":"https://api.github.com/repos/mfojtik/origin/stargazers","contributors_url":"https://api.github.com/repos/mfojtik/origin/contributors","subscribers_url":"https://api.github.com/repos/mfojtik/origin/subscribers","subscription_url":"https://api.github.com/repos/mfojtik/origin/subscription","commits_url":"https://api.github.com/repos/mfojtik/origin/commits{/sha}","git_commits_url":"https://api.github.com/repos/mfojtik/origin/git/commits{/sha}","comments_url":"https://api.github.com/repos/mfojtik/origin/comments{/number}","issue_comment_url":"https://api.github.com/repos/mfojtik/origin/issues/comments{/number}","contents_url":"https://api.github.com/repos/mfojtik/origin/contents/{+path}","compare_url":"https://api.github.com/repos/mfojtik/origin/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mfojtik/origin/merges","archive_url":"https://api.github.com/repos/mfojtik/origin/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mfojtik/origin/downloads","issues_url":"https://api.github.com/repos/mfojtik/origin/issues{/number}","pulls_url":"https://api.github.com/repos/mfojtik/origin/pulls{/number}","milestones_url":"https://api.github.com/repos/mfojtik/origin/milestones{/number}","notifications_url":"https://api.github.com/repos/mfojtik/origin/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mfojtik/origin/labels{/name}","releases_url":"https://api.github.com/repos/mfojtik/origin/releases{/id}","deployments_url":"https://api.github.com/repos/mfojtik/origin/deployments","created_at":"2014-08-14T13:11:47Z","updated_at":"2016-01-12T09:38:06Z","pushed_at":"2016-11-29T09:34:50Z","git_url":"git://github.com/mfojtik/origin.git","ssh_url":"git@github.com:mfojtik/origin.git","clone_url":"https://github.com/mfojtik/origin.git","svn_url":"https://github.com/mfojtik/origin","homepage":"https://openshift.github.io","size":386132,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"openshift:master","ref":"master","sha":"8a5025001e1cb4cd63b0ce9119b6e966d85f88ce","user":{"login":"openshift","id":792337,"avatar_url":"https://avatars.githubusercontent.com/u/792337?v=3","gravatar_id":"","url":"https://api.github.com/users/openshift","html_url":"https://github.com/openshift","followers_url":"https://api.github.com/users/openshift/followers","following_url":"https://api.github.com/users/openshift/following{/other_user}","gists_url":"https://api.github.com/users/openshift/gists{/gist_id}","starred_url":"https://api.github.com/users/openshift/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openshift/subscriptions","organizations_url":"https://api.github.com/users/openshift/orgs","repos_url":"https://api.github.com/users/openshift/repos","events_url":"https://api.github.com/users/openshift/events{/privacy}","received_events_url":"https://api.github.com/users/openshift/received_events","type":"Organization","site_admin":false},"repo":{"id":22442668,"name":"origin","full_name":"openshift/origin","owner":{"login":"openshift","id":792337,"avatar_url":"https://avatars.githubusercontent.com/u/792337?v=3","gravatar_id":"","url":"https://api.github.com/users/openshift","html_url":"https://github.com/openshift","followers_url":"https://api.github.com/users/openshift/followers","following_url":"https://api.github.com/users/openshift/following{/other_user}","gists_url":"https://api.github.com/users/openshift/gists{/gist_id}","starred_url":"https://api.github.com/users/openshift/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openshift/subscriptions","organizations_url":"https://api.github.com/users/openshift/orgs","repos_url":"https://api.github.com/users/openshift/repos","events_url":"https://api.github.com/users/openshift/events{/privacy}","received_events_url":"https://api.github.com/users/openshift/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/openshift/origin","description":"Enterprise Kubernetes for Developers","fork":false,"url":"https://api.github.com/repos/openshift/origin","forks_url":"https://api.github.com/repos/openshift/origin/forks","keys_url":"https://api.github.com/repos/openshift/origin/keys{/key_id}","collaborators_url":"https://api.github.com/repos/openshift/origin/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/openshift/origin/teams","hooks_url":"https://api.github.com/repos/openshift/origin/hooks","issue_events_url":"https://api.github.com/repos/openshift/origin/issues/events{/number}","events_url":"https://api.github.com/repos/openshift/origin/events","assignees_url":"https://api.github.com/repos/openshift/origin/assignees{/user}","branches_url":"https://api.github.com/repos/openshift/origin/branches{/branch}","tags_url":"https://api.github.com/repos/openshift/origin/tags","blobs_url":"https://api.github.com/repos/openshift/origin/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/openshift/origin/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/openshift/origin/git/refs{/sha}","trees_url":"https://api.github.com/repos/openshift/origin/git/trees{/sha}","statuses_url":"https://api.github.com/repos/openshift/origin/statuses/{sha}","languages_url":"https://api.github.com/repos/openshift/origin/languages","stargazers_url":"https://api.github.com/repos/openshift/origin/stargazers","contributors_url":"https://api.github.com/repos/openshift/origin/contributors","subscribers_url":"https://api.github.com/repos/openshift/origin/subscribers","subscription_url":"https://api.github.com/repos/openshift/origin/subscription","commits_url":"https://api.github.com/repos/openshift/origin/commits{/sha}","git_commits_url":"https://api.github.com/repos/openshift/origin/git/commits{/sha}","comments_url":"https://api.github.com/repos/openshift/origin/comments{/number}","issue_comment_url":"https://api.github.com/repos/openshift/origin/issues/comments{/number}","contents_url":"https://api.github.com/repos/openshift/origin/contents/{+path}","compare_url":"https://api.github.com/repos/openshift/origin/compare/{base}...{head}","merges_url":"https://api.github.com/repos/openshift/origin/merges","archive_url":"https://api.github.com/repos/openshift/origin/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/openshift/origin/downloads","issues_url":"https://api.github.com/repos/openshift/origin/issues{/number}","pulls_url":"https://api.github.com/repos/openshift/origin/pulls{/number}","milestones_url":"https://api.github.com/repos/openshift/origin/milestones{/number}","notifications_url":"https://api.github.com/repos/openshift/origin/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/openshift/origin/labels{/name}","releases_url":"https://api.github.com/repos/openshift/origin/releases{/id}","deployments_url":"https://api.github.com/repos/openshift/origin/deployments","created_at":"2014-07-30T20:04:05Z","updated_at":"2016-11-29T08:21:54Z","pushed_at":"2016-11-29T09:34:51Z","git_url":"git://github.com/openshift/origin.git","ssh_url":"git@github.com:openshift/origin.git","clone_url":"https://github.com/openshift/origin.git","svn_url":"https://github.com/openshift/origin","homepage":"http://www.openshift.org","size":645053,"stargazers_count":2076,"watchers_count":2076,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1381,"mirror_url":null,"open_issues_count":1163,"forks":1381,"open_issues":1163,"watchers":2076,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/openshift/origin/pulls/12032"},"html":{"href":"https://github.com/openshift/origin/pull/12032"},"issue":{"href":"https://api.github.com/repos/openshift/origin/issues/12032"},"comments":{"href":"https://api.github.com/repos/openshift/origin/issues/12032/comments"},"review_comments":{"href":"https://api.github.com/repos/openshift/origin/pulls/12032/comments"},"review_comment":{"href":"https://api.github.com/repos/openshift/origin/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/openshift/origin/pulls/12032/commits"},"statuses":{"href":"https://api.github.com/repos/openshift/origin/statuses/565c7420207157d9fd514de8d884da1531086b55"}}}}
{"actor":{"display_login":"miminar"}}