diff --git a/pkg/cmd/pipelinerun/logs_test.go b/pkg/cmd/pipelinerun/logs_test.go index 75dd66c922..1bd681fd94 100644 --- a/pkg/cmd/pipelinerun/logs_test.go +++ b/pkg/cmd/pipelinerun/logs_test.go @@ -3768,6 +3768,750 @@ func TestPipelinerunLog_finally(t *testing.T) { test.AssertOutput(t, expected, output) } +func TestLogs_Cluster_Resolver(t *testing.T) { + var ( + pipelineName = "pipeline" + prName = "pipeline-run" + ns = "namespace" + taskName = "task" + trName = "taskrun" + taskPodName = "taskPod" + tr1StartTime = test.FakeClock().Now().Add(20 * time.Second) + tr1Step1Name = "step-1" + ) + + nsList := []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: ns, + }, + }, + } + + prs := []*v1.PipelineRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: prName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/pipeline": prName}, + }, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{ + ResolverRef: v1.ResolverRef{ + Resolver: "cluster", + Params: v1.Params{ + { + Name: "kind", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "pipeline"}, + }, + { + Name: "name", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: pipelineName}, + }, + { + Name: "namespace", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: ns}, + }, + }, + }, + }, + }, + Status: v1.PipelineRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Message: "Success", + }, + }, + }, + PipelineRunStatusFields: v1.PipelineRunStatusFields{ + ChildReferences: []v1.ChildStatusReference{ + { + Name: trName, + PipelineTaskName: taskName, + TypeMeta: runtime.TypeMeta{ + APIVersion: "tekton.dev/v1beta1", + Kind: "TaskRun", + }, + }, + }, + PipelineSpec: &v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: taskName, + TaskRef: &v1.TaskRef{ + Kind: "task", + Name: taskName, + }, + }, + }, + }, + }, + }, + }, + } + + ps := []*v1.Pipeline{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: pipelineName, + Namespace: ns, + }, + Spec: v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: taskName, + TaskRef: &v1.TaskRef{ + Name: taskName, + }, + }, + }, + }, + }, + } + + p := []*corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: taskPodName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/task": pipelineName}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: tr1Step1Name, + Image: tr1Step1Name + ":latest", + }, + }, + }, + }, + } + + trs := []*v1.TaskRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: trName, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{ + Name: taskName, + }, + }, + Status: v1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Type: apis.ConditionSucceeded, + }, + }, + }, + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: tr1StartTime}, + PodName: taskPodName, + Steps: []v1.StepState{ + { + Name: tr1Step1Name, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + }, + }, + }, + }, + } + + cs, _ := test.SeedTestData(t, test.Data{PipelineRuns: prs, Pipelines: ps, Pods: p, Namespaces: nsList}) + cs.Pipeline.Resources = cb.APIResourceList(version, []string{"task", "taskrun", "pipelinerun"}) + tdc := testDynamic.Options{} + dc, err := tdc.Client( + cb.UnstructuredP(ps[0], version), + cb.UnstructuredPR(prs[0], version), + cb.UnstructuredTR(trs[0], version), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + + fakeLogStream := fake.Logs( + fake.Task(taskPodName, + fake.Step(tr1Step1Name, "task-1 completed\n"), + ), + ) + prlo := logOpts(prName, ns, cs, dc, fake.Streamer(fakeLogStream), false, false, false, taskName) + + output, err := fetchLogs(prlo) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + test.AssertOutput(t, "task-1 completed\n\n", output) +} + +func TestLogs_Git_Resolver(t *testing.T) { + var ( + pipelineName = "pipeline" + prName = "pipeline-run" + ns = "namespace" + taskName = "task" + trName = "taskrun" + taskPodName = "taskPod" + tr1StartTime = test.FakeClock().Now().Add(20 * time.Second) + tr1Step1Name = "step-1" + ) + + nsList := []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: ns, + }, + }, + } + + prs := []*v1.PipelineRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: prName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/pipeline": prName}, + }, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{ + ResolverRef: v1.ResolverRef{ + Resolver: "git", + Params: v1.Params{ + { + Name: "kind", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "pipeline"}, + }, + { + Name: "name", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: pipelineName}, + }, + { + Name: "namespace", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: ns}, + }, + }, + }, + }, + }, + Status: v1.PipelineRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Message: "Success", + }, + }, + }, + PipelineRunStatusFields: v1.PipelineRunStatusFields{ + ChildReferences: []v1.ChildStatusReference{ + { + Name: trName, + PipelineTaskName: taskName, + TypeMeta: runtime.TypeMeta{ + APIVersion: "tekton.dev/v1beta1", + Kind: "TaskRun", + }, + }, + }, + PipelineSpec: &v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: taskName, + TaskRef: &v1.TaskRef{ + Kind: "task", + Name: taskName, + }, + }, + }, + }, + }, + }, + }, + } + + ps := []*v1.Pipeline{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: pipelineName, + Namespace: ns, + }, + Spec: v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: taskName, + TaskRef: &v1.TaskRef{ + Name: taskName, + }, + }, + }, + }, + }, + } + + p := []*corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: taskPodName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/task": pipelineName}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: tr1Step1Name, + Image: tr1Step1Name + ":latest", + }, + }, + }, + }, + } + + trs := []*v1.TaskRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: trName, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{ + Name: taskName, + }, + }, + Status: v1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Type: apis.ConditionSucceeded, + }, + }, + }, + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: tr1StartTime}, + PodName: taskPodName, + Steps: []v1.StepState{ + { + Name: tr1Step1Name, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + }, + }, + }, + }, + } + + cs, _ := test.SeedTestData(t, test.Data{PipelineRuns: prs, Pipelines: ps, Pods: p, Namespaces: nsList}) + cs.Pipeline.Resources = cb.APIResourceList(version, []string{"task", "taskrun", "pipelinerun"}) + tdc := testDynamic.Options{} + dc, err := tdc.Client( + cb.UnstructuredP(ps[0], version), + cb.UnstructuredPR(prs[0], version), + cb.UnstructuredTR(trs[0], version), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + + fakeLogStream := fake.Logs( + fake.Task(taskPodName, + fake.Step(tr1Step1Name, "task-1 completed\n"), + ), + ) + prlo := logOpts(prName, ns, cs, dc, fake.Streamer(fakeLogStream), false, false, false, taskName) + + output, err := fetchLogs(prlo) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + test.AssertOutput(t, "task-1 completed\n\n", output) +} + +func TestLogs_Bundle_Resolver(t *testing.T) { + var ( + pipelineName = "pipeline" + prName = "pipeline-run" + ns = "namespace" + taskName = "task" + trName = "taskrun" + taskPodName = "taskPod" + tr1StartTime = test.FakeClock().Now().Add(20 * time.Second) + tr1Step1Name = "step-1" + ) + + nsList := []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: ns, + }, + }, + } + + prs := []*v1.PipelineRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: prName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/pipeline": prName}, + }, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{ + ResolverRef: v1.ResolverRef{ + Resolver: "bundle", + Params: v1.Params{ + { + Name: "kind", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "pipeline"}, + }, + { + Name: "name", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: pipelineName}, + }, + { + Name: "namespace", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: ns}, + }, + }, + }, + }, + }, + Status: v1.PipelineRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Message: "Success", + }, + }, + }, + PipelineRunStatusFields: v1.PipelineRunStatusFields{ + ChildReferences: []v1.ChildStatusReference{ + { + Name: trName, + PipelineTaskName: taskName, + TypeMeta: runtime.TypeMeta{ + APIVersion: "tekton.dev/v1beta1", + Kind: "TaskRun", + }, + }, + }, + PipelineSpec: &v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: taskName, + TaskRef: &v1.TaskRef{ + Kind: "task", + Name: taskName, + }, + }, + }, + }, + }, + }, + }, + } + + ps := []*v1.Pipeline{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: pipelineName, + Namespace: ns, + }, + Spec: v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: taskName, + TaskRef: &v1.TaskRef{ + Name: taskName, + }, + }, + }, + }, + }, + } + + p := []*corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: taskPodName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/task": pipelineName}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: tr1Step1Name, + Image: tr1Step1Name + ":latest", + }, + }, + }, + }, + } + + trs := []*v1.TaskRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: trName, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{ + Name: taskName, + }, + }, + Status: v1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Type: apis.ConditionSucceeded, + }, + }, + }, + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: tr1StartTime}, + PodName: taskPodName, + Steps: []v1.StepState{ + { + Name: tr1Step1Name, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + }, + }, + }, + }, + } + + cs, _ := test.SeedTestData(t, test.Data{PipelineRuns: prs, Pipelines: ps, Pods: p, Namespaces: nsList}) + cs.Pipeline.Resources = cb.APIResourceList(version, []string{"task", "taskrun", "pipelinerun"}) + tdc := testDynamic.Options{} + dc, err := tdc.Client( + cb.UnstructuredP(ps[0], version), + cb.UnstructuredPR(prs[0], version), + cb.UnstructuredTR(trs[0], version), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + + fakeLogStream := fake.Logs( + fake.Task(taskPodName, + fake.Step(tr1Step1Name, "task-1 completed\n"), + ), + ) + prlo := logOpts(prName, ns, cs, dc, fake.Streamer(fakeLogStream), false, false, false, taskName) + + output, err := fetchLogs(prlo) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + test.AssertOutput(t, "task-1 completed\n\n", output) +} + +func TestLogs_Hub_Resolver(t *testing.T) { + var ( + pipelineName = "pipeline" + prName = "pipeline-run" + ns = "namespace" + taskName = "task" + trName = "taskrun" + taskPodName = "taskPod" + tr1StartTime = test.FakeClock().Now().Add(20 * time.Second) + tr1Step1Name = "step-1" + ) + + nsList := []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: ns, + }, + }, + } + + prs := []*v1.PipelineRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: prName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/pipeline": prName}, + }, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{ + ResolverRef: v1.ResolverRef{ + Resolver: "hub", + Params: v1.Params{ + { + Name: "kind", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: "pipeline"}, + }, + { + Name: "name", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: pipelineName}, + }, + { + Name: "namespace", + Value: v1.ParamValue{Type: v1.ParamTypeString, StringVal: ns}, + }, + }, + }, + }, + }, + Status: v1.PipelineRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Message: "Success", + }, + }, + }, + PipelineRunStatusFields: v1.PipelineRunStatusFields{ + ChildReferences: []v1.ChildStatusReference{ + { + Name: trName, + PipelineTaskName: taskName, + TypeMeta: runtime.TypeMeta{ + APIVersion: "tekton.dev/v1beta1", + Kind: "TaskRun", + }, + }, + }, + PipelineSpec: &v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: taskName, + TaskRef: &v1.TaskRef{ + Kind: "task", + Name: taskName, + }, + }, + }, + }, + }, + }, + }, + } + + ps := []*v1.Pipeline{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: pipelineName, + Namespace: ns, + }, + Spec: v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: taskName, + TaskRef: &v1.TaskRef{ + Name: taskName, + }, + }, + }, + }, + }, + } + + p := []*corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: taskPodName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/task": pipelineName}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: tr1Step1Name, + Image: tr1Step1Name + ":latest", + }, + }, + }, + }, + } + + trs := []*v1.TaskRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: trName, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{ + Name: taskName, + }, + }, + Status: v1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Type: apis.ConditionSucceeded, + }, + }, + }, + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: tr1StartTime}, + PodName: taskPodName, + Steps: []v1.StepState{ + { + Name: tr1Step1Name, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + }, + }, + }, + }, + } + + cs, _ := test.SeedTestData(t, test.Data{PipelineRuns: prs, Pipelines: ps, Pods: p, Namespaces: nsList}) + cs.Pipeline.Resources = cb.APIResourceList(version, []string{"task", "taskrun", "pipelinerun"}) + tdc := testDynamic.Options{} + dc, err := tdc.Client( + cb.UnstructuredP(ps[0], version), + cb.UnstructuredPR(prs[0], version), + cb.UnstructuredTR(trs[0], version), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + + fakeLogStream := fake.Logs( + fake.Task(taskPodName, + fake.Step(tr1Step1Name, "task-1 completed\n"), + ), + ) + prlo := logOpts(prName, ns, cs, dc, fake.Streamer(fakeLogStream), false, false, false, taskName) + + output, err := fetchLogs(prlo) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + test.AssertOutput(t, "task-1 completed\n\n", output) +} + func logOptsv1beta1(name string, ns string, cs pipelinev1beta1test.Clients, dc dynamic.Interface, streamer stream.NewStreamerFunc, allSteps bool, follow bool, prefixing bool, tasks ...string) *options.LogOptions { p := test.Params{ Kube: cs.Kube, diff --git a/pkg/log/pipeline_reader.go b/pkg/log/pipeline_reader.go index a3b91070e3..a9af78dd1a 100644 --- a/pkg/log/pipeline_reader.go +++ b/pkg/log/pipeline_reader.go @@ -208,15 +208,22 @@ func (r *Reader) setUpTask(taskNumber int, tr taskrunpkg.Run) { // and return trh.Run after converted taskruns into trh.Run. func (r *Reader) getOrderedTasks(pr *v1.PipelineRun) ([]taskrunpkg.Run, error) { var tasks []v1.PipelineTask - switch { case pr.Spec.PipelineRef != nil: - pl, err := pipelinepkg.GetPipeline(pipelineGroupResource, r.clients, pr.Spec.PipelineRef.Name, r.ns) - if err != nil { - return nil, err + if pr.Spec.PipelineRef.Resolver != "" { + if pr.Status.PipelineSpec != nil { + tasks = append(tasks, pr.Status.PipelineSpec.Tasks...) + } else { + return nil, fmt.Errorf("pipelinerun %s does not have the PipelineRunSpec", pr.Name) + } + } else { + pl, err := pipelinepkg.GetPipeline(pipelineGroupResource, r.clients, pr.Spec.PipelineRef.Name, r.ns) + if err != nil { + return nil, err + } + tasks = pl.Spec.Tasks + tasks = append(tasks, pl.Spec.Finally...) } - tasks = pl.Spec.Tasks - tasks = append(tasks, pl.Spec.Finally...) case pr.Spec.PipelineSpec != nil: tasks = pr.Spec.PipelineSpec.Tasks tasks = append(tasks, pr.Spec.PipelineSpec.Finally...)