go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/luciexe/host/options.go (about) 1 // Copyright 2019 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package host 16 17 import ( 18 "io/ioutil" 19 "os" 20 "path/filepath" 21 "runtime" 22 23 "go.chromium.org/luci/auth" 24 "go.chromium.org/luci/auth/authctx" 25 bbpb "go.chromium.org/luci/buildbucket/proto" 26 "go.chromium.org/luci/common/errors" 27 "go.chromium.org/luci/common/logging" 28 "go.chromium.org/luci/hardcoded/chromeinfra" 29 ldOutput "go.chromium.org/luci/logdog/client/butler/output" 30 "go.chromium.org/luci/logdog/client/butler/output/null" 31 "go.chromium.org/luci/logdog/client/butlerlib/streamproto" 32 "go.chromium.org/luci/logdog/common/viewer" 33 ) 34 35 // Options is an optional struct which allows you to control how Run operates. 36 type Options struct { 37 // Where the butler will sink its data to. 38 // 39 // This is typically one of the implementations in 40 // go.chromium.org/luci/logdog/client/butler/output. 41 // 42 // If nil, will use the 'null' logdog Output. 43 LogdogOutput ldOutput.Output 44 45 // Butler is VERY noisy at debug level, potentially amplifying client writes 46 // by up to two orders of magnitude. 47 // 48 // If this is higher than the log level in the context, this will be applied 49 // to the butler agent. 50 ButlerLogLevel logging.Level 51 52 // If set, enables logging at context level for the butler streamserver. 53 // If unset (the default), logging in the butler streamserver is set to 54 // Warning. 55 // 56 // Streamsever logging is generally redundant with the butler logs at level 57 // Info or Debug. 58 StreamServerDisableLogAdjustment bool 59 60 // ExeAuth describes the LUCI Auth environment to run the user code within. 61 // 62 // `Run` will manage the lifecycle of ExeAuth entirely. 63 // 64 // If nil, defaults to `DefaultExeAuth("luciexe", nil)`. 65 // 66 // It's recommended to use DefaultExeAuth() explicitly with reasonable values 67 // for `id` and `knownGerritHosts`. 68 ExeAuth *authctx.Context 69 70 // The BaseDir becomes the root of this hosted luciexe session; All 71 // directories (workdirs, tempdirs, etc.) are derived relative to this. 72 // 73 // If not provided, Run will pick a random directory under os.TempDir as the 74 // value for BaseDir. 75 // 76 // The BaseDir (provided or picked) will be managed by Run; Prior to 77 // execution, Run will ensure that the directory is empty by removing its 78 // contents. 79 BaseDir string 80 81 // The base Build message to use as the template for all merged Build 82 // messages. 83 // 84 // This will add logdog tags based on Build.Builder to all log streams. 85 // e.g., "buildbucket.bucket" 86 BaseBuild *bbpb.Build 87 88 // If LeakBaseDir is true, Run will not try to remove BaseDir at the end if 89 // it's execution. 90 // 91 // If BaseDir is not provided, this must be false. 92 LeakBaseDir bool 93 94 // The viewer URL for this hosted execution (if any). This will be used to 95 // apply viewer.LogdogViewerURLTag to all logdog streams (the tag which is 96 // used to implement the "Back to build" link in Milo). 97 ViewerURL string 98 99 authDir string 100 lucictxDir string 101 streamServerPath string 102 103 logdogTags streamproto.TagMap 104 } 105 106 // The function below is in `var name = func` form so that it shows up in godoc. 107 108 // DefaultExeAuth returns a copy of the default value for Options.ExeAuth. 109 var DefaultExeAuth = func(id string, knownGerritHosts []string) *authctx.Context { 110 return &authctx.Context{ 111 ID: id, 112 Options: chromeinfra.SetDefaultAuthOptions(auth.Options{ 113 Scopes: []string{ 114 "https://www.googleapis.com/auth/cloud-platform", 115 "https://www.googleapis.com/auth/userinfo.email", 116 "https://www.googleapis.com/auth/gerritcodereview", 117 "https://www.googleapis.com/auth/firebase", 118 }, 119 }), 120 EnableGitAuth: true, 121 EnableGCEEmulation: true, 122 EnableDockerAuth: true, 123 EnableFirebaseAuth: true, 124 KnownGerritHosts: knownGerritHosts, 125 } 126 } 127 128 type pathToMake struct { 129 path string // unique directory name under BaseDir 130 name string // human readable name of this dir (i.e. it's purpose) 131 dest *string // output destination in Options struct 132 } 133 134 func (p pathToMake) create(base string) error { 135 *p.dest = filepath.Join(base, p.path) 136 return errors.Annotate(os.Mkdir(*p.dest, 0777), "making %q dir", p.name).Err() 137 } 138 139 func (o *Options) initialize() (err error) { 140 if o.BaseDir == "" { 141 if o.LeakBaseDir { 142 return errors.New("BaseDir was provided but LeakBaseDir == true") 143 } 144 o.BaseDir, err = ioutil.TempDir("", "luciexe-host-") 145 if err != nil { 146 return errors.Annotate(err, "Cannot create BaseDir").Err() 147 } 148 } else { 149 if o.BaseDir, err = filepath.Abs(o.BaseDir); err != nil { 150 return errors.Annotate(err, "resolving BaseDir").Err() 151 } 152 if err := os.RemoveAll(o.BaseDir); err != nil && !os.IsNotExist(err) { 153 return errors.Annotate(err, "clearing options.BaseDir").Err() 154 } 155 if err := os.Mkdir(o.BaseDir, 0777); err != nil { 156 return errors.Annotate(err, "creating options.BaseDir").Err() 157 } 158 } 159 160 if o.BaseBuild == nil { 161 o.BaseBuild = &bbpb.Build{} 162 } 163 164 pathsToMake := []pathToMake{ 165 {"a", "auth", &o.authDir}, 166 {"l", "luci context", &o.lucictxDir}, 167 } 168 169 if runtime.GOOS != "windows" { 170 pathsToMake = append( 171 pathsToMake, pathToMake{"ld", "logdog socket", &o.streamServerPath}) 172 } else { 173 o.streamServerPath = "luciexe/host" 174 } 175 176 merr := errors.NewLazyMultiError(len(pathsToMake)) 177 for i, paths := range pathsToMake { 178 merr.Assign(i, paths.create(o.BaseDir)) 179 } 180 if err := merr.Get(); err != nil { 181 return err 182 } 183 184 if runtime.GOOS != "windows" { 185 tFile, err := ioutil.TempFile(o.streamServerPath, "sock.") 186 if err != nil { 187 return errors.Annotate(err, "creating tempfile").Err() 188 } 189 o.streamServerPath = tFile.Name() 190 if err := tFile.Close(); err != nil { 191 return errors.Annotate(err, "closing tempfile %q", o.streamServerPath).Err() 192 } 193 } 194 195 if o.LogdogOutput == nil { 196 o.LogdogOutput = &null.Output{} 197 } 198 199 if o.ExeAuth == nil { 200 o.ExeAuth = DefaultExeAuth("luciexe", nil) 201 } 202 203 if o.ViewerURL != "" { 204 o.logdogTags = streamproto.TagMap{viewer.LogDogViewerURLTag: o.ViewerURL} 205 } 206 207 // BaseBuild is nil in testing scenarios only. 208 if builder := o.BaseBuild.GetBuilder(); builder != nil { 209 if o.logdogTags == nil { 210 o.logdogTags = streamproto.TagMap{} 211 } 212 o.logdogTags["buildbucket.bucket"] = builder.Bucket 213 o.logdogTags["buildbucket.builder"] = builder.Builder 214 o.logdogTags["buildbucket.project"] = builder.Project 215 } 216 217 return nil 218 }