Browse code

Convert builder images to use go

csrwng authored on 2014/10/30 21:56:31
Showing 30 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+// Package main contains the main executable for `openshift-docker-build` which performs
1
+// a docker build given an OpenShift build object
2
+package main
0 3
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+package main
1
+
2
+import (
3
+	"github.com/openshift/origin/pkg/build/builder/cmd"
4
+)
5
+
6
+func main() {
7
+	cmd.RunDockerBuild()
8
+}
0 9
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+// Package main contains the main executable for `openshift-sti-build` which performs
1
+// an STI build given an OpenShift build object
2
+package main
0 3
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+package main
1
+
2
+import (
3
+	"github.com/openshift/origin/pkg/build/builder/cmd"
4
+)
5
+
6
+func main() {
7
+	cmd.RunSTIBuild()
8
+}
... ...
@@ -33,7 +33,7 @@
33 33
       }
34 34
    ],
35 35
    "head_commit":{
36
-      "id":"9bdc3a26ff933b32f3e558636b58aea86a69f051",
36
+      "id":"",
37 37
       "distinct":true,
38 38
       "message":"Added license",
39 39
       "timestamp":"2014-08-28T16:55:36+02:00",
... ...
@@ -35,6 +35,8 @@ cp -f "${imagedir}/openshift"        images/origin/bin
35 35
 cp -f "${imagedir}/openshift-deploy" images/origin/bin
36 36
 cp -f "${imagedir}/openshift-router" images/origin/bin
37 37
 cp -f "${imagedir}/openshift-router" images/router/haproxy/bin
38
+cp -f "${imagedir}/openshift-docker-build" images/builder/docker/docker-builder/bin
39
+cp -f "${imagedir}/openshift-sti-build"    images/builder/docker/sti-builder/bin
38 40
 
39 41
 # build hello-openshift binary
40 42
 "${OS_ROOT}/hack/build-go.sh" examples/hello-openshift
... ...
@@ -28,6 +28,8 @@ readonly OS_COMPILE_PLATFORMS=(
28 28
 readonly OS_COMPILE_TARGETS=(
29 29
   cmd/openshift-router
30 30
   cmd/openshift-deploy
31
+  cmd/openshift-docker-build
32
+  cmd/openshift-sti-build
31 33
 )
32 34
 readonly OS_COMPILE_BINARIES=("${OS_COMPILE_TARGETS[@]##*/}")
33 35
 
... ...
@@ -8,5 +8,4 @@ FROM centos:centos7
8 8
 
9 9
 # components from EPEL must be installed in a separate yum install step
10 10
 RUN yum install -y git tar wget socat hostname epel-release && \
11
-    yum install -y docker && \
12 11
     yum clean all
... ...
@@ -1,12 +1,8 @@
1 1
 #
2
-# This is the image that executes a Docker build inside Origin. It expects a set of
3
-# environment variables to parameterize the build:
2
+# This is the image that executes a Docker build inside Origin. It expects the
3
+# following environment variables:
4 4
 #
5
-#   BUILD_TAG - the tag to assign the image after it is built
6
-#   REGISTRY - the Docker registry URL to push this image to (optional)
7
-#   SOURCE_URI - a URI to fetch the build context from
8
-#   SOURCE_REF - a reference to pass to Git for which commit to use (optional)
9
-#   CONTEXT_DIR - a subdirectory of the retrieved source to run the build from
5
+#   BUILD - JSON string containing the openshift build object
10 6
 #
11 7
 # This image expects to have the Docker socket bind-mounted into the container.
12 8
 # If "/root/.dockercfg" is bind mounted in, it will use that as authorization to a
... ...
@@ -17,5 +13,5 @@
17 17
 FROM openshift/origin-base
18 18
 
19 19
 ENV HOME /root
20
-ADD ./build.sh /tmp/build.sh
21
-CMD ["/tmp/build.sh"]
20
+ADD bin/openshift-docker-build /usr/bin/openshift-docker-build
21
+CMD ["/usr/bin/openshift-docker-build"]
22 22
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+openshift-docker-build
0 1
deleted file mode 100755
... ...
@@ -1,70 +0,0 @@
1
-#!/bin/bash
2
-set -o pipefail
3
-IFS=$'\n\t'
4
-
5
-DOCKER_SOCKET=/var/run/docker.sock
6
-
7
-if [ ! -e "${DOCKER_SOCKET}" ]; then
8
-  echo "Docker socket missing at ${DOCKER_SOCKET}"
9
-  exit 1
10
-fi
11
-
12
-TAG="${BUILD_TAG}"
13
-if [ -n "${REGISTRY}" ]; then
14
-  TAG="${REGISTRY}/${BUILD_TAG}"
15
-elif [ -n "${DOCKER_REGISTRY}" ]; then
16
-  TAG="${DOCKER_REGISTRY}/${BUILD_TAG}"
17
-fi
18
-
19
-# backwards compatibility for older openshift versions that passed docker context instead of source_uri
20
-if [ -n "${DOCKER_CONTEXT_URL}" ]; then
21
-  SOURCE_URI=${DOCKER_CONTEXT_URL}
22
-fi
23
-
24
-if [[ "${SOURCE_URI}" != "git://"* ]] && [[ "${SOURCE_URI}" != "git@"* ]]; then
25
-  URL="${SOURCE_URI}"
26
-  if [[ "${URL}" != "http://"* ]] && [[ "${URL}" != "https://"* ]]; then
27
-    URL="https://${URL}"
28
-  fi
29
-  curl --head --silent --fail --location --max-time 16 $URL > /dev/null
30
-  if [ $? != 0 ]; then
31
-    echo "Not found: ${SOURCE_URI}"
32
-    exit 1
33
-  fi
34
-fi
35
-
36
-if [ -n "${SOURCE_REF}" ] || [ -n "${CONTEXT_DIR}" ]; then
37
-  BUILD_DIR=$(mktemp --directory --suffix=docker-build)
38
-  git clone --recursive "${SOURCE_URI}" "${BUILD_DIR}"
39
-  if [ $? != 0 ]; then
40
-    echo "Error trying to fetch git source: ${SOURCE_URI}"
41
-    exit 1
42
-  fi
43
-  pushd "${BUILD_DIR}"
44
-  if [ -n "${SOURCE_REF}" ]; then
45
-    git checkout "${SOURCE_REF}"
46
-    if [ $? != 0 ]; then
47
-      echo "Error trying to checkout branch: ${SOURCE_REF}"
48
-      exit 1
49
-    fi
50
-  fi
51
-  if [ -n "${SOURCE_ID}" ]; then
52
-    git branch --contains ${SOURCE_ID} | grep ${SOURCE_REF}
53
-    if [ $? != 0 ]; then
54
-      echo "Branch '${SOURCE_REF}' does not contain commit: ${SOURCE_ID}"
55
-      exit 1
56
-    fi
57
-  fi
58
-  popd
59
-  if [ -n "${CONTEXT_DIR}" ] && [ ! -d "${BUILD_DIR}/${CONTEXT_DIR}" ]; then
60
-    echo "ContextDir does not exist in the repository: ${CONTEXT_DIR}"
61
-    exit 1
62
-  fi
63
-  docker build --rm -t "${TAG}" "${BUILD_DIR}/${CONTEXT_DIR}"
64
-else
65
-  docker build --rm -t "${TAG}" "${SOURCE_URI}"
66
-fi
67
-
68
-if [ -n "${REGISTRY}" ] || [ -n "${DOCKER_REGISTRY}" ] || [ -s "/root/.dockercfg" ]; then
69
-  docker push "${TAG}"
70
-fi
... ...
@@ -1,12 +1,8 @@
1 1
 #
2
-# This is the image that executes a Docker build inside Origin. It expects a set of
3
-# environment variables to parameterize the build:
2
+# This is the image that executes a STI build inside Origin. It expects the
3
+# following environment variables:
4 4
 #
5
-#   BUILD_TAG - the tag to assign the image after it is built
6
-#   REGISTRY - the Docker registry URL to push this image to (optional)
7
-#   SOURCE_URI - a URI to fetch the build context from
8
-#   SOURCE_REF - a reference to pass to Git for which commit to use (optional)
9
-#   CONTEXT_DIR - a subdirectory of the retrieved source to run the build from
5
+#   BUILD - JSON string containing the openshift build object
10 6
 #
11 7
 # This image expects to have the Docker socket bind-mounted into the container.
12 8
 # If "/root/.dockercfg" is bind mounted in, it will use that as authorization to a
... ...
@@ -16,17 +12,6 @@
16 16
 #
17 17
 FROM openshift/origin-base
18 18
 
19
-# TODO: When STI is vendored in OpenShift, it should be built as part of openshift/origin
20
-# and simply made the CMD here.
21
-RUN yum -y install golang golang-src golang-pkg-bin-linux-amd64 golang-pkg-linux-amd64 && \
22
-    yum clean all && \
23
-    mkdir -p /tmp/go/src/github.com/openshift && \
24
-    git clone git://github.com/openshift/geard /tmp/go/src/github.com/openshift/geard && \
25
-    export GOPATH=/tmp/go && \
26
-    cd /tmp/go/src/github.com/openshift/geard && \
27
-    ./contrib/build -n && \
28
-    cp /tmp/go/bin/sti /usr/bin/sti
29
-
30 19
 ENV HOME /root
31
-ADD ./build.sh /opt/build.sh
32
-CMD ["/opt/build.sh"]
20
+ADD ./bin/openshift-sti-build /usr/bin/openshift-sti-build
21
+CMD ["/usr/bin/openshift-sti-build"]
33 22
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+openshift-sti-build
0 1
deleted file mode 100755
... ...
@@ -1,27 +0,0 @@
1
-#!/bin/bash -ex
2
-
3
-DOCKER_SOCKET=/var/run/docker.sock
4
-
5
-if [ ! -e "${DOCKER_SOCKET}" ]; then
6
-  echo "Docker socket missing at $DOCKER_SOCKET"
7
-  exit 1
8
-fi
9
-
10
-TAG="${BUILD_TAG}"
11
-if [ -n "${REGISTRY}" ]; then
12
-  TAG="${REGISTRY}/${BUILD_TAG}"
13
-elif [ -n "${DOCKER_REGISTRY}" ]; then
14
-  TAG="${DOCKER_REGISTRY}/${BUILD_TAG}"
15
-fi
16
-
17
-REF_OPTION=""
18
-if [ -n "${SOURCE_REF}" ]; then
19
-  REF_OPTION="--ref ${SOURCE_REF}"
20
-fi
21
-
22
-BUILD_TEMP_DIR="${TEMP_DIR-$TMPDIR}"
23
-TMPDIR="${BUILD_TEMP_DIR}" sti build "${SOURCE_URI}" "${BUILDER_IMAGE}" "${TAG}" "${REF_OPTION}"
24
-
25
-if [ -n "${REGISTRY}" ] || [ -n "${DOCKER_REGISTRY}" ] || [ -s "/root/.dockercfg" ]; then
26
-  docker push "${TAG}"
27
-fi
28 1
new file mode 100644
... ...
@@ -0,0 +1,58 @@
0
+package cmd
1
+
2
+import (
3
+	"encoding/json"
4
+	"log"
5
+	"os"
6
+
7
+	"github.com/fsouza/go-dockerclient"
8
+	"github.com/openshift/origin/pkg/build/api"
9
+	bld "github.com/openshift/origin/pkg/build/builder"
10
+	"github.com/openshift/origin/pkg/build/builder/cmd/dockercfg"
11
+	dockerutil "github.com/openshift/origin/pkg/cmd/util/docker"
12
+)
13
+
14
+const DefaultDockerEndpoint = "unix:///var/run/docker.sock"
15
+const DockerCfgFile = ".dockercfg"
16
+
17
+type builder interface {
18
+	Build() error
19
+}
20
+type factoryFunc func(
21
+	client bld.DockerClient,
22
+	dockerSocket string,
23
+	authConfig docker.AuthConfiguration,
24
+	authPresent bool,
25
+	build *api.Build) builder
26
+
27
+func run(builderFactory factoryFunc) {
28
+	client, endpoint, err := dockerutil.NewHelper().GetClient()
29
+	if err != nil {
30
+		log.Fatalf("Error obtaining docker client: %v", err)
31
+	}
32
+	buildStr := os.Getenv("BUILD")
33
+	build := api.Build{}
34
+	err = json.Unmarshal([]byte(buildStr), &build)
35
+	if err != nil {
36
+		log.Fatalf("Unable to parse build: %v", err)
37
+	}
38
+	authcfg, authPresent := dockercfg.NewHelper().GetDockerAuth(build.Parameters.Output.Registry)
39
+	b := builderFactory(client, endpoint, authcfg, authPresent, &build)
40
+	if err = b.Build(); err != nil {
41
+		log.Fatalf("Build error: %v", err)
42
+	}
43
+}
44
+
45
+// RunDockerBuild creates a docker builder and runs its build
46
+func RunDockerBuild() {
47
+	run(func(client bld.DockerClient, sock string, auth docker.AuthConfiguration, present bool, build *api.Build) builder {
48
+		return bld.NewDockerBuilder(client, auth, present, build)
49
+	})
50
+}
51
+
52
+// RunSTIBuild creates a STI builder and runs its build
53
+func RunSTIBuild() {
54
+	run(func(client bld.DockerClient, sock string, auth docker.AuthConfiguration, present bool, build *api.Build) builder {
55
+		return bld.NewSTIBuilder(client, sock, auth, present, build)
56
+	})
57
+}
0 58
new file mode 100644
... ...
@@ -0,0 +1,4 @@
0
+// Package cmd contains the main entry point for the
1
+// docker and STI builders
2
+
3
+package cmd
0 4
new file mode 100644
... ...
@@ -0,0 +1,112 @@
0
+package dockercfg
1
+
2
+import (
3
+	"encoding/base64"
4
+	"encoding/json"
5
+	"io/ioutil"
6
+	"os"
7
+	"os/user"
8
+	"path/filepath"
9
+	"strings"
10
+
11
+	"github.com/fsouza/go-dockerclient"
12
+	"github.com/spf13/pflag"
13
+)
14
+
15
+//TODO: Remove this code once the methods in Kubernetes kubelet/dockertools/config.go are public
16
+
17
+// Default docker registry server
18
+const defaultRegistryServer = "https://index.docker.io/v1/"
19
+
20
+// Helper contains all the valid config options for reading the local dockercfg file
21
+type Helper struct {
22
+}
23
+
24
+// NewHelper creates a Flags object with the default values set.
25
+func NewHelper() *Helper {
26
+	return &Helper{}
27
+}
28
+
29
+// InstallFlags installs the Docker flag helper into a FlagSet with the default
30
+// options and default values from the Helper object.
31
+func (_ *Helper) InstallFlags(flags *pflag.FlagSet) {
32
+}
33
+
34
+// GetDockerAuth returns a valid Docker AuthConfiguration entry, and whether it was read
35
+// from the local dockercfg file
36
+func (_ *Helper) GetDockerAuth(registry string) (docker.AuthConfiguration, bool) {
37
+	var authCfg docker.AuthConfiguration
38
+	dockercfgPath := getDockercfgFile("")
39
+	if _, err := os.Stat(dockercfgPath); err != nil {
40
+		return authCfg, false
41
+	}
42
+	cfg, err := readDockercfg(dockercfgPath)
43
+	if err != nil {
44
+		return authCfg, false
45
+	}
46
+	server := registry
47
+	if server == "" {
48
+		server = defaultRegistryServer
49
+	}
50
+	entry, ok := cfg[server]
51
+	if !ok {
52
+		return authCfg, false
53
+	}
54
+	uname, pass, err := getCredentials(entry.Auth)
55
+	if err != nil {
56
+		return authCfg, false
57
+	}
58
+	authCfg.Username = uname
59
+	authCfg.Password = pass
60
+	return authCfg, true
61
+}
62
+
63
+// getDockercfgFile returns the path to the dockercfg file
64
+func getDockercfgFile(path string) string {
65
+	var cfgPath string
66
+	if path != "" {
67
+		cfgPath = path
68
+	} else if os.Getenv("DOCKERCFG_PATH") != "" {
69
+		cfgPath = os.Getenv("DOCKERCFG_PATH")
70
+	} else if currentUser, err := user.Current(); err == nil {
71
+		cfgPath = filepath.Join(currentUser.HomeDir, ".dockercfg")
72
+	}
73
+	return cfgPath
74
+}
75
+
76
+// authEntry is a single entry for a given server in a
77
+// .dockercfg file
78
+type authEntry struct {
79
+	Auth  string `json:auth`
80
+	Email string `json:email`
81
+}
82
+
83
+// dockercfg represents the contents of a .dockercfg file
84
+type dockercfg map[string]authEntry
85
+
86
+// readDockercfg reads the contents of a .dockercfg file into a map
87
+// with server name keys and AuthEntry values
88
+func readDockercfg(filePath string) (cfg dockercfg, err error) {
89
+	content, err := ioutil.ReadFile(filePath)
90
+	if err != nil {
91
+		return
92
+	}
93
+	cfg = dockercfg{}
94
+	if err := json.Unmarshal(content, &cfg); err != nil {
95
+		return nil, err
96
+	}
97
+	return
98
+}
99
+
100
+// getCredentials parses an auth string inside a dockercfg file into
101
+// a username and password
102
+func getCredentials(auth string) (username, password string, err error) {
103
+	creds, err := base64.StdEncoding.DecodeString(auth)
104
+	if err != nil {
105
+		return
106
+	}
107
+	unamepass := strings.Split(string(creds), ":")
108
+	username = unamepass[0]
109
+	password = unamepass[1]
110
+	return
111
+}
0 112
new file mode 100644
... ...
@@ -0,0 +1,47 @@
0
+package dockercfg
1
+
2
+import (
3
+	"io/ioutil"
4
+	"os"
5
+	"testing"
6
+)
7
+
8
+func TestReadDockercfg(t *testing.T) {
9
+	content := "{\"test-server-1\":{\"auth\":\"my-auth\",\"email\":\"test@email.test.com\"}}"
10
+	tempfile, err := ioutil.TempFile("", "cfgtest")
11
+	if err != nil {
12
+		t.Fatalf("Unable to create temp file: %v", err)
13
+	}
14
+	defer os.Remove(tempfile.Name())
15
+	tempfile.WriteString(content)
16
+	tempfile.Close()
17
+
18
+	dockercfg, err := readDockercfg(tempfile.Name())
19
+	if err != nil {
20
+		t.Errorf("Received unexpected error reading dockercfg: %v", err)
21
+		return
22
+	}
23
+
24
+	auth, ok := dockercfg["test-server-1"]
25
+	if !ok {
26
+		t.Errorf("Expected entry test-server-1 not found in dockercfg")
27
+		return
28
+	}
29
+	if auth.Auth != "my-auth" {
30
+		t.Errorf("Unexpected Auth value: %s", auth.Auth)
31
+	}
32
+	if auth.Email != "test@email.test.com" {
33
+		t.Errorf("Unexpected Email value: %s", auth.Email)
34
+	}
35
+}
36
+
37
+func TestGetCredentials(t *testing.T) {
38
+	testStr := "dGVzdDpwYXNzd29yZA==" // test:password
39
+	uname, pass, err := getCredentials(testStr)
40
+	if err != nil {
41
+		t.Errorf("Unexpected error getting credentials: %v", err)
42
+	}
43
+	if uname != "test" && pass != "password" {
44
+		t.Errorf("Unexpected username and password: %s,%s", uname, pass)
45
+	}
46
+}
0 47
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+// Package dockercfg contains a command helper to read .dockercfg files
1
+
2
+package dockercfg
0 3
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+// Package builder contains builders for STI and Docker in Openshift Origin
1
+
2
+package builder
0 3
new file mode 100644
... ...
@@ -0,0 +1,157 @@
0
+package builder
1
+
2
+import (
3
+	"fmt"
4
+	"io/ioutil"
5
+	"net"
6
+	"net/url"
7
+	"os"
8
+	"path/filepath"
9
+	"strings"
10
+	"time"
11
+
12
+	"github.com/fsouza/go-dockerclient"
13
+	"github.com/openshift/origin/pkg/build/api"
14
+	"github.com/openshift/source-to-image/pkg/sti/git"
15
+	"github.com/openshift/source-to-image/pkg/sti/tar"
16
+)
17
+
18
+// urlCheckTimeout is the timeout used to check the source URL
19
+// If fetching the URL exceeeds the timeout, then the build will
20
+// not proceed further and stop
21
+const urlCheckTimeout = 16 * time.Second
22
+
23
+// DockerBuilder builds Docker images given a git repository URL
24
+type DockerBuilder struct {
25
+	dockerClient DockerClient
26
+	authPresent  bool
27
+	auth         docker.AuthConfiguration
28
+	git          git.Git
29
+	tar          tar.Tar
30
+	build        *api.Build
31
+	urlTimeout   time.Duration
32
+}
33
+
34
+// NewDockerBuilder creates a new instance of DockerBuilder
35
+func NewDockerBuilder(dockerClient DockerClient, authCfg docker.AuthConfiguration, authPresent bool, build *api.Build) *DockerBuilder {
36
+	return &DockerBuilder{
37
+		dockerClient: dockerClient,
38
+		authPresent:  authPresent,
39
+		auth:         authCfg,
40
+		build:        build,
41
+		git:          git.NewGit(),
42
+		tar:          tar.NewTar(false),
43
+		urlTimeout:   urlCheckTimeout,
44
+	}
45
+}
46
+
47
+// Build executes a Docker build
48
+func (d *DockerBuilder) Build() error {
49
+	buildDir, err := ioutil.TempDir("", "docker-build")
50
+	if err != nil {
51
+		return err
52
+	}
53
+	if err = d.fetchSource(buildDir); err != nil {
54
+		return err
55
+	}
56
+	if err = d.dockerBuild(buildDir); err != nil {
57
+		return err
58
+	}
59
+	if err = d.addImageVars(); err != nil {
60
+		return err
61
+	}
62
+	if d.build.Parameters.Output.Registry != "" || d.authPresent {
63
+		return pushImage(d.dockerClient, imageTag(d.build), d.auth)
64
+	}
65
+	return nil
66
+}
67
+
68
+// checkSourceURI performs a check on the URI associated with the build
69
+// to make sure that it is live before proceeding with the build.
70
+func (d *DockerBuilder) checkSourceURI() error {
71
+	rawurl := d.build.Parameters.Source.Git.URI
72
+	if !d.git.ValidCloneSpec(rawurl) {
73
+		return fmt.Errorf("Invalid git source url: %s", rawurl)
74
+	}
75
+	if strings.HasPrefix(rawurl, "git://") || strings.HasPrefix(rawurl, "git@") {
76
+		return nil
77
+	}
78
+	if !strings.HasPrefix(rawurl, "http://") && !strings.HasPrefix(rawurl, "https://") {
79
+		rawurl = fmt.Sprintf("https://%s", rawurl)
80
+	}
81
+	srcURL, err := url.Parse(rawurl)
82
+	if err != nil {
83
+		return err
84
+	}
85
+	host := srcURL.Host
86
+	if strings.Index(host, ":") == -1 {
87
+		switch srcURL.Scheme {
88
+		case "http":
89
+			host += ":80"
90
+		case "https":
91
+			host += ":443"
92
+		}
93
+	}
94
+	dialer := net.Dialer{Timeout: d.urlTimeout}
95
+	conn, err := dialer.Dial("tcp", host)
96
+	if err != nil {
97
+		return err
98
+	}
99
+	return conn.Close()
100
+
101
+}
102
+
103
+// fetchSource retrieves the git source from the repository. If a commit ID
104
+// is included in the build revision, that commit ID is checked out. Otherwise
105
+// if a ref is included in the source definition, that ref is checked out.
106
+func (d *DockerBuilder) fetchSource(dir string) error {
107
+	if err := d.checkSourceURI(); err != nil {
108
+		return err
109
+	}
110
+	if err := d.git.Clone(d.build.Parameters.Source.Git.URI, dir); err != nil {
111
+		return err
112
+	}
113
+	if d.build.Parameters.Source.Git.Ref == "" &&
114
+		(d.build.Parameters.Revision == nil ||
115
+			d.build.Parameters.Revision.Git == nil ||
116
+			d.build.Parameters.Revision.Git.Commit == "") {
117
+		return nil
118
+	}
119
+	if d.build.Parameters.Revision != nil &&
120
+		d.build.Parameters.Revision.Git != nil &&
121
+		d.build.Parameters.Revision.Git.Commit != "" {
122
+		return d.git.Checkout(dir, d.build.Parameters.Revision.Git.Commit)
123
+	}
124
+	return d.git.Checkout(dir, d.build.Parameters.Source.Git.Ref)
125
+}
126
+
127
+// dockerBuild performs a docker build on the source that has been retrieved
128
+func (d *DockerBuilder) dockerBuild(dir string) error {
129
+	if d.build.Parameters.Strategy.DockerStrategy != nil &&
130
+		d.build.Parameters.Strategy.DockerStrategy.ContextDir != "" {
131
+		dir = filepath.Join(dir, d.build.Parameters.Strategy.DockerStrategy.ContextDir)
132
+	}
133
+	return buildImage(d.dockerClient, dir, imageTag(d.build), d.tar)
134
+}
135
+
136
+// addImageVars creates a new Dockerfile which adds certain environment
137
+// variables to the previously tagged image
138
+func (d *DockerBuilder) addImageVars() error {
139
+	envVars := getBuildEnvVars(d.build)
140
+	tempDir, err := ioutil.TempDir("", "overlay")
141
+	if err != nil {
142
+		return err
143
+	}
144
+	overlay, err := os.Create(filepath.Join(tempDir, "Dockerfile"))
145
+	if err != nil {
146
+		return err
147
+	}
148
+	overlay.WriteString(fmt.Sprintf("FROM %s\n", imageTag(d.build)))
149
+	for k, v := range envVars {
150
+		overlay.WriteString(fmt.Sprintf("ENV %s %s\n", k, v))
151
+	}
152
+	if err = overlay.Close(); err != nil {
153
+		return err
154
+	}
155
+	return buildImage(d.dockerClient, tempDir, imageTag(d.build), d.tar)
156
+}
0 157
new file mode 100644
... ...
@@ -0,0 +1,46 @@
0
+package builder
1
+
2
+import (
3
+	"os"
4
+
5
+	"github.com/fsouza/go-dockerclient"
6
+	"github.com/openshift/source-to-image/pkg/sti/tar"
7
+)
8
+
9
+// DockerClient is an interface to the Docker client that contains
10
+// the methods used by the common builder
11
+type DockerClient interface {
12
+	BuildImage(opts docker.BuildImageOptions) error
13
+	PushImage(opts docker.PushImageOptions, auth docker.AuthConfiguration) error
14
+}
15
+
16
+// pushImage pushes a docker image to the registry specified in its tag
17
+func pushImage(client DockerClient, name string, authConfig docker.AuthConfiguration) error {
18
+	repository, tag := docker.ParseRepositoryTag(name)
19
+	opts := docker.PushImageOptions{
20
+		Name:         repository,
21
+		Tag:          tag,
22
+		OutputStream: os.Stdout,
23
+	}
24
+	return client.PushImage(opts, authConfig)
25
+}
26
+
27
+// buildImage invokes a docker build on a particular directory
28
+func buildImage(client DockerClient, dir string, tag string, tar tar.Tar) error {
29
+	tarFile, err := tar.CreateTarFile("", dir)
30
+	if err != nil {
31
+		return err
32
+	}
33
+	tarStream, err := os.Open(tarFile)
34
+	if err != nil {
35
+		return err
36
+	}
37
+	defer tarStream.Close()
38
+	opts := docker.BuildImageOptions{
39
+		Name:           tag,
40
+		RmTmpContainer: true,
41
+		OutputStream:   os.Stdout,
42
+		InputStream:    tarStream,
43
+	}
44
+	return client.BuildImage(opts)
45
+}
0 46
new file mode 100644
... ...
@@ -0,0 +1,37 @@
0
+package builder
1
+
2
+import (
3
+	"testing"
4
+
5
+	"github.com/fsouza/go-dockerclient"
6
+)
7
+
8
+type FakeDocker struct {
9
+	pushImageFunc  func(opts docker.PushImageOptions, auth docker.AuthConfiguration) error
10
+	buildImageFunc func(opts docker.BuildImageOptions) error
11
+}
12
+
13
+func (d *FakeDocker) BuildImage(opts docker.BuildImageOptions) error {
14
+	if d.pushImageFunc != nil {
15
+		return d.buildImageFunc(opts)
16
+	}
17
+	return nil
18
+}
19
+
20
+func (d *FakeDocker) PushImage(opts docker.PushImageOptions, auth docker.AuthConfiguration) error {
21
+	if d.pushImageFunc != nil {
22
+		return d.pushImageFunc(opts, auth)
23
+	}
24
+	return nil
25
+}
26
+
27
+func TestDockerPush(t *testing.T) {
28
+	verifyFunc := func(opts docker.PushImageOptions, auth docker.AuthConfiguration) error {
29
+		if opts.Name != "test/image" {
30
+			t.Errorf("Unexpected image name: %s", opts.Name)
31
+		}
32
+		return nil
33
+	}
34
+	fd := &FakeDocker{pushImageFunc: verifyFunc}
35
+	pushImage(fd, "test/image", docker.AuthConfiguration{})
36
+}
0 37
new file mode 100644
... ...
@@ -0,0 +1,55 @@
0
+package builder
1
+
2
+import (
3
+	"github.com/fsouza/go-dockerclient"
4
+	"github.com/openshift/origin/pkg/build/api"
5
+	"github.com/openshift/source-to-image/pkg/sti"
6
+)
7
+
8
+// STIBuilder performs an STI build given the build object
9
+type STIBuilder struct {
10
+	dockerClient DockerClient
11
+	dockerSocket string
12
+	authPresent  bool
13
+	auth         docker.AuthConfiguration
14
+	build        *api.Build
15
+}
16
+
17
+// NewSTIBuilder creates a new STIBuilder instance
18
+func NewSTIBuilder(client DockerClient, dockerSocket string, authCfg docker.AuthConfiguration, authPresent bool, build *api.Build) *STIBuilder {
19
+	return &STIBuilder{
20
+		dockerClient: client,
21
+		dockerSocket: dockerSocket,
22
+		authPresent:  authPresent,
23
+		auth:         authCfg,
24
+		build:        build,
25
+	}
26
+}
27
+
28
+// Build executes the STI build
29
+func (s *STIBuilder) Build() error {
30
+	request := &sti.STIRequest{
31
+		BaseImage:    s.build.Parameters.Strategy.STIStrategy.BuilderImage,
32
+		DockerSocket: s.dockerSocket,
33
+		Source:       s.build.Parameters.Source.Git.URI,
34
+		Tag:          imageTag(s.build),
35
+		Environment:  getBuildEnvVars(s.build),
36
+	}
37
+	if s.build.Parameters.Revision != nil && s.build.Parameters.Revision.Git != nil &&
38
+		s.build.Parameters.Revision.Git.Commit != "" {
39
+		request.Ref = s.build.Parameters.Revision.Git.Commit
40
+	} else if s.build.Parameters.Source.Git.Ref != "" {
41
+		request.Ref = s.build.Parameters.Source.Git.Ref
42
+	}
43
+	builder, err := sti.NewBuilder(request)
44
+	if err != nil {
45
+		return err
46
+	}
47
+	if _, err = builder.Build(); err != nil {
48
+		return err
49
+	}
50
+	if s.build.Parameters.Output.Registry != "" || s.authPresent {
51
+		return pushImage(s.dockerClient, imageTag(s.build), s.auth)
52
+	}
53
+	return nil
54
+}
0 55
new file mode 100644
... ...
@@ -0,0 +1,40 @@
0
+package builder
1
+
2
+import (
3
+	"fmt"
4
+	"strings"
5
+
6
+	"github.com/openshift/origin/pkg/build/api"
7
+)
8
+
9
+type Builder interface {
10
+	Build() error
11
+}
12
+
13
+// imageTag returns the tag to be used for the build. If a registry has been
14
+// specified, it will prepend the registry to the name
15
+func imageTag(build *api.Build) string {
16
+	tag := build.Parameters.Output.ImageTag
17
+	if !strings.HasPrefix(tag, build.Parameters.Output.Registry) {
18
+		tag = fmt.Sprintf("%s/%s", build.Parameters.Output.Registry, tag)
19
+	}
20
+	return tag
21
+}
22
+
23
+// getBuildEnvVars returns a map with the environment variables that should be added
24
+// to the built image
25
+func getBuildEnvVars(build *api.Build) map[string]string {
26
+	envVars := map[string]string{
27
+		"OPENSHIFT_BUILD_NAME":   build.ID,
28
+		"OPENSHIFT_BUILD_SOURCE": build.Parameters.Source.Git.URI,
29
+	}
30
+	if build.Parameters.Source.Git.Ref != "" {
31
+		envVars["OPENSHIFT_BUILD_REFERENCE"] = build.Parameters.Source.Git.Ref
32
+	}
33
+	if build.Parameters.Revision != nil &&
34
+		build.Parameters.Revision.Git != nil &&
35
+		build.Parameters.Revision.Git.Commit != "" {
36
+		envVars["OPENSHIFT_BUILD_COMMIT"] = build.Parameters.Revision.Git.Commit
37
+	}
38
+	return envVars
39
+}
0 40
new file mode 100644
... ...
@@ -0,0 +1,90 @@
0
+package builder
1
+
2
+import (
3
+	"testing"
4
+
5
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
6
+	"github.com/openshift/origin/pkg/build/api"
7
+)
8
+
9
+func TestImageTag(t *testing.T) {
10
+	type tagTest struct {
11
+		build    api.Build
12
+		expected string
13
+	}
14
+	tests := []tagTest{
15
+		{
16
+			build: api.Build{
17
+				Parameters: api.BuildParameters{
18
+					Output: api.BuildOutput{
19
+						ImageTag: "test/tag",
20
+					},
21
+				},
22
+			},
23
+			expected: "test/tag",
24
+		},
25
+		{
26
+			build: api.Build{
27
+				Parameters: api.BuildParameters{
28
+					Output: api.BuildOutput{
29
+						ImageTag: "test/tag",
30
+						Registry: "registry-server.test:5000",
31
+					},
32
+				},
33
+			},
34
+			expected: "registry-server.test:5000/test/tag",
35
+		},
36
+		{
37
+			build: api.Build{
38
+				Parameters: api.BuildParameters{
39
+					Output: api.BuildOutput{
40
+						ImageTag: "registry-server.test:5000/test/tag",
41
+						Registry: "registry-server.test:5000",
42
+					},
43
+				},
44
+			},
45
+			expected: "registry-server.test:5000/test/tag",
46
+		},
47
+	}
48
+	for _, x := range tests {
49
+		result := imageTag(&x.build)
50
+		if result != x.expected {
51
+			t.Errorf("Unexpected imageTag result. Expected: %s, Actual: %s",
52
+				result, x.expected)
53
+		}
54
+	}
55
+}
56
+
57
+func TestGetBuildEnvVars(t *testing.T) {
58
+	b := &api.Build{
59
+		TypeMeta: kapi.TypeMeta{
60
+			ID: "1234",
61
+		},
62
+		Parameters: api.BuildParameters{
63
+			Source: api.BuildSource{
64
+				Git: &api.GitBuildSource{
65
+					URI: "github.com/build/uri",
66
+					Ref: "my-branch",
67
+				},
68
+			},
69
+			Revision: &api.SourceRevision{
70
+				Git: &api.GitSourceRevision{
71
+					Commit: "56789",
72
+				},
73
+			},
74
+		},
75
+	}
76
+
77
+	vars := getBuildEnvVars(b)
78
+	expected := map[string]string{
79
+		"OPENSHIFT_BUILD_NAME":      "1234",
80
+		"OPENSHIFT_BUILD_SOURCE":    "github.com/build/uri",
81
+		"OPENSHIFT_BUILD_REFERENCE": "my-branch",
82
+		"OPENSHIFT_BUILD_COMMIT":    "56789",
83
+	}
84
+	for k, v := range expected {
85
+		if vars[k] != v {
86
+			t.Errorf("Expected: %s,%s, Got: %s,%s", k, v, k, vars[k])
87
+		}
88
+	}
89
+}
... ...
@@ -22,16 +22,6 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod,
22 22
 		return nil, err
23 23
 	}
24 24
 
25
-	var sourceId string
26
-	if build.Parameters.Revision != nil {
27
-		sourceId = build.Parameters.Revision.Git.Commit
28
-	}
29
-
30
-	var contextDir string
31
-	if build.Parameters.Strategy.DockerStrategy != nil {
32
-		contextDir = build.Parameters.Strategy.DockerStrategy.ContextDir
33
-	}
34
-
35 25
 	pod := &kapi.Pod{
36 26
 		TypeMeta: kapi.TypeMeta{
37 27
 			ID: build.PodID,
... ...
@@ -44,12 +34,6 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod,
44 44
 						Name:  "docker-build",
45 45
 						Image: bs.BuilderImage,
46 46
 						Env: []kapi.EnvVar{
47
-							{Name: "SOURCE_URI", Value: build.Parameters.Source.Git.URI},
48
-							{Name: "SOURCE_REF", Value: build.Parameters.Source.Git.Ref},
49
-							{Name: "SOURCE_ID", Value: sourceId},
50
-							{Name: "CONTEXT_DIR", Value: contextDir},
51
-							{Name: "BUILD_TAG", Value: build.Parameters.Output.ImageTag},
52
-							{Name: "REGISTRY", Value: build.Parameters.Output.Registry},
53 47
 							{Name: "BUILD", Value: string(buildJson)},
54 48
 						},
55 49
 					},
... ...
@@ -37,18 +37,12 @@ func TestDockerCreateBuildPod(t *testing.T) {
37 37
 	if actual.DesiredState.Manifest.RestartPolicy.Never == nil {
38 38
 		t.Errorf("Expected never, got %#v", actual.DesiredState.Manifest.RestartPolicy)
39 39
 	}
40
-	if len(container.Env) != 7 {
41
-		t.Fatalf("Expected 7 elements in Env table, got %d", len(container.Env))
40
+	if len(container.Env) != 1 {
41
+		t.Fatalf("Expected 1 elements in Env table, got %d", len(container.Env))
42 42
 	}
43 43
 	buildJson, _ := json.Marshal(expected)
44 44
 	errorCases := map[int][]string{
45
-		0: {"SOURCE_URI", expected.Parameters.Source.Git.URI},
46
-		1: {"SOURCE_REF", expected.Parameters.Source.Git.Ref},
47
-		2: {"SOURCE_ID", expected.Parameters.Revision.Git.Commit},
48
-		3: {"CONTEXT_DIR", expected.Parameters.Strategy.DockerStrategy.ContextDir},
49
-		4: {"BUILD_TAG", expected.Parameters.Output.ImageTag},
50
-		5: {"REGISTRY", expected.Parameters.Output.Registry},
51
-		6: {"BUILD", string(buildJson)},
45
+		0: {"BUILD", string(buildJson)},
52 46
 	}
53 47
 	for index, exp := range errorCases {
54 48
 		if e := container.Env[index]; e.Name != exp[0] || e.Value != exp[1] {
... ...
@@ -47,12 +47,6 @@ func (bs *STIBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod, er
47 47
 						Name:  "sti-build",
48 48
 						Image: bs.BuilderImage,
49 49
 						Env: []kapi.EnvVar{
50
-							{Name: "SOURCE_URI", Value: build.Parameters.Source.Git.URI},
51
-							{Name: "SOURCE_REF", Value: build.Parameters.Source.Git.Ref},
52
-							{Name: "SOURCE_ID", Value: build.Parameters.Revision.Git.Commit},
53
-							{Name: "BUILDER_IMAGE", Value: build.Parameters.Strategy.STIStrategy.BuilderImage},
54
-							{Name: "BUILD_TAG", Value: build.Parameters.Output.ImageTag},
55
-							{Name: "REGISTRY", Value: build.Parameters.Output.Registry},
56 50
 							{Name: "BUILD", Value: string(buildJson)},
57 51
 						},
58 52
 					},
... ...
@@ -68,35 +62,7 @@ func (bs *STIBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod, er
68 68
 		pod.DesiredState.Manifest.Containers[0].ImagePullPolicy = kapi.PullIfNotPresent
69 69
 	}
70 70
 
71
-	if err := bs.setupTempVolume(pod); err != nil {
72
-		return nil, err
73
-	}
74
-
75 71
 	setupDockerSocket(pod)
76 72
 	setupDockerConfig(pod)
77 73
 	return pod, nil
78 74
 }
79
-
80
-func (bs *STIBuildStrategy) setupTempVolume(pod *kapi.Pod) error {
81
-	tempDir, err := bs.TempDirectoryCreator.CreateTempDirectory()
82
-	if err != nil {
83
-		return err
84
-	}
85
-	tmpVolume := kapi.Volume{
86
-		Name: "tmp",
87
-		Source: &kapi.VolumeSource{
88
-			HostDir: &kapi.HostDir{
89
-				Path: tempDir,
90
-			},
91
-		},
92
-	}
93
-	tmpMount := kapi.VolumeMount{Name: "tmp", ReadOnly: false, MountPath: tempDir}
94
-	pod.DesiredState.Manifest.Volumes = append(pod.DesiredState.Manifest.Volumes, tmpVolume)
95
-	pod.DesiredState.Manifest.Containers[0].VolumeMounts =
96
-		append(pod.DesiredState.Manifest.Containers[0].VolumeMounts, tmpMount)
97
-	pod.DesiredState.Manifest.Containers[0].Env =
98
-		append(pod.DesiredState.Manifest.Containers[0].Env, kapi.EnvVar{
99
-			Name: "TEMP_DIR", Value: tempDir})
100
-
101
-	return nil
102
-}
... ...
@@ -44,19 +44,12 @@ func TestSTICreateBuildPod(t *testing.T) {
44 44
 	if actual.DesiredState.Manifest.RestartPolicy.Never == nil {
45 45
 		t.Errorf("Expected never, got %#v", actual.DesiredState.Manifest.RestartPolicy)
46 46
 	}
47
-	if len(container.Env) != 8 {
48
-		t.Fatalf("Expected 8 elements in Env table, got %d", len(container.Env))
47
+	if len(container.Env) != 1 {
48
+		t.Fatalf("Expected 1 elements in Env table, got %d", len(container.Env))
49 49
 	}
50 50
 	buildJson, _ := json.Marshal(expected)
51 51
 	errorCases := map[int][]string{
52
-		0: {"SOURCE_URI", expected.Parameters.Source.Git.URI},
53
-		1: {"SOURCE_REF", expected.Parameters.Source.Git.Ref},
54
-		2: {"SOURCE_ID", expected.Parameters.Revision.Git.Commit},
55
-		3: {"BUILDER_IMAGE", expected.Parameters.Strategy.STIStrategy.BuilderImage},
56
-		4: {"BUILD_TAG", expected.Parameters.Output.ImageTag},
57
-		5: {"REGISTRY", expected.Parameters.Output.Registry},
58
-		6: {"BUILD", string(buildJson)},
59
-		7: {"TEMP_DIR", "test_temp"},
52
+		0: {"BUILD", string(buildJson)},
60 53
 	}
61 54
 	for index, exp := range errorCases {
62 55
 		if e := container.Env[index]; e.Name != exp[0] || e.Value != exp[1] {