... | ... |
@@ -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 |
}, |
... | ... |
@@ -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 |
|
... | ... |
@@ -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 |
|
... | ... |
@@ -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{ |