Skip to content

Commit 04763f7

Browse files
authored
[RC] Optimize retrieval of raw target files in ClientGetConfigs (#31987)
1 parent 007c8aa commit 04763f7

File tree

3 files changed

+140
-84
lines changed

3 files changed

+140
-84
lines changed

pkg/config/remote/service/service.go

Lines changed: 76 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -111,50 +111,21 @@ func (s *Service) getNewDirectorRoots(uptane uptaneClient, currentVersion uint64
111111
return roots, nil
112112
}
113113

114-
func (s *Service) getTargetFiles(uptane uptaneClient, products []rdata.Product, cachedTargetFiles []*pbgo.TargetFileMeta) ([]*pbgo.File, error) {
115-
productSet := make(map[rdata.Product]struct{})
116-
for _, product := range products {
117-
productSet[product] = struct{}{}
118-
}
119-
targets, err := uptane.Targets()
114+
func (s *Service) getTargetFiles(uptaneClient uptaneClient, targetFilePaths []string) ([]*pbgo.File, error) {
115+
files, err := uptaneClient.TargetFiles(targetFilePaths)
120116
if err != nil {
121117
return nil, err
122118
}
123-
cachedTargets := make(map[string]data.FileMeta)
124-
for _, cachedTarget := range cachedTargetFiles {
125-
hashes := make(data.Hashes)
126-
for _, hash := range cachedTarget.Hashes {
127-
h, err := hex.DecodeString(hash.Hash)
128-
if err != nil {
129-
return nil, err
130-
}
131-
hashes[hash.Algorithm] = h
132-
}
133-
cachedTargets[cachedTarget.Path] = data.FileMeta{
134-
Hashes: hashes,
135-
Length: cachedTarget.Length,
136-
}
137-
}
119+
138120
var configFiles []*pbgo.File
139-
for targetPath, targetMeta := range targets {
140-
configPathMeta, err := rdata.ParseConfigPath(targetPath)
141-
if err != nil {
142-
return nil, err
143-
}
144-
if _, inClientProducts := productSet[rdata.Product(configPathMeta.Product)]; inClientProducts {
145-
if notEqualErr := tufutil.FileMetaEqual(cachedTargets[targetPath], targetMeta.FileMeta); notEqualErr == nil {
146-
continue
147-
}
148-
fileContents, err := uptane.TargetFile(targetPath)
149-
if err != nil {
150-
return nil, err
151-
}
152-
configFiles = append(configFiles, &pbgo.File{
153-
Path: targetPath,
154-
Raw: fileContents,
155-
})
156-
}
121+
for path, contents := range files {
122+
// Note: This unconditionally succeeds as long as we don't change bufferDestination earlier
123+
configFiles = append(configFiles, &pbgo.File{
124+
Path: path,
125+
Raw: contents,
126+
})
157127
}
128+
158129
return configFiles, nil
159130
}
160131

@@ -211,6 +182,7 @@ type uptaneClient interface {
211182
StoredOrgUUID() (string, error)
212183
Targets() (data.TargetFiles, error)
213184
TargetFile(path string) ([]byte, error)
185+
TargetFiles(files []string) (map[string][]byte, error)
214186
TargetsMeta() ([]byte, error)
215187
TargetsCustom() ([]byte, error)
216188
TUFVersionState() (uptane.TUFVersions, error)
@@ -828,36 +800,30 @@ func (s *CoreAgentService) ClientGetConfigs(_ context.Context, request *pbgo.Cli
828800
if err != nil {
829801
return nil, err
830802
}
831-
targetsRaw, err := s.uptane.TargetsMeta()
803+
804+
directorTargets, err := s.uptane.Targets()
832805
if err != nil {
833806
return nil, err
834807
}
835-
targetFiles, err := s.getTargetFiles(s.uptane, rdata.StringListToProduct(request.Client.Products), request.CachedTargetFiles)
808+
matchedClientConfigs, err := executeTracerPredicates(request.Client, directorTargets)
836809
if err != nil {
837810
return nil, err
838811
}
839812

840-
directorTargets, err := s.uptane.Targets()
813+
neededFiles, err := filterNeededTargetFiles(matchedClientConfigs, request.CachedTargetFiles, directorTargets)
841814
if err != nil {
842815
return nil, err
843816
}
844-
matchedClientConfigs, err := executeTracerPredicates(request.Client, directorTargets)
817+
818+
targetFiles, err := s.getTargetFiles(s.uptane, neededFiles)
845819
if err != nil {
846820
return nil, err
847821
}
848822

849-
// filter files to only return the ones that predicates marked for this client
850-
matchedConfigsMap := make(map[string]interface{})
851-
for _, configPointer := range matchedClientConfigs {
852-
matchedConfigsMap[configPointer] = struct{}{}
853-
}
854-
filteredFiles := make([]*pbgo.File, 0, len(matchedClientConfigs))
855-
for _, targetFile := range targetFiles {
856-
if _, ok := matchedConfigsMap[targetFile.Path]; ok {
857-
filteredFiles = append(filteredFiles, targetFile)
858-
}
823+
targetsRaw, err := s.uptane.TargetsMeta()
824+
if err != nil {
825+
return nil, err
859826
}
860-
861827
canonicalTargets, err := enforceCanonicalJSON(targetsRaw)
862828
if err != nil {
863829
return nil, err
@@ -866,11 +832,42 @@ func (s *CoreAgentService) ClientGetConfigs(_ context.Context, request *pbgo.Cli
866832
return &pbgo.ClientGetConfigsResponse{
867833
Roots: roots,
868834
Targets: canonicalTargets,
869-
TargetFiles: filteredFiles,
835+
TargetFiles: targetFiles,
870836
ClientConfigs: matchedClientConfigs,
871837
}, nil
872838
}
873839

840+
func filterNeededTargetFiles(neededConfigs []string, cachedTargetFiles []*pbgo.TargetFileMeta, tufTargets data.TargetFiles) ([]string, error) {
841+
// Build an O(1) lookup of cached target files
842+
cachedTargetsMap := make(map[string]data.FileMeta)
843+
for _, cachedTarget := range cachedTargetFiles {
844+
hashes := make(data.Hashes)
845+
for _, hash := range cachedTarget.Hashes {
846+
h, err := hex.DecodeString(hash.Hash)
847+
if err != nil {
848+
return nil, err
849+
}
850+
hashes[hash.Algorithm] = h
851+
}
852+
cachedTargetsMap[cachedTarget.Path] = data.FileMeta{
853+
Hashes: hashes,
854+
Length: cachedTarget.Length,
855+
}
856+
}
857+
858+
// We don't need to pull the raw contents if the client already has the exact version of the file cached
859+
filteredList := make([]string, 0, len(neededConfigs))
860+
for _, path := range neededConfigs {
861+
if notEqualErr := tufutil.FileMetaEqual(cachedTargetsMap[path], tufTargets[path].FileMeta); notEqualErr == nil {
862+
continue
863+
}
864+
865+
filteredList = append(filteredList, path)
866+
}
867+
868+
return filteredList, nil
869+
}
870+
874871
// ConfigGetState returns the state of the configuration and the director repos in the local store
875872
func (s *CoreAgentService) ConfigGetState() (*pbgo.GetStateConfigResponse, error) {
876873
state, err := s.uptane.State()
@@ -1117,29 +1114,14 @@ func (c *HTTPClient) getUpdate(
11171114
if tufVersions.DirectorTargets == currentTargetsVersion {
11181115
return nil, nil
11191116
}
1120-
roots, err := c.getNewDirectorRoots(c.uptane, currentRootVersion, tufVersions.DirectorRoot)
1121-
if err != nil {
1122-
return nil, err
1123-
}
1124-
targetsRaw, err := c.uptane.TargetsMeta()
1125-
if err != nil {
1126-
return nil, err
1127-
}
1128-
targetFiles, err := c.getTargetFiles(c.uptane, rdata.StringListToProduct(products), cachedTargetFiles)
1129-
if err != nil {
1130-
return nil, err
1131-
}
1132-
1133-
canonicalTargets, err := enforceCanonicalJSON(targetsRaw)
1134-
if err != nil {
1135-
return nil, err
1136-
}
11371117

1118+
// Filter out files that either:
1119+
// - don't correspond to the product list the client is requesting
1120+
// - have expired
11381121
directorTargets, err := c.uptane.Targets()
11391122
if err != nil {
11401123
return nil, err
11411124
}
1142-
11431125
productsMap := make(map[string]struct{})
11441126
for _, product := range products {
11451127
productsMap[product] = struct{}{}
@@ -1165,14 +1147,33 @@ func (c *HTTPClient) getUpdate(
11651147

11661148
configs = append(configs, path)
11671149
}
1150+
span.SetTag("configs.returned", configs)
1151+
span.SetTag("configs.expired", expiredConfigs)
11681152

1153+
// Gather the files and map-ify them for the state data structure
1154+
targetFiles, err := c.getTargetFiles(c.uptane, configs)
1155+
if err != nil {
1156+
return nil, err
1157+
}
11691158
fileMap := make(map[string][]byte, len(targetFiles))
11701159
for _, f := range targetFiles {
11711160
fileMap[f.Path] = f.Raw
11721161
}
11731162

1174-
span.SetTag("configs.returned", configs)
1175-
span.SetTag("configs.expired", expiredConfigs)
1163+
// Gather some TUF metadata files we need to send down
1164+
roots, err := c.getNewDirectorRoots(c.uptane, currentRootVersion, tufVersions.DirectorRoot)
1165+
if err != nil {
1166+
return nil, err
1167+
}
1168+
targetsRaw, err := c.uptane.TargetsMeta()
1169+
if err != nil {
1170+
return nil, err
1171+
}
1172+
canonicalTargets, err := enforceCanonicalJSON(targetsRaw)
1173+
if err != nil {
1174+
return nil, err
1175+
}
1176+
11761177
return &state.Update{
11771178
TUFRoots: roots,
11781179
TUFTargets: canonicalTargets,

pkg/config/remote/service/service_test.go

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ func (m *mockUptane) TargetFile(path string) ([]byte, error) {
116116
return args.Get(0).([]byte), args.Error(1)
117117
}
118118

119+
func (m *mockUptane) TargetFiles(files []string) (map[string][]byte, error) {
120+
args := m.Called(files)
121+
return args.Get(0).(map[string][]byte), args.Error(1)
122+
}
123+
119124
func (m *mockUptane) TargetsMeta() ([]byte, error) {
120125
args := m.Called()
121126
return args.Get(0).([]byte), args.Error(1)
@@ -486,8 +491,8 @@ func TestService(t *testing.T) {
486491
}, nil)
487492
uptaneClient.On("DirectorRoot", uint64(3)).Return(root3, nil)
488493
uptaneClient.On("DirectorRoot", uint64(4)).Return(root4, nil)
489-
uptaneClient.On("TargetFile", "datadog/2/APM_SAMPLING/id/1").Return(fileAPM1, nil)
490-
uptaneClient.On("TargetFile", "datadog/2/APM_SAMPLING/id/2").Return(fileAPM2, nil)
494+
495+
uptaneClient.On("TargetFiles", mock.MatchedBy(listsEqual([]string{"datadog/2/APM_SAMPLING/id/1", "datadog/2/APM_SAMPLING/id/2"}))).Return(map[string][]byte{"datadog/2/APM_SAMPLING/id/1": fileAPM1, "datadog/2/APM_SAMPLING/id/2": fileAPM2}, nil)
491496
uptaneClient.On("Update", lastConfigResponse).Return(nil)
492497
api.On("Fetch", mock.Anything, &pbgo.LatestConfigsRequest{
493498
Hostname: service.hostname,
@@ -513,7 +518,7 @@ func TestService(t *testing.T) {
513518
configResponse, err := service.ClientGetConfigs(context.Background(), &pbgo.ClientGetConfigsRequest{Client: client})
514519
assert.NoError(t, err)
515520
assert.ElementsMatch(t, [][]byte{canonicalRoot3, canonicalRoot4}, configResponse.Roots)
516-
assert.ElementsMatch(t, []*pbgo.File{{Path: "datadog/2/APM_SAMPLING/id/1", Raw: fileAPM1}, {Path: "datadog/2/APM_SAMPLING/id/2", Raw: fileAPM2}}, configResponse.TargetFiles)
521+
assert.ElementsMatch(t, []*pbgo.File{{Path: "datadog/2/APM_SAMPLING/id/1", Raw: fileAPM1}, {Path: "datadog/2/APM_SAMPLING/id/2", Raw: fileAPM2}}, configResponse.TargetFiles, nil)
517522
assert.Equal(t, canonicalTargets, configResponse.Targets)
518523
assert.ElementsMatch(t,
519524
configResponse.ClientConfigs,
@@ -597,8 +602,7 @@ func TestServiceClientPredicates(t *testing.T) {
597602
DirectorRoot: 1,
598603
DirectorTargets: 5,
599604
}, nil)
600-
uptaneClient.On("TargetFile", "datadog/2/APM_SAMPLING/id/1").Return([]byte(``), nil)
601-
uptaneClient.On("TargetFile", "datadog/2/APM_SAMPLING/id/2").Return([]byte(``), nil)
605+
uptaneClient.On("TargetFiles", mock.MatchedBy(listsEqual([]string{"datadog/2/APM_SAMPLING/id/1", "datadog/2/APM_SAMPLING/id/2"}))).Return(map[string][]byte{"datadog/2/APM_SAMPLING/id/1": []byte(``), "datadog/2/APM_SAMPLING/id/2": []byte(``)}, nil)
602606
uptaneClient.On("Update", lastConfigResponse).Return(nil)
603607
api.On("Fetch", mock.Anything, &pbgo.LatestConfigsRequest{
604608
Hostname: service.hostname,
@@ -887,8 +891,7 @@ func TestConfigExpiration(t *testing.T) {
887891
DirectorRoot: 1,
888892
DirectorTargets: 5,
889893
}, nil)
890-
uptaneClient.On("TargetFile", "datadog/2/APM_SAMPLING/id/1").Return([]byte(``), nil)
891-
uptaneClient.On("TargetFile", "datadog/2/APM_SAMPLING/id/2").Return([]byte(``), nil)
894+
uptaneClient.On("TargetFiles", []string{"datadog/2/APM_SAMPLING/id/1"}).Return(map[string][]byte{"datadog/2/APM_SAMPLING/id/1": []byte(``), "datadog/2/APM_SAMPLING/id/2": []byte(``)}, nil)
892895
uptaneClient.On("Update", lastConfigResponse).Return(nil)
893896
api.On("Fetch", mock.Anything, &pbgo.LatestConfigsRequest{
894897
Hostname: service.hostname,
@@ -1190,7 +1193,7 @@ func TestHTTPClientRecentUpdate(t *testing.T) {
11901193
},
11911194
nil,
11921195
)
1193-
uptaneClient.On("TargetFile", "datadog/2/TESTING1/id/1").Return([]byte(`testing_1`), nil)
1196+
uptaneClient.On("TargetFiles", []string{"datadog/2/TESTING1/id/1"}).Return(map[string][]byte{"datadog/2/TESTING1/id/1": []byte(`testing_1`)}, nil)
11941197

11951198
client := setupCDNClient(t, uptaneClient)
11961199
defer client.Close()
@@ -1236,7 +1239,7 @@ func TestHTTPClientUpdateSuccess(t *testing.T) {
12361239
},
12371240
nil,
12381241
)
1239-
uptaneClient.On("TargetFile", "datadog/2/TESTING1/id/1").Return([]byte(`testing_1`), nil)
1242+
uptaneClient.On("TargetFiles", []string{"datadog/2/TESTING1/id/1"}).Return(map[string][]byte{"datadog/2/TESTING1/id/1": []byte(`testing_1`)}, nil)
12401243

12411244
updateErr := fmt.Errorf("uh oh")
12421245
if tt.updateSucceeds {
@@ -1261,3 +1264,24 @@ func TestHTTPClientUpdateSuccess(t *testing.T) {
12611264
})
12621265
}
12631266
}
1267+
1268+
func listsEqual(mustMatch []string) func(candidate []string) bool {
1269+
return func(candidate []string) bool {
1270+
if len(candidate) != len(mustMatch) {
1271+
return false
1272+
}
1273+
1274+
candidateSet := make(map[string]struct{})
1275+
for _, item := range candidate {
1276+
candidateSet[item] = struct{}{}
1277+
}
1278+
1279+
for _, item := range mustMatch {
1280+
if _, ok := candidateSet[item]; !ok {
1281+
return false
1282+
}
1283+
}
1284+
1285+
return true
1286+
}
1287+
}

pkg/config/remote/uptane/client.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,37 @@ func (c *Client) TargetFile(path string) ([]byte, error) {
342342
return c.unsafeTargetFile(path)
343343
}
344344

345+
// TargetFiles returns the content of various multiple target files if the repository is in a
346+
// verified state.
347+
func (c *Client) TargetFiles(targetFiles []string) (map[string][]byte, error) {
348+
c.Lock()
349+
defer c.Unlock()
350+
351+
err := c.verify()
352+
if err != nil {
353+
return nil, err
354+
}
355+
356+
// Build the storage space
357+
destinations := make(map[string]client.Destination)
358+
for _, path := range targetFiles {
359+
destinations[path] = &bufferDestination{}
360+
}
361+
362+
err = c.directorTUFClient.DownloadBatch(destinations)
363+
if err != nil {
364+
return nil, err
365+
}
366+
367+
// Build the return type
368+
files := make(map[string][]byte)
369+
for path, contents := range destinations {
370+
files[path] = contents.(*bufferDestination).Bytes()
371+
}
372+
373+
return files, nil
374+
}
375+
345376
// TargetsMeta returns the current raw targets.json meta of this uptane client
346377
func (c *Client) TargetsMeta() ([]byte, error) {
347378
c.Lock()

0 commit comments

Comments
 (0)