go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/led/job/util.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 job 16 17 import ( 18 "fmt" 19 "reflect" 20 "regexp" 21 "sort" 22 "strconv" 23 "strings" 24 25 "go.chromium.org/luci/common/errors" 26 swarmingpb "go.chromium.org/luci/swarming/proto/api_v2" 27 ) 28 29 func keysOf(mapish any) []string { 30 mapV := reflect.ValueOf(mapish) 31 if mapV.Kind() != reflect.Map { 32 panic("keysOf expected a map") 33 } 34 keys := []string{} 35 for _, key := range mapV.MapKeys() { 36 if key.Kind() != reflect.String { 37 panic("keysOf expected a map with string keys") 38 } 39 keys = append(keys, key.String()) 40 } 41 sort.Strings(keys) 42 return keys 43 } 44 45 func updateStringPairList(list *[]*swarmingpb.StringPair, updates map[string]string) { 46 if len(updates) == 0 { 47 return 48 } 49 50 current := make(map[string]string, len(*list)) 51 for _, pair := range *list { 52 current[pair.Key] = pair.Value 53 } 54 for key, value := range updates { 55 if value == "" { 56 delete(current, key) 57 } else { 58 current[key] = value 59 } 60 } 61 newList := make([]*swarmingpb.StringPair, 0, len(current)) 62 for key, value := range current { 63 newList = append(newList, &swarmingpb.StringPair{Key: key, Value: value}) 64 } 65 *list = newList 66 } 67 68 const ( 69 casInstanceTemplate = "projects/%s/instances/default_instance" 70 ) 71 72 var ( 73 swarmingHostRx = regexp.MustCompile(`(.*)\.appspot\.com`) 74 ) 75 76 // ToCasInstance converts a swarming host name to cas instance name. 77 func ToCasInstance(swarmingHost string) (string, error) { 78 match := swarmingHostRx.FindStringSubmatch(swarmingHost) 79 if match == nil { 80 return "", errors.New(fmt.Sprintf("invalid swarming host in job definition host=%s", swarmingHost)) 81 } 82 return fmt.Sprintf(casInstanceTemplate, match[1]), nil 83 } 84 85 // ToCasDigest converts a string (in the format of "hash/size", e.g. "dead...beef/1234") to cas digest. 86 func ToCasDigest(str string) (*swarmingpb.Digest, error) { 87 digest := &swarmingpb.Digest{} 88 var err error 89 switch strs := strings.Split(str, "/"); { 90 case len(strs) == 2: 91 digest.Hash = strs[0] 92 if digest.SizeBytes, err = strconv.ParseInt(strs[1], 10, 64); err != nil { 93 return nil, err 94 } 95 default: 96 err = errors.Reason("Invalid RBE-CAS digest %s", str).Err() 97 return nil, err 98 } 99 return digest, nil 100 }