go.chromium.org/luci@v0.0.0-20250314024836-d9a61d0730e6/tokenserver/appengine/impl/utils/policy/config_fetcher.go (about) 1 // Copyright 2017 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 policy 16 17 import ( 18 "context" 19 "fmt" 20 "sync" 21 "time" 22 23 protov1 "github.com/golang/protobuf/proto" 24 "google.golang.org/protobuf/proto" 25 26 "go.chromium.org/luci/common/logging" 27 "go.chromium.org/luci/config" 28 "go.chromium.org/luci/config/cfgclient" 29 ) 30 31 // luciConfigFetcher implements ConfigFetcher interface via LUCI Config client. 32 // 33 // It fetches all config files at a single revision: the first config file 34 // is fetched at HEAD, and the rest of them are fetched at the same revision. 35 type luciConfigFetcher struct { 36 m sync.Mutex 37 rev string 38 } 39 40 func (f *luciConfigFetcher) Revision() string { 41 f.m.Lock() 42 defer f.m.Unlock() 43 return f.rev 44 } 45 46 func (f *luciConfigFetcher) FetchTextProto(c context.Context, path string, out proto.Message) error { 47 logging.Infof(c, "Reading %q", path) 48 c, cancel := context.WithTimeout(c, 40*time.Second) // URL fetch deadline 49 defer cancel() 50 51 var meta config.Meta 52 err := cfgclient.Get(c, 53 "services/${appid}", 54 path, 55 cfgclient.ProtoText(protov1.MessageV1(out)), 56 &meta, 57 ) 58 if err != nil { 59 return err 60 } 61 62 f.m.Lock() 63 defer f.m.Unlock() 64 65 switch { 66 case f.rev == "": 67 f.rev = meta.Revision 68 case f.rev != meta.Revision: 69 // TODO(vadimsh): Lock all subsequent 'FetchTextProto' calls to the revision 70 // we fetched during the first call. Unfortunately cfgclient doesn't support 71 // fetching a specific revision. So we just fail if a wrong revision is 72 // fetched, assuming the state will "normalize" later. 73 return fmt.Errorf( 74 "expected config %q to be at rev %s, but got %s", 75 path, f.rev, meta.Revision) 76 } 77 78 return nil 79 }