Commit 25c7e6dc authored by Matt Butcher's avatar Matt Butcher

fix(helm): allow entries to be merged into index

Between Alpha.4 and Alpha.5 there was a change in the indexing logic.
This prevent indices from being appended to (because those index files
were often broken). This change allows the user to explicitly merge an
existing index and a generated index.

Closes #1334
parent 9c3f883e
......@@ -25,10 +25,22 @@ import (
"k8s.io/helm/pkg/repo"
)
const repoIndexDesc = `
Read the current directory and generate an index file based on the charts found.
This tool is used for creating an 'index.yaml' file for a chart repository. To
set an absolute URL to the charts, use '--url' flag.
To merge the generated index with an existing index file, use the '--merge'
flag. In this case, the charts found in the current directory will be merged
into the existing index, with local charts taking priority over existing charts.
`
type repoIndexCmd struct {
dir string
url string
out io.Writer
dir string
url string
out io.Writer
merge string
}
func newRepoIndexCmd(out io.Writer) *cobra.Command {
......@@ -39,6 +51,7 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "index [flags] [DIR]",
Short: "generate an index file given a directory containing packaged charts",
Long: repoIndexDesc,
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkArgsLength(len(args), "path to a directory"); err != nil {
return err
......@@ -52,6 +65,7 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command {
f := cmd.Flags()
f.StringVar(&index.url, "url", "", "url of chart repository")
f.StringVar(&index.merge, "merge", "", "merge the generated index into the given index")
return cmd
}
......@@ -62,14 +76,22 @@ func (i *repoIndexCmd) run() error {
return err
}
return index(path, i.url)
return index(path, i.url, i.merge)
}
func index(dir, url string) error {
func index(dir, url, mergeTo string) error {
chartRepo, err := repo.LoadChartRepository(dir, url)
if err != nil {
return err
}
if mergeTo != "" {
old, err := repo.LoadIndexFile(mergeTo)
if err != nil {
return err
}
return chartRepo.MergeIndex(old)
}
return chartRepo.Index()
}
......@@ -33,7 +33,9 @@ func TestRepoIndexCmd(t *testing.T) {
t.Fatal(err)
}
defer os.RemoveAll(dir)
if err := os.Link("testdata/testcharts/compressedchart-0.1.0.tgz", filepath.Join(dir, "compressedchart-0.1.0.tgz")); err != nil {
comp := filepath.Join(dir, "compressedchart-0.1.0.tgz")
if err := os.Link("testdata/testcharts/compressedchart-0.1.0.tgz", comp); err != nil {
t.Fatal(err)
}
......@@ -41,16 +43,42 @@ func TestRepoIndexCmd(t *testing.T) {
c := newRepoIndexCmd(buf)
if err := c.RunE(c, []string{dir}); err != nil {
t.Errorf("%q", err)
t.Error(err)
}
index, err := repo.LoadIndexFile(filepath.Join(dir, "index.yaml"))
destIndex := filepath.Join(dir, "index.yaml")
index, err := repo.LoadIndexFile(destIndex)
if err != nil {
t.Fatal(err)
}
if len(index.Entries) != 1 {
t.Errorf("expected 1 entry, got %v: %#v", len(index.Entries), index.Entries)
t.Errorf("expected 1 entry, got %d: %#v", len(index.Entries), index.Entries)
}
// Test with `--merge`
// Remove first chart.
if err := os.Remove(comp); err != nil {
t.Fatal(err)
}
// Add another chart.
if err := os.Link("testdata/testcharts/reqtest-0.1.0.tgz", filepath.Join(dir, "reqtest-0.1.0.tgz")); err != nil {
t.Fatal(err)
}
c.ParseFlags([]string{"--merge", destIndex})
if err := c.RunE(c, []string{dir}); err != nil {
t.Error(err)
}
index, err = repo.LoadIndexFile(destIndex)
if err != nil {
t.Fatal(err)
}
if len(index.Entries) != 2 {
t.Errorf("expected 2 entry, got %d: %#v", len(index.Entries), index.Entries)
}
}
......@@ -82,6 +82,67 @@ func TestLoadIndexFile(t *testing.T) {
verifyLocalIndex(t, i)
}
func TestMergeIndex(t *testing.T) {
dirName, err := ioutil.TempDir("", "tmp")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dirName)
ind := NewIndexFile()
ind.Add(&chart.Metadata{
Name: "dreadnought",
Version: "0.1.0",
}, "dreadnought-0.1.0.tgz", "http://example.com", "aaaa")
cr := &ChartRepository{
IndexFile: ind,
RootPath: dirName,
}
if err := cr.saveIndexFile(); err != nil {
t.Fatal(err)
}
ind2 := NewIndexFile()
ind2.Add(&chart.Metadata{
Name: "dreadnought",
Version: "0.2.0",
}, "dreadnought-0.2.0.tgz", "http://example.com", "aaaabbbb")
ind2.Add(&chart.Metadata{
Name: "doughnut",
Version: "0.2.0",
}, "doughnut-0.2.0.tgz", "http://example.com", "ccccbbbb")
cr.IndexFile = ind2
ind3, err := LoadIndexFile(filepath.Join(dirName, "index.yaml"))
if err != nil {
t.Fatal(err)
}
if err := cr.MergeIndex(ind3); err != nil {
t.Fatal(err)
}
ind4, err := LoadIndexFile(filepath.Join(dirName, "index.yaml"))
if err != nil {
t.Fatal(err)
}
if len(ind4.Entries) != 2 {
t.Errorf("Expected 2 entries, got %d", len(ind4.Entries))
vs := ind4.Entries["dreadnaught"]
if len(vs) != 2 {
t.Errorf("Expected 2 versions, got %d", len(vs))
}
v := vs[0]
if v.Version != "0.2.0" {
t.Errorf("Expected %q version to be 0.2.0, got %s", v.Name, v.Version)
}
}
}
func TestDownloadIndexFile(t *testing.T) {
fileBytes, err := ioutil.ReadFile("testdata/local-index.yaml")
if err != nil {
......
......@@ -191,6 +191,35 @@ func (r *ChartRepository) saveIndexFile() error {
// Index generates an index for the chart repository and writes an index.yaml file.
func (r *ChartRepository) Index() error {
err := r.generateIndex()
if err != nil {
return err
}
return r.saveIndexFile()
}
// MergeIndex merges the given index file into this index, and then writes the result.
//
// This provides a parallel function to the Index() method, but with the additional merge step.
func (r *ChartRepository) MergeIndex(f *IndexFile) error {
err := r.generateIndex()
if err != nil {
return err
}
for _, cvs := range f.Entries {
for _, cv := range cvs {
if !r.IndexFile.Has(cv.Name, cv.Version) {
e := r.IndexFile.Entries[cv.Name]
r.IndexFile.Entries[cv.Name] = append(e, cv)
}
}
}
return r.saveIndexFile()
}
func (r *ChartRepository) generateIndex() error {
if r.IndexFile == nil {
r.IndexFile = NewIndexFile()
}
......@@ -212,5 +241,5 @@ func (r *ChartRepository) Index() error {
// TODO: If a chart exists, but has a different Digest, should we error?
}
r.IndexFile.SortEntries()
return r.saveIndexFile()
return nil
}
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