{"action":"created","comment":{"url":"https://api.github.com/repos/coreos/rocket/pulls/comments/26763880","id":26763880,"diff_hunk":"@@ -0,0 +1,339 @@\n+package cas\n+\n+import (\n+\t\"archive/tar\"\n+\t\"crypto/sha512\"\n+\t\"encoding/json\"\n+\t\"fmt\"\n+\t\"io\"\n+\t\"io/ioutil\"\n+\t\"os\"\n+\t\"path/filepath\"\n+\t\"sort\"\n+\n+\tspecaci \"github.com/coreos/rocket/Godeps/_workspace/src/github.com/appc/spec/aci\"\n+\t\"github.com/coreos/rocket/Godeps/_workspace/src/github.com/appc/spec/pkg/tarheader\"\n+\t\"github.com/coreos/rocket/Godeps/_workspace/src/github.com/appc/spec/schema/types\"\n+\t\"github.com/coreos/rocket/pkg/aci\"\n+)\n+\n+const (\n+\thashfilename     = \"hash\"\n+\trenderedfilename = \"rendered\"\n+)\n+\n+// TreeStore represents a store of rendered ACIs\n+// The image's key becomes the name of the directory containing the rendered aci.\n+type TreeStore struct {\n+\tpath string\n+}\n+\n+// Write renders the ACI with the provided key in the treestore\n+// Write, to avoid having a rendered ACI with old stale files, requires that\n+// the destination directory doesn't exist (usually Remove should be called\n+// before Write)\n+func (ts *TreeStore) Write(key string, ds *Store) error {\n+\ttreepath := filepath.Join(ts.path, key)\n+\tfi, _ := os.Stat(treepath)\n+\tif fi != nil {\n+\t\treturn fmt.Errorf(\"treestore: path %s already exists\", treepath)\n+\t}\n+\timageID, err := types.NewHash(key)\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"treestore: cannot convert key to imageID: %v\", err)\n+\t}\n+\terr = aci.RenderACIWithImageID(*imageID, treepath, ds)\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"treestore: cannot render aci: %v\", err)\n+\t}\n+\thash, err := ts.Hash(key)\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"treestore: cannot calculate tree hash: %v\", err)\n+\t}\n+\terr = ioutil.WriteFile(filepath.Join(treepath, hashfilename), []byte(hash), 0644)\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"treestore: cannot write hash file: %v\", err)\n+\t}\n+\t// before creating the \"rendered\" flag file we need to ensure that all data is fsynced\n+\terr = filepath.Walk(treepath, func(path string, info os.FileInfo, err error) error {\n+\t\tif err != nil {\n+\t\t\treturn err\n+\t\t}\n+\t\tif !info.Mode().IsDir() && !info.Mode().IsRegular() {\n+\t\t\treturn nil\n+\t\t}\n+\t\tf, err := os.Open(path)\n+\t\tif err != nil {\n+\t\t\treturn err\n+\t\t}\n+\t\tdefer f.Close()\n+\t\terr = f.Sync()\n+\t\tif err != nil {\n+\t\t\treturn err\n+\t\t}\n+\t\treturn nil\n+\t})\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"treestore: failed to sync data: %v\", err)\n+\t}\n+\t// Create rendered file\n+\tf, err := os.Create(filepath.Join(treepath, renderedfilename))\n+\tif err != nil {\n+\t\treturn fmt.Errorf(\"treestore: cannot write rendered file: %v\", err)\n+\t}\n+\tdefer f.Close()\n+\terr = f.Sync()","path":"cas/tree.go","position":85,"original_position":85,"commit_id":"a864472c15ec677679e2f1f5501bc5d62b3ee2e2","original_commit_id":"a864472c15ec677679e2f1f5501bc5d62b3ee2e2","user":{"login":"iaguis","id":507354,"avatar_url":"https://avatars.githubusercontent.com/u/507354?v=3","gravatar_id":"","url":"https://api.github.com/users/iaguis","html_url":"https://github.com/iaguis","followers_url":"https://api.github.com/users/iaguis/followers","following_url":"https://api.github.com/users/iaguis/following{/other_user}","gists_url":"https://api.github.com/users/iaguis/gists{/gist_id}","starred_url":"https://api.github.com/users/iaguis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iaguis/subscriptions","organizations_url":"https://api.github.com/users/iaguis/orgs","repos_url":"https://api.github.com/users/iaguis/repos","events_url":"https://api.github.com/users/iaguis/events{/privacy}","received_events_url":"https://api.github.com/users/iaguis/received_events","type":"User","site_admin":false},"body":"We should sync the parent directory, not an empty file","created_at":"2015-03-19T15:20:07Z","updated_at":"2015-03-19T15:20:07Z","html_url":"https://github.com/coreos/rocket/pull/332#discussion_r26763880","pull_request_url":"https://api.github.com/repos/coreos/rocket/pulls/332","_links":{"self":{"href":"https://api.github.com/repos/coreos/rocket/pulls/comments/26763880"},"html":{"href":"https://github.com/coreos/rocket/pull/332#discussion_r26763880"},"pull_request":{"href":"https://api.github.com/repos/coreos/rocket/pulls/332"}}},"pull_request":{"url":"https://api.github.com/repos/coreos/rocket/pulls/332","id":26972374,"html_url":"https://github.com/coreos/rocket/pull/332","diff_url":"https://github.com/coreos/rocket/pull/332.diff","patch_url":"https://github.com/coreos/rocket/pull/332.patch","issue_url":"https://api.github.com/repos/coreos/rocket/issues/332","number":332,"state":"open","locked":false,"title":"cas: add a tree store (#223)","user":{"login":"sgotti","id":1690273,"avatar_url":"https://avatars.githubusercontent.com/u/1690273?v=3","gravatar_id":"","url":"https://api.github.com/users/sgotti","html_url":"https://github.com/sgotti","followers_url":"https://api.github.com/users/sgotti/followers","following_url":"https://api.github.com/users/sgotti/following{/other_user}","gists_url":"https://api.github.com/users/sgotti/gists{/gist_id}","starred_url":"https://api.github.com/users/sgotti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sgotti/subscriptions","organizations_url":"https://api.github.com/users/sgotti/orgs","repos_url":"https://api.github.com/users/sgotti/repos","events_url":"https://api.github.com/users/sgotti/events{/privacy}","received_events_url":"https://api.github.com/users/sgotti/received_events","type":"User","site_admin":false},"body":"The last 4 commits are the relevant ones. The others are from #395 and #533.\r\n\r\nAs explained in #223 this PR adds a tree store.\r\nThe first patch provides the Write, Remove and GetRootFs functions\r\nThe second patch changes cas.WriteACI to call the tree store Write function (not sure if this should be done directly inside WriteACI or outside).\r\n\r\nThe third patch adds validity checking to the rendered ACI. As discussed in #223 using an archive/tar writer doesn't work as it doesn't create reproducible tars due to some maps in pax headers. This implementation uses a special writer the tries to keep a consistent fileinfo struct and write it (by now in json) and the file contents to an hash writer. There are some TODOs that needs some thoughts.\r\n\r\nThanks!","created_at":"2015-01-07T17:21:42Z","updated_at":"2015-03-19T15:20:07Z","closed_at":null,"merged_at":null,"merge_commit_sha":"13a7e2a8e9aa6ccb4d125417bff98af2cca7f02d","assignee":null,"milestone":{"url":"https://api.github.com/repos/coreos/rocket/milestones/6","html_url":"https://github.com/coreos/rocket/milestones/v0.5.0","labels_url":"https://api.github.com/repos/coreos/rocket/milestones/6/labels","id":934109,"number":6,"title":"v0.5.0","description":"This milestone captures stabilisation in the on-disk format of rocket and updates to the latest spec, the most significant change in which is the move to pods.","creator":{"login":"jonboulle","id":2601015,"avatar_url":"https://avatars.githubusercontent.com/u/2601015?v=3","gravatar_id":"","url":"https://api.github.com/users/jonboulle","html_url":"https://github.com/jonboulle","followers_url":"https://api.github.com/users/jonboulle/followers","following_url":"https://api.github.com/users/jonboulle/following{/other_user}","gists_url":"https://api.github.com/users/jonboulle/gists{/gist_id}","starred_url":"https://api.github.com/users/jonboulle/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jonboulle/subscriptions","organizations_url":"https://api.github.com/users/jonboulle/orgs","repos_url":"https://api.github.com/users/jonboulle/repos","events_url":"https://api.github.com/users/jonboulle/events{/privacy}","received_events_url":"https://api.github.com/users/jonboulle/received_events","type":"User","site_admin":false},"open_issues":14,"closed_issues":14,"state":"open","created_at":"2015-01-13T21:11:48Z","updated_at":"2015-03-18T21:06:42Z","due_on":"2015-03-27T07:00:00Z","closed_at":null},"commits_url":"https://api.github.com/repos/coreos/rocket/pulls/332/commits","review_comments_url":"https://api.github.com/repos/coreos/rocket/pulls/332/comments","review_comment_url":"https://api.github.com/repos/coreos/rocket/pulls/comments{/number}","comments_url":"https://api.github.com/repos/coreos/rocket/issues/332/comments","statuses_url":"https://api.github.com/repos/coreos/rocket/statuses/a864472c15ec677679e2f1f5501bc5d62b3ee2e2","head":{"label":"sgotti:treestore","ref":"treestore","sha":"a864472c15ec677679e2f1f5501bc5d62b3ee2e2","user":{"login":"sgotti","id":1690273,"avatar_url":"https://avatars.githubusercontent.com/u/1690273?v=3","gravatar_id":"","url":"https://api.github.com/users/sgotti","html_url":"https://github.com/sgotti","followers_url":"https://api.github.com/users/sgotti/followers","following_url":"https://api.github.com/users/sgotti/following{/other_user}","gists_url":"https://api.github.com/users/sgotti/gists{/gist_id}","starred_url":"https://api.github.com/users/sgotti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sgotti/subscriptions","organizations_url":"https://api.github.com/users/sgotti/orgs","repos_url":"https://api.github.com/users/sgotti/repos","events_url":"https://api.github.com/users/sgotti/events{/privacy}","received_events_url":"https://api.github.com/users/sgotti/received_events","type":"User","site_admin":false},"repo":{"id":27428376,"name":"rocket","full_name":"sgotti/rocket","owner":{"login":"sgotti","id":1690273,"avatar_url":"https://avatars.githubusercontent.com/u/1690273?v=3","gravatar_id":"","url":"https://api.github.com/users/sgotti","html_url":"https://github.com/sgotti","followers_url":"https://api.github.com/users/sgotti/followers","following_url":"https://api.github.com/users/sgotti/following{/other_user}","gists_url":"https://api.github.com/users/sgotti/gists{/gist_id}","starred_url":"https://api.github.com/users/sgotti/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sgotti/subscriptions","organizations_url":"https://api.github.com/users/sgotti/orgs","repos_url":"https://api.github.com/users/sgotti/repos","events_url":"https://api.github.com/users/sgotti/events{/privacy}","received_events_url":"https://api.github.com/users/sgotti/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/sgotti/rocket","description":"App Container runtime","fork":true,"url":"https://api.github.com/repos/sgotti/rocket","forks_url":"https://api.github.com/repos/sgotti/rocket/forks","keys_url":"https://api.github.com/repos/sgotti/rocket/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sgotti/rocket/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sgotti/rocket/teams","hooks_url":"https://api.github.com/repos/sgotti/rocket/hooks","issue_events_url":"https://api.github.com/repos/sgotti/rocket/issues/events{/number}","events_url":"https://api.github.com/repos/sgotti/rocket/events","assignees_url":"https://api.github.com/repos/sgotti/rocket/assignees{/user}","branches_url":"https://api.github.com/repos/sgotti/rocket/branches{/branch}","tags_url":"https://api.github.com/repos/sgotti/rocket/tags","blobs_url":"https://api.github.com/repos/sgotti/rocket/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sgotti/rocket/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sgotti/rocket/git/refs{/sha}","trees_url":"https://api.github.com/repos/sgotti/rocket/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sgotti/rocket/statuses/{sha}","languages_url":"https://api.github.com/repos/sgotti/rocket/languages","stargazers_url":"https://api.github.com/repos/sgotti/rocket/stargazers","contributors_url":"https://api.github.com/repos/sgotti/rocket/contributors","subscribers_url":"https://api.github.com/repos/sgotti/rocket/subscribers","subscription_url":"https://api.github.com/repos/sgotti/rocket/subscription","commits_url":"https://api.github.com/repos/sgotti/rocket/commits{/sha}","git_commits_url":"https://api.github.com/repos/sgotti/rocket/git/commits{/sha}","comments_url":"https://api.github.com/repos/sgotti/rocket/comments{/number}","issue_comment_url":"https://api.github.com/repos/sgotti/rocket/issues/comments{/number}","contents_url":"https://api.github.com/repos/sgotti/rocket/contents/{+path}","compare_url":"https://api.github.com/repos/sgotti/rocket/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sgotti/rocket/merges","archive_url":"https://api.github.com/repos/sgotti/rocket/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sgotti/rocket/downloads","issues_url":"https://api.github.com/repos/sgotti/rocket/issues{/number}","pulls_url":"https://api.github.com/repos/sgotti/rocket/pulls{/number}","milestones_url":"https://api.github.com/repos/sgotti/rocket/milestones{/number}","notifications_url":"https://api.github.com/repos/sgotti/rocket/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sgotti/rocket/labels{/name}","releases_url":"https://api.github.com/repos/sgotti/rocket/releases{/id}","created_at":"2014-12-02T10:56:31Z","updated_at":"2014-12-02T10:56:31Z","pushed_at":"2015-03-17T13:18:12Z","git_url":"git://github.com/sgotti/rocket.git","ssh_url":"git@github.com:sgotti/rocket.git","clone_url":"https://github.com/sgotti/rocket.git","svn_url":"https://github.com/sgotti/rocket","homepage":"","size":5993,"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":"coreos:master","ref":"master","sha":"66f2a64afc12ab8d7d2ac188a9cbe27fd8569805","user":{"login":"coreos","id":3730757,"avatar_url":"https://avatars.githubusercontent.com/u/3730757?v=3","gravatar_id":"","url":"https://api.github.com/users/coreos","html_url":"https://github.com/coreos","followers_url":"https://api.github.com/users/coreos/followers","following_url":"https://api.github.com/users/coreos/following{/other_user}","gists_url":"https://api.github.com/users/coreos/gists{/gist_id}","starred_url":"https://api.github.com/users/coreos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coreos/subscriptions","organizations_url":"https://api.github.com/users/coreos/orgs","repos_url":"https://api.github.com/users/coreos/repos","events_url":"https://api.github.com/users/coreos/events{/privacy}","received_events_url":"https://api.github.com/users/coreos/received_events","type":"Organization","site_admin":false},"repo":{"id":26509369,"name":"rocket","full_name":"coreos/rocket","owner":{"login":"coreos","id":3730757,"avatar_url":"https://avatars.githubusercontent.com/u/3730757?v=3","gravatar_id":"","url":"https://api.github.com/users/coreos","html_url":"https://github.com/coreos","followers_url":"https://api.github.com/users/coreos/followers","following_url":"https://api.github.com/users/coreos/following{/other_user}","gists_url":"https://api.github.com/users/coreos/gists{/gist_id}","starred_url":"https://api.github.com/users/coreos/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coreos/subscriptions","organizations_url":"https://api.github.com/users/coreos/orgs","repos_url":"https://api.github.com/users/coreos/repos","events_url":"https://api.github.com/users/coreos/events{/privacy}","received_events_url":"https://api.github.com/users/coreos/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/coreos/rocket","description":"App Container runtime","fork":false,"url":"https://api.github.com/repos/coreos/rocket","forks_url":"https://api.github.com/repos/coreos/rocket/forks","keys_url":"https://api.github.com/repos/coreos/rocket/keys{/key_id}","collaborators_url":"https://api.github.com/repos/coreos/rocket/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/coreos/rocket/teams","hooks_url":"https://api.github.com/repos/coreos/rocket/hooks","issue_events_url":"https://api.github.com/repos/coreos/rocket/issues/events{/number}","events_url":"https://api.github.com/repos/coreos/rocket/events","assignees_url":"https://api.github.com/repos/coreos/rocket/assignees{/user}","branches_url":"https://api.github.com/repos/coreos/rocket/branches{/branch}","tags_url":"https://api.github.com/repos/coreos/rocket/tags","blobs_url":"https://api.github.com/repos/coreos/rocket/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/coreos/rocket/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/coreos/rocket/git/refs{/sha}","trees_url":"https://api.github.com/repos/coreos/rocket/git/trees{/sha}","statuses_url":"https://api.github.com/repos/coreos/rocket/statuses/{sha}","languages_url":"https://api.github.com/repos/coreos/rocket/languages","stargazers_url":"https://api.github.com/repos/coreos/rocket/stargazers","contributors_url":"https://api.github.com/repos/coreos/rocket/contributors","subscribers_url":"https://api.github.com/repos/coreos/rocket/subscribers","subscription_url":"https://api.github.com/repos/coreos/rocket/subscription","commits_url":"https://api.github.com/repos/coreos/rocket/commits{/sha}","git_commits_url":"https://api.github.com/repos/coreos/rocket/git/commits{/sha}","comments_url":"https://api.github.com/repos/coreos/rocket/comments{/number}","issue_comment_url":"https://api.github.com/repos/coreos/rocket/issues/comments{/number}","contents_url":"https://api.github.com/repos/coreos/rocket/contents/{+path}","compare_url":"https://api.github.com/repos/coreos/rocket/compare/{base}...{head}","merges_url":"https://api.github.com/repos/coreos/rocket/merges","archive_url":"https://api.github.com/repos/coreos/rocket/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/coreos/rocket/downloads","issues_url":"https://api.github.com/repos/coreos/rocket/issues{/number}","pulls_url":"https://api.github.com/repos/coreos/rocket/pulls{/number}","milestones_url":"https://api.github.com/repos/coreos/rocket/milestones{/number}","notifications_url":"https://api.github.com/repos/coreos/rocket/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/coreos/rocket/labels{/name}","releases_url":"https://api.github.com/repos/coreos/rocket/releases{/id}","created_at":"2014-11-11T23:13:18Z","updated_at":"2015-03-19T14:01:11Z","pushed_at":"2015-03-18T21:06:42Z","git_url":"git://github.com/coreos/rocket.git","ssh_url":"git@github.com:coreos/rocket.git","clone_url":"https://github.com/coreos/rocket.git","svn_url":"https://github.com/coreos/rocket","homepage":"","size":11050,"stargazers_count":3631,"watchers_count":3631,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":256,"mirror_url":null,"open_issues_count":83,"forks":256,"open_issues":83,"watchers":3631,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/coreos/rocket/pulls/332"},"html":{"href":"https://github.com/coreos/rocket/pull/332"},"issue":{"href":"https://api.github.com/repos/coreos/rocket/issues/332"},"comments":{"href":"https://api.github.com/repos/coreos/rocket/issues/332/comments"},"review_comments":{"href":"https://api.github.com/repos/coreos/rocket/pulls/332/comments"},"review_comment":{"href":"https://api.github.com/repos/coreos/rocket/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/coreos/rocket/pulls/332/commits"},"statuses":{"href":"https://api.github.com/repos/coreos/rocket/statuses/a864472c15ec677679e2f1f5501bc5d62b3ee2e2"}}}}