diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fb5f8286e3..0b2811db530 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ Here is an overview of all new **experimental** features: - **General**: Use (self-signed) certificates for all the communications (internals and externals) ([#3931](https://github.com/kedacore/keda/issues/3931)) - **Redis Scalers**: Add support to Redis 7 ([#4052](https://github.com/kedacore/keda/issues/4052)) +- **Selenium Grid Scaler**: Add 'platformName' to selenium-grid scaler metadata structure ([#4038](https://github.com/kedacore/keda/issues/4038)) ### Fixes diff --git a/pkg/scalers/selenium_grid_scaler.go b/pkg/scalers/selenium_grid_scaler.go index 6a59444ec68..64df897a036 100644 --- a/pkg/scalers/selenium_grid_scaler.go +++ b/pkg/scalers/selenium_grid_scaler.go @@ -35,6 +35,7 @@ type seleniumGridScalerMetadata struct { browserVersion string unsafeSsl bool scalerIndex int + platformName string } type seleniumResponse struct { @@ -65,10 +66,12 @@ type seleniumSession struct { type capability struct { BrowserName string `json:"browserName"` BrowserVersion string `json:"browserVersion"` + PlatformName string `json:"platformName"` } const ( DefaultBrowserVersion string = "latest" + DefaultPlatformName string = "linux" ) func NewSeleniumGridScaler(config *ScalerConfig) (Scaler, error) { @@ -143,6 +146,12 @@ func parseSeleniumGridScalerMetadata(config *ScalerConfig) (*seleniumGridScalerM meta.unsafeSsl = parsedVal } + if val, ok := config.TriggerMetadata["platformName"]; ok && val != "" { + meta.platformName = val + } else { + meta.platformName = DefaultPlatformName + } + meta.scalerIndex = config.ScalerIndex return &meta, nil } @@ -206,14 +215,14 @@ func (s *seleniumGridScaler) getSessionsCount(ctx context.Context, logger logr.L if err != nil { return -1, err } - v, err := getCountFromSeleniumResponse(b, s.metadata.browserName, s.metadata.browserVersion, s.metadata.sessionBrowserName, logger) + v, err := getCountFromSeleniumResponse(b, s.metadata.browserName, s.metadata.browserVersion, s.metadata.sessionBrowserName, s.metadata.platformName, logger) if err != nil { return -1, err } return v, nil } -func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion string, sessionBrowserName string, logger logr.Logger) (int64, error) { +func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion string, sessionBrowserName string, platformName string, logger logr.Logger) (int64, error) { var count int64 var seleniumResponse = seleniumResponse{} @@ -226,9 +235,10 @@ func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion s var capability = capability{} if err := json.Unmarshal([]byte(sessionQueueRequest), &capability); err == nil { if capability.BrowserName == browserName { - if strings.HasPrefix(capability.BrowserVersion, browserVersion) { + var platformNameMatches = capability.PlatformName == "" || strings.EqualFold(capability.PlatformName, platformName) + if strings.HasPrefix(capability.BrowserVersion, browserVersion) && platformNameMatches { count++ - } else if browserVersion == DefaultBrowserVersion { + } else if browserVersion == DefaultBrowserVersion && platformNameMatches { count++ } } @@ -241,10 +251,11 @@ func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion s for _, session := range sessions { var capability = capability{} if err := json.Unmarshal([]byte(session.Capabilities), &capability); err == nil { + var platformNameMatches = capability.PlatformName == "" || strings.EqualFold(capability.PlatformName, platformName) if capability.BrowserName == sessionBrowserName { - if strings.HasPrefix(capability.BrowserVersion, browserVersion) { + if strings.HasPrefix(capability.BrowserVersion, browserVersion) && platformNameMatches { count++ - } else if browserVersion == DefaultBrowserVersion { + } else if browserVersion == DefaultBrowserVersion && platformNameMatches { count++ } } diff --git a/pkg/scalers/selenium_grid_scaler_test.go b/pkg/scalers/selenium_grid_scaler_test.go index 76b8d70ab5c..78b33450404 100644 --- a/pkg/scalers/selenium_grid_scaler_test.go +++ b/pkg/scalers/selenium_grid_scaler_test.go @@ -13,6 +13,7 @@ func Test_getCountFromSeleniumResponse(t *testing.T) { browserName string sessionBrowserName string browserVersion string + platformName string } tests := []struct { name string @@ -82,6 +83,7 @@ func Test_getCountFromSeleniumResponse(t *testing.T) { browserName: "", sessionBrowserName: "", browserVersion: "latest", + platformName: "linux", }, want: 0, wantErr: false, @@ -104,6 +106,7 @@ func Test_getCountFromSeleniumResponse(t *testing.T) { browserName: "chrome", sessionBrowserName: "chrome", browserVersion: "latest", + platformName: "linux", }, want: 2, wantErr: false, @@ -137,6 +140,7 @@ func Test_getCountFromSeleniumResponse(t *testing.T) { browserName: "chrome", sessionBrowserName: "chrome", browserVersion: "latest", + platformName: "linux", }, want: 3, wantErr: false, @@ -170,6 +174,7 @@ func Test_getCountFromSeleniumResponse(t *testing.T) { browserName: "chrome", sessionBrowserName: "chrome", browserVersion: "latest", + platformName: "linux", }, want: 2, wantErr: false, @@ -203,6 +208,7 @@ func Test_getCountFromSeleniumResponse(t *testing.T) { browserName: "chrome", sessionBrowserName: "chrome", browserVersion: "latest", + platformName: "linux", }, want: 5, wantErr: false, @@ -231,12 +237,13 @@ func Test_getCountFromSeleniumResponse(t *testing.T) { browserName: "chrome", sessionBrowserName: "chrome", browserVersion: "91.0", + platformName: "linux", }, want: 2, wantErr: false, }, { - name: "1 active msedge session with matching browsername/sessionBroswerName should return count as 3", + name: "1 active msedge session with matching browsername/sessionBrowserName should return count as 3", args: args{ b: []byte(`{ "data": { @@ -259,6 +266,7 @@ func Test_getCountFromSeleniumResponse(t *testing.T) { browserName: "MicrosoftEdge", sessionBrowserName: "msedge", browserVersion: "latest", + platformName: "linux", }, want: 3, wantErr: false, @@ -287,6 +295,7 @@ func Test_getCountFromSeleniumResponse(t *testing.T) { browserName: "chrome", sessionBrowserName: "chrome", browserVersion: "latest", + platformName: "linux", }, want: 2, wantErr: false, @@ -315,14 +324,95 @@ func Test_getCountFromSeleniumResponse(t *testing.T) { browserName: "chrome", sessionBrowserName: "chrome", browserVersion: "latest", + platformName: "linux", }, want: 1, wantErr: false, }, + { + name: "session request with matching browsername and no specific platformName should return count as 2", + args: args{ + b: []byte(`{ + "data": { + "grid":{ + "maxSession": 1, + "nodeCount": 1 + }, + "sessionsInfo": { + "sessionQueueRequests": ["{\n \"browserName\": \"chrome\"\n}","{\n \"browserName\": \"chrome\",\n \"platformName\": \"Windows 11\"\n}"], + "sessions": [] + } + } + }`), + browserName: "chrome", + sessionBrowserName: "chrome", + browserVersion: "latest", + platformName: "Windows 11", + }, + want: 2, + wantErr: false, + }, + { + name: "sessions requests with matching browsername and platformName should return count as 1", + args: args{ + b: []byte(`{ + "data": { + "grid":{ + "maxSession": 1, + "nodeCount": 1 + }, + "sessionsInfo": { + "sessionQueueRequests": ["{\n \"browserName\": \"chrome\",\n \"platformName\": \"linux\"\n}","{\n \"browserName\": \"chrome\",\n \"platformName\": \"Windows 11\"\n}"], + "sessions": [] + } + } + }`), + browserName: "chrome", + sessionBrowserName: "chrome", + browserVersion: "latest", + platformName: "Windows 11", + }, + want: 1, + wantErr: false, + }, + { + name: "sessions requests and active sessions with matching browsername and platformName should return count as 2", + args: args{ + b: []byte(`{ + "data": { + "grid":{ + "maxSession": 1, + "nodeCount": 1 + }, + "sessionsInfo": { + "sessionQueueRequests": ["{\n \"browserName\": \"chrome\",\n \"platformName\": \"linux\"\n}","{\n \"browserName\": \"chrome\",\n \"platformName\": \"Windows 11\"\n}"], + "sessions": [ + { + "id": "0f9c5a941aa4d755a54b84be1f6535b1", + "capabilities": "{\n \"acceptInsecureCerts\": false,\n \"browserName\": \"chrome\",\n \"browserVersion\": \"91.0.4472.114\",\n \"chrome\": {\n \"chromedriverVersion\": \"91.0.4472.101 (af52a90bf87030dd1523486a1cd3ae25c5d76c9b-refs\\u002fbranch-heads\\u002f4472@{#1462})\",\n \"userDataDir\": \"\\u002ftmp\\u002f.com.google.Chrome.DMqx9m\"\n },\n \"goog:chromeOptions\": {\n \"debuggerAddress\": \"localhost:35839\"\n },\n \"networkConnectionEnabled\": false,\n \"pageLoadStrategy\": \"normal\",\n \"platformName\": \"Windows 11\",\n \"proxy\": {\n },\n \"se:cdp\": \"http:\\u002f\\u002flocalhost:35839\",\n \"se:cdpVersion\": \"91.0.4472.114\",\n \"se:vncEnabled\": true,\n \"se:vncLocalAddress\": \"ws:\\u002f\\u002flocalhost:7900\\u002fwebsockify\",\n \"setWindowRect\": true,\n \"strictFileInteractability\": false,\n \"timeouts\": {\n \"implicit\": 0,\n \"pageLoad\": 300000,\n \"script\": 30000\n },\n \"unhandledPromptBehavior\": \"dismiss and notify\",\n \"webauthn:extension:largeBlob\": true,\n \"webauthn:virtualAuthenticators\": true\n}", + "nodeId": "d44dcbc5-0b2c-4d5e-abf4-6f6aa5e0983c" + }, + { + "id": "0f9c5a941aa4d755a54b84be1f6535b1", + "capabilities": "{\n \"acceptInsecureCerts\": false,\n \"browserName\": \"chrome\",\n \"browserVersion\": \"91.0.4472.114\",\n \"chrome\": {\n \"chromedriverVersion\": \"91.0.4472.101 (af52a90bf87030dd1523486a1cd3ae25c5d76c9b-refs\\u002fbranch-heads\\u002f4472@{#1462})\",\n \"userDataDir\": \"\\u002ftmp\\u002f.com.google.Chrome.DMqx9m\"\n },\n \"goog:chromeOptions\": {\n \"debuggerAddress\": \"localhost:35839\"\n },\n \"networkConnectionEnabled\": false,\n \"pageLoadStrategy\": \"normal\",\n \"platformName\": \"linux\",\n \"proxy\": {\n },\n \"se:cdp\": \"http:\\u002f\\u002flocalhost:35839\",\n \"se:cdpVersion\": \"91.0.4472.114\",\n \"se:vncEnabled\": true,\n \"se:vncLocalAddress\": \"ws:\\u002f\\u002flocalhost:7900\\u002fwebsockify\",\n \"setWindowRect\": true,\n \"strictFileInteractability\": false,\n \"timeouts\": {\n \"implicit\": 0,\n \"pageLoad\": 300000,\n \"script\": 30000\n },\n \"unhandledPromptBehavior\": \"dismiss and notify\",\n \"webauthn:extension:largeBlob\": true,\n \"webauthn:virtualAuthenticators\": true\n}", + "nodeId": "d44dcbc5-0b2c-4d5e-abf4-6f6aa5e0983c" + } + ] + } + } + }`), + browserName: "chrome", + sessionBrowserName: "chrome", + browserVersion: "latest", + platformName: "Windows 11", + }, + want: 2, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := getCountFromSeleniumResponse(tt.args.b, tt.args.browserName, tt.args.browserVersion, tt.args.sessionBrowserName, logr.Discard()) + got, err := getCountFromSeleniumResponse(tt.args.b, tt.args.browserName, tt.args.browserVersion, tt.args.sessionBrowserName, tt.args.platformName, logr.Discard()) if (err != nil) != tt.wantErr { t.Errorf("getCountFromSeleniumResponse() error = %v, wantErr %v", err, tt.wantErr) return @@ -381,6 +471,7 @@ func Test_parseSeleniumGridScalerMetadata(t *testing.T) { sessionBrowserName: "chrome", targetValue: 1, browserVersion: "latest", + platformName: "linux", }, }, { @@ -401,6 +492,7 @@ func Test_parseSeleniumGridScalerMetadata(t *testing.T) { sessionBrowserName: "msedge", targetValue: 1, browserVersion: "latest", + platformName: "linux", }, }, { @@ -423,6 +515,7 @@ func Test_parseSeleniumGridScalerMetadata(t *testing.T) { sessionBrowserName: "msedge", targetValue: 1, browserVersion: "latest", + platformName: "linux", }, }, { @@ -445,6 +538,7 @@ func Test_parseSeleniumGridScalerMetadata(t *testing.T) { targetValue: 1, browserVersion: "91.0", unsafeSsl: false, + platformName: "linux", }, }, { @@ -469,6 +563,7 @@ func Test_parseSeleniumGridScalerMetadata(t *testing.T) { activationThreshold: 10, browserVersion: "91.0", unsafeSsl: true, + platformName: "linux", }, }, { @@ -486,6 +581,57 @@ func Test_parseSeleniumGridScalerMetadata(t *testing.T) { }, wantErr: true, }, + { + name: "valid url, browsername, unsafeSsl and activationThreshold with default platformName should return metadata", + args: args{ + config: &ScalerConfig{ + TriggerMetadata: map[string]string{ + "url": "http://selenium-hub:4444/graphql", + "browserName": "chrome", + "browserVersion": "91.0", + "unsafeSsl": "true", + "activationThreshold": "10", + }, + }, + }, + wantErr: false, + want: &seleniumGridScalerMetadata{ + url: "http://selenium-hub:4444/graphql", + browserName: "chrome", + sessionBrowserName: "chrome", + targetValue: 1, + activationThreshold: 10, + browserVersion: "91.0", + unsafeSsl: true, + platformName: "linux", + }, + }, + { + name: "valid url, browsername, unsafeSsl, activationThreshold and platformName should return metadata", + args: args{ + config: &ScalerConfig{ + TriggerMetadata: map[string]string{ + "url": "http://selenium-hub:4444/graphql", + "browserName": "chrome", + "browserVersion": "91.0", + "unsafeSsl": "true", + "activationThreshold": "10", + "platformName": "Windows 11", + }, + }, + }, + wantErr: false, + want: &seleniumGridScalerMetadata{ + url: "http://selenium-hub:4444/graphql", + browserName: "chrome", + sessionBrowserName: "chrome", + targetValue: 1, + activationThreshold: 10, + browserVersion: "91.0", + unsafeSsl: true, + platformName: "Windows 11", + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {