From 25c7e6dc217628e2235256d6c908d2575d71d2c5 Mon Sep 17 00:00:00 2001 From: Matt Butcher <mbutcher@engineyard.com> Date: Thu, 13 Oct 2016 16:01:51 -0600 Subject: [PATCH] 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 --- cmd/helm/repo_index.go | 32 ++++++++++++++++--- cmd/helm/repo_index_test.go | 36 +++++++++++++++++++--- pkg/repo/index_test.go | 61 +++++++++++++++++++++++++++++++++++++ pkg/repo/repo.go | 31 ++++++++++++++++++- 4 files changed, 150 insertions(+), 10 deletions(-) diff --git a/cmd/helm/repo_index.go b/cmd/helm/repo_index.go index 636d90090..f88127c91 100644 --- a/cmd/helm/repo_index.go +++ b/cmd/helm/repo_index.go @@ -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() } diff --git a/cmd/helm/repo_index_test.go b/cmd/helm/repo_index_test.go index d207a8b5e..ee00d546d 100644 --- a/cmd/helm/repo_index_test.go +++ b/cmd/helm/repo_index_test.go @@ -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) + } } diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index 080023f24..6f096103b 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -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 { diff --git a/pkg/repo/repo.go b/pkg/repo/repo.go index baa66a7d9..e1d9d4712 100644 --- a/pkg/repo/repo.go +++ b/pkg/repo/repo.go @@ -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 } -- GitLab