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

Side by Side Diff: vpython/venv/venv.go

Issue 2963503003: [errors] Greatly simplify common/errors package. (Closed)
Patch Set: fix nits 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 | « vpython/venv/util.go ('k') | vpython/venv/venv_resources_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. All rights reserved. 1 // Copyright 2017 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 venv 5 package venv
6 6
7 import ( 7 import (
8 "bytes" 8 "bytes"
9 "encoding/json" 9 "encoding/json"
10 "io/ioutil" 10 "io/ioutil"
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
48 return func() error { 48 return func() error {
49 logging.Debugf(c, "Lock is currently held. Sleeping %v and retry ing...", lockHeldDelay) 49 logging.Debugf(c, "Lock is currently held. Sleeping %v and retry ing...", lockHeldDelay)
50 tr := clock.Sleep(c, lockHeldDelay) 50 tr := clock.Sleep(c, lockHeldDelay)
51 return tr.Err 51 return tr.Err
52 } 52 }
53 } 53 }
54 54
55 func withTempDir(l logging.Logger, prefix string, fn func(string) error) error { 55 func withTempDir(l logging.Logger, prefix string, fn func(string) error) error {
56 tdir, err := ioutil.TempDir("", prefix) 56 tdir, err := ioutil.TempDir("", prefix)
57 if err != nil { 57 if err != nil {
58 » » return errors.Annotate(err).Reason("failed to create temporary d irectory").Err() 58 » » return errors.Annotate(err, "failed to create temporary director y").Err()
59 } 59 }
60 defer func() { 60 defer func() {
61 if err := filesystem.RemoveAll(tdir); err != nil { 61 if err := filesystem.RemoveAll(tdir); err != nil {
62 l.Infof("Failed to remove temporary directory: %s", err) 62 l.Infof("Failed to remove temporary directory: %s", err)
63 } 63 }
64 }() 64 }()
65 65
66 return fn(tdir) 66 return fn(tdir)
67 } 67 }
68 68
69 // EnvRootFromStampPath calculates the environment root from an exported 69 // EnvRootFromStampPath calculates the environment root from an exported
70 // environment specification file path. 70 // environment specification file path.
71 // 71 //
72 // The specification path is: <EnvRoot>/<SpecHash>/EnvironmentStampPath, so our 72 // The specification path is: <EnvRoot>/<SpecHash>/EnvironmentStampPath, so our
73 // EnvRoot is two directories up. 73 // EnvRoot is two directories up.
74 // 74 //
75 // We export EnvSpecPath as an asbolute path. However, since someone else 75 // We export EnvSpecPath as an asbolute path. However, since someone else
76 // could have overridden it or exported their own, let's make sure. 76 // could have overridden it or exported their own, let's make sure.
77 func EnvRootFromStampPath(path string) (string, error) { 77 func EnvRootFromStampPath(path string) (string, error) {
78 if err := filesystem.AbsPath(&path); err != nil { 78 if err := filesystem.AbsPath(&path); err != nil {
79 » » return "", errors.Annotate(err). 79 » » return "", errors.Annotate(err,
80 » » » Reason("failed to get absolute path for specification fi le path: %(path)s"). 80 » » » "failed to get absolute path for specification file path : %(path)s", path).Err()
81 » » » Err()
82 } 81 }
83 return filepath.Dir(filepath.Dir(path)), nil 82 return filepath.Dir(filepath.Dir(path)), nil
84 } 83 }
85 84
86 // With creates a new Env and executes "fn" with assumed ownership of that Env. 85 // With creates a new Env and executes "fn" with assumed ownership of that Env.
87 // 86 //
88 // The Context passed to "fn" will be cancelled if we lose perceived ownership 87 // The Context passed to "fn" will be cancelled if we lose perceived ownership
89 // of the configured environment. This is not an expected scenario, and should 88 // of the configured environment. This is not an expected scenario, and should
90 // be considered an error condition. The Env passed to "fn" is valid only for 89 // be considered an error condition. The Env passed to "fn" is valid only for
91 // the duration of the callback. 90 // the duration of the callback.
(...skipping 14 matching lines...) Expand all
106 // If our configured VirtualEnv is, itself, an empty then we can 105 // If our configured VirtualEnv is, itself, an empty then we can
107 // skip this. 106 // skip this.
108 var e *vpython.Environment 107 var e *vpython.Environment
109 if cfg.HasWheels() { 108 if cfg.HasWheels() {
110 // Use an empty VirtualEnv to probe the runtime environment. 109 // Use an empty VirtualEnv to probe the runtime environment.
111 // 110 //
112 // Disable pruning for this step since we'll be doing that later with the 111 // Disable pruning for this step since we'll be doing that later with the
113 // full environment initialization. 112 // full environment initialization.
114 emptyEnv, err := cfg.WithoutWheels().makeEnv(c, nil) 113 emptyEnv, err := cfg.WithoutWheels().makeEnv(c, nil)
115 if err != nil { 114 if err != nil {
116 » » » return errors.Annotate(err).Reason("failed to initialize empty probe environment").Err() 115 » » » return errors.Annotate(err, "failed to initialize empty probe environment").Err()
117 } 116 }
118 if err := emptyEnv.ensure(c, blocking); err != nil { 117 if err := emptyEnv.ensure(c, blocking); err != nil {
119 » » » return errors.Annotate(err).Reason("failed to create emp ty probe environment").Err() 118 » » » return errors.Annotate(err, "failed to create empty prob e environment").Err()
120 } 119 }
121 120
122 usedEnvs.Add(emptyEnv.Name) 121 usedEnvs.Add(emptyEnv.Name)
123 e = emptyEnv.Environment 122 e = emptyEnv.Environment
124 } 123 }
125 124
126 // Run the real config, now with runtime data. 125 // Run the real config, now with runtime data.
127 env, err := cfg.makeEnv(c, e) 126 env, err := cfg.makeEnv(c, e)
128 if err != nil { 127 if err != nil {
129 return err 128 return err
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
221 // This will generally happen if another process created the environment 220 // This will generally happen if another process created the environment
222 // in between when we checked for the co mpletion stamp initially and 221 // in between when we checked for the co mpletion stamp initially and
223 // when we actually obtained the lock. 222 // when we actually obtained the lock.
224 logging.Debugf(c, "Completion flag found ! Environment is set-up: %s", e.completeFlagPath) 223 logging.Debugf(c, "Completion flag found ! Environment is set-up: %s", e.completeFlagPath)
225 return nil 224 return nil
226 } 225 }
227 logging.WithError(err).Debugf(c, "VirtualEnv is not complete.") 226 logging.WithError(err).Debugf(c, "VirtualEnv is not complete.")
228 227
229 // No complete flag. Create a new VirtualEnv her e. 228 // No complete flag. Create a new VirtualEnv her e.
230 if err := e.createLocked(c); err != nil { 229 if err := e.createLocked(c); err != nil {
231 » » » » » return errors.Annotate(err).Reason("fail ed to create new VirtualEnv").Err() 230 » » » » » return errors.Annotate(err, "failed to c reate new VirtualEnv").Err()
232 } 231 }
233 232
234 // Mark that this environment is complete. This MUST succeed so other 233 // Mark that this environment is complete. This MUST succeed so other
235 // instances know that this environment is compl ete. 234 // instances know that this environment is compl ete.
236 if err := e.touchCompleteFlagLocked(); err != ni l { 235 if err := e.touchCompleteFlagLocked(); err != ni l {
237 » » » » » return errors.Annotate(err).Reason("fail ed to create complete flag").Err() 236 » » » » » return errors.Annotate(err, "failed to c reate complete flag").Err()
238 } 237 }
239 238
240 logging.Debugf(c, "Successfully created new virt ual environment [%s]!", e.Name) 239 logging.Debugf(c, "Successfully created new virt ual environment [%s]!", e.Name)
241 return nil 240 return nil
242 }) 241 })
243 242
244 case fslock.ErrLockHeld: 243 case fslock.ErrLockHeld:
245 // We couldn't get an exclusive lock. Try again to load the environment 244 // We couldn't get an exclusive lock. Try again to load the environment
246 // stamp, asserting the existence of the completion flag in the process. 245 // stamp, asserting the existence of the completion flag in the process.
247 // If another process has created the environment, we ma y be able to use 246 // If another process has created the environment, we ma y be able to use
248 // it without ever having to obtain its lock! 247 // it without ever having to obtain its lock!
249 if err := e.AssertCompleteAndLoad(); err == nil { 248 if err := e.AssertCompleteAndLoad(); err == nil {
250 logging.Infof(c, "Environment was completed whil e waiting for lock: %s", e.EnvironmentStampPath) 249 logging.Infof(c, "Environment was completed whil e waiting for lock: %s", e.EnvironmentStampPath)
251 return nil 250 return nil
252 } 251 }
253 252
254 logging.Fields{ 253 logging.Fields{
255 logging.ErrorKey: err, 254 logging.ErrorKey: err,
256 "path": e.EnvironmentStampPath, 255 "path": e.EnvironmentStampPath,
257 }.Debugf(c, "Lock is held, and environment is not comple te.") 256 }.Debugf(c, "Lock is held, and environment is not comple te.")
258 if !blocking { 257 if !blocking {
259 » » » » return errors.Annotate(err).Reason("VirtualEnv l ock is currently held (non-blocking)").Err() 258 » » » » return errors.Annotate(err, "VirtualEnv lock is currently held (non-blocking)").Err()
260 } 259 }
261 260
262 // Some other process holds the lock. Sleep a little and retry. 261 // Some other process holds the lock. Sleep a little and retry.
263 if err := blocker(c)(); err != nil { 262 if err := blocker(c)(); err != nil {
264 return err 263 return err
265 } 264 }
266 265
267 default: 266 default:
268 » » » return errors.Annotate(err).Reason("failed to create Vir tualEnv").Err() 267 » » » return errors.Annotate(err, "failed to create VirtualEnv ").Err()
269 } 268 }
270 } 269 }
271 } 270 }
272 271
273 func (e *Env) withImpl(c context.Context, blocking bool, used stringset.Set, 272 func (e *Env) withImpl(c context.Context, blocking bool, used stringset.Set,
274 fn func(context.Context, *Env) error) (err error) { 273 fn func(context.Context, *Env) error) (err error) {
275 274
276 // Setup the VirtualEnv environment. 275 // Setup the VirtualEnv environment.
277 // 276 //
278 // Setup will obtain an exclusive lock on the environment for set-up and 277 // Setup will obtain an exclusive lock on the environment for set-up and
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
323 if !environmentWasIncomplete { 322 if !environmentWasIncomplete {
324 return nil 323 return nil
325 } 324 }
326 325
327 case fslock.ErrLockHeld: 326 case fslock.ErrLockHeld:
328 logging.Fields{ 327 logging.Fields{
329 logging.ErrorKey: err, 328 logging.ErrorKey: err,
330 "path": e.EnvironmentStampPath, 329 "path": e.EnvironmentStampPath,
331 }.Debugf(c, "Could not obtain shared usage lock.") 330 }.Debugf(c, "Could not obtain shared usage lock.")
332 if !blocking { 331 if !blocking {
333 » » » » return errors.Annotate(err).Reason("VirtualEnv l ock is currently held (non-blocking)").Err() 332 » » » » return errors.Annotate(err, "VirtualEnv lock is currently held (non-blocking)").Err()
334 } 333 }
335 334
336 // Some other process holds the lock. Sleep a little and retry. 335 // Some other process holds the lock. Sleep a little and retry.
337 if err := blocker(c)(); err != nil { 336 if err := blocker(c)(); err != nil {
338 return err 337 return err
339 } 338 }
340 339
341 default: 340 default:
342 » » » return errors.Annotate(err).Reason("failed to use Virtua lEnv").Err() 341 » » » return errors.Annotate(err, "failed to use VirtualEnv"). Err()
343 } 342 }
344 } 343 }
345 } 344 }
346 345
347 // Interpreter returns the VirtualEnv's isolated Python Interpreter instance. 346 // Interpreter returns the VirtualEnv's isolated Python Interpreter instance.
348 func (e *Env) Interpreter() *python.Interpreter { 347 func (e *Env) Interpreter() *python.Interpreter {
349 if e.interpreter == nil { 348 if e.interpreter == nil {
350 e.interpreter = &python.Interpreter{ 349 e.interpreter = &python.Interpreter{
351 Python: e.Python, 350 Python: e.Python,
352 } 351 }
(...skipping 24 matching lines...) Expand all
377 // 376 //
378 // An error is returned if the completion flag does not exist, or if the 377 // An error is returned if the completion flag does not exist, or if the
379 // VirtualEnv environment stamp could not be loaded. 378 // VirtualEnv environment stamp could not be loaded.
380 func (e *Env) AssertCompleteAndLoad() error { 379 func (e *Env) AssertCompleteAndLoad() error {
381 if err := e.assertComplete(); err != nil { 380 if err := e.assertComplete(); err != nil {
382 return err 381 return err
383 } 382 }
384 383
385 content, err := ioutil.ReadFile(e.EnvironmentStampPath) 384 content, err := ioutil.ReadFile(e.EnvironmentStampPath)
386 if err != nil { 385 if err != nil {
387 » » return errors.Annotate(err).Reason("failed to load file from: %( path)s"). 386 » » return errors.Annotate(err, "failed to load file from: %s", e.En vironmentStampPath).Err()
388 » » » D("path", e.EnvironmentStampPath).
389 » » » Err()
390 } 387 }
391 388
392 var environment vpython.Environment 389 var environment vpython.Environment
393 if err := proto.UnmarshalText(string(content), &environment); err != nil { 390 if err := proto.UnmarshalText(string(content), &environment); err != nil {
394 » » return errors.Annotate(err).Reason("failed to unmarshal vpython. Env stamp from: %(path)s"). 391 » » return errors.Annotate(err, "failed to unmarshal vpython.Env sta mp from: %s",
395 » » » D("path", e.EnvironmentStampPath). 392 » » » e.EnvironmentStampPath).Err()
396 » » » Err()
397 } 393 }
398 if err := spec.NormalizeEnvironment(&environment); err != nil { 394 if err := spec.NormalizeEnvironment(&environment); err != nil {
399 » » return errors.Annotate(err).Reason("failed to normalize stamp en vironment").Err() 395 » » return errors.Annotate(err, "failed to normalize stamp environme nt").Err()
400 } 396 }
401 397
402 // If we are configured with an environment, validate that it matches th e 398 // If we are configured with an environment, validate that it matches th e
403 // the environment that we just loaded. 399 // the environment that we just loaded.
404 // 400 //
405 // We only consider our environment-defining fields (Spec and Runtime). 401 // We only consider our environment-defining fields (Spec and Runtime).
406 // 402 //
407 // Note that both environments will have been normalized at this point, so 403 // Note that both environments will have been normalized at this point, so
408 // comparison should be reliable. 404 // comparison should be reliable.
409 if e.Environment != nil { 405 if e.Environment != nil {
410 if !proto.Equal(e.Environment.Spec, environment.Spec) { 406 if !proto.Equal(e.Environment.Spec, environment.Spec) {
411 return errors.New("environment stamp specification does not match") 407 return errors.New("environment stamp specification does not match")
412 } 408 }
413 if !proto.Equal(e.Environment.Runtime, environment.Runtime) { 409 if !proto.Equal(e.Environment.Runtime, environment.Runtime) {
414 return errors.New("environment stamp runtime does not ma tch") 410 return errors.New("environment stamp runtime does not ma tch")
415 } 411 }
416 } 412 }
417 e.Environment = &environment 413 e.Environment = &environment
418 return nil 414 return nil
419 } 415 }
420 416
421 func (e *Env) assertComplete() error { 417 func (e *Env) assertComplete() error {
422 // Ensure that the environment has its completion flag. 418 // Ensure that the environment has its completion flag.
423 switch _, err := os.Stat(e.completeFlagPath); { 419 switch _, err := os.Stat(e.completeFlagPath); {
424 case filesystem.IsNotExist(err): 420 case filesystem.IsNotExist(err):
425 return ErrNotComplete 421 return ErrNotComplete
426 case err != nil: 422 case err != nil:
427 » » return errors.Annotate(err).Reason("failed to check for completi on flag").Err() 423 » » return errors.Annotate(err, "failed to check for completion flag ").Err()
428 default: 424 default:
429 return nil 425 return nil
430 } 426 }
431 } 427 }
432 428
433 func (e *Env) createLocked(c context.Context) error { 429 func (e *Env) createLocked(c context.Context) error {
434 // If our root directory already exists, delete it. 430 // If our root directory already exists, delete it.
435 if _, err := os.Stat(e.Root); err == nil { 431 if _, err := os.Stat(e.Root); err == nil {
436 logging.Infof(c, "Deleting existing VirtualEnv: %s", e.Root) 432 logging.Infof(c, "Deleting existing VirtualEnv: %s", e.Root)
437 if err := filesystem.RemoveAll(e.Root); err != nil { 433 if err := filesystem.RemoveAll(e.Root); err != nil {
438 return errors.Reason("failed to remove existing root").E rr() 434 return errors.Reason("failed to remove existing root").E rr()
439 } 435 }
440 } 436 }
441 437
442 // Make sure our environment's base directory exists. 438 // Make sure our environment's base directory exists.
443 if err := filesystem.MakeDirs(e.Root); err != nil { 439 if err := filesystem.MakeDirs(e.Root); err != nil {
444 » » return errors.Annotate(err).Reason("failed to create environment root").Err() 440 » » return errors.Annotate(err, "failed to create environment root") .Err()
445 } 441 }
446 logging.Infof(c, "Using virtual environment root: %s", e.Root) 442 logging.Infof(c, "Using virtual environment root: %s", e.Root)
447 443
448 // Build our package list. Always install our base VirtualEnv package. 444 // Build our package list. Always install our base VirtualEnv package.
449 packages := make([]*vpython.Spec_Package, 1, 1+len(e.Environment.Spec.Wh eel)) 445 packages := make([]*vpython.Spec_Package, 1, 1+len(e.Environment.Spec.Wh eel))
450 packages[0] = e.Environment.Spec.Virtualenv 446 packages[0] = e.Environment.Spec.Virtualenv
451 packages = append(packages, e.Environment.Spec.Wheel...) 447 packages = append(packages, e.Environment.Spec.Wheel...)
452 448
453 // Create a directory to bootstrap VirtualEnv from. 449 // Create a directory to bootstrap VirtualEnv from.
454 // 450 //
455 // This directory will be a very short-named temporary directory. This i s 451 // This directory will be a very short-named temporary directory. This i s
456 // because it really quickly runs up into traditional Windows path limit ations 452 // because it really quickly runs up into traditional Windows path limit ations
457 // when ZIP-importing sub-sub-sub-sub-packages (e.g., pip, requests, etc .). 453 // when ZIP-importing sub-sub-sub-sub-packages (e.g., pip, requests, etc .).
458 // 454 //
459 // We will clean this directory up on termination. 455 // We will clean this directory up on termination.
460 err := withTempDir(logging.Get(c), "vpython_bootstrap", func(bootstrapDi r string) error { 456 err := withTempDir(logging.Get(c), "vpython_bootstrap", func(bootstrapDi r string) error {
461 pkgDir := filepath.Join(bootstrapDir, "packages") 457 pkgDir := filepath.Join(bootstrapDir, "packages")
462 if err := filesystem.MakeDirs(pkgDir); err != nil { 458 if err := filesystem.MakeDirs(pkgDir); err != nil {
463 » » » return errors.Annotate(err).Reason("could not create boo tstrap packages directory").Err() 459 » » » return errors.Annotate(err, "could not create bootstrap packages directory").Err()
464 } 460 }
465 461
466 if err := e.downloadPackages(c, pkgDir, packages); err != nil { 462 if err := e.downloadPackages(c, pkgDir, packages); err != nil {
467 » » » return errors.Annotate(err).Reason("failed to download p ackages").Err() 463 » » » return errors.Annotate(err, "failed to download packages ").Err()
468 } 464 }
469 465
470 // Installing base VirtualEnv. 466 // Installing base VirtualEnv.
471 if err := e.installVirtualEnv(c, pkgDir); err != nil { 467 if err := e.installVirtualEnv(c, pkgDir); err != nil {
472 » » » return errors.Annotate(err).Reason("failed to install Vi rtualEnv").Err() 468 » » » return errors.Annotate(err, "failed to install VirtualEn v").Err()
473 } 469 }
474 470
475 // Load PEP425 tags, if we don't already have them. 471 // Load PEP425 tags, if we don't already have them.
476 if e.Environment.Pep425Tag == nil { 472 if e.Environment.Pep425Tag == nil {
477 pep425Tags, err := e.getPEP425Tags(c) 473 pep425Tags, err := e.getPEP425Tags(c)
478 if err != nil { 474 if err != nil {
479 » » » » return errors.Annotate(err).Reason("failed to ge t PEP425 tags").Err() 475 » » » » return errors.Annotate(err, "failed to get PEP42 5 tags").Err()
480 } 476 }
481 e.Environment.Pep425Tag = pep425Tags 477 e.Environment.Pep425Tag = pep425Tags
482 } 478 }
483 479
484 // Install our wheel files. 480 // Install our wheel files.
485 if len(e.Environment.Spec.Wheel) > 0 { 481 if len(e.Environment.Spec.Wheel) > 0 {
486 // Install wheels into our VirtualEnv. 482 // Install wheels into our VirtualEnv.
487 if err := e.installWheels(c, bootstrapDir, pkgDir); err != nil { 483 if err := e.installWheels(c, bootstrapDir, pkgDir); err != nil {
488 » » » » return errors.Annotate(err).Reason("failed to in stall wheels").Err() 484 » » » » return errors.Annotate(err, "failed to install w heels").Err()
489 } 485 }
490 } 486 }
491 return nil 487 return nil
492 }) 488 })
493 if err != nil { 489 if err != nil {
494 return err 490 return err
495 } 491 }
496 492
497 // Write our specification file. 493 // Write our specification file.
498 if err := e.WriteEnvironmentStamp(); err != nil { 494 if err := e.WriteEnvironmentStamp(); err != nil {
499 » » return errors.Annotate(err).Reason("failed to write environment stamp file to: %(path)s"). 495 » » return errors.Annotate(err, "failed to write environment stamp f ile to: %s",
500 » » » D("path", e.EnvironmentStampPath). 496 » » » e.EnvironmentStampPath).Err()
501 » » » Err()
502 } 497 }
503 logging.Debugf(c, "Wrote environment stamp file to: %s", e.EnvironmentSt ampPath) 498 logging.Debugf(c, "Wrote environment stamp file to: %s", e.EnvironmentSt ampPath)
504 499
505 // Finalize our VirtualEnv for bootstrap execution. 500 // Finalize our VirtualEnv for bootstrap execution.
506 if err := e.finalize(c); err != nil { 501 if err := e.finalize(c); err != nil {
507 » » return errors.Annotate(err).Reason("failed to prepare VirtualEnv ").Err() 502 » » return errors.Annotate(err, "failed to prepare VirtualEnv").Err( )
508 } 503 }
509 504
510 return nil 505 return nil
511 } 506 }
512 507
513 func (e *Env) downloadPackages(c context.Context, dst string, packages []*vpytho n.Spec_Package) error { 508 func (e *Env) downloadPackages(c context.Context, dst string, packages []*vpytho n.Spec_Package) error {
514 // Create a wheel sub-directory underneath of root. 509 // Create a wheel sub-directory underneath of root.
515 logging.Debugf(c, "Loading %d package(s) into: %s", len(packages), dst) 510 logging.Debugf(c, "Loading %d package(s) into: %s", len(packages), dst)
516 if err := e.Config.Loader.Ensure(c, dst, packages); err != nil { 511 if err := e.Config.Loader.Ensure(c, dst, packages); err != nil {
517 » » return errors.Annotate(err).Reason("failed to download packages" ).Err() 512 » » return errors.Annotate(err, "failed to download packages").Err()
518 } 513 }
519 return nil 514 return nil
520 } 515 }
521 516
522 func (e *Env) installVirtualEnv(c context.Context, pkgDir string) error { 517 func (e *Env) installVirtualEnv(c context.Context, pkgDir string) error {
523 // Create our VirtualEnv package staging sub-directory underneath of roo t. 518 // Create our VirtualEnv package staging sub-directory underneath of roo t.
524 bsDir := filepath.Join(e.Root, ".virtualenv") 519 bsDir := filepath.Join(e.Root, ".virtualenv")
525 if err := filesystem.MakeDirs(bsDir); err != nil { 520 if err := filesystem.MakeDirs(bsDir); err != nil {
526 » » return errors.Annotate(err).Reason("failed to create VirtualEnv bootstrap directory"). 521 » » return errors.Annotate(err, "failed to create VirtualEnv bootstr ap directory").
527 » » » D("path", bsDir). 522 » » » InternalReason("path(%s)", bsDir).Err()
528 » » » Err()
529 } 523 }
530 524
531 // Identify the virtualenv directory: will have "virtualenv-" prefix. 525 // Identify the virtualenv directory: will have "virtualenv-" prefix.
532 matches, err := filepath.Glob(filepath.Join(pkgDir, "virtualenv-*")) 526 matches, err := filepath.Glob(filepath.Join(pkgDir, "virtualenv-*"))
533 if err != nil { 527 if err != nil {
534 » » return errors.Annotate(err).Reason("failed to glob for 'virtuale nv-' directory").Err() 528 » » return errors.Annotate(err, "failed to glob for 'virtualenv-' di rectory").Err()
535 } 529 }
536 if len(matches) == 0 { 530 if len(matches) == 0 {
537 return errors.Reason("no 'virtualenv-' directory provided by pac kage").Err() 531 return errors.Reason("no 'virtualenv-' directory provided by pac kage").Err()
538 } 532 }
539 venvDir := matches[0] 533 venvDir := matches[0]
540 534
541 logging.Debugf(c, "Creating VirtualEnv at: %s", e.Root) 535 logging.Debugf(c, "Creating VirtualEnv at: %s", e.Root)
542 cmd := e.Config.systemInterpreter().IsolatedCommand(c, 536 cmd := e.Config.systemInterpreter().IsolatedCommand(c,
543 "virtualenv.py", 537 "virtualenv.py",
544 "--no-download", 538 "--no-download",
545 e.Root) 539 e.Root)
546 cmd.Dir = venvDir 540 cmd.Dir = venvDir
547 attachOutputForLogging(c, logging.Debug, cmd) 541 attachOutputForLogging(c, logging.Debug, cmd)
548 if err := cmd.Run(); err != nil { 542 if err := cmd.Run(); err != nil {
549 » » return errors.Annotate(err).Reason("failed to create VirtualEnv" ).Err() 543 » » return errors.Annotate(err, "failed to create VirtualEnv").Err()
550 } 544 }
551 545
552 logging.Debugf(c, "Making VirtualEnv relocatable at: %s", e.Root) 546 logging.Debugf(c, "Making VirtualEnv relocatable at: %s", e.Root)
553 cmd = e.Interpreter().IsolatedCommand(c, 547 cmd = e.Interpreter().IsolatedCommand(c,
554 "virtualenv.py", 548 "virtualenv.py",
555 "--relocatable", 549 "--relocatable",
556 e.Root) 550 e.Root)
557 cmd.Dir = venvDir 551 cmd.Dir = venvDir
558 attachOutputForLogging(c, logging.Debug, cmd) 552 attachOutputForLogging(c, logging.Debug, cmd)
559 if err := cmd.Run(); err != nil { 553 if err := cmd.Run(); err != nil {
560 » » return errors.Annotate(err).Reason("failed to create VirtualEnv" ).Err() 554 » » return errors.Annotate(err, "failed to create VirtualEnv").Err()
561 } 555 }
562 556
563 return nil 557 return nil
564 } 558 }
565 559
566 // getPEP425Tags calls Python's pip.pep425tags package to retrieve the tags. 560 // getPEP425Tags calls Python's pip.pep425tags package to retrieve the tags.
567 // 561 //
568 // This must be run while "pip" is installed in the VirtualEnv. 562 // This must be run while "pip" is installed in the VirtualEnv.
569 func (e *Env) getPEP425Tags(c context.Context) ([]*vpython.PEP425Tag, error) { 563 func (e *Env) getPEP425Tags(c context.Context) ([]*vpython.PEP425Tag, error) {
570 // This script will return a list of 3-entry lists: 564 // This script will return a list of 3-entry lists:
571 // [0]: version (e.g., "cp27") 565 // [0]: version (e.g., "cp27")
572 // [1]: abi (e.g., "cp27mu", "none") 566 // [1]: abi (e.g., "cp27mu", "none")
573 // [2]: arch (e.g., "x86_64", "armv7l", "any") 567 // [2]: arch (e.g., "x86_64", "armv7l", "any")
574 const script = `import json;` + 568 const script = `import json;` +
575 `import pip.pep425tags;` + 569 `import pip.pep425tags;` +
576 `import sys;` + 570 `import sys;` +
577 `sys.stdout.write(json.dumps(pip.pep425tags.get_supported()))` 571 `sys.stdout.write(json.dumps(pip.pep425tags.get_supported()))`
578 type pep425TagEntry []string 572 type pep425TagEntry []string
579 573
580 cmd := e.Interpreter().IsolatedCommand(c, "-c", script) 574 cmd := e.Interpreter().IsolatedCommand(c, "-c", script)
581 575
582 var stdout bytes.Buffer 576 var stdout bytes.Buffer
583 cmd.Stdout = &stdout 577 cmd.Stdout = &stdout
584 578
585 attachOutputForLogging(c, logging.Debug, cmd) 579 attachOutputForLogging(c, logging.Debug, cmd)
586 if err := cmd.Run(); err != nil { 580 if err := cmd.Run(); err != nil {
587 » » return nil, errors.Annotate(err).Reason("failed to get PEP425 ta gs").Err() 581 » » return nil, errors.Annotate(err, "failed to get PEP425 tags").Er r()
588 } 582 }
589 583
590 var tagEntries []pep425TagEntry 584 var tagEntries []pep425TagEntry
591 if err := json.Unmarshal(stdout.Bytes(), &tagEntries); err != nil { 585 if err := json.Unmarshal(stdout.Bytes(), &tagEntries); err != nil {
592 » » return nil, errors.Annotate(err).Reason("failed to unmarshal PEP 425 tag output: %(output)s"). 586 » » return nil, errors.Annotate(err, "failed to unmarshal PEP425 tag output: %s", stdout).Err()
593 » » » D("output", stdout.String()).
594 » » » Err()
595 } 587 }
596 588
597 tags := make([]*vpython.PEP425Tag, len(tagEntries)) 589 tags := make([]*vpython.PEP425Tag, len(tagEntries))
598 for i, te := range tagEntries { 590 for i, te := range tagEntries {
599 if len(te) != 3 { 591 if len(te) != 3 {
600 » » » return nil, errors.Reason("invalid PEP425 tag entry: %(e ntry)v"). 592 » » » return nil, errors.Reason("invalid PEP425 tag entry: %v" , te).
601 » » » » D("entry", te). 593 » » » » InternalReason("index(%d)", i).Err()
602 » » » » D("index", i).
603 » » » » Err()
604 } 594 }
605 595
606 tags[i] = &vpython.PEP425Tag{ 596 tags[i] = &vpython.PEP425Tag{
607 Python: te[0], 597 Python: te[0],
608 Abi: te[1], 598 Abi: te[1],
609 Platform: te[2], 599 Platform: te[2],
610 } 600 }
611 } 601 }
612 602
613 // If we're Debug-logging, calculate and display the tags that were prob ed. 603 // If we're Debug-logging, calculate and display the tags that were prob ed.
614 if logging.IsLogging(c, logging.Debug) { 604 if logging.IsLogging(c, logging.Debug) {
615 tagStr := make([]string, len(tags)) 605 tagStr := make([]string, len(tags))
616 for i, t := range tags { 606 for i, t := range tags {
617 tagStr[i] = t.TagString() 607 tagStr[i] = t.TagString()
618 } 608 }
619 logging.Debugf(c, "Loaded PEP425 tags: [%s]", strings.Join(tagSt r, ", ")) 609 logging.Debugf(c, "Loaded PEP425 tags: [%s]", strings.Join(tagSt r, ", "))
620 } 610 }
621 611
622 return tags, nil 612 return tags, nil
623 } 613 }
624 614
625 func (e *Env) installWheels(c context.Context, bootstrapDir, pkgDir string) erro r { 615 func (e *Env) installWheels(c context.Context, bootstrapDir, pkgDir string) erro r {
626 // Identify all downloaded wheels and parse them. 616 // Identify all downloaded wheels and parse them.
627 wheels, err := wheel.ScanDir(pkgDir) 617 wheels, err := wheel.ScanDir(pkgDir)
628 if err != nil { 618 if err != nil {
629 » » return errors.Annotate(err).Reason("failed to load wheels").Err( ) 619 » » return errors.Annotate(err, "failed to load wheels").Err()
630 } 620 }
631 621
632 // Build a "wheel" requirements file. 622 // Build a "wheel" requirements file.
633 reqPath := filepath.Join(bootstrapDir, "requirements.txt") 623 reqPath := filepath.Join(bootstrapDir, "requirements.txt")
634 logging.Debugf(c, "Rendering requirements file to: %s", reqPath) 624 logging.Debugf(c, "Rendering requirements file to: %s", reqPath)
635 if err := wheel.WriteRequirementsFile(reqPath, wheels); err != nil { 625 if err := wheel.WriteRequirementsFile(reqPath, wheels); err != nil {
636 » » return errors.Annotate(err).Reason("failed to render requirement s file").Err() 626 » » return errors.Annotate(err, "failed to render requirements file" ).Err()
637 } 627 }
638 628
639 cmd := e.Interpreter().IsolatedCommand(c, 629 cmd := e.Interpreter().IsolatedCommand(c,
640 "-m", "pip", 630 "-m", "pip",
641 "install", 631 "install",
642 "--use-wheel", 632 "--use-wheel",
643 "--compile", 633 "--compile",
644 "--no-index", 634 "--no-index",
645 "--find-links", pkgDir, 635 "--find-links", pkgDir,
646 "--requirement", reqPath) 636 "--requirement", reqPath)
647 attachOutputForLogging(c, logging.Debug, cmd) 637 attachOutputForLogging(c, logging.Debug, cmd)
648 if err := cmd.Run(); err != nil { 638 if err := cmd.Run(); err != nil {
649 » » return errors.Annotate(err).Reason("failed to install wheels").E rr() 639 » » return errors.Annotate(err, "failed to install wheels").Err()
650 } 640 }
651 return nil 641 return nil
652 } 642 }
653 643
654 func (e *Env) finalize(c context.Context) error { 644 func (e *Env) finalize(c context.Context) error {
655 // Change all files to read-only, except: 645 // Change all files to read-only, except:
656 // - Our root directory, which must be writable in order to update our 646 // - Our root directory, which must be writable in order to update our
657 // completion flag. 647 // completion flag.
658 // - Our environment stamp, which must be trivially re-writable. 648 // - Our environment stamp, which must be trivially re-writable.
659 if !e.Config.testLeaveReadWrite { 649 if !e.Config.testLeaveReadWrite {
660 err := filesystem.MakeReadOnly(e.Root, func(path string) bool { 650 err := filesystem.MakeReadOnly(e.Root, func(path string) bool {
661 switch path { 651 switch path {
662 case e.Root, e.completeFlagPath: 652 case e.Root, e.completeFlagPath:
663 return false 653 return false
664 default: 654 default:
665 return true 655 return true
666 } 656 }
667 }) 657 })
668 if err != nil { 658 if err != nil {
669 » » » return errors.Annotate(err).Reason("failed to mark envir onment read-only").Err() 659 » » » return errors.Annotate(err, "failed to mark environment read-only").Err()
670 } 660 }
671 } 661 }
672 return nil 662 return nil
673 } 663 }
674 664
675 func (e *Env) touchCompleteFlagLocked() error { 665 func (e *Env) touchCompleteFlagLocked() error {
676 if err := filesystem.Touch(e.completeFlagPath, time.Time{}, 0644); err ! = nil { 666 if err := filesystem.Touch(e.completeFlagPath, time.Time{}, 0644); err ! = nil {
677 » » return errors.Annotate(err).Err() 667 » » return errors.Annotate(err, "").Err()
678 } 668 }
679 return nil 669 return nil
680 } 670 }
681 671
682 // Delete removes all resources consumed by an environment. 672 // Delete removes all resources consumed by an environment.
683 // 673 //
684 // Delete will acquire an exclusive lock on the environment and assert that it 674 // Delete will acquire an exclusive lock on the environment and assert that it
685 // is not in use prior to deletion. This is non-blocking, and an error will be 675 // is not in use prior to deletion. This is non-blocking, and an error will be
686 // returned if the lock could not be acquired. 676 // returned if the lock could not be acquired.
687 // 677 //
688 // If the environment was not deleted, a non-nil wrapped error will be returned. 678 // If the environment was not deleted, a non-nil wrapped error will be returned.
689 // If the deletion failed because the lock was held, a wrapped 679 // If the deletion failed because the lock was held, a wrapped
690 // fslock.ErrLockHeld will be returned. 680 // fslock.ErrLockHeld will be returned.
691 func (e *Env) Delete(c context.Context) error { 681 func (e *Env) Delete(c context.Context) error {
692 removedLock := false 682 removedLock := false
693 err := e.withExclusiveLockNonBlocking(func() error { 683 err := e.withExclusiveLockNonBlocking(func() error {
694 logging.Debugf(c, "(Delete) Got exclusive lock for: %s", e.Name) 684 logging.Debugf(c, "(Delete) Got exclusive lock for: %s", e.Name)
695 685
696 // Delete our environment directory. 686 // Delete our environment directory.
697 if err := filesystem.RemoveAll(e.Root); err != nil { 687 if err := filesystem.RemoveAll(e.Root); err != nil {
698 » » » return errors.Annotate(err).Reason("failed to delete env ironment root").Err() 688 » » » return errors.Annotate(err, "failed to delete environmen t root").Err()
699 } 689 }
700 690
701 // Attempt to delete our lock. On POSIX systems, this will succe ssfully 691 // Attempt to delete our lock. On POSIX systems, this will succe ssfully
702 // delete the lock. On Windows systems, there will be contention , since the 692 // delete the lock. On Windows systems, there will be contention , since the
703 // lock is held by us. 693 // lock is held by us.
704 // 694 //
705 // In this case, we'll try again to delete it after we release t he lock. 695 // In this case, we'll try again to delete it after we release t he lock.
706 // If someone else takes out the lock in between, the delete wil l similarly 696 // If someone else takes out the lock in between, the delete wil l similarly
707 // fail. 697 // fail.
708 if err := os.Remove(e.lockPath); err == nil { 698 if err := os.Remove(e.lockPath); err == nil {
709 removedLock = true 699 removedLock = true
710 } else { 700 } else {
711 logging.WithError(err).Debugf(c, "failed to delete lock file while holding lock: %s", e.lockPath) 701 logging.WithError(err).Debugf(c, "failed to delete lock file while holding lock: %s", e.lockPath)
712 } 702 }
713 return nil 703 return nil
714 }) 704 })
715 logging.Debugf(c, "(Delete) Released exclusive lock for: %s", e.Name) 705 logging.Debugf(c, "(Delete) Released exclusive lock for: %s", e.Name)
716 706
717 if err != nil { 707 if err != nil {
718 » » return errors.Annotate(err).Reason("failed to delete environment ").Err() 708 » » return errors.Annotate(err, "failed to delete environment").Err( )
719 } 709 }
720 710
721 // Try and remove the lock now that we don't hold it. 711 // Try and remove the lock now that we don't hold it.
722 if !removedLock { 712 if !removedLock {
723 if err := os.Remove(e.lockPath); err != nil { 713 if err := os.Remove(e.lockPath); err != nil {
724 » » » return errors.Annotate(err).Reason("failed to remove loc k").Err() 714 » » » return errors.Annotate(err, "failed to remove lock").Err ()
725 } 715 }
726 } 716 }
727 return nil 717 return nil
728 } 718 }
729 719
730 // completionFlagTimestamp returns the timestamp on the environment's completion 720 // completionFlagTimestamp returns the timestamp on the environment's completion
731 // flag. 721 // flag.
732 // 722 //
733 // If the completion flag does not exist (incomplete environment), a zero time 723 // If the completion flag does not exist (incomplete environment), a zero time
734 // will be returned with no error. 724 // will be returned with no error.
735 // 725 //
736 // If an error is encountered while checking for the timestamp, it will be 726 // If an error is encountered while checking for the timestamp, it will be
737 // returned. 727 // returned.
738 func (e *Env) completionFlagTimestamp() (time.Time, error) { 728 func (e *Env) completionFlagTimestamp() (time.Time, error) {
739 // Read the complete flag file's timestamp. 729 // Read the complete flag file's timestamp.
740 switch st, err := os.Stat(e.completeFlagPath); { 730 switch st, err := os.Stat(e.completeFlagPath); {
741 case err == nil: 731 case err == nil:
742 return st.ModTime(), nil 732 return st.ModTime(), nil
743 733
744 case os.IsNotExist(err): 734 case os.IsNotExist(err):
745 return time.Time{}, nil 735 return time.Time{}, nil
746 736
747 default: 737 default:
748 » » return time.Time{}, errors.Annotate(err).Reason("failed to stat completion flag: %(path)s"). 738 » » return time.Time{}, errors.Annotate(err, "failed to stat complet ion flag: %s",
749 » » » D("path", e.completeFlagPath). 739 » » » e.completeFlagPath).Err()
750 » » » Err()
751 } 740 }
752 } 741 }
753 742
754 func attachOutputForLogging(c context.Context, l logging.Level, cmd *exec.Cmd) { 743 func attachOutputForLogging(c context.Context, l logging.Level, cmd *exec.Cmd) {
755 if logging.IsLogging(c, logging.Info) { 744 if logging.IsLogging(c, logging.Info) {
756 logging.Infof(c, "Running Python command (cwd=%s): %s", 745 logging.Infof(c, "Running Python command (cwd=%s): %s",
757 cmd.Dir, strings.Join(cmd.Args, " ")) 746 cmd.Dir, strings.Join(cmd.Args, " "))
758 } 747 }
759 748
760 if logging.IsLogging(c, l) { 749 if logging.IsLogging(c, l) {
761 if cmd.Stdout == nil { 750 if cmd.Stdout == nil {
762 cmd.Stdout = os.Stdout 751 cmd.Stdout = os.Stdout
763 } 752 }
764 if cmd.Stderr == nil { 753 if cmd.Stderr == nil {
765 cmd.Stderr = os.Stderr 754 cmd.Stderr = os.Stderr
766 } 755 }
767 } 756 }
768 } 757 }
769 758
770 // mustReleaseLock calls the wrapped function, releasing the lock at the end 759 // mustReleaseLock calls the wrapped function, releasing the lock at the end
771 // of its execution. If the lock could not be released, this function will 760 // of its execution. If the lock could not be released, this function will
772 // panic, since the locking state can no longer be determined. 761 // panic, since the locking state can no longer be determined.
773 func mustReleaseLock(c context.Context, lock fslock.Handle, fn func() error) err or { 762 func mustReleaseLock(c context.Context, lock fslock.Handle, fn func() error) err or {
774 defer func() { 763 defer func() {
775 if err := lock.Unlock(); err != nil { 764 if err := lock.Unlock(); err != nil {
776 » » » errors.Log(c, errors.Annotate(err).Reason("failed to rel ease lock").Err()) 765 » » » errors.Log(c, errors.Annotate(err, "failed to release lo ck").Err())
777 panic(err) 766 panic(err)
778 } 767 }
779 }() 768 }()
780 return fn() 769 return fn()
781 } 770 }
OLDNEW
« no previous file with comments | « vpython/venv/util.go ('k') | vpython/venv/venv_resources_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698