go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/bisection/internal/rotationproxy/rotationproxy.go (about) 1 // Copyright 2022 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 rotationproxy contains the logic to query for the on-call arborists 16 // from the Chrome Ops Rotation Proxy 17 package rotationproxy 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "net/http" 24 "time" 25 26 "go.chromium.org/luci/bisection/util" 27 28 "go.chromium.org/luci/common/errors" 29 ) 30 31 var MockedRotationProxyClientKey = "mocked chrome-ops-rotation-proxy client" 32 33 // GetOnCallEmails returns the emails of the on-call arborists for the given 34 // project. 35 func GetOnCallEmails(ctx context.Context, project string) ([]string, error) { 36 switch project { 37 case "chromium/src": 38 return getOnCallEmails(ctx, "oncallator:chrome-build-sheriff") 39 default: 40 // getting on-call rotation not supported 41 } 42 43 return nil, fmt.Errorf("could not get on-call rotation for project %s", project) 44 } 45 46 type rotationResponse struct { 47 Emails []string `json:"emails"` 48 UpdatedTimestamp int64 `json:"updated_unix_timestamp"` 49 } 50 51 // getOnCallEmails is a helper function to get the emails of the on-call 52 // arborists using the given rotation proxy name. 53 func getOnCallEmails(ctx context.Context, rotationProxyName string) ([]string, error) { 54 client := GetClient(ctx) 55 data, err := client.sendRequest(ctx, rotationProxyName) 56 if err != nil { 57 return nil, errors.Annotate(err, 58 "error when querying for on-call rotation").Err() 59 } 60 61 res := &rotationResponse{} 62 if err = json.Unmarshal([]byte(data), res); err != nil { 63 return nil, errors.Annotate(err, 64 "failed to unmarshal rotation response (data = %s)", data).Err() 65 } 66 67 return res.Emails, nil 68 } 69 70 func GetClient(c context.Context) Client { 71 if mockClient, ok := c.Value(MockedRotationProxyClientKey).(*MockedRotationProxyClient); ok { 72 return mockClient 73 } 74 75 return &RotationProxyClient{} 76 } 77 78 // Client interface is needed for testing purposes 79 type Client interface { 80 sendRequest(ctx context.Context, rotationProxyName string) (string, error) 81 } 82 83 type RotationProxyClient struct{} 84 85 func (client *RotationProxyClient) sendRequest(ctx context.Context, rotationProxyName string) (string, error) { 86 url := fmt.Sprintf("https://chrome-ops-rotation-proxy.appspot.com/current/%s", rotationProxyName) 87 req, err := http.NewRequest("GET", url, nil) 88 if err != nil { 89 return "", errors.Annotate(err, 90 "failed to construct request when getting on-call rotation with name '%s'", rotationProxyName).Err() 91 } 92 93 // Get the on-call rotation (timeout of 30s) 94 return util.SendHTTPRequest(ctx, req, 30*time.Second) 95 }