diff --git a/pkg/cmd/pipelinerun/logs_test.go b/pkg/cmd/pipelinerun/logs_test.go index f769e11fa..8bb0d5017 100644 --- a/pkg/cmd/pipelinerun/logs_test.go +++ b/pkg/cmd/pipelinerun/logs_test.go @@ -1308,7 +1308,7 @@ func updatePRv1beta1(finalRuns []*v1beta1.PipelineRun, watcher *watch.FakeWatche }() } -func TestPipelinerunLog_completed_taskrun_only_v1bea1(t *testing.T) { +func TestPipelinerunLog_completed_taskrun_only_v1beta1(t *testing.T) { var ( pipelineName = "output-pipeline" prName = "output-pipeline-1" @@ -1679,59 +1679,103 @@ func TestPipelinerunLog_follow_mode_v1beta1(t *testing.T) { }, } - trs := []*v1alpha1.TaskRun{ - tb.TaskRun(tr1Name, - tb.TaskRunNamespace(ns), - tb.TaskRunSpec( - tb.TaskRunTaskRef(task1Name), - ), - tb.TaskRunStatus( - tb.PodName(tr1Pod), - tb.TaskRunStartTime(tr1StartTime), - tb.StatusCondition(apis.Condition{ - Type: apis.ConditionSucceeded, - Status: corev1.ConditionTrue, - }), - tb.StepState( - cb.StepName(tr1Step1Name), - tb.StateTerminated(0), - ), - tb.StepState( - cb.StepName("nop"), - tb.StateTerminated(0), - ), - ), - tb.TaskRunSpec( - tb.TaskRunTaskRef(task1Name), - ), - ), + trs := []*v1beta1.TaskRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: tr1Name, + }, + Spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: task1Name, + }, + }, + Status: v1beta1.TaskRunStatus{ + Status: duckv1beta1.Status{ + Conditions: duckv1beta1.Conditions{ + { + Status: corev1.ConditionTrue, + Type: apis.ConditionSucceeded, + }, + }, + }, + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + PodName: tr1Pod, + StartTime: &metav1.Time{Time: tr1StartTime}, + CompletionTime: nil, + Steps: []v1beta1.StepState{ + { + Name: tr1Step1Name, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + Reason: "Completed", + }, + }, + }, + { + Name: "nop", + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + Reason: "Completed", + }, + }, + }, + }, + }, + }, + }, } - prs := []*v1alpha1.PipelineRun{ - tb.PipelineRun(prName, - tb.PipelineRunNamespace(ns), - tb.PipelineRunLabel("tekton.dev/pipeline", prName), - tb.PipelineRunSpec(pipelineName), - tb.PipelineRunStatus( - tb.PipelineRunStatusCondition(apis.Condition{ - Status: corev1.ConditionTrue, - Reason: v1beta1.PipelineRunReasonRunning.String(), - }), - tb.PipelineRunTaskRunsStatus(tr1Name, &v1alpha1.PipelineRunTaskRunStatus{ - PipelineTaskName: task1Name, - Status: &trs[0].Status, - }), - ), - ), + prs := []*v1beta1.PipelineRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: prName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/pipeline": prName}, + }, + Spec: v1beta1.PipelineRunSpec{ + PipelineRef: &v1beta1.PipelineRef{ + Name: pipelineName, + }, + }, + Status: v1beta1.PipelineRunStatus{ + Status: duckv1beta1.Status{ + Conditions: duckv1beta1.Conditions{ + { + Status: corev1.ConditionTrue, + Reason: v1beta1.PipelineRunReasonRunning.String(), + }, + }, + }, + PipelineRunStatusFields: v1beta1.PipelineRunStatusFields{ + TaskRuns: map[string]*v1beta1.PipelineRunTaskRunStatus{ + tr1Name: { + PipelineTaskName: task1Name, + Status: &trs[0].Status, + }, + }, + }, + }, + }, } - pps := []*v1alpha1.Pipeline{ - tb.Pipeline(pipelineName, - tb.PipelineNamespace(ns), - tb.PipelineSpec( - tb.PipelineTask(task1Name, task1Name), - ), - ), + pps := []*v1beta1.Pipeline{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: pipelineName, + Namespace: ns, + }, + Spec: v1beta1.PipelineSpec{ + Tasks: []v1beta1.PipelineTask{ + { + Name: task1Name, + TaskRef: &v1beta1.TaskRef{ + Name: task1Name, + }, + }, + }, + }, + }, } p := []*corev1.Pod{ @@ -1760,20 +1804,20 @@ func TestPipelinerunLog_follow_mode_v1beta1(t *testing.T) { ), ) - cs, _ := test.SeedTestData(t, pipelinetest.Data{PipelineRuns: prs, Pipelines: pps, TaskRuns: trs, Pods: p, Namespaces: nsList}) + cs, _ := test.SeedV1beta1TestData(t, pipelinev1beta1test.Data{PipelineRuns: prs, Pipelines: pps, TaskRuns: trs, Pods: p, Namespaces: nsList}) cs.Pipeline.Resources = cb.APIResourceList(versionB1, []string{"task", "taskrun", "pipeline", "pipelinerun"}) tdc := testDynamic.Options{} dc, err := tdc.Client( - cb.UnstructuredTR(trs[0], versionB1), - cb.UnstructuredPR(prs[0], versionB1), - cb.UnstructuredP(pps[0], versionB1), + cb.UnstructuredV1beta1TR(trs[0], versionB1), + cb.UnstructuredV1beta1PR(prs[0], versionB1), + cb.UnstructuredV1beta1P(pps[0], versionB1), ) if err != nil { t.Errorf("unable to create dynamic client: %v", err) } - prlo := logOptsv1aplha1(prName, ns, cs, dc, fake.Streamer(fakeLogStream), false, true) + prlo := logOptsv1beta1(prName, ns, cs, dc, fake.Streamer(fakeLogStream), false, true) output, _ := fetchLogs(prlo) expectedLogs := []string{ @@ -1971,41 +2015,63 @@ func TestLog_run_failed_with_and_without_follow_v1beta1(t *testing.T) { }, } - prs := []*v1alpha1.PipelineRun{ - tb.PipelineRun(prName, - tb.PipelineRunNamespace(ns), - tb.PipelineRunLabel("tekton.dev/pipeline", prName), - tb.PipelineRunSpec(pipelineName), - tb.PipelineRunStatus( - tb.PipelineRunStatusCondition(apis.Condition{ - Type: apis.ConditionSucceeded, - Status: corev1.ConditionFalse, - Message: failMessage, - }), - ), - ), + prs := []*v1beta1.PipelineRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: prName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/pipeline": prName}, + }, + Spec: v1beta1.PipelineRunSpec{ + PipelineRef: &v1beta1.PipelineRef{ + Name: pipelineName, + }, + }, + Status: v1beta1.PipelineRunStatus{ + Status: duckv1beta1.Status{ + Conditions: duckv1beta1.Conditions{ + { + Type: apis.ConditionSucceeded, + Status: corev1.ConditionFalse, + Message: failMessage, + }, + }, + }, + }, + }, } - ps := []*v1alpha1.Pipeline{ - tb.Pipeline(pipelineName, - tb.PipelineNamespace(ns), - tb.PipelineSpec( - tb.PipelineTask(taskName, taskName), - ), - ), + ps := []*v1beta1.Pipeline{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: pipelineName, + Namespace: ns, + }, + Spec: v1beta1.PipelineSpec{ + Tasks: []v1beta1.PipelineTask{ + { + Name: taskName, + TaskRef: &v1beta1.TaskRef{ + Name: taskName, + }, + }, + }, + }, + }, } - cs, _ := test.SeedTestData(t, pipelinetest.Data{PipelineRuns: prs, Pipelines: ps, Namespaces: nsList}) + + cs, _ := test.SeedV1beta1TestData(t, pipelinev1beta1test.Data{PipelineRuns: prs, Pipelines: ps, Namespaces: nsList}) cs.Pipeline.Resources = cb.APIResourceList(versionB1, []string{"pipeline", "pipelinerun"}) tdc := testDynamic.Options{} dc, err := tdc.Client( - cb.UnstructuredP(ps[0], versionB1), - cb.UnstructuredPR(prs[0], versionB1), + cb.UnstructuredV1beta1P(ps[0], versionB1), + cb.UnstructuredV1beta1PR(prs[0], versionB1), ) if err != nil { t.Errorf("unable to create dynamic client: %v", err) } // follow mode disabled - prlo := logOptsv1aplha1(prName, ns, cs, dc, fake.Streamer([]fake.Log{}), false, false) + prlo := logOptsv1beta1(prName, ns, cs, dc, fake.Streamer([]fake.Log{}), false, false) output, err := fetchLogs(prlo) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -2013,7 +2079,7 @@ func TestLog_run_failed_with_and_without_follow_v1beta1(t *testing.T) { test.AssertOutput(t, failMessage+"\n", output) // follow mode enabled - prlo = logOptsv1aplha1(prName, ns, cs, dc, fake.Streamer([]fake.Log{}), false, true) + prlo = logOptsv1beta1(prName, ns, cs, dc, fake.Streamer([]fake.Log{}), false, true) output, err = fetchLogs(prlo) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -2450,6 +2516,231 @@ func TestLog_pipelinerun_only_one_v1beta1(t *testing.T) { test.AssertOutput(t, prName, lopt.PipelineRunName) } +func TestPipelinerunLog_finally_v1beta1(t *testing.T) { + var ( + pipelineName = "output-pipeline" + prName = "output-pipeline-1" + prstart = clockwork.NewFakeClock() + ns = "namespace" + + task1Name = "output-task" + tr1Name = "output-task-1" + tr1StartTime = prstart.Now().Add(20 * time.Second) + tr1Pod = "output-task-pod-123456" + tr1Step1Name = "writefile-step" + + finallyName = "finally-task" + finallyTrName = "finally-task-1" + finallyStartTime = prstart.Now().Add(30 * time.Second) + finallyTrPod = "finally-task-pod-123456" + finallyTrStep1Name = "finally-step" + ) + + nsList := []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: ns, + }, + }, + } + + trs := []*v1beta1.TaskRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: tr1Name, + }, + Spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: task1Name, + }, + }, + Status: v1beta1.TaskRunStatus{ + Status: duckv1beta1.Status{ + Conditions: duckv1beta1.Conditions{ + { + Status: corev1.ConditionTrue, + Type: apis.ConditionSucceeded, + }, + }, + }, + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + PodName: tr1Pod, + StartTime: &metav1.Time{Time: tr1StartTime}, + CompletionTime: nil, + Steps: []v1beta1.StepState{ + { + Name: tr1Step1Name, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + Reason: "Completed", + }, + }, + }, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: finallyTrName, + }, + Spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: finallyName, + }, + }, + Status: v1beta1.TaskRunStatus{ + Status: duckv1beta1.Status{ + Conditions: duckv1beta1.Conditions{ + { + Status: corev1.ConditionTrue, + Type: apis.ConditionSucceeded, + }, + }, + }, + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + PodName: finallyTrPod, + StartTime: &metav1.Time{Time: finallyStartTime}, + CompletionTime: nil, + Steps: []v1beta1.StepState{ + { + Name: finallyTrStep1Name, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + Reason: "Completed", + }, + }, + }, + }, + }, + }, + }, + } + + prs := []*v1beta1.PipelineRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: prName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/pipeline": prName}, + }, + Spec: v1beta1.PipelineRunSpec{ + PipelineRef: &v1beta1.PipelineRef{ + Name: pipelineName, + }, + }, + Status: v1beta1.PipelineRunStatus{ + Status: duckv1beta1.Status{ + Conditions: duckv1beta1.Conditions{ + { + Status: corev1.ConditionTrue, + Reason: v1beta1.PipelineRunReasonRunning.String(), + }, + }, + }, + PipelineRunStatusFields: v1beta1.PipelineRunStatusFields{ + TaskRuns: map[string]*v1beta1.PipelineRunTaskRunStatus{ + tr1Name: { + PipelineTaskName: task1Name, + Status: &trs[0].Status, + }, + finallyTrName: { + PipelineTaskName: finallyName, + Status: &trs[1].Status, + }, + }, + }, + }, + }, + } + + pps := []*v1beta1.Pipeline{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: pipelineName, + Namespace: ns, + }, + Spec: v1beta1.PipelineSpec{ + Tasks: []v1beta1.PipelineTask{ + { + Name: task1Name, + TaskRef: &v1beta1.TaskRef{ + Name: task1Name, + }, + }, + }, + Finally: []v1beta1.PipelineTask{ + { + Name: finallyName, + TaskRef: &v1beta1.TaskRef{ + Name: finallyName, + }, + }, + }, + }, + }, + } + + p := []*corev1.Pod{ + tb.Pod(tr1Pod, + tb.PodNamespace(ns), + tb.PodLabel("tekton.dev/task", pipelineName), + tb.PodSpec( + tb.PodContainer(tr1Step1Name, tr1Step1Name+":latest"), + ), + cb.PodStatus( + cb.PodPhase(corev1.PodSucceeded), + ), + ), + tb.Pod(finallyTrPod, + tb.PodNamespace(ns), + tb.PodLabel("tekton.dev/task", pipelineName), + tb.PodSpec( + tb.PodContainer(finallyTrStep1Name, finallyTrStep1Name+":latest"), + ), + cb.PodStatus( + cb.PodPhase(corev1.PodSucceeded), + ), + ), + } + + fakeLogStream := fake.Logs( + fake.Task(tr1Pod, + fake.Step(tr1Step1Name, "wrote a file1"), + ), + fake.Task(finallyTrPod, + fake.Step(finallyTrStep1Name, "Finally"), + ), + ) + + cs, _ := test.SeedV1beta1TestData(t, pipelinev1beta1test.Data{PipelineRuns: prs, Pipelines: pps, TaskRuns: trs, Pods: p, Namespaces: nsList}) + cs.Pipeline.Resources = cb.APIResourceList(versionB1, []string{"task", "taskrun", "pipeline", "pipelinerun"}) + + tdc := testDynamic.Options{} + dc, err := tdc.Client( + cb.UnstructuredV1beta1TR(trs[0], versionB1), + cb.UnstructuredV1beta1TR(trs[1], versionB1), + cb.UnstructuredV1beta1PR(prs[0], versionB1), + cb.UnstructuredV1beta1P(pps[0], versionB1), + ) + + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + + prlo := logOptsv1beta1(prName, ns, cs, dc, fake.Streamer(fakeLogStream), false, false) + output, _ := fetchLogs(prlo) + + expectedLogs := []string{ + "[output-task : writefile-step] wrote a file1\n", + "[finally-task : finally-step] Finally\n", + } + expected := strings.Join(expectedLogs, "\n") + "\n" + test.AssertOutput(t, expected, output) +} + func logOptsv1aplha1(name string, ns string, cs pipelinetest.Clients, dc dynamic.Interface, streamer stream.NewStreamerFunc, allSteps bool, follow 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 039affe5a..b81d2cfbb 100644 --- a/pkg/log/pipeline_reader.go +++ b/pkg/log/pipeline_reader.go @@ -214,8 +214,10 @@ func (r *Reader) getOrderedTasks(pr *v1beta1.PipelineRun) ([]trh.Run, error) { return nil, err } 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...) default: return nil, fmt.Errorf("pipelinerun %s did not provide PipelineRef or PipelineSpec", pr.Name) } diff --git a/pkg/pipelinerun/tracker.go b/pkg/pipelinerun/tracker.go index 497c3230e..e0ca53559 100644 --- a/pkg/pipelinerun/tracker.go +++ b/pkg/pipelinerun/tracker.go @@ -15,15 +15,19 @@ package pipelinerun import ( + "context" "time" + "github.com/tektoncd/cli/pkg/actions" trh "github.com/tektoncd/cli/pkg/taskrun" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" informers "github.com/tektoncd/pipeline/pkg/client/informers/externalversions" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/tools/cache" ) @@ -57,7 +61,13 @@ func (t *Tracker) Monitor(allowed []string) <-chan []trh.Run { informers.WithNamespace(t.Ns), informers.WithTweakListOptions(pipelinerunOpts(t.Name))) - informer := factory.Tekton().V1alpha1().PipelineRuns().Informer() + gvr, _ := actions.GetGroupVersionResource( + schema.GroupVersionResource{Group: "tekton.dev", Resource: "pipelineruns"}, + t.Tekton.Discovery(), + ) + + genericInformer, _ := factory.ForResource(*gvr) + informer := genericInformer.Informer() stopC := make(chan struct{}) trC := make(chan []trh.Run) @@ -68,14 +78,27 @@ func (t *Tracker) Monitor(allowed []string) <-chan []trh.Run { }() eventHandler := func(obj interface{}) { - pr, ok := obj.(*v1alpha1.PipelineRun) - if !ok || pr == nil { - return + var pipelinerunConverted v1beta1.PipelineRun + switch gvr.Version { + case "v1alpha1": + pr, ok := obj.(*v1alpha1.PipelineRun) + if !ok || pr == nil { + return + } + if err := pr.ConvertTo(context.Background(), &pipelinerunConverted); err != nil { + return + } + case "v1beta1": + pr, ok := obj.(*v1beta1.PipelineRun) + if !ok || pr == nil { + return + } + pr.DeepCopyInto(&pipelinerunConverted) } - trC <- t.findNewTaskruns(pr, allowed) + trC <- t.findNewTaskruns(&pipelinerunConverted, allowed) - if hasCompleted(pr) { + if hasCompleted(&pipelinerunConverted) { close(stopC) // should close trC } } @@ -103,7 +126,7 @@ func pipelinerunOpts(name string) func(opts *metav1.ListOptions) { // handles changes to pipelinerun and pushes the Run information to the // channel if the task is new and is in the allowed list of tasks // returns true if the pipelinerun has finished -func (t *Tracker) findNewTaskruns(pr *v1alpha1.PipelineRun, allowed []string) []trh.Run { +func (t *Tracker) findNewTaskruns(pr *v1beta1.PipelineRun, allowed []string) []trh.Run { ret := []trh.Run{} for tr, trs := range pr.Status.TaskRuns { run := trh.Run{Name: tr, Task: trs.PipelineTaskName} @@ -121,7 +144,7 @@ func (t *Tracker) findNewTaskruns(pr *v1alpha1.PipelineRun, allowed []string) [] return ret } -func hasCompleted(pr *v1alpha1.PipelineRun) bool { +func hasCompleted(pr *v1beta1.PipelineRun) bool { if len(pr.Status.Conditions) == 0 { return false } diff --git a/pkg/pipelinerun/tracker_test.go b/pkg/pipelinerun/tracker_test.go index 9672da0cf..844dabb13 100644 --- a/pkg/pipelinerun/tracker_test.go +++ b/pkg/pipelinerun/tracker_test.go @@ -21,6 +21,7 @@ import ( trh "github.com/tektoncd/cli/pkg/taskrun" "github.com/tektoncd/cli/pkg/test" clitest "github.com/tektoncd/cli/pkg/test" + cb "github.com/tektoncd/cli/pkg/test/builder" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" @@ -155,7 +156,7 @@ func startPipelineRun(t *testing.T, data pipelinetest.Data, prStatus ...v1alpha1 // to keep pushing the taskrun over the period(simulate watch) watcher := watch.NewFake() cs.Pipeline.PrependWatchReactor("pipelineruns", k8stest.DefaultWatchReactor(watcher, nil)) - + cs.Pipeline.Resources = cb.APIResourceList("v1alpha1", []string{"task", "taskrun", "pipeline", "pipelinerun"}) go func() { for _, status := range prStatus { time.Sleep(time.Second * 2) diff --git a/test/e2e/helper.go b/test/e2e/helper.go index e649751a5..4612f3dee 100644 --- a/test/e2e/helper.go +++ b/test/e2e/helper.go @@ -19,7 +19,10 @@ import ( "path" "path/filepath" "runtime" + "testing" "text/template" + + "github.com/google/go-cmp/cmp" ) func Process(t *template.Template, vars interface{}) string { @@ -49,3 +52,22 @@ func ResourcePath(elem ...string) string { path := append([]string{tmp}, elem...) return filepath.Join(path...) } + +func AssertOutput(t *testing.T, expected, actual interface{}) { + t.Helper() + diff := cmp.Diff(actual, expected) + if diff == "" { + return + } + + t.Errorf(` +Unexpected output: +%s + +Expected +%s + +Actual +%s +`, diff, expected, actual) +} diff --git a/test/e2e/pipelinerun/pipelinerun_test.go b/test/e2e/pipelinerun/pipelinerun_test.go new file mode 100644 index 000000000..1ead5e231 --- /dev/null +++ b/test/e2e/pipelinerun/pipelinerun_test.go @@ -0,0 +1,55 @@ +// +build e2e +// Copyright © 2020 The Tekton Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pipelinerun + +import ( + "strings" + "testing" + + "github.com/tektoncd/cli/test/e2e" + "gotest.tools/v3/icmd" + knativetest "knative.dev/pkg/test" +) + +func TestPipelineRunLogE2E(t *testing.T) { + t.Parallel() + c, namespace := e2e.Setup(t) + knativetest.CleanupOnInterrupt(func() { e2e.TearDown(t, c, namespace) }, t.Logf) + defer e2e.TearDown(t, c, namespace) + + kubectl := e2e.NewKubectl(namespace) + tkn, err := e2e.NewTknRunner(namespace) + if err != nil { + t.Fatalf("Error creating tknRunner %+v", err) + } + + if tkn.CheckVersion("Pipeline", "v0.10.2") { + t.Skip("Skip test as pipeline v0.10.2 doesn't support finally") + } + + t.Logf("Creating pipelinerun in namespace: %s", namespace) + e2e.Assert(t, kubectl.Create(e2e.ResourcePath("pipelinerun-with-finally.yaml")), icmd.Success) + + t.Run("Pipelinerun logs with finally "+namespace, func(t *testing.T) { + res := tkn.Run("pipelinerun", "logs", "exit-handler", "-f") + s := []string{ + "[print-msg : main] printing a message\n", + "[echo-on-exit : main] finally\n", + } + expected := strings.Join(s, "\n") + "\n" + e2e.AssertOutput(t, expected, res.Stdout()) + }) +} diff --git a/test/resources/pipelinerun-with-finally.yaml b/test/resources/pipelinerun-with-finally.yaml new file mode 100644 index 000000000..abd78f149 --- /dev/null +++ b/test/resources/pipelinerun-with-finally.yaml @@ -0,0 +1,42 @@ +# Copyright 2020 The Tekton Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + name: exit-handler +spec: + pipelineSpec: + finally: + - name: echo-on-exit + taskSpec: + steps: + - args: + - echo "finally" + command: + - sh + - -c + image: library/bash:4.4.23 + name: main + tasks: + - name: print-msg + taskSpec: + steps: + - args: + - echo "printing a message" + command: + - sh + - -c + image: library/bash:4.4.23 + name: main