@@ -36,10 +36,10 @@ type seleniumGridScalerMetadata struct {
36
36
BrowserName string `keda:"name=browserName, order=triggerMetadata"`
37
37
SessionBrowserName string `keda:"name=sessionBrowserName, order=triggerMetadata, optional"`
38
38
ActivationThreshold int64 `keda:"name=activationThreshold, order=triggerMetadata, optional"`
39
- BrowserVersion string `keda:"name=browserVersion, order=triggerMetadata, optional, default=latest"`
40
- UnsafeSsl bool `keda:"name=unsafeSsl, order=triggerMetadata, optional, default=false"`
41
- PlatformName string `keda:"name=platformName, order=triggerMetadata, optional, default=linux"`
42
- NodeMaxSessions int `keda:"name=nodeMaxSessions, order=triggerMetadata, optional , default=1"`
39
+ BrowserVersion string `keda:"name=browserVersion, order=triggerMetadata, default=latest"`
40
+ UnsafeSsl bool `keda:"name=unsafeSsl, order=triggerMetadata, default=false"`
41
+ PlatformName string `keda:"name=platformName, order=triggerMetadata, default=linux"`
42
+ NodeMaxSessions int64 `keda:"name=nodeMaxSessions, order=triggerMetadata, default=1"`
43
43
44
44
TargetValue int64
45
45
}
@@ -55,9 +55,9 @@ type Data struct {
55
55
}
56
56
57
57
type Grid struct {
58
- SessionCount int `json:"sessionCount"`
59
- MaxSession int `json:"maxSession"`
60
- TotalSlots int `json:"totalSlots"`
58
+ SessionCount int64 `json:"sessionCount"`
59
+ MaxSession int64 `json:"maxSession"`
60
+ TotalSlots int64 `json:"totalSlots"`
61
61
}
62
62
63
63
type NodesInfo struct {
@@ -71,17 +71,17 @@ type SessionsInfo struct {
71
71
type Nodes []struct {
72
72
ID string `json:"id"`
73
73
Status string `json:"status"`
74
- SessionCount int `json:"sessionCount"`
75
- MaxSession int `json:"maxSession"`
76
- SlotCount int `json:"slotCount"`
74
+ SessionCount int64 `json:"sessionCount"`
75
+ MaxSession int64 `json:"maxSession"`
76
+ SlotCount int64 `json:"slotCount"`
77
77
Stereotypes string `json:"stereotypes"`
78
78
Sessions Sessions `json:"sessions"`
79
79
}
80
80
81
81
type ReservedNodes struct {
82
82
ID string `json:"id"`
83
- MaxSession int `json:"maxSession"`
84
- SlotCount int `json:"slotCount"`
83
+ MaxSession int64 `json:"maxSession"`
84
+ SlotCount int64 `json:"slotCount"`
85
85
}
86
86
87
87
type Sessions []struct {
@@ -102,7 +102,7 @@ type Capability struct {
102
102
}
103
103
104
104
type Stereotypes []struct {
105
- Slots int `json:"slots"`
105
+ Slots int64 `json:"slots"`
106
106
Stereotype Capability `json:"stereotype"`
107
107
}
108
108
@@ -148,6 +148,7 @@ func parseSeleniumGridScalerMetadata(config *scalersconfig.ScalerConfig) (*selen
148
148
if meta .SessionBrowserName == "" {
149
149
meta .SessionBrowserName = meta .BrowserName
150
150
}
151
+
151
152
return meta , nil
152
153
}
153
154
@@ -160,18 +161,18 @@ func (s *seleniumGridScaler) Close(context.Context) error {
160
161
}
161
162
162
163
func (s * seleniumGridScaler ) GetMetricsAndActivity (ctx context.Context , metricName string ) ([]external_metrics.ExternalMetricValue , bool , error ) {
163
- sessions , err := s .getSessionsCount (ctx , s .logger )
164
+ newRequestNodes , onGoingSessions , err := s .getSessionsQueueLength (ctx , s .logger )
164
165
if err != nil {
165
166
return []external_metrics.ExternalMetricValue {}, false , fmt .Errorf ("error requesting selenium grid endpoint: %w" , err )
166
167
}
167
168
168
- metric := GenerateMetricInMili (metricName , float64 (sessions ))
169
+ metric := GenerateMetricInMili (metricName , float64 (newRequestNodes + onGoingSessions ))
169
170
170
- return []external_metrics.ExternalMetricValue {metric }, sessions > s .metadata .ActivationThreshold , nil
171
+ return []external_metrics.ExternalMetricValue {metric }, ( newRequestNodes + onGoingSessions ) > s .metadata .ActivationThreshold , nil
171
172
}
172
173
173
174
func (s * seleniumGridScaler ) GetMetricSpecForScaling (context.Context ) []v2.MetricSpec {
174
- metricName := kedautil .NormalizeString (fmt .Sprintf ("seleniumgrid-%s " , s .metadata .BrowserName ))
175
+ metricName := kedautil .NormalizeString (fmt .Sprintf ("selenium-grid-%s-%s-%s " , s .metadata .BrowserName , s . metadata . BrowserVersion , s . metadata . PlatformName ))
175
176
externalMetric := & v2.ExternalMetricSource {
176
177
Metric : v2.MetricIdentifier {
177
178
Name : GenerateMetricNameWithIndex (s .metadata .triggerIndex , metricName ),
@@ -184,18 +185,18 @@ func (s *seleniumGridScaler) GetMetricSpecForScaling(context.Context) []v2.Metri
184
185
return []v2.MetricSpec {metricSpec }
185
186
}
186
187
187
- func (s * seleniumGridScaler ) getSessionsCount (ctx context.Context , logger logr.Logger ) (int64 , error ) {
188
+ func (s * seleniumGridScaler ) getSessionsQueueLength (ctx context.Context , logger logr.Logger ) (int64 , int64 , error ) {
188
189
body , err := json .Marshal (map [string ]string {
189
190
"query" : "{ grid { sessionCount, maxSession, totalSlots }, nodesInfo { nodes { id, status, sessionCount, maxSession, slotCount, stereotypes, sessions { id, capabilities, slot { id, stereotype } } } }, sessionsInfo { sessionQueueRequests } }" ,
190
191
})
191
192
192
193
if err != nil {
193
- return - 1 , err
194
+ return - 1 , - 1 , err
194
195
}
195
196
196
197
req , err := http .NewRequestWithContext (ctx , "POST" , s .metadata .URL , bytes .NewBuffer (body ))
197
198
if err != nil {
198
- return - 1 , err
199
+ return - 1 , - 1 , err
199
200
}
200
201
201
202
if (s .metadata .AuthType == "" || strings .EqualFold (s .metadata .AuthType , "Basic" )) && s .metadata .Username != "" && s .metadata .Password != "" {
@@ -206,28 +207,28 @@ func (s *seleniumGridScaler) getSessionsCount(ctx context.Context, logger logr.L
206
207
207
208
res , err := s .httpClient .Do (req )
208
209
if err != nil {
209
- return - 1 , err
210
+ return - 1 , - 1 , err
210
211
}
211
212
212
213
if res .StatusCode != http .StatusOK {
213
214
msg := fmt .Sprintf ("selenium grid returned %d" , res .StatusCode )
214
- return - 1 , errors .New (msg )
215
+ return - 1 , - 1 , errors .New (msg )
215
216
}
216
217
217
218
defer res .Body .Close ()
218
219
b , err := io .ReadAll (res .Body )
219
220
if err != nil {
220
- return - 1 , err
221
+ return - 1 , - 1 , err
221
222
}
222
- v , err := getCountFromSeleniumResponse (b , s .metadata .BrowserName , s .metadata .BrowserVersion , s .metadata .SessionBrowserName , s .metadata .PlatformName , s .metadata .NodeMaxSessions , logger )
223
+ newRequestNodes , onGoingSession , err := getCountFromSeleniumResponse (b , s .metadata .BrowserName , s .metadata .BrowserVersion , s .metadata .SessionBrowserName , s .metadata .PlatformName , s .metadata .NodeMaxSessions , logger )
223
224
if err != nil {
224
- return - 1 , err
225
+ return - 1 , - 1 , err
225
226
}
226
- return v , nil
227
+ return newRequestNodes , onGoingSession , nil
227
228
}
228
229
229
- func countMatchingSlotsStereotypes (stereotypes Stereotypes , request Capability , browserName string , browserVersion string , sessionBrowserName string , platformName string ) int {
230
- var matchingSlots int
230
+ func countMatchingSlotsStereotypes (stereotypes Stereotypes , request Capability , browserName string , browserVersion string , sessionBrowserName string , platformName string ) int64 {
231
+ var matchingSlots int64
231
232
for _ , stereotype := range stereotypes {
232
233
if checkCapabilitiesMatch (stereotype .Stereotype , request , browserName , browserVersion , sessionBrowserName , platformName ) {
233
234
matchingSlots += stereotype .Slots
@@ -236,8 +237,8 @@ func countMatchingSlotsStereotypes(stereotypes Stereotypes, request Capability,
236
237
return matchingSlots
237
238
}
238
239
239
- func countMatchingSessions (sessions Sessions , request Capability , browserName string , browserVersion string , sessionBrowserName string , platformName string , logger logr.Logger ) int {
240
- var matchingSessions int
240
+ func countMatchingSessions (sessions Sessions , request Capability , browserName string , browserVersion string , sessionBrowserName string , platformName string , logger logr.Logger ) int64 {
241
+ var matchingSessions int64
241
242
for _ , session := range sessions {
242
243
var capability = Capability {}
243
244
if err := json .Unmarshal ([]byte (session .Capabilities ), & capability ); err == nil {
@@ -274,7 +275,7 @@ func checkCapabilitiesMatch(capability Capability, requestCapability Capability,
274
275
return browserNameMatches && browserVersionMatches && platformNameMatches
275
276
}
276
277
277
- func checkNodeReservedSlots (reservedNodes []ReservedNodes , nodeID string , availableSlots int ) int {
278
+ func checkNodeReservedSlots (reservedNodes []ReservedNodes , nodeID string , availableSlots int64 ) int64 {
278
279
for _ , reservedNode := range reservedNodes {
279
280
if strings .EqualFold (reservedNode .ID , nodeID ) {
280
281
return reservedNode .SlotCount
@@ -283,7 +284,7 @@ func checkNodeReservedSlots(reservedNodes []ReservedNodes, nodeID string, availa
283
284
return availableSlots
284
285
}
285
286
286
- func updateOrAddReservedNode (reservedNodes []ReservedNodes , nodeID string , slotCount int , maxSession int ) []ReservedNodes {
287
+ func updateOrAddReservedNode (reservedNodes []ReservedNodes , nodeID string , slotCount int64 , maxSession int64 ) []ReservedNodes {
287
288
for i , reservedNode := range reservedNodes {
288
289
if strings .EqualFold (reservedNode .ID , nodeID ) {
289
290
// Update remaining available slots for the reserved node
@@ -295,17 +296,15 @@ func updateOrAddReservedNode(reservedNodes []ReservedNodes, nodeID string, slotC
295
296
return append (reservedNodes , ReservedNodes {ID : nodeID , SlotCount : slotCount , MaxSession : maxSession })
296
297
}
297
298
298
- func getCountFromSeleniumResponse (b []byte , browserName string , browserVersion string , sessionBrowserName string , platformName string , nodeMaxSessions int , logger logr.Logger ) (int64 , error ) {
299
- // The returned count of the number of new Nodes will be scaled up
300
- var count int64
299
+ func getCountFromSeleniumResponse (b []byte , browserName string , browserVersion string , sessionBrowserName string , platformName string , nodeMaxSessions int64 , logger logr.Logger ) (int64 , int64 , error ) {
301
300
// Track number of available slots of existing Nodes in the Grid can be reserved for the matched requests
302
- var availableSlots int
301
+ var availableSlots int64
303
302
// Track number of matched requests in the sessions queue will be served by this scaler
304
- var queueSlots int
303
+ var queueSlots int64
305
304
306
305
var seleniumResponse = SeleniumResponse {}
307
306
if err := json .Unmarshal (b , & seleniumResponse ); err != nil {
308
- return 0 , err
307
+ return 0 , 0 , err
309
308
}
310
309
311
310
var sessionQueueRequests = seleniumResponse .Data .SessionsInfo .SessionQueueRequests
@@ -314,6 +313,7 @@ func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion s
314
313
var reservedNodes []ReservedNodes
315
314
// Track list of new Nodes will be scaled up with number of available slots following scaler parameter `nodeMaxSessions`
316
315
var newRequestNodes []ReservedNodes
316
+ var onGoingSessions int64
317
317
for requestIndex , sessionQueueRequest := range sessionQueueRequests {
318
318
var isRequestMatched bool
319
319
var requestCapability = Capability {}
@@ -332,20 +332,22 @@ func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion s
332
332
}
333
333
334
334
var isRequestReserved bool
335
+ var sumOfCurrentSessionsMatch int64
335
336
// Check if the matched request can be assigned to available slots of existing Nodes in the Grid
336
337
for _ , node := range nodes {
338
+ // Count ongoing sessions that match the request capability and scaler metadata
339
+ var currentSessionsMatch = countMatchingSessions (node .Sessions , requestCapability , browserName , browserVersion , sessionBrowserName , platformName , logger )
340
+ sumOfCurrentSessionsMatch += currentSessionsMatch
337
341
// Check if node is UP and has available slots (maxSession > sessionCount)
338
342
if strings .EqualFold (node .Status , "UP" ) && checkNodeReservedSlots (reservedNodes , node .ID , node .MaxSession - node .SessionCount ) > 0 {
339
343
var stereotypes = Stereotypes {}
340
- var availableSlotsMatch int
344
+ var availableSlotsMatch int64
341
345
if err := json .Unmarshal ([]byte (node .Stereotypes ), & stereotypes ); err == nil {
342
346
// Count available slots that match the request capability and scaler metadata
343
347
availableSlotsMatch += countMatchingSlotsStereotypes (stereotypes , requestCapability , browserName , browserVersion , sessionBrowserName , platformName )
344
348
} else {
345
349
logger .Error (err , fmt .Sprintf ("Error when unmarshaling node stereotypes: %s" , err ))
346
350
}
347
- // Count ongoing sessions that match the request capability and scaler metadata
348
- var currentSessionsMatch = countMatchingSessions (node .Sessions , requestCapability , browserName , browserVersion , sessionBrowserName , platformName , logger )
349
351
// Count remaining available slots can be reserved for this request
350
352
var availableSlotsCanBeReserved = checkNodeReservedSlots (reservedNodes , node .ID , node .MaxSession - node .SessionCount )
351
353
// Reserve one available slot for the request if available slots match is greater than current sessions match
@@ -357,6 +359,9 @@ func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion s
357
359
}
358
360
}
359
361
}
362
+ if sumOfCurrentSessionsMatch > onGoingSessions {
363
+ onGoingSessions = sumOfCurrentSessionsMatch
364
+ }
360
365
// Check if the matched request can be assigned to available slots of new Nodes will be scaled up, since the scaler parameter `nodeMaxSessions` can be greater than 1
361
366
if ! isRequestReserved {
362
367
for _ , newRequestNode := range newRequestNodes {
@@ -373,11 +378,5 @@ func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion s
373
378
}
374
379
}
375
380
376
- if queueSlots > availableSlots {
377
- count = int64 (len (newRequestNodes ))
378
- } else {
379
- count = 0
380
- }
381
-
382
- return count , nil
381
+ return int64 (len (newRequestNodes )), onGoingSessions , nil
383
382
}
0 commit comments