From b9a35075f4ae8109adc56f5f95396c43a6838651 Mon Sep 17 00:00:00 2001 From: Ivan Krutov Date: Thu, 31 May 2018 07:36:42 +0300 Subject: [PATCH] Added ability to specify browser version constraints and download Android images (fixes #155, fixes #159) --- docs/selenoid-commands.adoc | 2 +- selenoid/docker.go | 122 +++++++++++++++++++++--------------- selenoid/docker_test.go | 28 +-------- selenoid/drivers.go | 1 + 4 files changed, 77 insertions(+), 76 deletions(-) diff --git a/docs/selenoid-commands.adoc b/docs/selenoid-commands.adoc index f81ec35..cd10c91 100644 --- a/docs/selenoid-commands.adoc +++ b/docs/selenoid-commands.adoc @@ -112,7 +112,7 @@ This command does nothing when already downloaded. Use `--force` flag to downloa + [source,bash] ---- -./cm selenoid configure [--browsers firefox,opera] [--last-versions 2] [--tmpfs 128] +./cm selenoid configure [--browsers firefox:>45.0,opera,android] [--last-versions 2] [--tmpfs 128] ---- + Use `--browsers` to limit browsers to be configured, `--tmpfs` - to add https://en.wikipedia.org/wiki/Tmpfs[Tmpfs] support, `--last-versions` - to limit how many last browser versions to download. If you wish to download all available versions - specify `--last-versions 0`. diff --git a/selenoid/docker.go b/selenoid/docker.go index d2419c4..21d5188 100644 --- a/selenoid/docker.go +++ b/selenoid/docker.go @@ -45,6 +45,7 @@ import ( const ( Latest = "latest" firefox = "firefox" + android = "android" opera = "opera" tag_1216 = "12.16" selenoidImage = "aerokube/selenoid" @@ -158,15 +159,15 @@ func (c *DockerConfigurator) initAuthConfig() (*types.AuthConfig, error) { } func (c *DockerConfigurator) initRegistryClient() error { - url := strings.TrimSuffix(c.RegistryUrl, "/") + u := strings.TrimSuffix(c.RegistryUrl, "/") username, password := "", "" if c.authConfig != nil { username, password = c.authConfig.Username, c.authConfig.Password } reg := ®istry.Registry{ - URL: url, + URL: u, Client: &http.Client{ - Transport: registry.WrapTransport(http.DefaultTransport, url, username, password), + Transport: registry.WrapTransport(http.DefaultTransport, u, username, password), }, Logf: func(format string, args ...interface{}) { c.Tracef(format, args...) @@ -319,32 +320,21 @@ func (c *DockerConfigurator) Configure() (*SelenoidConfig, error) { } func (c *DockerConfigurator) createConfig() SelenoidConfig { - supportedBrowsers := c.getSupportedBrowsers() + requestedBrowsers := c.parseRequestedBrowsers(c.Browsers) + browsersToIterate := c.getBrowsersToIterate(requestedBrowsers) browsers := make(map[string]config.Versions) - browsersToIterate := supportedBrowsers - if c.Browsers != "" { - requestedBrowsers := strings.Split(c.Browsers, comma) - if len(requestedBrowsers) > 0 { - browsersToIterate = make(map[string]string) - for _, rb := range requestedBrowsers { - if image, ok := supportedBrowsers[rb]; ok { - browsersToIterate[rb] = image - continue - } - c.Errorf("Unsupported browser: %s", rb) - } - } - } for browserName, image := range browsersToIterate { c.Titlef(`Processing browser "%v"...`, color.GreenString(browserName)) tags := c.fetchImageTags(image) - image, tags = c.preProcessImageTags(image, browserName, tags) - pulledTags := tags + if c.VNC { + c.Pointf("Requested to download VNC images...") + image = fmt.Sprintf("selenoid/vnc_%s", browserName) + } + versionConstraint := requestedBrowsers[browserName] + pulledTags := c.filterTags(tags, versionConstraint) fullyQualifiedImage := c.getFullyQualifiedImageRef(image) if c.DownloadNeeded { - pulledTags = c.pullImages(fullyQualifiedImage, tags) - } else if c.LastVersions > 0 && c.LastVersions <= len(tags) { - pulledTags = tags[:c.LastVersions] + pulledTags = c.pullImages(fullyQualifiedImage, pulledTags) } if len(pulledTags) > 0 { @@ -357,12 +347,46 @@ func (c *DockerConfigurator) createConfig() SelenoidConfig { return browsers } -func (c *DockerConfigurator) getSupportedBrowsers() map[string]string { - return map[string]string{ +func (c *DockerConfigurator) parseRequestedBrowsers(requestedBrowsers string) map[string]*ver.Constraints { + ret := make(map[string]*ver.Constraints) + for _, section := range strings.Split(requestedBrowsers, comma) { + pieces := strings.Split(section, colon) + if len(pieces) == 2 { + browserName := pieces[0] + versionConstraintString := pieces[1] + versionConstraint, err := ver.NewConstraint(versionConstraintString) + if err != nil { + c.Errorf("Invalid version constraint %s: %v - ignoring browser %s...", versionConstraintString, err, browserName) + continue + } + ret[browserName] = &versionConstraint + } else if len(pieces) == 1 { + browserName := pieces[0] + ret[browserName] = nil + } + } + return ret +} + +func (c *DockerConfigurator) getBrowsersToIterate(requestedBrowsers map[string]*ver.Constraints) map[string]string { + ret := make(map[string]string) + defaultBrowsers := map[string]string{ "firefox": "selenoid/firefox", "chrome": "selenoid/chrome", "opera": "selenoid/opera", } + for browserName := range requestedBrowsers { + if image, ok := defaultBrowsers[browserName]; ok { + ret[browserName] = image + continue + } + c.Errorf("Unsupported browser: %s", browserName) + } + + if _, ok := requestedBrowsers[android]; ok { + ret["android"] = "selenoid/android" + } + return ret } func (c *DockerConfigurator) fetchImageTags(image string) []string { @@ -379,7 +403,7 @@ func (c *DockerConfigurator) fetchImageTags(image string) []string { } func filterOutLatest(tags []string) []string { - ret := []string{} + var ret []string for _, tag := range tags { if !strings.HasPrefix(tag, Latest) { ret = append(ret, tag) @@ -388,6 +412,26 @@ func filterOutLatest(tags []string) []string { return ret } +func (c *DockerConfigurator) filterTags(tags []string, versionConstraint *ver.Constraints) []string { + if versionConstraint != nil { + var ret []string + for _, tag := range tags { + version, err := ver.NewVersion(tag) + if err != nil { + c.Errorf("Skipping tag %s as it does not follow semantic versioning: %v", tag, err) + continue + } + if versionConstraint.Check(version) { + ret = append(ret, tag) + } + } + return ret + } else if c.LastVersions > 0 && c.LastVersions <= len(tags) { + return tags[:c.LastVersions] + } + return tags +} + func (c *DockerConfigurator) createVersions(browserName string, image string, tags []string) config.Versions { versions := config.Versions{ Default: c.getVersionFromTag(browserName, tags[0]), @@ -400,7 +444,7 @@ func (c *DockerConfigurator) createVersions(browserName string, image string, ta Port: "4444", Path: "/", } - if browserName == firefox || (browserName == opera && version == tag_1216) { + if browserName == firefox || browserName == android || (browserName == opera && version == tag_1216) { browser.Path = "/wd/hub" } if c.Tmpfs > 0 { @@ -422,18 +466,14 @@ func imageWithTag(image string, tag string) string { } func (c *DockerConfigurator) pullImages(image string, tags []string) []string { - pulledTags := []string{} + var pulledTags []string ctx := context.Background() -loop: for _, tag := range tags { ref := imageWithTag(image, tag) if !c.pullImage(ctx, ref) { continue } pulledTags = append(pulledTags, tag) - if c.LastVersions > 0 && len(pulledTags) == c.LastVersions { - break loop - } } return pulledTags } @@ -450,24 +490,6 @@ func (c *DockerConfigurator) getFullyQualifiedImageRef(ref string) string { return ref } -func (c *DockerConfigurator) preProcessImageTags(image string, browserName string, tags []string) (string, []string) { - imageToProcess := image - tagsToProcess := tags - if c.VNC { - c.Pointf("Requested to download VNC images...") - imageToProcess = "selenoid/vnc" - tagsToProcess = []string{} - for _, tag := range tags { - tagsToProcess = append(tagsToProcess, createVNCTag(browserName, tag)) - } - } - return imageToProcess, tagsToProcess -} - -func createVNCTag(browserName string, version string) string { - return fmt.Sprintf("%s_%s", browserName, version) -} - func (c *DockerConfigurator) getVersionFromTag(browserName string, tag string) string { if c.VNC { return strings.TrimPrefix(tag, browserName+"_") diff --git a/selenoid/docker_test.go b/selenoid/docker_test.go index df275ed..3f45e5b 100644 --- a/selenoid/docker_test.go +++ b/selenoid/docker_test.go @@ -243,21 +243,6 @@ func TestPullImages(t *testing.T) { AssertThat(t, tags[1], EqualTo{"45.0"}) } -func TestPreProcessImageTags(t *testing.T) { - image := "selenoid/firefox" - browserName := "firefox" - tags := []string{"33.0", "34.0"} - - c, err := NewDockerConfigurator(&LifecycleConfig{ - RegistryUrl: mockDockerServer.URL, - VNC: true, - }) - AssertThat(t, err, Is{nil}) - imageToProcess, tagsToProcess := c.preProcessImageTags(image, browserName, tags) - AssertThat(t, imageToProcess, EqualTo{"selenoid/vnc"}) - AssertThat(t, tagsToProcess, EqualTo{[]string{"firefox_33.0", "firefox_34.0"}}) -} - func TestConfigureDocker(t *testing.T) { testConfigure(t, true) } @@ -276,7 +261,7 @@ func testConfigure(t *testing.T, download bool) { Quiet: false, LastVersions: 2, Tmpfs: 512, - Browsers: "firefox,opera", + Browsers: "firefox:>45.0,opera", Args: "-limit 42", VNC: true, Env: testEnv, @@ -303,14 +288,7 @@ func testConfigure(t *testing.T, download bool) { correctFFBrowsers := make(map[string]*config.Browser) correctFFBrowsers["46.0"] = &config.Browser{ - Image: "selenoid/vnc:firefox_46.0", - Port: "4444", - Path: "/wd/hub", - Tmpfs: tmpfsMap, - Env: []string{testEnv}, - } - correctFFBrowsers["45.0"] = &config.Browser{ - Image: "selenoid/vnc:firefox_45.0", + Image: "selenoid/vnc_firefox:46.0", Port: "4444", Path: "/wd/hub", Tmpfs: tmpfsMap, @@ -328,7 +306,7 @@ func testConfigure(t *testing.T, download bool) { correctOperaBrowsers := make(map[string]*config.Browser) correctOperaBrowsers["44.0"] = &config.Browser{ - Image: "selenoid/vnc:opera_44.0", + Image: "selenoid/vnc_opera:44.0", Port: "4444", Path: "/", Tmpfs: tmpfsMap, diff --git a/selenoid/drivers.go b/selenoid/drivers.go index 13b81f2..9dd3da7 100644 --- a/selenoid/drivers.go +++ b/selenoid/drivers.go @@ -31,6 +31,7 @@ const ( zipMagicHeader = "504b" gzipMagicHeader = "1f8b" comma = "," + colon = ":" owner = "aerokube" selenoidRepo = "selenoid" selenoidUIRepo = "selenoid-ui"