Commit e869c36c authored by Matt Butcher's avatar Matt Butcher

feat(helm, tiller): implement list

parent 6950fe42
......@@ -2,31 +2,42 @@ package main
import (
"fmt"
"sort"
"time"
"github.com/gosuri/uitable"
"github.com/kubernetes/helm/pkg/helm"
"github.com/kubernetes/helm/pkg/proto/hapi/release"
"github.com/kubernetes/helm/pkg/timeconv"
"github.com/spf13/cobra"
)
var listHelp = `
This command lists all of the currently deployed releases.
By default, items are sorted alphabetically.
By default, items are sorted alphabetically. Sorting is done client-side, so if
the number of releases is less than the setting in '--max', some values will
be omitted, and in no particular lexicographic order.
`
var listCommand = &cobra.Command{
Use: "list [flags] [FILTER]",
Use: "list [flags]",
Short: "List releases",
Long: listHelp,
RunE: listCmd,
Aliases: []string{"ls"},
}
var listLong bool
var listMax int
var listOffset int
var listByDate bool
func init() {
listCommand.LocalFlags().BoolVar(&listLong, "l", false, "output long listing format")
listCommand.LocalFlags().IntVar(&listMax, "m", 256, "maximum number of releases to fetch")
listCommand.Flags().BoolVarP(&listLong, "long", "l", false, "output long listing format")
listCommand.Flags().BoolVarP(&listByDate, "date", "d", false, "sort by release date")
listCommand.Flags().IntVarP(&listMax, "max", "m", 256, "maximum number of releases to fetch")
listCommand.Flags().IntVarP(&listOffset, "offset", "o", 0, "offset from start value (zero-indexed)")
RootCommand.AddCommand(listCommand)
}
......@@ -35,7 +46,7 @@ func listCmd(cmd *cobra.Command, args []string) error {
fmt.Println("TODO: Implement filter.")
}
res, err := helm.ListReleases(listMax, 0)
res, err := helm.ListReleases(listMax, listOffset)
if err != nil {
return err
}
......@@ -45,7 +56,11 @@ func listCmd(cmd *cobra.Command, args []string) error {
fmt.Println("Not all values were fetched.")
}
// TODO: Add sort here.
if listByDate {
sort.Sort(byDate(rels))
} else {
sort.Sort(byName(rels))
}
// Purty output, ya'll
if listLong {
......@@ -59,10 +74,38 @@ func listCmd(cmd *cobra.Command, args []string) error {
}
func formatList(rels []*release.Release) error {
// TODO: Pretty it up
table := uitable.New()
table.MaxColWidth = 30
table.AddRow("NAME", "UPDATED", "CHART")
for _, r := range rels {
fmt.Println(r.Name)
c := fmt.Sprintf("%s-%s", r.Chart.Metadata.Name, r.Chart.Metadata.Version)
t := timeconv.Format(r.Info.LastDeployed, time.ANSIC)
table.AddRow(r.Name, t, c)
}
fmt.Println(table)
return nil
}
// byName implements the sort.Interface for []*release.Release.
type byName []*release.Release
func (r byName) Len() int {
return len(r)
}
func (r byName) Swap(p, q int) {
r[p], r[q] = r[q], r[p]
}
func (r byName) Less(i, j int) bool {
return r[i].Name < r[j].Name
}
type byDate []*release.Release
func (r byDate) Len() int { return len(r) }
func (r byDate) Swap(p, q int) {
r[p], r[q] = r[q], r[p]
}
func (r byDate) Less(p, q int) bool {
return r[p].Info.LastDeployed.Seconds < r[q].Info.LastDeployed.Seconds
}
......@@ -3,6 +3,7 @@ package main
import (
"bytes"
"errors"
"fmt"
"log"
"github.com/kubernetes/helm/cmd/tiller/environment"
......@@ -33,18 +34,39 @@ var (
errMissingRelease = errors.New("no release provided")
)
// ListDefaultLimit is the default limit for number of items returned in a list.
var ListDefaultLimit int64 = 512
func (s *releaseServer) ListReleases(req *services.ListReleasesRequest, stream services.ReleaseService_ListReleasesServer) error {
rels, err := s.env.Releases.List()
if err != nil {
return err
}
total := int64(len(rels))
l := int64(len(rels))
if req.Offset > 0 {
if req.Offset >= l {
return fmt.Errorf("offset %d is outside of range %d", req.Offset, l)
}
rels = rels[req.Offset:]
l = int64(len(rels))
}
if req.Limit == 0 {
req.Limit = ListDefaultLimit
}
if l > req.Limit {
rels = rels[0:req.Limit]
l = int64(len(rels))
}
res := &services.ListReleasesResponse{
Offset: 0,
Count: l,
Total: l,
Total: total,
Releases: rels,
}
stream.Send(res)
......
package main
import (
"fmt"
"strings"
"testing"
......@@ -12,6 +13,7 @@ import (
"github.com/kubernetes/helm/pkg/storage"
"github.com/kubernetes/helm/pkg/timeconv"
"golang.org/x/net/context"
"google.golang.org/grpc/metadata"
)
func rsFixture() *releaseServer {
......@@ -193,8 +195,44 @@ func TestGetReleaseStatus(t *testing.T) {
}
}
func TestListReleases(t *testing.T) {
rs := rsFixture()
num := 7
for i := 0; i < num; i++ {
rel := releaseMock()
rel.Name = fmt.Sprintf("rel-%d", i)
if err := rs.env.Releases.Create(rel); err != nil {
t.Fatalf("Could not store mock release: %s", err)
}
}
mrs := &mockListServer{}
if err := rs.ListReleases(&services.ListReleasesRequest{Offset: 0, Limit: 64}, mrs); err != nil {
t.Fatalf("Failed listing: %s", err)
}
if len(mrs.val.Releases) != num {
t.Errorf("Expected %d releases, got %d", num, len(mrs.val.Releases))
}
}
func mockEnvironment() *environment.Environment {
e := environment.New()
e.Releases = storage.NewMemory()
return e
}
type mockListServer struct {
val *services.ListReleasesResponse
}
func (l *mockListServer) Send(res *services.ListReleasesResponse) error {
l.val = res
return nil
}
func (l *mockListServer) Context() context.Context { return context.TODO() }
func (l *mockListServer) SendMsg(v interface{}) error { return nil }
func (l *mockListServer) RecvMsg(v interface{}) error { return nil }
func (l *mockListServer) SendHeader(m metadata.MD) error { return nil }
func (l *mockListServer) SetTrailer(m metadata.MD) {}
......@@ -20,7 +20,10 @@ func ListReleases(limit, offset int) (*services.ListReleasesResponse, error) {
}
defer c.Close()
req := &services.ListReleasesRequest{}
req := &services.ListReleasesRequest{
Limit: int64(limit),
Offset: int64(offset),
}
cli, err := c.impl.ListReleases(context.TODO(), req, c.cfg.CallOpts()...)
if err != nil {
return nil, err
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment