Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |