11package main
22
33import (
4- "context"
4+ "errors"
5+ "fmt"
6+ "io"
57 "log/slog"
68 "net/http"
79 "net/url"
@@ -10,11 +12,13 @@ import (
1012 armruntime "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/runtime"
1113 "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
1214 "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
13- armquota "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/quota/armquota/v2"
1415 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions"
1516 "github.com/prometheus/client_golang/prometheus"
1617 "github.com/webdevops/go-common/prometheus/collector"
1718 "github.com/webdevops/go-common/utils/to"
19+
20+ "github.com/webdevops/azure-resourcemanager-exporter/config"
21+ "github.com/webdevops/azure-resourcemanager-exporter/models/quota"
1822)
1923
2024type MetricsCollectorAzureRmQuota struct {
@@ -105,10 +109,15 @@ func (m *MetricsCollectorAzureRmQuota) Collect(callback chan<- func()) {
105109
106110 if registered , err := AzureClient .IsResourceProviderRegistered (m .Context (), * subscription .SubscriptionID , "Microsoft.Capacity" ); registered {
107111 for _ , provider := range Config .Collectors .Quota .ResourceProviders {
108- if registered , err := AzureClient .IsResourceProviderRegistered (m .Context (), * subscription .SubscriptionID , provider ); registered {
109- m .collectQuotaUsage (subscription , provider , logger , callback )
112+ if registered , err := AzureClient .IsResourceProviderRegistered (m .Context (), * subscription .SubscriptionID , provider .Provider ); registered {
113+
114+ for _ , location := range Config .Azure .Locations {
115+ quotaLogger := logger .With (slog .String ("provider" , provider .Provider ), slog .String ("location" , location ))
116+ m .collectQuotaUsage (subscription , provider , location , quotaLogger , callback )
117+ }
118+
110119 } else if err != nil {
111- logger .Error ("quota for resourceProvider requested, but not registered" , slog .String ("resourceProvider" , provider ), slog .Any ("error" , err ))
120+ logger .Error ("quota for resourceProvider requested, but not registered" , slog .String ("resourceProvider" , provider . Provider ), slog .Any ("error" , err ))
112121 }
113122 }
114123 } else if err != nil {
@@ -138,15 +147,14 @@ func (m *MetricsCollectorAzureRmQuota) collectAuthorizationUsage(subscription *a
138147 quotaLimitMetric := m .Collector .GetMetricList ("quotaLimit" )
139148 quotaUsageMetric := m .Collector .GetMetricList ("quotaUsage" )
140149
141- ctx := context .Background ()
142-
143150 urlPath := "/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/roleassignmentsusagemetrics"
144151 urlPath = strings .ReplaceAll (urlPath , "{subscriptionId}" , url .PathEscape (* subscription .SubscriptionID ))
145152
146- req , err := runtime .NewRequest (ctx , http .MethodGet , runtime .JoinPaths (ep , urlPath ))
153+ req , err := runtime .NewRequest (m . Context () , http .MethodGet , runtime .JoinPaths (ep , urlPath ))
147154 if err != nil {
148155 panic (err )
149156 }
157+ defer req .Close ()
150158 reqQP := req .Raw ().URL .Query ()
151159 reqQP .Set ("api-version" , "2019-08-01-preview" )
152160 req .Raw ().URL .RawQuery = reqQP .Encode ()
@@ -159,11 +167,7 @@ func (m *MetricsCollectorAzureRmQuota) collectAuthorizationUsage(subscription *a
159167 defer resp .Body .Close ()
160168
161169 if runtime .HasStatusCode (resp , http .StatusOK ) {
162- result := struct {
163- RoleAssignmentsLimit float64 `json:"roleAssignmentsLimit"`
164- RoleAssignmentsCurrentCount float64 `json:"roleAssignmentsCurrentCount"`
165- RoleAssignmentsRemainingCount float64 `json:"roleAssignmentsRemainingCount"`
166- }{}
170+ result := quota.RoleAssignmentUsage {}
167171
168172 if err := runtime .UnmarshalAsJSON (resp , & result ); err == nil {
169173 currentValue := result .RoleAssignmentsCurrentCount
@@ -187,121 +191,90 @@ func (m *MetricsCollectorAzureRmQuota) collectAuthorizationUsage(subscription *a
187191 }
188192}
189193
190- type (
191- Quota struct {
192- Labels prometheus.Labels
193- Limit * float64
194- Current * float64
195- }
196-
197- QuotaList map [string ]* Quota
198- )
199-
200- func (q * QuotaList ) Get (name string ) * Quota {
201- name = strings .ToLower (name )
202- if _ , ok := (* q )[name ]; ! ok {
203- (* q )[name ] = & Quota {}
204- }
205-
206- return (* q )[name ]
207- }
208-
209194// collectQuotaUsage collect generic quota usages
210- func (m * MetricsCollectorAzureRmQuota ) collectQuotaUsage (subscription * armsubscriptions.Subscription , provider string , logger * slog.Logger , callback chan <- func ()) {
211- quotaClient , err := armquota .NewClient (AzureClient .GetCred (), AzureClient .NewArmClientOptions ())
212- if err != nil {
213- panic (err )
214- }
215-
216- usageClient , err := armquota .NewUsagesClient (AzureClient .GetCred (), AzureClient .NewArmClientOptions ())
217- if err != nil {
218- panic (err )
219- }
220-
195+ func (m * MetricsCollectorAzureRmQuota ) collectQuotaUsage (subscription * armsubscriptions.Subscription , provider config.CollectorQuotaResourceProvider , location string , logger * slog.Logger , callback chan <- func ()) {
221196 quotaMetric := m .Collector .GetMetricList ("quota" )
222197 quotaCurrentMetric := m .Collector .GetMetricList ("quotaCurrent" )
223198 quotaLimitMetric := m .Collector .GetMetricList ("quotaLimit" )
224199 quotaUsageMetric := m .Collector .GetMetricList ("quotaUsage" )
225200
226- for _ , location := range Config .Azure .Locations {
227- scope := "/subscriptions/{subscriptionId}/providers/{provider}/locations/{location}"
228- scope = strings .ReplaceAll (scope , "{subscriptionId}" , url .PathEscape (* subscription .SubscriptionID ))
229- scope = strings .ReplaceAll (scope , "{provider}" , url .PathEscape (provider ))
230- scope = strings .ReplaceAll (scope , "{location}" , url .PathEscape (location ))
231-
232- quotaList := QuotaList {}
233-
234- // -----------------------
235- // Quotas
236- quotaPager := quotaClient .NewListPager (scope , nil )
237- for quotaPager .More () {
238- result , err := quotaPager .NextPage (m .Context ())
239- if err != nil {
240- panic (err )
241- }
201+ options := AzureClient .NewArmClientOptions ()
202+ ep := cloud .AzurePublic .Services [cloud .ResourceManager ].Endpoint
203+ if c , ok := options .Cloud .Services [cloud .ResourceManager ]; ok {
204+ ep = c .Endpoint
205+ }
242206
243- if result .Value == nil {
244- continue
245- }
207+ pl , err := armruntime .NewPipeline ("azurerm-quota" , gitTag , AzureClient .GetCred (), runtime.PipelineOptions {}, options )
208+ if err != nil {
209+ logger .Error ("failed to create arm client" , slog .Any ("error" , err ))
210+ panic (err )
211+ }
246212
247- for _ , row := range result .Value {
248- switch v := row .Properties .Limit .(type ) {
249- case * armquota.LimitObject :
250- if v .Value != nil {
251- quotaName := to .String (row .Name )
252-
253- labels := prometheus.Labels {
254- "subscriptionID" : to .StringLower (subscription .SubscriptionID ),
255- "location" : strings .ToLower (location ),
256- "provider" : provider ,
257- "quota" : to .String (row .Properties .Name .Value ),
258- "quotaName" : to .String (row .Properties .Name .LocalizedValue ),
259- }
260-
261- quotaList .Get (quotaName ).Limit = to .Ptr (float64 (to .Number (v .Value )))
262- quotaList .Get (quotaName ).Labels = labels
263- }
264- }
265- }
213+ urlPath := "/subscriptions/{subscriptionId}/providers/{provider}/locations/{location}/usages"
214+ urlPath = strings .ReplaceAll (urlPath , "{subscriptionId}" , url .PathEscape (* subscription .SubscriptionID ))
215+ urlPath = strings .ReplaceAll (urlPath , "{provider}" , url .PathEscape (provider .Provider ))
216+ urlPath = strings .ReplaceAll (urlPath , "{location}" , url .PathEscape (location ))
217+
218+ requestUrl := runtime .JoinPaths (ep , urlPath )
219+ fmt .Println (requestUrl )
220+ for {
221+ req , err := runtime .NewRequest (m .Context (), http .MethodGet , requestUrl )
222+ if err != nil {
223+ logger .Error ("failed to create request" , slog .Any ("error" , err ))
224+ panic (err )
225+ }
226+ defer req .Close ()
227+ reqQP := req .Raw ().URL .Query ()
228+ reqQP .Set ("api-version" , provider .ApiVersion )
229+ req .Raw ().URL .RawQuery = reqQP .Encode ()
230+ req .Raw ().Header ["Accept" ] = []string {"application/json" }
231+
232+ resp , err := pl .Do (req )
233+ if err != nil {
234+ logger .Error ("failed to send request" , slog .Any ("error" , err ))
235+ panic (err )
266236 }
237+ defer resp .Body .Close ()
267238
268- // -----------------------
269- // Usages
270- usagePager := usageClient .NewListPager (scope , nil )
271- for usagePager .More () {
272- result , err := usagePager .NextPage (m .Context ())
239+ if ! runtime .HasStatusCode (resp , http .StatusOK ) {
240+ buf := new (strings.Builder )
241+ _ , err := io .Copy (buf , resp .Body )
273242 if err != nil {
274243 panic (err )
275244 }
245+ logger .Error ("request failed" , slog .String ("statusCode" , resp .Status ), slog .Any ("error" , errors .New (buf .String ())))
246+ panic ("request failed" )
247+ }
276248
277- if result .Value == nil {
278- continue
279- }
280-
281- for _ , row := range result .Value {
282- quotaName := to .String (row .Name )
283- value := float64 (to .Number (row .Properties .Usages .Value ))
249+ result := quota.ListUsageResult {}
250+ if err := runtime .UnmarshalAsJSON (resp , & result ); err == nil {
251+ for _ , quotaUsage := range result .Value {
284252
285253 labels := prometheus.Labels {
286254 "subscriptionID" : to .StringLower (subscription .SubscriptionID ),
287255 "location" : strings .ToLower (location ),
288- "provider" : provider ,
289- "quota" : to .String (row . Properties .Name .Value ),
290- "quotaName" : to .String (row . Properties .Name .LocalizedValue ),
256+ "provider" : provider . Provider ,
257+ "quota" : to .String (quotaUsage .Name .Value ),
258+ "quotaName" : to .String (quotaUsage .Name .LocalizedValue ),
291259 }
292260
293- quotaList .Get (quotaName ).Current = to .Ptr (value )
294- quotaList .Get (quotaName ).Labels = labels
261+ quotaMetric .Add (labels , 1 )
262+ quotaCurrentMetric .AddIfNotNil (labels , quotaUsage .CurrentValue )
263+ quotaLimitMetric .AddIfNotNil (labels , quotaUsage .Limit )
264+ if quotaUsage .CurrentValue != nil && quotaUsage .Limit != nil && * quotaUsage .Limit != 0 {
265+ quotaUsageMetric .Add (labels , * quotaUsage .CurrentValue / * quotaUsage .Limit )
266+ }
295267 }
268+ } else {
269+ logger .Error ("failed to parse request" , slog .Any ("error" , err ))
270+ panic ("parsing failed" )
296271 }
297272
298- for _ , quota := range quotaList {
299- quotaMetric .Add (quota .Labels , 1 )
300- quotaCurrentMetric .AddIfNotNil (quota .Labels , quota .Current )
301- quotaLimitMetric .AddIfNotNil (quota .Labels , quota .Limit )
302- if quota .Current != nil && quota .Limit != nil && * quota .Limit != 0 {
303- quotaUsageMetric .Add (quota .Labels , * quota .Current / * quota .Limit )
304- }
273+ if result .NextLink != nil && * result .NextLink != "" {
274+ requestUrl = * result .NextLink
275+ continue
305276 }
277+
278+ break
306279 }
307280}
0 commit comments