From 75a782c96ce02883a9e6a0fbf3e3c5499c0ee7f4 Mon Sep 17 00:00:00 2001 From: shubhamrai9122-creator Date: Tue, 20 Oct 2026 19:13:01 +0530 Subject: [PATCH] fix(customrun): return error when customrun is not found during delete Previously, deleteCustomRuns printed a 'not found' message when customRunExists returned an error, but skipped the resource without recording the error into deleteErr. As a result, tkn customrun delete exited 0 even when specified resources were not found. Record lookup and delete errors with multierr.Append and return them, to align CustomRun delete behavior with Task and Pipeline delete. Each failure is reported once, through the returned error, instead of also being printed to stderr, so non-NotFound lookup errors are no longer shown as "not found". The tests assert that the not found and delete failure messages appear once in the combined output. Fixes #3329 Signed-off-by: shubhamrai9122-creator --- pkg/cmd/customrun/delete.go | 6 +-- pkg/cmd/customrun/delete_test.go | 64 +++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/pkg/cmd/customrun/delete.go b/pkg/cmd/customrun/delete.go index da972e214..c61daeaf7 100644 --- a/pkg/cmd/customrun/delete.go +++ b/pkg/cmd/customrun/delete.go @@ -23,6 +23,7 @@ import ( "github.com/tektoncd/cli/pkg/cli" "github.com/tektoncd/cli/pkg/formatted" "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "go.uber.org/multierr" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -103,14 +104,13 @@ func deleteCustomRuns(s *cli.Stream, p cli.Params, crNames []string, output stri for _, crName := range crNames { err := customRunExists(cs, namespace, crName) if err != nil { - fmt.Fprintf(s.Err, "CustomRun %s not found in namespace %s\n", crName, namespace) + deleteErr = multierr.Append(deleteErr, err) continue } err = deleteCustomRun(cs, namespace, crName) if err != nil { - fmt.Fprintf(s.Err, "failed to delete CustomRun %s: %v\n", crName, err) - deleteErr = err + deleteErr = multierr.Append(deleteErr, err) continue } deleted = append(deleted, crName) diff --git a/pkg/cmd/customrun/delete_test.go b/pkg/cmd/customrun/delete_test.go index 1fc8ebd68..5d06e7c00 100644 --- a/pkg/cmd/customrun/delete_test.go +++ b/pkg/cmd/customrun/delete_test.go @@ -334,21 +334,21 @@ func TestCustomRunDelete(t *testing.T) { command: commandV1beta1(t, crs, now, ns, dynamicClient), args: []string{"delete", "customrun-xyz", "-n", "ns-1"}, wantError: true, - want: "failed to delete CustomRun customrun-xyz: customruns.tekton.dev customrun-xyz not found\n", + want: "CustomRun customrun-xyz not found in namespace ns-1", }, { name: "Delete one customrun without namespace", command: commandV1beta1(t, crs, now, ns, dynamicClient), args: []string{"delete", "customrun-1"}, - wantError: false, - want: "CustomRun customrun-1 not found in namespace \n", + wantError: true, + want: "CustomRun customrun-1 not found in namespace ", }, { name: "Delete multiple customruns without namespace", command: commandV1beta1(t, crs, now, ns, dynamicClient), args: []string{"delete", "customrun-2", "customrun-3"}, - wantError: false, - want: "CustomRun customrun-2 not found in namespace \nCustomRun customrun-3 not found in namespace \n", + wantError: true, + want: "CustomRun customrun-2 not found in namespace ; CustomRun customrun-3 not found in namespace ", }, { name: "Delete one customrun with namespace", @@ -375,7 +375,9 @@ func TestCustomRunDelete(t *testing.T) { } if td.wantError { - if err != nil { + if err == nil { + t.Errorf("Expected error but got none") + } else { test.AssertOutput(t, td.want, err.Error()) } } else { @@ -540,4 +542,54 @@ func TestCustomRunDeleteStructuredOutputPartialFailure(t *testing.T) { if !strings.Contains(out, "\"deleted\"") { t.Fatalf("expected deleted key in structured output, got: %q", out) } + if n := strings.Count(out, "failed to delete CustomRun customrun-2"); n != 1 { + t.Fatalf("expected the delete failure to be reported once, got %d times in: %q", n, out) + } +} + +func TestCustomRunDeleteResourceNotFound(t *testing.T) { + now := time.Now() + crs := []*v1beta1.CustomRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "customrun-1", + Namespace: "ns-1", + }, + Spec: v1beta1.CustomRunSpec{}, + Status: v1beta1.CustomRunStatus{ + Status: duckv1.Status{ + Conditions: duckv1.Conditions{ + {Status: corev1.ConditionTrue, Reason: v1beta1.CustomRunReasonSuccessful.String()}, + }, + }, + }, + }, + } + + ns := []*corev1.Namespace{ + {ObjectMeta: metav1.ObjectMeta{Name: "ns-1"}}, + } + + tdc := testDynamic.Options{} + dynamicClient, err := tdc.Client( + cb.UnstructuredV1beta1CustomRun(crs[0], versionv1beta1), + ) + if err != nil { + t.Fatalf("unable to create dynamic client: %v", err) + } + + out, err := test.ExecuteCommand(commandV1beta1(t, crs, now, ns, dynamicClient), + "delete", "customrun-1", "nonexistent", "-n", "ns-1", "-o", "json") + if err == nil { + t.Fatal("expected error when a resource is not found") + } + if !strings.Contains(err.Error(), "CustomRun nonexistent not found in namespace ns-1") { + t.Fatalf("expected not found error, got: %v", err) + } + if !strings.Contains(out, "\"customrun-1\"") { + t.Fatalf("expected successful delete of existing resource in structured output, got: %q", out) + } + if n := strings.Count(out, "CustomRun nonexistent not found in namespace ns-1"); n != 1 { + t.Fatalf("expected the not found error to be reported once, got %d times in: %q", n, out) + } }