From 159c06de8bd912553ec9dc7c8ec2e8574c6bd62a Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Thu, 23 Jul 2015 23:01:48 +0200 Subject: [PATCH] Include list of recent builds in error message Fixes #3359 --- pkg/build/api/sort.go | 17 +++++++++++ pkg/build/api/sort_test.go | 33 +++++++++++++++++++++ pkg/cmd/cli/cmd/buildlogs.go | 56 +++++++++++++++++++++++++++++++++++- 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 pkg/build/api/sort.go create mode 100644 pkg/build/api/sort_test.go diff --git a/pkg/build/api/sort.go b/pkg/build/api/sort.go new file mode 100644 index 000000000000..8d7107875079 --- /dev/null +++ b/pkg/build/api/sort.go @@ -0,0 +1,17 @@ +package api + +// ByCreationTimestamp implements sort.Interface for []Build based on the +// CreationTimestamp field. +type ByCreationTimestamp []Build + +func (b ByCreationTimestamp) Len() int { + return len(b) +} + +func (b ByCreationTimestamp) Less(i, j int) bool { + return b[i].CreationTimestamp.Before(b[j].CreationTimestamp) +} + +func (b ByCreationTimestamp) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} diff --git a/pkg/build/api/sort_test.go b/pkg/build/api/sort_test.go new file mode 100644 index 000000000000..61b7724c258e --- /dev/null +++ b/pkg/build/api/sort_test.go @@ -0,0 +1,33 @@ +package api + +import ( + "sort" + "testing" + "time" + + kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" +) + +func TestSortByCreationTimestamp(t *testing.T) { + present := util.Now() + past := util.NewTime(present.Add(-time.Minute)) + builds := []Build{ + { + ObjectMeta: kapi.ObjectMeta{ + Name: "present", + CreationTimestamp: present, + }, + }, + { + ObjectMeta: kapi.ObjectMeta{ + Name: "past", + CreationTimestamp: past, + }, + }, + } + sort.Sort(ByCreationTimestamp(builds)) + if [2]string{builds[0].Name, builds[1].Name} != [2]string{"past", "present"} { + t.Errorf("Unexpected sort order") + } +} diff --git a/pkg/cmd/cli/cmd/buildlogs.go b/pkg/cmd/cli/cmd/buildlogs.go index 61d1dafb6b5d..9ff2093188ec 100644 --- a/pkg/cmd/cli/cmd/buildlogs.go +++ b/pkg/cmd/cli/cmd/buildlogs.go @@ -4,12 +4,17 @@ import ( "fmt" "io" "os" + "sort" "strings" + "time" + "github.com/docker/docker/pkg/units" "github.com/spf13/cobra" kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" + "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" + "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/openshift/origin/pkg/build/api" buildutil "github.com/openshift/origin/pkg/build/util" @@ -56,7 +61,37 @@ func NewCmdBuildLogs(fullName string, f *clientcmd.Factory, out io.Writer) *cobr // RunBuildLogs contains all the necessary functionality for the OpenShift cli build-logs command func RunBuildLogs(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, opts api.BuildLogOptions, args []string) error { if len(args) != 1 { - return cmdutil.UsageError(cmd, "A build name is required") + // maximum time to wait for a list of builds + timeout := 800 * time.Millisecond + // maximum number of builds to list + maxBuildListLen := 10 + ch := make(chan error) + go func() { + // TODO fetch via API no more than maxBuildListLen builds + builds, err := getBuilds(f) + if err != nil { + return + } + if len(builds) == 0 { + ch <- cmdutil.UsageError(cmd, "There are no builds in the current project") + return + } + sort.Sort(sort.Reverse(api.ByCreationTimestamp(builds))) + msg := "A build name is required. Most recent builds:" + for i, b := range builds { + if i == maxBuildListLen { + break + } + msg += fmt.Sprintf("\n* %s\t%s\t%s ago", b.Name, b.Status.Phase, units.HumanDuration(time.Since(b.CreationTimestamp.Time))) + } + ch <- cmdutil.UsageError(cmd, msg) + }() + select { + case <-time.After(timeout): + return cmdutil.UsageError(cmd, "A build name is required") + case err := <-ch: + return err + } } namespace, _, err := f.DefaultNamespace() @@ -78,3 +113,22 @@ func RunBuildLogs(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, opts _, err = io.Copy(out, readCloser) return err } + +func getBuilds(f *clientcmd.Factory) ([]api.Build, error) { + namespace, _, err := f.DefaultNamespace() + if err != nil { + return nil, err + } + + c, _, err := f.Clients() + if err != nil { + return nil, err + } + + b, err := c.Builds(namespace).List(labels.Everything(), fields.Everything()) + if err != nil { + return nil, err + } + + return b.Items, nil +}