| ... | ... |
@@ -13819,6 +13819,11 @@ |
| 13819 | 13819 |
"resources": {
|
| 13820 | 13820 |
"$ref": "v1.ResourceRequirements", |
| 13821 | 13821 |
"description": "the desired compute resources the build should have" |
| 13822 |
+ }, |
|
| 13823 |
+ "completionDeadlineSeconds": {
|
|
| 13824 |
+ "type": "integer", |
|
| 13825 |
+ "format": "int64", |
|
| 13826 |
+ "description": "optional duration in seconds the build may be active on a node before the system will actively try to mark it failed and kill associated containers; value must be a positive integer" |
|
| 13822 | 13827 |
} |
| 13823 | 13828 |
} |
| 13824 | 13829 |
}, |
| ... | ... |
@@ -14481,6 +14486,11 @@ |
| 14481 | 14481 |
"resources": {
|
| 14482 | 14482 |
"$ref": "v1.ResourceRequirements", |
| 14483 | 14483 |
"description": "the desired compute resources the build should have" |
| 14484 |
+ }, |
|
| 14485 |
+ "completionDeadlineSeconds": {
|
|
| 14486 |
+ "type": "integer", |
|
| 14487 |
+ "format": "int64", |
|
| 14488 |
+ "description": "optional duration in seconds the build may be active on a node before the system will actively try to mark it failed and kill associated containers; value must be a positive integer" |
|
| 14484 | 14489 |
} |
| 14485 | 14490 |
} |
| 14486 | 14491 |
}, |
| ... | ... |
@@ -918,6 +918,12 @@ func deepCopy_api_BuildSpec(in buildapi.BuildSpec, out *buildapi.BuildSpec, c *c |
| 918 | 918 |
} else {
|
| 919 | 919 |
out.Resources = newVal.(pkgapi.ResourceRequirements) |
| 920 | 920 |
} |
| 921 |
+ if in.CompletionDeadlineSeconds != nil {
|
|
| 922 |
+ out.CompletionDeadlineSeconds = new(int64) |
|
| 923 |
+ *out.CompletionDeadlineSeconds = *in.CompletionDeadlineSeconds |
|
| 924 |
+ } else {
|
|
| 925 |
+ out.CompletionDeadlineSeconds = nil |
|
| 926 |
+ } |
|
| 921 | 927 |
return nil |
| 922 | 928 |
} |
| 923 | 929 |
|
| ... | ... |
@@ -49,6 +49,7 @@ func checkDescriptions(objType reflect.Type, seen *map[reflect.Type]bool, t *tes |
| 49 | 49 |
|
| 50 | 50 |
descriptionTag := structField.Tag.Get("description")
|
| 51 | 51 |
if len(descriptionTag) == 0 {
|
| 52 |
+ t.Errorf("%v", structField.Tag)
|
|
| 52 | 53 |
t.Errorf("%v.%v does not have a description", objType, structField.Name)
|
| 53 | 54 |
} |
| 54 | 55 |
|
| ... | ... |
@@ -771,6 +771,12 @@ func convert_api_BuildSpec_To_v1_BuildSpec(in *buildapi.BuildSpec, out *apiv1.Bu |
| 771 | 771 |
if err := convert_api_ResourceRequirements_To_v1_ResourceRequirements(&in.Resources, &out.Resources, s); err != nil {
|
| 772 | 772 |
return err |
| 773 | 773 |
} |
| 774 |
+ if in.CompletionDeadlineSeconds != nil {
|
|
| 775 |
+ out.CompletionDeadlineSeconds = new(int64) |
|
| 776 |
+ *out.CompletionDeadlineSeconds = *in.CompletionDeadlineSeconds |
|
| 777 |
+ } else {
|
|
| 778 |
+ out.CompletionDeadlineSeconds = nil |
|
| 779 |
+ } |
|
| 774 | 780 |
return nil |
| 775 | 781 |
} |
| 776 | 782 |
|
| ... | ... |
@@ -1159,6 +1165,12 @@ func convert_v1_BuildSpec_To_api_BuildSpec(in *apiv1.BuildSpec, out *buildapi.Bu |
| 1159 | 1159 |
if err := convert_v1_ResourceRequirements_To_api_ResourceRequirements(&in.Resources, &out.Resources, s); err != nil {
|
| 1160 | 1160 |
return err |
| 1161 | 1161 |
} |
| 1162 |
+ if in.CompletionDeadlineSeconds != nil {
|
|
| 1163 |
+ out.CompletionDeadlineSeconds = new(int64) |
|
| 1164 |
+ *out.CompletionDeadlineSeconds = *in.CompletionDeadlineSeconds |
|
| 1165 |
+ } else {
|
|
| 1166 |
+ out.CompletionDeadlineSeconds = nil |
|
| 1167 |
+ } |
|
| 1162 | 1168 |
return nil |
| 1163 | 1169 |
} |
| 1164 | 1170 |
|
| ... | ... |
@@ -942,6 +942,12 @@ func deepCopy_v1_BuildSpec(in apiv1.BuildSpec, out *apiv1.BuildSpec, c *conversi |
| 942 | 942 |
} else {
|
| 943 | 943 |
out.Resources = newVal.(pkgapiv1.ResourceRequirements) |
| 944 | 944 |
} |
| 945 |
+ if in.CompletionDeadlineSeconds != nil {
|
|
| 946 |
+ out.CompletionDeadlineSeconds = new(int64) |
|
| 947 |
+ *out.CompletionDeadlineSeconds = *in.CompletionDeadlineSeconds |
|
| 948 |
+ } else {
|
|
| 949 |
+ out.CompletionDeadlineSeconds = nil |
|
| 950 |
+ } |
|
| 945 | 951 |
return nil |
| 946 | 952 |
} |
| 947 | 953 |
|
| ... | ... |
@@ -809,6 +809,12 @@ func convert_api_BuildSpec_To_v1beta3_BuildSpec(in *buildapi.BuildSpec, out *api |
| 809 | 809 |
if err := convert_api_ResourceRequirements_To_v1beta3_ResourceRequirements(&in.Resources, &out.Resources, s); err != nil {
|
| 810 | 810 |
return err |
| 811 | 811 |
} |
| 812 |
+ if in.CompletionDeadlineSeconds != nil {
|
|
| 813 |
+ out.CompletionDeadlineSeconds = new(int64) |
|
| 814 |
+ *out.CompletionDeadlineSeconds = *in.CompletionDeadlineSeconds |
|
| 815 |
+ } else {
|
|
| 816 |
+ out.CompletionDeadlineSeconds = nil |
|
| 817 |
+ } |
|
| 812 | 818 |
return nil |
| 813 | 819 |
} |
| 814 | 820 |
|
| ... | ... |
@@ -1197,6 +1203,12 @@ func convert_v1beta3_BuildSpec_To_api_BuildSpec(in *apiv1beta3.BuildSpec, out *b |
| 1197 | 1197 |
if err := convert_v1beta3_ResourceRequirements_To_api_ResourceRequirements(&in.Resources, &out.Resources, s); err != nil {
|
| 1198 | 1198 |
return err |
| 1199 | 1199 |
} |
| 1200 |
+ if in.CompletionDeadlineSeconds != nil {
|
|
| 1201 |
+ out.CompletionDeadlineSeconds = new(int64) |
|
| 1202 |
+ *out.CompletionDeadlineSeconds = *in.CompletionDeadlineSeconds |
|
| 1203 |
+ } else {
|
|
| 1204 |
+ out.CompletionDeadlineSeconds = nil |
|
| 1205 |
+ } |
|
| 1200 | 1206 |
return nil |
| 1201 | 1207 |
} |
| 1202 | 1208 |
|
| ... | ... |
@@ -950,6 +950,12 @@ func deepCopy_v1beta3_BuildSpec(in apiv1beta3.BuildSpec, out *apiv1beta3.BuildSp |
| 950 | 950 |
} else {
|
| 951 | 951 |
out.Resources = newVal.(pkgapiv1beta3.ResourceRequirements) |
| 952 | 952 |
} |
| 953 |
+ if in.CompletionDeadlineSeconds != nil {
|
|
| 954 |
+ out.CompletionDeadlineSeconds = new(int64) |
|
| 955 |
+ *out.CompletionDeadlineSeconds = *in.CompletionDeadlineSeconds |
|
| 956 |
+ } else {
|
|
| 957 |
+ out.CompletionDeadlineSeconds = nil |
|
| 958 |
+ } |
|
| 953 | 959 |
return nil |
| 954 | 960 |
} |
| 955 | 961 |
|
| ... | ... |
@@ -58,6 +58,11 @@ type BuildSpec struct {
|
| 58 | 58 |
|
| 59 | 59 |
// Compute resource requirements to execute the build |
| 60 | 60 |
Resources kapi.ResourceRequirements |
| 61 |
+ |
|
| 62 |
+ // Optional duration in seconds, counted from the time when a build pod gets |
|
| 63 |
+ // scheduled in the system, that the build may be active on a node before the |
|
| 64 |
+ // system actively tries to terminate the build; value must be positive integer |
|
| 65 |
+ CompletionDeadlineSeconds *int64 |
|
| 61 | 66 |
} |
| 62 | 67 |
|
| 63 | 68 |
// BuildStatus contains the status of a build |
| ... | ... |
@@ -42,6 +42,11 @@ type BuildSpec struct {
|
| 42 | 42 |
|
| 43 | 43 |
// Compute resource requirements to execute the build |
| 44 | 44 |
Resources kapi.ResourceRequirements `json:"resources,omitempty" description:"the desired compute resources the build should have"` |
| 45 |
+ |
|
| 46 |
+ // Optional duration in seconds, counted from the time when a build pod gets |
|
| 47 |
+ // scheduled in the system, that the build may be active on a node before the |
|
| 48 |
+ // system actively tries to terminate the build; value must be positive integer |
|
| 49 |
+ CompletionDeadlineSeconds *int64 `json:"completionDeadlineSeconds,omitempty" description:"optional duration in seconds the build may be active on a node before the system will actively try to mark it failed and kill associated containers; value must be a positive integer"` |
|
| 45 | 50 |
} |
| 46 | 51 |
|
| 47 | 52 |
// BuildStatus contains the status of a build |
| ... | ... |
@@ -42,6 +42,11 @@ type BuildSpec struct {
|
| 42 | 42 |
|
| 43 | 43 |
// Compute resource requirements to execute the build |
| 44 | 44 |
Resources kapi.ResourceRequirements `json:"resources,omitempty" description:"the desired compute resources the build should have"` |
| 45 |
+ |
|
| 46 |
+ // Optional duration in seconds, counted from the time when a build pod gets |
|
| 47 |
+ // scheduled in the system, that the build may be active on a node before the |
|
| 48 |
+ // system actively tries to terminate the build; value must be positive integer |
|
| 49 |
+ CompletionDeadlineSeconds *int64 `json:"completionDeadlineSeconds,omitempty" description:"optional duration in seconds the build may be active on a node before the system will actively try to mark it failed and kill associated containers; value must be a positive integer"` |
|
| 45 | 50 |
} |
| 46 | 51 |
|
| 47 | 52 |
// BuildStatus contains the status of a build |
| ... | ... |
@@ -125,6 +125,11 @@ func validateBuildSpec(spec *buildapi.BuildSpec) fielderrors.ValidationErrorList |
| 125 | 125 |
if spec.Revision != nil {
|
| 126 | 126 |
allErrs = append(allErrs, validateRevision(spec.Revision).Prefix("revision")...)
|
| 127 | 127 |
} |
| 128 |
+ if spec.CompletionDeadlineSeconds != nil {
|
|
| 129 |
+ if *spec.CompletionDeadlineSeconds <= 0 {
|
|
| 130 |
+ allErrs = append(allErrs, fielderrors.NewFieldInvalid("completionDeadlineSeconds", spec.CompletionDeadlineSeconds, "completionDeadlineSeconds must be a positive integer greater than 0"))
|
|
| 131 |
+ } |
|
| 132 |
+ } |
|
| 128 | 133 |
|
| 129 | 134 |
allErrs = append(allErrs, validateOutput(&spec.Output).Prefix("output")...)
|
| 130 | 135 |
allErrs = append(allErrs, validateStrategy(&spec.Strategy).Prefix("strategy")...)
|
| ... | ... |
@@ -567,6 +567,7 @@ func TestValidateSource(t *testing.T) {
|
| 567 | 567 |
} |
| 568 | 568 |
|
| 569 | 569 |
func TestValidateBuildSpec(t *testing.T) {
|
| 570 |
+ zero := int64(0) |
|
| 570 | 571 |
longString := strings.Repeat("1234567890", 100*61)
|
| 571 | 572 |
shortString := "FROM foo" |
| 572 | 573 |
errorCases := []struct {
|
| ... | ... |
@@ -873,6 +874,7 @@ func TestValidateBuildSpec(t *testing.T) {
|
| 873 | 873 |
}, |
| 874 | 874 |
}, |
| 875 | 875 |
}, |
| 876 |
+ // 13 |
|
| 876 | 877 |
{
|
| 877 | 878 |
string(fielderrors.ValidationErrorTypeInvalid) + "source.dockerfile", |
| 878 | 879 |
&buildapi.BuildSpec{
|
| ... | ... |
@@ -886,6 +888,7 @@ func TestValidateBuildSpec(t *testing.T) {
|
| 886 | 886 |
}, |
| 887 | 887 |
}, |
| 888 | 888 |
}, |
| 889 |
+ // 14 |
|
| 889 | 890 |
{
|
| 890 | 891 |
string(fielderrors.ValidationErrorTypeInvalid) + "source.dockerfile", |
| 891 | 892 |
&buildapi.BuildSpec{
|
| ... | ... |
@@ -902,6 +905,31 @@ func TestValidateBuildSpec(t *testing.T) {
|
| 902 | 902 |
}, |
| 903 | 903 |
}, |
| 904 | 904 |
}, |
| 905 |
+ // 15 |
|
| 906 |
+ // invalid because CompletionDeadlineSeconds <= 0 |
|
| 907 |
+ {
|
|
| 908 |
+ string(fielderrors.ValidationErrorTypeInvalid) + "completionDeadlineSeconds", |
|
| 909 |
+ &buildapi.BuildSpec{
|
|
| 910 |
+ Source: buildapi.BuildSource{
|
|
| 911 |
+ Type: buildapi.BuildSourceGit, |
|
| 912 |
+ Git: &buildapi.GitBuildSource{
|
|
| 913 |
+ URI: "http://github.com/my/repository", |
|
| 914 |
+ }, |
|
| 915 |
+ ContextDir: "context", |
|
| 916 |
+ }, |
|
| 917 |
+ Strategy: buildapi.BuildStrategy{
|
|
| 918 |
+ Type: buildapi.DockerBuildStrategyType, |
|
| 919 |
+ DockerStrategy: &buildapi.DockerBuildStrategy{},
|
|
| 920 |
+ }, |
|
| 921 |
+ Output: buildapi.BuildOutput{
|
|
| 922 |
+ To: &kapi.ObjectReference{
|
|
| 923 |
+ Kind: "DockerImage", |
|
| 924 |
+ Name: "repository/data", |
|
| 925 |
+ }, |
|
| 926 |
+ }, |
|
| 927 |
+ CompletionDeadlineSeconds: &zero, |
|
| 928 |
+ }, |
|
| 929 |
+ }, |
|
| 905 | 930 |
} |
| 906 | 931 |
|
| 907 | 932 |
for count, config := range errorCases {
|
| ... | ... |
@@ -70,6 +70,9 @@ func (bs *CustomBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod, |
| 70 | 70 |
RestartPolicy: kapi.RestartPolicyNever, |
| 71 | 71 |
}, |
| 72 | 72 |
} |
| 73 |
+ if build.Spec.CompletionDeadlineSeconds != nil {
|
|
| 74 |
+ pod.Spec.ActiveDeadlineSeconds = build.Spec.CompletionDeadlineSeconds |
|
| 75 |
+ } |
|
| 73 | 76 |
|
| 74 | 77 |
if err := setupBuildEnv(build, pod); err != nil {
|
| 75 | 78 |
return nil, err |
| ... | ... |
@@ -51,6 +51,9 @@ func TestCustomCreateBuildPod(t *testing.T) {
|
| 51 | 51 |
if len(container.VolumeMounts) != 3 {
|
| 52 | 52 |
t.Fatalf("Expected 3 volumes in container, got %d", len(container.VolumeMounts))
|
| 53 | 53 |
} |
| 54 |
+ if *actual.Spec.ActiveDeadlineSeconds != 60 {
|
|
| 55 |
+ t.Errorf("Expected ActiveDeadlineSeconds 60, got %d", *actual.Spec.ActiveDeadlineSeconds)
|
|
| 56 |
+ } |
|
| 54 | 57 |
for i, expected := range []string{dockerSocketPath, DockerPushSecretMountPath, sourceSecretMountPath} {
|
| 55 | 58 |
if container.VolumeMounts[i].MountPath != expected {
|
| 56 | 59 |
t.Fatalf("Expected %s in VolumeMount[%d], got %s", expected, i, container.VolumeMounts[i].MountPath)
|
| ... | ... |
@@ -83,19 +86,26 @@ func TestCustomCreateBuildPod(t *testing.T) {
|
| 83 | 83 |
t.Errorf("Expected %s variable to be set", name)
|
| 84 | 84 |
} |
| 85 | 85 |
} |
| 86 |
+} |
|
| 86 | 87 |
|
| 87 |
- expectedForcePull := mockCustomBuild(true) |
|
| 88 |
- actualForcePull, fperr := strategy.CreateBuildPod(expectedForcePull) |
|
| 88 |
+func TestCustomCreateBuildPodExpectedForcePull(t *testing.T) {
|
|
| 89 |
+ strategy := CustomBuildStrategy{
|
|
| 90 |
+ Codec: latest.Codec, |
|
| 91 |
+ } |
|
| 92 |
+ |
|
| 93 |
+ expected := mockCustomBuild(true) |
|
| 94 |
+ actual, fperr := strategy.CreateBuildPod(expected) |
|
| 89 | 95 |
if fperr != nil {
|
| 90 | 96 |
t.Fatalf("Unexpected error: %v", fperr)
|
| 91 | 97 |
} |
| 92 |
- containerForcePull := actualForcePull.Spec.Containers[0] |
|
| 93 |
- if containerForcePull.ImagePullPolicy != kapi.PullAlways {
|
|
| 98 |
+ container := actual.Spec.Containers[0] |
|
| 99 |
+ if container.ImagePullPolicy != kapi.PullAlways {
|
|
| 94 | 100 |
t.Errorf("Expected %v, got %v", kapi.PullAlways, container.ImagePullPolicy)
|
| 95 | 101 |
} |
| 96 | 102 |
} |
| 97 | 103 |
|
| 98 | 104 |
func mockCustomBuild(forcePull bool) *buildapi.Build {
|
| 105 |
+ timeout := int64(60) |
|
| 99 | 106 |
return &buildapi.Build{
|
| 100 | 107 |
ObjectMeta: kapi.ObjectMeta{
|
| 101 | 108 |
Name: "customBuild", |
| ... | ... |
@@ -143,6 +153,7 @@ func mockCustomBuild(forcePull bool) *buildapi.Build {
|
| 143 | 143 |
kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("10G"),
|
| 144 | 144 |
}, |
| 145 | 145 |
}, |
| 146 |
+ CompletionDeadlineSeconds: &timeout, |
|
| 146 | 147 |
}, |
| 147 | 148 |
Status: buildapi.BuildStatus{
|
| 148 | 149 |
Phase: buildapi.BuildPhaseNew, |
| ... | ... |
@@ -67,6 +67,9 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod, |
| 67 | 67 |
} |
| 68 | 68 |
pod.Spec.Containers[0].ImagePullPolicy = kapi.PullIfNotPresent |
| 69 | 69 |
pod.Spec.Containers[0].Resources = build.Spec.Resources |
| 70 |
+ if build.Spec.CompletionDeadlineSeconds != nil {
|
|
| 71 |
+ pod.Spec.ActiveDeadlineSeconds = build.Spec.CompletionDeadlineSeconds |
|
| 72 |
+ } |
|
| 70 | 73 |
|
| 71 | 74 |
setupDockerSocket(pod) |
| 72 | 75 |
setupDockerSecrets(pod, build.Spec.Output.PushSecret, strategy.PullSecret) |
| ... | ... |
@@ -49,6 +49,9 @@ func TestDockerCreateBuildPod(t *testing.T) {
|
| 49 | 49 |
if len(container.VolumeMounts) != 4 {
|
| 50 | 50 |
t.Fatalf("Expected 4 volumes in container, got %d", len(container.VolumeMounts))
|
| 51 | 51 |
} |
| 52 |
+ if *actual.Spec.ActiveDeadlineSeconds != 60 {
|
|
| 53 |
+ t.Errorf("Expected ActiveDeadlineSeconds 60, got %d", *actual.Spec.ActiveDeadlineSeconds)
|
|
| 54 |
+ } |
|
| 52 | 55 |
for i, expected := range []string{dockerSocketPath, DockerPushSecretMountPath, DockerPullSecretMountPath, sourceSecretMountPath} {
|
| 53 | 56 |
if container.VolumeMounts[i].MountPath != expected {
|
| 54 | 57 |
t.Fatalf("Expected %s in VolumeMount[%d], got %s", expected, i, container.VolumeMounts[i].MountPath)
|
| ... | ... |
@@ -89,6 +92,7 @@ func TestDockerCreateBuildPod(t *testing.T) {
|
| 89 | 89 |
} |
| 90 | 90 |
|
| 91 | 91 |
func mockDockerBuild() *buildapi.Build {
|
| 92 |
+ timeout := int64(60) |
|
| 92 | 93 |
return &buildapi.Build{
|
| 93 | 94 |
ObjectMeta: kapi.ObjectMeta{
|
| 94 | 95 |
Name: "dockerBuild", |
| ... | ... |
@@ -131,6 +135,7 @@ func mockDockerBuild() *buildapi.Build {
|
| 131 | 131 |
kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("10G"),
|
| 132 | 132 |
}, |
| 133 | 133 |
}, |
| 134 |
+ CompletionDeadlineSeconds: &timeout, |
|
| 134 | 135 |
}, |
| 135 | 136 |
Status: buildapi.BuildStatus{
|
| 136 | 137 |
Phase: buildapi.BuildPhaseNew, |
| ... | ... |
@@ -89,6 +89,9 @@ func (bs *SourceBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod, |
| 89 | 89 |
} |
| 90 | 90 |
pod.Spec.Containers[0].ImagePullPolicy = kapi.PullIfNotPresent |
| 91 | 91 |
pod.Spec.Containers[0].Resources = build.Spec.Resources |
| 92 |
+ if build.Spec.CompletionDeadlineSeconds != nil {
|
|
| 93 |
+ pod.Spec.ActiveDeadlineSeconds = build.Spec.CompletionDeadlineSeconds |
|
| 94 |
+ } |
|
| 92 | 95 |
|
| 93 | 96 |
setupDockerSocket(pod) |
| 94 | 97 |
setupDockerSecrets(pod, build.Spec.Output.PushSecret, strategy.PullSecret) |
| ... | ... |
@@ -96,6 +96,9 @@ func testSTICreateBuildPod(t *testing.T, rootAllowed bool) {
|
| 96 | 96 |
if len(actual.Spec.Volumes) != 4 {
|
| 97 | 97 |
t.Fatalf("Expected 4 volumes in Build pod, got %d", len(actual.Spec.Volumes))
|
| 98 | 98 |
} |
| 99 |
+ if *actual.Spec.ActiveDeadlineSeconds != 60 {
|
|
| 100 |
+ t.Errorf("Expected ActiveDeadlineSeconds 60, got %d", *actual.Spec.ActiveDeadlineSeconds)
|
|
| 101 |
+ } |
|
| 99 | 102 |
if !kapi.Semantic.DeepEqual(container.Resources, expected.Spec.Resources) {
|
| 100 | 103 |
t.Fatalf("Expected actual=expected, %v != %v", container.Resources, expected.Spec.Resources)
|
| 101 | 104 |
} |
| ... | ... |
@@ -137,6 +140,7 @@ func testSTICreateBuildPod(t *testing.T, rootAllowed bool) {
|
| 137 | 137 |
} |
| 138 | 138 |
|
| 139 | 139 |
func mockSTIBuild() *buildapi.Build {
|
| 140 |
+ timeout := int64(60) |
|
| 140 | 141 |
return &buildapi.Build{
|
| 141 | 142 |
ObjectMeta: kapi.ObjectMeta{
|
| 142 | 143 |
Name: "stiBuild", |
| ... | ... |
@@ -184,6 +188,7 @@ func mockSTIBuild() *buildapi.Build {
|
| 184 | 184 |
kapi.ResourceName(kapi.ResourceMemory): resource.MustParse("10G"),
|
| 185 | 185 |
}, |
| 186 | 186 |
}, |
| 187 |
+ CompletionDeadlineSeconds: &timeout, |
|
| 187 | 188 |
}, |
| 188 | 189 |
Status: buildapi.BuildStatus{
|
| 189 | 190 |
Phase: buildapi.BuildPhaseNew, |
| ... | ... |
@@ -314,12 +314,13 @@ func (g *BuildGenerator) generateBuildFromConfig(ctx kapi.Context, bc *buildapi. |
| 314 | 314 |
bcCopy := obj.(*buildapi.BuildConfig) |
| 315 | 315 |
build := &buildapi.Build{
|
| 316 | 316 |
Spec: buildapi.BuildSpec{
|
| 317 |
- ServiceAccount: serviceAccount, |
|
| 318 |
- Source: bcCopy.Spec.Source, |
|
| 319 |
- Strategy: bcCopy.Spec.Strategy, |
|
| 320 |
- Output: bcCopy.Spec.Output, |
|
| 321 |
- Revision: revision, |
|
| 322 |
- Resources: bcCopy.Spec.Resources, |
|
| 317 |
+ ServiceAccount: serviceAccount, |
|
| 318 |
+ Source: bcCopy.Spec.Source, |
|
| 319 |
+ Strategy: bcCopy.Spec.Strategy, |
|
| 320 |
+ Output: bcCopy.Spec.Output, |
|
| 321 |
+ Revision: revision, |
|
| 322 |
+ Resources: bcCopy.Spec.Resources, |
|
| 323 |
+ CompletionDeadlineSeconds: bcCopy.Spec.CompletionDeadlineSeconds, |
|
| 323 | 324 |
}, |
| 324 | 325 |
ObjectMeta: kapi.ObjectMeta{
|
| 325 | 326 |
Labels: bcCopy.Labels, |
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
"strconv" |
| 9 | 9 |
"strings" |
| 10 | 10 |
"text/tabwriter" |
| 11 |
+ "time" |
|
| 11 | 12 |
|
| 12 | 13 |
"github.com/docker/docker/pkg/units" |
| 13 | 14 |
|
| ... | ... |
@@ -249,6 +250,10 @@ func describeBuildSpec(p buildapi.BuildSpec, out *tabwriter.Writer) {
|
| 249 | 249 |
formatString(out, "Revision Message", p.Revision.Git.Message) |
| 250 | 250 |
} |
| 251 | 251 |
} |
| 252 |
+ |
|
| 253 |
+ if p.CompletionDeadlineSeconds != nil {
|
|
| 254 |
+ formatString(out, "Fail Build After", time.Duration(*p.CompletionDeadlineSeconds)*time.Second) |
|
| 255 |
+ } |
|
| 252 | 256 |
} |
| 253 | 257 |
|
| 254 | 258 |
func describeSourceStrategy(s *buildapi.SourceBuildStrategy, out *tabwriter.Writer) {
|
| 255 | 259 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,84 @@ |
| 0 |
+package builds |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ kapi "k8s.io/kubernetes/pkg/api" |
|
| 4 |
+ "k8s.io/kubernetes/pkg/fields" |
|
| 5 |
+ "k8s.io/kubernetes/pkg/labels" |
|
| 6 |
+ |
|
| 7 |
+ g "github.com/onsi/ginkgo" |
|
| 8 |
+ o "github.com/onsi/gomega" |
|
| 9 |
+ |
|
| 10 |
+ buildapi "github.com/openshift/origin/pkg/build/api" |
|
| 11 |
+ buildutil "github.com/openshift/origin/pkg/build/util" |
|
| 12 |
+ exutil "github.com/openshift/origin/test/extended/util" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+var _ = g.Describe("builds: build with CompletionDeadlineSeconds", func() {
|
|
| 16 |
+ defer g.GinkgoRecover() |
|
| 17 |
+ var ( |
|
| 18 |
+ sourceFixture = exutil.FixturePath("..", "extended", "fixtures", "test-cds-sourcebuild.json")
|
|
| 19 |
+ dockerFixture = exutil.FixturePath("..", "extended", "fixtures", "test-cds-dockerbuild.json")
|
|
| 20 |
+ oc = exutil.NewCLI("cli-start-build", exutil.KubeConfigPath())
|
|
| 21 |
+ ) |
|
| 22 |
+ |
|
| 23 |
+ g.JustBeforeEach(func() {
|
|
| 24 |
+ g.By("waiting for builder service account")
|
|
| 25 |
+ err := exutil.WaitForBuilderAccount(oc.KubeREST().ServiceAccounts(oc.Namespace())) |
|
| 26 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 27 |
+ }) |
|
| 28 |
+ |
|
| 29 |
+ g.Describe("oc start-build source-build --wait", func() {
|
|
| 30 |
+ g.It("Source: should start a build and wait for the build failed and build pod being killed by kubelet", func() {
|
|
| 31 |
+ |
|
| 32 |
+ g.By("calling oc create source-build")
|
|
| 33 |
+ err := oc.Run("create").Args("-f", sourceFixture).Execute()
|
|
| 34 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 35 |
+ |
|
| 36 |
+ g.By("starting the build with --wait flag")
|
|
| 37 |
+ _, err = oc.Run("start-build").Args("source-build", "--wait").Output()
|
|
| 38 |
+ o.Expect(err).To(o.HaveOccurred()) |
|
| 39 |
+ |
|
| 40 |
+ g.By("verifying the build status")
|
|
| 41 |
+ builds, err := oc.REST().Builds(oc.Namespace()).List(labels.Everything(), fields.Everything()) |
|
| 42 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 43 |
+ o.Expect(builds.Items).To(o.HaveLen(1)) |
|
| 44 |
+ |
|
| 45 |
+ build := builds.Items[0] |
|
| 46 |
+ o.Expect(build.Status.Phase).Should(o.BeEquivalentTo(buildapi.BuildPhaseFailed)) |
|
| 47 |
+ |
|
| 48 |
+ g.By("verifying the build pod status")
|
|
| 49 |
+ pod, err := oc.KubeREST().Pods(oc.Namespace()).Get(buildutil.GetBuildPodName(&build)) |
|
| 50 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 51 |
+ o.Expect(pod.Status.Phase).Should(o.BeEquivalentTo(kapi.PodFailed)) |
|
| 52 |
+ o.Expect(pod.Status.Reason).Should(o.ContainSubstring("DeadlineExceeded"))
|
|
| 53 |
+ }) |
|
| 54 |
+ }) |
|
| 55 |
+ |
|
| 56 |
+ g.Describe("oc start-build docker-build --wait", func() {
|
|
| 57 |
+ g.It("Docker: should start a build and wait for the build failed and build pod being killed by kubelet", func() {
|
|
| 58 |
+ |
|
| 59 |
+ g.By("calling oc create docker-build")
|
|
| 60 |
+ err := oc.Run("create").Args("-f", dockerFixture).Execute()
|
|
| 61 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 62 |
+ |
|
| 63 |
+ g.By("starting the build with --wait flag")
|
|
| 64 |
+ _, err = oc.Run("start-build").Args("docker-build", "--wait").Output()
|
|
| 65 |
+ o.Expect(err).To(o.HaveOccurred()) |
|
| 66 |
+ |
|
| 67 |
+ g.By("verifying the build status")
|
|
| 68 |
+ builds, err := oc.REST().Builds(oc.Namespace()).List(labels.Everything(), fields.Everything()) |
|
| 69 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 70 |
+ o.Expect(builds.Items).To(o.HaveLen(1)) |
|
| 71 |
+ |
|
| 72 |
+ build := builds.Items[0] |
|
| 73 |
+ o.Expect(build.Status.Phase).Should(o.BeEquivalentTo(buildapi.BuildPhaseFailed)) |
|
| 74 |
+ |
|
| 75 |
+ g.By("verifying the build pod status")
|
|
| 76 |
+ pod, err := oc.KubeREST().Pods(oc.Namespace()).Get(buildutil.GetBuildPodName(&build)) |
|
| 77 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 78 |
+ o.Expect(pod.Status.Phase).Should(o.BeEquivalentTo(kapi.PodFailed)) |
|
| 79 |
+ o.Expect(pod.Status.Reason).Should(o.ContainSubstring("DeadlineExceeded"))
|
|
| 80 |
+ }) |
|
| 81 |
+ }) |
|
| 82 |
+ |
|
| 83 |
+}) |
| 0 | 84 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 0 |
+{
|
|
| 1 |
+ "kind":"BuildConfig", |
|
| 2 |
+ "apiVersion":"v1", |
|
| 3 |
+ "metadata":{
|
|
| 4 |
+ "name":"docker-build" |
|
| 5 |
+ }, |
|
| 6 |
+ "spec":{
|
|
| 7 |
+ "completionDeadlineSeconds": 5, |
|
| 8 |
+ "triggers":[], |
|
| 9 |
+ "source":{
|
|
| 10 |
+ "type":"Git", |
|
| 11 |
+ "git":{
|
|
| 12 |
+ "uri":"https://github.com/openshift/origin" |
|
| 13 |
+ }, |
|
| 14 |
+ "contextDir":"test/extended/fixtures/test-build-app" |
|
| 15 |
+ }, |
|
| 16 |
+ "strategy":{
|
|
| 17 |
+ "type":"Docker", |
|
| 18 |
+ "dockerStrategy":{
|
|
| 19 |
+ "from":{
|
|
| 20 |
+ "kind":"DockerImage", |
|
| 21 |
+ "name":"openshift/ruby-20-centos7" |
|
| 22 |
+ } |
|
| 23 |
+ } |
|
| 24 |
+ } |
|
| 25 |
+ } |
|
| 26 |
+} |
| 0 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,44 @@ |
| 0 |
+{
|
|
| 1 |
+ "kind": "List", |
|
| 2 |
+ "apiVersion": "v1", |
|
| 3 |
+ "metadata": {},
|
|
| 4 |
+ "items": [ |
|
| 5 |
+ {
|
|
| 6 |
+ "kind": "ImageStream", |
|
| 7 |
+ "apiVersion": "v1", |
|
| 8 |
+ "metadata": {
|
|
| 9 |
+ "name": "origin-ruby-sample" |
|
| 10 |
+ }, |
|
| 11 |
+ "spec": {},
|
|
| 12 |
+ "status": {
|
|
| 13 |
+ "dockerImageRepository": "" |
|
| 14 |
+ } |
|
| 15 |
+ }, |
|
| 16 |
+ {
|
|
| 17 |
+ "kind": "BuildConfig", |
|
| 18 |
+ "apiVersion": "v1", |
|
| 19 |
+ "metadata": {
|
|
| 20 |
+ "name": "source-build" |
|
| 21 |
+ }, |
|
| 22 |
+ "spec": {
|
|
| 23 |
+ "completionDeadlineSeconds": 5, |
|
| 24 |
+ "triggers": [], |
|
| 25 |
+ "source": {
|
|
| 26 |
+ "type": "Git", |
|
| 27 |
+ "git": {
|
|
| 28 |
+ "uri": "git://github.com/openshift/ruby-hello-world.git" |
|
| 29 |
+ } |
|
| 30 |
+ }, |
|
| 31 |
+ "strategy": {
|
|
| 32 |
+ "type": "Source", |
|
| 33 |
+ "sourceStrategy": {
|
|
| 34 |
+ "from": {
|
|
| 35 |
+ "kind": "DockerImage", |
|
| 36 |
+ "name": "openshift/ruby-20-centos7" |
|
| 37 |
+ } |
|
| 38 |
+ } |
|
| 39 |
+ } |
|
| 40 |
+ } |
|
| 41 |
+ } |
|
| 42 |
+ ] |
|
| 43 |
+} |