From a65732a495bbcd40ce26c735dd59b9b960956118 Mon Sep 17 00:00:00 2001 From: Ivan Krutov Date: Sat, 8 Jul 2017 11:04:55 +0300 Subject: [PATCH] Ability to customize service startup timeout (fixes #157) --- config/config.go | 19 +++++++------ config_test.go | 30 +++++++-------------- main.go | 13 +++++++-- selenoid.go | 44 ++++++++++++++----------------- selenoid_test.go | 3 +-- service/docker.go | 42 ++++++++++++++--------------- service/driver.go | 25 ++++++++---------- service/service.go | 66 +++++++++++++++++++++++++++++++++++----------- utils_test.go | 38 ++++++++++++++------------ 9 files changed, 155 insertions(+), 125 deletions(-) diff --git a/config/config.go b/config/config.go index eaf87d57..9d6cd45e 100644 --- a/config/config.go +++ b/config/config.go @@ -108,27 +108,26 @@ func (config *Config) Load(browsers, containerLogs string) error { } // Find - find concrete browser -func (config *Config) Find(name string, version *string) (*Browser, bool) { +func (config *Config) Find(name string, version string) (*Browser, string, bool) { config.lock.RLock() defer config.lock.RUnlock() browser, ok := config.Browsers[name] if !ok { - return nil, false + return nil, "", false } - if *version == "" { + if version == "" { log.Println("Using default version:", browser.Default) - *version = browser.Default - if *version == "" { - return nil, false + version = browser.Default + if version == "" { + return nil, "", false } } for v, b := range browser.Versions { - if strings.HasPrefix(v, *version) { - *version = v - return b, true + if strings.HasPrefix(v, version) { + return b, v, true } } - return nil, false + return nil, version, false } // State - get current state diff --git a/config_test.go b/config_test.go index 234c3368..9596f4b9 100644 --- a/config_test.go +++ b/config_test.go @@ -120,8 +120,7 @@ func TestConfigFindMissingBrowser(t *testing.T) { conf := config.NewConfig() conf.Load(confFile, logConfPath) - v := "" - _, ok := conf.Find("firefox", &v) + _, _, ok := conf.Find("firefox", "") AssertThat(t, ok, Is{false}) } @@ -131,8 +130,7 @@ func TestConfigFindDefaultVersionError(t *testing.T) { conf := config.NewConfig() conf.Load(confFile, logConfPath) - v := "" - _, ok := conf.Find("firefox", &v) + _, _, ok := conf.Find("firefox", "") AssertThat(t, ok, Is{false}) } @@ -142,8 +140,7 @@ func TestConfigFindDefaultVersion(t *testing.T) { conf := config.NewConfig() conf.Load(confFile, logConfPath) - v := "" - _, ok := conf.Find("firefox", &v) + _, v, ok := conf.Find("firefox", "") AssertThat(t, ok, Is{false}) AssertThat(t, v, EqualTo{"49.0"}) } @@ -154,8 +151,7 @@ func TestConfigFindFoundByEmptyPrefix(t *testing.T) { conf := config.NewConfig() conf.Load(confFile, logConfPath) - v := "" - _, ok := conf.Find("firefox", &v) + _, v, ok := conf.Find("firefox", "") AssertThat(t, ok, Is{true}) AssertThat(t, v, EqualTo{"49.0"}) } @@ -166,8 +162,7 @@ func TestConfigFindFoundByPrefix(t *testing.T) { conf := config.NewConfig() conf.Load(confFile, logConfPath) - v := "49" - _, ok := conf.Find("firefox", &v) + _, v, ok := conf.Find("firefox", "49") AssertThat(t, ok, Is{true}) AssertThat(t, v, EqualTo{"49.0"}) } @@ -178,8 +173,7 @@ func TestConfigFindFoundByMatch(t *testing.T) { conf := config.NewConfig() conf.Load(confFile, logConfPath) - v := "49.0" - _, ok := conf.Find("firefox", &v) + _, v, ok := conf.Find("firefox", "49.0") AssertThat(t, ok, Is{true}) AssertThat(t, v, EqualTo{"49.0"}) } @@ -190,8 +184,7 @@ func TestConfigFindImage(t *testing.T) { conf := config.NewConfig() conf.Load(confFile, logConfPath) - v := "49.0" - b, ok := conf.Find("firefox", &v) + b, v, ok := conf.Find("firefox", "49.0") AssertThat(t, ok, Is{true}) AssertThat(t, v, EqualTo{"49.0"}) AssertThat(t, b.Image, EqualTo{"image"}) @@ -223,8 +216,7 @@ func TestConfigConcurrentLoadAndRead(t *testing.T) { } done := make(chan string) go func() { - v := "" - browser, _ := conf.Find("firefox", &v) + browser, _, _ := conf.Find("firefox", "") done <- browser.Tmpfs["/tmp"] }() conf.Load(confFile, logConfPath) @@ -241,13 +233,11 @@ func TestConfigConcurrentRead(t *testing.T) { } done := make(chan string) go func() { - v := "" - browser, _ := conf.Find("firefox", &v) + browser, _, _ := conf.Find("firefox", "") done <- browser.Tmpfs["/tmp"] }() go func() { - v := "" - browser, _ := conf.Find("firefox", &v) + browser, _, _ := conf.Find("firefox", "") done <- browser.Tmpfs["/tmp"] }() <-done diff --git a/main.go b/main.go index 66685ead..432b36bd 100644 --- a/main.go +++ b/main.go @@ -65,6 +65,7 @@ var ( timeout time.Duration newSessionAttemptTimeout time.Duration sessionDeleteTimeout time.Duration + serviceStartupTimeout time.Duration limit int sessions = session.NewMap() confPath string @@ -92,6 +93,7 @@ func init() { flag.DurationVar(&timeout, "timeout", 60*time.Second, "Session idle timeout in time.Duration format") flag.DurationVar(&newSessionAttemptTimeout, "session-attempt-timeout", 30*time.Second, "New session attempt timeout in time.Duration format") flag.DurationVar(&sessionDeleteTimeout, "session-delete-timeout", 30*time.Second, "Session delete timeout in time.Duration format") + flag.DurationVar(&serviceStartupTimeout, "service-startup-timeout", 30*time.Second, "Service startup timeout in time.Duration format") flag.BoolVar(&version, "version", false, "Show version and exit") flag.Var(&mem, "mem", "Containers memory limit e.g. 128m or 1g") flag.Var(&cpu, "cpu", "Containers cpu limit as float e.g. 0.2 or 1.0") @@ -125,8 +127,14 @@ func init() { if err == nil { inDocker = true } + environment := service.Environment{ + InDocker: inDocker, + CPU: int64(cpu), + Memory: int64(mem), + StartupTimeout: serviceStartupTimeout, + } if disableDocker { - manager = &service.DefaultManager{InDocker: inDocker, Config: conf} + manager = &service.DefaultManager{Environment: &environment, Config: conf} return } dockerHost := os.Getenv("DOCKER_HOST") @@ -138,11 +146,12 @@ func init() { log.Fatal(err) } ip, _, _ := net.SplitHostPort(addr) + environment.IP = ip cli, err = client.NewEnvClient() if err != nil { log.Fatalf("new docker client: %v\n", err) } - manager = &service.DefaultManager{IP: ip, InDocker: inDocker, CPU: int64(cpu), Memory: int64(mem), Client: cli, Config: conf} + manager = &service.DefaultManager{Environment: &environment, Client: cli, Config: conf} } func cancelOnSignal() { diff --git a/selenoid.go b/selenoid.go index ca36fc13..29e16889 100644 --- a/selenoid.go +++ b/selenoid.go @@ -21,6 +21,7 @@ import ( "sync" "time" + "github.com/aerokube/selenoid/service" "github.com/aerokube/selenoid/session" "github.com/docker/docker/api/types" "golang.org/x/net/websocket" @@ -60,13 +61,6 @@ func (s *sess) url() string { return fmt.Sprintf("http://%s/wd/hub/session/%s", s.addr, s.id) } -type caps struct { - Name string `json:"browserName"` - Version string `json:"version"` - ScreenResolution string `json:"screenResolution"` - VNC bool `json:"enableVNC"` -} - func jsonError(w http.ResponseWriter, msg string, code int) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) @@ -112,7 +106,7 @@ func serial() uint64 { func create(w http.ResponseWriter, r *http.Request) { sessionStartTime := time.Now() - id := serial() + requestId := serial() quota, _, ok := r.BasicAuth() if !ok { quota = "unknown" @@ -120,43 +114,45 @@ func create(w http.ResponseWriter, r *http.Request) { body, err := ioutil.ReadAll(r.Body) r.Body.Close() if err != nil { - log.Printf("[%d] [ERROR_READING_REQUEST] [%s] [%v]\n", id, quota, err) + log.Printf("[%d] [ERROR_READING_REQUEST] [%s] [%v]\n", requestId, quota, err) jsonError(w, err.Error(), http.StatusBadRequest) queue.Drop() return } var browser struct { - Caps caps `json:"desiredCapabilities"` + Caps service.Caps `json:"desiredCapabilities"` } err = json.Unmarshal(body, &browser) if err != nil { - log.Printf("[%d] [BAD_JSON_FORMAT] [%s] [%v]\n", id, quota, err) + log.Printf("[%d] [BAD_JSON_FORMAT] [%s] [%v]\n", requestId, quota, err) jsonError(w, err.Error(), http.StatusBadRequest) queue.Drop() return } resolution, err := getScreenResolution(browser.Caps.ScreenResolution) if err != nil { - log.Printf("[%d] [BAD_SCREEN_RESOLUTION] [%s] [%s]\n", id, quota, browser.Caps.ScreenResolution) + log.Printf("[%d] [BAD_SCREEN_RESOLUTION] [%s] [%s]\n", requestId, quota, browser.Caps.ScreenResolution) jsonError(w, err.Error(), http.StatusBadRequest) queue.Drop() return } browser.Caps.ScreenResolution = resolution - starter, ok := manager.Find(browser.Caps.Name, &browser.Caps.Version, browser.Caps.ScreenResolution, browser.Caps.VNC, id) + starter, ok := manager.Find(browser.Caps, requestId) if !ok { - log.Printf("[%d] [ENVIRONMENT_NOT_AVAILABLE] [%s] [%s-%s]\n", id, quota, browser.Caps.Name, browser.Caps.Version) + log.Printf("[%d] [ENVIRONMENT_NOT_AVAILABLE] [%s] [%s-%s]\n", requestId, quota, browser.Caps.Name, browser.Caps.Version) jsonError(w, "Requested environment is not available", http.StatusBadRequest) queue.Drop() return } - u, container, vnc, cancel, err := starter.StartWithCancel() + startedService, err := starter.StartWithCancel() if err != nil { - log.Printf("[%d] [SERVICE_STARTUP_FAILED] [%s] [%v]\n", id, quota, err) + log.Printf("[%d] [SERVICE_STARTUP_FAILED] [%s] [%v]\n", requestId, quota, err) jsonError(w, err.Error(), http.StatusInternalServerError) queue.Drop() return } + u := startedService.Url + cancel := startedService.Cancel var resp *http.Response i := 1 for ; ; i++ { @@ -164,7 +160,7 @@ func create(w http.ResponseWriter, r *http.Request) { req, _ := http.NewRequest(http.MethodPost, r.URL.String(), bytes.NewReader(body)) ctx, done := context.WithTimeout(r.Context(), newSessionAttemptTimeout) defer done() - log.Printf("[%d] [SESSION_ATTEMPTED] [%s] [%s] [%d]\n", id, quota, u.String(), i) + log.Printf("[%d] [SESSION_ATTEMPTED] [%s] [%s] [%d]\n", requestId, quota, u.String(), i) rsp, err := httpClient.Do(req.WithContext(ctx)) select { case <-ctx.Done(): @@ -173,10 +169,10 @@ func create(w http.ResponseWriter, r *http.Request) { } switch ctx.Err() { case context.DeadlineExceeded: - log.Printf("[%d] [SESSION_ATTEMPT_TIMED_OUT] [%s]\n", id, quota) + log.Printf("[%d] [SESSION_ATTEMPT_TIMED_OUT] [%s]\n", requestId, quota) continue case context.Canceled: - log.Printf("[%d] [CLIENT_DISCONNECTED] [%s]\n", id, quota) + log.Printf("[%d] [CLIENT_DISCONNECTED] [%s]\n", requestId, quota) queue.Drop() cancel() return @@ -187,7 +183,7 @@ func create(w http.ResponseWriter, r *http.Request) { if rsp != nil { rsp.Body.Close() } - log.Printf("[%d] [SESSION_FAILED] [%s] - [%s]\n", id, u.String(), err) + log.Printf("[%d] [SESSION_FAILED] [%s] - [%s]\n", requestId, u.String(), err) jsonError(w, err.Error(), http.StatusInternalServerError) queue.Drop() cancel() @@ -230,7 +226,7 @@ func create(w http.ResponseWriter, r *http.Request) { } } if s.ID == "" { - log.Printf("[%d] [SESSION_FAILED] [%s] [Bad response from %s - %v]\n", id, quota, u.String(), resp.Status) + log.Printf("[%d] [SESSION_FAILED] [%s] [Bad response from %s - %v]\n", requestId, quota, u.String(), resp.Status) queue.Drop() cancel() return @@ -240,15 +236,15 @@ func create(w http.ResponseWriter, r *http.Request) { Browser: browser.Caps.Name, Version: browser.Caps.Version, URL: u, - Container: container, - VNC: vnc, + Container: startedService.ID, + VNC: startedService.VNCHostPort, Screen: browser.Caps.ScreenResolution, Cancel: cancel, Timeout: onTimeout(timeout, func() { request{r}.session(s.ID).Delete() })}) queue.Create() - log.Printf("[%d] [SESSION_CREATED] [%s] [%s] [%s] [%d] [%v]\n", id, quota, s.ID, u, i, time.Since(sessionStartTime)) + log.Printf("[%d] [SESSION_CREATED] [%s] [%s] [%s] [%d] [%v]\n", requestId, quota, s.ID, u, i, time.Since(sessionStartTime)) } func getScreenResolution(input string) (string, error) { diff --git a/selenoid_test.go b/selenoid_test.go index 69cac67b..531cb079 100644 --- a/selenoid_test.go +++ b/selenoid_test.go @@ -447,11 +447,10 @@ func TestFileUploadTwoFiles(t *testing.T) { var sess map[string]string AssertThat(t, resp, AllOf{Code{http.StatusOK}, IsJson{&sess}}) - resp, err = http.Post(With(srv.URL).Path(fmt.Sprintf("/wd/hub/session/%s/file", sess["sessionId"])), "", bytes.NewReader([]byte(`{"file":"UEsDBAoAAAAAAKGJ4koAAAAAAAAAAAAAAAAHABwAb25lLnR4dFVUCQADbv9YWZT/WFl1eAsAAQT1AQAABBQAAABQSwMECgAAAAAApIniSgAAAAAAAAAAAAAAAAcAHAB0d28udHh0VVQJAANz/1hZc/9YWXV4CwABBPUBAAAEFAAAAFBLAQIeAwoAAAAAAKGJ4koAAAAAAAAAAAAAAAAHABgAAAAAAAAAAACkgQAAAABvbmUudHh0VVQFAANu/1hZdXgLAAEE9QEAAAQUAAAAUEsBAh4DCgAAAAAApIniSgAAAAAAAAAAAAAAAAcAGAAAAAAAAAAAAKSBQQAAAHR3by50eHRVVAUAA3P/WFl1eAsAAQT1AQAABBQAAABQSwUGAAAAAAIAAgCaAAAAggAAAAAA"}`))) + resp, err = http.Post(With(srv.URL).Path(fmt.Sprintf("/wd/hub/session/%s/file", sess["sessionId"])), "", bytes.NewReader([]byte(`{"file":"UEsDBAoAAAAAAKGJ4koAAAAAAAAAAAAAAAAHABwAb25lLnR4dFVUCQADbv9YWZT/WFl1eAsAAQT1AQAABBQAAABQSwMECgAAAAAApIniSgAAAAAAAAAAAAAAAAcAHAB0d28udHh0VVQJAANz/1hZc/9YWXV4CwABBPUBAAAEFAAAAFBLAQIeAwoAAAAAAKGJ4koAAAAAAAAAAAAAAAAHABgAAAAAAAAAAACkgQAAAABvbmUudHh0VVQFAANu/1hZdXgLAAEE9QEAAAQUAAAAUEsBAh4DCgAAAAAApIniSgAAAAAAAAAAAAAAAAcAGAAAAAAAAAAAAKSBQQAAAHR3by50eHRVVAUAA3P/WFl1eAsAAQT1AQAABBQAAABQSwUGAAAAAAIAAgCaAAAAggAAAAAA"}`))) AssertThat(t, err, Is{nil}) AssertThat(t, resp, Code{http.StatusBadRequest}) sessions.Remove(sess["sessionId"]) queue.Release() } - diff --git a/service/docker.go b/service/docker.go index 81a3eba8..d150e235 100644 --- a/service/docker.go +++ b/service/docker.go @@ -8,7 +8,6 @@ import ( "net/url" "time" - "github.com/aerokube/selenoid/config" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" @@ -18,30 +17,25 @@ import ( // Docker - docker container manager type Docker struct { - IP string - InDocker bool - CPU int64 - Memory int64 - Client *client.Client - Service *config.Browser - LogConfig *container.LogConfig - ScreenResolution string - VNC bool - RequestId uint64 + ServiceBase + Environment + Caps + LogConfig *container.LogConfig + Client *client.Client } // StartWithCancel - Starter interface implementation -func (d *Docker) StartWithCancel() (*url.URL, string, string, func(), error) { +func (d *Docker) StartWithCancel() (*StartedService, error) { selenium, err := nat.NewPort("tcp", d.Service.Port) if err != nil { - return nil, "", "", nil, fmt.Errorf("new selenium port: %v", err) + return nil, fmt.Errorf("new selenium port: %v", err) } exposedPorts := map[nat.Port]struct{}{selenium: {}} var vnc nat.Port if d.VNC { vnc, err = nat.NewPort("tcp", "5900") if err != nil { - return nil, "", "", nil, fmt.Errorf("new vnc port: %v", err) + return nil, fmt.Errorf("new vnc port: %v", err) } exposedPorts[vnc] = struct{}{} } @@ -82,25 +76,25 @@ func (d *Docker) StartWithCancel() (*url.URL, string, string, func(), error) { }, &network.NetworkingConfig{}, "") if err != nil { - return nil, "", "", nil, fmt.Errorf("create container: %v", err) + return nil, fmt.Errorf("create container: %v", err) } containerStartTime := time.Now() log.Printf("[%d] [STARTING_CONTAINER] [%s] [%s]\n", d.RequestId, d.Service.Image, container.ID) err = d.Client.ContainerStart(ctx, container.ID, types.ContainerStartOptions{}) if err != nil { d.removeContainer(ctx, d.Client, container.ID) - return nil, "", "", nil, fmt.Errorf("start container: %v", err) + return nil, fmt.Errorf("start container: %v", err) } log.Printf("[%d] [CONTAINER_STARTED] [%s] [%s] [%v]\n", d.RequestId, d.Service.Image, container.ID, time.Since(containerStartTime)) stat, err := d.Client.ContainerInspect(ctx, container.ID) if err != nil { d.removeContainer(ctx, d.Client, container.ID) - return nil, "", "", nil, fmt.Errorf("inspect container %s: %s", container.ID, err) + return nil, fmt.Errorf("inspect container %s: %s", container.ID, err) } _, ok := stat.NetworkSettings.Ports[selenium] if !ok { d.removeContainer(ctx, d.Client, container.ID) - return nil, "", "", nil, fmt.Errorf("no bingings available for %v", selenium) + return nil, fmt.Errorf("no bingings available for %v", selenium) } seleniumHostPort, vncHostPort := "", "" if d.IP == "" { @@ -123,14 +117,20 @@ func (d *Docker) StartWithCancel() (*url.URL, string, string, func(), error) { } u := &url.URL{Scheme: "http", Host: seleniumHostPort, Path: d.Service.Path} serviceStartTime := time.Now() - err = wait(u.String(), 30*time.Second) + err = wait(u.String(), d.StartupTimeout) if err != nil { d.removeContainer(ctx, d.Client, container.ID) - return nil, "", "", nil, fmt.Errorf("wait: %v", err) + return nil, fmt.Errorf("wait: %v", err) } log.Printf("[%d] [SERVICE_STARTED] [%s] [%s] [%v]\n", d.RequestId, d.Service.Image, container.ID, time.Since(serviceStartTime)) log.Printf("[%d] [PROXY_TO] [%s] [%s] [%s]\n", d.RequestId, d.Service.Image, container.ID, u.String()) - return u, container.ID, vncHostPort, func() { d.removeContainer(ctx, d.Client, container.ID) }, nil + s := StartedService{ + Url: u, + ID: container.ID, + VNCHostPort: vncHostPort, + Cancel: func() { d.removeContainer(ctx, d.Client, container.ID) }, + } + return &s, nil } func (d *Docker) removeContainer(ctx context.Context, cli *client.Client, id string) { diff --git a/service/driver.go b/service/driver.go index b9605d32..6a40074f 100644 --- a/service/driver.go +++ b/service/driver.go @@ -9,38 +9,35 @@ import ( "time" "errors" - - "github.com/aerokube/selenoid/config" ) // Driver - driver processes manager type Driver struct { - InDocker bool - Service *config.Browser - RequestId uint64 + ServiceBase + Environment } // StartWithCancel - Starter interface implementation -func (d *Driver) StartWithCancel() (*url.URL, string, string, func(), error) { +func (d *Driver) StartWithCancel() (*StartedService, error) { requestId := d.RequestId slice, ok := d.Service.Image.([]interface{}) if !ok { - return nil, "", "", nil, fmt.Errorf("configuration error: image is not an array: %v", d.Service.Image) + return nil, fmt.Errorf("configuration error: image is not an array: %v", d.Service.Image) } cmdLine := []string{} for _, c := range slice { if _, ok := c.(string); !ok { - return nil, "", "", nil, fmt.Errorf("configuration error: value is not a string: %v", c) + return nil, fmt.Errorf("configuration error: value is not a string: %v", c) } cmdLine = append(cmdLine, c.(string)) } if len(cmdLine) == 0 { - return nil, "", "", nil, errors.New("configuration error: image is empty") + return nil, errors.New("configuration error: image is empty") } log.Printf("[%d] [ALLOCATING_PORT]\n", requestId) l, err := net.Listen("tcp", "localhost:0") if err != nil { - return nil, "", "", nil, fmt.Errorf("cannot bind to port: %v", err) + return nil, fmt.Errorf("cannot bind to port: %v", err) } u := &url.URL{Scheme: "http", Host: l.Addr().String()} _, port, _ := net.SplitHostPort(l.Addr().String()) @@ -54,16 +51,16 @@ func (d *Driver) StartWithCancel() (*url.URL, string, string, func(), error) { err = cmd.Start() if err != nil { e := fmt.Errorf("cannot start process %v: %v", cmdLine, err) - return nil, "", "", nil, e + return nil, e } - err = wait(u.String(), 30*time.Second) + err = wait(u.String(), d.StartupTimeout) if err != nil { d.stopProcess(cmd) - return nil, "", "", nil, err + return nil, err } log.Printf("[%d] [PROCESS_STARTED] [%d] [%v]\n", requestId, cmd.Process.Pid, time.Since(s)) log.Printf("[%d] [PROXYING_REQUESTS] [%s]\n", requestId, u.String()) - return u, "", "", func() { d.stopProcess(cmd) }, nil + return &StartedService{Url: u, Cancel: func() { d.stopProcess(cmd) }}, nil } func (d *Driver) stopProcess(cmd *exec.Cmd) { diff --git a/service/service.go b/service/service.go index 30fdd410..2d79e722 100644 --- a/service/service.go +++ b/service/service.go @@ -11,30 +11,61 @@ import ( "github.com/docker/docker/client" ) +// Caps - user capabilities +type Caps struct { + Name string `json:"browserName"` + Version string `json:"version"` + ScreenResolution string `json:"screenResolution"` + VNC bool `json:"enableVNC"` +} + +// Environment - all settings that influence browser startup +type Environment struct { + IP string + InDocker bool + CPU int64 + Memory int64 + StartupTimeout time.Duration +} + +// ServiceBase - stores fields required by all services +type ServiceBase struct { + RequestId uint64 + Service *config.Browser +} + +// StartedService - all started service properties +type StartedService struct { + Url *url.URL + ID string + VNCHostPort string + Cancel func() +} + // Starter - interface to create session with cancellation ability type Starter interface { - StartWithCancel() (*url.URL, string, string, func(), error) + StartWithCancel() (*StartedService, error) } // Manager - interface to choose appropriate starter type Manager interface { - Find(s string, v *string, sr string, vnc bool, requestId uint64) (Starter, bool) + Find(caps Caps, requestId uint64) (Starter, bool) } // DefaultManager - struct for default implementation type DefaultManager struct { - IP string - InDocker bool - CPU int64 - Memory int64 - Client *client.Client - Config *config.Config + Environment *Environment + Client *client.Client + Config *config.Config } // Find - default implementation Manager interface -func (m *DefaultManager) Find(s string, v *string, sr string, vnc bool, requestId uint64) (Starter, bool) { - log.Printf("[%d] [LOCATING_SERVICE] [%s-%s]\n", requestId, s, *v) - service, ok := m.Config.Find(s, v) +func (m *DefaultManager) Find(caps Caps, requestId uint64) (Starter, bool) { + browserName := caps.Name + version := caps.Version + log.Printf("[%d] [LOCATING_SERVICE] [%s-%s]\n", requestId, browserName, version) + service, version, ok := m.Config.Find(browserName, version) + serviceBase := ServiceBase{RequestId: requestId, Service: service} if !ok { return nil, false } @@ -43,11 +74,16 @@ func (m *DefaultManager) Find(s string, v *string, sr string, vnc bool, requestI if m.Client == nil { return nil, false } - log.Printf("[%d] [USING_DOCKER] [%s-%s]\n", requestId, s, *v) - return &Docker{m.IP, m.InDocker, m.CPU, m.Memory, m.Client, service, m.Config.ContainerLogs, sr, vnc, requestId}, true + log.Printf("[%d] [USING_DOCKER] [%s-%s]\n", requestId, browserName, version) + return &Docker{ + ServiceBase: serviceBase, + Environment: *m.Environment, + Caps: caps, + Client: m.Client, + LogConfig: m.Config.ContainerLogs}, true case []interface{}: - log.Printf("[%d] [USING_DRIVER] [%s-%s]\n", requestId, s, *v) - return &Driver{m.InDocker, service, requestId}, true + log.Printf("[%d] [USING_DRIVER] [%s-%s]\n", requestId, browserName, version) + return &Driver{ServiceBase: serviceBase, Environment: *m.Environment}, true } return nil, false } diff --git a/utils_test.go b/utils_test.go index daa3ad60..a379b43f 100644 --- a/utils_test.go +++ b/utils_test.go @@ -29,49 +29,53 @@ func HTTPResponse(msg string, status int) http.Handler { }) } -func (m *HTTPTest) StartWithCancel() (*url.URL, string, string, func(), error) { +func (m *HTTPTest) StartWithCancel() (*service.StartedService, error) { log.Println("Starting HTTPTest Service...") s := httptest.NewServer(m.Handler) u, err := url.Parse(s.URL) if err != nil { log.Println("Failed to start HTTPTest Service...") - return nil, "", "", func() {}, err + return nil, err } log.Println("HTTPTest Service started...") if m.Action != nil { m.Action(s) } - return u, "", "", func() { - log.Println("Stopping HTTPTest Service...") - s.Close() - log.Println("HTTPTest Service stopped...") - if m.Cancel != nil { - go func() { - m.Cancel <- true - }() - } - }, nil + ss := service.StartedService{ + Url: u, + Cancel: func() { + log.Println("Stopping HTTPTest Service...") + s.Close() + log.Println("HTTPTest Service stopped...") + if m.Cancel != nil { + go func() { + m.Cancel <- true + }() + } + }, + } + return &ss, nil } -func (m *HTTPTest) Find(s string, v *string, sr string, vnc bool, requestId uint64) (service.Starter, bool) { +func (m *HTTPTest) Find(caps service.Caps, requestId uint64) (service.Starter, bool) { return m, true } type StartupError struct{} -func (m *StartupError) StartWithCancel() (*url.URL, string, string, func(), error) { +func (m *StartupError) StartWithCancel() (*service.StartedService, error) { log.Println("Starting StartupError Service...") log.Println("Failed to start StartupError Service...") - return nil, "", "", nil, errors.New("Failed to start Service") + return nil, errors.New("Failed to start Service") } -func (m *StartupError) Find(s string, v *string, sr string, vnc bool, requestId uint64) (service.Starter, bool) { +func (m *StartupError) Find(caps service.Caps, requestId uint64) (service.Starter, bool) { return m, true } type BrowserNotFound struct{} -func (m *BrowserNotFound) Find(s string, v *string, sr string, vnc bool, requestId uint64) (service.Starter, bool) { +func (m *BrowserNotFound) Find(caps service.Caps, requestId uint64) (service.Starter, bool) { return nil, false }