diff --git a/docs/cmd/tkn_pipelinerun_delete.md b/docs/cmd/tkn_pipelinerun_delete.md index e6e943fd5..6fff0a576 100644 --- a/docs/cmd/tkn_pipelinerun_delete.md +++ b/docs/cmd/tkn_pipelinerun_delete.md @@ -28,6 +28,7 @@ or ### Options ``` + --all Delete all pipelineruns in a namespace (default: false) --allow-missing-template-keys If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. (default true) -f, --force Whether to force deletion (default: false) -h, --help help for delete diff --git a/docs/man/man1/tkn-pipelinerun-delete.1 b/docs/man/man1/tkn-pipelinerun-delete.1 index c2da3af79..ba7c03c05 100644 --- a/docs/man/man1/tkn-pipelinerun-delete.1 +++ b/docs/man/man1/tkn-pipelinerun-delete.1 @@ -19,6 +19,10 @@ Delete pipelineruns in a namespace .SH OPTIONS +.PP +\fB\-\-all\fP[=false] + Delete all pipelineruns in a namespace (default: false) + .PP \fB\-\-allow\-missing\-template\-keys\fP[=true] If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. diff --git a/pkg/cmd/pipelinerun/delete.go b/pkg/cmd/pipelinerun/delete.go index a226a1544..6291104dc 100644 --- a/pkg/cmd/pipelinerun/delete.go +++ b/pkg/cmd/pipelinerun/delete.go @@ -28,7 +28,7 @@ import ( ) func deleteCommand(p cli.Params) *cobra.Command { - opts := &options.DeleteOptions{Resource: "pipelinerun", ForceDelete: false, ParentResource: "pipeline"} + opts := &options.DeleteOptions{Resource: "pipelinerun", ForceDelete: false, ParentResource: "pipeline", DeleteAllNs: false} f := cliopts.NewPrintFlags("delete") eg := `Delete PipelineRuns with names 'foo' and 'bar' in namespace 'quux': @@ -64,37 +64,54 @@ or return err } - return deletePipelineRuns(s, p, args, opts.ParentResourceName) + return deletePipelineRuns(s, p, args, opts) }, } f.AddFlags(c) c.Flags().BoolVarP(&opts.ForceDelete, "force", "f", false, "Whether to force deletion (default: false)") c.Flags().StringVarP(&opts.ParentResourceName, "pipeline", "p", "", "The name of a pipeline whose pipelineruns should be deleted (does not delete the pipeline)") + c.Flags().BoolVarP(&opts.DeleteAllNs, "all", "", false, "Delete all pipelineruns in a namespace (default: false)") _ = c.MarkZshCompPositionalArgumentCustom(1, "__tkn_get_pipelinerun") return c } -func deletePipelineRuns(s *cli.Stream, p cli.Params, prNames []string, parentPipeline string) error { +func deletePipelineRuns(s *cli.Stream, p cli.Params, prNames []string, opts *options.DeleteOptions) error { cs, err := p.Clients() if err != nil { return fmt.Errorf("failed to create tekton client") } var d *deleter.Deleter - if parentPipeline == "" { + switch { + case opts.DeleteAllNs: + d = deleter.New("PipelineRun", func(pipelineRunName string) error { + return cs.Tekton.TektonV1alpha1().PipelineRuns(p.Namespace()).Delete(pipelineRunName, &metav1.DeleteOptions{}) + }) + prs, err := allPipelineRunNames(p, cs) + if err != nil { + return err + } + d.Delete(s, prs) + case opts.ParentResourceName == "": d = deleter.New("PipelineRun", func(pipelineRunName string) error { return cs.Tekton.TektonV1alpha1().PipelineRuns(p.Namespace()).Delete(pipelineRunName, &metav1.DeleteOptions{}) }) d.Delete(s, prNames) - } else { + default: d = deleter.New("Pipeline", func(_ string) error { return errors.New("the pipeline should not be deleted") }) d.WithRelated("PipelineRun", pipelineRunLister(p, cs), func(pipelineRunName string) error { return cs.Tekton.TektonV1alpha1().PipelineRuns(p.Namespace()).Delete(pipelineRunName, &metav1.DeleteOptions{}) }) - d.DeleteRelated(s, []string{parentPipeline}) + d.DeleteRelated(s, []string{opts.ParentResourceName}) + } + if !opts.DeleteAllNs { + d.PrintSuccesses(s) + } else if opts.DeleteAllNs { + if d.Errors() == nil { + fmt.Fprintf(s.Out, "All PipelineRuns deleted in namespace %q\n", p.Namespace()) + } } - d.PrintSuccesses(s) return d.Errors() } @@ -114,3 +131,15 @@ func pipelineRunLister(p cli.Params, cs *cli.Clients) func(string) ([]string, er return names, nil } } + +func allPipelineRunNames(p cli.Params, cs *cli.Clients) ([]string, error) { + pipelineRuns, err := cs.Tekton.TektonV1alpha1().PipelineRuns(p.Namespace()).List(metav1.ListOptions{}) + if err != nil { + return nil, err + } + var names []string + for _, pr := range pipelineRuns.Items { + names = append(names, pr.Name) + } + return names, nil +} diff --git a/pkg/cmd/pipelinerun/delete_test.go b/pkg/cmd/pipelinerun/delete_test.go index 4c1151e4f..c9a4e1b47 100644 --- a/pkg/cmd/pipelinerun/delete_test.go +++ b/pkg/cmd/pipelinerun/delete_test.go @@ -44,7 +44,7 @@ func TestPipelineRunDelete(t *testing.T) { } seeds := make([]pipelinetest.Clients, 0) - for i := 0; i < 3; i++ { + for i := 0; i < 5; i++ { cs, _ := test.SeedTestData(t, pipelinetest.Data{ Pipelines: []*v1alpha1.Pipeline{ tb.Pipeline("pipeline", "ns", @@ -68,7 +68,38 @@ func TestPipelineRunDelete(t *testing.T) { cb.PipelineRunCompletionTime(clock.Now().Add(10*time.Minute)), ), ), + tb.PipelineRun("pipeline-run-2", "ns", + cb.PipelineRunCreationTimestamp(clock.Now()), + tb.PipelineRunLabel("tekton.dev/pipeline", "pipeline"), + tb.PipelineRunSpec("pipeline"), + tb.PipelineRunStatus( + tb.PipelineRunStatusCondition(apis.Condition{ + Status: corev1.ConditionTrue, + Reason: resources.ReasonSucceeded, + }), + // pipeline run starts now + tb.PipelineRunStartTime(clock.Now()), + // takes 10 minutes to complete + cb.PipelineRunCompletionTime(clock.Now().Add(10*time.Minute)), + ), + ), + tb.PipelineRun("pipeline-run-3", "ns", + cb.PipelineRunCreationTimestamp(clock.Now()), + tb.PipelineRunLabel("tekton.dev/pipeline", "pipeline"), + tb.PipelineRunSpec("pipeline"), + tb.PipelineRunStatus( + tb.PipelineRunStatusCondition(apis.Condition{ + Status: corev1.ConditionTrue, + Reason: resources.ReasonSucceeded, + }), + // pipeline run starts now + tb.PipelineRunStartTime(clock.Now()), + // takes 10 minutes to complete + cb.PipelineRunCompletionTime(clock.Now().Add(10*time.Minute)), + ), + ), }, + Namespaces: ns, }) seeds = append(seeds, cs) @@ -144,7 +175,31 @@ func TestPipelineRunDelete(t *testing.T) { input: seeds[0], inputStream: strings.NewReader("y"), wantError: false, - want: `Are you sure you want to delete all pipelineruns related to pipeline "pipeline" (y/n): `, + want: "Are you sure you want to delete all pipelineruns related to pipeline \"pipeline\" (y/n): PipelineRuns deleted: \"pipeline-run-2\", \"pipeline-run-3\"\n", + }, + { + name: "Delete all with prompt", + command: []string{"delete", "--all", "-n", "ns"}, + input: seeds[3], + inputStream: strings.NewReader("y"), + wantError: false, + want: "Are you sure you want to delete all pipelineruns in namespace \"ns\" (y/n): All PipelineRuns deleted in namespace \"ns\"\n", + }, + { + name: "Delete all with -f", + command: []string{"delete", "--all", "-f", "-n", "ns"}, + input: seeds[4], + inputStream: nil, + wantError: false, + want: "All PipelineRuns deleted in namespace \"ns\"\n", + }, + { + name: "Error from using pipelinerun name with --all", + command: []string{"delete", "pipelinerun", "--all", "-n", "ns"}, + input: seeds[4], + inputStream: nil, + wantError: true, + want: "--all flag should not have any arguments or flags specified with it", }, } diff --git a/pkg/cmd/taskrun/delete_test.go b/pkg/cmd/taskrun/delete_test.go index eff8cf398..b48e2ae7b 100644 --- a/pkg/cmd/taskrun/delete_test.go +++ b/pkg/cmd/taskrun/delete_test.go @@ -160,7 +160,7 @@ func TestTaskRunDelete(t *testing.T) { name: "Delete all with -f", command: []string{"delete", "--all", "-f", "-n", "ns"}, input: seeds[4], - inputStream: strings.NewReader("y"), + inputStream: nil, wantError: false, want: "All TaskRuns deleted in namespace \"ns\"\n", }, @@ -168,7 +168,7 @@ func TestTaskRunDelete(t *testing.T) { name: "Error from using taskrun name with --all", command: []string{"delete", "taskrun", "--all", "-n", "ns"}, input: seeds[4], - inputStream: strings.NewReader("y"), + inputStream: nil, wantError: true, want: "--all flag should not have any arguments or flags specified with it", },