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

Side by Side Diff: cipd/client/cipd/local/deployer.go

Issue 2640703002: Change CIPD internal pkgs directory layout to use numeric indices. (Closed)
Patch Set: Address comments Created 3 years, 11 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
OLDNEW
1 // Copyright 2014 The LUCI Authors. All rights reserved. 1 // Copyright 2014 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 local 5 package local
6 6
7 import ( 7 import (
8 "crypto/sha1"
9 "encoding/base64"
10 "fmt" 8 "fmt"
11 "io/ioutil" 9 "io/ioutil"
10 "math/rand"
12 "os" 11 "os"
13 "path/filepath" 12 "path/filepath"
14 "runtime" 13 "runtime"
15 "sort" 14 "sort"
15 "strconv"
16 "strings" 16 "strings"
17 "sync" 17 "sync"
18 "syscall"
19 "time"
18 20
19 "golang.org/x/net/context" 21 "golang.org/x/net/context"
20 22
21 "github.com/luci/luci-go/cipd/client/cipd/common" 23 "github.com/luci/luci-go/cipd/client/cipd/common"
22 "github.com/luci/luci-go/common/logging" 24 "github.com/luci/luci-go/common/logging"
23 ) 25 )
24 26
25 // TODO(vadimsh): How to handle path conflicts between two packages? Currently 27 // TODO(vadimsh): How to handle path conflicts between two packages? Currently
26 // the last one installed wins. 28 // the last one installed wins.
27 29
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 } 147 }
146 if _, err := d.fs.EnsureDirectory(ctx, d.fs.Root()); err != nil { 148 if _, err := d.fs.EnsureDirectory(ctx, d.fs.Root()); err != nil {
147 return common.Pin{}, err 149 return common.Pin{}, err
148 } 150 }
149 151
150 // Extract new version to the .cipd/pkgs/* guts. For "symlink" install m ode it 152 // Extract new version to the .cipd/pkgs/* guts. For "symlink" install m ode it
151 // is the final destination. For "copy" install mode it's a temp destina tion 153 // is the final destination. For "copy" install mode it's a temp destina tion
152 // and files will be moved to the site root later (in addToSiteRoot call ). 154 // and files will be moved to the site root later (in addToSiteRoot call ).
153 // ExtractPackageInstance knows how to build full paths and how to atomi cally 155 // ExtractPackageInstance knows how to build full paths and how to atomi cally
154 // extract a package. No need to delete garbage if it fails. 156 // extract a package. No need to delete garbage if it fails.
155 » pkgPath := d.packagePath(ctx, pin.PackageName) 157 » pkgPath, err := d.packagePath(ctx, pin.PackageName, true)
158 » if err != nil {
159 » » return common.Pin{}, err
160 » }
161
156 destPath := filepath.Join(pkgPath, pin.InstanceID) 162 destPath := filepath.Join(pkgPath, pin.InstanceID)
157 if err := ExtractInstance(ctx, inst, NewFileSystemDestination(destPath, d.fs)); err != nil { 163 if err := ExtractInstance(ctx, inst, NewFileSystemDestination(destPath, d.fs)); err != nil {
158 return common.Pin{}, err 164 return common.Pin{}, err
159 } 165 }
160 newManifest, err := d.readManifest(ctx, destPath) 166 newManifest, err := d.readManifest(ctx, destPath)
161 if err != nil { 167 if err != nil {
162 return common.Pin{}, err 168 return common.Pin{}, err
163 } 169 }
164 170
165 // Remember currently deployed version (to remove it later). Do not frea k out 171 // Remember currently deployed version (to remove it later). Do not frea k out
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 } 234 }
229 if err == nil { 235 if err == nil {
230 logging.Infof(ctx, "Successfully deployed %s", pin) 236 logging.Infof(ctx, "Successfully deployed %s", pin)
231 } else { 237 } else {
232 logging.Errorf(ctx, "Failed to deploy %s: %s", pin, err) 238 logging.Errorf(ctx, "Failed to deploy %s: %s", pin, err)
233 } 239 }
234 return newPin, err 240 return newPin, err
235 } 241 }
236 242
237 func (d *deployerImpl) CheckDeployed(ctx context.Context, pkg string) (common.Pi n, error) { 243 func (d *deployerImpl) CheckDeployed(ctx context.Context, pkg string) (common.Pi n, error) {
238 » current, err := d.getCurrentInstanceID(d.packagePath(ctx, pkg)) 244 » pkgPath, err := d.packagePath(ctx, pkg, false)
239 if err != nil { 245 if err != nil {
240 return common.Pin{}, err 246 return common.Pin{}, err
241 } 247 }
248 if pkgPath == "" {
249 return common.Pin{}, fmt.Errorf("package %s is not installed", p kg)
250 }
251
252 current, err := d.getCurrentInstanceID(pkgPath)
253 if err != nil {
254 return common.Pin{}, err
255 }
242 if current == "" { 256 if current == "" {
243 return common.Pin{}, fmt.Errorf("package %s is not installed", p kg) 257 return common.Pin{}, fmt.Errorf("package %s is not installed", p kg)
244 } 258 }
245 return common.Pin{ 259 return common.Pin{
246 PackageName: pkg, 260 PackageName: pkg,
247 InstanceID: current, 261 InstanceID: current,
248 }, nil 262 }, nil
249 } 263 }
250 264
251 func (d *deployerImpl) FindDeployed(ctx context.Context) ([]common.Pin, error) { 265 func (d *deployerImpl) FindDeployed(ctx context.Context) ([]common.Pin, error) {
252 // Directories with packages are direct children of .cipd/pkgs/. 266 // Directories with packages are direct children of .cipd/pkgs/.
253 pkgs := filepath.Join(d.fs.Root(), filepath.FromSlash(packagesDir)) 267 pkgs := filepath.Join(d.fs.Root(), filepath.FromSlash(packagesDir))
254 infos, err := ioutil.ReadDir(pkgs) 268 infos, err := ioutil.ReadDir(pkgs)
255 if err != nil { 269 if err != nil {
256 if os.IsNotExist(err) { 270 if os.IsNotExist(err) {
257 return nil, nil 271 return nil, nil
258 } 272 }
259 return nil, err 273 return nil, err
260 } 274 }
261 275
262 found := map[string]common.Pin{} 276 found := map[string]common.Pin{}
263 keys := []string{} 277 keys := []string{}
264 for _, info := range infos { 278 for _, info := range infos {
265 if !info.IsDir() { 279 if !info.IsDir() {
266 continue 280 continue
267 } 281 }
268 » » // Attempt to read the manifest. If it is there -> valid package is found. 282 » » // Read the description and the 'current' link.
269 pkgPath := filepath.Join(pkgs, info.Name()) 283 pkgPath := filepath.Join(pkgs, info.Name())
284 desc, err := d.readDescription(ctx, pkgPath)
285 if err != nil || desc == nil {
286 continue
287 }
270 currentID, err := d.getCurrentInstanceID(pkgPath) 288 currentID, err := d.getCurrentInstanceID(pkgPath)
271 if err != nil || currentID == "" { 289 if err != nil || currentID == "" {
272 continue 290 continue
273 } 291 }
274 » » manifest, err := d.readManifest(ctx, filepath.Join(pkgPath, curr entID)) 292
275 » » if err != nil {
276 » » » continue
277 » » }
278 // Ignore duplicate entries, they can appear if someone messes w ith pkgs/* 293 // Ignore duplicate entries, they can appear if someone messes w ith pkgs/*
279 // structure manually. 294 // structure manually.
280 » » if _, ok := found[manifest.PackageName]; !ok { 295 » » if _, ok := found[desc.PackageName]; !ok {
281 » » » keys = append(keys, manifest.PackageName) 296 » » » keys = append(keys, desc.PackageName)
282 » » » found[manifest.PackageName] = common.Pin{ 297 » » » found[desc.PackageName] = common.Pin{
283 » » » » PackageName: manifest.PackageName, 298 » » » » PackageName: desc.PackageName,
284 InstanceID: currentID, 299 InstanceID: currentID,
285 } 300 }
286 } 301 }
287 } 302 }
288 303
289 // Sort by package name. 304 // Sort by package name.
290 sort.Strings(keys) 305 sort.Strings(keys)
291 out := make([]common.Pin, len(found)) 306 out := make([]common.Pin, len(found))
292 for i, k := range keys { 307 for i, k := range keys {
293 out[i] = found[k] 308 out[i] = found[k]
294 } 309 }
295 return out, nil 310 return out, nil
296 } 311 }
297 312
298 func (d *deployerImpl) RemoveDeployed(ctx context.Context, packageName string) e rror { 313 func (d *deployerImpl) RemoveDeployed(ctx context.Context, packageName string) e rror {
299 logging.Infof(ctx, "Removing %s from %s", packageName, d.fs.Root()) 314 logging.Infof(ctx, "Removing %s from %s", packageName, d.fs.Root())
300 if err := common.ValidatePackageName(packageName); err != nil { 315 if err := common.ValidatePackageName(packageName); err != nil {
301 return err 316 return err
302 } 317 }
303 » pkgPath := d.packagePath(ctx, packageName) 318 » pkgPath, err := d.packagePath(ctx, packageName, false)
319 » if err != nil {
320 » » return err
321 » }
322 » if pkgPath == "" {
323 » » logging.Warningf(ctx, "Package %s not found", packageName)
324 » » return nil
325 » }
304 326
305 // Read the manifest of the currently installed version. 327 // Read the manifest of the currently installed version.
306 manifest := Manifest{} 328 manifest := Manifest{}
307 currentID, err := d.getCurrentInstanceID(pkgPath) 329 currentID, err := d.getCurrentInstanceID(pkgPath)
308 if err == nil && currentID != "" { 330 if err == nil && currentID != "" {
309 manifest, err = d.readManifest(ctx, filepath.Join(pkgPath, curre ntID)) 331 manifest, err = d.readManifest(ctx, filepath.Join(pkgPath, curre ntID))
310 } 332 }
311 333
312 // Warn, but continue with removal anyway. EnsureDirectoryGone call belo w 334 // Warn, but continue with removal anyway. EnsureDirectoryGone call belo w
313 // will nuke everything (even if it's half broken). 335 // will nuke everything (even if it's half broken).
314 if err != nil { 336 if err != nil {
315 logging.Warningf(ctx, "Package %s is in a broken state: %s", pac kageName, err) 337 logging.Warningf(ctx, "Package %s is in a broken state: %s", pac kageName, err)
316 } else { 338 } else {
317 d.removeFromSiteRoot(ctx, manifest.Files) 339 d.removeFromSiteRoot(ctx, manifest.Files)
318 } 340 }
319 return d.fs.EnsureDirectoryGone(ctx, pkgPath) 341 return d.fs.EnsureDirectoryGone(ctx, pkgPath)
320 } 342 }
321 343
322 func (d *deployerImpl) TempFile(ctx context.Context, prefix string) (*os.File, e rror) { 344 func (d *deployerImpl) TempFile(ctx context.Context, prefix string) (*os.File, e rror) {
323 dir, err := d.fs.EnsureDirectory(ctx, filepath.Join(d.fs.Root(), SiteSer viceDir, "tmp")) 345 dir, err := d.fs.EnsureDirectory(ctx, filepath.Join(d.fs.Root(), SiteSer viceDir, "tmp"))
324 if err != nil { 346 if err != nil {
325 return nil, err 347 return nil, err
326 } 348 }
327 return ioutil.TempFile(dir, prefix) 349 return ioutil.TempFile(dir, prefix)
328 } 350 }
329 351
352 func (d *deployerImpl) TempDir(ctx context.Context, prefix string) (string, erro r) {
353 dir, err := d.fs.EnsureDirectory(ctx, filepath.Join(d.fs.Root(), SiteSer viceDir, "tmp"))
354 if err != nil {
355 return "", err
356 }
357 return ioutil.TempDir(dir, prefix)
358 }
359
330 func (d *deployerImpl) CleanupTrash(ctx context.Context) error { 360 func (d *deployerImpl) CleanupTrash(ctx context.Context) error {
331 return d.fs.CleanupTrash(ctx) 361 return d.fs.CleanupTrash(ctx)
332 } 362 }
333 363
334 //////////////////////////////////////////////////////////////////////////////// 364 ////////////////////////////////////////////////////////////////////////////////
335 // Utility methods. 365 // Utility methods.
336 366
367 type numSet sort.IntSlice
368
369 func (s *numSet) addNum(n int) {
370 idx := sort.IntSlice((*s)).Search(n)
371 if idx == len(*s) {
372 // it's insertion point is off the end of the slice
373 *s = append(*s, n)
374 } else if (*s)[idx] != n {
375 // it's insertion point is inside the slice, but is not present.
376 *s = append(append((*s)[idx:], n), (*s)[:idx]...)
377 }
378 // it's already present in the slice
379 }
380
381 func (s *numSet) smallestNewNum() int {
382 if len(*s) == 0 {
383 return 0
384 }
385 prev := (*s)[0]
386 for _, n := range (*s)[1:] {
387 if n-1 != prev {
388 return prev + 1
389 }
390 prev = n
391 }
392 return prev + 1
393 }
394
337 // packagePath returns a path to a package directory in .cipd/pkgs/. 395 // packagePath returns a path to a package directory in .cipd/pkgs/.
338 func (d *deployerImpl) packagePath(ctx context.Context, pkg string) string { 396 //
339 » rel := filepath.Join(filepath.FromSlash(packagesDir), packageNameDigest( pkg)) 397 // This will scan all directories under pkgs, looking for a description.json. If
398 // an old-style package folder is encountered (e.g. has an instance folder and
399 // current manifest, but doesn't have a description.json), the description.json
400 // will be added.
401 //
402 // If no suitable path is found and allocate is true, this will create a new
403 // directory with an accompanying description.json. Otherwise this returns "".
404 func (d *deployerImpl) packagePath(ctx context.Context, pkg string, allocate boo l) (string, error) {
405 » if err := common.ValidatePackageName(pkg); err != nil {
406 » » return "", err
407 » }
408
409 » rel := filepath.Join(filepath.FromSlash(packagesDir))
340 abs, err := d.fs.RootRelToAbs(rel) 410 abs, err := d.fs.RootRelToAbs(rel)
341 if err != nil { 411 if err != nil {
342 » » msg := fmt.Sprintf("can't get absolute path of %q", rel) 412 » » logging.Errorf(ctx, "Can't get absolute path of %q: %s", rel, er r)
343 » » logging.Errorf(ctx, "%s", msg) 413 » » return "", err
344 » » panic(msg)
345 } 414 }
346 » return abs 415 » files, err := ioutil.ReadDir(abs)
416 » if err != nil && !os.IsNotExist(err) {
417 » » logging.Errorf(ctx, "Can't read packages dir %q: %s", abs, err)
418 » » return "", err
419 » }
420
421 » seenNumbers := numSet{}
422
423 » for _, f := range files {
424 » » // keep track of all numeric children of .cipd/pkgs
425 » » if n, err := strconv.Atoi(f.Name()); err == nil {
426 » » » seenNumbers.addNum(n)
427 » » }
428
429 » » fullPkgPath := filepath.Join(abs, f.Name())
430 » » description, err := d.readDescription(ctx, fullPkgPath)
431 » » if err != nil {
432 » » » logging.Warningf(ctx, "Skipping %q: %s", fullPkgPath, er r)
433 » » » continue
434 » » }
435 » » if description.PackageName == pkg {
436 » » » return fullPkgPath, nil
437 » » }
438 » }
439
440 » if !allocate {
441 » » return "", nil
442 » }
443
444 » // we didn't find one, so we have to make one
445 » if _, err := d.fs.EnsureDirectory(ctx, abs); err != nil {
446 » » logging.Errorf(ctx, "Cannot ensure packages directory: %s", err)
447 » » return "", err
448 » }
449
450 » tmpDir, err := d.TempDir(ctx, strings.Replace(pkg, "/", "_", -1))
451 » if err != nil {
452 » » logging.Errorf(ctx, "Cannot create new pkg tempdir: %s", err)
453 » » return "", err
454 » }
455 » defer d.fs.EnsureDirectoryGone(ctx, tmpDir)
456 » err = d.fs.EnsureFile(ctx, filepath.Join(tmpDir, descriptionName), func( f *os.File) error {
Vadim Sh. 2017/01/19 01:26:01 this is a bit of overkill here, since we now the n
iannucci 2017/01/19 02:18:57 Yeah, I know, but it's less code here so *shrug*
457 » » return writeDescription(&Description{PackageName: pkg}, f)
458 » })
459 » if err != nil {
460 » » logging.Errorf(ctx, "Cannot create new pkg description.json: %s" , err)
461 » » return "", err
462 » }
463
464 » // now we have to find a suitable index folder for it.
465 » for attempts := 0; attempts < 3; attempts++ {
466 » » if attempts > 0 {
467 » » » // random sleep up to 1s to help avoid collisions betwee n clients.
468 » » » time.Sleep(time.Duration(rand.Int31n(1000)) * time.Milli second)
469 » » }
470 » » n := seenNumbers.smallestNewNum()
471 » » seenNumbers.addNum(n)
472
473 » » pkgPath := filepath.Join(abs, strconv.Itoa(n))
474 » » // We use os.Rename instead of d.fs.Replace because we want it t o fail if
475 » » // the target directory already exists.
476 » » switch err := os.Rename(tmpDir, pkgPath); le := err.(type) {
477 » » case nil:
478 » » » return pkgPath, nil
479
480 » » case *os.LinkError:
481 » » » if le.Err != syscall.ENOTEMPTY {
482 » » » » logging.Errorf(ctx, "Error while creating pkg di r %s: %s", pkgPath, err)
483 » » » » return "", err
484 » » » }
485
486 » » default:
487 » » » logging.Errorf(ctx, "Unknown error while creating pkg di r %s: %s", pkgPath, err)
488 » » » return "", err
489 » » }
490
491 » » // rename failed with ENOTEMPTY, that means that another client wrote this
492 » » // directory.
493 » » description, err := d.readDescription(ctx, pkgPath)
494 » » if err != nil {
495 » » » logging.Warningf(ctx, "Skipping %q: %s", pkgPath, err)
496 » » » continue
497 » » }
498 » » if description.PackageName == pkg {
499 » » » return pkgPath, nil
500 » » }
501 » }
502
503 » logging.Errorf(ctx, "Unable to find valid index for package %q in %s!", pkg, abs)
504 » return "", err
347 } 505 }
348 506
349 // getCurrentInstanceID returns instance ID of currently installed instance 507 // getCurrentInstanceID returns instance ID of currently installed instance
350 // given a path to a package directory (.cipd/pkgs/<name>). 508 // given a path to a package directory (.cipd/pkgs/<name>).
351 // 509 //
352 // It returns ("", nil) if no package is installed there. 510 // It returns ("", nil) if no package is installed there.
353 func (d *deployerImpl) getCurrentInstanceID(packageDir string) (string, error) { 511 func (d *deployerImpl) getCurrentInstanceID(packageDir string) (string, error) {
354 var current string 512 var current string
355 var err error 513 var err error
356 if runtime.GOOS == "windows" { 514 if runtime.GOOS == "windows" {
(...skipping 26 matching lines...) Expand all
383 return err 541 return err
384 } 542 }
385 if runtime.GOOS == "windows" { 543 if runtime.GOOS == "windows" {
386 return EnsureFile( 544 return EnsureFile(
387 ctx, d.fs, filepath.Join(packageDir, currentTxt), 545 ctx, d.fs, filepath.Join(packageDir, currentTxt),
388 strings.NewReader(instanceID)) 546 strings.NewReader(instanceID))
389 } 547 }
390 return d.fs.EnsureSymlink(ctx, filepath.Join(packageDir, currentSymlink) , instanceID) 548 return d.fs.EnsureSymlink(ctx, filepath.Join(packageDir, currentSymlink) , instanceID)
391 } 549 }
392 550
551 // readDescription reads the package description.json given a path to a package
552 // directory.
553 //
554 // As a backwards-compatibility measure, it will also upgrade CIPD < 1.4 folders
555 // to contain a description.json. Previous to 1.4, package folders only had
556 // instance subfolders, and the current instances' manifest was used to
557 // determine the package name. Versions prior to 1.4 also installed all packages
558 // at the base (root ""), hence the implied root location here.
559 //
560 // Returns (nil, nil) if no description.json exists and there are no instance
561 // folders present.
562 func (d *deployerImpl) readDescription(ctx context.Context, pkgDir string) (desc *Description, err error) {
563 descriptionPath := filepath.Join(pkgDir, descriptionName)
564 r, err := os.Open(descriptionPath)
565 switch {
566 case os.IsNotExist(err):
567 // try fixup
568 break
569 case err == nil:
570 defer r.Close()
571 return readDescription(r)
572 default:
573 return
574 }
575
576 // see if this is a pre 1.4 directory
577 currentID, err := d.getCurrentInstanceID(pkgDir)
578 if err != nil {
579 return
580 }
581
582 if currentID == "" {
583 logging.Warningf(ctx, "No current instance id in %s", pkgDir)
584 err = nil
585 return
586 }
587
588 manifest, err := d.readManifest(ctx, filepath.Join(pkgDir, currentID))
589 if err != nil {
590 return
591 }
592
593 desc = &Description{
594 PackageName: manifest.PackageName,
595 }
596 // To handle the case where some other user owns these directories, all errors
597 // from here to the end are treated as warnings.
598 err = d.fs.EnsureFile(ctx, descriptionPath, func(f *os.File) error {
599 return writeDescription(desc, f)
600 })
601 if err != nil {
602 logging.Warningf(ctx, "Unable to create description.json: %s", e rr)
603 err = nil
604 }
605 return
606 }
607
393 // readManifest reads package manifest given a path to a package instance 608 // readManifest reads package manifest given a path to a package instance
394 // (.cipd/pkgs/<name>/<instance id>). 609 // (.cipd/pkgs/<name>/<instance id>).
395 func (d *deployerImpl) readManifest(ctx context.Context, instanceDir string) (Ma nifest, error) { 610 func (d *deployerImpl) readManifest(ctx context.Context, instanceDir string) (Ma nifest, error) {
396 manifestPath := filepath.Join(instanceDir, filepath.FromSlash(manifestNa me)) 611 manifestPath := filepath.Join(instanceDir, filepath.FromSlash(manifestNa me))
397 r, err := os.Open(manifestPath) 612 r, err := os.Open(manifestPath)
398 if err != nil { 613 if err != nil {
399 return Manifest{}, err 614 return Manifest{}, err
400 } 615 }
401 defer r.Close() 616 defer r.Close()
402 manifest, err := readManifest(r) 617 manifest, err := readManifest(r)
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
489 // 704 //
490 // The root directory itself will not be removed (even if it's empty). Best 705 // The root directory itself will not be removed (even if it's empty). Best
491 // effort, logs errors. 706 // effort, logs errors.
492 func (d *deployerImpl) removeEmptyDirs(ctx context.Context, root string) { 707 func (d *deployerImpl) removeEmptyDirs(ctx context.Context, root string) {
493 // TODO(vadimsh): Implement. 708 // TODO(vadimsh): Implement.
494 } 709 }
495 710
496 //////////////////////////////////////////////////////////////////////////////// 711 ////////////////////////////////////////////////////////////////////////////////
497 // Utility functions. 712 // Utility functions.
498 713
499 // packageNameDigest returns a filename to use for naming a package directory in
500 // the file system. Using package names as is can introduce problems on file
501 // systems with path length limits (on Windows in particular). Returns stripped
502 // SHA1 of the whole package name on Windows. On Linux\Mac also prepends last
503 // two components of the package name (for better readability of .cipd/*
504 // directory).
505 func packageNameDigest(pkg string) string {
506 // Be paranoid.
507 err := common.ValidatePackageName(pkg)
508 if err != nil {
509 panic(err.Error())
510 }
511
512 // Grab stripped SHA1 of the full package name.
513 digest := sha1.Sum([]byte(pkg))
514 hash := base64.URLEncoding.EncodeToString(digest[:])[:10]
515
516 // On Windows paths are restricted to 260 chars, so every byte counts.
517 if runtime.GOOS == "windows" {
518 return hash
519 }
520
521 // On Posix file paths are not so restricted, so we can make names more
522 // readable. Grab last <= 2 components of the package path and join them with
523 // the digest.
524 chunks := strings.Split(pkg, "/")
525 if len(chunks) > 2 {
526 chunks = chunks[len(chunks)-2:]
527 }
528 chunks = append(chunks, hash)
529 return strings.Join(chunks, "_")
530 }
531
532 // scanPackageDir finds a set of regular files (and symlinks) in a package 714 // scanPackageDir finds a set of regular files (and symlinks) in a package
533 // instance directory and returns them as FileInfo structs (with slash-separated 715 // instance directory and returns them as FileInfo structs (with slash-separated
534 // paths relative to dir directory). Skips package service directories (.cipdpkg 716 // paths relative to dir directory). Skips package service directories (.cipdpkg
535 // and .cipd) since they contain package deployer gut files, not something that 717 // and .cipd) since they contain package deployer gut files, not something that
536 // needs to be deployed. 718 // needs to be deployed.
537 func scanPackageDir(ctx context.Context, dir string) ([]FileInfo, error) { 719 func scanPackageDir(ctx context.Context, dir string) ([]FileInfo, error) {
538 out := []FileInfo{} 720 out := []FileInfo{}
539 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 721 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
540 if err != nil { 722 if err != nil {
541 return err 723 return err
(...skipping 21 matching lines...) Expand all
563 Size: uint64(info.Size()), 745 Size: uint64(info.Size()),
564 Executable: (info.Mode().Perm() & 0111) != 0, 746 Executable: (info.Mode().Perm() & 0111) != 0,
565 Symlink: symlink, 747 Symlink: symlink,
566 }) 748 })
567 } 749 }
568 } 750 }
569 return nil 751 return nil
570 }) 752 })
571 return out, err 753 return out, err
572 } 754 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698