@@ -12,49 +12,82 @@ import (
1212 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph"
1313 "github.com/gardener/machine-controller-manager-provider-azure/pkg/azure/access/errors"
1414 "github.com/gardener/machine-controller-manager-provider-azure/pkg/azure/instrument"
15- "k8s.io/utils/pointer"
15+ "k8s.io/klog/v2"
16+ "k8s.io/utils/ptr"
1617)
1718
1819const (
1920 resourceGraphQueryServiceLabel = "resource_graph_query"
2021)
2122
23+ // ResourceGraphClient is an interface for Azure Resource Graph client operations.
24+ // This allows for easier testing by enabling mock implementations.
25+ type ResourceGraphClient interface {
26+ Resources (ctx context.Context , query armresourcegraph.QueryRequest , options * armresourcegraph.ClientResourcesOptions ) (armresourcegraph.ClientResourcesResponse , error )
27+ }
28+
29+ // Azure client implements interface
30+ var _ ResourceGraphClient = (* armresourcegraph .Client )(nil )
31+
2232// MapperFn maps a row of result (represented as map[string]interface{}) to any type T.
2333type MapperFn [T any ] func (map [string ]interface {}) * T
2434
2535// QueryAndMap fires a resource graph KUSTO query constructing it from queryTemplate and templateArgs.
2636// The result of the query are then mapped using a mapperFn and the result or an error is returned.
2737// NOTE: All calls to this Azure API are instrumented as prometheus metric.
28- func QueryAndMap [T any ](ctx context.Context , client * armresourcegraph. Client , subscriptionID string , mapperFn MapperFn [T ], queryTemplate string , templateArgs ... any ) (results []T , err error ) {
38+ func QueryAndMap [T any ](ctx context.Context , client ResourceGraphClient , subscriptionID string , mapperFn MapperFn [T ], queryTemplate string , templateArgs ... any ) (results []T , err error ) {
2939 defer instrument .AZAPIMetricRecorderFn (resourceGraphQueryServiceLabel , & err )()
3040
3141 query := fmt .Sprintf (queryTemplate , templateArgs ... )
32- resources , err := client .Resources (ctx ,
33- armresourcegraph.QueryRequest {
42+ var skipToken * string
43+ pageCount := 0
44+
45+ // Continue fetching results while there is a skipToken
46+ for {
47+ queryRequest := armresourcegraph.QueryRequest {
3448 Query : to .Ptr (query ),
3549 Options : nil ,
3650 Subscriptions : []* string {to .Ptr (subscriptionID )},
37- }, nil )
51+ }
3852
39- if err != nil {
40- errors .LogAzAPIError (err , "ResourceGraphQuery failure to execute Query: %s" , query )
41- return nil , err
42- }
53+ // Set skipToken in options if present for subsequent pages
54+ if skipToken != nil {
55+ queryRequest .Options = & armresourcegraph.QueryRequestOptions {
56+ SkipToken : skipToken ,
57+ }
58+ }
4359
44- if resources .TotalRecords == pointer .Int64 (0 ) {
45- return results , nil
46- }
60+ resources , err := client .Resources (ctx , queryRequest , nil )
61+ if err != nil {
62+ errors .LogAzAPIError (err , "ResourceGraphQuery failure to execute Query: %s, with skipToken: %s" , query , ptr .Deref (skipToken , "<nil>" ))
63+ return nil , err
64+ }
65+ pageCount ++
4766
48- // resourceResponse.Data is a []interface{}
49- if objSlice , ok := resources .Data .([]interface {}); ok {
50- for _ , obj := range objSlice {
51- // Each obj in resourceResponse.Data is a map[string]Interface{}
52- rowElements := obj .(map [string ]interface {})
53- result := mapperFn (rowElements )
54- if result != nil {
55- results = append (results , * result )
67+ if ptr .Deref (resources .TotalRecords , 0 ) == 0 {
68+ klog .Infof ("Query completed: fetched %d pages, no results retrieved" , pageCount )
69+ return results , nil
70+ }
71+
72+ // resourceResponse.Data is a []interface{}
73+ if objSlice , ok := resources .Data .([]interface {}); ok {
74+ for _ , obj := range objSlice {
75+ // Each obj in resourceResponse.Data is a map[string]Interface{}
76+ rowElements := obj .(map [string ]interface {})
77+ result := mapperFn (rowElements )
78+ if result != nil {
79+ results = append (results , * result )
80+ }
5681 }
5782 }
83+ // Check if there are more pages to fetch and set skipToken for next iteration
84+ if resources .SkipToken == nil || * resources .SkipToken == "" {
85+ break
86+ }
87+ klog .Infof ("Fetching next page (page %d) with skipToken: %s" , pageCount + 1 , * resources .SkipToken )
88+ skipToken = resources .SkipToken
5889 }
59- return
90+
91+ klog .Infof ("Query completed: fetched %d pages, total results: %d" , pageCount , len (results ))
92+ return results , nil
6093}
0 commit comments