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

Side by Side Diff: go/src/infra/tools/cipd/local/deployer.go

Issue 1154423015: cipd: Extract high level file system operations into the separate interface. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 6 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 | go/src/infra/tools/cipd/local/deployer_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 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package local 5 package local
6 6
7 import ( 7 import (
8 "crypto/sha1" 8 "crypto/sha1"
9 "encoding/base64" 9 "encoding/base64"
10 "fmt" 10 "fmt"
11 "io/ioutil" 11 "io/ioutil"
12 "os" 12 "os"
13 "path/filepath" 13 "path/filepath"
14 "sort" 14 "sort"
15 "strings" 15 "strings"
16 "sync" 16 "sync"
17 "time"
18 17
19 "github.com/luci/luci-go/common/logging" 18 "github.com/luci/luci-go/common/logging"
20 19
21 "infra/tools/cipd/common" 20 "infra/tools/cipd/common"
22 ) 21 )
23 22
24 // TODO(vadimsh): Make it work on Windows. 23 // TODO(vadimsh): Make it work on Windows.
25 24
26 // TODO(vadimsh): How to handle path conflicts between two packages? Currently 25 // TODO(vadimsh): How to handle path conflicts between two packages? Currently
27 // the last one installed wins. 26 // the last one installed wins.
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
75 err = fmt.Errorf("Site root path is not provided") 74 err = fmt.Errorf("Site root path is not provided")
76 } else { 75 } else {
77 root, err = filepath.Abs(filepath.Clean(root)) 76 root, err = filepath.Abs(filepath.Clean(root))
78 } 77 }
79 if err != nil { 78 if err != nil {
80 return errDeployer{err} 79 return errDeployer{err}
81 } 80 }
82 if logger == nil { 81 if logger == nil {
83 logger = logging.Null() 82 logger = logging.Null()
84 } 83 }
85 » return &deployerImpl{root, logger} 84 » return &deployerImpl{NewFileSystem(root, logger), logger}
86 } 85 }
87 86
88 //////////////////////////////////////////////////////////////////////////////// 87 ////////////////////////////////////////////////////////////////////////////////
89 // Implementation that returns error on all requests. 88 // Implementation that returns error on all requests.
90 89
91 type errDeployer struct{ err error } 90 type errDeployer struct{ err error }
92 91
93 func (d errDeployer) DeployInstance(PackageInstance) (common.Pin, error) { ret urn common.Pin{}, d.err } 92 func (d errDeployer) DeployInstance(PackageInstance) (common.Pin, error) { ret urn common.Pin{}, d.err }
94 func (d errDeployer) CheckDeployed(packageName string) (common.Pin, error) { ret urn common.Pin{}, d.err } 93 func (d errDeployer) CheckDeployed(packageName string) (common.Pin, error) { ret urn common.Pin{}, d.err }
95 func (d errDeployer) FindDeployed() (out []common.Pin, err error) { ret urn nil, d.err } 94 func (d errDeployer) FindDeployed() (out []common.Pin, err error) { ret urn nil, d.err }
96 func (d errDeployer) RemoveDeployed(packageName string) error { ret urn d.err } 95 func (d errDeployer) RemoveDeployed(packageName string) error { ret urn d.err }
97 func (d errDeployer) TempFile(prefix string) (*os.File, error) { ret urn nil, d.err } 96 func (d errDeployer) TempFile(prefix string) (*os.File, error) { ret urn nil, d.err }
98 97
99 //////////////////////////////////////////////////////////////////////////////// 98 ////////////////////////////////////////////////////////////////////////////////
100 // Real deployer implementation. 99 // Real deployer implementation.
101 100
102 // packagesDir is a subdirectory of site root to extract packages to. 101 // packagesDir is a subdirectory of site root to extract packages to.
103 const packagesDir = siteServiceDir + "/pkgs" 102 const packagesDir = siteServiceDir + "/pkgs"
104 103
105 // currentSymlink is a name of a symlink that points to latest deployed version. 104 // currentSymlink is a name of a symlink that points to latest deployed version.
106 const currentSymlink = "_current" 105 const currentSymlink = "_current"
107 106
108 // deployerImpl implements Deployer interface. 107 // deployerImpl implements Deployer interface.
109 type deployerImpl struct { 108 type deployerImpl struct {
110 » root string 109 » fs FileSystem
111 logger logging.Logger 110 logger logging.Logger
112 } 111 }
113 112
114 func (d *deployerImpl) DeployInstance(inst PackageInstance) (common.Pin, error) { 113 func (d *deployerImpl) DeployInstance(inst PackageInstance) (common.Pin, error) {
115 pin := inst.Pin() 114 pin := inst.Pin()
116 » d.logger.Infof("Deploying %s into %s", pin, d.root) 115 » d.logger.Infof("Deploying %s into %s", pin, d.fs.Root())
117 116
118 // Be paranoid. 117 // Be paranoid.
119 if err := common.ValidatePin(pin); err != nil { 118 if err := common.ValidatePin(pin); err != nil {
120 return common.Pin{}, err 119 return common.Pin{}, err
121 } 120 }
122 » if err := d.ensureRootExists(); err != nil { 121 » if _, err := d.fs.EnsureDirectory(d.fs.Root()); err != nil {
123 return common.Pin{}, err 122 return common.Pin{}, err
124 } 123 }
125 124
126 // Remember currently deployed version (to remove it later). Do not frea k out 125 // Remember currently deployed version (to remove it later). Do not frea k out
127 // if it's not there (prevID is "" in that case). 126 // if it's not there (prevID is "" in that case).
128 oldFiles := makeStringSet() 127 oldFiles := makeStringSet()
129 prevID := d.findDeployedInstance(pin.PackageName, oldFiles) 128 prevID := d.findDeployedInstance(pin.PackageName, oldFiles)
130 129
131 // Extract new version to a final destination. 130 // Extract new version to a final destination.
132 newFiles := makeStringSet() 131 newFiles := makeStringSet()
133 destPath, err := d.deployInstance(inst, newFiles) 132 destPath, err := d.deployInstance(inst, newFiles)
134 if err != nil { 133 if err != nil {
135 return common.Pin{}, err 134 return common.Pin{}, err
136 } 135 }
137 136
138 // Switch '_current' symlink to point to a new package instance. It is a 137 // Switch '_current' symlink to point to a new package instance. It is a
139 // point of no return. The function must not fail going forward. 138 // point of no return. The function must not fail going forward.
140 mainSymlinkPath := d.packagePath(pin.PackageName, currentSymlink) 139 mainSymlinkPath := d.packagePath(pin.PackageName, currentSymlink)
141 » err = ensureSymlink(mainSymlinkPath, pin.InstanceID) 140 » err = d.fs.EnsureSymlink(mainSymlinkPath, pin.InstanceID)
142 if err != nil { 141 if err != nil {
143 » » ensureDirectoryGone(destPath, d.logger) 142 » » d.fs.EnsureDirectoryGone(destPath)
144 return common.Pin{}, err 143 return common.Pin{}, err
145 } 144 }
146 145
147 // Asynchronously remove previous version (best effort). 146 // Asynchronously remove previous version (best effort).
148 wg := sync.WaitGroup{} 147 wg := sync.WaitGroup{}
149 defer wg.Wait() 148 defer wg.Wait()
150 if prevID != "" && prevID != pin.InstanceID { 149 if prevID != "" && prevID != pin.InstanceID {
151 wg.Add(1) 150 wg.Add(1)
152 go func() { 151 go func() {
153 defer wg.Done() 152 defer wg.Done()
154 » » » ensureDirectoryGone(d.packagePath(pin.PackageName, prevI D), d.logger) 153 » » » d.fs.EnsureDirectoryGone(d.packagePath(pin.PackageName, prevID))
155 }() 154 }()
156 } 155 }
157 156
158 d.logger.Infof("Adjusting symlinks for %s", pin.PackageName) 157 d.logger.Infof("Adjusting symlinks for %s", pin.PackageName)
159 158
160 // Make symlinks in the site directory for all new files. Reference a pa ckage 159 // Make symlinks in the site directory for all new files. Reference a pa ckage
161 // root via '_current' symlink (instead of direct destPath), to make 160 // root via '_current' symlink (instead of direct destPath), to make
162 // subsequent updates 'more atomic' (since they'll need to switch only 161 // subsequent updates 'more atomic' (since they'll need to switch only
163 // '_current' symlink to update _all_ files in the site root at once). 162 // '_current' symlink to update _all_ files in the site root at once).
164 d.linkFilesToRoot(mainSymlinkPath, newFiles) 163 d.linkFilesToRoot(mainSymlinkPath, newFiles)
165 164
166 // Delete symlinks to files no longer needed i.e. set(old) - set(new). 165 // Delete symlinks to files no longer needed i.e. set(old) - set(new).
167 for relPath := range oldFiles.diff(newFiles) { 166 for relPath := range oldFiles.diff(newFiles) {
168 » » ensureFileGone(filepath.Join(d.root, relPath), d.logger) 167 » » d.fs.EnsureFileGone(filepath.Join(d.fs.Root(), relPath))
169 } 168 }
170 169
171 // Verify it's all right, read the manifest. 170 // Verify it's all right, read the manifest.
172 newPin, err := d.CheckDeployed(pin.PackageName) 171 newPin, err := d.CheckDeployed(pin.PackageName)
173 if err == nil && newPin.InstanceID != pin.InstanceID { 172 if err == nil && newPin.InstanceID != pin.InstanceID {
174 err = fmt.Errorf("Other instance (%s) was deployed concurrently" , newPin.InstanceID) 173 err = fmt.Errorf("Other instance (%s) was deployed concurrently" , newPin.InstanceID)
175 } 174 }
176 if err == nil { 175 if err == nil {
177 d.logger.Infof("Successfully deployed %s", pin) 176 d.logger.Infof("Successfully deployed %s", pin)
178 } else { 177 } else {
179 d.logger.Errorf("Failed to deploy %s: %s", pin, err) 178 d.logger.Errorf("Failed to deploy %s: %s", pin, err)
180 } 179 }
181 return newPin, err 180 return newPin, err
182 } 181 }
183 182
184 func (d *deployerImpl) CheckDeployed(pkg string) (common.Pin, error) { 183 func (d *deployerImpl) CheckDeployed(pkg string) (common.Pin, error) {
185 pin, err := readPackageState(d.packagePath(pkg)) 184 pin, err := readPackageState(d.packagePath(pkg))
186 if err == nil && pin.PackageName != pkg { 185 if err == nil && pin.PackageName != pkg {
187 err = fmt.Errorf("Package path and package name in the manifest do not match") 186 err = fmt.Errorf("Package path and package name in the manifest do not match")
188 } 187 }
189 return pin, err 188 return pin, err
190 } 189 }
191 190
192 func (d *deployerImpl) FindDeployed() (out []common.Pin, err error) { 191 func (d *deployerImpl) FindDeployed() (out []common.Pin, err error) {
193 // Directories with packages are direct children of .cipd/pkgs/. 192 // Directories with packages are direct children of .cipd/pkgs/.
194 » pkgs := filepath.Join(d.root, filepath.FromSlash(packagesDir)) 193 » pkgs := filepath.Join(d.fs.Root(), filepath.FromSlash(packagesDir))
195 infos, err := ioutil.ReadDir(pkgs) 194 infos, err := ioutil.ReadDir(pkgs)
196 if err != nil { 195 if err != nil {
197 if os.IsNotExist(err) { 196 if os.IsNotExist(err) {
198 err = nil 197 err = nil
199 return 198 return
200 } 199 }
201 return 200 return
202 } 201 }
203 202
204 // Read the package name from the package manifest. Skip broken stuff. 203 // Read the package name from the package manifest. Skip broken stuff.
(...skipping 17 matching lines...) Expand all
222 // Sort by package name. 221 // Sort by package name.
223 sort.Strings(keys) 222 sort.Strings(keys)
224 out = make([]common.Pin, len(found)) 223 out = make([]common.Pin, len(found))
225 for i, k := range keys { 224 for i, k := range keys {
226 out[i] = found[k] 225 out[i] = found[k]
227 } 226 }
228 return 227 return
229 } 228 }
230 229
231 func (d *deployerImpl) RemoveDeployed(packageName string) error { 230 func (d *deployerImpl) RemoveDeployed(packageName string) error {
232 » d.logger.Infof("Removing %s from %s", packageName, d.root) 231 » d.logger.Infof("Removing %s from %s", packageName, d.fs.Root())
233 232
234 // Be paranoid. 233 // Be paranoid.
235 err := common.ValidatePackageName(packageName) 234 err := common.ValidatePackageName(packageName)
236 if err != nil { 235 if err != nil {
237 return err 236 return err
238 } 237 }
239 238
240 // Grab list of files in currently deployed package to unlink them from root. 239 // Grab list of files in currently deployed package to unlink them from root.
241 files := makeStringSet() 240 files := makeStringSet()
242 instanceID := d.findDeployedInstance(packageName, files) 241 instanceID := d.findDeployedInstance(packageName, files)
243 242
244 // If was installed, removed symlinks pointing to the package files. 243 // If was installed, removed symlinks pointing to the package files.
245 if instanceID != "" { 244 if instanceID != "" {
246 for relPath := range files { 245 for relPath := range files {
247 » » » ensureFileGone(filepath.Join(d.root, relPath), d.logger) 246 » » » d.fs.EnsureFileGone(filepath.Join(d.fs.Root(), relPath))
248 } 247 }
249 } 248 }
250 249
251 // Ensure all garbage is gone even if instanceID == "" was returned. 250 // Ensure all garbage is gone even if instanceID == "" was returned.
252 » return ensureDirectoryGone(d.packagePath(packageName), d.logger) 251 » return d.fs.EnsureDirectoryGone(d.packagePath(packageName))
253 } 252 }
254 253
255 func (d *deployerImpl) TempFile(prefix string) (*os.File, error) { 254 func (d *deployerImpl) TempFile(prefix string) (*os.File, error) {
256 » if err := d.ensureRootExists(); err != nil { 255 » dir, err := d.fs.EnsureDirectory(filepath.Join(d.fs.Root(), siteServiceD ir, "tmp"))
257 » » return nil, err
258 » }
259 » tempPath := filepath.Join(d.root, siteServiceDir, "tmp")
260 » err := os.MkdirAll(tempPath, 0777)
261 if err != nil { 256 if err != nil {
262 return nil, err 257 return nil, err
263 } 258 }
264 » return ioutil.TempFile(tempPath, prefix) 259 » return ioutil.TempFile(dir, prefix)
265 } 260 }
266 261
267 //////////////////////////////////////////////////////////////////////////////// 262 ////////////////////////////////////////////////////////////////////////////////
268 // Utility methods. 263 // Utility methods.
269 264
270 // ensureRootExists makes site root directory if it is missing.
271 func (d *deployerImpl) ensureRootExists() error {
272 err := os.MkdirAll(d.root, 0777)
273 if err == nil || os.IsExist(err) {
274 return nil
275 }
276 return err
277 }
278
279 // findDeployedInstance returns instanceID of a currently deployed package 265 // findDeployedInstance returns instanceID of a currently deployed package
280 // instance and finds all files in it (adding them to 'files' set). Returns "" 266 // instance and finds all files in it (adding them to 'files' set). Returns ""
281 // if nothing is deployed. File paths in 'files' are relative to package root. 267 // if nothing is deployed. File paths in 'files' are relative to package root.
282 func (d *deployerImpl) findDeployedInstance(pkg string, files stringSet) string { 268 func (d *deployerImpl) findDeployedInstance(pkg string, files stringSet) string {
283 state, err := d.CheckDeployed(pkg) 269 state, err := d.CheckDeployed(pkg)
284 if err != nil { 270 if err != nil {
285 return "" 271 return ""
286 } 272 }
287 scanPackageDir(d.packagePath(pkg, state.InstanceID), files) 273 scanPackageDir(d.packagePath(pkg, state.InstanceID), files)
288 return state.InstanceID 274 return state.InstanceID
289 } 275 }
290 276
291 // deployInstance atomically extracts a package instance to its final 277 // deployInstance atomically extracts a package instance to its final
292 // destination and returns a path to it. It writes a list of extracted files 278 // destination and returns a path to it. It writes a list of extracted files
293 // to 'files'. File paths in 'files' are relative to package root. 279 // to 'files'. File paths in 'files' are relative to package root.
294 func (d *deployerImpl) deployInstance(inst PackageInstance, files stringSet) (st ring, error) { 280 func (d *deployerImpl) deployInstance(inst PackageInstance, files stringSet) (st ring, error) {
295 // Extract new version to a final destination. ExtractPackageInstance kn ows 281 // Extract new version to a final destination. ExtractPackageInstance kn ows
296 // how to build full paths and how to atomically extract a package. No n eed 282 // how to build full paths and how to atomically extract a package. No n eed
297 // to delete garbage if it fails. 283 // to delete garbage if it fails.
298 destPath := d.packagePath(inst.Pin().PackageName, inst.Pin().InstanceID) 284 destPath := d.packagePath(inst.Pin().PackageName, inst.Pin().InstanceID)
299 » err := ExtractInstance(inst, NewFileSystemDestination(destPath)) 285 » err := ExtractInstance(inst, NewFileSystemDestination(destPath, d.fs))
300 if err != nil { 286 if err != nil {
301 return "", err 287 return "", err
302 } 288 }
303 // Enumerate files inside. Nuke it and fail if it's unreadable. 289 // Enumerate files inside. Nuke it and fail if it's unreadable.
304 err = scanPackageDir(d.packagePath(inst.Pin().PackageName, inst.Pin().In stanceID), files) 290 err = scanPackageDir(d.packagePath(inst.Pin().PackageName, inst.Pin().In stanceID), files)
305 if err != nil { 291 if err != nil {
306 » » ensureDirectoryGone(destPath, d.logger) 292 » » d.fs.EnsureDirectoryGone(destPath)
307 return "", err 293 return "", err
308 } 294 }
309 return destPath, err 295 return destPath, err
310 } 296 }
311 297
312 // linkFilesToRoot makes symlinks in root that point to files in packageRoot. 298 // linkFilesToRoot makes symlinks in root that point to files in packageRoot.
313 // All targets are specified by 'files' as paths relative to packageRoot. This 299 // All targets are specified by 'files' as paths relative to packageRoot. This
314 // function is best effort and thus doesn't return errors. 300 // function is best effort and thus doesn't return errors.
315 func (d *deployerImpl) linkFilesToRoot(packageRoot string, files stringSet) { 301 func (d *deployerImpl) linkFilesToRoot(packageRoot string, files stringSet) {
316 for relPath := range files { 302 for relPath := range files {
317 // E.g <root>/bin/tool. 303 // E.g <root>/bin/tool.
318 » » symlinkAbs := filepath.Join(d.root, relPath) 304 » » symlinkAbs := filepath.Join(d.fs.Root(), relPath)
319 // E.g. <root>/.cipd/pkgs/name/_current/bin/tool. 305 // E.g. <root>/.cipd/pkgs/name/_current/bin/tool.
320 targetAbs := filepath.Join(packageRoot, relPath) 306 targetAbs := filepath.Join(packageRoot, relPath)
321 // E.g. ../.cipd/pkgs/name/_current/bin/tool. 307 // E.g. ../.cipd/pkgs/name/_current/bin/tool.
322 targetRel, err := filepath.Rel(filepath.Dir(symlinkAbs), targetA bs) 308 targetRel, err := filepath.Rel(filepath.Dir(symlinkAbs), targetA bs)
323 if err != nil { 309 if err != nil {
324 d.logger.Warningf("Can't get relative path from %s to %s ", filepath.Dir(symlinkAbs), targetAbs) 310 d.logger.Warningf("Can't get relative path from %s to %s ", filepath.Dir(symlinkAbs), targetAbs)
325 continue 311 continue
326 } 312 }
327 » » err = ensureSymlink(symlinkAbs, targetRel) 313 » » err = d.fs.EnsureSymlink(symlinkAbs, targetRel)
328 if err != nil { 314 if err != nil {
329 d.logger.Warningf("Failed to create symlink for %s", rel Path) 315 d.logger.Warningf("Failed to create symlink for %s", rel Path)
330 continue 316 continue
331 } 317 }
332 } 318 }
333 } 319 }
334 320
335 // packagePath joins paths together to return absolute path to .cipd/pkgs sub pa th. 321 // packagePath joins paths together to return absolute path to .cipd/pkgs sub pa th.
336 func (d *deployerImpl) packagePath(pkg string, rest ...string) string { 322 func (d *deployerImpl) packagePath(pkg string, rest ...string) string {
337 » root := filepath.Join(d.root, filepath.FromSlash(packagesDir), packageNa meDigest(pkg)) 323 » root := filepath.Join(d.fs.Root(), filepath.FromSlash(packagesDir), pack ageNameDigest(pkg))
338 result := filepath.Join(append([]string{root}, rest...)...) 324 result := filepath.Join(append([]string{root}, rest...)...)
339 325
340 // Be paranoid and check that everything is inside .cipd directory. 326 // Be paranoid and check that everything is inside .cipd directory.
341 abs, err := filepath.Abs(result) 327 abs, err := filepath.Abs(result)
342 if err != nil { 328 if err != nil {
343 msg := fmt.Sprintf("Can't get absolute path of '%s'", result) 329 msg := fmt.Sprintf("Can't get absolute path of '%s'", result)
344 d.logger.Errorf("%s", msg) 330 d.logger.Errorf("%s", msg)
345 panic(msg) 331 panic(msg)
346 } 332 }
347 if !isSubpath(abs, root) { 333 if !isSubpath(abs, root) {
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 manifest, err := readManifest(r) 390 manifest, err := readManifest(r)
405 if err != nil { 391 if err != nil {
406 return common.Pin{}, err 392 return common.Pin{}, err
407 } 393 }
408 return common.Pin{ 394 return common.Pin{
409 PackageName: manifest.PackageName, 395 PackageName: manifest.PackageName,
410 InstanceID: current, 396 InstanceID: current,
411 }, nil 397 }, nil
412 } 398 }
413 399
414 // ensureSymlink atomically creates a symlink pointing to a target. It will
415 // create full directory path if necessary.
416 func ensureSymlink(symlink string, target string) error {
417 // Already set?
418 existing, err := os.Readlink(symlink)
419 if err != nil && existing == target {
420 return nil
421 }
422
423 // Make sure path exists.
424 err = os.MkdirAll(filepath.Dir(symlink), 0777)
425 if err != nil {
426 return err
427 }
428
429 // Create a new symlink file, can't modify existing one.
430 temp := fmt.Sprintf("%s_%v", symlink, time.Now().UnixNano())
431 err = os.Symlink(target, temp)
432 if err != nil {
433 return err
434 }
435
436 // Atomically replace current symlink with a new one.
437 err = os.Rename(temp, symlink)
438 if err != nil {
439 os.Remove(temp)
440 return err
441 }
442
443 return nil
444 }
445
446 // scanPackageDir finds a set of regular files (and symlinks) in a package 400 // scanPackageDir finds a set of regular files (and symlinks) in a package
447 // instance directory. Adds paths relative to 'dir' to 'out'. Skips package 401 // instance directory. Adds paths relative to 'dir' to 'out'. Skips package
448 // service directories (.cipdpkg and .cipd) since they contain package deployer 402 // service directories (.cipdpkg and .cipd) since they contain package deployer
449 // gut files, not something that needs to be deployed. 403 // gut files, not something that needs to be deployed.
450 func scanPackageDir(dir string, out stringSet) error { 404 func scanPackageDir(dir string, out stringSet) error {
451 return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 405 return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
452 if err != nil { 406 if err != nil {
453 return err 407 return err
454 } 408 }
455 rel, err := filepath.Rel(dir, path) 409 rel, err := filepath.Rel(dir, path)
456 if err != nil { 410 if err != nil {
457 return err 411 return err
458 } 412 }
459 if rel == packageServiceDir || rel == siteServiceDir { 413 if rel == packageServiceDir || rel == siteServiceDir {
460 return filepath.SkipDir 414 return filepath.SkipDir
461 } 415 }
462 if info.Mode().IsRegular() || info.Mode()&os.ModeSymlink != 0 { 416 if info.Mode().IsRegular() || info.Mode()&os.ModeSymlink != 0 {
463 out.add(rel) 417 out.add(rel)
464 } 418 }
465 return nil 419 return nil
466 }) 420 })
467 } 421 }
468 422
469 // ensureDirectoryGone removes the directory as instantly as possible by
470 // renaming it first and only then recursively deleting.
471 func ensureDirectoryGone(path string, logger logging.Logger) error {
472 temp := fmt.Sprintf("%s_%v", path, time.Now().UnixNano())
473 err := os.Rename(path, temp)
474 if err != nil {
475 if !os.IsNotExist(err) {
476 if logger != nil {
477 logger.Warningf("Failed to rename directory %s: %v", path, err)
478 }
479 return err
480 }
481 return nil
482 }
483 err = os.RemoveAll(temp)
484 if err != nil {
485 if logger != nil {
486 logger.Warningf("Failed to remove directory %s: %v", tem p, err)
487 }
488 return err
489 }
490 return nil
491 }
492
493 // ensureFileGone removes file, logging the errors (if any).
494 func ensureFileGone(path string, logger logging.Logger) error {
495 err := os.Remove(path)
496 if err != nil && !os.IsNotExist(err) {
497 if logger != nil {
498 logger.Warningf("Failed to remove %s", path)
499 }
500 return err
501 }
502 return nil
503 }
504
505 //////////////////////////////////////////////////////////////////////////////// 423 ////////////////////////////////////////////////////////////////////////////////
506 // Simple stringSet implementation for keeping a set of filenames. 424 // Simple stringSet implementation for keeping a set of filenames.
507 425
508 type stringSet map[string]struct{} 426 type stringSet map[string]struct{}
509 427
510 func makeStringSet() stringSet { 428 func makeStringSet() stringSet {
511 return make(stringSet) 429 return make(stringSet)
512 } 430 }
513 431
514 // add adds an element to the string set. 432 // add adds an element to the string set.
515 func (a stringSet) add(elem string) { 433 func (a stringSet) add(elem string) {
516 a[elem] = struct{}{} 434 a[elem] = struct{}{}
517 } 435 }
518 436
519 // diff returns set(a) - set(b). 437 // diff returns set(a) - set(b).
520 func (a stringSet) diff(b stringSet) stringSet { 438 func (a stringSet) diff(b stringSet) stringSet {
521 out := makeStringSet() 439 out := makeStringSet()
522 for elem := range a { 440 for elem := range a {
523 if _, ok := b[elem]; !ok { 441 if _, ok := b[elem]; !ok {
524 out.add(elem) 442 out.add(elem)
525 } 443 }
526 } 444 }
527 return out 445 return out
528 } 446 }
OLDNEW
« no previous file with comments | « no previous file | go/src/infra/tools/cipd/local/deployer_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698