Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 The LUCI Authors. All rights reserved. | |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | |
| 3 // that can be found in the LICENSE file. | |
| 4 | |
| 5 package vpython | |
| 6 | |
| 7 import ( | |
| 8 "context" | |
| 9 "os" | |
| 10 | |
| 11 "github.com/luci/luci-go/vpython/filesystem" | |
| 12 "github.com/luci/luci-go/vpython/python" | |
| 13 "github.com/luci/luci-go/vpython/spec" | |
| 14 "github.com/luci/luci-go/vpython/venv" | |
| 15 | |
| 16 "github.com/luci/luci-go/common/errors" | |
| 17 "github.com/luci/luci-go/common/logging" | |
| 18 "github.com/luci/luci-go/common/system/environ" | |
| 19 ) | |
| 20 | |
| 21 // Options is the set of options to use to construct and execute a VirtualEnv | |
| 22 // Python application. | |
| 23 type Options struct { | |
| 24 // EnvConfig is the VirtualEnv configuration to run from. | |
| 25 EnvConfig venv.Config | |
| 26 | |
| 27 // Args are the arguments to forward to the Python process. | |
| 28 Args []string | |
| 29 | |
| 30 // WaitForEnv, if true, means that if another agent holds a lock on the target | |
| 31 // environment, we will wait until it is available. If false, we will | |
| 32 // immediately exit Setup with an error. | |
| 33 WaitForEnv bool | |
| 34 | |
| 35 // WorkDir is the Python working directory. If empty, the current workin g | |
| 36 // directory will be used. | |
| 37 // | |
| 38 // If EnvRoot is empty, WorkDir will be used as the base enviornment roo t. | |
| 39 WorkDir string | |
| 40 | |
| 41 // Environ is the system environment. It can be used for some default va lues | |
| 42 // if present. | |
| 43 Environ environ.Env | |
| 44 } | |
| 45 | |
| 46 func (o *Options) resolve(c context.Context) error { | |
| 47 // Resolve our working directory to an absolute path. | |
| 48 if o.WorkDir == "" { | |
| 49 wd, err := os.Getwd() | |
| 50 if err != nil { | |
| 51 return errors.Annotate(err).Reason("failed to get workin g directory").Err() | |
| 52 } | |
| 53 o.WorkDir = wd | |
| 54 } | |
| 55 if err := filesystem.AbsPath(&o.WorkDir); err != nil { | |
| 56 return errors.Annotate(err).Reason("failed to resolve absolute p ath of WorkDir").Err() | |
| 57 } | |
| 58 | |
| 59 // Resolve our target python script. | |
| 60 if err := o.resolveEnvSpec(c); err != nil { | |
| 61 return errors.Annotate(err).Reason("failed to resolve Python scr ipt").Err() | |
| 62 } | |
| 63 | |
| 64 // If no enviornment base directory was supplied, create one under the c urrent | |
| 65 // working directory. | |
| 66 if o.EnvConfig.BaseDir == "" { | |
| 67 // Is one specified in our environment? | |
| 68 if v, ok := o.Environ.Get(EnvSpecPath); ok { | |
| 69 var err error | |
| 70 if o.EnvConfig.BaseDir, err = venv.EnvRootFromSpecPath(v ); err != nil { | |
| 71 return errors.Annotate(err).Reason("failed to ge t env root from environment: %(path)s"). | |
| 72 D("path", v). | |
| 73 Err() | |
| 74 } | |
| 75 logging.Debugf(c, "Loaded environment root from environm ent variable: %s", o.EnvConfig.BaseDir) | |
| 76 } | |
| 77 } | |
| 78 return nil | |
| 79 } | |
| 80 | |
| 81 func (o *Options) resolveEnvSpec(c context.Context) error { | |
| 82 cmd, err := python.ParseCommandLine(o.Args) | |
| 83 if err != nil { | |
| 84 return errors.Annotate(err).Reason("failed to parse Python comma nd-line").Err() | |
| 85 } | |
| 86 | |
| 87 // If we're running a Python script, assert that the target script exist s. | |
| 88 // Additionally, track whether its a file or a module (directory). | |
|
iannucci
2017/02/23 02:15:40
it's
dnj
2017/03/11 17:49:29
Done.
| |
| 89 isModule := false | |
| 90 if cmd.Type == python.TargetScript { | |
| 91 logging.Debugf(c, "Resolved Python target script: %s", cmd.Value ) | |
| 92 | |
| 93 // Resolve to absolute script path. | |
| 94 if err := filesystem.AbsPath(&cmd.Value); err != nil { | |
| 95 return errors.Annotate(err).Reason("failed to get absolu te path of: %(path)s"). | |
| 96 D("path", cmd.Value). | |
| 97 Err() | |
| 98 } | |
| 99 | |
| 100 // Confirm that the script path actually exists. | |
| 101 st, err := os.Stat(cmd.Value) | |
|
iannucci
2017/02/23 02:15:40
does s/Value/RunnableTarget make sense? or somethi
dnj
2017/03/11 17:49:29
Changed to "Target", went with the type assertion
| |
| 102 if err != nil { | |
| 103 return errors.Annotate(err).Reason("failed to stat Pytho n script: %(path)s"). | |
| 104 D("path", cmd.Value). | |
| 105 Err() | |
| 106 } | |
| 107 | |
| 108 // If the script is a directory, then we assume that we're doing a module | |
| 109 // invocation (__main__.py). | |
| 110 isModule = st.IsDir() | |
| 111 } | |
| 112 | |
| 113 // If a spec is explicitly provided, we're done. | |
| 114 if o.EnvConfig.Spec != nil { | |
| 115 return nil | |
| 116 } | |
| 117 | |
| 118 // If it's a script, try resolving from filesystem first. | |
| 119 if cmd.Type == python.TargetScript { | |
| 120 spec, err := spec.LoadForScript(c, cmd.Value, isModule) | |
| 121 if err != nil { | |
| 122 return errors.Annotate(err).Reason("failed to load spec for script: %(path)s"). | |
| 123 D("path", cmd.Value). | |
| 124 D("isModule", isModule). | |
| 125 Err() | |
| 126 } | |
| 127 if spec != nil { | |
| 128 o.EnvConfig.Spec = spec | |
| 129 return nil | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 // Do we have a spec file in the enviornment? | |
| 134 if v, ok := o.Environ.Get(EnvSpecPath); ok { | |
| 135 if o.EnvConfig.Spec, err = spec.Load(v); err != nil { | |
| 136 return errors.Annotate(err).Reason("failed to load envio rnment-supplied spec from: %(path)s"). | |
| 137 D("path", v). | |
| 138 Err() | |
| 139 } | |
| 140 logging.Infof(c, "Loaded spec from environment: %s", v) | |
| 141 return nil | |
| 142 } | |
| 143 | |
| 144 // Unable to load a spec. | |
| 145 logging.Infof(c, "Unable to resolve specification path. Using empty spec ification.") | |
|
iannucci
2017/02/23 02:15:40
Warn?
dnj
2017/03/11 17:49:29
Nah, no specification is totally legit, just means
| |
| 146 return nil | |
| 147 } | |
| OLD | NEW |