From 16ed163f06670b1f76ce6947c428d93e15cb1f3b Mon Sep 17 00:00:00 2001 From: ericzzzzzzz <102683393+ericzzzzzzz@users.noreply.github.com> Date: Sun, 25 Feb 2024 09:59:59 -0500 Subject: [PATCH] feat: surface artifacts through termination message --- pkg/apis/pipeline/v1/artifact_types.go | 5 + pkg/apis/pipeline/v1beta1/artifact_types.go | 5 + pkg/entrypoint/entrypointer.go | 19 ++++ pkg/pod/status.go | 12 +++ pkg/pod/status_test.go | 112 ++++++++++++++++++++ pkg/result/result.go | 5 + 6 files changed, 158 insertions(+) diff --git a/pkg/apis/pipeline/v1/artifact_types.go b/pkg/apis/pipeline/v1/artifact_types.go index dbad6637be4..7f08165c823 100644 --- a/pkg/apis/pipeline/v1/artifact_types.go +++ b/pkg/apis/pipeline/v1/artifact_types.go @@ -11,3 +11,8 @@ type ArtifactValue struct { } type TaskRunStepArtifact = Artifact + +type Artifacts struct { + Inputs []Artifact `json:"inputs"` + Outputs []Artifact `json:"outputs"` +} diff --git a/pkg/apis/pipeline/v1beta1/artifact_types.go b/pkg/apis/pipeline/v1beta1/artifact_types.go index 19e3f950319..0fb5c34cab5 100644 --- a/pkg/apis/pipeline/v1beta1/artifact_types.go +++ b/pkg/apis/pipeline/v1beta1/artifact_types.go @@ -11,3 +11,8 @@ type ArtifactValue struct { } type TaskRunStepArtifact = Artifact + +type Artifacts struct { + Inputs []Artifact `json:"inputs"` + Outputs []Artifact `json:"outputs"` +} diff --git a/pkg/entrypoint/entrypointer.go b/pkg/entrypoint/entrypointer.go index 199de2b79d7..c68d4654778 100644 --- a/pkg/entrypoint/entrypointer.go +++ b/pkg/entrypoint/entrypointer.go @@ -269,9 +269,28 @@ func (e Entrypointer) Go() error { } } + artifacts, err := e.readArtifacts() + if err != nil { + logger.Fatalf("Error while handling artifacts: %s", err) + } + output = append(output, artifacts...) + return err } +func (e Entrypointer) readArtifacts() ([]result.RunResult, error) { + fp := filepath.Join(e.StepMetadataDir, "artifacts", "provenance.json") + + file, err := os.ReadFile(fp) + if os.IsNotExist(err) { + return []result.RunResult{}, nil + } + if err != nil { + return nil, err + } + return []result.RunResult{{Key: fp, Value: string(file), ResultType: result.ArtifactsResultType}}, nil +} + func (e Entrypointer) readResultsFromDisk(ctx context.Context, resultDir string, resultType result.ResultType) error { output := []result.RunResult{} results := e.Results diff --git a/pkg/pod/status.go b/pkg/pod/status.go index f3ad845c3f6..595e380e663 100644 --- a/pkg/pod/status.go +++ b/pkg/pod/status.go @@ -273,6 +273,7 @@ func setTaskRunStatusBasedOnStepStatus(ctx context.Context, logger *zap.SugaredL // Parse termination messages terminationReason := "" + var as v1.Artifacts if state.Terminated != nil && len(state.Terminated.Message) != 0 { msg := state.Terminated.Message @@ -281,6 +282,15 @@ func setTaskRunStatusBasedOnStepStatus(ctx context.Context, logger *zap.SugaredL logger.Errorf("termination message could not be parsed as JSON: %v", err) merr = multierror.Append(merr, err) } else { + for _, r := range results { + if r.ResultType == result.ArtifactsResultType { + if err := json.Unmarshal([]byte(r.Value), &as); err != nil { + merr = multierror.Append(merr, err) + } + // there should be only one ArtifactsResult + break + } + } time, err := extractStartedAtTimeFromResults(results) if err != nil { logger.Errorf("error setting the start time of step %q in taskrun %q: %v", s.Name, tr.Name, err) @@ -324,6 +334,8 @@ func setTaskRunStatusBasedOnStepStatus(ctx context.Context, logger *zap.SugaredL ImageID: s.ImageID, Results: taskRunStepResults, TerminationReason: terminationReason, + Inputs: as.Inputs, + Outputs: as.Outputs, }) } diff --git a/pkg/pod/status_test.go b/pkg/pod/status_test.go index 5686acbf0e0..5958c97989a 100644 --- a/pkg/pod/status_test.go +++ b/pkg/pod/status_test.go @@ -433,6 +433,118 @@ func TestMakeTaskRunStatus_StepResults(t *testing.T) { } } +func TestMakeTaskRunStatus_StepArtifacts(t *testing.T) { + for _, c := range []struct { + desc string + podStatus corev1.PodStatus + pod corev1.Pod + tr v1.TaskRun + want v1.TaskRunStatus + }{ + { + desc: "step artifacts result type", + podStatus: corev1.PodStatus{ + Phase: corev1.PodSucceeded, + ContainerStatuses: []corev1.ContainerStatus{{ + Name: "step-one", + State: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + Message: `[{"key":"/tekton/run/0/status/artifacts/provenance.json","value":"{\n \"inputs\":[\n {\n \"name\":\"input-artifacts\",\n \"values\":[\n {\n \"uri\":\"git:jjjsss\",\n \"digest\":{\n \"sha256\":\"b35cacccfdb1e24dc497d15d553891345fd155713ffe647c281c583269eaaae0\"\n }\n }\n ]\n }\n ],\n \"outputs\":[\n {\n \"name\":\"build-results\",\n \"values\":[\n {\n \"uri\":\"pkg:balba\",\n \"digest\":{\n \"sha256\":\"df85b9e3983fe2ce20ef76ad675ecf435cc99fc9350adc54fa230bae8c32ce48\",\n \"sha1\":\"95588b8f34c31eb7d62c92aaa4e6506639b06ef2\"\n }\n }\n ]\n }\n ]\n}\n","type":5}]`, + }, + }, + }}, + }, + tr: v1.TaskRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: "task-run", + Namespace: "foo", + }, + Spec: v1.TaskRunSpec{ + TaskSpec: &v1.TaskSpec{ + Steps: []v1.Step{{ + Name: "one", + }}, + }, + }, + }, + want: v1.TaskRunStatus{ + Status: statusSuccess(), + TaskRunStatusFields: v1.TaskRunStatusFields{ + Steps: []v1.StepState{{ + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + Message: `[{"key":"/tekton/run/0/status/artifacts/provenance.json","value":"{\n \"inputs\":[\n {\n \"name\":\"input-artifacts\",\n \"values\":[\n {\n \"uri\":\"git:jjjsss\",\n \"digest\":{\n \"sha256\":\"b35cacccfdb1e24dc497d15d553891345fd155713ffe647c281c583269eaaae0\"\n }\n }\n ]\n }\n ],\n \"outputs\":[\n {\n \"name\":\"build-results\",\n \"values\":[\n {\n \"uri\":\"pkg:balba\",\n \"digest\":{\n \"sha256\":\"df85b9e3983fe2ce20ef76ad675ecf435cc99fc9350adc54fa230bae8c32ce48\",\n \"sha1\":\"95588b8f34c31eb7d62c92aaa4e6506639b06ef2\"\n }\n }\n ]\n }\n ]\n}\n","type":5}]`, + }}, + Name: "one", + Container: "step-one", + Inputs: []v1.Artifact{ + { + Name: "input-artifacts", + Values: []v1.ArtifactValue{{ + Digest: map[string]string{"sha256": "b35cacccfdb1e24dc497d15d553891345fd155713ffe647c281c583269eaaae0"}, + Uri: "git:jjjsss", + }, + }, + }, + }, + Outputs: []v1.Artifact{ + { + Name: "build-results", + Values: []v1.ArtifactValue{{ + Digest: map[string]string{ + "sha1": "95588b8f34c31eb7d62c92aaa4e6506639b06ef2", + "sha256": "df85b9e3983fe2ce20ef76ad675ecf435cc99fc9350adc54fa230bae8c32ce48", + }, + Uri: "pkg:balba", + }, + }, + }, + }, + Results: []v1.TaskRunResult{}, + }}, + Sidecars: []v1.SidecarState{}, + // We don't actually care about the time, just that it's not nil + CompletionTime: &metav1.Time{Time: time.Now()}, + }, + }, + }, + } { + t.Run(c.desc, func(t *testing.T) { + now := metav1.Now() + if cmp.Diff(c.pod, corev1.Pod{}) == "" { + c.pod = corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod", + Namespace: "foo", + CreationTimestamp: now, + }, + Status: c.podStatus, + } + } + + logger, _ := logging.NewLogger("", "status") + kubeclient := fakek8s.NewSimpleClientset() + got, err := MakeTaskRunStatus(context.Background(), logger, c.tr, &c.pod, kubeclient, c.tr.Spec.TaskSpec) + if err != nil { + t.Errorf("MakeTaskRunResult: %s", err) + } + + // Common traits, set for test case brevity. + c.want.PodName = "pod" + + ensureTimeNotNil := cmp.Comparer(func(x, y *metav1.Time) bool { + if x == nil { + return y == nil + } + return y != nil + }) + if d := cmp.Diff(c.want, got, ignoreVolatileTime, ensureTimeNotNil); d != "" { + t.Errorf("Diff %s", diff.PrintWantGot(d)) + } + }) + } +} + func TestMakeTaskRunStatus(t *testing.T) { for _, c := range []struct { desc string diff --git a/pkg/result/result.go b/pkg/result/result.go index 515fe9a6025..e3d66b596ff 100644 --- a/pkg/result/result.go +++ b/pkg/result/result.go @@ -35,6 +35,9 @@ const ( UnknownResultType = 10 // StepResultType default step result value StepResultType ResultType = 4 + + // ArtifactsResultType default artifacts result value + ArtifactsResultType ResultType = 5 ) // RunResult is used to write key/value pairs to TaskRun pod termination messages. @@ -88,6 +91,8 @@ func (r *ResultType) UnmarshalJSON(data []byte) error { *r = TaskRunResultType case "InternalTektonResult": *r = InternalTektonResultType + case "ArtifactsResult": + *r = ArtifactsResultType default: *r = UnknownResultType }