diff --git a/docs/taskruns.md b/docs/taskruns.md index 6b0f5dd4b72..ebd145f967f 100644 --- a/docs/taskruns.md +++ b/docs/taskruns.md @@ -18,7 +18,8 @@ A `TaskRun` runs until all `steps` have completed or until a failure occurs. - [Overriding where resources are copied from](#overriding-where-resources-are-copied-from) - [Service Account](#service-account) - [Pod Template](#pod-template) -- [Steps](#steps) +- [Status](#status) + - [Steps](#steps) - [Cancelling a TaskRun](#cancelling-a-taskrun) - [Examples](#examples) - [Sidecars](#sidecars) @@ -300,7 +301,39 @@ spec: emptyDir: {} ``` -## Steps +## Status + +As a TaskRun completes, it's `status` field is filled in with relevant information for +the overall run, as well as each step. + +The following example shows a completed TaskRun and it's `status` field: + +```yaml +completionTime: "2019-08-12T18:22:57Z" +conditions: +- lastTransitionTime: "2019-08-12T18:22:57Z" + message: All Steps have completed executing + reason: Succeeded + status: "True" + type: Succeeded +podName: status-taskrun-pod-6488ef +startTime: "2019-08-12T18:22:51Z" +steps: +- container: step-hello + imageID: docker-pullable://busybox@sha256:895ab622e92e18d6b461d671081757af7dbaa3b00e3e28e12505af7817f73649 + name: hello + terminated: + containerID: docker://d5a54f5bbb8e7a6fd3bc7761b78410403244cf4c9c5822087fb0209bf59e3621 + exitCode: 0 + finishedAt: "2019-08-12T18:22:56Z" + reason: Completed + startedAt: "2019-08-12T18:22:54Z" + ``` + +Fields include start and stop times for the `TaskRun` and each `Step`, and exit codes. +For each step we also include the fully-qualified image used, with the digest. + +### Steps If multiple `steps` are defined in the `Task` invoked by the `TaskRun`, we will see the `status.steps` of the `TaskRun` displayed in the same order as they are defined in diff --git a/pkg/apis/pipeline/v1alpha1/taskrun_types.go b/pkg/apis/pipeline/v1alpha1/taskrun_types.go index 8b88896f7ae..4561eda86d1 100644 --- a/pkg/apis/pipeline/v1alpha1/taskrun_types.go +++ b/pkg/apis/pipeline/v1alpha1/taskrun_types.go @@ -196,6 +196,7 @@ type StepState struct { corev1.ContainerState Name string `json:"name,omitempty"` ContainerName string `json:"container,omitempty"` + ImageID string `json:"imageID,omitempty"` } // CloudEventDelivery is the target of a cloud event along with the state of diff --git a/pkg/status/taskrunpod.go b/pkg/status/taskrunpod.go index 6fa0ce72330..a946ab9e454 100644 --- a/pkg/status/taskrunpod.go +++ b/pkg/status/taskrunpod.go @@ -53,6 +53,7 @@ func UpdateStatusFromPod(taskRun *v1alpha1.TaskRun, pod *corev1.Pod, resourceLis ContainerState: *s.State.DeepCopy(), Name: resources.TrimContainerNamePrefix(s.Name), ContainerName: s.Name, + ImageID: s.ImageID, }) } } diff --git a/pkg/status/taskrunpod_test.go b/pkg/status/taskrunpod_test.go index 4ab34ad3017..adfffda331d 100644 --- a/pkg/status/taskrunpod_test.go +++ b/pkg/status/taskrunpod_test.go @@ -74,9 +74,11 @@ func TestUpdateStatusFromPod(t *testing.T) { podStatus: corev1.PodStatus{ InitContainerStatuses: []corev1.ContainerStatus{{ // creds-init; ignored + ImageID: "ignored", }}, ContainerStatuses: []corev1.ContainerStatus{{ - Name: "step-state-name", + Name: "step-state-name", + ImageID: "", State: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ ExitCode: 123, @@ -102,11 +104,14 @@ func TestUpdateStatusFromPod(t *testing.T) { podStatus: corev1.PodStatus{ InitContainerStatuses: []corev1.ContainerStatus{{ // creds-init; ignored. + ImageID: "ignoreme", }, { // git-init; ignored. + ImageID: "ignoreme", }}, ContainerStatuses: []corev1.ContainerStatus{{ - Name: "step-state-name", + Name: "step-state-name", + ImageID: "image-id", State: corev1.ContainerState{ Terminated: &corev1.ContainerStateTerminated{ ExitCode: 123, @@ -125,6 +130,7 @@ func TestUpdateStatusFromPod(t *testing.T) { }}, Name: "state-name", ContainerName: "step-state-name", + ImageID: "image-id", }}, }, }, { @@ -138,6 +144,7 @@ func TestUpdateStatusFromPod(t *testing.T) { ExitCode: 0, }, }, + ImageID: "image-id", }}, }, want: v1alpha1.TaskRunStatus{ @@ -151,6 +158,7 @@ func TestUpdateStatusFromPod(t *testing.T) { }}, Name: "step-push", ContainerName: "step-step-push", + ImageID: "image-id", }}, // We don't actually care about the time, just that it's not nil CompletionTime: &metav1.Time{Time: time.Now()}, @@ -181,9 +189,10 @@ func TestUpdateStatusFromPod(t *testing.T) { }, { desc: "failure-terminated", podStatus: corev1.PodStatus{ - Phase: corev1.PodFailed, + Phase: corev1.PodFailed, InitContainerStatuses: []corev1.ContainerStatus{{ // creds-init status; ignored + ImageID: "ignore-me", }}, ContainerStatuses: []corev1.ContainerStatus{{ Name: "step-failure", @@ -209,8 +218,10 @@ func TestUpdateStatusFromPod(t *testing.T) { Terminated: &corev1.ContainerStateTerminated{ ExitCode: 123, }}, + Name: "failure", ContainerName: "step-failure", + ImageID: "image-id", }}, // We don't actually care about the time, just that it's not nil CompletionTime: &metav1.Time{Time: time.Now()}, diff --git a/test/taskrun_test.go b/test/taskrun_test.go index d4e49b13dfa..ad9f3dc8c1e 100644 --- a/test/taskrun_test.go +++ b/test/taskrun_test.go @@ -99,8 +99,64 @@ func TestTaskRunFailure(t *testing.T) { Name: "world", ContainerName: "step-world", }} - ignoreFields := cmpopts.IgnoreFields(corev1.ContainerStateTerminated{}, "StartedAt", "FinishedAt", "ContainerID") - if d := cmp.Diff(taskrun.Status.Steps, expectedStepState, ignoreFields); d != "" { + ignoreTerminatedFields := cmpopts.IgnoreFields(corev1.ContainerStateTerminated{}, "StartedAt", "FinishedAt", "ContainerID") + ignoreStepFields := cmpopts.IgnoreFields(v1alpha1.StepState{}, "ImageID") + if d := cmp.Diff(taskrun.Status.Steps, expectedStepState, ignoreTerminatedFields, ignoreStepFields); d != "" { + t.Fatalf("-got, +want: %v", d) + } +} + +func TestTaskRunStatus(t *testing.T) { + c, namespace := setup(t) + t.Parallel() + + knativetest.CleanupOnInterrupt(func() { tearDown(t, c, namespace) }, t.Logf) + defer tearDown(t, c, namespace) + + taskRunName := "status-taskrun" + + fqImageName := "busybox@sha256:895ab622e92e18d6b461d671081757af7dbaa3b00e3e28e12505af7817f73649" + t.Logf("Creating Task and TaskRun in namespace %s", namespace) + task := tb.Task("status-task", namespace, tb.TaskSpec( + // This was the digest of the latest tag as of 8/12/2019 + tb.Step("hello", "busybox@sha256:895ab622e92e18d6b461d671081757af7dbaa3b00e3e28e12505af7817f73649", + tb.Command("/bin/sh"), tb.Args("-c", "echo hello"), + ), + )) + if _, err := c.TaskClient.Create(task); err != nil { + t.Fatalf("Failed to create Task: %s", err) + } + taskRun := tb.TaskRun(taskRunName, namespace, tb.TaskRunSpec( + tb.TaskRunTaskRef("status-task"), + )) + if _, err := c.TaskRunClient.Create(taskRun); err != nil { + t.Fatalf("Failed to create TaskRun: %s", err) + } + + t.Logf("Waiting for TaskRun in namespace %s to fail", namespace) + if err := WaitForTaskRunState(c, taskRunName, TaskRunSucceed(taskRunName), "TaskRunSucceed"); err != nil { + t.Errorf("Error waiting for TaskRun to finish: %s", err) + } + + taskrun, err := c.TaskRunClient.Get(taskRunName, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Couldn't get expected TaskRun %s: %s", taskRunName, err) + } + + expectedStepState := []v1alpha1.StepState{{ + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + Reason: "Completed", + }, + }, + Name: "hello", + ContainerName: "step-hello", + ImageID: "docker-pullable://" + fqImageName, + }} + + ignoreTerminatedFields := cmpopts.IgnoreFields(corev1.ContainerStateTerminated{}, "StartedAt", "FinishedAt", "ContainerID") + if d := cmp.Diff(taskrun.Status.Steps, expectedStepState, ignoreTerminatedFields); d != "" { t.Fatalf("-got, +want: %v", d) } }