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  }