You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pc...@apache.org on 2023/05/24 13:50:25 UTC
[camel-k] 04/09: feat(build): added container execution condition
This is an automated email from the ASF dual-hosted git repository.
pcongiusti pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 1494ca9f1f78e7367ec58adb6534333929e3d783
Author: Pasquale Congiusti <pa...@gmail.com>
AuthorDate: Fri May 19 11:45:11 2023 +0200
feat(build): added container execution condition
* Added a condition for each container execution during build (either success or failure)
* Added E2E test to cover such feature
---
e2e/common/traits/builder_test.go | 43 ++++++++++++++++++++++++++++++++++++-
e2e/support/test_support.go | 10 +++++++++
pkg/controller/build/monitor_pod.go | 41 +++++++++++++++++++----------------
3 files changed, 74 insertions(+), 20 deletions(-)
diff --git a/e2e/common/traits/builder_test.go b/e2e/common/traits/builder_test.go
index 9b47e48ff..da8e6f39b 100644
--- a/e2e/common/traits/builder_test.go
+++ b/e2e/common/traits/builder_test.go
@@ -93,7 +93,7 @@ func TestBuilderTrait(t *testing.T) {
Eventually(BuilderPod(ns, builderKitName)().Spec.InitContainers[0].Resources.Requests.Memory().String(), TestTimeoutShort).Should(Equal("2Gi"))
Eventually(BuilderPod(ns, builderKitName)().Spec.InitContainers[0].Resources.Limits.Memory().String(), TestTimeoutShort).Should(Equal("3Gi"))
- Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
+ Expect(Kamel("reset", "-n", ns).Execute()).To(Succeed())
})
t.Run("Run custom pipeline task", func(t *testing.T) {
@@ -114,9 +114,50 @@ func TestBuilderTrait(t *testing.T) {
Eventually(BuilderPod(ns, builderKitName)().Spec.InitContainers[0].Name, TestTimeoutShort).Should(Equal("builder"))
Eventually(BuilderPod(ns, builderKitName)().Spec.InitContainers[1].Name, TestTimeoutShort).Should(Equal("custom1"))
Eventually(BuilderPod(ns, builderKitName)().Spec.InitContainers[2].Name, TestTimeoutShort).Should(Equal("custom2"))
+
+ // Check containers conditions
+ Eventually(Build(ns, integrationKitName), TestTimeoutShort).ShouldNot(BeNil())
+ Eventually(
+ Build(
+ ns, integrationKitName)().Status.GetCondition(v1.BuildConditionType("Container custom1 succeeded")).Status,
+ TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(
+ Build(ns, integrationKitName)().Status.GetCondition(v1.BuildConditionType("Container custom1 succeeded")).Message,
+ TestTimeoutShort).Should(ContainSubstring("generated-bytecode.jar"))
+ Eventually(Build(ns, integrationKitName), TestTimeoutShort).ShouldNot(BeNil())
+ Eventually(
+ Build(ns, integrationKitName)().Status.GetCondition(v1.BuildConditionType("Container custom2 succeeded")).Status,
+ TestTimeoutShort).Should(Equal(corev1.ConditionTrue))
+ Eventually(
+ Build(ns, integrationKitName)().Status.GetCondition(v1.BuildConditionType("Container custom2 succeeded")).Message,
+ TestTimeoutShort).Should(ContainSubstring("</project>"))
+
+ // Check logs
Eventually(Logs(ns, builderKitName, corev1.PodLogOptions{Container: "custom1"})).Should(ContainSubstring(`generated-bytecode.jar`))
Eventually(Logs(ns, builderKitName, corev1.PodLogOptions{Container: "custom2"})).Should(ContainSubstring(`<artifactId>camel-k-runtime-bom</artifactId>`))
Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
})
+
+ name = "java-error"
+ t.Run("Run custom pipeline task error", func(t *testing.T) {
+ Expect(KamelRunWithID(operatorID, ns, "files/Java.java",
+ "--name", name,
+ "-t", "builder.tasks=custom1;alpine;cat missingfile.txt",
+ ).Execute()).To(Succeed())
+
+ Eventually(IntegrationPhase(ns, name)).Should(Equal(v1.IntegrationPhaseBuildingKit))
+ integrationKitName := IntegrationKit(ns, name)()
+ // Check containers conditions
+ Eventually(Build(ns, integrationKitName), TestTimeoutShort).ShouldNot(BeNil())
+ Eventually(BuildConditions(ns, integrationKitName), TestTimeoutShort).ShouldNot(BeNil())
+ Eventually(
+ Build(ns, integrationKitName)().Status.GetCondition(v1.BuildConditionType("Container custom1 succeeded")).Status,
+ TestTimeoutShort).Should(Equal(corev1.ConditionFalse))
+ Eventually(
+ Build(ns, integrationKitName)().Status.GetCondition(v1.BuildConditionType("Container custom1 succeeded")).Message,
+ TestTimeoutShort).Should(ContainSubstring("No such file or directory"))
+
+ Expect(Kamel("delete", "--all", "-n", ns).Execute()).To(Succeed())
+ })
}
diff --git a/e2e/support/test_support.go b/e2e/support/test_support.go
index 6ebb7976b..3dd61b2f9 100644
--- a/e2e/support/test_support.go
+++ b/e2e/support/test_support.go
@@ -1630,6 +1630,16 @@ func BuildPhase(ns, name string) func() v1.BuildPhase {
}
}
+func BuildConditions(ns, name string) func() []v1.BuildCondition {
+ return func() []v1.BuildCondition {
+ build := Build(ns, name)()
+ if build != nil && &build.Status != nil && build.Status.Conditions != nil {
+ return build.Status.Conditions
+ }
+ return nil
+ }
+}
+
func BuildFailureRecovery(ns, name string) func() int {
return func() int {
build := Build(ns, name)()
diff --git a/pkg/controller/build/monitor_pod.go b/pkg/controller/build/monitor_pod.go
index 2e64e667d..439cacf0f 100644
--- a/pkg/controller/build/monitor_pod.go
+++ b/pkg/controller/build/monitor_pod.go
@@ -139,7 +139,7 @@ func (action *monitorPodAction) Handle(ctx context.Context, build *v1.Build) (*v
finishedAt := action.getTerminatedTime(pod)
duration := finishedAt.Sub(build.Status.StartedAt.Time)
build.Status.Duration = duration.String()
-
+ action.setConditionsFromTerminationMessages(ctx, pod, &build.Status)
monitorFinishedBuild(build)
buildCreator := kubernetes.GetCamelCreator(build)
@@ -168,15 +168,12 @@ func (action *monitorPodAction) Handle(ctx context.Context, build *v1.Build) (*v
case corev1.PodFailed:
phase := v1.BuildPhaseFailed
- message := "Pod failed"
- if terminationMessage := action.getTerminationMessage(ctx, pod); terminationMessage != "" {
- message = terminationMessage
- }
+ message := fmt.Sprintf("Builder Pod %s failed (see conditions for more details)", pod.Name)
if pod.DeletionTimestamp != nil {
phase = v1.BuildPhaseInterrupted
- message = "Pod deleted"
+ message = fmt.Sprintf("Builder Pod %s deleted", pod.Name)
} else if _, ok := pod.GetAnnotations()[timeoutAnnotation]; ok {
- message = "Build timeout"
+ message = fmt.Sprintf("Builder Pod %s timeout", pod.Name)
}
// Do not override errored build
if build.Status.Phase == v1.BuildPhaseError {
@@ -187,7 +184,7 @@ func (action *monitorPodAction) Handle(ctx context.Context, build *v1.Build) (*v
finishedAt := action.getTerminatedTime(pod)
duration := finishedAt.Sub(build.Status.StartedAt.Time)
build.Status.Duration = duration.String()
-
+ action.setConditionsFromTerminationMessages(ctx, pod, &build.Status)
monitorFinishedBuild(build)
buildCreator := kubernetes.GetCamelCreator(build)
@@ -304,36 +301,42 @@ func (action *monitorPodAction) getTerminatedTime(pod *corev1.Pod) metav1.Time {
return finishedAt
}
-func (action *monitorPodAction) getTerminationMessage(ctx context.Context, pod *corev1.Pod) string {
+// setConditionsFromTerminationMessages sets a condition for all those containers which have been terminated (successfully or not)
+func (action *monitorPodAction) setConditionsFromTerminationMessages(ctx context.Context, pod *corev1.Pod, buildStatus *v1.BuildStatus) {
var containers []corev1.ContainerStatus
containers = append(containers, pod.Status.InitContainerStatuses...)
containers = append(containers, pod.Status.ContainerStatuses...)
for _, container := range containers {
- if t := container.State.Terminated; t != nil && t.ExitCode != 0 {
- if t.Message != "" {
- return fmt.Sprintf("Container %s failed with: %s", container.Name, t.Message)
+ if t := container.State.Terminated; t != nil {
+ terminationMessage := t.Message
+ // Dynamic condition type (it depends on each container name)
+ containerConditionType := v1.BuildConditionType(fmt.Sprintf("Container %s succeeded", container.Name))
+ containerSucceeded := corev1.ConditionTrue
+ if t.ExitCode != 0 {
+ containerSucceeded = corev1.ConditionFalse
}
var maxLines int64
- maxLines = 20
+ // TODO we can make it a user variable !?
+ maxLines = 10
logOptions := corev1.PodLogOptions{
Container: container.Name,
TailLines: &maxLines,
}
- message, err := log.DumpLog(ctx, action.client, pod, logOptions)
+ terminationMessage, err := log.DumpLog(ctx, action.client, pod, logOptions)
if err != nil {
- action.L.Errorf(err, "Dumping log for %s Pod failed", pod.Name)
- return fmt.Sprintf(
- "Container %s failed. Operator was not able to retrieve the error message, please, check the container log from %s Pod",
+ action.L.Errorf(err, "Dumping log for %s container in %s Pod failed", container.Name, pod.Name)
+ terminationMessage = fmt.Sprintf(
+ "Operator was not able to retrieve the error message, please, check the container %s log directly from %s Pod",
container.Name,
pod.Name,
)
}
- return fmt.Sprintf("Container %s failed with: %s", container.Name, message)
+ terminationReason := fmt.Sprintf("%s (%d)", t.Reason, t.ExitCode)
+ buildStatus.SetCondition(containerConditionType, containerSucceeded, terminationReason, terminationMessage)
}
}
- return ""
}