Skip to content
This repository has been archived by the owner on Dec 17, 2024. It is now read-only.

Commit

Permalink
Added private registry support (fixes #98)
Browse files Browse the repository at this point in the history
  • Loading branch information
vania-pooh committed Dec 15, 2017
1 parent 263b2ba commit 2af7ea1
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 31 deletions.
10 changes: 8 additions & 2 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 5 additions & 7 deletions cmd/selenoid.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ import (
"runtime"
)

const (
registryUrl = "https://registry.hub.docker.com"
defaultBrowsersJsonURL = "https://raw.githubusercontent.com/aerokube/cm/master/browsers.json"
)

var (
lastVersions int
tmpfs int
Expand Down Expand Up @@ -111,9 +106,12 @@ func initFlags() {
selenoidConfigureCmd,
selenoidStartCmd,
selenoidUpdateCmd,
selenoidDownloadUICmd,
selenoidStartUICmd,
selenoidUpdateUICmd,
} {
c.Flags().StringVarP(&version, "version", "v", selenoid.Latest, "desired version; default is latest release")
c.Flags().StringVarP(&registry, "registry", "r", registryUrl, "Docker registry to use")
c.Flags().StringVarP(&registry, "registry", "r", selenoid.DefaultRegistryUrl, "Docker registry to use")
}
for _, c := range []*cobra.Command{
selenoidConfigureCmd,
Expand All @@ -122,7 +120,7 @@ func initFlags() {
} {
c.Flags().StringVarP(&browsers, "browsers", "b", "", "comma separated list of browser names to process")
c.Flags().StringVarP(&browserEnv, "browser-env", "w", "", "override container or driver environment variables (e.g. \"KEY1=value1 KEY2=value2\")")
c.Flags().StringVarP(&browsersJSONUrl, "browsers-json", "j", defaultBrowsersJsonURL, "browsers JSON data URL (in most cases never need to be set manually)")
c.Flags().StringVarP(&browsersJSONUrl, "browsers-json", "j", selenoid.DefaultBrowsersJsonURL, "browsers JSON data URL (in most cases never need to be set manually)")
c.Flags().BoolVarP(&skipDownload, "no-download", "n", false, "only output config file without downloading images or drivers")
c.Flags().IntVarP(&lastVersions, "last-versions", "l", 2, "process only last N versions (Docker only)")
c.Flags().IntVarP(&tmpfs, "tmpfs", "t", 0, "add tmpfs volume sized in megabytes (Docker only)")
Expand Down
5 changes: 5 additions & 0 deletions docs/selenoid-commands.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ To override Selenoid startup arguments sessions add `--args` flag:

$ ./cm selenoid start --args "-limit 10"
+
To download images from private registry - log in with `docker login` command and add `--registry` flag:

$ docker login my-registry.example.com # Specify user name and password
$ ./cm selenoid start --registry https://my-registry.example.com
+
An alternative to downloading `cm` manually is using Docker container:

# docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v ${HOME}:/root -e OVERRIDE_HOME=${HOME} aerokube/cm:latest-release selenoid start
6 changes: 4 additions & 2 deletions selenoid/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,10 @@ type PortAware struct {
}

const (
SelenoidDefaultPort = 4444
SelenoidUIDefaultPort = 8080
SelenoidDefaultPort = 4444
SelenoidUIDefaultPort = 8080
DefaultRegistryUrl = "https://registry.hub.docker.com"
DefaultBrowsersJsonURL = "https://raw.githubusercontent.com/aerokube/cm/master/browsers.json"
)

func getHomeDir() string {
Expand Down
86 changes: 70 additions & 16 deletions selenoid/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice"
authconfig "github.com/docker/docker/cliconfig"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"github.com/heroku/docker-registry-client/registry"
Expand All @@ -31,8 +32,10 @@ import (
"regexp"
"runtime"

"encoding/base64"
"github.com/aerokube/cm/render/rewriter"
"github.com/fatih/color"
"net/url"
. "vbom.ml/util/sortorder"
)

Expand Down Expand Up @@ -61,13 +64,15 @@ type DockerConfigurator struct {
EnvAware
BrowserEnvAware
PortAware
LastVersions int
Pull bool
RegistryUrl string
Tmpfs int
VNC bool
docker *client.Client
reg *registry.Registry
LastVersions int
Pull bool
RegistryUrl string
Tmpfs int
VNC bool
docker *client.Client
reg *registry.Registry
authConfig *types.AuthConfig
registryHostname string
}

func NewDockerConfigurator(config *LifecycleConfig) (*DockerConfigurator, error) {
Expand All @@ -94,6 +99,12 @@ func NewDockerConfigurator(config *LifecycleConfig) (*DockerConfigurator, error)
if err != nil {
return nil, fmt.Errorf("new configurator: %v", err)
}
authConfig, err := c.initAuthConfig()
if err != nil {

} else {
c.authConfig = authConfig
}
err = c.initRegistryClient()
if err != nil {
return nil, fmt.Errorf("new configurator: %v", err)
Expand All @@ -110,12 +121,38 @@ func (c *DockerConfigurator) initDockerClient() error {
return nil
}

func (c *DockerConfigurator) initAuthConfig() (*types.AuthConfig, error) {
configFile, err := authconfig.Load("")
if err != nil {
return nil, err
}
u, err := url.Parse(c.RegistryUrl)
if err != nil {
return nil, err
}

registryHostname := u.Hostname()
if c.RegistryUrl != DefaultRegistryUrl {
c.registryHostname = registryHostname
}
if cfg, ok := configFile.AuthConfigs[registryHostname]; ok {
c.Titlef(`Loaded authentication data for "%s"`, registryHostname)
return &cfg, nil
}

return nil, nil
}

func (c *DockerConfigurator) initRegistryClient() error {
url := strings.TrimSuffix(c.RegistryUrl, "/")
username, password := "", ""
if c.authConfig != nil {
username, password = c.authConfig.Username, c.authConfig.Password
}
reg := &registry.Registry{
URL: url,
Client: &http.Client{
Transport: registry.WrapTransport(http.DefaultTransport, url, "", ""),
Transport: registry.WrapTransport(http.DefaultTransport, url, username, password),
},
Logf: func(format string, args ...interface{}) {
c.Tracef(format, args...)
Expand Down Expand Up @@ -223,9 +260,9 @@ func (c *DockerConfigurator) downloadImpl(imageName string, version string, erro
version = *latestVersion
}
}
ref := imageName
ref := c.getFullyQualifiedImageRef(imageName)
if version != Latest {
ref = fmt.Sprintf("%s:%s", ref, version)
ref = imageWithTag(ref, version)
}
if !c.pullImage(context.Background(), ref) {
return "", errors.New(errorMessage)
Expand Down Expand Up @@ -280,14 +317,15 @@ func (c *DockerConfigurator) createConfig() SelenoidConfig {
tags := c.fetchImageTags(image)
image, tags = c.preProcessImageTags(image, browserName, tags)
pulledTags := tags
fullyQualifiedImage := c.getFullyQualifiedImageRef(image)
if c.DownloadNeeded {
pulledTags = c.pullImages(image, tags)
pulledTags = c.pullImages(fullyQualifiedImage, tags)
} else if c.LastVersions > 0 && c.LastVersions <= len(tags) {
pulledTags = tags[:c.LastVersions]
}

if len(pulledTags) > 0 {
browsers[browserName] = c.createVersions(browserName, image, pulledTags)
browsers[browserName] = c.createVersions(browserName, fullyQualifiedImage, pulledTags)
}
}
if c.DownloadNeeded {
Expand All @@ -299,8 +337,8 @@ func (c *DockerConfigurator) createConfig() SelenoidConfig {
func (c *DockerConfigurator) getSupportedBrowsers() map[string]string {
return map[string]string{
"firefox": "selenoid/firefox",
"chrome": "selenoid/chrome",
"opera": "selenoid/opera",
"chrome": "selenoid/chrome",
"opera": "selenoid/opera",
}
}

Expand Down Expand Up @@ -379,7 +417,14 @@ loop:

func (c *DockerConfigurator) pullVideoRecorderImage() {
c.Titlef("Pulling video recorder image...")
c.pullImage(context.Background(), videoRecorderImage)
c.pullImage(context.Background(), c.getFullyQualifiedImageRef(videoRecorderImage))
}

func (c *DockerConfigurator) getFullyQualifiedImageRef(ref string) string {
if c.registryHostname != "" {
return fmt.Sprintf("%s/%s", c.registryHostname, ref)
}
return ref
}

func (c *DockerConfigurator) preProcessImageTags(image string, browserName string, tags []string) (string, []string) {
Expand Down Expand Up @@ -431,7 +476,16 @@ type JSONProgress struct {

func (c *DockerConfigurator) pullImage(ctx context.Context, ref string) bool {
c.Pointf("Pulling image %v", color.BlueString(ref))
resp, err := c.docker.ImagePull(ctx, ref, types.ImagePullOptions{})
pullOptions := types.ImagePullOptions{}
if c.authConfig != nil {
buf, err := json.Marshal(c.authConfig)
if err != nil {
c.Errorf("Failed to prepare registry authentication config: %v", err)
} else {
pullOptions.RegistryAuth = base64.URLEncoding.EncodeToString(buf)
}
}
resp, err := c.docker.ImagePull(ctx, ref, pullOptions)
if err != nil {
c.Errorf(`Failed to pull image "%s": %v`, ref, err)
return false
Expand Down
31 changes: 27 additions & 4 deletions selenoid/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ func mux() http.Handler {
w.WriteHeader(http.StatusOK)
},
))

mux.HandleFunc("/v2/aerokube/selenoid/tags/list", http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
fmt.Fprintln(w, `{"name":"selenoid", "tags": ["1.4.0", "1.4.1"]}`)
},
))

mux.HandleFunc("/v2/aerokube/selenoid-ui/tags/list", http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
fmt.Fprintln(w, `{"name":"selenoid-ui", "tags": ["1.5.2"]}`)
},
))

mux.HandleFunc("/v2/selenoid/firefox/tags/list", http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
Expand Down Expand Up @@ -249,6 +264,7 @@ func testConfigure(t *testing.T, download bool) {
c, err := NewDockerConfigurator(&lcConfig)
AssertThat(t, err, Is{nil})
defer c.Close()
c.registryHostname = ""
AssertThat(t, c.IsConfigured(), Is{false})
cfgPointer, err := (*c).Configure()
AssertThat(t, err, Is{nil})
Expand Down Expand Up @@ -290,12 +306,17 @@ func testConfigure(t *testing.T, download bool) {
AssertThat(t, operaVersions.Default, EqualTo{"44.0"})

correctOperaBrowsers := make(map[string]*config.Browser)
correctOperaBrowsers["2.1.1"] = &config.Browser{
Image: "selenoid/opera:44.0",
correctOperaBrowsers["44.0"] = &config.Browser{
Image: "selenoid/vnc:opera_44.0",
Port: "4444",
Path: "/",
Tmpfs: tmpfsMap,
Env: []string{testEnv},
}
AssertThat(t, operaVersions, EqualTo{config.Versions{
Default: "44.0",
Versions: correctOperaBrowsers,
}})
})
}

Expand Down Expand Up @@ -337,8 +358,9 @@ func TestDownload(t *testing.T) {
Quiet: true,
Version: Latest,
})
AssertThat(t, c.IsDownloaded(), Is{true})
AssertThat(t, err, Is{nil})
c.registryHostname = ""
AssertThat(t, c.IsDownloaded(), Is{true})
ref, err := c.Download()
AssertThat(t, ref, Not{nil})
AssertThat(t, err, Is{nil})
Expand All @@ -354,8 +376,9 @@ func TestDownloadUI(t *testing.T) {
Version: Latest,
})
setImageName(selenoidUIImage)
AssertThat(t, c.IsUIDownloaded(), Is{true})
AssertThat(t, err, Is{nil})
c.registryHostname = ""
AssertThat(t, c.IsUIDownloaded(), Is{true})
ref, err := c.DownloadUI()
AssertThat(t, ref, Not{nil})
AssertThat(t, err, Is{nil})
Expand Down

0 comments on commit 2af7ea1

Please sign in to comment.