go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/config_service/rpc/common.go (about) 1 // Copyright 2023 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 rpc 16 17 import ( 18 "path" 19 "strings" 20 21 "google.golang.org/protobuf/types/known/fieldmaskpb" 22 "google.golang.org/protobuf/types/known/timestamppb" 23 24 "go.chromium.org/luci/common/data/stringset" 25 "go.chromium.org/luci/common/errors" 26 "go.chromium.org/luci/common/proto/mask" 27 28 "go.chromium.org/luci/config_service/internal/common" 29 "go.chromium.org/luci/config_service/internal/model" 30 pb "go.chromium.org/luci/config_service/proto" 31 ) 32 33 // maxRawContentSize is the max allowed raw content size per config in rpc 34 // responses. Any size larger than it will be responded with a GCS signed url. 35 const maxRawContentSize = 30 * 1024 * 1024 36 37 // defaultConfigSetMask is the default mask used for ConfigSet related RPCs. 38 var defaultConfigSetMask = mask.MustFromReadMask(&pb.ConfigSet{}, "name", "url", "revision") 39 40 // validatePath validates path in rpc requests. 41 func validatePath(p string) error { 42 if p == "" { 43 return errors.New("not specified") 44 } 45 if path.IsAbs(p) { 46 return errors.Reason("must not be absolute").Err() 47 } 48 if strings.HasPrefix(p, "./") || strings.HasPrefix(p, "../") { 49 return errors.Reason("should not start with './' or '../'").Err() 50 } 51 return nil 52 } 53 54 // toConfigMask converts the given field mask for Config proto to a config mask. 55 func toConfigMask(fields *fieldmaskpb.FieldMask) (*mask.Mask, error) { 56 // Convert "content" path to "raw_content" and "signed_url", as "content" is 57 // 'oneof' field type and the mask lib hasn't supported to parse it yet. 58 if fieldSet := stringset.NewFromSlice(fields.GetPaths()...); fieldSet.Has("content") { 59 fieldSet.Del("content") 60 fieldSet.Add("raw_content") 61 fieldSet.Add("signed_url") 62 fields.Paths = fieldSet.ToSlice() 63 } 64 return mask.FromFieldMask(fields, &pb.Config{}, false, false) 65 } 66 67 // toConfigSetMask converts the given field mask for ConfigSet proto to a 68 // ConfigSet mask. 69 func toConfigSetMask(fields *fieldmaskpb.FieldMask) (*mask.Mask, error) { 70 if len(fields.GetPaths()) == 0 { 71 return defaultConfigSetMask, nil 72 } 73 return mask.FromFieldMask(fields, &pb.ConfigSet{}, false, false) 74 } 75 76 // toConfigPb converts *model.File to Config proto, excluding its content. 77 func toConfigPb(cs string, f *model.File) *pb.Config { 78 return &pb.Config{ 79 ConfigSet: cs, 80 Path: f.Path, 81 ContentSha256: f.ContentSHA256, 82 Size: f.Size, 83 Revision: f.Revision.StringID(), 84 Url: common.GitilesURL(f.Location.GetGitilesLocation()), 85 } 86 } 87 88 // toConfigSetPb converts *model.ConfigSet to ConfigSet proto. 89 func toConfigSetPb(cs *model.ConfigSet) *pb.ConfigSet { 90 if cs == nil { 91 return nil 92 } 93 return &pb.ConfigSet{ 94 Name: string(cs.ID), 95 Url: common.GitilesURL(cs.Location.GetGitilesLocation()), 96 Revision: &pb.ConfigSet_Revision{ 97 Id: cs.LatestRevision.ID, 98 Url: common.GitilesURL(cs.LatestRevision.Location.GetGitilesLocation()), 99 CommitterEmail: cs.LatestRevision.CommitterEmail, 100 AuthorEmail: cs.LatestRevision.AuthorEmail, 101 Timestamp: timestamppb.New(cs.LatestRevision.CommitTime), 102 }, 103 } 104 } 105 106 // toImportAttempt converts *model.ImportAttempt to ConfigSet_Attempt proto. 107 func toImportAttempt(attempt *model.ImportAttempt) *pb.ConfigSet_Attempt { 108 if attempt == nil { 109 return nil 110 } 111 return &pb.ConfigSet_Attempt{ 112 Message: attempt.Message, 113 Success: attempt.Success, 114 Revision: &pb.ConfigSet_Revision{ 115 Id: attempt.Revision.ID, 116 Url: common.GitilesURL(attempt.Revision.Location.GetGitilesLocation()), 117 CommitterEmail: attempt.Revision.CommitterEmail, 118 AuthorEmail: attempt.Revision.AuthorEmail, 119 Timestamp: timestamppb.New(attempt.Revision.CommitTime), 120 }, 121 ValidationResult: attempt.ValidationResult, 122 } 123 }