github.com/distbuild/reclient@v0.0.0-20240401075343-3de72e395564/cmd/reproxy/main.go (about) 1 // Copyright 2023 Google LLC 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 // Binary reproxy is a long running server that rewrapper binary talks to 16 // for fast and efficient remote-execution and caching of various types of actions. 17 package main 18 19 import ( 20 "context" 21 "errors" 22 "flag" 23 "fmt" 24 "net/http" 25 _ "net/http/pprof" 26 "os" 27 "os/signal" 28 "runtime" 29 "runtime/pprof" 30 "strings" 31 "sync" 32 "syscall" 33 "time" 34 35 "github.com/bazelbuild/reclient/internal/pkg/auth" 36 "github.com/bazelbuild/reclient/internal/pkg/ignoremismatch" 37 "github.com/bazelbuild/reclient/internal/pkg/interceptors" 38 "github.com/bazelbuild/reclient/internal/pkg/ipc" 39 "github.com/bazelbuild/reclient/internal/pkg/localresources" 40 "github.com/bazelbuild/reclient/internal/pkg/localresources/usage" 41 "github.com/bazelbuild/reclient/internal/pkg/logger" 42 "github.com/bazelbuild/reclient/internal/pkg/loghttp" 43 "github.com/bazelbuild/reclient/internal/pkg/monitoring" 44 "github.com/bazelbuild/reclient/internal/pkg/pathtranslator" 45 "github.com/bazelbuild/reclient/internal/pkg/rbeflag" 46 "github.com/bazelbuild/reclient/internal/pkg/reproxy" 47 "github.com/bazelbuild/reclient/internal/pkg/reproxypid" 48 "github.com/bazelbuild/reclient/internal/pkg/stats" 49 "github.com/bazelbuild/reclient/internal/pkg/subprocess" 50 "github.com/bazelbuild/reclient/pkg/inputprocessor" 51 "github.com/bazelbuild/reclient/pkg/version" 52 53 "cloud.google.com/go/profiler" 54 "github.com/bazelbuild/remote-apis-sdks/go/pkg/client" 55 "github.com/bazelbuild/remote-apis-sdks/go/pkg/rexec" 56 57 "github.com/bazelbuild/remote-apis-sdks/go/pkg/filemetadata" 58 "google.golang.org/grpc" 59 "google.golang.org/grpc/codes" 60 "google.golang.org/grpc/status" 61 62 pb "github.com/bazelbuild/reclient/api/proxy" 63 64 rflags "github.com/bazelbuild/remote-apis-sdks/go/pkg/flags" 65 "github.com/bazelbuild/remote-apis-sdks/go/pkg/moreflag" 66 log "github.com/golang/glog" 67 ) 68 69 var ( 70 homeDir, _ = os.UserHomeDir() 71 labels = make(map[string]string) 72 start = time.Now() 73 ) 74 75 var ( 76 proxyLogDir []string 77 clangDepScanIgnoredPlugins = flag.String("clang_depscan_ignored_plugins", "", `Comma-separated list of plugins that should be ignored by clang dependency scanner. 78 Use this flag if you're using custom llvm build as your toolchain and your llvm plugins cause dependency scanning failures.`) 79 serverAddr = flag.String("server_address", "127.0.0.1:8000", "The server address in the format of host:port for network, or unix:///file for unix domain sockets.") 80 logFormat = flag.String("log_format", "reducedtext", "Format of proxy log. Currently only text and reducedtext are supported. Defaults to reducedtext.") 81 logPath = flag.String("log_path", "", "DEPRECATED. Use proxy_log_dir instead. If provided, the path to a log file of all executed records. The format is e.g. text://full/file/path.") 82 mismatchIgnoreConfigPath = flag.String("mismatch_ignore_config_path", "", "If provided, mismatches will be ignored according to the provided rule config.") 83 enableDepsCache = flag.Bool("enable_deps_cache", false, "Enables the deps cache if --cache_dir is provided") 84 cacheDir = flag.String("cache_dir", "", "Directory from which to load the cache files at startup and update at shutdown.") 85 keepRecords = flag.Int("num_records_to_keep", 0, "The number of last executed records to keep in memory for serving.") 86 // TODO(b/157446611): remove this flag. 87 _ = flag.String("cpp_dependency_scanner_plugin", "", "Deprecated: Location of the CPP dependency scanner plugin.") 88 localResourceFraction = flag.Float64("local_resource_fraction", 1, "Number [0,1] indicating how much of the local machine resources are available for local execution, 1 being all of the machine's CPUs and RAM, 0 being no resources available for local execution.") 89 cacheSilo = flag.String("cache_silo", "", "Cache silo key to be used for all the actions. Usually used to segregate cache-hits between various builds.") 90 versionCacheSilo = flag.Bool("version_cache_silo", false, "Indicates whether to add a re-client version as cache-silo key to all remotely-executed actions. Not applicable for actions run in local-execution-remote-cache (LERC) mode.") 91 remoteDisabled = flag.Bool("remote_disabled", false, "Whether to disable all remote operations and run all actions locally.") 92 dumpInputTree = flag.Bool("dump_input_tree", false, "Whether to dump the input tree of received actions to the tmp directory.") 93 useUnifiedCASOps = flag.Bool("use_unified_cas_ops", false, "Deprecated: use_unified_uploads/downloads instead. Whether to use the unified uploader / downloader for deduplicating uploads / downloads.") 94 useUnifiedUploads = flag.Bool("use_unified_uploads", false, "Whether to use the unified uploader for deduplicating uploads.") 95 uploadBufferSize = flag.Int("upload_buffer_size", 10000, "Buffer size to flush unified uploader daemon.") 96 uploadTickDuration = flag.Duration("upload_tick_duration", 50*time.Millisecond, "How often to flush unified uploader daemon.") 97 useUnifiedDownloads = flag.Bool("use_unified_downloads", false, "Whether to use the unified downloader for deduplicating downloads.") 98 downloadBufferSize = flag.Int("download_buffer_size", 10000, "Buffer size to flush unified downloader daemon.") 99 downloadTickDuration = flag.Duration("download_tick_duration", 50*time.Millisecond, "How often to flush unified downloader daemon.") 100 compressionThreshold = flag.Int("compression_threshold", -1, "Threshold size in bytes for compressing Bytestream reads or writes. Use a negative value for turning off compression.") 101 useBatches = flag.Bool("use_batches", true, "Use batch operations for relatively small blobs.") 102 logKeepDuration = flag.Duration("log_keep_duration", 24*time.Hour, "Delete all RE logs older than the specified duration on startup.") 103 idleTimeout = flag.Duration("proxy_idle_timeout", 6*time.Hour, "Inactivity period after which the running reproxy process will be killed. Default is 6 hours. When set to 0, idle timeout is disabled.") 104 depsCacheMaxMb = flag.Int("deps_cache_max_mb", 128, "Maximum size of the deps cache file (for goma input processor only).") 105 // TODO(b/233275188): remove this flag. 106 _ = flag.Duration("ip_reset_min_delay", 3*time.Minute, "Deprecated. The minimum time after the input processor has been reset before it can be reset again. Negative values disable resetting.") 107 ipTimeout = flag.Duration("ip_timeout", 10*time.Minute, "The maximum time to wait for an input processor action. Zero and negative values disable timeout.") 108 metricsProject = flag.String("metrics_project", "", "If set, action and build metrics are exported to Cloud Monitoring in the specified GCP project") 109 metricsPrefix = flag.String("metrics_prefix", "", "Prefix of metrics exported to Cloud Monitoring") 110 metricsNamespace = flag.String("metrics_namespace", "", "Namespace of metrics exported to Cloud Monitoring (e.g. RBE project)") 111 experimentalCredentialsHelper = flag.String(auth.CredshelperPathFlag, "", "Path to the credentials helper binary. If given execrel://, looks for the `credshelper` binary in the same folder as reproxy") 112 experimentalCredentialsHelperArgs = flag.String(auth.CredshelperArgsFlag, "", "Arguments for the experimental credentials helper, separated by space.") 113 failEarlyMinActionCount = flag.Int64("fail_early_min_action_count", 0, "Minimum number of actions received by reproxy before the fail early mechanism can take effect. 0 indicates fail early is disabled.") 114 failEarlyMinFallbackRatio = flag.Float64("fail_early_min_fallback_ratio", 0, "Minimum ratio of fallbacks to total actions above which the build terminates early. Ratio is a number in the range [0,1]. 0 indicates fail early is disabled.") 115 failEarlyWindow = flag.Duration("fail_early_window", 0, "Window of time to consider for fail_early_min_action_count and fail_early_min_fallback_ratio. 0 indicates all datapoints should be used.") 116 racingBias = flag.Float64("racing_bias", 0.75, "Value between [0,1] to indicate how racing manages the tradeoff of saving bandwidth (0) versus speed (1). The default is to prefer speed over bandwidth.") 117 racingTmp = flag.String("racing_tmp_dir", "", "DEPRECATED. Use download_tmp_dir instead.") 118 downloadTmp = flag.String("download_tmp_dir", "", "Directory where reproxy should store outputs temporarily before moving them to the desired location. This should be on the same device as the output directory for the build. The default is outputs will be written to a subdirectory inside the action's working directory. Note that the download_tmp_dir will only be used if the action has racing as its exec strategy or it explicitly sets EnableAtomicDownloads=true. See proxy.proto for details.") 119 120 debugPort = flag.Int("pprof_port", 0, "Enable pprof http server if not zero") 121 cpuProfFile = flag.String("pprof_file", "", "Enable cpu pprof if not empty. Will not work on windows as reproxy shutdowns through an uncatchable sigkill.") 122 memProfFile = flag.String("pprof_mem_file", "", "Enable memory pprof if not empty. Will not work on windows as reproxy shutdowns through an uncatchable sigkill.") 123 124 profilerService = flag.String("profiler_service", "", "Service name to associate with profiles uploaded to Cloud Profiling. If unset, Cloud Profiling is disabled.") 125 profilerProjectID = flag.String("profiler_project_id", "", "project id used for cloud profiler") 126 127 cppLinkDeepScan = flag.Bool("clang_depscan_archive", false, "Deep scan .a files for dependencies during clang linking") 128 129 depsScannerAddress = flag.String("depsscanner_address", "execrel://", "If set, connects to the given address for C++ dependency scanning; a path with the prefix 'exec://' will start the target executable and connect to it. Defaults to execrel:// which looks for the `scandeps_server` binary in the same folder as reproxy. When set to \"\", the internal dependency scanner will be used.") 130 131 credsFile = flag.String("creds_file", "", "Path to file where short-lived credentials are stored. If the file includes a token, reproxy will update the token if it refreshes it. Token refresh is only applicable if use_external_auth_token is used.") 132 waitForShutdownRPC = flag.Bool("wait_for_shutdown_rpc", false, "If set, will only shutdown after 3 SIGINT signals") 133 useCasNg = flag.Bool("use_casng", false, "Use casng pkg.") 134 logHTTPCalls = flag.Bool("log_http_calls", false, "Log all http requests made with the default http client.") 135 ) 136 137 func verifyFlags() { 138 if *localResourceFraction < 0 || *localResourceFraction > 1 { 139 log.Exitf("Invalid local_resource_fraction: %v, want [0,1]", *localResourceFraction) 140 } 141 if *failEarlyMinActionCount < 0 { 142 log.Exitf("Invalid fail_early_min_action_acount: %v, want [0,MaxInt64]", *failEarlyMinActionCount) 143 } 144 if *failEarlyMinFallbackRatio < 0 { 145 log.Exitf("Invalid fail_early_min_fallback_ratio: %v, want [0,1]", *failEarlyMinFallbackRatio) 146 } 147 if *failEarlyWindow < 0 { 148 log.Exitf("Invalid fail_early_window: %v, want >0", *failEarlyWindow) 149 } 150 if *racingBias < 0 || *racingBias > 1 { 151 log.Exitf("Invalid racing_bias: %v, want [0,1]", *racingBias) 152 } 153 if *failEarlyMinActionCount == 0 && *failEarlyMinFallbackRatio > 0 { 154 log.Exitf("fail_early_min_fallback_ratio is set to %v while fail_early_min_action_count is disabled", *failEarlyMinFallbackRatio) 155 } 156 if *failEarlyMinActionCount > 0 && *failEarlyMinFallbackRatio == 0 { 157 log.Exitf("fail_early_min_action_count is set to %v while fail_early_min_fallback_ratio is disabled", *failEarlyMinActionCount) 158 } 159 os.Setenv("RBE_clang_depscan_ignored_plugins", *clangDepScanIgnoredPlugins) 160 } 161 162 func main() { 163 flag.Var((*moreflag.StringListValue)(&proxyLogDir), "proxy_log_dir", "If provided, the directory path to a proxy log file of executed records.") 164 flag.StringVar(&filemetadata.XattrDigestName, "xattr_digest", "", "Extended file attribute to obtain the digest from, if available, formatted as hash/size. If the value contains the hash only, the file size as reported by stat is used.") 165 flag.Var((*moreflag.StringMapValue)(&labels), "metrics_labels", "Comma-separated key value pairs in the form key=value. This is used to add arbitrary labels to exported metrics.") 166 rbeflag.Parse() 167 rbeflag.LogAllFlags(0) 168 defer log.Flush() 169 if *logHTTPCalls { 170 loghttp.Register() 171 } 172 defer func() { 173 pf, err := reproxypid.ReadFile(*serverAddr) 174 if err != nil { 175 log.Warningf("Unable to find pid file for deletion: %v", err) 176 return 177 } 178 pf.Delete() 179 }() 180 if *depsScannerAddress != "" { 181 if *depsScannerAddress == "execrel://" { 182 scandepsServerPath, err := pathtranslator.BinaryRelToAbs("scandeps_server") 183 if err != nil { 184 log.Fatalf("Specified --depsscanner_address=execrel:// but `scandeps_server` was not found in the same directory as `reproxy`: %v", err) 185 } 186 *depsScannerAddress = "exec://" + scandepsServerPath 187 } 188 // If the depsscanner crashes or times out, all actions in flight will be counted as 189 // timeouts. Therefore we bump the number allowed to account for multiple fallbacks from 190 // one failure. 191 // There will be at most NumCPU actions at any given time; this gives us approximately 192 // two failures before aborting the build on the third. 193 reproxy.AllowedIPTimeouts += int64(runtime.NumCPU() * 2) 194 } else { 195 log.Fatalf("--depsscanner_address must be specified") 196 } 197 log.Flush() 198 version.PrintAndExitOnVersionFlag(true) 199 verifyFlags() 200 201 if *profilerService != "" { 202 log.Infof("Enable cloud profiler: service=%s project=%s", *profilerService, *profilerProjectID) 203 err := profiler.Start(profiler.Config{ 204 Service: *profilerService, 205 ServiceVersion: version.CurrentVersion(), 206 MutexProfiling: true, 207 ProjectID: *profilerProjectID, 208 }) 209 if err != nil { 210 log.Errorf("Failed to start cloud profiler: %v", err) 211 } 212 } 213 214 if *debugPort > 0 { 215 go func() { 216 addr := fmt.Sprintf("127.0.0.1:%d", *debugPort) 217 log.Infof("start http server for pprof at %s", addr) 218 log.Exit(http.ListenAndServe(addr, nil)) 219 }() 220 } else { 221 if *cpuProfFile != "" { 222 f, err := os.Create(*cpuProfFile) 223 if err != nil { 224 log.Fatal("Could not create CPU profile: ", err) 225 } 226 defer f.Close() 227 if err := pprof.StartCPUProfile(f); err != nil { 228 log.Fatal("Could not start CPU profile: ", err) 229 } 230 } 231 if *memProfFile != "" { 232 f, err := os.Create(*memProfFile) 233 if err != nil { 234 log.Fatal("Could not create memory profile: ", err) 235 } 236 defer f.Close() 237 if err := pprof.WriteHeapProfile(f); err != nil { 238 log.Fatal("Could not start memory profile: ", err) 239 } 240 } 241 } 242 243 listener, err := ipc.Listen(*serverAddr) 244 if err != nil { 245 log.Exitf("Failed to listen: %v", err) 246 } 247 248 logDir := getLogDir() 249 var opts []grpc.ServerOption 250 truncateInterceptor := interceptors.NewTruncInterceptor(ipc.GrpcMaxMsgSize, logDir) 251 opts = append( 252 opts, 253 grpc.MaxRecvMsgSize(ipc.GrpcMaxMsgSize), 254 grpc.ChainUnaryInterceptor(interceptors.UnaryServerInterceptor, truncateInterceptor), 255 grpc.StreamInterceptor(interceptors.StreamServerInterceptor), 256 ) 257 grpcServer := grpc.NewServer(opts...) 258 259 ctx := context.Background() 260 var c *auth.Credentials 261 if !*remoteDisabled { 262 c = mustBuildCredentials() 263 defer c.SaveToDisk() 264 } 265 var e *monitoring.Exporter 266 var exportActionMetrics logger.ExportActionMetricsFunc 267 if *metricsProject != "" { 268 e, err = newExporter(c) 269 if err != nil { 270 log.Warningf("Failed to initialize cloud monitoring: %v", err) 271 } else { 272 exportActionMetrics = e.ExportActionMetrics 273 defer e.Close() 274 } 275 } 276 mi, err := ignoremismatch.New(*mismatchIgnoreConfigPath) 277 if err != nil { 278 log.Errorf("Failed to create mismatch ignorer: %v", err) 279 } 280 l, err := initializeLogger(mi, exportActionMetrics) 281 if err != nil { 282 log.Exitf("%v", err) 283 } 284 285 st := filemetadata.NewSingleFlightCache() 286 287 exec := &subprocess.SystemExecutor{} 288 resMgr := localresources.NewFractionalDefaultManager(*localResourceFraction) 289 290 dTmp := *racingTmp 291 if *downloadTmp != "" { 292 dTmp = *downloadTmp 293 } 294 295 initCtx, cancelInit := context.WithCancel(ctx) 296 server := &reproxy.Server{ 297 FileMetadataStore: st, 298 LocalPool: reproxy.NewLocalPool(exec, resMgr), 299 KeepLastRecords: *keepRecords, 300 CacheSilo: *cacheSilo, 301 VersionCacheSilo: *versionCacheSilo, 302 RemoteDisabled: *remoteDisabled, 303 DumpInputTree: *dumpInputTree, 304 Forecast: &reproxy.Forecast{}, 305 StartTime: start, 306 FailEarlyMinActionCount: *failEarlyMinActionCount, 307 FailEarlyMinFallbackRatio: *failEarlyMinFallbackRatio, 308 FailEarlyWindow: *failEarlyWindow, 309 RacingBias: *racingBias, 310 DownloadTmp: dTmp, 311 MaxHoldoff: time.Minute, 312 Logger: l, 313 StartupCancelFn: cancelInit, 314 } 315 server.Init() 316 317 ipOpts := &inputprocessor.Options{ 318 CacheDir: *cacheDir, 319 EnableDepsCache: *enableDepsCache, 320 LogDir: logDir, 321 DepsCacheMaxMb: *depsCacheMaxMb, 322 CppLinkDeepScan: *cppLinkDeepScan, 323 IPTimeout: *ipTimeout, 324 DepsScannerAddress: *depsScannerAddress, 325 ProxyServerAddress: *serverAddr, 326 } 327 go func() { 328 log.Infof("Setting up input processor") 329 ip, cleanup, err := inputprocessor.NewInputProcessor(initCtx, exec, resMgr, st, l, ipOpts) 330 if err != nil { 331 log.Errorf("Failed to initialize input processor: %+v", err) 332 server.SetStartupErr(status.Error(codes.Internal, err.Error())) 333 cancelInit() 334 } else { 335 log.Infof("Finished setting up input processor") 336 server.SetInputProcessor(ip, cleanup) 337 } 338 }() 339 340 if *remoteDisabled { 341 server.SetREClient(&rexec.Client{st, nil}, func() {}) 342 } else { 343 // Backward compatibility until useUnifiedCASOps is deprecated: 344 if *useUnifiedCASOps { 345 *useUnifiedUploads = true 346 *useUnifiedDownloads = true 347 } 348 clientOpts := []client.Opt{ 349 client.UnifiedUploads(*useUnifiedUploads), 350 client.UnifiedUploadBufferSize(*uploadBufferSize), 351 client.UnifiedUploadTickDuration(*uploadTickDuration), 352 client.UnifiedDownloads(*useUnifiedDownloads), 353 client.UnifiedDownloadBufferSize(*downloadBufferSize), 354 client.UnifiedDownloadTickDuration(*downloadTickDuration), 355 client.UseBatchOps(*useBatches), 356 client.CompressedBytestreamThreshold(*compressionThreshold), 357 client.UseCASNG(*useCasNg), 358 } 359 if ts := c.TokenSource(); ts != nil { 360 clientOpts = append(clientOpts, &client.PerRPCCreds{Creds: ts}) 361 } 362 go func() { 363 log.Infof("Creating a new SDK client") 364 grpcClient, err := rflags.NewClientFromFlags(initCtx, clientOpts...) 365 if err != nil { 366 log.Errorf("Failed to initialize SDK client: %+v", err) 367 if ce, ok := err.(*client.InitError); ok { 368 err = formatAuthError(c.Mechanism(), ce) 369 } 370 server.SetStartupErr(err) 371 cancelInit() 372 } else { 373 log.Infof("Finished setting up SDK client") 374 server.SetREClient(&rexec.Client{st, grpcClient}, func() { grpcClient.Close() }) 375 } 376 }() 377 } 378 go server.Forecast.Run(ctx) 379 go server.MonitorFailBuildConditions(ctx) 380 go reproxy.IdleTimeout(ctx, *idleTimeout) 381 // Log all reproxy flags. 382 if server.Logger != nil { 383 server.Logger.AddFlags(flag.CommandLine) 384 } else { 385 log.Warningf("nil logger pointer") 386 } 387 // Delete old logs in the background. 388 go reproxy.DeleteOldLogFiles(*logKeepDuration, logDir) 389 pb.RegisterCommandsServer(grpcServer, server) 390 pb.RegisterStatsServer(grpcServer, server) 391 pb.RegisterStatusServer(grpcServer, l) 392 log.Infof("RE proxy server listening on %s://%s", listener.Addr().Network(), listener.Addr().String()) 393 log.Flush() 394 sigs := make(chan os.Signal, 1) 395 signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 396 wg := sync.WaitGroup{} 397 wg.Add(2) 398 go func() { 399 sigCnt := 1 400 if *waitForShutdownRPC { 401 sigCnt = 3 402 } 403 for sigCnt > 0 { 404 select { 405 case sig := <-sigs: 406 sigCnt-- 407 go server.DrainAndReleaseResources() // Start draining server immediately while waiting for Shutdown rpc 408 if sigCnt > 0 { 409 log.Infof("RE proxy server received %v signal, waiting for Shutdown rpc or %d more signals...", sig, sigCnt) 410 } else { 411 log.Infof("RE proxy server received %v signal, shutting down...", sig) 412 } 413 case <-server.WaitForShutdownCommand(): 414 sigCnt = 0 415 log.Infof("RE proxy server received a Shutdown RPC call, shutting down...") 416 } 417 } 418 if *cpuProfFile != "" { 419 pprof.StopCPUProfile() 420 } 421 grpcServer.GracefulStop() 422 <-server.WaitForCleanupDone() 423 log.Infof("Finished shutting down and wrote log records...") 424 log.Flush() 425 wg.Done() 426 }() 427 go func() { 428 grpcServer.Serve(listener) 429 wg.Done() 430 }() 431 wg.Wait() 432 } 433 434 func formatAuthError(m auth.Mechanism, ce *client.InitError) error { 435 if errors.Is(ce.Err, context.Canceled) { 436 return ce.Err 437 } 438 errMsg := "Unable to authenticate with RBE" 439 switch ce.AuthUsed { 440 case client.ExternalTokenAuth: 441 errMsg += ", externally provided auth token was invalid" 442 case client.ApplicationDefaultCredsAuth: 443 errMsg += ", try restarting the build after running the following command:\n" 444 errMsg += " gcloud auth application-default login --disable-quota-project\n" 445 errMsg += "If this is a headless machine, use:\n" 446 errMsg += " gcloud auth application-default login --no-launch-browser --disable-quota-project" 447 } 448 return status.Errorf(codes.Unauthenticated, errMsg+"\n%s", ce.Error()) 449 } 450 451 // mustBuildCredentials either returns a valid auth.Credentials struct or exits 452 func mustBuildCredentials() *auth.Credentials { 453 if *experimentalCredentialsHelper != "" { 454 creds, err := auth.NewExternalCredentials(*experimentalCredentialsHelper, strings.Fields(*experimentalCredentialsHelperArgs), *credsFile) 455 if err != nil { 456 fmt.Fprintf(os.Stderr, "Experimental credentials helper failed. Please try again or use application default credentials:%v", err) 457 os.Exit(auth.ExitCodeExternalTokenAuth) 458 } 459 return creds 460 } 461 m, err := auth.MechanismFromFlags() 462 if err != nil || m == auth.Unknown { 463 log.Errorf("Failed to determine auth mechanism: %v", err) 464 os.Exit(auth.ExitCodeNoAuth) 465 } 466 c, err := auth.NewCredentials(m, *credsFile, 0) 467 if err != nil { 468 log.Errorf("Failed to initialize credentials: %v", err) 469 if aerr, ok := err.(*auth.Error); ok { 470 os.Exit(aerr.ExitCode) 471 } 472 os.Exit(auth.ExitCodeUnknown) 473 } 474 return c 475 } 476 477 func initializeLogger(mi *ignoremismatch.MismatchIgnorer, e logger.ExportActionMetricsFunc) (*logger.Logger, error) { 478 u := usage.New() 479 if len(proxyLogDir) > 0 { 480 format, err := logger.ParseFormat(*logFormat) 481 if err != nil { 482 return nil, fmt.Errorf("error initializing logger: %v", err) 483 } 484 l, err := logger.New(format, proxyLogDir[0], stats.New(), mi, e, u) 485 if err != nil { 486 return nil, fmt.Errorf("error initializing logger: %v", err) 487 } 488 return l, nil 489 } 490 491 if *logPath != "" { 492 l, err := logger.NewFromFormatFile(*logPath, stats.New(), mi, e, u) 493 if err != nil { 494 return nil, fmt.Errorf("error initializing log file %v: %v", *logPath, err) 495 } 496 return l, nil 497 } 498 return nil, nil 499 } 500 501 func newExporter(creds *auth.Credentials) (*monitoring.Exporter, error) { 502 if err := monitoring.SetupViews(labels); err != nil { 503 return nil, err 504 } 505 return monitoring.NewExporter(context.Background(), *metricsProject, *metricsPrefix, *metricsNamespace, creds.TokenSource()) 506 } 507 508 func getLogDir() string { 509 if len(proxyLogDir) > 0 { 510 return proxyLogDir[0] 511 } 512 513 if f := flag.Lookup("log_dir"); f != nil && f.Value.String() != "" { 514 return f.Value.String() 515 } 516 return os.TempDir() 517 }