diff --git a/docs/cmd/tkn_pipeline_logs.md b/docs/cmd/tkn_pipeline_logs.md index 6df6e856e1..7be16f2a5c 100644 --- a/docs/cmd/tkn_pipeline_logs.md +++ b/docs/cmd/tkn_pipeline_logs.md @@ -40,6 +40,7 @@ Show logs for given Pipeline and PipelineRun: -h, --help help for logs -L, --last show logs for last PipelineRun --limit int lists number of PipelineRuns (default 5) + --prefix prefix each log line with the log source (task name and step name) (default true) -t, --timestamps show logs with timestamp ``` diff --git a/docs/man/man1/tkn-pipeline-logs.1 b/docs/man/man1/tkn-pipeline-logs.1 index e8676ff797..df89e77ddd 100644 --- a/docs/man/man1/tkn-pipeline-logs.1 +++ b/docs/man/man1/tkn-pipeline-logs.1 @@ -39,6 +39,10 @@ Show Pipeline logs \fB\-\-limit\fP=5 lists number of PipelineRuns +.PP +\fB\-\-prefix\fP[=true] + prefix each log line with the log source (task name and step name) + .PP \fB\-t\fP, \fB\-\-timestamps\fP[=false] show logs with timestamp diff --git a/pipeline b/pipeline new file mode 100644 index 0000000000..b04ddf6556 Binary files /dev/null and b/pipeline differ diff --git a/pkg/cmd/pipeline/logs.go b/pkg/cmd/pipeline/logs.go index d7b6cb26d5..8aa733627e 100644 --- a/pkg/cmd/pipeline/logs.go +++ b/pkg/cmd/pipeline/logs.go @@ -97,7 +97,7 @@ Show logs for given Pipeline and PipelineRun: c.Flags().BoolVarP(&opts.Follow, "follow", "f", false, "stream live logs") c.Flags().BoolVarP(&opts.Timestamps, "timestamps", "t", false, "show logs with timestamp") c.Flags().IntVarP(&opts.Limit, "limit", "", 5, "lists number of PipelineRuns") - + c.Flags().BoolVarP(&opts.Prefixing, "prefix", "", true, "prefix each log line with the log source (task name and step name)") return c } @@ -206,3 +206,7 @@ func askRunName(opts *options.LogOptions) error { return opts.Ask(options.ResourceNamePipelineRun, prs) } + + + + diff --git a/pkg/cmd/pipeline/logs_test.go b/pkg/cmd/pipeline/logs_test.go index c5b64df510..756fe938cf 100644 --- a/pkg/cmd/pipeline/logs_test.go +++ b/pkg/cmd/pipeline/logs_test.go @@ -15,7 +15,10 @@ package pipeline import ( + //"bytes" + "bytes" "errors" + "testing" "time" @@ -23,7 +26,10 @@ import ( "github.com/AlecAivazis/survey/v2/terminal" goexpect "github.com/Netflix/go-expect" "github.com/tektoncd/cli/pkg/cli" + "github.com/tektoncd/cli/pkg/cmd/pipelinerun" "github.com/tektoncd/cli/pkg/options" + //"github.com/tektoncd/cli/pkg/pods/fake" + "github.com/tektoncd/cli/pkg/pods/stream" "github.com/tektoncd/cli/pkg/test" cb "github.com/tektoncd/cli/pkg/test/builder" testDynamic "github.com/tektoncd/cli/pkg/test/dynamic" @@ -33,7 +39,9 @@ import ( pipelinetest "github.com/tektoncd/pipeline/test" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/dynamic" + "knative.dev/pkg/apis" duckv1 "knative.dev/pkg/apis/duck/v1" ) @@ -55,7 +63,275 @@ const ( ) func TestPipelineLog_v1beta1(t *testing.T) { + clock := test.FakeClock() + var ( + //pipelineName = "output-pipeline" + //prName = "output-pipeline-1" + prstart = test.FakeClock() + //ns = "namespace" + + task1Name = "output-task" + tr1Name = "output-task-1" + tr1StartTime = prstart.Now().Add(20 * time.Second) + tr1Pod = "output-task-pod-123456" + tr1Step1Name = "writefile-step" + tr1InitStep1 = "credential-initializer-mdzbr" + tr1InitStep2 = "place-tools" + + task2Name = "read-task" + tr2Name = "read-task-1" + tr2StartTime = prstart.Now().Add(2 * time.Minute) + tr2Pod = "read-task-pod-123456" + tr2Step1Name = "readfile-step" + + nopStep = "nop" + ) + + /*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: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Type: apis.ConditionSucceeded, + }, + }, + }, + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: tr1StartTime}, + PodName: tr1Pod, + Steps: []v1beta1.StepState{ + { + Name: tr1Step1Name, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + { + Name: nopStep, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: tr2Name, + }, + Spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: task2Name, + }, + }, + Status: v1beta1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Type: apis.ConditionSucceeded, + }, + }, + }, + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: tr2StartTime}, + PodName: tr2Pod, + Steps: []v1beta1.StepState{ + { + Name: tr2Step1Name, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + { + Name: nopStep, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + }, + }, + }, + }, + } + + prs := []*v1beta1.PipelineRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: prName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/pipeline": pipelineName}, + }, + Spec: v1beta1.PipelineRunSpec{ + PipelineRef: &v1beta1.PipelineRef{ + Name: pipelineName, + }, + }, + Status: v1beta1.PipelineRunStatus{ + PipelineRunStatusFields: v1beta1.PipelineRunStatusFields{ + ChildReferences: []v1beta1.ChildStatusReference{ + { + Name: tr1Name, + PipelineTaskName: task1Name, + TypeMeta: runtime.TypeMeta{ + APIVersion: "tekton.dev/v1beta1", + Kind: "TaskRun", + }, + }, { + Name: tr2Name, + PipelineTaskName: task2Name, + TypeMeta: runtime.TypeMeta{ + APIVersion: "tekton.dev/v1beta1", + Kind: "TaskRun", + }, + }, + }, + }, + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Reason: v1beta1.PipelineRunReasonSuccessful.String(), + }, + }, + }, + }, + }, + } + pps := []*v1beta1.Pipeline{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: pipelineName, + Namespace: ns, + }, + Spec: v1beta1.PipelineSpec{ + Tasks: []v1beta1.PipelineTask{ + { + Name: task1Name, + TaskRef: &v1beta1.TaskRef{ + Name: task1Name, + }, + }, + { + Name: task2Name, + TaskRef: &v1beta1.TaskRef{ + Name: task2Name, + }, + }, + }, + }, + }, + } + + p := []*corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: tr1Pod, + Namespace: ns, + Labels: map[string]string{"tekton.dev/task": task1Name}, + }, + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: tr1InitStep1, + Image: "override-with-creds:latest", + }, + { + Name: tr1InitStep2, + Image: "override-with-tools:latest", + }, + }, + Containers: []corev1.Container{ + { + Name: tr1Step1Name, + Image: tr1Step1Name + ":latest", + }, + { + Name: nopStep, + Image: "override-with-nop:latest", + }, + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodPhase(corev1.PodSucceeded), + InitContainerStatuses: []corev1.ContainerStatus{ + { + Name: tr1InitStep1, + Image: "override-with-creds:latest", + }, + { + Name: tr1InitStep2, + Image: "override-with-tools:latest", + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: tr2Pod, + Namespace: ns, + Labels: map[string]string{"tekton.dev/task": task2Name}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: tr2Step1Name, + Image: tr1Step1Name + ":latest", + }, + { + Name: nopStep, + Image: "override-with-nop:latest", + }, + }, + }, + }, + } + + /*fakeLogs := fake.Logs( + fake.Task(tr1Pod, + fake.Step(tr1InitStep1, "initialized the credentials"), + fake.Step(tr1InitStep2, "place tools log"), + fake.Step(tr1Step1Name, "written a file"), + fake.Step(nopStep, "Build successful"), + ), + fake.Task(tr2Pod, + fake.Step(tr2Step1Name, "able to read a file"), + fake.Step(nopStep, "Build successful"), + ), + )*/ + + + //clock := test.FakeClock() pdata := []*v1beta1.Pipeline{ { ObjectMeta: metav1.ObjectMeta{ @@ -69,11 +345,12 @@ func TestPipelineLog_v1beta1(t *testing.T) { Namespaces: []*corev1.Namespace{ { ObjectMeta: metav1.ObjectMeta{ - Name: "ns", + Name: "ns", ////"ns" }, }, }, }) + cs.Pipeline.Resources = cb.APIResourceList(versionv1beta1, []string{"pipeline", "pipelinerun"}) tdc := testDynamic.Options{} dc, err := tdc.Client( @@ -95,7 +372,7 @@ func TestPipelineLog_v1beta1(t *testing.T) { cs2.Pipeline.Resources = cb.APIResourceList(versionv1beta1, []string{"pipeline", "pipelinerun"}) tdc2 := testDynamic.Options{} dc2, err := tdc2.Client( - cb.UnstructuredV1beta1P(pdata[0], versionv1beta1), + cb.UnstructuredV1beta1P(pps[0], versionv1beta1), ) if err != nil { t.Errorf("unable to create dynamic client: %v", err) @@ -193,6 +470,36 @@ func TestPipelineLog_v1beta1(t *testing.T) { t.Errorf("unable to create dynamic client: %v", err) } + // Seed test data + cs4, _ := test.SeedV1beta1TestData(t, test.Data{ + Pipelines: pps, + PipelineRuns: prs, + TaskRuns: trs, + Pods: p, + Namespaces: []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: ns, + }, + }, + }, + }) + + // Define resources for the dynamic client + cs4.Pipeline.Resources = cb.APIResourceList(versionv1beta1, []string{"pipeline", "pipelinerun", "task", "taskrun", "pod"}) + + // Create dynamic client + tdc4 := testDynamic.Options{} + dc4, err := tdc4.Client( + cb.UnstructuredV1beta1P(pps[0], versionv1beta1), + cb.UnstructuredV1beta1PR(prs[0], versionv1beta1), + cb.UnstructuredV1beta1TR(trs[0], versionv1beta1), + cb.UnstructuredV1beta1TR(trs[1], versionv1beta1), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + testParams := []struct { name string command []string @@ -200,6 +507,7 @@ func TestPipelineLog_v1beta1(t *testing.T) { dynamic dynamic.Interface input test.Clients wantError bool + prefixing bool want string }{ { @@ -209,6 +517,7 @@ func TestPipelineLog_v1beta1(t *testing.T) { dynamic: dc, input: cs, wantError: false, + prefixing: true, want: "No Pipelines found in namespace invalid", }, { @@ -218,6 +527,7 @@ func TestPipelineLog_v1beta1(t *testing.T) { dynamic: dc, input: cs, wantError: false, + prefixing: true, want: "No Pipelines found in namespace ns", }, { @@ -227,6 +537,7 @@ func TestPipelineLog_v1beta1(t *testing.T) { dynamic: dc2, input: cs2, wantError: false, + prefixing: true, want: "No PipelineRuns found for Pipeline output-pipeline", }, { @@ -236,6 +547,7 @@ func TestPipelineLog_v1beta1(t *testing.T) { dynamic: dc2, input: cs2, wantError: true, + prefixing: true, want: "pipelines.tekton.dev \"pipeline\" not found", }, { @@ -245,6 +557,7 @@ func TestPipelineLog_v1beta1(t *testing.T) { dynamic: dc, input: cs, wantError: true, + prefixing: true, want: "pipelineruns.tekton.dev \"pipelinerun\" not found", }, { @@ -254,6 +567,7 @@ func TestPipelineLog_v1beta1(t *testing.T) { dynamic: dc2, input: cs2, wantError: true, + prefixing: true, want: "limit was -1 but must be a positive number", }, { @@ -263,19 +577,51 @@ func TestPipelineLog_v1beta1(t *testing.T) { dynamic: dc3, input: cs3, wantError: false, + prefixing: true, want: "", }, + { + name: "Prefixing enabled for Pipelines", + command: []string{"logs", pipelineName, "--prefix=true", "-n", ns}, + namespace: "", + dynamic: dc4, + input: cs4, + wantError: false, + prefixing: true, + want: "[output-task : writefile-step] written a file\n\nBuild Successful\n\n[read-task : readfile-step] able to read a file\n\nBuild Successful\n\n", + }, + { + name: "Prefixing disabled for Pipelines", + command: []string{"logs", pipelineName, "--prefix=false", "-n", ns}, + namespace: "", + dynamic: dc4, + input: cs4, + wantError: false, + prefixing: false, + want: "written a file\n\nBuild Successful\n\nable to read a file \n\nBuild Successful\n\n", + }, } for _, tp := range testParams { t.Run(tp.name, func(t *testing.T) { + p := &test.Params{Tekton: tp.input.Pipeline, Clock: clock, Kube: tp.input.Kube, Dynamic: tp.dynamic} if tp.namespace != "" { p.SetNamespace(tp.namespace) } c := Command(p) + //prlo := logOptsv1beta1(prName, tp.namespace, cs, dc, fake.Streamer(fakeLogs), false, tp.prefixing) /// ns + //output, _ := fetchLogs(prlo) + + //expected := strings.Join(tp.want, "\n") + "\n" + + //test.AssertOutput(t, tp.want, output) + + out, err := test.ExecuteCommand(c, tp.command...) + //test.AssertOutput(t, tp.want, output) + if tp.wantError { if err == nil { t.Errorf("error expected here") @@ -293,6 +639,272 @@ func TestPipelineLog_v1beta1(t *testing.T) { func TestPipelineLog(t *testing.T) { clock := test.FakeClock() + + var ( + //pipelineName = "output-pipeline" + //prrName = "output-pipeline-1" + prstart = test.FakeClock() + //ns = "namespace" + + task1Name = "output-task" + tr1Name = "output-task-1" + tr1StartTime = prstart.Now().Add(20 * time.Second) + tr1Pod = "output-task-pod-123456" + tr1Step1Name = "writefile-step" + tr1InitStep1 = "credential-initializer-mdzbr" + tr1InitStep2 = "place-tools" + + task2Name = "read-task" + tr2Name = "read-task-1" + tr2StartTime = prstart.Now().Add(2 * time.Minute) + tr2Pod = "read-task-pod-123456" + tr2Step1Name = "readfile-step" + + nopStep = "nop" + ) + + /*nsList := []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: ns, + }, + }, + }*/ + + tr := []*v1.TaskRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: tr1Name, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{ + Name: task1Name, + }, + }, + Status: v1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Type: apis.ConditionSucceeded, + }, + }, + }, + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: tr1StartTime}, + PodName: tr1Pod, + Steps: []v1.StepState{ + { + Name: tr1Step1Name, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + { + Name: nopStep, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: tr2Name, + }, + Spec: v1.TaskRunSpec{ + TaskRef: &v1.TaskRef{ + Name: task2Name, + }, + }, + Status: v1.TaskRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Type: apis.ConditionSucceeded, + }, + }, + }, + TaskRunStatusFields: v1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: tr2StartTime}, + PodName: tr2Pod, + Steps: []v1.StepState{ + { + Name: tr2Step1Name, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + { + Name: nopStep, + ContainerState: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ + ExitCode: 0, + }, + }, + }, + }, + }, + }, + }, + } + + pr := []*v1.PipelineRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: prName, + Namespace: ns, + Labels: map[string]string{"tekton.dev/pipeline": pipelineName}, + }, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{ + Name: pipelineName, + }, + }, + Status: v1.PipelineRunStatus{ + PipelineRunStatusFields: v1.PipelineRunStatusFields{ + ChildReferences: []v1.ChildStatusReference{ + { + Name: tr1Name, + PipelineTaskName: task1Name, + TypeMeta: runtime.TypeMeta{ + APIVersion: "tekton.dev/v1", + Kind: "TaskRun", + }, + }, { + Name: tr2Name, + PipelineTaskName: task2Name, + TypeMeta: runtime.TypeMeta{ + APIVersion: "tekton.dev/v1", + Kind: "TaskRun", + }, + }, + }, + }, + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + { + Status: corev1.ConditionTrue, + Reason: v1beta1.PipelineRunReasonSuccessful.String(), + }, + }, + }, + }, + }, + } + pp := []*v1.Pipeline{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: pipelineName, + Namespace: ns, + }, + Spec: v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: task1Name, + TaskRef: &v1.TaskRef{ + Name: task1Name, + }, + }, + { + Name: task2Name, + TaskRef: &v1.TaskRef{ + Name: task2Name, + }, + }, + }, + }, + }, + } + + p := []*corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: tr1Pod, + Namespace: ns, + Labels: map[string]string{"tekton.dev/task": task1Name}, + }, + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: tr1InitStep1, + Image: "override-with-creds:latest", + }, + { + Name: tr1InitStep2, + Image: "override-with-tools:latest", + }, + }, + Containers: []corev1.Container{ + { + Name: tr1Step1Name, + Image: tr1Step1Name + ":latest", + }, + { + Name: nopStep, + Image: "override-with-nop:latest", + }, + }, + }, + Status: corev1.PodStatus{ + Phase: corev1.PodPhase(corev1.PodSucceeded), + InitContainerStatuses: []corev1.ContainerStatus{ + { + Name: tr1InitStep1, + Image: "override-with-creds:latest", + }, + { + Name: tr1InitStep2, + Image: "override-with-tools:latest", + }, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: tr2Pod, + Namespace: ns, + Labels: map[string]string{"tekton.dev/task": task2Name}, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: tr2Step1Name, + Image: tr1Step1Name + ":latest", + }, + { + Name: nopStep, + Image: "override-with-nop:latest", + }, + }, + }, + }, + } + + /*fakeLogs := fake.Logs( + fake.Task(tr1Pod, + fake.Step(tr1InitStep1, "initialized the credentials"), + fake.Step(tr1InitStep2, "place tools log"), + fake.Step(tr1Step1Name, "written a file"), + fake.Step(nopStep, "Build successful"), + ), + fake.Task(tr2Pod, + fake.Step(tr2Step1Name, "able to read a file"), + fake.Step(nopStep, "Build successful"), + ), + )*/ + pdata := []*v1.Pipeline{ { ObjectMeta: metav1.ObjectMeta{ @@ -430,6 +1042,36 @@ func TestPipelineLog(t *testing.T) { t.Errorf("unable to create dynamic client: %v", err) } + // Seed test data + cs4, _ := test.SeedTestData(t, pipelinetest.Data{ + Pipelines: pp, + PipelineRuns: pr, + TaskRuns: tr, + Pods: p, + Namespaces: []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: ns, + }, + }, + }, + }) + + // Define resources for the dynamic client + cs4.Pipeline.Resources = cb.APIResourceList(version, []string{"pipeline", "pipelinerun", "task", "taskrun", "pod"}) + + // Create dynamic client + tdc4 := testDynamic.Options{} + dc4, err := tdc4.Client( + cb.UnstructuredP(pp[0], version), + cb.UnstructuredPR(pr[0], version), + cb.UnstructuredTR(tr[0], version), + cb.UnstructuredTR(tr[1], version), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + testParams := []struct { name string command []string @@ -437,6 +1079,7 @@ func TestPipelineLog(t *testing.T) { dynamic dynamic.Interface input pipelinetest.Clients wantError bool + prefixing bool want string }{ { @@ -446,6 +1089,7 @@ func TestPipelineLog(t *testing.T) { dynamic: dc, input: cs, wantError: false, + prefixing: true, want: "No Pipelines found in namespace invalid", }, { @@ -455,6 +1099,7 @@ func TestPipelineLog(t *testing.T) { dynamic: dc, input: cs, wantError: false, + prefixing: true, want: "No Pipelines found in namespace ns", }, { @@ -464,6 +1109,7 @@ func TestPipelineLog(t *testing.T) { dynamic: dc2, input: cs2, wantError: false, + prefixing: true, want: "No PipelineRuns found for Pipeline output-pipeline", }, { @@ -473,6 +1119,7 @@ func TestPipelineLog(t *testing.T) { dynamic: dc2, input: cs2, wantError: true, + prefixing: true, want: "pipelines.tekton.dev \"pipeline\" not found", }, { @@ -482,6 +1129,7 @@ func TestPipelineLog(t *testing.T) { dynamic: dc, input: cs, wantError: true, + prefixing: true, want: "pipelineruns.tekton.dev \"pipelinerun\" not found", }, { @@ -491,6 +1139,7 @@ func TestPipelineLog(t *testing.T) { dynamic: dc2, input: cs2, wantError: true, + prefixing: true, want: "limit was -1 but must be a positive number", }, { @@ -500,8 +1149,29 @@ func TestPipelineLog(t *testing.T) { dynamic: dc3, input: cs3, wantError: false, + prefixing: true, want: "", }, + { + name: "Prefixing enabled for Pipelines", + command: []string{"logs", pipelineName, "--prefix=true"}, + namespace: "", + dynamic: dc4, + input: cs4, + wantError: false, + prefixing: true, + want: "[output-task : writefile-step] written a file\n\nBuild Successful\n\n[read-task : readfile-step] able to read a file\n\nBuild Successful\n\n", + }, + { + name: "Prefixing disabled for Pipelines", + command: []string{"logs", pipelineName, "--prefix=false"}, + namespace: "", + dynamic: dc4, + input: cs4, + wantError: false, + prefixing: false, + want: "written a file\n\nBuild Successful\n\nable to read a file \n\nBuild Successful\n\n", + }, } for _, tp := range testParams { @@ -510,6 +1180,7 @@ func TestPipelineLog(t *testing.T) { if tp.namespace != "" { p.SetNamespace(tp.namespace) } + c := Command(p) out, err := test.ExecuteCommand(c, tp.command...) @@ -1410,6 +2081,15 @@ func TestLogs_Auto_Select_FirstPipeline(t *testing.T) { Limit: 5, Params: &p, } + + // Set the prefixing flag to true or false as needed + prefixing := true + + // Set the prefix flag based on the prefixing flag + if prefixing { + lopt.Prefixing = true + } + err = getAllInputs(lopt) if err != nil { t.Errorf("Unexpected error: %v", err) @@ -1419,3 +2099,47 @@ func TestLogs_Auto_Select_FirstPipeline(t *testing.T) { t.Error("No auto selection of the first pipeline when we have only one") } } + +func logOpts(name string, ns string, cs pipelinetest.Clients, dc dynamic.Interface, streamer stream.NewStreamerFunc, follow bool, prefixing bool) *options.LogOptions { + p := test.Params{ + Kube: cs.Kube, + Tekton: cs.Pipeline, + Dynamic: dc, + } + p.SetNamespace(ns) + + logOptions := options.LogOptions{ + PipelineRunName: name, + Follow: follow, + Params: &p, + Streamer: streamer, + Prefixing: prefixing, + } + + return &logOptions +} + +func fetchLogs(lo *options.LogOptions) (string, error) { + out := new(bytes.Buffer) + lo.Stream = &cli.Stream{Out: out, Err: out} + err := pipelinerun.Run(lo) + return out.String(), err +} +func logOptsv1beta1(name string, ns string, cs test.Clients, dc dynamic.Interface, streamer stream.NewStreamerFunc, follow bool, prefixing bool) *options.LogOptions { + p := test.Params{ + Kube: cs.Kube, + Tekton: cs.Pipeline, + Dynamic: dc, + } + p.SetNamespace(ns) + + logOptions := options.LogOptions{ + PipelineRunName: name, + Follow: follow, + Params: &p, + Streamer: streamer, + Prefixing: prefixing, + } + + return &logOptions +}