/
status.go
121 lines (98 loc) · 3.44 KB
/
status.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package controllers
import (
"context"
"fmt"
korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1"
"github.com/go-logr/logr"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)
type StatusGetter struct {
logger logr.Logger
k8sClient client.Client
}
func NewStatusGetter(logger logr.Logger, k8sClient client.Client) *StatusGetter {
return &StatusGetter{
logger: logger,
k8sClient: k8sClient,
}
}
func (s *StatusGetter) GetStatusConditions(ctx context.Context, job *batchv1.Job) ([]metav1.Condition, error) {
conditions := []metav1.Condition{
{
Type: korifiv1alpha1.TaskInitializedConditionType,
Status: metav1.ConditionTrue,
Reason: "JobCreated",
},
}
if job.Status.StartTime == nil {
return conditions, nil
}
conditions = append(conditions, metav1.Condition{
Type: korifiv1alpha1.TaskStartedConditionType,
Status: metav1.ConditionTrue,
LastTransitionTime: *job.Status.StartTime,
Reason: "JobStarted",
})
if job.Status.Succeeded > 0 && job.Status.CompletionTime != nil {
conditions = append(conditions, metav1.Condition{
Type: korifiv1alpha1.TaskSucceededConditionType,
Status: metav1.ConditionTrue,
LastTransitionTime: *job.Status.CompletionTime,
Reason: "JobSucceeded",
})
}
lastFailureTimestamp := getLastFailureTimestamp(job.Status)
if job.Status.Failed > 0 && lastFailureTimestamp != nil {
terminationState, err := s.getFailedContainerStatus(ctx, job)
if err != nil {
return nil, fmt.Errorf("failed to get container status: %w", err)
}
conditions = append(conditions, metav1.Condition{
Type: korifiv1alpha1.TaskFailedConditionType,
Status: metav1.ConditionTrue,
LastTransitionTime: *lastFailureTimestamp,
Reason: terminationState.Reason,
Message: fmt.Sprintf("Failed with exit code: %d", terminationState.ExitCode),
})
}
return conditions, nil
}
func (s *StatusGetter) getFailedContainerStatus(ctx context.Context, job *batchv1.Job) (*corev1.ContainerStateTerminated, error) {
var jobPods corev1.PodList
if err := s.k8sClient.List(ctx, &jobPods, client.InNamespace(job.Namespace), client.MatchingLabels{"job-name": job.Name}); err != nil {
return nil, err
}
if len(jobPods.Items) > 1 {
return nil, fmt.Errorf("found more than one pod for job %s:%s", job.Namespace, job.Name)
}
if len(jobPods.Items) == 0 {
return nil, fmt.Errorf("no pods found for job %s:%s", job.Namespace, job.Name)
}
jobPod := jobPods.Items[0]
for _, containerStatus := range jobPod.Status.ContainerStatuses {
if containerStatus.Name != workloadContainerName {
continue
}
if containerStatus.State.Terminated == nil {
return nil, fmt.Errorf("no terminated state found for job %s:%s", job.Namespace, job.Name)
}
return containerStatus.State.Terminated, nil
}
return nil, fmt.Errorf("no workload container found for job %s:%s", job.Namespace, job.Name)
}
func getLastFailureTimestamp(jobStatus batchv1.JobStatus) *metav1.Time {
var lastFailure *metav1.Time
for _, condition := range jobStatus.Conditions {
condition := condition
if condition.Type != batchv1.JobFailed {
continue
}
if lastFailure == nil || condition.LastTransitionTime.After(lastFailure.Time) {
lastFailure = &condition.LastTransitionTime
}
}
return lastFailure
}