Skip to content

Commit 680f3e7

Browse files
author
zhenghanzhou
committed
Update
Signed-off-by: zhenghanzhou <zhenghanzhou@microsoft.com>
1 parent c2d4c44 commit 680f3e7

File tree

2 files changed

+88
-141
lines changed

2 files changed

+88
-141
lines changed

pkg/scalers/azure_log_analytics_scaler.go

Lines changed: 86 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package scalers
1919
import (
2020
"context"
2121
"fmt"
22-
"strconv"
2322
"strings"
2423
"time"
2524

@@ -48,18 +47,77 @@ type azureLogAnalyticsScaler struct {
4847
}
4948

5049
type azureLogAnalyticsMetadata struct {
51-
tenantID string
52-
clientID string
53-
clientSecret string
54-
workspaceID string
55-
podIdentity kedav1alpha1.AuthPodIdentity
56-
query string
57-
threshold float64
58-
activationThreshold float64
59-
triggerIndex int
60-
cloud azcloud.Configuration
61-
unsafeSsl bool
62-
timeout time.Duration // custom HTTP client timeout
50+
TenantID string `keda:"name=tenantId, order=authParams;triggerMetadata;resolvedEnv"`
51+
ClientID string `keda:"name=clientId, order=authParams;triggerMetadata;resolvedEnv"`
52+
ClientSecret string `keda:"name=clientSecret, order=authParams;triggerMetadata;resolvedEnv"`
53+
WorkspaceID string `keda:"name=workspaceId, order=authParams;triggerMetadata;resolvedEnv"`
54+
PodIdentity kedav1alpha1.AuthPodIdentity
55+
Query string `keda:"name=query, order=triggerMetadata"`
56+
Threshold float64 `keda:"name=threshold, order=triggerMetadata"`
57+
ActivationThreshold float64 `keda:"name=activationThreshold, order=triggerMetadata, default=0"`
58+
LogAnalyticsResourceURL string `keda:"name=logAnalyticsResourceURL, order=triggerMetadata, optional"`
59+
TriggerIndex int
60+
CloudName string `keda:"name=cloud, order=triggerMetadata, default=azurePublicCloud"`
61+
Cloud azcloud.Configuration
62+
UnsafeSsl bool `keda:"name=unsafeSsl, order=triggerMetadata, default=false"`
63+
MillisecondTimeout int32 `keda:"name=timeout, order=triggerMetadata, optional"`
64+
Timeout time.Duration // custom HTTP client timeout
65+
}
66+
67+
func (m *azureLogAnalyticsMetadata) Validate() error {
68+
missingParameter := ""
69+
70+
switch m.PodIdentity.Provider {
71+
case "", kedav1alpha1.PodIdentityProviderNone:
72+
if m.TenantID == "" {
73+
missingParameter = "tenantId"
74+
}
75+
if m.ClientID == "" {
76+
missingParameter = "clientId"
77+
}
78+
if m.ClientSecret == "" {
79+
missingParameter = "clientSecret"
80+
}
81+
case kedav1alpha1.PodIdentityProviderAzureWorkload:
82+
break
83+
default:
84+
return fmt.Errorf("error parsing metadata. Details: Log Analytics Scaler doesn't support pod identity %s", m.PodIdentity.Provider)
85+
}
86+
87+
if m.WorkspaceID == "" {
88+
missingParameter = "workspaceId"
89+
}
90+
if m.Query == "" {
91+
missingParameter = "query"
92+
}
93+
94+
m.Cloud = azcloud.AzurePublic
95+
if strings.EqualFold(m.CloudName, azure.PrivateCloud) {
96+
if m.LogAnalyticsResourceURL != "" {
97+
m.Cloud.Services[azquery.ServiceNameLogs] = azcloud.ServiceConfiguration{
98+
Endpoint: fmt.Sprintf("%s/v1", m.LogAnalyticsResourceURL),
99+
Audience: m.LogAnalyticsResourceURL,
100+
}
101+
} else {
102+
return fmt.Errorf("logAnalyticsResourceURL must be provided for %s cloud type", azure.PrivateCloud)
103+
}
104+
} else if resource, ok := azure.AzureClouds[strings.ToUpper(m.CloudName)]; ok {
105+
m.Cloud = resource
106+
} else {
107+
return fmt.Errorf("there is no cloud environment matching the name %s", m.CloudName)
108+
}
109+
110+
if m.MillisecondTimeout > 0 {
111+
m.Timeout = time.Duration(m.MillisecondTimeout) * time.Millisecond
112+
} else if m.MillisecondTimeout < 0 {
113+
return fmt.Errorf("timeout must be greater than 0")
114+
}
115+
116+
if missingParameter != "" {
117+
return fmt.Errorf("error parsing metadata. Details: %s was not found in metadata. Check your ScaledObject configuration", missingParameter)
118+
}
119+
120+
return nil
63121
}
64122

65123
// NewAzureLogAnalyticsScaler creates a new Azure Log Analytics Scaler
@@ -96,7 +154,7 @@ func CreateAzureLogsClient(config *scalersconfig.ScalerConfig, meta *azureLogAna
96154
var err error
97155
switch config.PodIdentity.Provider {
98156
case "", kedav1alpha1.PodIdentityProviderNone:
99-
creds, err = azidentity.NewClientSecretCredential(meta.tenantID, meta.clientID, meta.clientSecret, nil)
157+
creds, err = azidentity.NewClientSecretCredential(meta.TenantID, meta.ClientID, meta.ClientSecret, nil)
100158
case kedav1alpha1.PodIdentityProviderAzureWorkload:
101159
creds, err = azure.NewChainedCredential(logger, config.PodIdentity)
102160
default:
@@ -107,8 +165,8 @@ func CreateAzureLogsClient(config *scalersconfig.ScalerConfig, meta *azureLogAna
107165
}
108166
client, err := azquery.NewLogsClient(creds, &azquery.LogsClientOptions{
109167
ClientOptions: policy.ClientOptions{
110-
Transport: kedautil.CreateHTTPClient(meta.timeout, meta.unsafeSsl),
111-
Cloud: meta.cloud,
168+
Transport: kedautil.CreateHTTPClient(meta.Timeout, meta.UnsafeSsl),
169+
Cloud: meta.Cloud,
112170
},
113171
})
114172
if err != nil {
@@ -118,124 +176,15 @@ func CreateAzureLogsClient(config *scalersconfig.ScalerConfig, meta *azureLogAna
118176
}
119177

120178
func parseAzureLogAnalyticsMetadata(config *scalersconfig.ScalerConfig) (*azureLogAnalyticsMetadata, error) {
121-
meta := azureLogAnalyticsMetadata{}
122-
switch config.PodIdentity.Provider {
123-
case "", kedav1alpha1.PodIdentityProviderNone:
124-
// Getting tenantId
125-
tenantID, err := getParameterFromConfig(config, "tenantId", true)
126-
if err != nil {
127-
return nil, err
128-
}
129-
meta.tenantID = tenantID
130-
131-
// Getting clientId
132-
clientID, err := getParameterFromConfig(config, "clientId", true)
133-
if err != nil {
134-
return nil, err
135-
}
136-
meta.clientID = clientID
137-
138-
// Getting clientSecret
139-
clientSecret, err := getParameterFromConfig(config, "clientSecret", true)
140-
if err != nil {
141-
return nil, err
142-
}
143-
meta.clientSecret = clientSecret
144-
145-
meta.podIdentity = config.PodIdentity
146-
case kedav1alpha1.PodIdentityProviderAzureWorkload:
147-
meta.podIdentity = config.PodIdentity
148-
default:
149-
return nil, fmt.Errorf("error parsing metadata. Details: Log Analytics Scaler doesn't support pod identity %s", config.PodIdentity.Provider)
150-
}
151-
152-
// Getting workspaceId
153-
workspaceID, err := getParameterFromConfig(config, "workspaceId", true)
154-
if err != nil {
155-
return nil, err
156-
}
157-
meta.workspaceID = workspaceID
158-
159-
// Getting query, observe that we dont check AuthParams for query
160-
query, err := getParameterFromConfig(config, "query", false)
161-
if err != nil {
162-
return nil, err
163-
}
164-
meta.query = query
165-
166-
// Getting threshold, observe that we don't check AuthParams for threshold
167-
val, err := getParameterFromConfig(config, "threshold", false)
168-
if err != nil {
169-
if config.AsMetricSource {
170-
val = "0"
171-
} else {
172-
return nil, err
173-
}
174-
}
175-
threshold, err := strconv.ParseFloat(val, 64)
176-
if err != nil {
177-
return nil, fmt.Errorf("error parsing metadata. Details: can't parse threshold. Inner Error: %w", err)
178-
}
179-
meta.threshold = threshold
180-
181-
// Getting activationThreshold
182-
meta.activationThreshold = 0
183-
val, err = getParameterFromConfig(config, "activationThreshold", false)
184-
if err == nil {
185-
activationThreshold, err := strconv.ParseFloat(val, 64)
186-
if err != nil {
187-
return nil, fmt.Errorf("error parsing metadata. Details: can't parse threshold. Inner Error: %w", err)
188-
}
189-
meta.activationThreshold = activationThreshold
190-
}
191-
meta.triggerIndex = config.TriggerIndex
192-
193-
meta.cloud = azcloud.AzurePublic
194-
if cloud, ok := config.TriggerMetadata["cloud"]; ok {
195-
if strings.EqualFold(cloud, azure.PrivateCloud) {
196-
if resource, ok := config.TriggerMetadata["logAnalyticsResourceURL"]; ok && resource != "" {
197-
meta.cloud.Services[azquery.ServiceNameLogs] = azcloud.ServiceConfiguration{
198-
Endpoint: fmt.Sprintf("%s/v1", resource),
199-
Audience: resource,
200-
}
201-
} else {
202-
return nil, fmt.Errorf("logAnalyticsResourceURL must be provided for %s cloud type", azure.PrivateCloud)
203-
}
204-
} else if resource, ok := azure.AzureClouds[strings.ToUpper(cloud)]; ok {
205-
meta.cloud = resource
206-
} else {
207-
return nil, fmt.Errorf("there is no cloud environment matching the name %s", cloud)
208-
}
209-
}
210-
211-
// Getting unsafeSsl, observe that we don't check AuthParams for unsafeSsl
212-
meta.unsafeSsl = false
213-
unsafeSslVal, err := getParameterFromConfig(config, "unsafeSsl", false)
214-
if err == nil {
215-
unsafeSsl, err := strconv.ParseBool(unsafeSslVal)
216-
if err != nil {
217-
return nil, fmt.Errorf("error parsing metadata. Details: can't parse unsafeSsl. Inner Error: %w", err)
218-
}
219-
meta.unsafeSsl = unsafeSsl
220-
}
221-
222-
// Resolve HTTP client timeout
223-
meta.timeout = config.GlobalHTTPTimeout
224-
timeoutVal, err := getParameterFromConfig(config, "timeout", false)
225-
if err == nil {
226-
timeout, err := strconv.Atoi(timeoutVal)
227-
if err != nil {
228-
return nil, fmt.Errorf("unable to parse timeout: %w", err)
229-
}
230-
231-
if timeout <= 0 {
232-
return nil, fmt.Errorf("timeout must be greater than 0: %w", err)
233-
}
234-
235-
meta.timeout = time.Duration(timeout) * time.Millisecond
179+
meta := &azureLogAnalyticsMetadata{}
180+
meta.TriggerIndex = config.TriggerIndex
181+
meta.Timeout = config.GlobalHTTPTimeout
182+
meta.PodIdentity = config.PodIdentity
183+
if err := config.TypedConfig(meta); err != nil {
184+
return nil, fmt.Errorf("error parsing azure loganalytics metadata: %w", err)
236185
}
237186

238-
return &meta, nil
187+
return meta, nil
239188
}
240189

241190
// getParameterFromConfig gets the parameter from the configs, if checkAuthParams is true
@@ -254,9 +203,9 @@ func getParameterFromConfig(config *scalersconfig.ScalerConfig, parameter string
254203
func (s *azureLogAnalyticsScaler) GetMetricSpecForScaling(context.Context) []v2.MetricSpec {
255204
externalMetric := &v2.ExternalMetricSource{
256205
Metric: v2.MetricIdentifier{
257-
Name: GenerateMetricNameWithIndex(s.metadata.triggerIndex, kedautil.NormalizeString(fmt.Sprintf("%s-%s", "azure-log-analytics", s.metadata.workspaceID))),
206+
Name: GenerateMetricNameWithIndex(s.metadata.TriggerIndex, kedautil.NormalizeString(fmt.Sprintf("%s-%s", "azure-log-analytics", s.metadata.WorkspaceID))),
258207
},
259-
Target: GetMetricTargetMili(s.metricType, s.metadata.threshold),
208+
Target: GetMetricTargetMili(s.metricType, s.metadata.Threshold),
260209
}
261210
metricSpec := v2.MetricSpec{External: externalMetric, Type: externalMetricType}
262211
return []v2.MetricSpec{metricSpec}
@@ -272,16 +221,16 @@ func (s *azureLogAnalyticsScaler) GetMetricsAndActivity(ctx context.Context, met
272221

273222
metric := GenerateMetricInMili(metricName, val)
274223

275-
return []external_metrics.ExternalMetricValue{metric}, val > s.metadata.activationThreshold, nil
224+
return []external_metrics.ExternalMetricValue{metric}, val > s.metadata.ActivationThreshold, nil
276225
}
277226

278227
func (s *azureLogAnalyticsScaler) Close(context.Context) error {
279228
return nil
280229
}
281230

282231
func (s *azureLogAnalyticsScaler) getMetricData(ctx context.Context) (float64, error) {
283-
response, err := s.client.QueryWorkspace(ctx, s.metadata.workspaceID, azquery.Body{
284-
Query: &s.metadata.query,
232+
response, err := s.client.QueryWorkspace(ctx, s.metadata.WorkspaceID, azquery.Body{
233+
Query: &s.metadata.Query,
285234
}, nil)
286235
if err != nil {
287236
return -1, err

pkg/scalers/azure_log_analytics_scaler_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,6 @@ var testLogAnalyticsMetadata = []parseLogAnalyticsMetadataTestData{
100100
{map[string]string{"tenantIdFromEnv": "d248da64-0e1e-4f79-b8c6-72ab7aa055eb", "clientIdFromEnv": "41826dd4-9e0a-4357-a5bd-a88ad771ea7d", "clientSecretFromEnv": "U6DtAX5r6RPZxd~l12Ri3X8J9urt5Q-xs", "workspaceIdFromEnv": "074dd9f8-c368-4220-9400-acb6e80fc325", "query": query, "threshold": "1900000000", "cloud": "azureGermanCloud"}, true},
101101
// Valid HTTP timeout
102102
{map[string]string{"tenantId": "d248da64-0e1e-4f79-b8c6-72ab7aa055eb", "clientId": "41826dd4-9e0a-4357-a5bd-a88ad771ea7d", "clientSecret": "U6DtAX5r6RPZxd~l12Ri3X8J9urt5Q-xs", "workspaceId": "074dd9f8-c368-4220-9400-acb6e80fc325", "query": query, "threshold": "1900000000", "timeout": "1000"}, false},
103-
// Invalid - 0 - HTTP timeout
104-
{map[string]string{"tenantId": "d248da64-0e1e-4f79-b8c6-72ab7aa055eb", "clientId": "41826dd4-9e0a-4357-a5bd-a88ad771ea7d", "clientSecret": "U6DtAX5r6RPZxd~l12Ri3X8J9urt5Q-xs", "workspaceId": "074dd9f8-c368-4220-9400-acb6e80fc325", "query": query, "threshold": "1900000000", "timeout": "0"}, true},
105103
// Invalid - negative - HTTP timeout
106104
{map[string]string{"tenantId": "d248da64-0e1e-4f79-b8c6-72ab7aa055eb", "clientId": "41826dd4-9e0a-4357-a5bd-a88ad771ea7d", "clientSecret": "U6DtAX5r6RPZxd~l12Ri3X8J9urt5Q-xs", "workspaceId": "074dd9f8-c368-4220-9400-acb6e80fc325", "query": query, "threshold": "1900000000", "timeout": "-1"}, true},
107105
// Invalid - not a number - HTTP timeout
@@ -233,8 +231,8 @@ func TestLogAnalyticsParseMetadataUnsafeSsl(t *testing.T) {
233231
t.Error("Expected error but got success")
234232
}
235233
if meta != nil {
236-
if meta.unsafeSsl != testData.unsafeSsl {
237-
t.Errorf("Expected unsafeSsl to be %v but got %v", testData.unsafeSsl, meta.unsafeSsl)
234+
if meta.UnsafeSsl != testData.unsafeSsl {
235+
t.Errorf("Expected unsafeSsl to be %v but got %v", testData.unsafeSsl, meta.UnsafeSsl)
238236
}
239237
}
240238
}

0 commit comments

Comments
 (0)