diff --git a/cmd/helm/repo_index.go b/cmd/helm/repo_index.go index 636d900900470a4efe9aff62e201fcc7a865655a..f88127c918ae8512758c944a674451d244e1f900 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 d207a8b5ee114f34a0cecec2323d21cab948a285..ee00d546dab689054219cc7810887091a339011b 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 080023f24914faedb9637beb8f950ea118c34e03..6f096103b953d49b7c8410bc74befae20055a385 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 baa66a7d9946801f6c87367772a145ab529169da..e1d9d4712457b0f5e969a6d3539da987a67bb9b6 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 }