Browse code

Allow different version encoding for custom builds

Martin Nagy authored on 2016/01/21 22:42:57
Showing 14 changed files
... ...
@@ -17295,6 +17295,10 @@
17295 17295
        "$ref": "v1.SecretSpec"
17296 17296
       },
17297 17297
       "description": "a list of secrets to include in the build pod in addition to pull, push and source secrets"
17298
+     },
17299
+     "buildAPIVersion": {
17300
+      "type": "string",
17301
+      "description": "requested API version for the Build object serialized and passed to the custom builder"
17298 17302
      }
17299 17303
     }
17300 17304
    },
... ...
@@ -1165,6 +1165,7 @@ func deepCopy_api_CustomBuildStrategy(in buildapi.CustomBuildStrategy, out *buil
1165 1165
 	} else {
1166 1166
 		out.Secrets = nil
1167 1167
 	}
1168
+	out.BuildAPIVersion = in.BuildAPIVersion
1168 1169
 	return nil
1169 1170
 }
1170 1171
 
... ...
@@ -1623,6 +1623,7 @@ func autoconvert_api_CustomBuildStrategy_To_v1_CustomBuildStrategy(in *buildapi.
1623 1623
 	} else {
1624 1624
 		out.Secrets = nil
1625 1625
 	}
1626
+	out.BuildAPIVersion = in.BuildAPIVersion
1626 1627
 	return nil
1627 1628
 }
1628 1629
 
... ...
@@ -2418,6 +2419,7 @@ func autoconvert_v1_CustomBuildStrategy_To_api_CustomBuildStrategy(in *apiv1.Cus
2418 2418
 	} else {
2419 2419
 		out.Secrets = nil
2420 2420
 	}
2421
+	out.BuildAPIVersion = in.BuildAPIVersion
2421 2422
 	return nil
2422 2423
 }
2423 2424
 
... ...
@@ -1191,6 +1191,7 @@ func deepCopy_v1_CustomBuildStrategy(in apiv1.CustomBuildStrategy, out *apiv1.Cu
1191 1191
 	} else {
1192 1192
 		out.Secrets = nil
1193 1193
 	}
1194
+	out.BuildAPIVersion = in.BuildAPIVersion
1194 1195
 	return nil
1195 1196
 }
1196 1197
 
... ...
@@ -1632,6 +1632,7 @@ func autoconvert_api_CustomBuildStrategy_To_v1beta3_CustomBuildStrategy(in *buil
1632 1632
 	} else {
1633 1633
 		out.Secrets = nil
1634 1634
 	}
1635
+	out.BuildAPIVersion = in.BuildAPIVersion
1635 1636
 	return nil
1636 1637
 }
1637 1638
 
... ...
@@ -2427,6 +2428,7 @@ func autoconvert_v1beta3_CustomBuildStrategy_To_api_CustomBuildStrategy(in *apiv
2427 2427
 	} else {
2428 2428
 		out.Secrets = nil
2429 2429
 	}
2430
+	out.BuildAPIVersion = in.BuildAPIVersion
2430 2431
 	return nil
2431 2432
 }
2432 2433
 
... ...
@@ -1199,6 +1199,7 @@ func deepCopy_v1beta3_CustomBuildStrategy(in apiv1beta3.CustomBuildStrategy, out
1199 1199
 	} else {
1200 1200
 		out.Secrets = nil
1201 1201
 	}
1202
+	out.BuildAPIVersion = in.BuildAPIVersion
1202 1203
 	return nil
1203 1204
 }
1204 1205
 
... ...
@@ -356,6 +356,9 @@ type CustomBuildStrategy struct {
356 356
 
357 357
 	// Secrets is a list of additional secrets that will be included in the custom build pod
358 358
 	Secrets []SecretSpec
359
+
360
+	// BuildAPIVersion is the requested API version for the Build object serialized and passed to the custom builder
361
+	BuildAPIVersion string
359 362
 }
360 363
 
361 364
 // DockerBuildStrategy defines input parameters specific to Docker build.
... ...
@@ -339,6 +339,9 @@ type CustomBuildStrategy struct {
339 339
 
340 340
 	// Secrets is a list of additional secrets that will be included in the build pod
341 341
 	Secrets []SecretSpec `json:"secrets,omitempty" description:"a list of secrets to include in the build pod in addition to pull, push and source secrets"`
342
+
343
+	// BuildAPIVersion is the requested API version for the Build object serialized and passed to the custom builder
344
+	BuildAPIVersion string `json:"buildAPIVersion,omitempty" description:"requested API version for the Build object serialized and passed to the custom builder"`
342 345
 }
343 346
 
344 347
 // DockerBuildStrategy defines input parameters specific to Docker build.
... ...
@@ -329,6 +329,9 @@ type CustomBuildStrategy struct {
329 329
 
330 330
 	// Secrets is a list of additional secrets that will be included in the build pod
331 331
 	Secrets []SecretSpec `json:"secrets,omitempty" description:"a list of secrets to include in the build pod in addition to pull, push and source secrets"`
332
+
333
+	// BuildAPIVersion is the requested API version for the Build object serialized and passed to the custom builder
334
+	BuildAPIVersion string `json:"buildAPIVersion,omitempty" description:"requested API version for the Build object serialized and passed to the custom builder"`
332 335
 }
333 336
 
334 337
 // DockerBuildStrategy defines input parameters specific to Docker build.
... ...
@@ -13,6 +13,7 @@ import (
13 13
 
14 14
 	buildapi "github.com/openshift/origin/pkg/build/api"
15 15
 	buildclient "github.com/openshift/origin/pkg/build/client"
16
+	strategy "github.com/openshift/origin/pkg/build/controller/strategy"
16 17
 	buildutil "github.com/openshift/origin/pkg/build/util"
17 18
 	imageapi "github.com/openshift/origin/pkg/image/api"
18 19
 )
... ...
@@ -153,6 +154,9 @@ func (bc *BuildController) nextBuildPhase(build *buildapi.Build) error {
153 153
 	podSpec, err := bc.BuildStrategy.CreateBuildPod(buildCopy)
154 154
 	if err != nil {
155 155
 		build.Status.Reason = buildapi.StatusReasonCannotCreateBuildPodSpec
156
+		if strategy.IsFatal(err) {
157
+			return strategy.FatalError(fmt.Sprintf("failed to create a build pod spec for build %s/%s: %v", build.Namespace, build.Name, err))
158
+		}
156 159
 		return fmt.Errorf("failed to create a build pod spec for build %s/%s: %v", build.Namespace, build.Name, err)
157 160
 	}
158 161
 	glog.V(4).Infof("Pod %s for build %s/%s is about to be created", podSpec.Name, build.Namespace, build.Name)
... ...
@@ -32,13 +32,16 @@ const maxRetries = 60
32 32
 // limitedLogAndRetry stops retrying after maxTimeout, failing the build.
33 33
 func limitedLogAndRetry(buildupdater buildclient.BuildUpdater, maxTimeout time.Duration) controller.RetryFunc {
34 34
 	return func(obj interface{}, err error, retries controller.Retry) bool {
35
+		isFatal := strategy.IsFatal(err)
35 36
 		build := obj.(*buildapi.Build)
36
-		if time.Since(retries.StartTimestamp.Time) < maxTimeout {
37
+		if !isFatal && time.Since(retries.StartTimestamp.Time) < maxTimeout {
37 38
 			glog.V(4).Infof("Retrying Build %s/%s with error: %v", build.Namespace, build.Name, err)
38 39
 			return true
39 40
 		}
40 41
 		build.Status.Phase = buildapi.BuildPhaseFailed
41
-		build.Status.Reason = buildapi.StatusReasonExceededRetryTimeout
42
+		if !isFatal {
43
+			build.Status.Reason = buildapi.StatusReasonExceededRetryTimeout
44
+		}
42 45
 		build.Status.Message = errors.ErrorToSentence(err)
43 46
 		now := unversioned.Now()
44 47
 		build.Status.CompletionTimestamp = &now
... ...
@@ -6,8 +6,10 @@ import (
6 6
 
7 7
 	"github.com/golang/glog"
8 8
 	kapi "k8s.io/kubernetes/pkg/api"
9
+	"k8s.io/kubernetes/pkg/api/unversioned"
9 10
 	"k8s.io/kubernetes/pkg/runtime"
10 11
 
12
+	"github.com/openshift/origin/pkg/api/latest"
11 13
 	buildapi "github.com/openshift/origin/pkg/build/api"
12 14
 	buildutil "github.com/openshift/origin/pkg/build/util"
13 15
 )
... ...
@@ -22,12 +24,26 @@ type CustomBuildStrategy struct {
22 22
 
23 23
 // CreateBuildPod creates the pod to be used for the Custom build
24 24
 func (bs *CustomBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod, error) {
25
-	data, err := bs.Codec.Encode(build)
25
+	strategy := build.Spec.Strategy.CustomStrategy
26
+
27
+	codec := bs.Codec
28
+	if len(strategy.BuildAPIVersion) != 0 {
29
+		gv, err := unversioned.ParseGroupVersion(strategy.BuildAPIVersion)
30
+		if err != nil {
31
+			return nil, FatalError(fmt.Sprintf("failed to parse buildAPIVersion specified in custom build strategy (%q): %v", strategy.BuildAPIVersion, err))
32
+		}
33
+		interfaces, err := latest.InterfacesFor(gv)
34
+		if err != nil {
35
+			return nil, FatalError(fmt.Sprintf("invalid buildAPIVersion specified in custom build strategy (%q): %v", strategy.BuildAPIVersion, err))
36
+		}
37
+		codec = interfaces.Codec
38
+	}
39
+
40
+	data, err := codec.Encode(build)
26 41
 	if err != nil {
27 42
 		return nil, fmt.Errorf("failed to encode the build: %v", err)
28 43
 	}
29 44
 
30
-	strategy := build.Spec.Strategy.CustomStrategy
31 45
 	containerEnv := []kapi.EnvVar{{Name: "BUILD", Value: string(data)}}
32 46
 
33 47
 	if build.Spec.Source.Git != nil {
... ...
@@ -1,7 +1,9 @@
1 1
 package strategy
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"reflect"
6
+	"strings"
5 7
 	"testing"
6 8
 
7 9
 	kapi "k8s.io/kubernetes/pkg/api"
... ...
@@ -104,6 +106,36 @@ func TestCustomCreateBuildPodExpectedForcePull(t *testing.T) {
104 104
 	}
105 105
 }
106 106
 
107
+func TestCustomCreateBuildPodWithCustomCodec(t *testing.T) {
108
+	strategy := CustomBuildStrategy{
109
+		Codec: latest.Codec,
110
+	}
111
+
112
+	for _, version := range latest.Versions {
113
+		// Create new Build specification and modify Spec API version
114
+		build := mockCustomBuild(false)
115
+		build.Spec.Strategy.CustomStrategy.BuildAPIVersion = fmt.Sprintf("%s/%s", version.Group, version.Version)
116
+
117
+		pod, err := strategy.CreateBuildPod(build)
118
+		if err != nil {
119
+			t.Fatalf("Unexpected error: %v", err)
120
+		}
121
+		versionFound := false
122
+		for _, envVar := range pod.Spec.Containers[0].Env {
123
+			if envVar.Name == "BUILD" {
124
+				if strings.Contains(envVar.Value, fmt.Sprintf(`"apiVersion":"%s"`, version)) {
125
+					versionFound = true
126
+					break
127
+				}
128
+				t.Fatalf("BUILD environment variable doesn't contain correct API version")
129
+			}
130
+		}
131
+		if !versionFound {
132
+			t.Fatalf("Couldn't find BUILD environment variable in pod spec")
133
+		}
134
+	}
135
+}
136
+
107 137
 func mockCustomBuild(forcePull bool) *buildapi.Build {
108 138
 	timeout := int64(60)
109 139
 	return &buildapi.Build{
... ...
@@ -26,6 +26,20 @@ const (
26 26
 
27 27
 var whitelistEnvVarNames = []string{"BUILD_LOGLEVEL"}
28 28
 
29
+// FatalError is an error which can't be retried.
30
+type FatalError string
31
+
32
+// Error implements the error interface.
33
+func (e FatalError) Error() string {
34
+	return string(e)
35
+}
36
+
37
+// IsFatal returns true if the error is fatal
38
+func IsFatal(err error) bool {
39
+	_, isFatal := err.(FatalError)
40
+	return isFatal
41
+}
42
+
29 43
 // setupDockerSocket configures the pod to support the host's Docker socket
30 44
 func setupDockerSocket(podSpec *kapi.Pod) {
31 45
 	dockerSocketVolume := kapi.Volume{