go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/led/job/jobexport/swarming.go (about) 1 // Copyright 2020 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 jobexport 16 17 import ( 18 "go.chromium.org/luci/common/errors" 19 "go.chromium.org/luci/led/job" 20 swarmingpb "go.chromium.org/luci/swarming/proto/api_v2" 21 ) 22 23 var ( 24 // In order to have swarming service to upload output to RBE-CAS when no inputs. 25 dummyCasDigest = &swarmingpb.Digest{ 26 Hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 27 SizeBytes: 0, 28 } 29 ) 30 31 // ToSwarmingNewTask renders a swarming proto task to a 32 // NewTaskRequest. 33 func ToSwarmingNewTask(sw *job.Swarming) (*swarmingpb.NewTaskRequest, error) { 34 task := sw.Task 35 ret := &swarmingpb.NewTaskRequest{ 36 BotPingToleranceSecs: task.GetBotPingToleranceSecs(), 37 Name: task.Name, 38 User: task.User, 39 ParentTaskId: task.ParentTaskId, 40 Priority: task.Priority, 41 ServiceAccount: task.ServiceAccount, 42 Realm: task.Realm, 43 Tags: task.Tags, 44 TaskSlices: make([]*swarmingpb.TaskSlice, 0, len(task.TaskSlices)), 45 } 46 if rdbEnabled := task.GetResultdb().GetEnable(); rdbEnabled { 47 ret.Resultdb = &swarmingpb.ResultDBCfg{ 48 Enable: true, 49 } 50 } 51 52 casUserPayload := sw.GetCasUserPayload() 53 cupDigest := casUserPayload.GetDigest() 54 for i, slice := range task.TaskSlices { 55 props := slice.Properties 56 57 slcCasDgst := props.GetCasInputRoot().GetDigest() 58 // validate all isolate and rbe-cas related fields. 59 if slcCasDgst != nil && cupDigest != nil && 60 (slcCasDgst.Hash != cupDigest.Hash || slcCasDgst.SizeBytes != cupDigest.SizeBytes) { 61 return nil, errors.Reason( 62 "slice %d defines CasInputRoot, but job.CasUserPayload is also defined. "+ 63 "Call ConsolidateIsolateds before calling ToSwarmingNewTask.", i).Err() 64 } 65 66 toAdd := &swarmingpb.TaskSlice{ 67 ExpirationSecs: slice.ExpirationSecs, 68 WaitForCapacity: slice.WaitForCapacity, 69 Properties: &swarmingpb.TaskProperties{ 70 Caches: make([]*swarmingpb.CacheEntry, 0, len(props.Caches)), 71 72 Dimensions: make([]*swarmingpb.StringPair, 0, len(props.Dimensions)), 73 74 ExecutionTimeoutSecs: props.GetExecutionTimeoutSecs(), 75 GracePeriodSecs: props.GetGracePeriodSecs(), 76 IoTimeoutSecs: props.GetIoTimeoutSecs(), 77 78 CipdInput: &swarmingpb.CipdInput{ 79 Packages: make([]*swarmingpb.CipdPackage, 0, len(props.CipdInput.Packages)), 80 }, 81 82 Env: make([]*swarmingpb.StringPair, 0, len(props.Env)), 83 EnvPrefixes: make([]*swarmingpb.StringListPair, 0, len(props.EnvPrefixes)), 84 85 Command: props.Command, 86 RelativeCwd: props.RelativeCwd, 87 }, 88 } 89 90 if con := props.GetContainment(); con.GetContainmentType() != swarmingpb.ContainmentType_NOT_SPECIFIED { 91 toAdd.Properties.Containment = &swarmingpb.Containment{ 92 ContainmentType: con.GetContainmentType(), 93 } 94 } 95 96 // If we have rbe-cas digest info, use that info. 97 // Otherwise, populate a dummy rbe-cas prop. 98 // 99 // The digest info in the slice will be used first. If it's not there, then 100 // fall back to use the info in job-global "CasUserPayload" 101 // 102 // (The twisted logic will look a little bit better, after completely getting rid of isolate.) 103 104 var casToUse *swarmingpb.CASReference 105 sliceCas := props.CasInputRoot 106 jobCas := casUserPayload 107 switch { 108 case sliceCas.GetDigest().GetHash() != "": 109 casToUse = sliceCas 110 case jobCas.GetDigest().GetHash() != "": 111 casToUse = jobCas 112 case sliceCas.GetCasInstance() != "": 113 casToUse = sliceCas 114 default: 115 casToUse = jobCas 116 } 117 118 if casToUse != nil { 119 toAdd.Properties.CasInputRoot = &swarmingpb.CASReference{ 120 CasInstance: casToUse.CasInstance, 121 Digest: &swarmingpb.Digest{ 122 Hash: casToUse.Digest.GetHash(), 123 SizeBytes: casToUse.Digest.GetSizeBytes(), 124 }, 125 } 126 } else { 127 // populate a dummy CasInputRoot in order to use RBE-CAS. 128 casIns, err := job.ToCasInstance(sw.Hostname) 129 if err != nil { 130 return nil, err 131 } 132 toAdd.Properties.CasInputRoot = &swarmingpb.CASReference{ 133 CasInstance: casIns, 134 Digest: dummyCasDigest, 135 } 136 } 137 if toAdd.Properties.CasInputRoot.Digest.Hash == "" { 138 toAdd.Properties.CasInputRoot.Digest = dummyCasDigest 139 } 140 141 for _, env := range props.Env { 142 toAdd.Properties.Env = append(toAdd.Properties.Env, &swarmingpb.StringPair{ 143 Key: env.Key, 144 Value: env.Value, 145 }) 146 } 147 148 for _, path := range props.EnvPrefixes { 149 toAdd.Properties.EnvPrefixes = append(toAdd.Properties.EnvPrefixes, &swarmingpb.StringListPair{ 150 Key: path.Key, 151 Value: path.Value, 152 }) 153 } 154 155 for _, cache := range props.Caches { 156 toAdd.Properties.Caches = append(toAdd.Properties.Caches, &swarmingpb.CacheEntry{ 157 Name: cache.Name, 158 Path: cache.Path, 159 }) 160 } 161 162 for _, pkg := range props.CipdInput.Packages { 163 toAdd.Properties.CipdInput.Packages = append(toAdd.Properties.CipdInput.Packages, &swarmingpb.CipdPackage{ 164 PackageName: pkg.PackageName, 165 Version: pkg.Version, 166 Path: pkg.Path, 167 }) 168 } 169 170 for _, dim := range props.Dimensions { 171 toAdd.Properties.Dimensions = append(toAdd.Properties.Dimensions, &swarmingpb.StringPair{ 172 Key: dim.Key, 173 Value: dim.Value, 174 }) 175 } 176 177 ret.TaskSlices = append(ret.TaskSlices, toAdd) 178 } 179 180 return ret, nil 181 }