-
Notifications
You must be signed in to change notification settings - Fork 76
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: tkn-result add pipelinerun list and result describe command
- Loading branch information
Showing
14 changed files
with
617 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package pipelinerun | ||
|
||
import ( | ||
"fmt" | ||
"text/tabwriter" | ||
"text/template" | ||
|
||
"github.com/tektoncd/cli/pkg/cli" | ||
|
||
"github.com/jonboulle/clockwork" | ||
"github.com/spf13/cobra" | ||
"github.com/tektoncd/results/pkg/cli/flags" | ||
"github.com/tektoncd/results/pkg/cli/format" | ||
pb "github.com/tektoncd/results/proto/v1alpha2/results_go_proto" | ||
) | ||
|
||
const listTemplate = `{{- $size := len .Results -}}{{- if eq $size 0 -}} | ||
No PipelineRuns found | ||
{{ else -}} | ||
NAMESPACE UID STARTED DURATION STATUS | ||
{{- range $_, $result := .Results }} | ||
{{ formatNamespace $result.Name }} {{ $result.Uid }} {{ formatAge $result.Summary.StartTime $.Time }} {{ formatDuration $result.Summary.StartTime $result.Summary.EndTime }} {{ formatStatus $result.Summary.Status }} | ||
{{- end -}} | ||
{{- end -}}` | ||
|
||
type ListOptions struct { | ||
Namespace string | ||
Limit int | ||
} | ||
|
||
// listCommand initializes a cobra command to list PipelineRuns | ||
func listCommand(params *flags.Params) *cobra.Command { | ||
opts := &ListOptions{Limit: 0, Namespace: "default"} | ||
|
||
eg := `List all PipelineRuns in a namespace 'foo': | ||
tkn-results pipelinerun list -n foo | ||
List all PipelineRuns in 'default' namespace: | ||
tkn-results pipelinerun list -n default | ||
` | ||
cmd := &cobra.Command{ | ||
Use: "list", | ||
Aliases: []string{"ls"}, | ||
Short: "List PipelineRuns in a namespace", | ||
Annotations: map[string]string{ | ||
"commandType": "main", | ||
}, | ||
Example: eg, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
if opts.Limit < 0 { | ||
return fmt.Errorf("limit was %d, but must be greater than 0", opts.Limit) | ||
} | ||
|
||
resp, err := params.ResultsClient.ListResults(cmd.Context(), &pb.ListResultsRequest{ | ||
Parent: opts.Namespace, | ||
PageSize: int32(opts.Limit), | ||
Filter: `summary.type==PIPELINE_RUN`, | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("failed to list PipelineRuns from namespace %s: %v", opts.Namespace, err) | ||
} | ||
stream := &cli.Stream{ | ||
Out: cmd.OutOrStdout(), | ||
Err: cmd.OutOrStderr(), | ||
} | ||
return printFormatted(stream, resp.Results, params.Clock) | ||
}, | ||
} | ||
cmd.Flags().StringVarP(&opts.Namespace, "namespace", "n", "default", "Namespace to list PipelineRuns in") | ||
cmd.Flags().IntVarP(&opts.Limit, "limit", "l", 0, "Limit the number of PipelineRuns to return") | ||
return cmd | ||
} | ||
|
||
func printFormatted(s *cli.Stream, results []*pb.Result, c clockwork.Clock) error { | ||
var data = struct { | ||
Results []*pb.Result | ||
Time clockwork.Clock | ||
}{ | ||
Results: results, | ||
Time: c, | ||
} | ||
funcMap := template.FuncMap{ | ||
"formatAge": format.Age, | ||
"formatDuration": format.Duration, | ||
"formatStatus": format.Status, | ||
"formatNamespace": format.Namespace, | ||
} | ||
|
||
w := tabwriter.NewWriter(s.Out, 0, 5, 3, ' ', tabwriter.TabIndent) | ||
t := template.Must(template.New("List PipelineRuns").Funcs(funcMap).Parse(listTemplate)) | ||
|
||
err := t.Execute(w, data) | ||
if err != nil { | ||
return err | ||
} | ||
return w.Flush() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package pipelinerun | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/tektoncd/results/pkg/test" | ||
|
||
"github.com/tektoncd/results/pkg/cli/flags" | ||
"github.com/tektoncd/results/pkg/test/fake" | ||
|
||
"github.com/jonboulle/clockwork" | ||
|
||
"github.com/spf13/cobra" | ||
pb "github.com/tektoncd/results/proto/v1alpha2/results_go_proto" | ||
"google.golang.org/protobuf/types/known/timestamppb" | ||
) | ||
|
||
func TestListPipelineRuns_empty(t *testing.T) { | ||
results := []*pb.Result{} | ||
now := time.Now() | ||
cmd := command(results, now) | ||
|
||
output, err := test.ExecuteCommand(cmd, "list") | ||
if err != nil { | ||
t.Errorf("Unexpected error: %v", err) | ||
} | ||
test.AssertOutput(t, "No PipelineRuns found\n", output) | ||
} | ||
|
||
func TestListPipelineRuns(t *testing.T) { | ||
clock := clockwork.NewFakeClock() | ||
createTime := clock.Now().Add(time.Duration(-3) * time.Minute) | ||
updateTime := clock.Now().Add(time.Duration(-2) * time.Minute) | ||
startTime := clock.Now().Add(time.Duration(-3) * time.Minute) | ||
endTime := clock.Now().Add(time.Duration(-1) * time.Minute) | ||
results := []*pb.Result{ | ||
{ | ||
Name: "default/results/e6b4b2e3-d876-4bbe-a927-95c691b6fdc7", | ||
Uid: "949eebd9-1cf7-478f-a547-9ee313035f10", | ||
CreateTime: timestamppb.New(createTime), | ||
UpdateTime: timestamppb.New(updateTime), | ||
Annotations: map[string]string{ | ||
"object.metadata.name": "hello-goodbye-run-vfsxn", | ||
"tekton.dev/pipeline": "hello-goodbye", | ||
}, | ||
Summary: &pb.RecordSummary{ | ||
Record: "default/results/e6b4b2e3-d876-4bbe-a927-95c691b6fdc7/records/e6b4b2e3-d876-4bbe-a927-95c691b6fdc7", | ||
Type: "tekton.dev/v1.PipelineRun", | ||
StartTime: timestamppb.New(startTime), | ||
EndTime: timestamppb.New(endTime), | ||
Status: pb.RecordSummary_SUCCESS, | ||
}, | ||
}, | ||
{ | ||
Name: "default/results/3dacd30b-ce42-476c-be7e-84b0f664df55", | ||
Uid: "c8d4cd50-06e8-4325-9ba2-044e6cc45235", | ||
CreateTime: timestamppb.New(createTime), | ||
UpdateTime: timestamppb.New(updateTime), | ||
Annotations: map[string]string{ | ||
"object.metadata.name": "hello-goodbye-run-xtw2j", | ||
}, | ||
Summary: &pb.RecordSummary{ | ||
Record: "default/results/3dacd30b-ce42-476c-be7e-84b0f664df55/records/3dacd30b-ce42-476c-be7e-84b0f664df55", | ||
Type: "tekton.dev/v1.PipelineRun", | ||
}, | ||
}, | ||
} | ||
cmd := command(results, clock.Now()) | ||
output, err := test.ExecuteCommand(cmd, "list") | ||
if err != nil { | ||
t.Errorf("Unexpected error: %v", err) | ||
} | ||
test.AssertOutput(t, `NAMESPACE UID STARTED DURATION STATUS | ||
default 949eebd9-1cf7-478f-a547-9ee313035f10 3 minutes ago 2m0s Succeeded | ||
default c8d4cd50-06e8-4325-9ba2-044e6cc45235 --- --- Unknown`, output) | ||
} | ||
|
||
func command(results []*pb.Result, now time.Time) *cobra.Command { | ||
clock := clockwork.NewFakeClockAt(now) | ||
|
||
param := &flags.Params{ | ||
ResultsClient: fake.NewResultsClient(results), | ||
LogsClient: nil, | ||
PluginLogsClient: nil, | ||
Clock: clock, | ||
} | ||
cmd := Command(param) | ||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package pipelinerun | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
"github.com/tektoncd/results/pkg/cli/flags" | ||
) | ||
|
||
// Command initializes a cobra command for `pipelinerun` sub commands | ||
func Command(params *flags.Params) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "pipelinerun", | ||
Aliases: []string{"pr", "pipelineruns"}, | ||
Short: "Query PipelineRuns", | ||
Annotations: map[string]string{ | ||
"commandType": "main", | ||
}, | ||
} | ||
|
||
cmd.AddCommand(listCommand(params)) | ||
|
||
return cmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package result | ||
|
||
import ( | ||
"fmt" | ||
"text/tabwriter" | ||
"text/template" | ||
|
||
"github.com/tektoncd/cli/pkg/cli" | ||
|
||
"github.com/jonboulle/clockwork" | ||
"github.com/spf13/cobra" | ||
"github.com/tektoncd/results/pkg/cli/flags" | ||
"github.com/tektoncd/results/pkg/cli/format" | ||
pb "github.com/tektoncd/results/proto/v1alpha2/results_go_proto" | ||
) | ||
|
||
const resultDescTmpl = `Name: {{.Result.Name}} | ||
UID: {{.Result.Uid}} | ||
{{- if .Result.Annotations}} | ||
Annotations: | ||
{{- range $key, $value := .Result.Annotations}} | ||
{{$key}}={{$value}} | ||
{{- end}} | ||
{{- end}} | ||
Status: | ||
Created: {{formatAge .Result.CreateTime .Time}} DURATION: {{formatDuration .Result.CreateTime .Result.UpdateTime}} | ||
{{- if .Result.Summary}} | ||
Summary: | ||
Type: {{.Result.Summary.Type}} | ||
Status: | ||
STARTED DURATION STATUS | ||
{{formatAge .Result.Summary.StartTime .Time}} {{formatDuration .Result.Summary.StartTime .Result.Summary.EndTime}} {{.Result.Summary.Status}} | ||
{{- if .Result.Summary.Annotations}} | ||
Annotations: | ||
{{- range $key, $value := .Result.Summary.Annotations}} | ||
{{$key}}={{$value}} | ||
{{- end}} | ||
{{- end}} | ||
{{- end}} | ||
` | ||
|
||
type DescribeOptions struct { | ||
Parent string | ||
Uid string | ||
} | ||
|
||
func describeCommand(params *flags.Params) *cobra.Command { | ||
opts := &DescribeOptions{} | ||
eg := `Query by name: | ||
tkn-result result describe default/results/e6b4b2e3-d876-4bbe-a927-95c691b6fdc7 | ||
Query by parent and uid: | ||
tkn-result result desc --parent default --uid 949eebd9-1cf7-478f-a547-9ee313035f10 | ||
` | ||
cmd := &cobra.Command{ | ||
Use: "describe [-p parent -u uid] [name]", | ||
Aliases: []string{"desc"}, | ||
Short: "Describes a Result", | ||
Annotations: map[string]string{ | ||
"commandType": "main", | ||
}, | ||
Example: eg, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
stream := &cli.Stream{ | ||
Out: cmd.OutOrStdout(), | ||
Err: cmd.OutOrStderr(), | ||
} | ||
if len(args) > 0 { | ||
name := args[0] | ||
result, err := params.ResultsClient.GetResult(cmd.Context(), &pb.GetResultRequest{ | ||
Name: name, | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("failed to get result of name %s: %v", name, err) | ||
} | ||
return printResultDescription(stream, result, params.Clock) | ||
} | ||
|
||
if opts.Parent != "" && opts.Uid != "" { | ||
resp, err := params.ResultsClient.ListResults(cmd.Context(), &pb.ListResultsRequest{ | ||
Parent: opts.Parent, | ||
Filter: fmt.Sprintf(`uid=="%s"`, opts.Uid), | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("failed to get result of parent %s and uid %s: %v", opts.Parent, opts.Uid, err) | ||
} | ||
if len(resp.Results) == 0 { | ||
return fmt.Errorf("no result found with parent %s and uid %s", opts.Parent, opts.Uid) | ||
} | ||
return printResultDescription(stream, resp.Results[0], params.Clock) | ||
} | ||
return nil | ||
}, | ||
} | ||
|
||
cmd.Flags().StringVarP(&opts.Parent, "parent", "p", "", "parent of the result") | ||
cmd.Flags().StringVarP(&opts.Uid, "uid", "u", "", "uid of the result") | ||
return cmd | ||
} | ||
|
||
func printResultDescription(s *cli.Stream, result *pb.Result, c clockwork.Clock) error { | ||
data := struct { | ||
Result *pb.Result | ||
Time clockwork.Clock | ||
}{ | ||
Result: result, | ||
Time: c, | ||
} | ||
funcMap := template.FuncMap{ | ||
"formatAge": format.Age, | ||
"formatDuration": format.Duration, | ||
"formatStatus": format.Status, | ||
} | ||
w := tabwriter.NewWriter(s.Out, 0, 5, 3, ' ', tabwriter.TabIndent) | ||
t := template.Must(template.New("Describe A Result").Funcs(funcMap).Parse(resultDescTmpl)) | ||
err := t.Execute(w, data) | ||
if err != nil { | ||
return err | ||
} | ||
return w.Flush() | ||
} |
Oops, something went wrong.