feat(helm): add --versions flag on search

This causes search to index by name/version instead of just name, which
means you can get a list of versions of a chart. The '--versions' flag
enables this behavior.

Partially fixes #1199
pull/1284/head
Matt Butcher 8 years ago
parent ea66d66d2d
commit d0cefeaf82

@ -43,7 +43,8 @@ type searchCmd struct {
out io.Writer
helmhome helmpath.Home
regexp bool
versions bool
regexp bool
}
func newSearchCmd(out io.Writer) *cobra.Command {
@ -59,7 +60,9 @@ func newSearchCmd(out io.Writer) *cobra.Command {
PreRunE: requireInit,
}
cmd.Flags().BoolVarP(&sc.regexp, "regexp", "r", false, "use regular expressions for searching")
f := cmd.Flags()
f.BoolVarP(&sc.regexp, "regexp", "r", false, "use regular expressions for searching")
f.BoolVarP(&sc.versions, "versions", "l", false, "show the long listing, with each version of each chart on its own line.")
return cmd
}
@ -88,16 +91,7 @@ func (s *searchCmd) run(args []string) error {
}
func (s *searchCmd) showAllCharts(i *search.Index) {
e := i.Entries()
res := make([]*search.Result, len(e))
j := 0
for name, ch := range e {
res[j] = &search.Result{
Name: name,
Chart: ch,
}
j++
}
res := i.All()
search.SortScore(res)
fmt.Fprintln(s.out, s.formatSearchResults(res))
}
@ -129,7 +123,7 @@ func (s *searchCmd) buildIndex() (*search.Index, error) {
continue
}
i.AddRepo(n, ind)
i.AddRepo(n, ind, s.versions)
}
return i, nil
}

@ -29,6 +29,8 @@ import (
"sort"
"strings"
"github.com/Masterminds/semver"
"k8s.io/helm/pkg/repo"
)
@ -55,25 +57,51 @@ func NewIndex() *Index {
return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}}
}
// verSep is a separator for version fields in map keys.
const verSep = "$$"
// AddRepo adds a repository index to the search index.
func (i *Index) AddRepo(rname string, ind *repo.IndexFile) {
func (i *Index) AddRepo(rname string, ind *repo.IndexFile, all bool) {
for name, ref := range ind.Entries {
if len(ref) == 0 {
// Skip chart names that havae zero releases.
// Skip chart names that have zero releases.
continue
}
// By convention, an index file is supposed to have the newest at the
// 0 slot, so our best bet is to grab the 0 entry and build the index
// entry off of that.
fname := filepath.Join(rname, name)
i.lines[fname] = indstr(rname, ref[0])
i.charts[fname] = ref[0]
if !all {
i.lines[fname] = indstr(rname, ref[0])
i.charts[fname] = ref[0]
continue
}
// If 'all' is set, then we go through all of the refs, and add them all
// to the index. This will generate a lot of near-duplicate entries.
for _, rr := range ref {
versionedName := fname + verSep + rr.Version
i.lines[versionedName] = indstr(rname, rr)
i.charts[versionedName] = rr
}
}
}
// Entries returns the entries in an index.
func (i *Index) Entries() map[string]*repo.ChartVersion {
return i.charts
// All returns all charts in the index as if they were search results.
//
// Each will be given a score of 0.
func (i *Index) All() []*Result {
res := make([]*Result, len(i.charts))
j := 0
for name, ch := range i.charts {
parts := strings.Split(name, verSep)
res[j] = &Result{
Name: parts[0],
Chart: ch,
}
j++
}
return res
}
// Search searches an index for the given term.
@ -118,7 +146,8 @@ func (i *Index) SearchLiteral(term string, threshold int) []*Result {
for k, v := range i.lines {
res := strings.Index(v, term)
if score := i.calcScore(res, v); res != -1 && score < threshold {
buf = append(buf, &Result{Name: k, Score: score, Chart: i.charts[k]})
parts := strings.Split(k, verSep) // Remove version, if it is there.
buf = append(buf, &Result{Name: parts[0], Score: score, Chart: i.charts[k]})
}
}
return buf
@ -137,7 +166,8 @@ func (i *Index) SearchRegexp(re string, threshold int) ([]*Result, error) {
continue
}
if score := i.calcScore(ind[0], v); ind[0] >= 0 && score < threshold {
buf = append(buf, &Result{Name: k, Score: score, Chart: i.charts[k]})
parts := strings.Split(k, verSep) // Remove version, if it is there.
buf = append(buf, &Result{Name: parts[0], Score: score, Chart: i.charts[k]})
}
}
return buf, nil
@ -179,6 +209,17 @@ func (s scoreSorter) Less(a, b int) bool {
if first.Score < second.Score {
return true
}
if first.Name == second.Name {
v1, err := semver.NewVersion(first.Chart.Version)
if err != nil {
return true
}
v2, err := semver.NewVersion(second.Chart.Version)
if err != nil {
return true
}
return v1.GreaterThan(v2)
}
return first.Name < second.Name
}

@ -93,9 +93,9 @@ var indexfileEntries = map[string]repo.ChartVersions{
},
}
func loadTestIndex(t *testing.T) *Index {
func loadTestIndex(t *testing.T, all bool) *Index {
i := NewIndex()
i.AddRepo("testing", &repo.IndexFile{Entries: indexfileEntries})
i.AddRepo("testing", &repo.IndexFile{Entries: indexfileEntries}, all)
i.AddRepo("ztesting", &repo.IndexFile{Entries: map[string]repo.ChartVersions{
"pinta": {
{
@ -107,10 +107,24 @@ func loadTestIndex(t *testing.T) *Index {
},
},
},
}})
}}, all)
return i
}
func TestAll(t *testing.T) {
i := loadTestIndex(t, false)
all := i.All()
if len(all) != 4 {
t.Errorf("Expected 4 entries, got %d", len(all))
}
i = loadTestIndex(t, true)
all = i.All()
if len(all) != 5 {
t.Errorf("Expected 5 entries, got %d", len(all))
}
}
func TestSearchByName(t *testing.T) {
tests := []struct {
@ -188,7 +202,7 @@ func TestSearchByName(t *testing.T) {
},
}
i := loadTestIndex(t)
i := loadTestIndex(t, false)
for _, tt := range tests {
@ -224,6 +238,18 @@ func TestSearchByName(t *testing.T) {
}
}
func TestSearchByNameAll(t *testing.T) {
// Test with the All bit turned on.
i := loadTestIndex(t, true)
cs, err := i.Search("santa-maria", 100, false)
if err != nil {
t.Fatal(err)
}
if len(cs) != 2 {
t.Errorf("expected 2 charts, got %d", len(cs))
}
}
func TestCalcScore(t *testing.T) {
i := NewIndex()

@ -41,6 +41,12 @@ func TestSearchCmd(t *testing.T) {
args: []string{"alpine"},
expect: "NAME \tVERSION\tDESCRIPTION \ntesting/alpine\t0.1.0 \tDeploy a basic Alpine Linux pod",
},
{
name: "search for 'alpine' with versions, expect three matches",
args: []string{"alpine"},
flags: []string{"--versions"},
expect: "NAME \tVERSION\tDESCRIPTION \ntesting/alpine\t0.2.0 \tDeploy a basic Alpine Linux pod\ntesting/alpine\t0.1.0 \tDeploy a basic Alpine Linux pod",
},
{
name: "search for 'syzygy', expect no matches",
args: []string{"syzygy"},

Loading…
Cancel
Save