Skip to content

Commit

Permalink
add sessionsPerNode and setSessionsFromHub parameters to selenium-gri…
Browse files Browse the repository at this point in the history
…d scaler (#6055)

* add sessionsPerNode and setSessionsFromHub parameters to selenium-grid scaler, add tests and changelog

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* fix tests

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* fix json

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* fix json

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* fix tests

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* fix metadata tests

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* fix syntax

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* link Issue to Changelog

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* fix link to issue

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* add sessionBrowserVersion and tests

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* add metadata tests

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* Update CHANGELOG.md

Co-authored-by: Jorge Turrado Ferrero <[email protected]>
Signed-off-by: Robert Hanzlík <[email protected]>
Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

* make cyclomatic complexity smaller

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>

---------

Signed-off-by: Hanzlik, Robert (MONETA) <[email protected]>
Signed-off-by: Robert Hanzlík <[email protected]>
Signed-off-by: Jorge Turrado Ferrero <[email protected]>
Co-authored-by: Jorge Turrado Ferrero <[email protected]>
  • Loading branch information
rubroboletus and JorTurFer authored Oct 16, 2024
1 parent 9fcc86f commit a103204
Show file tree
Hide file tree
Showing 3 changed files with 319 additions and 67 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Here is an overview of all new **experimental** features:
- **Kafka**: Fix logic to scale to zero on invalid offset even with earliest offsetResetPolicy ([#5689](https://github.com/kedacore/keda/issues/5689))
- **RabbitMQ Scaler**: Add connection name for AMQP ([#5958](https://github.com/kedacore/keda/issues/5958))
- **Selenium Scaler**: Add Support for Username and Password Authentication ([#6144](https://github.com/kedacore/keda/issues/6144))
- **Selenium Scaler**: Introduce new parameters setSessionsFromHub, sessionsPerNode and sessionBrowserVersion. ([#6080](https://github.com/kedacore/keda/issues/6080))
- TODO ([#XXX](https://github.com/kedacore/keda/issues/XXX))

### Fixes
Expand Down
83 changes: 71 additions & 12 deletions pkg/scalers/selenium_grid_scaler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ type seleniumGridScaler struct {
type seleniumGridScalerMetadata struct {
triggerIndex int

URL string `keda:"name=url, order=triggerMetadata;authParams"`
BrowserName string `keda:"name=browserName, order=triggerMetadata"`
SessionBrowserName string `keda:"name=sessionBrowserName, order=triggerMetadata, optional"`
ActivationThreshold int64 `keda:"name=activationThreshold, order=triggerMetadata, optional"`
BrowserVersion string `keda:"name=browserVersion, order=triggerMetadata, optional, default=latest"`
UnsafeSsl bool `keda:"name=unsafeSsl, order=triggerMetadata, optional, default=false"`
PlatformName string `keda:"name=platformName, order=triggerMetadata, optional, default=linux"`
URL string `keda:"name=url, order=triggerMetadata;authParams"`
BrowserName string `keda:"name=browserName, order=triggerMetadata"`
SessionBrowserName string `keda:"name=sessionBrowserName, order=triggerMetadata, optional"`
ActivationThreshold int64 `keda:"name=activationThreshold, order=triggerMetadata, optional"`
BrowserVersion string `keda:"name=browserVersion, order=triggerMetadata, optional, default=latest"`
UnsafeSsl bool `keda:"name=unsafeSsl, order=triggerMetadata, optional, default=false"`
PlatformName string `keda:"name=platformName, order=triggerMetadata, optional, default=linux"`
SessionsPerNode int64 `keda:"name=sessionsPerNode, order=triggerMetadata, optional, default=1"`
SetSessionsFromHub bool `keda:"name=setSessionsFromHub, order=triggerMetadata, optional, default=false"`
SessionBrowserVersion string `keda:"name=sessionBrowserVersion, order=triggerMetadata, optional"`

// auth
Username string `keda:"name=username, order=authParams;resolvedEnv;triggerMetadata, optional"`
Expand All @@ -50,6 +53,7 @@ type seleniumResponse struct {

type data struct {
Grid grid `json:"grid"`
NodesInfo nodesInfo `json:"nodesInfo"`
SessionsInfo sessionsInfo `json:"sessionsInfo"`
}

Expand All @@ -75,6 +79,19 @@ type capability struct {
PlatformName string `json:"platformName"`
}

type nodesInfo struct {
Nodes []nodes `json:"nodes"`
}

type nodes struct {
Stereotypes string `json:"stereotypes"`
}

type stereotype struct {
Slots int64 `json:"slots"`
Stereotype capability `json:"stereotype"`
}

const (
DefaultBrowserVersion string = "latest"
DefaultPlatformName string = "linux"
Expand Down Expand Up @@ -118,6 +135,9 @@ func parseSeleniumGridScalerMetadata(config *scalersconfig.ScalerConfig) (*selen
if meta.SessionBrowserName == "" {
meta.SessionBrowserName = meta.BrowserName
}
if meta.SessionBrowserVersion == "" {
meta.SessionBrowserVersion = meta.BrowserVersion
}
return meta, nil
}

Expand Down Expand Up @@ -156,7 +176,7 @@ func (s *seleniumGridScaler) GetMetricSpecForScaling(context.Context) []v2.Metri

func (s *seleniumGridScaler) getSessionsCount(ctx context.Context, logger logr.Logger) (int64, error) {
body, err := json.Marshal(map[string]string{
"query": "{ grid { maxSession, nodeCount }, sessionsInfo { sessionQueueRequests, sessions { id, capabilities, nodeId } } }",
"query": "{ grid { maxSession, nodeCount }, nodesInfo { nodes { stereotypes } }, sessionsInfo { sessionQueueRequests, sessions { id, capabilities, nodeId } } }",
})

if err != nil {
Expand Down Expand Up @@ -186,21 +206,26 @@ 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, s.metadata.PlatformName, logger)
v, err := getCountFromSeleniumResponse(b, s.metadata.BrowserName, s.metadata.BrowserVersion, s.metadata.SessionBrowserName, s.metadata.PlatformName, s.metadata.SessionsPerNode, s.metadata.SetSessionsFromHub, s.metadata.SessionBrowserVersion, logger)
if err != nil {
return -1, err
}
return v, nil
}

func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion string, sessionBrowserName string, platformName string, logger logr.Logger) (int64, error) {
func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion string, sessionBrowserName string, platformName string, sessionsPerNode int64, setSessionsFromHub bool, sessionBrowserVersion string, logger logr.Logger) (int64, error) {
var count int64
var slots int64
var seleniumResponse = seleniumResponse{}

if err := json.Unmarshal(b, &seleniumResponse); err != nil {
return 0, err
}

if setSessionsFromHub {
slots = getSlotsFromSeleniumResponse(seleniumResponse, browserName, browserVersion, platformName, logger)
}

var sessionQueueRequests = seleniumResponse.Data.SessionsInfo.SessionQueueRequests
for _, sessionQueueRequest := range sessionQueueRequests {
var capability = capability{}
Expand All @@ -224,7 +249,7 @@ func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion s
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) && platformNameMatches {
if strings.HasPrefix(capability.BrowserVersion, sessionBrowserVersion) && platformNameMatches {
count++
} else if browserVersion == DefaultBrowserVersion && platformNameMatches {
count++
Expand All @@ -238,10 +263,44 @@ func getCountFromSeleniumResponse(b []byte, browserName string, browserVersion s
var gridMaxSession = int64(seleniumResponse.Data.Grid.MaxSession)
var gridNodeCount = int64(seleniumResponse.Data.Grid.NodeCount)

if gridMaxSession > 0 && gridNodeCount > 0 {
if setSessionsFromHub {
if slots == 0 {
slots = sessionsPerNode
}
var floatCount = float64(count) / float64(slots)
count = int64(math.Ceil(floatCount))
} else if gridMaxSession > 0 && gridNodeCount > 0 {
// Get count, convert count to next highest int64
var floatCount = float64(count) / (float64(gridMaxSession) / float64(gridNodeCount))
count = int64(math.Ceil(floatCount))
}

return count, nil
}

func getSlotsFromSeleniumResponse(seleniumResponse seleniumResponse, browserName string, browserVersion string, platformName string, logger logr.Logger) int64 {
var slots int64

var nodes = seleniumResponse.Data.NodesInfo.Nodes
slots:
for _, node := range nodes {
var stereotypes = []stereotype{}
if err := json.Unmarshal([]byte(node.Stereotypes), &stereotypes); err == nil {
for _, stereotype := range stereotypes {
if stereotype.Stereotype.BrowserName == browserName {
var platformNameMatches = stereotype.Stereotype.PlatformName == "" || strings.EqualFold(stereotype.Stereotype.PlatformName, platformName)
if strings.HasPrefix(stereotype.Stereotype.BrowserVersion, browserVersion) && platformNameMatches {
slots = stereotype.Slots
break slots
} else if len(strings.TrimSpace(stereotype.Stereotype.BrowserVersion)) == 0 && browserVersion == DefaultBrowserVersion && platformNameMatches {
slots = stereotype.Slots
break slots
}
}
}
} else {
logger.Error(err, fmt.Sprintf("Error when unmarshalling stereotypes: %s", err))
}
}
return slots
}
Loading

0 comments on commit a103204

Please sign in to comment.