Browse code

Allow docker type build from a repo with additional directory

Maciej Szulik authored on 2014/10/09 22:56:22
Showing 26 changed files
... ...
@@ -5,7 +5,6 @@
5 5
     "input": {
6 6
         "imageTag": "test/dockerBuild",
7 7
         "sourceURI": "github.com/test/dockerBuild",
8
-        "type": "docker"
9 8
     },
10 9
     "kind": "Build",
11 10
     "podID": "build-sti-5065bcf2-3d4d-11e4-a95b-0800279696e1",
... ...
@@ -2,11 +2,12 @@
2 2
     "apiVersion": "v1beta1",
3 3
     "creationTimestamp": "2014-09-16T19:33:36Z",
4 4
     "desiredInput": {
5
-        "builderImage": "openshift/ruby-19-centos",
6 5
         "imageTag": "test-ruby-app",
7 6
         "registry": "localhost:5000",
8 7
         "sourceURI": "git://github.com/pmorie/simple-ruby",
9
-        "type": "sti"
8
+        "stiInput": {
9
+            "builderImage": "openshift/ruby-19-centos"
10
+        }
10 11
     },
11 12
     "id": "5a6a5b9c-3dd8-11e4-9d75-0800279696e1",
12 13
     "kind": "BuildConfig",
... ...
@@ -8,7 +8,6 @@
8 8
             "input": {
9 9
                 "imageTag": "test/dockerbuild",
10 10
                 "sourceURI": "git://github.com/test/dockerbuild",
11
-                "type": "docker"
12 11
             },
13 12
             "podID": "build-sti-5065bcf2-3d4d-11e4-a95b-0800279696e1",
14 13
             "resourceVersion": 114,
... ...
@@ -2,7 +2,6 @@
2 2
 	"kind": "Build",
3 3
 	"apiVersion": "v1beta1",
4 4
 	"input": {
5
-		"type": "docker",
6 5
 		"sourceURI": "github.com/test/dockerBuild",
7 6
 		"imageTag": "test/dockerBuild"
8 7
 	}
... ...
@@ -1,10 +1,11 @@
1 1
 {
2
-	"kind": "BuildConfig",
3
-	"apiVersion": "v1beta1",
4
-	"desiredInput": {
5
-		"type": "sti",
6
-		"sourceURI": "git://github.com/pmorie/simple-ruby",
7
-		"builderImage": "openshift/ruby-19-centos",
8
-		"imageTag": "test-ruby-app"
9
-	}
2
+    "kind": "BuildConfig",
3
+    "apiVersion": "v1beta1",
4
+    "desiredInput": {
5
+        "sourceURI": "git://github.com/pmorie/simple-ruby",
6
+        "imageTag": "test-ruby-app",
7
+        "stiInput": {
8
+            "builderImage": "openshift/ruby-19-centos",
9
+        }
10
+    }
10 11
 }
... ...
@@ -1,4 +1,4 @@
1
-<!DOCTYPE HTML><html><head><title>OpenShift 3 API documentation</title><meta http-equiv=X-UA-Compatible content="IE=edge"><meta http-equiv=Content-Type content="text/html; charset=utf-8"><meta name=generator content="https://github.com/kevinrenskers/raml2html 1.0.3"><link rel=stylesheet href=http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css><link rel=stylesheet href=http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/styles/default.min.css><script type=text/javascript src=http://code.jquery.com/jquery-1.11.0.min.js></script><script type=text/javascript src=http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js></script><script type=text/javascript src=http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/highlight.min.js></script><script type=text/javascript>
1
+<!DOCTYPE HTML><html><head><title>OpenShift 3 API documentation</title><meta http-equiv=X-UA-Compatible content="IE=edge"><meta http-equiv=Content-Type content="text/html; charset=utf-8"><meta name=generator content="https://github.com/kevinrenskers/raml2html 1.0.4"><link rel=stylesheet href=http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css><link rel=stylesheet href=http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/styles/default.min.css><script type=text/javascript src=http://code.jquery.com/jquery-1.11.0.min.js></script><script type=text/javascript src=http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js></script><script type=text/javascript src=http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/highlight.min.js></script><script type=text/javascript>
2 2
         $(document).ready(function() {
3 3
             $('.page-header pre code, .top-resource-description pre code').each(function(i, block) {
4 4
                 hljs.highlightBlock(block);
... ...
@@ -176,24 +176,26 @@
176 176
     "status": "success"
177 177
 }
178 178
 </code></pre></div></div></div></div></div></div></div></div></div></div><div class="panel panel-default"><div class=panel-heading><h3 id=_buildConfigHooks__buildID___secret___plugin_ class=panel-title>/buildConfigHooks/{buildID}/{secret}/{plugin}</h3></div><div class=panel-body><div class=panel-group><div class="panel panel-white"><div class=panel-heading><h4 class=panel-title><a class=collapsed data-toggle=collapse href=#panel__buildConfigHooks__buildID___secret___plugin_><span class=parent></span>/buildConfigHooks/{buildID}/{secret}/{plugin}</a> <span class=methods><a href=# data-toggle=modal data-target=#_buildConfigHooks__buildID___secret___plugin__post><span class="badge badge_post">post</span></a></span></h4></div><div id=panel__buildConfigHooks__buildID___secret___plugin_ class="panel-collapse collapse"><div class=panel-body><div class=list-group><div data-toggle=modal data-target=#_buildConfigHooks__buildID___secret___plugin__post class=list-group-item><span class="badge badge_post">post</span><div class=method_description><p>Webhook on push event from external repository.</p><p>buildID specifies which build to trigger, whereas plugin defines source of the request, this might be github, bitbucket or others.</p></div><div class=clearfix></div></div></div></div></div><div class="modal fade" tabindex=0 id=_buildConfigHooks__buildID___secret___plugin__post><div class=modal-dialog><div class=modal-content><div class=modal-header><button type=button class=close data-dismiss=modal aria-hidden=true>&times;</button><h4 class=modal-title id=myModalLabel><span class="badge badge_post">post</span> <span class=parent></span>/buildConfigHooks/{buildID}/{secret}/{plugin}</h4></div><div class=modal-body><div class="alert alert-info"><p>Webhook on push event from external repository.</p><p>buildID specifies which build to trigger, whereas plugin defines source of the request, this might be github, bitbucket or others.</p></div><ul class="nav nav-tabs"><li class=active><a href=#_buildConfigHooks__buildID___secret___plugin__post_request data-toggle=tab>Request</a></li><li><a href=#_buildConfigHooks__buildID___secret___plugin__post_response data-toggle=tab>Response</a></li></ul><div class=tab-content><div class="tab-pane active" id=_buildConfigHooks__buildID___secret___plugin__post_request><h3>URI Parameters</h3><ul><li><strong>buildID</strong>: <em>required (string)</em></li><li><strong>secret</strong>: <em>required (string)</em></li><li><strong>plugin</strong>: <em>required (string)</em></li></ul></div><div class=tab-pane id=_buildConfigHooks__buildID___secret___plugin__post_response><h2>HTTP status code <a href=http://httpstatus.es/204 target=_blank>204</a></h2><p>No content</p></div></div></div></div></div></div></div></div></div></div><div class="panel panel-default"><div class=panel-heading><h3 id=_buildConfigs class=panel-title>/buildConfigs</h3></div><div class=panel-body><div class=panel-group><div class="panel panel-white"><div class=panel-heading><h4 class=panel-title><a class=collapsed data-toggle=collapse href=#panel__buildConfigs><span class=parent></span>/buildConfigs</a> <span class=methods><a href=# data-toggle=modal data-target=#_buildConfigs_get><span class="badge badge_get">get</span></a> <a href=# data-toggle=modal data-target=#_buildConfigs_post><span class="badge badge_post">post</span></a></span></h4></div><div id=panel__buildConfigs class="panel-collapse collapse"><div class=panel-body><div class=list-group><div data-toggle=modal data-target=#_buildConfigs_get class=list-group-item><span class="badge badge_get">get</span><div class=method_description><p>List all BuildConfigs.</p><p>BuildConfig contains the inputs needed to produce a new deployable image.</p></div><div class=clearfix></div></div><div data-toggle=modal data-target=#_buildConfigs_post class=list-group-item><span class="badge badge_post">post</span><div class=method_description><p>Create a new build.</p></div><div class=clearfix></div></div></div></div></div><div class="modal fade" tabindex=0 id=_buildConfigs_get><div class=modal-dialog><div class=modal-content><div class=modal-header><button type=button class=close data-dismiss=modal aria-hidden=true>&times;</button><h4 class=modal-title id=myModalLabel><span class="badge badge_get">get</span> <span class=parent></span>/buildConfigs</h4></div><div class=modal-body><div class="alert alert-info"><p>List all BuildConfigs.</p><p>BuildConfig contains the inputs needed to produce a new deployable image.</p></div><ul class="nav nav-tabs"><li class=active><a href=#_buildConfigs_get_request data-toggle=tab>Request</a></li><li><a href=#_buildConfigs_get_response data-toggle=tab>Response</a></li></ul><div class=tab-content><div class="tab-pane active" id=_buildConfigs_get_request></div><div class=tab-pane id=_buildConfigs_get_response><h2>HTTP status code <a href=http://httpstatus.es/200 target=_blank>200</a></h2><h3>Body</h3><p><strong>Type: application/json</strong></p></div></div></div></div></div></div><div class="modal fade" tabindex=0 id=_buildConfigs_post><div class=modal-dialog><div class=modal-content><div class=modal-header><button type=button class=close data-dismiss=modal aria-hidden=true>&times;</button><h4 class=modal-title id=myModalLabel><span class="badge badge_post">post</span> <span class=parent></span>/buildConfigs</h4></div><div class=modal-body><div class="alert alert-info"><p>Create a new build.</p></div><ul class="nav nav-tabs"><li class=active><a href=#_buildConfigs_post_request data-toggle=tab>Request</a></li></ul><div class=tab-content><div class="tab-pane active" id=_buildConfigs_post_request><h3>Body</h3><p><strong>Type: application/json</strong></p><p><strong>Example</strong>:</p><pre><code>{
179
-	"kind": "BuildConfig",
180
-	"apiVersion": "v1beta1",
181
-	"desiredInput": {
182
-		"type": "sti",
183
-		"sourceURI": "git://github.com/pmorie/simple-ruby",
184
-		"builderImage": "openshift/ruby-19-centos",
185
-		"imageTag": "test-ruby-app"
186
-	}
179
+    "kind": "BuildConfig",
180
+    "apiVersion": "v1beta1",
181
+    "desiredInput": {
182
+        "sourceURI": "git://github.com/pmorie/simple-ruby",
183
+        "imageTag": "test-ruby-app",
184
+        "stiInput": {
185
+            "builderImage": "openshift/ruby-19-centos",
186
+        }
187
+    }
187 188
 }
188 189
 </code></pre></div></div></div></div></div></div></div><div class="panel panel-white"><div class=panel-heading><h4 class=panel-title><a class=collapsed data-toggle=collapse href=#panel__buildConfigs__configID_><span class=parent>/buildConfigs</span>/{configID}</a> <span class=methods><a href=# data-toggle=modal data-target=#_buildConfigs__configID__get><span class="badge badge_get">get</span></a> <a href=# data-toggle=modal data-target=#_buildConfigs__configID__put><span class="badge badge_put">put</span></a> <a href=# data-toggle=modal data-target=#_buildConfigs__configID__delete><span class="badge badge_delete">delete</span></a></span></h4></div><div id=panel__buildConfigs__configID_ class="panel-collapse collapse"><div class=panel-body><div class=list-group><div data-toggle=modal data-target=#_buildConfigs__configID__get class=list-group-item><span class="badge badge_get">get</span><div class=method_description><p>Get a specific build configuration.</p></div><div class=clearfix></div></div><div data-toggle=modal data-target=#_buildConfigs__configID__put class=list-group-item><span class="badge badge_put">put</span><div class=method_description><p>Update a specific build configuration.</p></div><div class=clearfix></div></div><div data-toggle=modal data-target=#_buildConfigs__configID__delete class=list-group-item><span class="badge badge_delete">delete</span><div class=method_description><p>Delete a specific build configuration.</p></div><div class=clearfix></div></div></div></div></div><div class="modal fade" tabindex=0 id=_buildConfigs__configID__get><div class=modal-dialog><div class=modal-content><div class=modal-header><button type=button class=close data-dismiss=modal aria-hidden=true>&times;</button><h4 class=modal-title id=myModalLabel><span class="badge badge_get">get</span> <span class=parent>/buildConfigs</span>/{configID}</h4></div><div class=modal-body><div class="alert alert-info"><p>Get a specific build configuration.</p></div><ul class="nav nav-tabs"><li class=active><a href=#_buildConfigs__configID__get_request data-toggle=tab>Request</a></li><li><a href=#_buildConfigs__configID__get_response data-toggle=tab>Response</a></li></ul><div class=tab-content><div class="tab-pane active" id=_buildConfigs__configID__get_request><h3>URI Parameters</h3><ul><li><strong>configID</strong>: <em>required (string)</em></li></ul></div><div class=tab-pane id=_buildConfigs__configID__get_response><h2>HTTP status code <a href=http://httpstatus.es/200 target=_blank>200</a></h2><h3>Body</h3><p><strong>Type: application/json</strong></p><p><strong>Example</strong>:</p><pre><code>{
189 190
     "apiVersion": "v1beta1",
190 191
     "creationTimestamp": "2014-09-16T19:33:36Z",
191 192
     "desiredInput": {
192
-        "builderImage": "openshift/ruby-19-centos",
193 193
         "imageTag": "test-ruby-app",
194 194
         "registry": "localhost:5000",
195 195
         "sourceURI": "git://github.com/pmorie/simple-ruby",
196
-        "type": "sti"
196
+        "stiInput": {
197
+            "builderImage": "openshift/ruby-19-centos"
198
+        }
197 199
     },
198 200
     "id": "5a6a5b9c-3dd8-11e4-9d75-0800279696e1",
199 201
     "kind": "BuildConfig",
... ...
@@ -203,11 +205,12 @@
203 203
     "apiVersion": "v1beta1",
204 204
     "creationTimestamp": "2014-09-16T19:33:36Z",
205 205
     "desiredInput": {
206
-        "builderImage": "openshift/ruby-19-centos",
207 206
         "imageTag": "test-ruby-app",
208 207
         "registry": "localhost:5000",
209 208
         "sourceURI": "git://github.com/pmorie/simple-ruby",
210
-        "type": "sti"
209
+        "stiInput": {
210
+            "builderImage": "openshift/ruby-19-centos"
211
+        }
211 212
     },
212 213
     "id": "5a6a5b9c-3dd8-11e4-9d75-0800279696e1",
213 214
     "kind": "BuildConfig",
... ...
@@ -229,7 +232,6 @@
229 229
             "input": {
230 230
                 "imageTag": "test/dockerbuild",
231 231
                 "sourceURI": "git://github.com/test/dockerbuild",
232
-                "type": "docker"
233 232
             },
234 233
             "podID": "build-sti-5065bcf2-3d4d-11e4-a95b-0800279696e1",
235 234
             "resourceVersion": 114,
... ...
@@ -243,7 +245,6 @@
243 243
 	"kind": "Build",
244 244
 	"apiVersion": "v1beta1",
245 245
 	"input": {
246
-		"type": "docker",
247 246
 		"sourceURI": "github.com/test/dockerBuild",
248 247
 		"imageTag": "test/dockerBuild"
249 248
 	}
... ...
@@ -255,7 +256,6 @@
255 255
     "input": {
256 256
         "imageTag": "test/dockerBuild",
257 257
         "sourceURI": "github.com/test/dockerBuild",
258
-        "type": "docker"
259 258
     },
260 259
     "kind": "Build",
261 260
     "podID": "build-sti-5065bcf2-3d4d-11e4-a95b-0800279696e1",
... ...
@@ -269,7 +269,6 @@
269 269
     "input": {
270 270
         "imageTag": "test/dockerBuild",
271 271
         "sourceURI": "github.com/test/dockerBuild",
272
-        "type": "docker"
273 272
     },
274 273
     "kind": "Build",
275 274
     "podID": "build-sti-5065bcf2-3d4d-11e4-a95b-0800279696e1",
... ...
@@ -4,7 +4,6 @@
4 4
     "apiVersion": "v1beta1",
5 5
     "desiredInput":
6 6
     {
7
-        "type":      "docker",
8 7
         "sourceURI": "git://github.com/openshift/ruby-hello-world.git",
9 8
         "imageTag":  "openshift/origin-ruby-sample",
10 9
         "registry":  "127.0.0.1:5001"
... ...
@@ -4,30 +4,53 @@ IFS=$'\n\t'
4 4
 
5 5
 DOCKER_SOCKET=/var/run/docker.sock
6 6
 
7
-if [ ! -e $DOCKER_SOCKET ]; then
8
-  echo "Docker socket missing at $DOCKER_SOCKET"
7
+if [ ! -e "${DOCKER_SOCKET}" ]; then
8
+  echo "Docker socket missing at ${DOCKER_SOCKET}"
9 9
   exit 1
10 10
 fi
11 11
 
12
-TAG=$BUILD_TAG
13
-if [ -n "$DOCKER_REGISTRY" ]; then
14
-  TAG=$DOCKER_REGISTRY/$BUILD_TAG
12
+TAG="${BUILD_TAG}"
13
+if [ -n "${REGISTRY}" ]; then
14
+  TAG="${REGISTRY}/${BUILD_TAG}"
15 15
 fi
16 16
 
17
-if [[ $DOCKER_CONTEXT_URL != "git://"* ]] && [[ $DOCKER_CONTEXT_URL != "git@"* ]]; then
18
-  URL=$DOCKER_CONTEXT_URL
19
-  if [[ $URL != "http://"* ]] && [[ $URL != "https://"* ]]; then
20
-    URL="https://"$URL
17
+if [[ "${SOURCE_URI}" != "git://"* ]] && [[ "${SOURCE_URI}" != "git@"* ]]; then
18
+  URL="${SOURCE_URI}"
19
+  if [[ "${URL}" != "http://"* ]] && [[ "${URL}" != "https://"* ]]; then
20
+    URL="https://${URL}"
21 21
   fi
22 22
   curl --head --silent --fail --location --max-time 16 $URL > /dev/null
23 23
   if [ $? != 0 ]; then
24
-    echo "Not found: "$DOCKER_CONTEXT_URL
24
+    echo "Not found: ${SOURCE_URI}"
25 25
     exit 1
26 26
   fi
27 27
 fi
28 28
 
29
-docker build --rm -t $TAG $DOCKER_CONTEXT_URL
29
+if [ -n "${SOURCE_REF}" ] || [ -n "${CONTEXT_DIR}" ]; then
30
+  BUILD_DIR=$(mktemp --directory --suffix=docker-build)
31
+  git clone --recursive "${SOURCE_URI}" "${BUILD_DIR}"
32
+  if [ $? != 0 ]; then
33
+    echo "Error trying to fetch git source: ${SOURCE_URI}"
34
+    exit 1
35
+  fi
36
+  if [ -n "${SOURCE_REF}" ]; then
37
+    pushd "${BUILD_DIR}"
38
+    git checkout "${SOURCE_REF}"
39
+    if [ $? != 0 ]; then
40
+      echo "Error trying to checkout branch: ${SOURCE_REF}"
41
+      exit 1
42
+    fi
43
+    popd
44
+  fi
45
+  if [ -n "${CONTEXT_DIR}" ] && [ ! -d "${BUILD_DIR}/${CONTEXT_DIR}" ]; then
46
+    echo "ContextDir does not exist in the repository: ${CONTEXT_DIR}"
47
+    exit 1
48
+  fi
49
+  docker build --rm -t "${TAG}" "${BUILD_DIR}/${CONTEXT_DIR}"
50
+else
51
+  docker build --rm -t "${TAG}" "${SOURCE_URI}"
52
+fi
30 53
 
31
-if [ -n "$DOCKER_REGISTRY" ] || [ -s "/root/.dockercfg" ]; then
32
-  docker push $TAG
54
+if [ -n "${REGISTRY}" ] || [ -s "/root/.dockercfg" ]; then
55
+  docker push "${TAG}"
33 56
 fi
... ...
@@ -2,24 +2,24 @@
2 2
 
3 3
 DOCKER_SOCKET=/var/run/docker.sock
4 4
 
5
-if [ ! -e $DOCKER_SOCKET ]; then
5
+if [ ! -e "${DOCKER_SOCKET}" ]; then
6 6
   echo "Docker socket missing at $DOCKER_SOCKET"
7 7
   exit 1
8 8
 fi
9 9
 
10
-TAG=$BUILD_TAG
11
-if [ -n "$DOCKER_REGISTRY" ]; then
12
-  TAG=$DOCKER_REGISTRY/$BUILD_TAG
10
+TAG="${BUILD_TAG}"
11
+if [ -n "$REGISTRY" ]; then
12
+  TAG=$REGISTRY/$BUILD_TAG
13 13
 fi
14 14
 
15 15
 REF_OPTION=""
16
-if [ -n "$SOURCE_REF" ]; then
17
-  REF_OPTION="--ref $SOURCE_REF"
16
+if [ -n "${SOURCE_REF}" ]; then
17
+  REF_OPTION="--ref ${SOURCE_REF}"
18 18
 fi
19 19
 
20
-BUILD_TEMP_DIR=${TEMP_DIR-$TMPDIR}
21
-TMPDIR=$BUILD_TEMP_DIR sti build $SOURCE_URI $BUILDER_IMAGE $TAG $REF_OPTION
20
+BUILD_TEMP_DIR="${TEMP_DIR-$TMPDIR}"
21
+TMPDIR="${BUILD_TEMP_DIR}" sti build "${SOURCE_URI}" "${BUILDER_IMAGE}" "${TAG}" "${REF_OPTION}"
22 22
 
23
-if [ -n "$DOCKER_REGISTRY" ] || [ -s "/root/.dockercfg" ]; then
24
-  docker push $TAG
23
+if [ -n "${REGISTRY}" ] || [ -s "/root/.dockercfg" ]; then
24
+  docker push "${TAG}"
25 25
 fi
... ...
@@ -20,11 +20,8 @@ type Build struct {
20 20
 	PodID string `json:"podID,omitempty" yaml:"podID,omitempty"`
21 21
 }
22 22
 
23
-// BuildInput defines the type of build and input parameters for a given build
23
+// BuildInput defines input parameters for a given build
24 24
 type BuildInput struct {
25
-	// Type is the type of build to execute
26
-	Type BuildType `json:"type,omitempty" yaml:"type,omitempty"`
27
-
28 25
 	// SourceURI points to the source that will be built. The structure of the source
29 26
 	// will depend on the type of build to run
30 27
 	SourceURI string `json:"sourceURI,omitempty" yaml:"sourceURI,omitempty"`
... ...
@@ -36,9 +33,25 @@ type BuildInput struct {
36 36
 	ImageTag string `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
37 37
 
38 38
 	// Registry to push the result image to
39
-	Registry string `json:"registry",omitempty" yaml:"registry,omitempty"`
39
+	Registry string `json:"registry,omitempty" yaml:"registry,omitempty"`
40
+
41
+	// DockerBuild represents build parameters specific to docker build
42
+	DockerInput *DockerBuildInput `json:"dockerInput,omitempty" yaml:"dockerInput,omitempty"`
43
+
44
+	// STIBuild represents build parameters specific to STI build
45
+	STIInput *STIBuildInput `json:"stiInput,omitempty" yaml:"stiInput,omitempty"`
46
+}
47
+
48
+// DockerBuildInput defines input parameters specific to docker build
49
+type DockerBuildInput struct {
50
+	// ContextDir is a directory inside the SourceURI structure which should be used as a docker
51
+	// context when building
52
+	ContextDir string `json:"contextDir,omitempty" yaml:"contextDir,omitempty"`
53
+}
40 54
 
41
-	// BuilderImage is the image used to execute the build when running STI builds
55
+// STIBuildInput defines input parameters specific to sti build
56
+type STIBuildInput struct {
57
+	// BuilderImage is the image used to execute the build
42 58
 	BuilderImage string `json:"builderImage,omitempty" yaml:"builderImage,omitempty"`
43 59
 }
44 60
 
... ...
@@ -20,11 +20,8 @@ type Build struct {
20 20
 	PodID string `json:"podID,omitempty" yaml:"podID,omitempty"`
21 21
 }
22 22
 
23
-// BuildInput defines the type of build and input parameters for a given build
23
+// BuildInput defines input parameters for a given build
24 24
 type BuildInput struct {
25
-	// Type is the type of build to execute
26
-	Type BuildType `json:"type,omitempty" yaml:"type,omitempty"`
27
-
28 25
 	// SourceURI points to the source that will be built. The structure of the source
29 26
 	// will depend on the type of build to run
30 27
 	SourceURI string `json:"sourceURI,omitempty" yaml:"sourceURI,omitempty"`
... ...
@@ -36,9 +33,25 @@ type BuildInput struct {
36 36
 	ImageTag string `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
37 37
 
38 38
 	// Registry to push the result image to
39
-	Registry string `json:"registry",omitempty" yaml:"registry,omitempty"`
39
+	Registry string `json:"registry,omitempty" yaml:"registry,omitempty"`
40
+
41
+	// DockerBuild represents build parameters specific to docker build
42
+	DockerInput *DockerBuildInput `json:"dockerInput,omitempty" yaml:"dockerInput,omitempty"`
43
+
44
+	// STIBuild represents build parameters specific to STI build
45
+	STIInput *STIBuildInput `json:"stiInput,omitempty" yaml:"stiInput,omitempty"`
46
+}
47
+
48
+// DockerBuildInput defines input parameters specific to docker build
49
+type DockerBuildInput struct {
50
+	// ContextDir is a directory inside the SourceURI structure which should be used as a docker
51
+	// context when building
52
+	ContextDir string `json:"contextDir,omitempty" yaml:"contextDir,omitempty"`
53
+}
40 54
 
41
-	// BuilderImage is the image used to execute the build when running STI builds
55
+// STIBuildInput defines input parameters specific to sti build
56
+type STIBuildInput struct {
57
+	// BuilderImage is the image used to execute the build
42 58
 	BuilderImage string `json:"builderImage,omitempty" yaml:"builderImage,omitempty"`
43 59
 }
44 60
 
... ...
@@ -37,14 +37,16 @@ func validateBuildInput(input *api.BuildInput) errs.ErrorList {
37 37
 	if len(input.ImageTag) == 0 {
38 38
 		allErrs = append(allErrs, errs.NewFieldRequired("imageTag", input.ImageTag))
39 39
 	}
40
-	if input.Type == api.STIBuildType {
41
-		if len(input.BuilderImage) == 0 {
42
-			allErrs = append(allErrs, errs.NewFieldRequired("builderImage", input.BuilderImage))
43
-		}
44
-	} else {
45
-		if len(input.BuilderImage) != 0 {
46
-			allErrs = append(allErrs, errs.NewFieldInvalid("builderImage", input.BuilderImage))
47
-		}
40
+	if input.STIInput != nil {
41
+		allErrs = append(allErrs, validateSTIBuild(input.STIInput).Prefix("stiBuild")...)
42
+	}
43
+	return allErrs
44
+}
45
+
46
+func validateSTIBuild(sti *api.STIBuildInput) errs.ErrorList {
47
+	allErrs := errs.ErrorList{}
48
+	if len(sti.BuilderImage) == 0 {
49
+		allErrs = append(allErrs, errs.NewFieldRequired("builderImage", sti.BuilderImage))
48 50
 	}
49 51
 	return allErrs
50 52
 }
... ...
@@ -4,14 +4,14 @@ import (
4 4
 	"testing"
5 5
 
6 6
 	kubeapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
7
+	errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
7 8
 	"github.com/openshift/origin/pkg/build/api"
8 9
 )
9 10
 
10 11
 func TestBuildValdationSuccess(t *testing.T) {
11 12
 	build := &api.Build{
12
-		JSONBase: kubeapi.JSONBase{ID: "buildId"},
13
+		JSONBase: kubeapi.JSONBase{ID: "buildID"},
13 14
 		Input: api.BuildInput{
14
-			Type:      api.DockerBuildType,
15 15
 			SourceURI: "http://github.com/my/repository",
16 16
 			ImageTag:  "repository/data",
17 17
 		},
... ...
@@ -26,7 +26,6 @@ func TestBuildValidationFailure(t *testing.T) {
26 26
 	build := &api.Build{
27 27
 		JSONBase: kubeapi.JSONBase{ID: ""},
28 28
 		Input: api.BuildInput{
29
-			Type:      api.DockerBuildType,
30 29
 			SourceURI: "http://github.com/my/repository",
31 30
 			ImageTag:  "repository/data",
32 31
 		},
... ...
@@ -39,9 +38,8 @@ func TestBuildValidationFailure(t *testing.T) {
39 39
 
40 40
 func TestBuildConfigValidationSuccess(t *testing.T) {
41 41
 	buildConfig := &api.BuildConfig{
42
-		JSONBase: kubeapi.JSONBase{ID: "configId"},
42
+		JSONBase: kubeapi.JSONBase{ID: "configID"},
43 43
 		DesiredInput: api.BuildInput{
44
-			Type:      api.DockerBuildType,
45 44
 			SourceURI: "http://github.com/my/repository",
46 45
 			ImageTag:  "repository/data",
47 46
 		},
... ...
@@ -55,7 +53,6 @@ func TestBuildConfigValidationFailure(t *testing.T) {
55 55
 	buildConfig := &api.BuildConfig{
56 56
 		JSONBase: kubeapi.JSONBase{ID: ""},
57 57
 		DesiredInput: api.BuildInput{
58
-			Type:      api.DockerBuildType,
59 58
 			SourceURI: "http://github.com/my/repository",
60 59
 			ImageTag:  "repository/data",
61 60
 		},
... ...
@@ -67,32 +64,24 @@ func TestBuildConfigValidationFailure(t *testing.T) {
67 67
 
68 68
 func TestValidateBuildInput(t *testing.T) {
69 69
 	errorCases := map[string]*api.BuildInput{
70
-		"No source URI": &api.BuildInput{
71
-			Type:      api.DockerBuildType,
70
+		string(errs.ValidationErrorTypeRequired) + "sourceURI": &api.BuildInput{
72 71
 			SourceURI: "",
73 72
 			ImageTag:  "repository/data",
74 73
 		},
75
-		"Invalid source URI": &api.BuildInput{
76
-			Type:      api.DockerBuildType,
74
+		string(errs.ValidationErrorTypeInvalid) + "sourceURI": &api.BuildInput{
77 75
 			SourceURI: "::",
78 76
 			ImageTag:  "repository/data",
79 77
 		},
80
-		"No image tag": &api.BuildInput{
81
-			Type:      api.DockerBuildType,
78
+		string(errs.ValidationErrorTypeRequired) + "imageTag": &api.BuildInput{
82 79
 			SourceURI: "http://github.com/test/uri",
83 80
 			ImageTag:  "",
84 81
 		},
85
-		"No builder image with STIBuildType": &api.BuildInput{
86
-			Type:         api.STIBuildType,
87
-			SourceURI:    "http://github.com/test/uri",
88
-			ImageTag:     "repository/data",
89
-			BuilderImage: "",
90
-		},
91
-		"Builder image with DockerBuildType": &api.BuildInput{
92
-			Type:         api.DockerBuildType,
93
-			SourceURI:    "http://github.com/test/uri",
94
-			ImageTag:     "repository/data",
95
-			BuilderImage: "builder/image",
82
+		string(errs.ValidationErrorTypeRequired) + "stiBuild.builderImage": &api.BuildInput{
83
+			SourceURI: "http://github.com/test/uri",
84
+			ImageTag:  "repository/data",
85
+			STIInput: &api.STIBuildInput{
86
+				BuilderImage: "",
87
+			},
96 88
 		},
97 89
 	}
98 90
 
... ...
@@ -101,6 +90,10 @@ func TestValidateBuildInput(t *testing.T) {
101 101
 		if len(errors) != 1 {
102 102
 			t.Errorf("%s: Unexpected validation result: %v", desc, errors)
103 103
 		}
104
-		// TODO: Verify we got the right type of validation error.
104
+		err := errors[0].(errs.ValidationError)
105
+		errDesc := string(err.Type) + err.Field
106
+		if desc != errDesc {
107
+			t.Errorf("Unexpected validation result for %s: expected %s, got %s", err.Field, desc, errDesc)
108
+		}
105 109
 	}
106 110
 }
... ...
@@ -94,16 +94,20 @@ func hasTimeoutElapsed(build *api.Build, timeout int) bool {
94 94
 func (bc *BuildController) synchronize(ctx kapi.Context, build *api.Build) (api.BuildStatus, error) {
95 95
 	glog.Infof("Syncing build %s", build.ID)
96 96
 
97
+	buildType := api.DockerBuildType
98
+	if build.Input.STIInput != nil {
99
+		buildType = api.STIBuildType
100
+	}
101
+
97 102
 	switch build.Status {
98 103
 	case api.BuildNew:
99
-		build.PodID = "build-" + string(build.Input.Type) + "-" + build.ID // TODO: better naming
104
+		build.PodID = fmt.Sprintf("build-%v-%s", buildType, build.ID) // TODO: better naming
100 105
 		return api.BuildPending, nil
101 106
 	case api.BuildPending:
102
-		buildStrategy, ok := bc.buildStrategies[build.Input.Type]
107
+		buildStrategy, ok := bc.buildStrategies[buildType]
103 108
 		if !ok {
104
-			return api.BuildError, fmt.Errorf("No build type for %s", build.Input.Type)
109
+			return api.BuildError, fmt.Errorf("No build strategy for build %s", buildType)
105 110
 		}
106
-
107 111
 		podSpec, err := buildStrategy.CreateBuildPod(build)
108 112
 		if err != nil {
109 113
 			glog.Errorf("Unable to create build pod: %v", err)
... ...
@@ -37,6 +37,12 @@ func (_ *okStrategy) CreateBuildPod(build *api.Build) (*kapi.Pod, error) {
37 37
 	return &kapi.Pod{}, nil
38 38
 }
39 39
 
40
+type errStrategy struct{}
41
+
42
+func (_ *errStrategy) CreateBuildPod(build *api.Build) (*kapi.Pod, error) {
43
+	return nil, errors.New("CreateBuildPod error!")
44
+}
45
+
40 46
 type errKubeClient struct {
41 47
 	kubeclient.Fake
42 48
 }
... ...
@@ -46,7 +52,15 @@ func (_ *errKubeClient) CreatePod(ctx kapi.Context, pod *kapi.Pod) (*kapi.Pod, e
46 46
 }
47 47
 
48 48
 func (_ *errKubeClient) GetPod(ctx kapi.Context, name string) (*kapi.Pod, error) {
49
-	return &kapi.Pod{}, errors.New("GedPod error!")
49
+	return &kapi.Pod{}, errors.New("GetPod error!")
50
+}
51
+
52
+type errExistsKubeClient struct {
53
+	kubeclient.Fake
54
+}
55
+
56
+func (_ *errExistsKubeClient) CreatePod(ctx kapi.Context, pod *kapi.Pod) (*kapi.Pod, error) {
57
+	return &kapi.Pod{}, errors.New("CreatePod already exists error!")
50 58
 }
51 59
 
52 60
 type okKubeClient struct {
... ...
@@ -59,6 +73,25 @@ func (_ *okKubeClient) GetPod(ctx kapi.Context, name string) (*kapi.Pod, error)
59 59
 	}, nil
60 60
 }
61 61
 
62
+type termKubeClient struct {
63
+	kubeclient.Fake
64
+}
65
+
66
+func (_ *termKubeClient) GetPod(ctx kapi.Context, name string) (*kapi.Pod, error) {
67
+	return &kapi.Pod{
68
+		CurrentState: kapi.PodState{
69
+			Status: kapi.PodTerminated,
70
+			Info: kapi.PodInfo{
71
+				"container1": kapi.ContainerStatus{
72
+					State: kapi.ContainerState{
73
+						Termination: &kapi.ContainerStateTerminated{ExitCode: 1},
74
+					},
75
+				},
76
+			},
77
+		},
78
+	}, nil
79
+}
80
+
62 81
 func TestSynchronizeBuildNew(t *testing.T) {
63 82
 	ctrl, build, ctx := setup()
64 83
 	build.Status = api.BuildNew
... ...
@@ -71,16 +104,16 @@ func TestSynchronizeBuildNew(t *testing.T) {
71 71
 	}
72 72
 }
73 73
 
74
-func TestSynchronizeBuildPendingUnknownStrategy(t *testing.T) {
74
+func TestSynchronizeBuildPendingFailedCreateBuildPod(t *testing.T) {
75 75
 	ctrl, build, ctx := setup()
76
+	ctrl.buildStrategies[api.DockerBuildType] = &errStrategy{}
76 77
 	build.Status = api.BuildPending
77
-	build.Input.Type = "unknownStrategy"
78 78
 	status, err := ctrl.synchronize(ctx, build)
79 79
 	if err == nil {
80 80
 		t.Error("Expected error, but none happened!")
81 81
 	}
82
-	if status != api.BuildError {
83
-		t.Errorf("Expected BuildError, got %s!", status)
82
+	if status != api.BuildFailed {
83
+		t.Errorf("Expected BuildFailed, got %s!", status)
84 84
 	}
85 85
 }
86 86
 
... ...
@@ -97,6 +130,20 @@ func TestSynchronizeBuildPendingFailedCreatePod(t *testing.T) {
97 97
 	}
98 98
 }
99 99
 
100
+func TestSynchronizeBuildPendingFailedCreatePodAlreadyExists(t *testing.T) {
101
+	ctrl, build, ctx := setup()
102
+	ctrl.kubeClient = &errExistsKubeClient{}
103
+	build.Status = api.BuildPending
104
+	build.CreationTimestamp.Time = time.Now()
105
+	status, err := ctrl.synchronize(ctx, build)
106
+	if err == nil {
107
+		t.Error("Expected error, but none happened!")
108
+	}
109
+	if status != api.BuildPending {
110
+		t.Errorf("Expected BuildPending, got %s!", status)
111
+	}
112
+}
113
+
100 114
 func TestSynchronizeBuildPending(t *testing.T) {
101 115
 	ctrl, build, ctx := setup()
102 116
 	build.Status = api.BuildPending
... ...
@@ -149,6 +196,20 @@ func TestSynchronizeBuildRunningPodRunning(t *testing.T) {
149 149
 	}
150 150
 }
151 151
 
152
+func TestSynchronizeBuildRunningPodTerminationExitCode(t *testing.T) {
153
+	ctrl, build, ctx := setup()
154
+	ctrl.kubeClient = &termKubeClient{}
155
+	build.Status = api.BuildRunning
156
+	build.CreationTimestamp.Time = time.Now()
157
+	status, err := ctrl.synchronize(ctx, build)
158
+	if err != nil {
159
+		t.Errorf("Unexpected error, got %s!", err.Error())
160
+	}
161
+	if status != api.BuildFailed {
162
+		t.Errorf("Expected BuildFailed, got %s!", status)
163
+	}
164
+}
165
+
152 166
 func TestSynchronizeBuildRunningPodTerminated(t *testing.T) {
153 167
 	ctrl, build, ctx := setup()
154 168
 	ctrl.kubeClient = &okKubeClient{}
... ...
@@ -159,7 +220,7 @@ func TestSynchronizeBuildRunningPodTerminated(t *testing.T) {
159 159
 		t.Errorf("Unexpected error, got %s!", err.Error())
160 160
 	}
161 161
 	if status != api.BuildComplete {
162
-		t.Errorf("Expected BuildRunning, got %s!", status)
162
+		t.Errorf("Expected BuildComplete, got %s!", status)
163 163
 	}
164 164
 }
165 165
 
... ...
@@ -214,7 +275,7 @@ func TestSynchronizeBuildUnknownStatus(t *testing.T) {
214 214
 func setup() (buildController *BuildController, build *api.Build, ctx kapi.Context) {
215 215
 	buildController = &BuildController{
216 216
 		buildStrategies: map[api.BuildType]BuildJobStrategy{
217
-			"okStrategy": &okStrategy{},
217
+			api.DockerBuildType: &okStrategy{},
218 218
 		},
219 219
 		kubeClient: &kubeclient.Fake{},
220 220
 		timeout:    1000,
... ...
@@ -224,7 +285,6 @@ func setup() (buildController *BuildController, build *api.Build, ctx kapi.Conte
224 224
 			ID: "dataBuild",
225 225
 		},
226 226
 		Input: api.BuildInput{
227
-			Type:      "okStrategy",
228 227
 			SourceURI: "http://my.build.com/the/build/Dockerfile",
229 228
 			ImageTag:  "repository/dataBuild",
230 229
 		},
... ...
@@ -333,7 +333,6 @@ func TestBuildRESTValidatesUpdate(t *testing.T) {
333 333
 		"empty ID": {
334 334
 			JSONBase: kubeapi.JSONBase{ID: ""},
335 335
 			Input: api.BuildInput{
336
-				Type:      api.DockerBuildType,
337 336
 				SourceURI: "http://my.build.com/the/build/Dockerfile",
338 337
 				ImageTag:  "repository/dataBuild",
339 338
 			},
... ...
@@ -360,7 +359,6 @@ func mockBuild() *api.Build {
360 360
 			ID: "dataBuild",
361 361
 		},
362 362
 		Input: api.BuildInput{
363
-			Type:      api.DockerBuildType,
364 363
 			SourceURI: "http://my.build.com/the/build/Dockerfile",
365 364
 			ImageTag:  "repository/dataBuild",
366 365
 		},
... ...
@@ -254,7 +254,6 @@ func mockBuildConfig() *api.BuildConfig {
254 254
 			ID: "dataBuild",
255 255
 		},
256 256
 		DesiredInput: api.BuildInput{
257
-			Type:      api.DockerBuildType,
258 257
 			SourceURI: "http://my.build.com/the/buildConfig/Dockerfile",
259 258
 			ImageTag:  "repository/dataBuild",
260 259
 		},
... ...
@@ -321,10 +320,11 @@ func TestBuildConfigRESTValidatesCreate(t *testing.T) {
321 321
 		"blank sourceURI": {
322 322
 			JSONBase: kubeapi.JSONBase{ID: "abc"},
323 323
 			DesiredInput: api.BuildInput{
324
-				SourceURI:    "",
325
-				ImageTag:     "data/image",
326
-				Type:         api.STIBuildType,
327
-				BuilderImage: "builder/image",
324
+				SourceURI: "",
325
+				ImageTag:  "data/image",
326
+				STIInput: &api.STIBuildInput{
327
+					BuilderImage: "builder/image",
328
+				},
328 329
 			},
329 330
 		},
330 331
 		"blank ImageTag": {
... ...
@@ -332,16 +332,16 @@ func TestBuildConfigRESTValidatesCreate(t *testing.T) {
332 332
 			DesiredInput: api.BuildInput{
333 333
 				SourceURI: "http://github.com/test/source",
334 334
 				ImageTag:  "",
335
-				Type:      api.DockerBuildType,
336 335
 			},
337 336
 		},
338 337
 		"blank BuilderImage": {
339 338
 			JSONBase: kubeapi.JSONBase{ID: "abc"},
340 339
 			DesiredInput: api.BuildInput{
341
-				SourceURI:    "http://github.com/test/source",
342
-				ImageTag:     "data/image",
343
-				Type:         api.STIBuildType,
344
-				BuilderImage: "",
340
+				SourceURI: "http://github.com/test/source",
341
+				ImageTag:  "data/image",
342
+				STIInput: &api.STIBuildInput{
343
+					BuilderImage: "",
344
+				},
345 345
 			},
346 346
 		},
347 347
 	}
... ...
@@ -365,16 +365,16 @@ func TestBuildRESTValidatesUpdate(t *testing.T) {
365 365
 			DesiredInput: api.BuildInput{
366 366
 				SourceURI: "http://github.com/test/source",
367 367
 				ImageTag:  "data/image",
368
-				Type:      api.DockerBuildType,
369 368
 			},
370 369
 		},
371 370
 		"blank sourceURI": {
372 371
 			JSONBase: kubeapi.JSONBase{ID: "abc"},
373 372
 			DesiredInput: api.BuildInput{
374
-				SourceURI:    "",
375
-				ImageTag:     "data/image",
376
-				Type:         api.STIBuildType,
377
-				BuilderImage: "builder/image",
373
+				SourceURI: "",
374
+				ImageTag:  "data/image",
375
+				STIInput: &api.STIBuildInput{
376
+					BuilderImage: "builder/image",
377
+				},
378 378
 			},
379 379
 		},
380 380
 		"blank ImageTag": {
... ...
@@ -382,25 +382,16 @@ func TestBuildRESTValidatesUpdate(t *testing.T) {
382 382
 			DesiredInput: api.BuildInput{
383 383
 				SourceURI: "http://github.com/test/source",
384 384
 				ImageTag:  "",
385
-				Type:      api.DockerBuildType,
386 385
 			},
387 386
 		},
388 387
 		"blank BuilderImage on STIBuildType": {
389 388
 			JSONBase: kubeapi.JSONBase{ID: "abc"},
390 389
 			DesiredInput: api.BuildInput{
391
-				SourceURI:    "http://github.com/test/source",
392
-				ImageTag:     "data/image",
393
-				Type:         api.STIBuildType,
394
-				BuilderImage: "",
395
-			},
396
-		},
397
-		"non-blank BuilderImage on DockerBuildType": {
398
-			JSONBase: kubeapi.JSONBase{ID: "abc"},
399
-			DesiredInput: api.BuildInput{
400
-				SourceURI:    "http://github.com/test/source",
401
-				ImageTag:     "data/image",
402
-				Type:         api.DockerBuildType,
403
-				BuilderImage: "builder/image",
390
+				SourceURI: "http://github.com/test/source",
391
+				ImageTag:  "data/image",
392
+				STIInput: &api.STIBuildInput{
393
+					BuilderImage: "",
394
+				},
404 395
 			},
405 396
 		},
406 397
 	}
... ...
@@ -61,7 +61,6 @@ func TestEtcdCreateBuild(t *testing.T) {
61 61
 			ID: "foo",
62 62
 		},
63 63
 		Input: api.BuildInput{
64
-			Type:      api.DockerBuildType,
65 64
 			SourceURI: "http://my.build.com/the/build/Dockerfile",
66 65
 			ImageTag:  "repository/dataBuild",
67 66
 		},
... ...
@@ -229,7 +228,6 @@ func TestEtcdCreateBuildConfig(t *testing.T) {
229 229
 			ID: "foo",
230 230
 		},
231 231
 		DesiredInput: api.BuildInput{
232
-			Type:      api.DockerBuildType,
233 232
 			SourceURI: "http://my.build.com/the/build/Dockerfile",
234 233
 			ImageTag:  "repository/dataBuild",
235 234
 		},
... ...
@@ -19,6 +19,11 @@ func NewDockerBuildStrategy(dockerBuilderImage string, useLocalImage bool) *Dock
19 19
 // CreateBuildPod creates the pod to be used for the Docker build
20 20
 // TODO: Make the Pod definition configurable
21 21
 func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*api.Pod, error) {
22
+	contextDir := ""
23
+	if build.Input.DockerInput != nil {
24
+		contextDir = build.Input.DockerInput.ContextDir
25
+	}
26
+
22 27
 	pod := &api.Pod{
23 28
 		JSONBase: api.JSONBase{
24 29
 			ID: build.PodID,
... ...
@@ -32,8 +37,10 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*api.Pod,
32 32
 						Image: bs.dockerBuilderImage,
33 33
 						Env: []api.EnvVar{
34 34
 							{Name: "BUILD_TAG", Value: build.Input.ImageTag},
35
-							{Name: "DOCKER_CONTEXT_URL", Value: build.Input.SourceURI},
36
-							{Name: "DOCKER_REGISTRY", Value: build.Input.Registry},
35
+							{Name: "SOURCE_URI", Value: build.Input.SourceURI},
36
+							{Name: "SOURCE_REF", Value: build.Input.SourceRef},
37
+							{Name: "REGISTRY", Value: build.Input.Registry},
38
+							{Name: "CONTEXT_DIR", Value: contextDir},
37 39
 						},
38 40
 					},
39 41
 				},
... ...
@@ -31,14 +31,20 @@ func TestDockerCreateBuildPod(t *testing.T) {
31 31
 	if actual.DesiredState.Manifest.RestartPolicy.Never == nil {
32 32
 		t.Errorf("Expected never, got %#v", actual.DesiredState.Manifest.RestartPolicy)
33 33
 	}
34
-	if e := container.Env[0]; e.Name != "BUILD_TAG" && e.Value != expected.Input.ImageTag {
35
-		t.Errorf("Expected %s, got %s:%s!", expected.Input.ImageTag, e.Name, e.Value)
34
+	if len(container.Env) != 5 {
35
+		t.Fatalf("Expected 5 elements in Env table, got %d", len(container.Env))
36 36
 	}
37
-	if e := container.Env[1]; e.Name != "DOCKER_CONTEXT_URL" && e.Value != expected.Input.SourceURI {
38
-		t.Errorf("Expected %s, got %s:%s!", expected.Input.ImageTag, e.Name, e.Value)
37
+	errorCases := map[int][]string{
38
+		0: {"BUILD_TAG", expected.Input.ImageTag},
39
+		1: {"SOURCE_URI", expected.Input.SourceURI},
40
+		2: {"SOURCE_REF", expected.Input.SourceRef},
41
+		3: {"REGISTRY", expected.Input.Registry},
42
+		4: {"CONTEXT_DIR", expected.Input.DockerInput.ContextDir},
39 43
 	}
40
-	if e := container.Env[2]; e.Name != "DOCKER_REGISTRY" && e.Value != expected.Input.Registry {
41
-		t.Errorf("Expected %s got %s:%s!", expected.Input.Registry, e.Name, e.Value)
44
+	for index, exp := range errorCases {
45
+		if e := container.Env[index]; e.Name != exp[0] || e.Value != exp[1] {
46
+			t.Errorf("Expected %s:%s, got %s:%s!\n", exp[0], exp[1], e.Name, e.Value)
47
+		}
42 48
 	}
43 49
 }
44 50
 
... ...
@@ -48,10 +54,10 @@ func mockDockerBuild() *api.Build {
48 48
 			ID: "dockerBuild",
49 49
 		},
50 50
 		Input: api.BuildInput{
51
-			Type:      api.DockerBuildType,
52
-			SourceURI: "http://my.build.com/the/dockerbuild/Dockerfile",
53
-			ImageTag:  "repository/dockerBuild",
54
-			Registry:  "docker-registry",
51
+			SourceURI:   "http://my.build.com/the/dockerbuild/Dockerfile",
52
+			ImageTag:    "repository/dockerBuild",
53
+			Registry:    "docker-registry",
54
+			DockerInput: &api.DockerBuildInput{ContextDir: "my/test/dir"},
55 55
 		},
56 56
 		Status: api.BuildNew,
57 57
 		PodID:  "-the-pod-id",
... ...
@@ -48,10 +48,10 @@ func (bs *STIBuildStrategy) CreateBuildPod(build *buildapi.Build) (*api.Pod, err
48 48
 						Image: bs.stiBuilderImage,
49 49
 						Env: []api.EnvVar{
50 50
 							{Name: "BUILD_TAG", Value: build.Input.ImageTag},
51
-							{Name: "DOCKER_REGISTRY", Value: build.Input.Registry},
52 51
 							{Name: "SOURCE_URI", Value: build.Input.SourceURI},
53 52
 							{Name: "SOURCE_REF", Value: build.Input.SourceRef},
54
-							{Name: "BUILDER_IMAGE", Value: build.Input.BuilderImage},
53
+							{Name: "REGISTRY", Value: build.Input.Registry},
54
+							{Name: "BUILDER_IMAGE", Value: build.Input.STIInput.BuilderImage},
55 55
 						},
56 56
 					},
57 57
 				},
... ...
@@ -10,7 +10,7 @@ import (
10 10
 type FakeTempDirCreator struct{}
11 11
 
12 12
 func (t *FakeTempDirCreator) CreateTempDirectory() (string, error) {
13
-	return "", nil
13
+	return "test_temp", nil
14 14
 }
15 15
 
16 16
 func TestSTICreateBuildPod(t *testing.T) {
... ...
@@ -37,20 +37,21 @@ func TestSTICreateBuildPod(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 e := container.Env[0]; e.Name != "BUILD_TAG" || e.Value != expected.Input.ImageTag {
41
-		t.Errorf("Expected %s, got %s:%s!", expected.Input.ImageTag, e.Name, e.Value)
40
+	if len(container.Env) != 6 {
41
+		t.Fatalf("Expected 6 elements in Env table, got %d", len(container.Env))
42 42
 	}
43
-	if e := container.Env[1]; e.Name != "DOCKER_REGISTRY" || e.Value != expected.Input.Registry {
44
-		t.Errorf("Expected %s got %s:%s!", expected.Input.Registry, e.Name, e.Value)
43
+	errorCases := map[int][]string{
44
+		0: {"BUILD_TAG", expected.Input.ImageTag},
45
+		1: {"SOURCE_URI", expected.Input.SourceURI},
46
+		2: {"SOURCE_REF", expected.Input.SourceRef},
47
+		3: {"REGISTRY", expected.Input.Registry},
48
+		4: {"BUILDER_IMAGE", expected.Input.STIInput.BuilderImage},
49
+		5: {"TEMP_DIR", "test_temp"},
45 50
 	}
46
-	if e := container.Env[2]; e.Name != "SOURCE_URI" || e.Value != expected.Input.SourceURI {
47
-		t.Errorf("Expected %s got %s:%s!", expected.Input.SourceURI, e.Name, e.Value)
48
-	}
49
-	if e := container.Env[3]; e.Name != "SOURCE_REF" || e.Value != expected.Input.SourceRef {
50
-		t.Errorf("Expected %s got %s:%s!", expected.Input.SourceRef, e.Name, e.Value)
51
-	}
52
-	if e := container.Env[4]; e.Name != "BUILDER_IMAGE" || e.Value != expected.Input.BuilderImage {
53
-		t.Errorf("Expected %s, got %s:%s!", expected.Input.BuilderImage, e.Name, e.Value)
51
+	for index, exp := range errorCases {
52
+		if e := container.Env[index]; e.Name != exp[0] || e.Value != exp[1] {
53
+			t.Errorf("Expected %s:%s, got %s:%s!\n", exp[0], exp[1], e.Name, e.Value)
54
+		}
54 55
 	}
55 56
 }
56 57
 
... ...
@@ -60,10 +61,10 @@ func mockSTIBuild() *api.Build {
60 60
 			ID: "stiBuild",
61 61
 		},
62 62
 		Input: api.BuildInput{
63
-			Type:      api.STIBuildType,
64 63
 			SourceURI: "http://my.build.com/the/stibuild/Dockerfile",
65 64
 			ImageTag:  "repository/stiBuild",
66 65
 			Registry:  "docker-registry",
66
+			STIInput:  &api.STIBuildInput{BuilderImage: "repository/sti-builder"},
67 67
 		},
68 68
 		Status: api.BuildNew,
69 69
 		PodID:  "-the-pod-id",
... ...
@@ -24,6 +24,7 @@ func printBuild(build *api.Build, w io.Writer) error {
24 24
 	_, err := fmt.Fprintf(w, "%s\t%s\t%s\n", build.ID, build.Status, build.PodID)
25 25
 	return err
26 26
 }
27
+
27 28
 func printBuildList(buildList *api.BuildList, w io.Writer) error {
28 29
 	for _, build := range buildList.Items {
29 30
 		if err := printBuild(&build, w); err != nil {
... ...
@@ -34,9 +35,15 @@ func printBuildList(buildList *api.BuildList, w io.Writer) error {
34 34
 }
35 35
 
36 36
 func printBuildConfig(bc *api.BuildConfig, w io.Writer) error {
37
-	_, err := fmt.Fprintf(w, "%s\t%s\t%s\n", bc.ID, bc.DesiredInput.Type, bc.DesiredInput.SourceURI)
37
+	buildType := api.DockerBuildType
38
+	if bc.DesiredInput.STIInput != nil {
39
+		buildType = api.STIBuildType
40
+	}
41
+
42
+	_, err := fmt.Fprintf(w, "%s\t%v\t%s\n", bc.ID, buildType, bc.DesiredInput.SourceURI)
38 43
 	return err
39 44
 }
45
+
40 46
 func printBuildConfigList(buildList *api.BuildConfigList, w io.Writer) error {
41 47
 	for _, buildConfig := range buildList.Items {
42 48
 		if err := printBuildConfig(&buildConfig, w); err != nil {
... ...
@@ -77,20 +77,13 @@ func TestBuildConfigClient(t *testing.T) {
77 77
 			"label2": "value2",
78 78
 		},
79 79
 		DesiredInput: api.BuildInput{
80
-			Type:         api.DockerBuildType,
81
-			SourceURI:    "http://my.docker/build",
82
-			ImageTag:     "namespace/builtimage",
83
-			BuilderImage: "anImage",
80
+			SourceURI: "http://my.docker/build",
81
+			ImageTag:  "namespace/builtimage",
84 82
 		},
85 83
 	}
86
-	got, err := osClient.CreateBuildConfig(ctx, buildConfig)
87
-	if err == nil {
88
-		t.Fatalf("unexpected non-error: %v", err)
89
-	}
90 84
 
91 85
 	// get a created buildConfig
92
-	buildConfig.DesiredInput.BuilderImage = ""
93
-	got, err = osClient.CreateBuildConfig(ctx, buildConfig)
86
+	got, err := osClient.CreateBuildConfig(ctx, buildConfig)
94 87
 	if err != nil {
95 88
 		t.Fatalf("unexpected error: %v", err)
96 89
 	}
... ...
@@ -77,20 +77,13 @@ func TestBuildClient(t *testing.T) {
77 77
 			"label2": "value2",
78 78
 		},
79 79
 		Input: api.BuildInput{
80
-			Type:         api.DockerBuildType,
81
-			SourceURI:    "http://my.docker/build",
82
-			ImageTag:     "namespace/builtimage",
83
-			BuilderImage: "anImage",
80
+			SourceURI: "http://my.docker/build",
81
+			ImageTag:  "namespace/builtimage",
84 82
 		},
85 83
 	}
86
-	got, err := osClient.CreateBuild(ctx, build)
87
-	if err == nil {
88
-		t.Fatalf("unexpected non-error: %v", err)
89
-	}
90 84
 
91 85
 	// get a created build
92
-	build.Input.BuilderImage = ""
93
-	got, err = osClient.CreateBuild(ctx, build)
86
+	got, err := osClient.CreateBuild(ctx, build)
94 87
 	if err != nil {
95 88
 		t.Fatalf("unexpected error: %v", err)
96 89
 	}
... ...
@@ -44,7 +44,6 @@ func TestWebhookGithubPush(t *testing.T) {
44 44
 			ID: "pushbuild",
45 45
 		},
46 46
 		DesiredInput: buildapi.BuildInput{
47
-			Type:      buildapi.DockerBuildType,
48 47
 			SourceURI: "http://my.docker/build",
49 48
 			ImageTag:  "namespace/builtimage",
50 49
 		},
... ...
@@ -86,7 +85,6 @@ func TestWebhookGithubPing(t *testing.T) {
86 86
 			ID: "pingbuild",
87 87
 		},
88 88
 		DesiredInput: buildapi.BuildInput{
89
-			Type:      buildapi.DockerBuildType,
90 89
 			SourceURI: "http://my.docker/build",
91 90
 			ImageTag:  "namespace/builtimage",
92 91
 		},