| 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/python" |
| 12 "github.com/luci/luci-go/vpython/spec" |
| 13 "github.com/luci/luci-go/vpython/venv" |
| 14 |
| 15 "github.com/luci/luci-go/common/errors" |
| 16 "github.com/luci/luci-go/common/logging" |
| 17 "github.com/luci/luci-go/common/system/environ" |
| 18 "github.com/luci/luci-go/common/system/filesystem" |
| 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 it's a file or a module (directory). |
| 89 script, isScriptTarget := cmd.Target.(python.ScriptTarget) |
| 90 isModule := false |
| 91 if isScriptTarget { |
| 92 logging.Debugf(c, "Resolved Python target script: %s", cmd.Targe
t) |
| 93 |
| 94 // Resolve to absolute script path. |
| 95 if err := filesystem.AbsPath(&script.Path); err != nil { |
| 96 return errors.Annotate(err).Reason("failed to get absolu
te path of: %(path)s"). |
| 97 D("path", cmd.Target). |
| 98 Err() |
| 99 } |
| 100 |
| 101 // Confirm that the script path actually exists. |
| 102 st, err := os.Stat(script.Path) |
| 103 if err != nil { |
| 104 return errors.Annotate(err).Reason("failed to stat Pytho
n script: %(path)s"). |
| 105 D("path", cmd.Target). |
| 106 Err() |
| 107 } |
| 108 |
| 109 // If the script is a directory, then we assume that we're doing
a module |
| 110 // invocation (__main__.py). |
| 111 isModule = st.IsDir() |
| 112 } |
| 113 |
| 114 // If a spec is explicitly provided, we're done. |
| 115 if o.EnvConfig.Spec != nil { |
| 116 return nil |
| 117 } |
| 118 |
| 119 // If it's a script, try resolving from filesystem first. |
| 120 if isScriptTarget { |
| 121 spec, err := spec.LoadForScript(c, script.Path, isModule) |
| 122 if err != nil { |
| 123 return errors.Annotate(err).Reason("failed to load spec
for script: %(path)s"). |
| 124 D("path", cmd.Target). |
| 125 D("isModule", isModule). |
| 126 Err() |
| 127 } |
| 128 if spec != nil { |
| 129 o.EnvConfig.Spec = spec |
| 130 return nil |
| 131 } |
| 132 } |
| 133 |
| 134 // Do we have a spec file in the enviornment? |
| 135 if v, ok := o.Environ.Get(EnvSpecPath); ok { |
| 136 if o.EnvConfig.Spec, err = spec.Load(v); err != nil { |
| 137 return errors.Annotate(err).Reason("failed to load envio
rnment-supplied spec from: %(path)s"). |
| 138 D("path", v). |
| 139 Err() |
| 140 } |
| 141 logging.Infof(c, "Loaded spec from environment: %s", v) |
| 142 return nil |
| 143 } |
| 144 |
| 145 // Unable to load a spec. |
| 146 logging.Infof(c, "Unable to resolve specification path. Using empty spec
ification.") |
| 147 return nil |
| 148 } |
| OLD | NEW |