golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/coordinator/pool/pool.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build linux || darwin 6 7 package pool 8 9 import ( 10 "context" 11 "fmt" 12 "log" 13 "math/rand" 14 "strings" 15 "time" 16 17 "golang.org/x/build/buildlet" 18 "golang.org/x/build/dashboard" 19 "golang.org/x/build/internal/coordinator/pool/queue" 20 ) 21 22 // Buildlet defines an interface for a pool of buildlets. 23 type Buildlet interface { 24 // GetBuildlet returns a new buildlet client. 25 // 26 // The hostType is the key into the dashboard.Hosts 27 // map (such as "host-linux-bullseye"), NOT the buidler type 28 // ("linux-386"). 29 // 30 // Users of GetBuildlet must both call Client.Close when done 31 // with the client as well as cancel the provided Context. 32 GetBuildlet(ctx context.Context, hostType string, lg Logger, item *queue.SchedItem) (buildlet.Client, error) 33 34 String() string // TODO(bradfitz): more status stuff 35 } 36 37 // IsRemoteBuildletFunc should report whether the buildlet instance name is 38 // is a remote buildlet. This is applicable to GCE and EC2 instances. 39 // 40 // TODO(golang.org/issue/38337): should be removed once remote buildlet management 41 // functions are moved into a package. 42 type IsRemoteBuildletFunc func(instanceName string) bool 43 44 // randHex generates a random hex string. 45 func randHex(n int) string { 46 buf := make([]byte, n/2+1) 47 if _, err := rand.Read(buf); err != nil { 48 log.Fatalf("randHex: %v", err) 49 } 50 return fmt.Sprintf("%x", buf)[:n] 51 } 52 53 func friendlyDuration(d time.Duration) string { 54 if d > 10*time.Second { 55 d2 := ((d + 50*time.Millisecond) / (100 * time.Millisecond)) * (100 * time.Millisecond) 56 return d2.String() 57 } 58 if d > time.Second { 59 d2 := ((d + 5*time.Millisecond) / (10 * time.Millisecond)) * (10 * time.Millisecond) 60 return d2.String() 61 } 62 d2 := ((d + 50*time.Microsecond) / (100 * time.Microsecond)) * (100 * time.Microsecond) 63 return d2.String() 64 } 65 66 // instanceName generates a random instance name according to the host type. 67 func instanceName(hostType string, length int) string { 68 return fmt.Sprintf("buildlet-%s-rn%s", strings.TrimPrefix(hostType, "host-"), randHex(length)) 69 } 70 71 // determineDeleteTimeout reports the buildlet delete timeout duration 72 // with the following priority: 73 // 74 // 1. Host type override from host config. 75 // 2. Global default. 76 func determineDeleteTimeout(host *dashboard.HostConfig) time.Duration { 77 if host.CustomDeleteTimeout != 0 { 78 return host.CustomDeleteTimeout 79 } 80 81 // The value we return below is effectively a global default. 82 // 83 // The comment of CleanUpOldVMs (and CleanUpOldPodsLoop) includes: 84 // 85 // This is the safety mechanism to delete VMs which stray from the 86 // normal deleting process. VMs are created to run a single build and 87 // should be shut down by a controlling process. Due to various types 88 // of failures, they might get stranded. To prevent them from getting 89 // stranded and wasting resources forever, we instead set the 90 // "delete-at" metadata attribute on them when created to some time 91 // that's well beyond their expected lifetime. 92 // 93 // Issue go.dev/issue/52929 tracks what to do about this global 94 // timeout in the long term. Unless something changes, 95 // it needs to be maintained manually so that it's always 96 // "well beyond their expected lifetime" of each builder that doesn't 97 // otherwise override this timeout—otherwise it'll cause even more 98 // resources to be used due the automatic (and unlimited) retrying 99 // as described in go.dev/issue/42699. 100 // 101 // A global timeout of 45 minutes was chosen in 2015. 102 // Longtest builders were added in 2018 started to reach 45 mins by 2021-2022. 103 // Try 2 hours next, which might last some years (depending on test volume and test speed). 104 return 2 * time.Hour 105 } 106 107 // isBuildlet checks the name string in order to determine if the name is for a buildlet. 108 func isBuildlet(name string) bool { 109 return strings.HasPrefix(name, "buildlet-") 110 } 111 112 // TestPoolHook is used to override the buildlet returned by ForConf. It should only be used for 113 // testing purposes. 114 var TestPoolHook func(*dashboard.HostConfig) Buildlet 115 116 // ForHost returns the appropriate buildlet depending on the host configuration that is passed it. 117 // The returned buildlet can be overridden for testing purposes by registering a test hook. 118 func ForHost(conf *dashboard.HostConfig) Buildlet { 119 if TestPoolHook != nil { 120 return TestPoolHook(conf) 121 } 122 if conf == nil { 123 panic("nil conf") 124 } 125 switch { 126 case conf.IsEC2: 127 return EC2BuildetPool() 128 case conf.IsVM(), conf.IsContainer(): 129 return NewGCEConfiguration().BuildletPool() 130 case conf.IsReverse: 131 return ReversePool() 132 default: 133 panic(fmt.Sprintf("no buildlet pool for host type %q", conf.HostType)) 134 } 135 }