From fa4dc60545ebc64d4e9c97b4a2e3fd08a15521fc Mon Sep 17 00:00:00 2001 From: Kyle Vigen Date: Wed, 18 Jun 2014 17:39:33 -0700 Subject: [PATCH 01/14] Add docker.io Drone plugin which creates a docker image for each successful build This change adds a new Drone plugin for creating a docker image. The plugin creates a docker image for the test build and pushes the image to images.docker.io. --- pkg/plugin/publish/docker.go | 65 +++++++++++++++++++++++++++++++++++ pkg/plugin/publish/publish.go | 14 +++++--- 2 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 pkg/plugin/publish/docker.go diff --git a/pkg/plugin/publish/docker.go b/pkg/plugin/publish/docker.go new file mode 100644 index 000000000..b854d181b --- /dev/null +++ b/pkg/plugin/publish/docker.go @@ -0,0 +1,65 @@ +package publish + +import ( + "fmt" + "strconv" + + "github.com/drone/drone/pkg/build/buildfile" + "github.com/drone/drone/pkg/build/repo" +) + +type Docker struct { + // The path to the dockerfile to create the image from + Dockerfile string `yaml:"docker_file"` + + // Connection information for the docker server that will build the image + Server string `yaml:"docker_server"` + Port int `yaml:"docker_port"` + // The Docker client version to download. This must match the docker version on the server + DockerVersion string `yaml:"docker_version"` + + RepoBaseName string `yaml:"repo_base_name"` + + // Authentication credentials for index.docker.io + Username string `yaml:"username"` + Password string `yaml:"password"` + Email string `yaml:"email"` + + Branch string `yaml:"branch,omitempty"` +} + +// Write adds commands to the buildfile to do the following: +// 1. Install the docker client in the Drone container. +// 2. Build a docker image based on the dockerfile defined in the config. +// 3. Push that docker image to index.docker.io. +// 4. Delete the docker image on the server it was build on so we conserve disk space. +func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { + if len(d.Dockerfile) == 0 || len(d.Server) == 0 || d.Port == 0 || len(d.DockerVersion) == 0 || + len(d.RepoBaseName) == 0 || len(d.Username) == 0 || len(d.Password) == 0 || + len(d.Email) == 0 { + f.WriteCmdSilent(`echo "Docker Plugin: Missing argument(s)"`) + return + } + + // Install Docker on the container + f.WriteCmd("sudo sh -c \"echo deb http://get.docker.io/ubuntu docker main\\ > " + + "/etc/apt/sources.list.d/docker.list\"") + f.WriteCmd("sudo apt-get update") + f.WriteCmd("sudo apt-get --yes install lxc-docker-" + d.DockerVersion) + + dockerServerUrl := d.Server + ":" + strconv.Itoa(d.Port) + dockerRepo := d.RepoBaseName + "/" + r.Name + // Run the command commands to build and deploy the image. Note that the image is tagged + // with the git hash. + f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s:$(git rev-parse --short HEAD) - < %s", + dockerServerUrl, dockerRepo, d.Dockerfile)) + + // Login and push to index.docker.io + f.WriteCmd(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s", + dockerServerUrl, d.Username, d.Password, d.Email)) + f.WriteCmd(fmt.Sprintf("docker -H %s push %s", dockerServerUrl, dockerRepo)) + + // Delete the image from the docker server we built on. + f.WriteCmd(fmt.Sprintf("docker -H %s rmi %s:$(git rev-parse --short HEAD)", + dockerServerUrl, dockerRepo)) +} diff --git a/pkg/plugin/publish/publish.go b/pkg/plugin/publish/publish.go index 546be583e..d9bdbf928 100644 --- a/pkg/plugin/publish/publish.go +++ b/pkg/plugin/publish/publish.go @@ -9,10 +9,11 @@ import ( // for publishing build artifacts when // a Build has succeeded type Publish struct { - S3 *S3 `yaml:"s3,omitempty"` - Swift *Swift `yaml:"swift,omitempty"` - PyPI *PyPI `yaml:"pypi,omitempty"` - NPM *NPM `yaml:"npm,omitempty"` + S3 *S3 `yaml:"s3,omitempty"` + Swift *Swift `yaml:"swift,omitempty"` + PyPI *PyPI `yaml:"pypi,omitempty"` + NPM *NPM `yaml:"npm,omitempty"` + Docker *Docker `yaml:"docker,omitempty"` } func (p *Publish) Write(f *buildfile.Buildfile, r *repo.Repo) { @@ -35,4 +36,9 @@ func (p *Publish) Write(f *buildfile.Buildfile, r *repo.Repo) { if p.NPM != nil && (len(p.NPM.Branch) == 0 || (len(p.NPM.Branch) > 0 && r.Branch == p.NPM.Branch)) { p.NPM.Write(f) } + + // Docker + if p.Docker != nil && (len(p.Docker.Branch) == 0 || (len(p.Docker.Branch) > 0 && r.Branch == p.Docker.Branch)) { + p.Docker.Write(f, r) + } } From b1c019b4dd1b69595089a996e61208a16586899d Mon Sep 17 00:00:00 2001 From: Kyle Vigen Date: Fri, 20 Jun 2014 15:47:32 -0700 Subject: [PATCH 02/14] Add tests for docker plugin These tests just check that the docker commands show up in the buildfile --- pkg/plugin/publish/docker_test.go | 75 +++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 pkg/plugin/publish/docker_test.go diff --git a/pkg/plugin/publish/docker_test.go b/pkg/plugin/publish/docker_test.go new file mode 100644 index 000000000..44271f719 --- /dev/null +++ b/pkg/plugin/publish/docker_test.go @@ -0,0 +1,75 @@ +package publish + +import ( + "strings" + "testing" + + "gopkg.in/v1/yaml" + "github.com/drone/drone/pkg/build/buildfile" + "github.com/drone/drone/pkg/build/repo" +) + +type PublishToDrone struct { + Publish *Publish `yaml:"publish,omitempty"` +} + +func setUpWithDrone(input string) (string, error) { + var buildStruct PublishToDrone + err := yaml.Unmarshal([]byte(input), &buildStruct) + if err != nil { + return "", err + } + bf := buildfile.New() + buildStruct.Publish.Write(bf, &repo.Repo{Name: "name"}) + return bf.String(), err +} + +var missingFieldsYaml = ` +publish: + docker: + dockerfile: file +` + +func TestMissingFields(t *testing.T) { + response, err := setUpWithDrone(missingFieldsYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s", err.Error()) + } + if !strings.Contains(response, "echo \"Docker Plugin: Missing argument(s)") { + t.Fatalf("Response: " + response + " didn't contain missing arguments warning") + } +} + +var validYaml = ` +publish: + docker: + docker_file: file_path + docker_server: server + docker_port: 1000 + docker_version: 1.0 + repo_base_name: base_repo + username: user + password: password + email: email +` + +func TestValidYaml(t *testing.T) { + response, err := setUpWithDrone(validYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s", err.Error()) + } + if !strings.Contains(response, "docker -H server:1000 build -t base_repo/name" + + ":$(git rev-parse --short HEAD)") { + t.Fatalf("Response: " + response + "doesn't contain build command") + } + if !strings.Contains(response, "docker -H server:1000 login -u user -p password -e email") { + t.Fatalf("Response: " + response + " doesn't contain login command") + } + if !strings.Contains(response, "docker -H server:1000 push base_repo/name") { + t.Fatalf("Response: " + response + " doesn't contain push command") + } + if !strings.Contains(response, "docker -H server:1000 rmi base_repo/name:" + + "$(git rev-parse --short HEAD)") { + t.Fatalf("Response: " + response + " doesn't contain remove image command") + } +} From c14d65cf3e2bc59956e9cfb723dbe1eb95cabef8 Mon Sep 17 00:00:00 2001 From: Kyle Vigen Date: Mon, 23 Jun 2014 15:54:20 -0700 Subject: [PATCH 03/14] Write login command silently so it doesn't print password --- pkg/plugin/publish/docker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/plugin/publish/docker.go b/pkg/plugin/publish/docker.go index b854d181b..224a18fbb 100644 --- a/pkg/plugin/publish/docker.go +++ b/pkg/plugin/publish/docker.go @@ -55,7 +55,7 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { dockerServerUrl, dockerRepo, d.Dockerfile)) // Login and push to index.docker.io - f.WriteCmd(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s", + f.WriteCmdSilent(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s", dockerServerUrl, d.Username, d.Password, d.Email)) f.WriteCmd(fmt.Sprintf("docker -H %s push %s", dockerServerUrl, dockerRepo)) From e0b2084f4240aacd73ac4cc61c9a22b4f8102c62 Mon Sep 17 00:00:00 2001 From: Kyle Vigen Date: Mon, 23 Jun 2014 13:58:38 -0700 Subject: [PATCH 04/14] Only grab the last part of the docker repo name --- pkg/plugin/publish/docker.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/plugin/publish/docker.go b/pkg/plugin/publish/docker.go index 224a18fbb..e1a2638c1 100644 --- a/pkg/plugin/publish/docker.go +++ b/pkg/plugin/publish/docker.go @@ -3,6 +3,7 @@ package publish import ( "fmt" "strconv" + "strings" "github.com/drone/drone/pkg/build/buildfile" "github.com/drone/drone/pkg/build/repo" @@ -48,7 +49,8 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { f.WriteCmd("sudo apt-get --yes install lxc-docker-" + d.DockerVersion) dockerServerUrl := d.Server + ":" + strconv.Itoa(d.Port) - dockerRepo := d.RepoBaseName + "/" + r.Name + splitRepoName := strings.Split(r.Name, "/") + dockerRepo := d.RepoBaseName + "/" + splitRepoName[len(splitRepoName) - 1] // Run the command commands to build and deploy the image. Note that the image is tagged // with the git hash. f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s:$(git rev-parse --short HEAD) - < %s", From bcd29fc31f41ecb41d39e5b282da1625fac0d7fa Mon Sep 17 00:00:00 2001 From: Kyle Vigen Date: Wed, 25 Jun 2014 14:37:45 -0700 Subject: [PATCH 05/14] Also publish "latest" image --- pkg/plugin/publish/docker.go | 5 +++-- pkg/plugin/publish/docker_test.go | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pkg/plugin/publish/docker.go b/pkg/plugin/publish/docker.go index e1a2638c1..417ac89dd 100644 --- a/pkg/plugin/publish/docker.go +++ b/pkg/plugin/publish/docker.go @@ -51,8 +51,9 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { dockerServerUrl := d.Server + ":" + strconv.Itoa(d.Port) splitRepoName := strings.Split(r.Name, "/") dockerRepo := d.RepoBaseName + "/" + splitRepoName[len(splitRepoName) - 1] - // Run the command commands to build and deploy the image. Note that the image is tagged - // with the git hash. + // Run the command commands to build and deploy the image. Note that we both create a new image + // tagged with the git hash as well as update the "latest" image. + f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s - < %s", dockerServerUrl, dockerRepo, d.Dockerfile)) f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s:$(git rev-parse --short HEAD) - < %s", dockerServerUrl, dockerRepo, d.Dockerfile)) diff --git a/pkg/plugin/publish/docker_test.go b/pkg/plugin/publish/docker_test.go index 44271f719..6e6133114 100644 --- a/pkg/plugin/publish/docker_test.go +++ b/pkg/plugin/publish/docker_test.go @@ -58,9 +58,13 @@ func TestValidYaml(t *testing.T) { if err != nil { t.Fatalf("Can't unmarshal script: %s", err.Error()) } + + if !strings.Contains(response, "docker -H server:1000 build -t base_repo/name - <") { + t.Fatalf("Response: " + response + " doesn't contain build command for latest") + } if !strings.Contains(response, "docker -H server:1000 build -t base_repo/name" + ":$(git rev-parse --short HEAD)") { - t.Fatalf("Response: " + response + "doesn't contain build command") + t.Fatalf("Response: " + response + "doesn't contain build command for commit hash") } if !strings.Contains(response, "docker -H server:1000 login -u user -p password -e email") { t.Fatalf("Response: " + response + " doesn't contain login command") From b009475ea1aa6a988c1b05cb13b5d8ffc56d56cf Mon Sep 17 00:00:00 2001 From: Kyle Vigen Date: Fri, 27 Jun 2014 15:22:45 -0700 Subject: [PATCH 06/14] Allow publishing from base directory Some our our respositories have Dockerfiles with commands like ADD . /path. These don't work if we do - < Dockerfile because the only thing in "." will be the Dockerfile, not the other things we want. This allows us to support that use case. --- pkg/plugin/publish/docker.go | 19 ++++++++++++------- pkg/plugin/publish/docker_test.go | 23 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/pkg/plugin/publish/docker.go b/pkg/plugin/publish/docker.go index 417ac89dd..0dfce6148 100644 --- a/pkg/plugin/publish/docker.go +++ b/pkg/plugin/publish/docker.go @@ -10,7 +10,8 @@ import ( ) type Docker struct { - // The path to the dockerfile to create the image from + // The path to the dockerfile to create the image from. If the path is empty or no + // path is specified then the docker file will be built from the base directory. Dockerfile string `yaml:"docker_file"` // Connection information for the docker server that will build the image @@ -35,9 +36,8 @@ type Docker struct { // 3. Push that docker image to index.docker.io. // 4. Delete the docker image on the server it was build on so we conserve disk space. func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { - if len(d.Dockerfile) == 0 || len(d.Server) == 0 || d.Port == 0 || len(d.DockerVersion) == 0 || - len(d.RepoBaseName) == 0 || len(d.Username) == 0 || len(d.Password) == 0 || - len(d.Email) == 0 { + if len(d.Email) == 0 || len(d.Server) == 0 || d.Port == 0 || len(d.DockerVersion) == 0 || + len(d.RepoBaseName) == 0 || len(d.Username) == 0 || len(d.Password) == 0 { f.WriteCmdSilent(`echo "Docker Plugin: Missing argument(s)"`) return } @@ -51,11 +51,16 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { dockerServerUrl := d.Server + ":" + strconv.Itoa(d.Port) splitRepoName := strings.Split(r.Name, "/") dockerRepo := d.RepoBaseName + "/" + splitRepoName[len(splitRepoName) - 1] + dockerPath := "." + if len(d.Dockerfile) != 0 { + dockerPath = fmt.Sprintf("- < %s", d.Dockerfile) + } + // Run the command commands to build and deploy the image. Note that we both create a new image // tagged with the git hash as well as update the "latest" image. - f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s - < %s", dockerServerUrl, dockerRepo, d.Dockerfile)) - f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s:$(git rev-parse --short HEAD) - < %s", - dockerServerUrl, dockerRepo, d.Dockerfile)) + f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s %s", dockerServerUrl, dockerRepo, dockerPath)) + f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s:$(git rev-parse --short HEAD) %s", + dockerServerUrl, dockerRepo, dockerPath)) // Login and push to index.docker.io f.WriteCmdSilent(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s", diff --git a/pkg/plugin/publish/docker_test.go b/pkg/plugin/publish/docker_test.go index 6e6133114..d319f8821 100644 --- a/pkg/plugin/publish/docker_test.go +++ b/pkg/plugin/publish/docker_test.go @@ -77,3 +77,26 @@ func TestValidYaml(t *testing.T) { t.Fatalf("Response: " + response + " doesn't contain remove image command") } } + +var withoutDockerFileYaml = ` +publish: + docker: + docker_server: server + docker_port: 1000 + docker_version: 1.0 + repo_base_name: base_repo + username: user + password: password + email: email +` + +func TestWithoutDockerFile(t *testing.T) { + response, err := setUpWithDrone(withoutDockerFileYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s", err.Error()) + } + + if !strings.Contains(response, "docker -H server:1000 build -t base_repo/name .") { + t.Fatalf("Response: " + response + " doesn't contain build command") + } +} From 618d197b66fd5077e37a0c909f73969a7d9273ef Mon Sep 17 00:00:00 2001 From: Kyle Vigen Date: Tue, 22 Jul 2014 14:51:37 -0700 Subject: [PATCH 07/14] Tweak command to install docker per upstream comments --- pkg/plugin/publish/docker.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/plugin/publish/docker.go b/pkg/plugin/publish/docker.go index 0dfce6148..573d09b79 100644 --- a/pkg/plugin/publish/docker.go +++ b/pkg/plugin/publish/docker.go @@ -43,14 +43,17 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { } // Install Docker on the container - f.WriteCmd("sudo sh -c \"echo deb http://get.docker.io/ubuntu docker main\\ > " + + f.WriteCmd("sudo sh -c \"echo deb https://get.docker.io/ubuntu docker main\\ > " + "/etc/apt/sources.list.d/docker.list\"") + f.WriteCmd("apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys " + + "36A1D7869245C8950F966E92D8576A8BA88D21E9") f.WriteCmd("sudo apt-get update") f.WriteCmd("sudo apt-get --yes install lxc-docker-" + d.DockerVersion) dockerServerUrl := d.Server + ":" + strconv.Itoa(d.Port) splitRepoName := strings.Split(r.Name, "/") - dockerRepo := d.RepoBaseName + "/" + splitRepoName[len(splitRepoName) - 1] + dockerRepo := d.RepoBaseName + "/" + splitRepoName[len(splitRepoName)-1] + dockerPath := "." if len(d.Dockerfile) != 0 { dockerPath = fmt.Sprintf("- < %s", d.Dockerfile) @@ -67,7 +70,7 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { dockerServerUrl, d.Username, d.Password, d.Email)) f.WriteCmd(fmt.Sprintf("docker -H %s push %s", dockerServerUrl, dockerRepo)) - // Delete the image from the docker server we built on. + // Delete the image from the docker server we built on. f.WriteCmd(fmt.Sprintf("docker -H %s rmi %s:$(git rev-parse --short HEAD)", dockerServerUrl, dockerRepo)) } From 1c83609193f24917e256314a72d41074ec7f04be Mon Sep 17 00:00:00 2001 From: Jonathan Eldridge Date: Mon, 28 Jul 2014 15:57:41 -0700 Subject: [PATCH 08/14] Add image for Go 1.3 --- pkg/build/images.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/build/images.go b/pkg/build/images.go index 5c09820b1..16ef64a53 100644 --- a/pkg/build/images.go +++ b/pkg/build/images.go @@ -197,6 +197,7 @@ var builders = map[string]*image{ "go1": {Tag: "bradrydzewski/go:1.0"}, "go1.1": {Tag: "bradrydzewski/go:1.1"}, "go1.2": {Tag: "bradrydzewski/go:1.2"}, + "go1.3": {Tag: "bradrydzewski/go:1.3"}, // Haskell build images "haskell": {Tag: "bradrydzewski/haskell:7.4"}, From c66b623b77c0b9727932feb06e68a0df77f3d3de Mon Sep 17 00:00:00 2001 From: Jonathan Eldridge Date: Mon, 28 Jul 2014 16:11:32 -0700 Subject: [PATCH 09/14] Updates README to mention go 1.3 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6f9bb2257..1dce5043f 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ docker pull bradrydzewski/gcc:4.8 # image: gcc4.8 docker pull bradrydzewski/go:1.0 # image: go1 docker pull bradrydzewski/go:1.1 # image: go1.1 docker pull bradrydzewski/go:1.2 # image: go1.2 +docker pull bradrydzewski/go:1.3 # image: go1.3 # haskell images docker pull bradrydzewski/haskell:7.4 # image: haskell From ab5b8a7705f70d648357d7b028e1f5eb84583a65 Mon Sep 17 00:00:00 2001 From: Kyle Vigen Date: Wed, 30 Jul 2014 15:52:33 -0700 Subject: [PATCH 10/14] Derp when updating the docker install command --- pkg/plugin/publish/docker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/plugin/publish/docker.go b/pkg/plugin/publish/docker.go index 573d09b79..e1f63be84 100644 --- a/pkg/plugin/publish/docker.go +++ b/pkg/plugin/publish/docker.go @@ -45,7 +45,7 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { // Install Docker on the container f.WriteCmd("sudo sh -c \"echo deb https://get.docker.io/ubuntu docker main\\ > " + "/etc/apt/sources.list.d/docker.list\"") - f.WriteCmd("apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys " + + f.WriteCmd("sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys " + "36A1D7869245C8950F966E92D8576A8BA88D21E9") f.WriteCmd("sudo apt-get update") f.WriteCmd("sudo apt-get --yes install lxc-docker-" + d.DockerVersion) From 8ac2ca6f42f00e3696f7a56c3c84ec28236b8a9e Mon Sep 17 00:00:00 2001 From: David Dyball Date: Thu, 7 Aug 2014 23:50:51 +0100 Subject: [PATCH 11/14] Private Repository Support --- pkg/plugin/publish/docker.go | 111 +++++++++++++++---- pkg/plugin/publish/docker_test.go | 176 +++++++++++++++++++++++++++--- 2 files changed, 250 insertions(+), 37 deletions(-) diff --git a/pkg/plugin/publish/docker.go b/pkg/plugin/publish/docker.go index e1f63be84..46bb01a98 100644 --- a/pkg/plugin/publish/docker.go +++ b/pkg/plugin/publish/docker.go @@ -15,19 +15,33 @@ type Docker struct { Dockerfile string `yaml:"docker_file"` // Connection information for the docker server that will build the image - Server string `yaml:"docker_server"` - Port int `yaml:"docker_port"` + DockerServer string `yaml:"docker_server"` + DockerServerPort int `yaml:"docker_port"` // The Docker client version to download. This must match the docker version on the server DockerVersion string `yaml:"docker_version"` - RepoBaseName string `yaml:"repo_base_name"` + // Optional Arguments to allow finer-grained control of registry + // endpoints + RegistryHost string `yaml:"registry_host"` + RegistryProtocol string `yaml:"registry_protocol"` + RegistryPort int `yaml:"registry_port"` + RegistryLogin bool `yaml:"registry_login"` + RegistryLoginUri string `yaml:"registry_login_uri"` + + ImageName string `yaml:"image_name"` // Authentication credentials for index.docker.io Username string `yaml:"username"` Password string `yaml:"password"` Email string `yaml:"email"` + // Keep the build on the Docker host after pushing? + KeepBuild bool `yaml:"keep_build"` + // Do we want to override "latest" automatically with this build? + PushLatest bool `yaml:"push_latest"` + Branch string `yaml:"branch,omitempty"` + Tag string `yaml:"custom_tag"` } // Write adds commands to the buildfile to do the following: @@ -36,12 +50,15 @@ type Docker struct { // 3. Push that docker image to index.docker.io. // 4. Delete the docker image on the server it was build on so we conserve disk space. func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { - if len(d.Email) == 0 || len(d.Server) == 0 || d.Port == 0 || len(d.DockerVersion) == 0 || - len(d.RepoBaseName) == 0 || len(d.Username) == 0 || len(d.Password) == 0 { + if len(d.DockerServer) == 0 || d.DockerServerPort == 0 || len(d.DockerVersion) == 0 || + len(d.ImageName) == 0 { f.WriteCmdSilent(`echo "Docker Plugin: Missing argument(s)"`) return } + // Ensure correct apt-get has the https method-driver as per (http://askubuntu.com/questions/165676/) + f.WriteCmd("sudo apt-get install apt-transport-https") + // Install Docker on the container f.WriteCmd("sudo sh -c \"echo deb https://get.docker.io/ubuntu docker main\\ > " + "/etc/apt/sources.list.d/docker.list\"") @@ -50,27 +67,83 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { f.WriteCmd("sudo apt-get update") f.WriteCmd("sudo apt-get --yes install lxc-docker-" + d.DockerVersion) - dockerServerUrl := d.Server + ":" + strconv.Itoa(d.Port) - splitRepoName := strings.Split(r.Name, "/") - dockerRepo := d.RepoBaseName + "/" + splitRepoName[len(splitRepoName)-1] + // Format our Build Server Endpoint + dockerServerUrl := d.DockerServer + ":" + strconv.Itoa(d.DockerServerPort) + + // Construct Image BaseName + // e.g. "docker.mycompany.com/myimage" for private registries + // "myuser/myimage" for index.docker.io + imageBaseName := "" + if len(d.RegistryHost) > 0 { + imageBaseName = fmt.Sprintf("%s/%s",d.RegistryHost,d.ImageName) + } else { + imageBaseName = fmt.Sprintf("%s/%s",d.Username,d.ImageName) + } + + registryLoginEndpoint := "" + + // Gather information to build our Registry Endpoint for private registries + if len(d.RegistryHost) > 0 { + // Set Protocol + if len(d.RegistryProtocol) > 0 { + registryLoginEndpoint = fmt.Sprintf("%s://%s", d.RegistryProtocol,d.RegistryHost) + } else { + registryLoginEndpoint = fmt.Sprintf("http://%s", d.RegistryHost) + } + // Set Port + if d.RegistryPort > 0 { + registryLoginEndpoint = fmt.Sprintf("%s:%d",registryLoginEndpoint,d.RegistryPort) + } + // Set Login URI + if len(d.RegistryLoginUri) > 0 { + registryLoginEndpoint = fmt.Sprintf("%s/%s",registryLoginEndpoint,strings.TrimPrefix(d.RegistryLoginUri,"/")) + } else { + registryLoginEndpoint = fmt.Sprintf("%s/v1/",registryLoginEndpoint) + } + } + + //splitRepoName := strings.Split(r.Name, "/") + //dockerRepo := d.ImageName + "/" + splitRepoName[len(splitRepoName)-1] dockerPath := "." if len(d.Dockerfile) != 0 { dockerPath = fmt.Sprintf("- < %s", d.Dockerfile) } - // Run the command commands to build and deploy the image. Note that we both create a new image - // tagged with the git hash as well as update the "latest" image. - f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s %s", dockerServerUrl, dockerRepo, dockerPath)) - f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s:$(git rev-parse --short HEAD) %s", - dockerServerUrl, dockerRepo, dockerPath)) + // Run the command commands to build and deploy the image. + // Are we setting a custom tag, or do we use the git hash? + imageTag := "" + if len(d.Tag) > 0 { + imageTag = d.Tag + } else { + imageTag = "$(git rev-parse --short HEAD)" + } + f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s:%s %s", dockerServerUrl, imageBaseName, imageTag, dockerPath)) - // Login and push to index.docker.io - f.WriteCmdSilent(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s", - dockerServerUrl, d.Username, d.Password, d.Email)) - f.WriteCmd(fmt.Sprintf("docker -H %s push %s", dockerServerUrl, dockerRepo)) + // Login? + if len(d.RegistryHost) > 0 && d.RegistryLogin == true { + f.WriteCmdSilent(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s %s", + dockerServerUrl, d.Username, d.Password, d.Email, registryLoginEndpoint)) + } else if len(d.RegistryHost) == 0 { + // Assume that because no private registry is specified it requires auth + // for index.docker.io + f.WriteCmdSilent(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s", + dockerServerUrl, d.Username, d.Password, d.Email)) + } + + // Are we overriding the "latest" tag? + if d.PushLatest { + f.WriteCmd(fmt.Sprintf("docker -H %s tag %s:%s %s:latest", + dockerServerUrl, imageBaseName, imageTag, imageBaseName)) + } + + f.WriteCmd(fmt.Sprintf("docker -H %s push %s", dockerServerUrl, imageBaseName)) // Delete the image from the docker server we built on. - f.WriteCmd(fmt.Sprintf("docker -H %s rmi %s:$(git rev-parse --short HEAD)", - dockerServerUrl, dockerRepo)) + if ! d.KeepBuild { + f.WriteCmd(fmt.Sprintf("docker -H %s rmi %s:%s", + dockerServerUrl, imageBaseName, imageTag)) + f.WriteCmd(fmt.Sprintf("docker -H %s rmi %s:latest", + dockerServerUrl, imageBaseName)) + } } diff --git a/pkg/plugin/publish/docker_test.go b/pkg/plugin/publish/docker_test.go index d319f8821..c18495aa7 100644 --- a/pkg/plugin/publish/docker_test.go +++ b/pkg/plugin/publish/docker_test.go @@ -24,6 +24,143 @@ func setUpWithDrone(input string) (string, error) { return bf.String(), err } + +// Private Registry Test (no auth) +var privateRegistryNoAuthYaml = ` +publish: + docker: + dockerfile: file_path + docker_server: server + docker_port: 1000 + docker_version: 1.0 + registry_host: registry + registry_login: false + image_name: image +` +func TestPrivateRegistryNoAuth(t *testing.T) { + response, err := setUpWithDrone(privateRegistryNoAuthYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) + } + if !strings.Contains(response, "docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD)") { + t.Fatalf("Response: " + response + " doesn't contain registry in image-names: expected registry/image\n\n") + } +} + +// Private Registry Test (with auth) +var privateRegistryAuthYaml = ` +publish: + docker: + dockerfile: file_path + docker_server: server + docker_port: 1000 + docker_version: 1.0 + registry_host: registry + registry_protocol: https + registry_port: 8000 + registry_login: true + username: username + password: password + email: email@example.com + image_name: image +` +func TestPrivateRegistryAuth(t *testing.T) { + response, err := setUpWithDrone(privateRegistryAuthYaml) + t.Log(privateRegistryAuthYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) + } + if !strings.Contains(response, "docker -H server:1000 login -u username -p password -e email@example.com https://registry:8000/v1/") { + t.Log("\n\n\n\ndocker -H server:1000 login -u username -p xxxxxxxx -e email@example.com https://registry:8000/v1/\n\n\n\n") + t.Fatalf("Response: " + response + " doesn't contain private registry login\n\n") + } + if !strings.Contains(response, "docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD) .") { + t.Log("docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD) .") + t.Fatalf("Response: " + response + " doesn't contain registry in image-names\n\n") + } +} + +// Override "latest" Test +var overrideLatestTagYaml = ` +publish: + docker: + docker_server: server + docker_port: 1000 + docker_version: 1.0 + username: username + password: password + email: email@example.com + image_name: image + push_latest: true +` +func TestOverrideLatestTag(t *testing.T) { + response, err := setUpWithDrone(overrideLatestTagYaml) + t.Log(overrideLatestTagYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) + } + if !strings.Contains(response, "docker -H server:1000 build -t username/image:$(git rev-parse --short HEAD) .") { + t.Fatalf("Response: " + response + " doesn't contain the git-ref tagged image\n\n") + } + if !strings.Contains(response, "docker -H server:1000 tag username/image:$(git rev-parse --short HEAD) username/image:latest") { + t.Fatalf("Response: " + response + " doesn't contain 'latest' tag command\n\n") + } +} + +// Keep builds Test +var keepBuildsYaml = ` +publish: + docker: + docker_server: server + docker_port: 1000 + docker_version: 1.0 + keep_build: true + username: username + password: password + email: email@example.com + image_name: image +` +func TestKeepBuilds(t *testing.T) { + response, err := setUpWithDrone(keepBuildsYaml) + t.Log(keepBuildsYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) + } + if strings.Contains(response, "docker -H server:1000 rmi") { + t.Fatalf("Response: " + response + " incorrectly instructs the docker server to remove the builds when it shouldn't\n\n") + } +} + +// Custom Tag test +var customTagYaml = ` +publish: + docker: + docker_server: server + docker_port: 1000 + docker_version: 1.0 + custom_tag: release-0.1 + username: username + password: password + email: email@example.com + image_name: image +` +func TestCustomTag(t *testing.T) { + response, err := setUpWithDrone(customTagYaml) + t.Log(customTagYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s\n", err.Error()) + } + if strings.Contains(response, "$(git rev-parse --short HEAD)") { + t.Fatalf("Response: " + response + " is tagging images from git-refs when it should use a custom tag\n\n") + } + if !strings.Contains(response, "docker -H server:1000 build -t username/image:release-0.1") { + t.Fatalf("Response: " + response + " isn't tagging images using our custom tag\n\n") + } + if !strings.Contains(response, "docker -H server:1000 push username/image"){ + t.Fatalf("Response: " + response + " doesn't push the custom tagged image\n\n") + } +} + var missingFieldsYaml = ` publish: docker: @@ -32,11 +169,12 @@ publish: func TestMissingFields(t *testing.T) { response, err := setUpWithDrone(missingFieldsYaml) + t.Log(missingFieldsYaml) if err != nil { - t.Fatalf("Can't unmarshal script: %s", err.Error()) + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) } - if !strings.Contains(response, "echo \"Docker Plugin: Missing argument(s)") { - t.Fatalf("Response: " + response + " didn't contain missing arguments warning") + if !strings.Contains(response, "echo \"Docker Plugin: Missing argument(s)\"") { + t.Fatalf("Response: " + response + " didn't contain missing arguments warning\n\n") } } @@ -51,30 +189,32 @@ publish: username: user password: password email: email + image_name: image + push_latest: true ` func TestValidYaml(t *testing.T) { response, err := setUpWithDrone(validYaml) + t.Log(validYaml) if err != nil { - t.Fatalf("Can't unmarshal script: %s", err.Error()) + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) } - if !strings.Contains(response, "docker -H server:1000 build -t base_repo/name - <") { - t.Fatalf("Response: " + response + " doesn't contain build command for latest") + if !strings.Contains(response, "docker -H server:1000 tag user/image:$(git rev-parse --short HEAD) user/image:latest") { + t.Fatalf("Response: " + response + " doesn't contain tag command for latest\n\n") } - if !strings.Contains(response, "docker -H server:1000 build -t base_repo/name" + - ":$(git rev-parse --short HEAD)") { - t.Fatalf("Response: " + response + "doesn't contain build command for commit hash") + if !strings.Contains(response, "docker -H server:1000 build -t user/image:$(git rev-parse --short HEAD) - <") { + t.Fatalf("Response: " + response + "doesn't contain build command for commit hash\n\n") } if !strings.Contains(response, "docker -H server:1000 login -u user -p password -e email") { - t.Fatalf("Response: " + response + " doesn't contain login command") + t.Fatalf("Response: " + response + " doesn't contain login command\n\n") } - if !strings.Contains(response, "docker -H server:1000 push base_repo/name") { - t.Fatalf("Response: " + response + " doesn't contain push command") + if !strings.Contains(response, "docker -H server:1000 push user/image") { + t.Fatalf("Response: " + response + " doesn't contain push command\n\n") } - if !strings.Contains(response, "docker -H server:1000 rmi base_repo/name:" + + if !strings.Contains(response, "docker -H server:1000 rmi user/image:" + "$(git rev-parse --short HEAD)") { - t.Fatalf("Response: " + response + " doesn't contain remove image command") + t.Fatalf("Response: " + response + " doesn't contain remove image command\n\n") } } @@ -84,7 +224,7 @@ publish: docker_server: server docker_port: 1000 docker_version: 1.0 - repo_base_name: base_repo + image_name: image username: user password: password email: email @@ -93,10 +233,10 @@ publish: func TestWithoutDockerFile(t *testing.T) { response, err := setUpWithDrone(withoutDockerFileYaml) if err != nil { - t.Fatalf("Can't unmarshal script: %s", err.Error()) + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) } - if !strings.Contains(response, "docker -H server:1000 build -t base_repo/name .") { - t.Fatalf("Response: " + response + " doesn't contain build command") + if !strings.Contains(response, "docker -H server:1000 build -t user/image:$(git rev-parse --short HEAD) .") { + t.Fatalf("Response: " + response + " doesn't contain build command\n\n") } } From 39695c45bd9893ef14d558eb7d1c1b8e817bddcf Mon Sep 17 00:00:00 2001 From: David Dyball Date: Fri, 8 Aug 2014 09:15:32 +0100 Subject: [PATCH 12/14] Fixing indents, adding repo_name option and additional tests --- pkg/plugin/publish/docker.go | 45 +++++--- pkg/plugin/publish/docker_test.go | 169 +++++++++++++++++------------- 2 files changed, 126 insertions(+), 88 deletions(-) diff --git a/pkg/plugin/publish/docker.go b/pkg/plugin/publish/docker.go index 46bb01a98..718a495af 100644 --- a/pkg/plugin/publish/docker.go +++ b/pkg/plugin/publish/docker.go @@ -16,7 +16,7 @@ type Docker struct { // Connection information for the docker server that will build the image DockerServer string `yaml:"docker_server"` - DockerServerPort int `yaml:"docker_port"` + DockerServerPort int `yaml:"docker_port"` // The Docker client version to download. This must match the docker version on the server DockerVersion string `yaml:"docker_version"` @@ -25,20 +25,23 @@ type Docker struct { RegistryHost string `yaml:"registry_host"` RegistryProtocol string `yaml:"registry_protocol"` RegistryPort int `yaml:"registry_port"` - RegistryLogin bool `yaml:"registry_login"` + RegistryLogin bool `yaml:"registry_login"` RegistryLoginUri string `yaml:"registry_login_uri"` + // Allow setting Repo + Image names for delivery + // NOTE: RepoName is not compatible with private Registries + RepoName string `yaml:"repo_name"` ImageName string `yaml:"image_name"` // Authentication credentials for index.docker.io Username string `yaml:"username"` Password string `yaml:"password"` - Email string `yaml:"email"` + Email string `yaml:"email"` // Keep the build on the Docker host after pushing? KeepBuild bool `yaml:"keep_build"` - // Do we want to override "latest" automatically with this build? - PushLatest bool `yaml:"push_latest"` + // Do we want to override "latest" automatically with this build? + PushLatest bool `yaml:"push_latest"` Branch string `yaml:"branch,omitempty"` Tag string `yaml:"custom_tag"` @@ -52,12 +55,21 @@ type Docker struct { func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { if len(d.DockerServer) == 0 || d.DockerServerPort == 0 || len(d.DockerVersion) == 0 || len(d.ImageName) == 0 { - f.WriteCmdSilent(`echo "Docker Plugin: Missing argument(s)"`) + f.WriteCmdSilent(`echo -e "Docker Plugin: Missing argument(s)"\n\n`) + if len(d.DockerServer) == 0 { f.WriteCmdSilent(`echo -e "\tdocker_server not defined in yaml`) } + if d.DockerServerPort == 0 { f.WriteCmdSilent(`echo -e "\tdocker_port not defined in yaml`) } + if len(d.DockerVersion) == 0 { f.WriteCmdSilent(`echo -e "\tdocker_version not defined in yaml`) } + if len(d.ImageName) == 0 { f.WriteCmdSilent(`echo -e "\timage_name not defined in yaml`) } return } - // Ensure correct apt-get has the https method-driver as per (http://askubuntu.com/questions/165676/) - f.WriteCmd("sudo apt-get install apt-transport-https") + if len(d.RepoName) > 0 && len(d.RegistryHost) > 0 { + f.WriteCmdSilent(`echo -e "Docker Plugin: Invalid Arguments Specified\n\n cannot combine repo_name and registry_host\n\t(It's not possible to host sub-repo's on private registries)\n"`) + return + } + + // Ensure correct apt-get has the https method-driver as per (http://askubuntu.com/questions/165676/) + f.WriteCmd("sudo apt-get install apt-transport-https") // Install Docker on the container f.WriteCmd("sudo sh -c \"echo deb https://get.docker.io/ubuntu docker main\\ > " + @@ -72,12 +84,16 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { // Construct Image BaseName // e.g. "docker.mycompany.com/myimage" for private registries - // "myuser/myimage" for index.docker.io + // "myuser/myimage" for index.docker.io imageBaseName := "" if len(d.RegistryHost) > 0 { imageBaseName = fmt.Sprintf("%s/%s",d.RegistryHost,d.ImageName) } else { - imageBaseName = fmt.Sprintf("%s/%s",d.Username,d.ImageName) + if len(d.RepoName) > 0 { + imageBaseName = fmt.Sprintf("%s/%s",d.RepoName,d.ImageName) + } else { + imageBaseName = fmt.Sprintf("%s/%s",d.Username,d.ImageName) + } } registryLoginEndpoint := "" @@ -102,9 +118,6 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { } } - //splitRepoName := strings.Split(r.Name, "/") - //dockerRepo := d.ImageName + "/" + splitRepoName[len(splitRepoName)-1] - dockerPath := "." if len(d.Dockerfile) != 0 { dockerPath = fmt.Sprintf("- < %s", d.Dockerfile) @@ -131,11 +144,11 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { dockerServerUrl, d.Username, d.Password, d.Email)) } - // Are we overriding the "latest" tag? - if d.PushLatest { + // Are we overriding the "latest" tag? + if d.PushLatest { f.WriteCmd(fmt.Sprintf("docker -H %s tag %s:%s %s:latest", dockerServerUrl, imageBaseName, imageTag, imageBaseName)) - } + } f.WriteCmd(fmt.Sprintf("docker -H %s push %s", dockerServerUrl, imageBaseName)) diff --git a/pkg/plugin/publish/docker_test.go b/pkg/plugin/publish/docker_test.go index c18495aa7..f13c9bcf8 100644 --- a/pkg/plugin/publish/docker_test.go +++ b/pkg/plugin/publish/docker_test.go @@ -1,29 +1,52 @@ package publish import ( - "strings" - "testing" + "strings" + "testing" - "gopkg.in/v1/yaml" - "github.com/drone/drone/pkg/build/buildfile" - "github.com/drone/drone/pkg/build/repo" + "gopkg.in/v1/yaml" + "github.com/drone/drone/pkg/build/buildfile" + "github.com/drone/drone/pkg/build/repo" ) type PublishToDrone struct { - Publish *Publish `yaml:"publish,omitempty"` + Publish *Publish `yaml:"publish,omitempty"` } func setUpWithDrone(input string) (string, error) { - var buildStruct PublishToDrone - err := yaml.Unmarshal([]byte(input), &buildStruct) - if err != nil { - return "", err - } - bf := buildfile.New() - buildStruct.Publish.Write(bf, &repo.Repo{Name: "name"}) - return bf.String(), err + var buildStruct PublishToDrone + err := yaml.Unmarshal([]byte(input), &buildStruct) + if err != nil { + return "", err + } + bf := buildfile.New() + buildStruct.Publish.Write(bf, &repo.Repo{Name: "name"}) + return bf.String(), err } +// Private Registry + RepoName (invalid config) +var privateRegistryRepoNameYaml = ` +publish: + docker: + docker_server: server + docker_port: 1000 + docker_version: 1.0 + registry_host: server + registry_login: false + repo_name: company + image_name: image +` + +func TestPrivateRegistryRepoName(t *testing.T) { + response, err := setUpWithDrone(privateRegistryRepoNameYaml) + t.Log(privateRegistryRepoNameYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) + } + if !strings.Contains(response, "Docker Plugin: Invalid Arguments Specified") { + t.Fatalf("registry_host + repo_name should produce an invalid config error, it didn't") + } +} // Private Registry Test (no auth) var privateRegistryNoAuthYaml = ` @@ -39,12 +62,13 @@ publish: ` func TestPrivateRegistryNoAuth(t *testing.T) { response, err := setUpWithDrone(privateRegistryNoAuthYaml) - if err != nil { - t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) - } - if !strings.Contains(response, "docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD)") { - t.Fatalf("Response: " + response + " doesn't contain registry in image-names: expected registry/image\n\n") - } + t.Log(privateRegistryNoAuthYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) + } + if !strings.Contains(response, "docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD)") { + t.Fatalf("Response: " + response + " doesn't contain registry in image-names: expected registry/image\n\n") + } } // Private Registry Test (with auth) @@ -66,18 +90,18 @@ publish: ` func TestPrivateRegistryAuth(t *testing.T) { response, err := setUpWithDrone(privateRegistryAuthYaml) - t.Log(privateRegistryAuthYaml) - if err != nil { - t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) - } - if !strings.Contains(response, "docker -H server:1000 login -u username -p password -e email@example.com https://registry:8000/v1/") { - t.Log("\n\n\n\ndocker -H server:1000 login -u username -p xxxxxxxx -e email@example.com https://registry:8000/v1/\n\n\n\n") + t.Log(privateRegistryAuthYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) + } + if !strings.Contains(response, "docker -H server:1000 login -u username -p password -e email@example.com https://registry:8000/v1/") { + t.Log("\n\n\n\ndocker -H server:1000 login -u username -p xxxxxxxx -e email@example.com https://registry:8000/v1/\n\n\n\n") t.Fatalf("Response: " + response + " doesn't contain private registry login\n\n") } - if !strings.Contains(response, "docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD) .") { - t.Log("docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD) .") - t.Fatalf("Response: " + response + " doesn't contain registry in image-names\n\n") - } + if !strings.Contains(response, "docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD) .") { + t.Log("docker -H server:1000 build -t registry/image:$(git rev-parse --short HEAD) .") + t.Fatalf("Response: " + response + " doesn't contain registry in image-names\n\n") + } } // Override "latest" Test @@ -95,16 +119,16 @@ publish: ` func TestOverrideLatestTag(t *testing.T) { response, err := setUpWithDrone(overrideLatestTagYaml) - t.Log(overrideLatestTagYaml) - if err != nil { - t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) - } - if !strings.Contains(response, "docker -H server:1000 build -t username/image:$(git rev-parse --short HEAD) .") { - t.Fatalf("Response: " + response + " doesn't contain the git-ref tagged image\n\n") - } - if !strings.Contains(response, "docker -H server:1000 tag username/image:$(git rev-parse --short HEAD) username/image:latest") { - t.Fatalf("Response: " + response + " doesn't contain 'latest' tag command\n\n") - } + t.Log(overrideLatestTagYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) + } + if !strings.Contains(response, "docker -H server:1000 build -t username/image:$(git rev-parse --short HEAD) .") { + t.Fatalf("Response: " + response + " doesn't contain the git-ref tagged image\n\n") + } + if !strings.Contains(response, "docker -H server:1000 tag username/image:$(git rev-parse --short HEAD) username/image:latest") { + t.Fatalf("Response: " + response + " doesn't contain 'latest' tag command\n\n") + } } // Keep builds Test @@ -122,7 +146,7 @@ publish: ` func TestKeepBuilds(t *testing.T) { response, err := setUpWithDrone(keepBuildsYaml) - t.Log(keepBuildsYaml) + t.Log(keepBuildsYaml) if err != nil { t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) } @@ -146,8 +170,8 @@ publish: ` func TestCustomTag(t *testing.T) { response, err := setUpWithDrone(customTagYaml) - t.Log(customTagYaml) - if err != nil { + t.Log(customTagYaml) + if err != nil { t.Fatalf("Can't unmarshal script: %s\n", err.Error()) } if strings.Contains(response, "$(git rev-parse --short HEAD)") { @@ -168,14 +192,14 @@ publish: ` func TestMissingFields(t *testing.T) { - response, err := setUpWithDrone(missingFieldsYaml) - t.Log(missingFieldsYaml) - if err != nil { - t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) - } - if !strings.Contains(response, "echo \"Docker Plugin: Missing argument(s)\"") { - t.Fatalf("Response: " + response + " didn't contain missing arguments warning\n\n") - } + response, err := setUpWithDrone(missingFieldsYaml) + t.Log(missingFieldsYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) + } + if !strings.Contains(response, "Missing argument(s)") { + t.Fatalf("Response: " + response + " didn't contain missing arguments warning\n\n") + } } var validYaml = ` @@ -194,28 +218,28 @@ publish: ` func TestValidYaml(t *testing.T) { - response, err := setUpWithDrone(validYaml) - t.Log(validYaml) - if err != nil { - t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) - } + response, err := setUpWithDrone(validYaml) + t.Log(validYaml) + if err != nil { + t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) + } - if !strings.Contains(response, "docker -H server:1000 tag user/image:$(git rev-parse --short HEAD) user/image:latest") { - t.Fatalf("Response: " + response + " doesn't contain tag command for latest\n\n") - } - if !strings.Contains(response, "docker -H server:1000 build -t user/image:$(git rev-parse --short HEAD) - <") { - t.Fatalf("Response: " + response + "doesn't contain build command for commit hash\n\n") - } - if !strings.Contains(response, "docker -H server:1000 login -u user -p password -e email") { - t.Fatalf("Response: " + response + " doesn't contain login command\n\n") - } - if !strings.Contains(response, "docker -H server:1000 push user/image") { - t.Fatalf("Response: " + response + " doesn't contain push command\n\n") - } - if !strings.Contains(response, "docker -H server:1000 rmi user/image:" + - "$(git rev-parse --short HEAD)") { - t.Fatalf("Response: " + response + " doesn't contain remove image command\n\n") - } + if !strings.Contains(response, "docker -H server:1000 tag user/image:$(git rev-parse --short HEAD) user/image:latest") { + t.Fatalf("Response: " + response + " doesn't contain tag command for latest\n\n") + } + if !strings.Contains(response, "docker -H server:1000 build -t user/image:$(git rev-parse --short HEAD) - <") { + t.Fatalf("Response: " + response + "doesn't contain build command for commit hash\n\n") + } + if !strings.Contains(response, "docker -H server:1000 login -u user -p password -e email") { + t.Fatalf("Response: " + response + " doesn't contain login command\n\n") + } + if !strings.Contains(response, "docker -H server:1000 push user/image") { + t.Fatalf("Response: " + response + " doesn't contain push command\n\n") + } + if !strings.Contains(response, "docker -H server:1000 rmi user/image:" + + "$(git rev-parse --short HEAD)") { + t.Fatalf("Response: " + response + " doesn't contain remove image command\n\n") + } } var withoutDockerFileYaml = ` @@ -232,6 +256,7 @@ publish: func TestWithoutDockerFile(t *testing.T) { response, err := setUpWithDrone(withoutDockerFileYaml) + t.Log(withoutDockerFileYaml) if err != nil { t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) } From cf198247275299c6011d0d29c87c583ad91abba7 Mon Sep 17 00:00:00 2001 From: David Dyball Date: Sat, 9 Aug 2014 02:16:40 +0100 Subject: [PATCH 13/14] Simplified endpoint login behaviour --- pkg/plugin/publish/docker.go | 94 ++++++++----------------------- pkg/plugin/publish/docker_test.go | 43 +++----------- 2 files changed, 32 insertions(+), 105 deletions(-) diff --git a/pkg/plugin/publish/docker.go b/pkg/plugin/publish/docker.go index 718a495af..885787a76 100644 --- a/pkg/plugin/publish/docker.go +++ b/pkg/plugin/publish/docker.go @@ -3,7 +3,6 @@ package publish import ( "fmt" "strconv" - "strings" "github.com/drone/drone/pkg/build/buildfile" "github.com/drone/drone/pkg/build/repo" @@ -22,16 +21,9 @@ type Docker struct { // Optional Arguments to allow finer-grained control of registry // endpoints - RegistryHost string `yaml:"registry_host"` - RegistryProtocol string `yaml:"registry_protocol"` - RegistryPort int `yaml:"registry_port"` - RegistryLogin bool `yaml:"registry_login"` - RegistryLoginUri string `yaml:"registry_login_uri"` - - // Allow setting Repo + Image names for delivery - // NOTE: RepoName is not compatible with private Registries - RepoName string `yaml:"repo_name"` + RegistryLoginUrl string `yaml:"registry_login_url"` ImageName string `yaml:"image_name"` + RegistryLogin bool `yaml:"registry_login"` // Authentication credentials for index.docker.io Username string `yaml:"username"` @@ -42,9 +34,8 @@ type Docker struct { KeepBuild bool `yaml:"keep_build"` // Do we want to override "latest" automatically with this build? PushLatest bool `yaml:"push_latest"` - - Branch string `yaml:"branch,omitempty"` - Tag string `yaml:"custom_tag"` + CustomTag string `yaml:"custom_tag"` + Branch string `yaml:"branch"` } // Write adds commands to the buildfile to do the following: @@ -63,11 +54,6 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { return } - if len(d.RepoName) > 0 && len(d.RegistryHost) > 0 { - f.WriteCmdSilent(`echo -e "Docker Plugin: Invalid Arguments Specified\n\n cannot combine repo_name and registry_host\n\t(It's not possible to host sub-repo's on private registries)\n"`) - return - } - // Ensure correct apt-get has the https method-driver as per (http://askubuntu.com/questions/165676/) f.WriteCmd("sudo apt-get install apt-transport-https") @@ -82,42 +68,6 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { // Format our Build Server Endpoint dockerServerUrl := d.DockerServer + ":" + strconv.Itoa(d.DockerServerPort) - // Construct Image BaseName - // e.g. "docker.mycompany.com/myimage" for private registries - // "myuser/myimage" for index.docker.io - imageBaseName := "" - if len(d.RegistryHost) > 0 { - imageBaseName = fmt.Sprintf("%s/%s",d.RegistryHost,d.ImageName) - } else { - if len(d.RepoName) > 0 { - imageBaseName = fmt.Sprintf("%s/%s",d.RepoName,d.ImageName) - } else { - imageBaseName = fmt.Sprintf("%s/%s",d.Username,d.ImageName) - } - } - - registryLoginEndpoint := "" - - // Gather information to build our Registry Endpoint for private registries - if len(d.RegistryHost) > 0 { - // Set Protocol - if len(d.RegistryProtocol) > 0 { - registryLoginEndpoint = fmt.Sprintf("%s://%s", d.RegistryProtocol,d.RegistryHost) - } else { - registryLoginEndpoint = fmt.Sprintf("http://%s", d.RegistryHost) - } - // Set Port - if d.RegistryPort > 0 { - registryLoginEndpoint = fmt.Sprintf("%s:%d",registryLoginEndpoint,d.RegistryPort) - } - // Set Login URI - if len(d.RegistryLoginUri) > 0 { - registryLoginEndpoint = fmt.Sprintf("%s/%s",registryLoginEndpoint,strings.TrimPrefix(d.RegistryLoginUri,"/")) - } else { - registryLoginEndpoint = fmt.Sprintf("%s/v1/",registryLoginEndpoint) - } - } - dockerPath := "." if len(d.Dockerfile) != 0 { dockerPath = fmt.Sprintf("- < %s", d.Dockerfile) @@ -126,37 +76,41 @@ func (d *Docker) Write(f *buildfile.Buildfile, r *repo.Repo) { // Run the command commands to build and deploy the image. // Are we setting a custom tag, or do we use the git hash? imageTag := "" - if len(d.Tag) > 0 { - imageTag = d.Tag + if len(d.CustomTag) > 0 { + imageTag = d.CustomTag } else { imageTag = "$(git rev-parse --short HEAD)" } - f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s:%s %s", dockerServerUrl, imageBaseName, imageTag, dockerPath)) + f.WriteCmd(fmt.Sprintf("docker -H %s build -t %s:%s %s", dockerServerUrl, d.ImageName, imageTag, dockerPath)) // Login? - if len(d.RegistryHost) > 0 && d.RegistryLogin == true { - f.WriteCmdSilent(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s %s", - dockerServerUrl, d.Username, d.Password, d.Email, registryLoginEndpoint)) - } else if len(d.RegistryHost) == 0 { - // Assume that because no private registry is specified it requires auth - // for index.docker.io - f.WriteCmdSilent(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s", - dockerServerUrl, d.Username, d.Password, d.Email)) + if d.RegistryLogin == true { + // Are we logging in to a custom Registry? + if len(d.RegistryLoginUrl) > 0 { + f.WriteCmdSilent(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s %s", + dockerServerUrl, d.Username, d.Password, d.Email, d.RegistryLoginUrl)) + } else { + // Assume index.docker.io + f.WriteCmdSilent(fmt.Sprintf("docker -H %s login -u %s -p %s -e %s", + dockerServerUrl, d.Username, d.Password, d.Email)) + } } // Are we overriding the "latest" tag? if d.PushLatest { f.WriteCmd(fmt.Sprintf("docker -H %s tag %s:%s %s:latest", - dockerServerUrl, imageBaseName, imageTag, imageBaseName)) + dockerServerUrl, d.ImageName, imageTag, d.ImageName)) } - f.WriteCmd(fmt.Sprintf("docker -H %s push %s", dockerServerUrl, imageBaseName)) + f.WriteCmd(fmt.Sprintf("docker -H %s push %s", dockerServerUrl, d.ImageName)) // Delete the image from the docker server we built on. if ! d.KeepBuild { f.WriteCmd(fmt.Sprintf("docker -H %s rmi %s:%s", - dockerServerUrl, imageBaseName, imageTag)) - f.WriteCmd(fmt.Sprintf("docker -H %s rmi %s:latest", - dockerServerUrl, imageBaseName)) + dockerServerUrl, d.ImageName, imageTag)) + if d.PushLatest { + f.WriteCmd(fmt.Sprintf("docker -H %s rmi %s:latest", + dockerServerUrl, d.ImageName)) + } } } diff --git a/pkg/plugin/publish/docker_test.go b/pkg/plugin/publish/docker_test.go index f13c9bcf8..01e10e03a 100644 --- a/pkg/plugin/publish/docker_test.go +++ b/pkg/plugin/publish/docker_test.go @@ -24,30 +24,6 @@ func setUpWithDrone(input string) (string, error) { return bf.String(), err } -// Private Registry + RepoName (invalid config) -var privateRegistryRepoNameYaml = ` -publish: - docker: - docker_server: server - docker_port: 1000 - docker_version: 1.0 - registry_host: server - registry_login: false - repo_name: company - image_name: image -` - -func TestPrivateRegistryRepoName(t *testing.T) { - response, err := setUpWithDrone(privateRegistryRepoNameYaml) - t.Log(privateRegistryRepoNameYaml) - if err != nil { - t.Fatalf("Can't unmarshal script: %s\n\n", err.Error()) - } - if !strings.Contains(response, "Docker Plugin: Invalid Arguments Specified") { - t.Fatalf("registry_host + repo_name should produce an invalid config error, it didn't") - } -} - // Private Registry Test (no auth) var privateRegistryNoAuthYaml = ` publish: @@ -56,9 +32,8 @@ publish: docker_server: server docker_port: 1000 docker_version: 1.0 - registry_host: registry registry_login: false - image_name: image + image_name: registry/image ` func TestPrivateRegistryNoAuth(t *testing.T) { response, err := setUpWithDrone(privateRegistryNoAuthYaml) @@ -79,14 +54,12 @@ publish: docker_server: server docker_port: 1000 docker_version: 1.0 - registry_host: registry - registry_protocol: https - registry_port: 8000 + registry_login_url: https://registry:8000/v1/ registry_login: true username: username password: password email: email@example.com - image_name: image + image_name: registry/image ` func TestPrivateRegistryAuth(t *testing.T) { response, err := setUpWithDrone(privateRegistryAuthYaml) @@ -114,7 +87,7 @@ publish: username: username password: password email: email@example.com - image_name: image + image_name: username/image push_latest: true ` func TestOverrideLatestTag(t *testing.T) { @@ -166,7 +139,7 @@ publish: username: username password: password email: email@example.com - image_name: image + image_name: username/image ` func TestCustomTag(t *testing.T) { response, err := setUpWithDrone(customTagYaml) @@ -209,12 +182,12 @@ publish: docker_server: server docker_port: 1000 docker_version: 1.0 - repo_base_name: base_repo username: user password: password email: email - image_name: image + image_name: user/image push_latest: true + registry_login: true ` func TestValidYaml(t *testing.T) { @@ -248,7 +221,7 @@ publish: docker_server: server docker_port: 1000 docker_version: 1.0 - image_name: image + image_name: user/image username: user password: password email: email From 05591afd465f0868f7c3480f49e3abc43a44a9f7 Mon Sep 17 00:00:00 2001 From: Kyle Vigen Date: Mon, 11 Aug 2014 15:27:13 -0700 Subject: [PATCH 14/14] Revert "Add image for Go 1.3" --- README.md | 1 - pkg/build/images.go | 1 - 2 files changed, 2 deletions(-) diff --git a/README.md b/README.md index cdb1a7816..ca66f554a 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,6 @@ docker pull bradrydzewski/gcc:4.8 # image: gcc4.8 docker pull bradrydzewski/go:1.0 # image: go1 docker pull bradrydzewski/go:1.1 # image: go1.1 docker pull bradrydzewski/go:1.2 # image: go1.2 -docker pull bradrydzewski/go:1.3 # image: go1.3 # haskell images docker pull bradrydzewski/haskell:7.4 # image: haskell diff --git a/pkg/build/images.go b/pkg/build/images.go index 16ef64a53..5c09820b1 100644 --- a/pkg/build/images.go +++ b/pkg/build/images.go @@ -197,7 +197,6 @@ var builders = map[string]*image{ "go1": {Tag: "bradrydzewski/go:1.0"}, "go1.1": {Tag: "bradrydzewski/go:1.1"}, "go1.2": {Tag: "bradrydzewski/go:1.2"}, - "go1.3": {Tag: "bradrydzewski/go:1.3"}, // Haskell build images "haskell": {Tag: "bradrydzewski/haskell:7.4"},