Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(68)

Side by Side Diff: client/cmd/isolate/exp_archive.go

Issue 2958913002: isolate: pull exparchive filewalk code into its own func (Closed)
Patch Set: address review comments Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | client/cmd/isolate/exp_archive_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The LUCI Authors. All rights reserved. 1 // Copyright 2015 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 package main 5 package main
6 6
7 import ( 7 import (
8 "encoding/json" 8 "encoding/json"
9 "errors" 9 "errors"
10 "fmt" 10 "fmt"
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
70 // Item represents a file or symlink referenced by an isolate file. 70 // Item represents a file or symlink referenced by an isolate file.
71 type Item struct { 71 type Item struct {
72 Path string 72 Path string
73 RelPath string 73 RelPath string
74 Size int64 74 Size int64
75 Mode os.FileMode 75 Mode os.FileMode
76 76
77 Digest isolated.HexDigest 77 Digest isolated.HexDigest
78 } 78 }
79 79
80 // itemGroup is a list of Items, plus a count of the aggregate size.
81 type itemGroup struct {
82 items []*Item
83 totalSize int64
84 }
85
86 func (ig *itemGroup) AddItem(item *Item) {
87 ig.items = append(ig.items, item)
88 ig.totalSize += item.Size
89 }
90
91 // partitioningWalker contains the state necessary to partition isolate deps by handling multiple os.WalkFunc invocations.
92 type partitioningWalker struct {
93 // rootDir must be initialized before walkFn is called.
94 rootDir string
95
96 parts partitionedDeps
97 }
98
99 // partitionedDeps contains a list of items to be archived, partitioned into sym links and files categorized by size.
100 type partitionedDeps struct {
101 links itemGroup
102 filesToArchive itemGroup
103 indivFiles itemGroup
104 }
105
106 // walkFn implements filepath.WalkFunc, for use traversing a directory hierarchy to be isolated.
107 // It accumulates files in pw.parts, partitioned into symlinks and files categor ized by size.
108 func (pw *partitioningWalker) walkFn(path string, info os.FileInfo, err error) e rror {
109 if err != nil {
110 return err
111 }
112 if info.IsDir() {
113 return nil
114 }
115
116 relPath, err := filepath.Rel(pw.rootDir, path)
117 if err != nil {
118 return err
119 }
120
121 item := &Item{
122 Path: path,
123 RelPath: relPath,
124 Mode: info.Mode(),
125 Size: info.Size(),
126 }
127
128 switch {
129 case item.Mode&os.ModeSymlink == os.ModeSymlink:
130 pw.parts.links.AddItem(item)
131 case item.Size < archiveThreshold:
132 pw.parts.filesToArchive.AddItem(item)
133 default:
134 pw.parts.indivFiles.AddItem(item)
135 }
136 return nil
137 }
138
139 // partitionDeps walks each of the deps, partioning the results into symlinks an d files categorized by size.
140 func partitionDeps(deps []string, rootDir string) (partitionedDeps, error) {
141 walker := partitioningWalker{rootDir: rootDir}
142 for _, dep := range deps {
143 // Try to walk dep. If dep is a file (or symlink), the inner fun ction is called exactly once.
144 if err := filepath.Walk(filepath.Clean(dep), walker.walkFn); err != nil {
145 return partitionedDeps{}, err
146 }
147 }
148 return walker.parts, nil
149 }
150
80 // main contains the core logic for experimental archive. 151 // main contains the core logic for experimental archive.
81 func (c *expArchiveRun) main() error { 152 func (c *expArchiveRun) main() error {
82 // TODO(djd): This func is long and has a lot of internal complexity (li ke, 153 // TODO(djd): This func is long and has a lot of internal complexity (li ke,
83 // such as, archiveCallback). Refactor. 154 // such as, archiveCallback). Refactor.
84 155
85 start := time.Now() 156 start := time.Now()
86 archiveOpts := &c.isolateFlags.ArchiveOptions 157 archiveOpts := &c.isolateFlags.ArchiveOptions
87 // Parse the incoming isolate file. 158 // Parse the incoming isolate file.
88 deps, rootDir, isol, err := isolate.ProcessIsolate(archiveOpts) 159 deps, rootDir, isol, err := isolate.ProcessIsolate(archiveOpts)
89 if err != nil { 160 if err != nil {
(...skipping 12 matching lines...) Expand all
102 } 173 }
103 client := isolatedclient.New(nil, authCl, c.isolatedFlags.ServerURL, c.i solatedFlags.Namespace, nil, nil) 174 client := isolatedclient.New(nil, authCl, c.isolatedFlags.ServerURL, c.i solatedFlags.Namespace, nil, nil)
104 175
105 // Set up a checker and uploader. We limit the uploader to one concurren t 176 // Set up a checker and uploader. We limit the uploader to one concurren t
106 // upload, since the uploads are all coming from disk (with the exceptio n of 177 // upload, since the uploads are all coming from disk (with the exceptio n of
107 // the isolated JSON itself) and we only want a single goroutine reading from 178 // the isolated JSON itself) and we only want a single goroutine reading from
108 // disk at once. 179 // disk at once.
109 checker := NewChecker(ctx, client) 180 checker := NewChecker(ctx, client)
110 uploader := NewUploader(ctx, client, 1) 181 uploader := NewUploader(ctx, client, 1)
111 182
112 » // Walk each of the deps, partioning the results into symlinks and files categorised by size. 183 » parts, err := partitionDeps(deps, rootDir)
113 » var links, archiveFiles, indivFiles []*Item 184 » if err != nil {
114 » var archiveSize, indivSize int64 // Cumulative size of archived/individu al files. 185 » » return fmt.Errorf("partitioning deps: %v", err)
115 » for _, dep := range deps {
116 » » // Try to walk dep. If dep is a file (or symlink), the inner fun ction is called exactly once.
117 » » err := filepath.Walk(filepath.Clean(dep), func(path string, info os.FileInfo, err error) error {
118 » » » if err != nil {
119 » » » » return err
120 » » » }
121 » » » if info.IsDir() {
122 » » » » return nil
123 » » » }
124
125 » » » relPath, err := filepath.Rel(rootDir, path)
126 » » » if err != nil {
127 » » » » return err
128 » » » }
129
130 » » » item := &Item{
131 » » » » Path: path,
132 » » » » RelPath: relPath,
133 » » » » Mode: info.Mode(),
134 » » » » Size: info.Size(),
135 » » » }
136
137 » » » switch {
138 » » » case item.Mode&os.ModeSymlink == os.ModeSymlink:
139 » » » » links = append(links, item)
140 » » » case item.Size < archiveThreshold:
141 » » » » archiveFiles = append(archiveFiles, item)
142 » » » » archiveSize += item.Size
143 » » » default:
144 » » » » indivFiles = append(indivFiles, item)
145 » » » » indivSize += item.Size
146 » » » }
147 » » » return nil
148 » » })
149 » » if err != nil {
150 » » » return err
151 » » }
152 } 186 }
153 187
154 // Construct a map of the files that constitute the isolate. 188 // Construct a map of the files that constitute the isolate.
155 files := make(map[string]isolated.File) 189 files := make(map[string]isolated.File)
156 190
157 » log.Printf("Isolate expanded to %d files (total size %s) and %d symlinks ", len(archiveFiles)+len(indivFiles), humanize.Bytes(uint64(archiveSize+indivSiz e)), len(links)) 191 » numFiles := len(parts.filesToArchive.items) + len(parts.indivFiles.items )
158 » log.Printf("\t%d files (%s) to be isolated individually", len(indivFiles ), humanize.Bytes(uint64(indivSize))) 192 » filesSize := uint64(parts.filesToArchive.totalSize + parts.indivFiles.to talSize)
159 » log.Printf("\t%d files (%s) to be isolated in archives", len(archiveFile s), humanize.Bytes(uint64(archiveSize))) 193 » log.Printf("Isolate expanded to %d files (total size %s) and %d symlinks ", numFiles, humanize.Bytes(filesSize), len(parts.links.items))
194 » log.Printf("\t%d files (%s) to be isolated individually", len(parts.indi vFiles.items), humanize.Bytes(uint64(parts.indivFiles.totalSize)))
195 » log.Printf("\t%d files (%s) to be isolated in archives", len(parts.files ToArchive.items), humanize.Bytes(uint64(parts.filesToArchive.totalSize)))
160 196
161 // Handle the symlinks. 197 // Handle the symlinks.
162 » for _, item := range links { 198 » for _, item := range parts.links.items {
163 l, err := os.Readlink(item.Path) 199 l, err := os.Readlink(item.Path)
164 if err != nil { 200 if err != nil {
165 return fmt.Errorf("unable to resolve symlink for %q: %v" , item.Path, err) 201 return fmt.Errorf("unable to resolve symlink for %q: %v" , item.Path, err)
166 } 202 }
167 files[item.RelPath] = isolated.SymLink(l) 203 files[item.RelPath] = isolated.SymLink(l)
168 } 204 }
169 205
170 // Handle the small to-be-archived files. 206 // Handle the small to-be-archived files.
171 » bundles := ShardItems(archiveFiles, archiveMaxSize) 207 » bundles := ShardItems(parts.filesToArchive.items, archiveMaxSize)
172 log.Printf("\t%d TAR archives to be isolated", len(bundles)) 208 log.Printf("\t%d TAR archives to be isolated", len(bundles))
173 209
174 for _, bundle := range bundles { 210 for _, bundle := range bundles {
175 bundle := bundle 211 bundle := bundle
176 digest, tarSize, err := bundle.Digest() 212 digest, tarSize, err := bundle.Digest()
177 if err != nil { 213 if err != nil {
178 return err 214 return err
179 } 215 }
180 216
181 log.Printf("Created tar archive %q (%s)", digest, humanize.Bytes (uint64(tarSize))) 217 log.Printf("Created tar archive %q (%s)", digest, humanize.Bytes (uint64(tarSize)))
(...skipping 13 matching lines...) Expand all
195 return 231 return
196 } 232 }
197 log.Printf("QUEUED %q for upload", item.RelPath) 233 log.Printf("QUEUED %q for upload", item.RelPath)
198 uploader.Upload(item.RelPath, bundle.Contents, ps, func( ) { 234 uploader.Upload(item.RelPath, bundle.Contents, ps, func( ) {
199 log.Printf("UPLOADED %q", item.RelPath) 235 log.Printf("UPLOADED %q", item.RelPath)
200 }) 236 })
201 }) 237 })
202 } 238 }
203 239
204 // Handle the large individually-uploaded files. 240 // Handle the large individually-uploaded files.
205 » for _, item := range indivFiles { 241 » for _, item := range parts.indivFiles.items {
206 d, err := hashFile(item.Path) 242 d, err := hashFile(item.Path)
207 if err != nil { 243 if err != nil {
208 return err 244 return err
209 } 245 }
210 item.Digest = d 246 item.Digest = d
211 files[item.RelPath] = isolated.BasicFile(item.Digest, int(item.M ode), item.Size) 247 files[item.RelPath] = isolated.BasicFile(item.Digest, int(item.M ode), item.Size)
212 checker.AddItem(item, false, func(item *Item, ps *isolatedclient .PushState) { 248 checker.AddItem(item, false, func(item *Item, ps *isolatedclient .PushState) {
213 if ps == nil { 249 if ps == nil {
214 return 250 return
215 } 251 }
216 log.Printf("QUEUED %q for upload", item.RelPath) 252 log.Printf("QUEUED %q for upload", item.RelPath)
217 uploader.UploadFile(item, ps, func() { 253 uploader.UploadFile(item, ps, func() {
218 log.Printf("UPLOADED %q", item.RelPath) 254 log.Printf("UPLOADED %q", item.RelPath)
219 }) 255 })
220 }) 256 })
221 } 257 }
222 258
223 // Marshal the isolated file into JSON, and create an Item to describe i t. 259 // Marshal the isolated file into JSON, and create an Item to describe i t.
224 isol.Files = files 260 isol.Files = files
225 » isolJSON, err := json.Marshal(isol) 261 » var isolJSON []byte
262 » isolJSON, err = json.Marshal(isol)
226 if err != nil { 263 if err != nil {
227 return err 264 return err
228 } 265 }
229 isolItem := &Item{ 266 isolItem := &Item{
230 Path: archiveOpts.Isolated, 267 Path: archiveOpts.Isolated,
231 RelPath: filepath.Base(archiveOpts.Isolated), 268 RelPath: filepath.Base(archiveOpts.Isolated),
232 Digest: isolated.HashBytes(isolJSON), 269 Digest: isolated.HashBytes(isolJSON),
233 Size: int64(len(isolJSON)), 270 Size: int64(len(isolJSON)),
234 } 271 }
235 272
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
332 } 369 }
333 370
334 func hashFile(path string) (isolated.HexDigest, error) { 371 func hashFile(path string) (isolated.HexDigest, error) {
335 f, err := os.Open(path) 372 f, err := os.Open(path)
336 if err != nil { 373 if err != nil {
337 return "", err 374 return "", err
338 } 375 }
339 defer f.Close() 376 defer f.Close()
340 return isolated.Hash(f) 377 return isolated.Hash(f)
341 } 378 }
OLDNEW
« no previous file with comments | « no previous file | client/cmd/isolate/exp_archive_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698