Skip to content

Commit de825d0

Browse files
committed
Introducing InternalTektonResultType as a ResultType
In light of tektoncd#3087 the need for a ResultType that is not exposed as a TaskRunResult or PipelineResourceResult arises. In tektoncd#3087, a Step can emit a result indicating a Step timeout has occurred. This is a result that should not be exposed hence the need for a new ResultType called InternalTektonResultType. This commit ensures results of this type are filtered out. Introducing an InternalTektonResultType ensures a future proof solution to internal results that should not be exposed. Aside from the example in tektoncd#3087, a present candidate is the result written out by a Step containing a "StartedAt" key. Currently this result is filtered out with a specific function. Marking it as an InternalTektonResultType now allows for this result to automatically be filtered out. Additionally this commit brings about refactoring (and sometimes renaming) of functions related to converting pod statuses to taskrun statuses from pkg/reconciler/taskrun/taskrun.go to pkg/pod/status/status.go. This is accompanied with moving unit test cases from taskrun_test.go to status_test.go.
1 parent fb296e6 commit de825d0

File tree

6 files changed

+439
-398
lines changed

6 files changed

+439
-398
lines changed

pkg/apis/pipeline/v1beta1/task_types.go

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const (
2626
TaskRunResultType ResultType = "TaskRunResult"
2727
// PipelineResourceResultType default pipeline result value
2828
PipelineResourceResultType ResultType = "PipelineResourceResult"
29+
// InternalTektonResultType default internal tekton result value
30+
InternalTektonResultType ResultType = "InternalTektonResult"
2931
// UnknownResultType default unknown result type value
3032
UnknownResultType ResultType = ""
3133
)

pkg/entrypoint/entrypointer.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,9 @@ func (e Entrypointer) Go() error {
102102
// *but* we write postfile to make next steps bail too.
103103
e.WritePostFile(e.PostFile, err)
104104
output = append(output, v1beta1.PipelineResourceResult{
105-
Key: "StartedAt",
106-
Value: time.Now().Format(timeFormat),
105+
Key: "StartedAt",
106+
Value: time.Now().Format(timeFormat),
107+
ResultType: v1beta1.InternalTektonResultType,
107108
})
108109

109110
return err
@@ -114,8 +115,9 @@ func (e Entrypointer) Go() error {
114115
e.Args = append([]string{e.Entrypoint}, e.Args...)
115116
}
116117
output = append(output, v1beta1.PipelineResourceResult{
117-
Key: "StartedAt",
118-
Value: time.Now().Format(timeFormat),
118+
Key: "StartedAt",
119+
Value: time.Now().Format(timeFormat),
120+
ResultType: v1beta1.InternalTektonResultType,
119121
})
120122

121123
err := e.Runner.Run(e.Args...)

pkg/pod/status.go

+81-39
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package pod
1818

1919
import (
20-
"encoding/json"
2120
"fmt"
2221
"sort"
2322
"strings"
@@ -97,27 +96,51 @@ func SidecarsReady(podStatus corev1.PodStatus) bool {
9796
}
9897

9998
// MakeTaskRunStatus returns a TaskRunStatus based on the Pod's status.
100-
func MakeTaskRunStatus(logger *zap.SugaredLogger, tr v1beta1.TaskRun, pod *corev1.Pod, taskSpec v1beta1.TaskSpec) v1beta1.TaskRunStatus {
99+
func MakeTaskRunStatus(logger *zap.SugaredLogger, tr v1beta1.TaskRun, pod *corev1.Pod, taskSpec v1beta1.TaskSpec) (v1beta1.TaskRunStatus, error) {
101100
trs := &tr.Status
102101
if trs.GetCondition(apis.ConditionSucceeded) == nil || trs.GetCondition(apis.ConditionSucceeded).Status == corev1.ConditionUnknown {
103102
// If the taskRunStatus doesn't exist yet, it's because we just started running
104103
MarkStatusRunning(trs, v1beta1.TaskRunReasonRunning.String(), "Not all Steps in the Task have finished executing")
105104
}
106105

106+
// Complete if we did not find a step that is not complete, or the pod is in a definitely complete phase
107+
complete := areStepsComplete(pod) || pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed
108+
109+
if complete {
110+
updateCompletedTaskRun(trs, pod)
111+
} else {
112+
updateIncompleteTaskRun(trs, pod)
113+
}
114+
107115
trs.PodName = pod.Name
108116
trs.Steps = []v1beta1.StepState{}
109117
trs.Sidecars = []v1beta1.SidecarState{}
110118

119+
var err error
120+
111121
for _, s := range pod.Status.ContainerStatuses {
112122
if IsContainerStep(s.Name) {
113123
if s.State.Terminated != nil && len(s.State.Terminated.Message) != 0 {
114-
message, time, err := removeStartInfoFromTerminationMessage(s)
124+
125+
var results []v1beta1.PipelineResourceResult
126+
results, err = termination.ParseMessage(s.State.Terminated.Message)
115127
if err != nil {
116-
logger.Errorf("error setting the start time of step %q in taskrun %q: %w", s.Name, tr.Name, err)
117-
}
118-
if time != nil {
119-
s.State.Terminated.StartedAt = *time
120-
s.State.Terminated.Message = message
128+
logger.Errorf("termination message could not be parsed as JSON: %v", err)
129+
} else {
130+
//Further processing if the termination message is JSON formatted
131+
var time *metav1.Time
132+
time, err = extractStartedAtTimeFromResults(results)
133+
if err != nil {
134+
logger.Errorf("error setting the start time of step %q in taskrun %q: %v", s.Name, tr.Name, err)
135+
}
136+
if time != nil {
137+
s.State.Terminated.StartedAt = *time
138+
}
139+
if tr.IsSuccessful() {
140+
taskResults, pipelineResourceResults := filterResultsAndResources(results)
141+
trs.TaskRunResults = append(trs.TaskRunResults, taskResults...)
142+
trs.ResourcesResult = append(trs.ResourcesResult, pipelineResourceResults...)
143+
}
121144
}
122145
}
123146
trs.Steps = append(trs.Steps, v1beta1.StepState{
@@ -135,51 +158,70 @@ func MakeTaskRunStatus(logger *zap.SugaredLogger, tr v1beta1.TaskRun, pod *corev
135158
})
136159
}
137160
}
138-
139-
// Complete if we did not find a step that is not complete, or the pod is in a definitely complete phase
140-
complete := areStepsComplete(pod) || pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed
141-
142-
if complete {
143-
updateCompletedTaskRun(trs, pod)
144-
} else {
145-
updateIncompleteTaskRun(trs, pod)
146-
}
161+
trs.TaskRunResults = removeDuplicateResults(trs.TaskRunResults)
147162

148163
// Sort step states according to the order specified in the TaskRun spec's steps.
149164
trs.Steps = sortTaskRunStepOrder(trs.Steps, taskSpec.Steps)
150165

151-
return *trs
166+
return *trs, err
167+
}
168+
169+
func filterResultsAndResources(results []v1beta1.PipelineResourceResult) ([]v1beta1.TaskRunResult, []v1beta1.PipelineResourceResult) {
170+
var taskResults []v1beta1.TaskRunResult
171+
var pipelineResourceResults []v1beta1.PipelineResourceResult
172+
for _, r := range results {
173+
switch r.ResultType {
174+
case v1beta1.TaskRunResultType:
175+
taskRunResult := v1beta1.TaskRunResult{
176+
Name: r.Key,
177+
Value: r.Value,
178+
}
179+
taskResults = append(taskResults, taskRunResult)
180+
case v1beta1.InternalTektonResultType:
181+
// Internal messages are ignored because they're not used as external result
182+
continue
183+
case v1beta1.PipelineResourceResultType:
184+
fallthrough
185+
default:
186+
pipelineResourceResults = append(pipelineResourceResults, r)
187+
}
188+
}
189+
190+
return taskResults, pipelineResourceResults
152191
}
153192

154-
// removeStartInfoFromTerminationMessage searches for a result called "StartedAt" in the JSON-formatted
155-
// termination message of a step and returns the values to use for sets State.Terminated if it's
156-
// found. The "StartedAt" result is also removed from the list of results in the container status.
157-
func removeStartInfoFromTerminationMessage(s corev1.ContainerStatus) (string, *metav1.Time, error) {
158-
r, err := termination.ParseMessage(s.State.Terminated.Message)
159-
if err != nil {
160-
return "", nil, fmt.Errorf("termination message could not be parsed as JSON: %w", err)
193+
func removeDuplicateResults(taskRunResult []v1beta1.TaskRunResult) []v1beta1.TaskRunResult {
194+
if len(taskRunResult) == 0 {
195+
return nil
196+
}
197+
198+
uniq := make([]v1beta1.TaskRunResult, 0)
199+
latest := make(map[string]v1beta1.TaskRunResult, 0)
200+
for _, res := range taskRunResult {
201+
if _, seen := latest[res.Name]; !seen {
202+
uniq = append(uniq, res)
203+
}
204+
latest[res.Name] = res
205+
}
206+
for i, res := range uniq {
207+
uniq[i] = latest[res.Name]
161208
}
162-
for index, result := range r {
209+
return uniq
210+
}
211+
212+
func extractStartedAtTimeFromResults(results []v1beta1.PipelineResourceResult) (*metav1.Time, error) {
213+
214+
for _, result := range results {
163215
if result.Key == "StartedAt" {
164216
t, err := time.Parse(timeFormat, result.Value)
165217
if err != nil {
166-
return "", nil, fmt.Errorf("could not parse time value %q in StartedAt field: %w", result.Value, err)
218+
return nil, fmt.Errorf("could not parse time value %q in StartedAt field: %w", result.Value, err)
167219
}
168-
message := ""
169220
startedAt := metav1.NewTime(t)
170-
// remove the entry for the starting time
171-
r = append(r[:index], r[index+1:]...)
172-
if len(r) == 0 {
173-
message = ""
174-
} else if bytes, err := json.Marshal(r); err != nil {
175-
return "", nil, fmt.Errorf("error marshalling remaining results back into termination message: %w", err)
176-
} else {
177-
message = string(bytes)
178-
}
179-
return message, &startedAt, nil
221+
return &startedAt, nil
180222
}
181223
}
182-
return "", nil, nil
224+
return nil, nil
183225
}
184226

185227
func updateCompletedTaskRun(trs *v1beta1.TaskRunStatus, pod *corev1.Pod) {

0 commit comments

Comments
 (0)