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

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

Issue 2992693002: isolate: add uploadtracker tests for symlinks and isolated files (Closed)
Patch Set: address review comments Created 3 years, 4 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 | « client/cmd/isolate/checker.go ('k') | client/cmd/isolate/upload_tracker_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 2017 The LUCI Authors. 1 // Copyright 2017 The LUCI Authors.
2 // 2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License. 4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at 5 // You may obtain a copy of the License at
6 // 6 //
7 // http://www.apache.org/licenses/LICENSE-2.0 7 // http://www.apache.org/licenses/LICENSE-2.0
8 // 8 //
9 // Unless required by applicable law or agreed to in writing, software 9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, 10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 package main 15 package main
16 16
17 import ( 17 import (
18 "bytes"
18 "encoding/json" 19 "encoding/json"
19 "fmt" 20 "fmt"
20 » "io/ioutil" 21 » "io"
21 "log" 22 "log"
22 "os" 23 "os"
23 "path/filepath" 24 "path/filepath"
24 "strings" 25 "strings"
25 26
26 humanize "github.com/dustin/go-humanize" 27 humanize "github.com/dustin/go-humanize"
27 "github.com/luci/luci-go/common/isolated" 28 "github.com/luci/luci-go/common/isolated"
28 "github.com/luci/luci-go/common/isolatedclient" 29 "github.com/luci/luci-go/common/isolatedclient"
29 ) 30 )
30 31
32 // limitedOS contains a subset of the functions from the os package.
33 type limitedOS interface {
34 Readlink(string) (string, error)
35 openFiler
36 }
37
38 type openFiler interface {
39 // OpenFile is like os.OpenFile, but returns an io.WriteCloser since
40 // that's all we need and it's easier to implment with a fake.
41 OpenFile(string, int, os.FileMode) (io.WriteCloser, error)
42 }
43
44 // standardOS implements limitedOS by delegating to the standard library's os pa ckage.
45 type standardOS struct{}
46
47 func (sos standardOS) Readlink(name string) (string, error) {
48 return os.Readlink(name)
49 }
50
51 func (sos standardOS) OpenFile(name string, flag int, perm os.FileMode) (io.Writ eCloser, error) {
52 return os.OpenFile(name, flag, perm)
53 }
54
31 // UploadTracker uploads and keeps track of files. 55 // UploadTracker uploads and keeps track of files.
32 type UploadTracker struct { 56 type UploadTracker struct {
33 » checker *Checker 57 » checker Checker
34 » uploader *Uploader 58 » uploader Uploader
35 isol *isolated.Isolated 59 isol *isolated.Isolated
60
61 // Override for testing.
62 lOS limitedOS
36 } 63 }
37 64
38 // NewUploadTracker constructs an UploadTracker. It tracks uploaded files in is ol.Files. 65 // NewUploadTracker constructs an UploadTracker. It tracks uploaded files in is ol.Files.
39 func NewUploadTracker(checker *Checker, uploader *Uploader, isol *isolated.Isola ted) *UploadTracker { 66 func NewUploadTracker(checker Checker, uploader Uploader, isol *isolated.Isolate d) *UploadTracker {
40 // TODO: share a Checker and Uploader with other UploadTrackers 67 // TODO: share a Checker and Uploader with other UploadTrackers
41 // when batch uploading. 68 // when batch uploading.
42 isol.Files = make(map[string]isolated.File) 69 isol.Files = make(map[string]isolated.File)
43 return &UploadTracker{ 70 return &UploadTracker{
44 checker: checker, 71 checker: checker,
45 uploader: uploader, 72 uploader: uploader,
46 isol: isol, 73 isol: isol,
74 lOS: standardOS{},
47 } 75 }
48 } 76 }
49 77
50 // UploadDeps uploads all of the items in parts. 78 // UploadDeps uploads all of the items in parts.
51 func (ut *UploadTracker) UploadDeps(parts partitionedDeps) error { 79 func (ut *UploadTracker) UploadDeps(parts partitionedDeps) error {
52 if err := ut.populateSymlinks(parts.links.items); err != nil { 80 if err := ut.populateSymlinks(parts.links.items); err != nil {
53 return err 81 return err
54 } 82 }
55 83
56 if err := ut.tarAndUploadFiles(parts.filesToArchive.items); err != nil { 84 if err := ut.tarAndUploadFiles(parts.filesToArchive.items); err != nil {
57 return err 85 return err
58 } 86 }
59 87
60 if err := ut.uploadFiles(parts.indivFiles.items); err != nil { 88 if err := ut.uploadFiles(parts.indivFiles.items); err != nil {
61 return err 89 return err
62 } 90 }
63 return nil 91 return nil
64 } 92 }
65 93
66 // Files returns the files which have been uploaded. 94 // Files returns the files which have been uploaded.
67 // Note: files may not have completed uploading until the tracker's Checker and 95 // Note: files may not have completed uploading until the tracker's Checker and
68 // Uploader have been closed. 96 // Uploader have been closed.
69 func (ut *UploadTracker) Files() map[string]isolated.File { 97 func (ut *UploadTracker) Files() map[string]isolated.File {
70 return ut.isol.Files 98 return ut.isol.Files
71 } 99 }
72 100
73 // populateSymlinks adds an isolated.File to files for each provided symlink 101 // populateSymlinks adds an isolated.File to files for each provided symlink
74 func (ut *UploadTracker) populateSymlinks(symlinks []*Item) error { 102 func (ut *UploadTracker) populateSymlinks(symlinks []*Item) error {
75 for _, item := range symlinks { 103 for _, item := range symlinks {
76 » » l, err := os.Readlink(item.Path) 104 » » l, err := ut.lOS.Readlink(item.Path)
77 if err != nil { 105 if err != nil {
78 return fmt.Errorf("unable to resolve symlink for %q: %v" , item.Path, err) 106 return fmt.Errorf("unable to resolve symlink for %q: %v" , item.Path, err)
79 } 107 }
80 ut.isol.Files[item.RelPath] = isolated.SymLink(l) 108 ut.isol.Files[item.RelPath] = isolated.SymLink(l)
81 } 109 }
82 return nil 110 return nil
83 } 111 }
84 112
85 // tarAndUploadFiles creates bundles of files, uploads them, and adds each bundl e to files. 113 // tarAndUploadFiles creates bundles of files, uploads them, and adds each bundl e to files.
86 func (ut *UploadTracker) tarAndUploadFiles(smallFiles []*Item) error { 114 func (ut *UploadTracker) tarAndUploadFiles(smallFiles []*Item) error {
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 if err := ut.checker.Close(); err != nil { 201 if err := ut.checker.Close(); err != nil {
174 return IsolatedSummary{}, err 202 return IsolatedSummary{}, err
175 } 203 }
176 204
177 // Make sure that all the uploads have completed successfully. 205 // Make sure that all the uploads have completed successfully.
178 if err := ut.uploader.Close(); err != nil { 206 if err := ut.uploader.Close(); err != nil {
179 return IsolatedSummary{}, err 207 return IsolatedSummary{}, err
180 } 208 }
181 209
182 // Write the isolated file... 210 // Write the isolated file...
183 » if err := isolFile.writeJSONFile(); err != nil { 211 » if err := isolFile.writeJSONFile(ut.lOS); err != nil {
184 return IsolatedSummary{}, err 212 return IsolatedSummary{}, err
185 } 213 }
186 214
187 return IsolatedSummary{ 215 return IsolatedSummary{
188 Name: isolFile.name(), 216 Name: isolFile.name(),
189 Digest: isolFile.item().Digest, 217 Digest: isolFile.item().Digest,
190 }, nil 218 }, nil
191 } 219 }
192 220
193 // isolatedFile is an isolated file which is stored in memory. 221 // isolatedFile is an isolated file which is stored in memory.
(...skipping 20 matching lines...) Expand all
214 Digest: isolated.HashBytes(ij.json), 242 Digest: isolated.HashBytes(ij.json),
215 Size: int64(len(ij.json)), 243 Size: int64(len(ij.json)),
216 } 244 }
217 } 245 }
218 246
219 func (ij *isolatedFile) contents() []byte { 247 func (ij *isolatedFile) contents() []byte {
220 return ij.json 248 return ij.json
221 } 249 }
222 250
223 // writeJSONFile writes the file contents to the filesystem. 251 // writeJSONFile writes the file contents to the filesystem.
224 func (ij *isolatedFile) writeJSONFile() error { 252 func (ij *isolatedFile) writeJSONFile(opener openFiler) error {
225 » return ioutil.WriteFile(ij.path, ij.json, 0644) 253 » f, err := opener.OpenFile(ij.path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 064 4)
254 » if err != nil {
255 » » return err
256 » }
257 » _, err = io.Copy(f, bytes.NewBuffer(ij.json))
258 » err2 := f.Close()
259 » if err != nil {
260 » » return err
261 » }
262 » return err2
226 } 263 }
227 264
228 // name returns the base name of the isolated file, extension stripped. 265 // name returns the base name of the isolated file, extension stripped.
229 func (ij *isolatedFile) name() string { 266 func (ij *isolatedFile) name() string {
230 name := filepath.Base(ij.path) 267 name := filepath.Base(ij.path)
231 if i := strings.LastIndex(name, "."); i != -1 { 268 if i := strings.LastIndex(name, "."); i != -1 {
232 name = name[:i] 269 name = name[:i]
233 } 270 }
234 return name 271 return name
235 } 272 }
OLDNEW
« no previous file with comments | « client/cmd/isolate/checker.go ('k') | client/cmd/isolate/upload_tracker_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698