Skip to content

Commit 4050099

Browse files
authored
Merge pull request #4 from qa-dev/strategies
strategies
2 parents ae2c5e7 + ae068e4 commit 4050099

File tree

2,701 files changed

+1255236
-380
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

2,701 files changed

+1255236
-380
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@
2323

2424
# Your local config
2525
config.json
26-
bindata.go
26+
/bindata.go
2727
dist/
2828
dbconfig.yml

config-sample.json

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,46 @@
77
"connection": "root:@(localhost:3306)/grid?tx_isolation=SERIALIZABLE&parseTime=true&interpolateParams=true"
88
},
99
"grid": {
10+
"client_type": "selenium",
1011
"port": 4444,
1112
"strategy_list": [
1213
{
13-
"type": "default",
14-
"limit": 0
14+
"type": "persistent"
15+
},
16+
{
17+
"type": "kubernetes",
18+
"limit": 20,
19+
"params": {},
20+
"node_list": [
21+
{
22+
"params": {
23+
"image":"myimage:latest",
24+
"port": "5555"
25+
},
26+
"capabilities_list": [
27+
{
28+
"browserName": "firefox",
29+
"browserVersion": 50
30+
},
31+
{
32+
"browserName": "chrome",
33+
"browserVersion": 150
34+
}
35+
]
36+
},
37+
{
38+
"params": {
39+
"image":"myimage2:latest",
40+
"port": "5555"
41+
},
42+
"capabilities_list": [
43+
{
44+
"browserName": "firefox",
45+
"browserVersion": 54
46+
}
47+
]
48+
}
49+
]
1550
}
1651
],
1752
"busy_node_duration": "15m",

config-test.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
"connection": "root:@(127.0.0.1:3306)/grid?tx_isolation=SERIALIZABLE&parseTime=true&interpolateParams=true"
88
},
99
"grid": {
10+
"client_type": "selenium",
1011
"port": 4444,
1112
"strategy_list": [
1213
{
13-
"type": "default",
14-
"limit": 0
14+
"type": "persistent"
1515
}
1616
],
1717
"busy_node_duration": "15m",

config/config.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,23 @@ type Config struct {
1616
}
1717

1818
type Grid struct {
19+
ClientType string `json:"client_type"`
1920
Port int `json:"port"`
2021
StrategyList []Strategy `json:"strategy_list"`
2122
BusyNodeDuration string `json:"busy_node_duration"` // duration string format ex. 12m, see time.ParseDuration()
2223
ReservedDuration string `json:"reserved_node_duration"` // duration string format ex. 12m, see time.ParseDuration()
2324
}
2425

2526
type Strategy struct {
26-
Type string `json:"type"`
27-
Limit int `json:"limit"`
27+
Params json.RawMessage `json:"params"` // ex. docker config, kubernetes config, etc.
28+
Type string `json:"type"`
29+
Limit int `json:"limit"`
30+
NodeList []Node `json:"node_list"`
31+
}
32+
33+
type Node struct {
34+
Params json.RawMessage `json:"params"` // ex. image_name, etc.
35+
CapabilitiesList []map[string]interface{} `json:"capabilities_list"`
2836
}
2937

3038
type Logger struct {

handlers/createSession.go

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,25 @@ import (
77
log "github.com/Sirupsen/logrus"
88
"github.com/qa-dev/jsonwire-grid/jsonwire"
99
"github.com/qa-dev/jsonwire-grid/pool"
10+
"github.com/qa-dev/jsonwire-grid/pool/capabilities"
1011
"github.com/qa-dev/jsonwire-grid/proxy"
11-
"github.com/qa-dev/jsonwire-grid/selenium"
12-
"github.com/qa-dev/jsonwire-grid/wda"
1312
"io/ioutil"
1413
"net/http"
1514
"net/http/httputil"
1615
"net/url"
1716
)
1817

1918
type CreateSession struct {
20-
Pool *pool.Pool
19+
Pool *pool.Pool
20+
ClientFactory jsonwire.ClientFactoryInterface
2121
}
2222

2323
func (h *CreateSession) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
2424
if r.Method != http.MethodPost {
2525
http.Error(rw, "Method Not Allowed", http.StatusMethodNotAllowed)
2626
return
2727
}
28-
var capabilities map[string]jsonwire.Capabilities
28+
var caps map[string]jsonwire.Capabilities
2929
body, err := ioutil.ReadAll(r.Body)
3030
if err != nil {
3131
errorMessage := "Error reading request: " + err.Error()
@@ -42,21 +42,21 @@ func (h *CreateSession) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
4242
rc := ioutil.NopCloser(bytes.NewReader(body))
4343
r.Body = rc
4444
log.Infof("requested session with params: %s", string(body))
45-
err = json.Unmarshal(body, &capabilities)
45+
err = json.Unmarshal(body, &caps)
4646
if err != nil {
4747
errorMessage := "Error unmarshal json: " + err.Error()
4848
log.Error(errorMessage)
4949
http.Error(rw, errorMessage, http.StatusInternalServerError)
5050
return
5151
}
52-
desiredCapabilities, ok := capabilities["desiredCapabilities"]
52+
desiredCapabilities, ok := caps["desiredCapabilities"]
5353
if !ok {
5454
errorMessage := "Not passed 'desiredCapabilities'"
5555
log.Error(errorMessage)
5656
http.Error(rw, errorMessage, http.StatusInternalServerError)
5757
return
5858
}
59-
poolCapabilities := pool.Capabilities(desiredCapabilities)
59+
poolCapabilities := capabilities.Capabilities(desiredCapabilities)
6060
tw, err := h.tryCreateSession(r, &poolCapabilities)
6161
if err != nil {
6262
http.Error(rw, "Can't create session: "+err.Error(), http.StatusInternalServerError)
@@ -66,7 +66,7 @@ func (h *CreateSession) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
6666
rw.Write(tw.Output)
6767
}
6868

69-
func (h *CreateSession) tryCreateSession(r *http.Request, capabilities *pool.Capabilities) (*proxy.ResponseWriter, error) {
69+
func (h *CreateSession) tryCreateSession(r *http.Request, capabilities *capabilities.Capabilities) (*proxy.ResponseWriter, error) {
7070
select {
7171
case <-r.Context().Done():
7272
err := errors.New("Request cancelled by client, " + r.Context().Err().Error())
@@ -79,16 +79,11 @@ func (h *CreateSession) tryCreateSession(r *http.Request, capabilities *pool.Cap
7979
return nil, errors.New("reserve node error: " + err.Error())
8080
}
8181
//todo: посылать в мониторинг событие, если вернулся не 0
82-
seleniumClient, err := createClient(node.Address, capabilities)
83-
if err != nil {
84-
return nil, errors.New("create Client error: " + err.Error())
85-
}
82+
seleniumClient := h.ClientFactory.Create(node.Address)
8683
seleniumNode := jsonwire.NewNode(seleniumClient)
8784
_, err = seleniumNode.RemoveAllSessions()
8885
if err != nil {
89-
log.Warn("Can't remove all sessions from node: " + err.Error() + ", go to next available node: " + node.String())
90-
h.Pool.Remove(node)
91-
return h.tryCreateSession(r, capabilities)
86+
return nil, errors.New("Can't remove all sessions from node: " + err.Error() + ", go to next available node: " + node.String())
9287
}
9388
reverseProxy := httputil.NewSingleHostReverseProxy(&url.URL{
9489
Scheme: "http",
@@ -100,23 +95,8 @@ func (h *CreateSession) tryCreateSession(r *http.Request, capabilities *pool.Cap
10095
reverseProxy.ServeHTTP(tw, r)
10196

10297
if !transport.IsSuccess {
103-
log.Warn("Failure proxy request on node " + node.String() + ": " + string(tw.Output))
104-
h.Pool.Remove(node)
105-
return h.tryCreateSession(r, capabilities)
98+
return nil, errors.New("Failure proxy request on node " + node.String() + ": " + string(tw.Output))
10699
}
107100

108101
return tw, nil
109102
}
110-
111-
func createClient(addr string, capabilities *pool.Capabilities) (jsonwire.ClientInterface, error) {
112-
if capabilities == nil {
113-
return nil, errors.New("capabilities must be not nil")
114-
}
115-
platformName := (*capabilities)["platformName"]
116-
switch platformName {
117-
case "WDA":
118-
return wda.NewClient(addr), nil
119-
default:
120-
return selenium.NewClient(addr), nil
121-
}
122-
}

handlers/registerNode.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
log "github.com/Sirupsen/logrus"
1010
"github.com/qa-dev/jsonwire-grid/jsonwire"
1111
"github.com/qa-dev/jsonwire-grid/pool"
12+
"github.com/qa-dev/jsonwire-grid/pool/capabilities"
1213
)
1314

1415
type RegisterNode struct {
@@ -41,12 +42,12 @@ func (h *RegisterNode) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
4142
} else {
4243
capabilitiesList = register.CapabilitiesList
4344
}
44-
poolCapabilitiesList := make([]pool.Capabilities, len(capabilitiesList))
45+
poolCapabilitiesList := make([]capabilities.Capabilities, len(capabilitiesList))
4546
for i, value := range capabilitiesList {
46-
poolCapabilitiesList[i] = pool.Capabilities(value)
47+
poolCapabilitiesList[i] = capabilities.Capabilities(value)
4748
}
4849
err = h.Pool.Add(
49-
pool.NodeTypeRegular,
50+
pool.NodeTypePersistent,
5051
register.Configuration.Host+":"+strconv.Itoa(register.Configuration.Port),
5152
poolCapabilitiesList,
5253
)

invoke.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,25 @@ import (
55
"github.com/qa-dev/jsonwire-grid/config"
66

77
"errors"
8-
"github.com/qa-dev/jsonwire-grid/storage"
8+
"github.com/qa-dev/jsonwire-grid/jsonwire"
9+
"github.com/qa-dev/jsonwire-grid/pool"
10+
"github.com/qa-dev/jsonwire-grid/pool/capabilities"
11+
"github.com/qa-dev/jsonwire-grid/pool/strategy/kubernetes"
12+
"github.com/qa-dev/jsonwire-grid/pool/strategy/persistent"
13+
"github.com/qa-dev/jsonwire-grid/selenium"
914
"github.com/qa-dev/jsonwire-grid/storage/mysql"
15+
"github.com/qa-dev/jsonwire-grid/wda"
1016
)
1117

12-
func invokeStorageFactory(config config.Config) (factory storage.StorageFactoryInterface, err error) {
18+
type StorageFactoryInterface interface {
19+
Create(config.Config) (pool.StorageInterface, error)
20+
}
21+
22+
type StrategyFactoryInterface interface {
23+
Create(pool.StorageInterface, capabilities.ComparatorInterface, jsonwire.ClientFactoryInterface) (pool.StrategyInterface, error)
24+
}
25+
26+
func invokeStorageFactory(config config.Config) (factory StorageFactoryInterface, err error) {
1327
switch config.DB.Implementation {
1428
case "mysql":
1529
factory = new(mysql.Factory)
@@ -18,3 +32,29 @@ func invokeStorageFactory(config config.Config) (factory storage.StorageFactoryI
1832
}
1933
return
2034
}
35+
36+
func invokeStrategyFactoryList(config config.Config) (factoryList []StrategyFactoryInterface, err error) {
37+
for _, strategyConfig := range config.Grid.StrategyList {
38+
switch strategyConfig.Type {
39+
case string(pool.NodeTypePersistent):
40+
factoryList = append(factoryList, new(persistent.StrategyFactory))
41+
case string(pool.NodeTypeKubernetes):
42+
factoryList = append(factoryList, &kubernetes.StrategyFactory{Config: strategyConfig})
43+
default:
44+
err = errors.New("Undefined strategy type: " + strategyConfig.Type)
45+
return
46+
}
47+
}
48+
return
49+
}
50+
51+
func createClient(config config.Config) (jsonwire.ClientFactoryInterface, error) {
52+
switch config.Grid.ClientType {
53+
case "wda":
54+
return new(wda.ClientFactory), nil
55+
case "selenium":
56+
return new(selenium.ClientFactory), nil
57+
default:
58+
return nil, errors.New("undefined value config.Grid.ClientType")
59+
}
60+
}

jsonwire/interfaces.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package jsonwire
22

3+
type ClientFactoryInterface interface {
4+
Create(address string) ClientInterface
5+
}
6+
37
type ClientInterface interface {
8+
Status() (*Message, error)
49
Sessions() (*Sessions, error)
510
CloseSession(sessionId string) (*Message, error)
611
Address() string

jsonwire/mocks.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package jsonwire
2+
3+
import (
4+
"github.com/stretchr/testify/mock"
5+
)
6+
7+
type ClientFactoryMock struct {
8+
mock.Mock
9+
}
10+
11+
func (cf *ClientFactoryMock) Create(address string) ClientInterface {
12+
args := cf.Called(address)
13+
return args.Get(0).(ClientInterface)
14+
}
15+
16+
type ClientMock struct {
17+
mock.Mock
18+
}
19+
20+
func (c *ClientMock) Status() (*Message, error) {
21+
args := c.Called()
22+
return args.Get(0).(*Message), args.Error(1)
23+
}
24+
25+
func (c *ClientMock) Sessions() (*Sessions, error) {
26+
args := c.Called()
27+
return args.Get(0).(*Sessions), args.Error(1)
28+
}
29+
30+
func (c *ClientMock) CloseSession(sessionId string) (*Message, error) {
31+
args := c.Called(sessionId)
32+
return args.Get(0).(*Message), args.Error(1)
33+
}
34+
35+
func (c *ClientMock) Address() string {
36+
args := c.Called()
37+
return args.String(0)
38+
}

main.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/qa-dev/jsonwire-grid/logger"
1010
"github.com/qa-dev/jsonwire-grid/middleware"
1111
"github.com/qa-dev/jsonwire-grid/pool"
12+
"github.com/qa-dev/jsonwire-grid/pool/capabilities"
1213
poolMetrics "github.com/qa-dev/jsonwire-grid/pool/metrics"
1314
"github.com/qa-dev/jsonwire-grid/utils/metrics"
1415
"net/http"
@@ -55,7 +56,28 @@ func main() {
5556
if err != nil {
5657
log.Fatalf("Can't create storage factory, %s", err)
5758
}
58-
poolInstance := pool.NewPool(storage)
59+
capsComparator := capabilities.NewComparator()
60+
strategyFactoryList, err := invokeStrategyFactoryList(*cfg)
61+
if err != nil {
62+
log.Fatalf("Can't create strategy factory list, %s", err)
63+
}
64+
65+
clientFactory, err := createClient(*cfg)
66+
if err != nil {
67+
log.Fatalf("Create ClientFactory error, %s", err)
68+
}
69+
70+
var strategyList []pool.StrategyInterface
71+
for _, strategyFactory := range strategyFactoryList {
72+
strategy, err := strategyFactory.Create(storage, capsComparator, clientFactory)
73+
if err != nil {
74+
log.Fatalf("Can't create strategy, %s", err)
75+
}
76+
strategyList = append(strategyList, strategy)
77+
}
78+
79+
strategyListStruct := pool.NewStrategyList(strategyList)
80+
poolInstance := pool.NewPool(storage, strategyListStruct)
5981
poolInstance.SetBusyNodeDuration(busyNodeDuration)
6082
poolInstance.SetReservedNodeDuration(reservedNodeDuration)
6183

@@ -71,8 +93,8 @@ func main() {
7193
}()
7294

7395
m := middleware.NewLogMiddleware(statsdClient)
74-
http.Handle("/wd/hub/session", m.Log(&handlers.CreateSession{Pool: poolInstance})) //selenium
75-
http.Handle("/session", m.Log(&handlers.CreateSession{Pool: poolInstance})) //wda
96+
http.Handle("/wd/hub/session", m.Log(&handlers.CreateSession{Pool: poolInstance, ClientFactory: clientFactory})) //selenium
97+
http.Handle("/session", m.Log(&handlers.CreateSession{Pool: poolInstance, ClientFactory: clientFactory})) //wda
7698
http.Handle("/grid/register", m.Log(&handlers.RegisterNode{Pool: poolInstance}))
7799
http.Handle("/grid/api/proxy", &handlers.ApiProxy{Pool: poolInstance})
78100
http.HandleFunc("/_info", heartbeat)

0 commit comments

Comments
 (0)