Skip to content

Commit 3b49f8a

Browse files
authored
Adds a new helmcache implementation backed by an index.yaml (#2263)
* Adds a new helmcache implementation backed by an index.yml - For use on on the CLI reading local helm cache - To avoid a dependency on sqlite - Also adds a NilCache * Corrects ChartsCacherWriter -> ChartsCacheWriter everywhere * Remove tmpDir generation for sqlite, don't need that anymore
1 parent 47b96de commit 3b49f8a

File tree

11 files changed

+345
-72
lines changed

11 files changed

+345
-72
lines changed

cmd/gitops/app/create/templates/cmd.go

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
clitemplates "github.com/weaveworks/weave-gitops-enterprise/cmd/gitops/pkg/templates"
2424
"github.com/weaveworks/weave-gitops-enterprise/pkg/estimation"
2525
"github.com/weaveworks/weave-gitops-enterprise/pkg/helm"
26-
"github.com/weaveworks/weave-gitops-enterprise/pkg/helm/multiwatcher/controller"
2726
"github.com/weaveworks/weave-gitops/core/logger"
2827
"helm.sh/helm/v3/pkg/cli"
2928
"helm.sh/helm/v3/pkg/helmpath"
@@ -241,29 +240,14 @@ func export(template string, out io.Writer) error {
241240
}
242241

243242
func generateFilesLocally(tmpl *gapiv1.GitOpsTemplate, params map[string]string, helmRepoName string, profiles []*capiv1_proto.ProfileValues, settings *cli.EnvSettings, log logr.Logger) ([]gitprovider.CommitFile, error) {
244-
// create tmp dir for charts cache
245-
tmpDir, err := os.MkdirTemp("", "gitops")
246-
if err != nil {
247-
return nil, fmt.Errorf("failed to create tmp dir: %w", err)
248-
}
249-
defer func() {
250-
if err := os.RemoveAll(tmpDir); err != nil {
251-
log.Error(err, "failed to remove temporary chart cache")
252-
}
253-
}()
254-
255-
chartsCache, err := helm.NewChartIndexer(tmpDir, DefaultCluster)
256-
if err != nil {
257-
return nil, fmt.Errorf("could not create charts cache in %s: %w", tmpDir, err)
258-
}
259-
260243
templateHasRequiredProfiles, err := templates.TemplateHasRequiredProfiles(tmpl)
261244
if err != nil {
262245
return nil, fmt.Errorf("failed to check if template has required profiles: %w", err)
263246
}
264247

265248
var helmRepo *sourcev1.HelmRepository
266249
var helmRepoRef types.NamespacedName
250+
var chartsCache helm.ChartsCacheReader = helm.NilCache{}
267251
if len(profiles) > 0 || templateHasRequiredProfiles {
268252
entry, index, err := localHelmRepo(helmRepoName, settings)
269253
if err != nil {
@@ -276,7 +260,7 @@ func generateFilesLocally(tmpl *gapiv1.GitOpsTemplate, params map[string]string,
276260
}
277261
helmRepo = fluxHelmRepo(entry)
278262
helmRepoRef = types.NamespacedName{Name: helmRepo.Name, Namespace: helmRepo.Namespace}
279-
controller.LoadIndex(index, chartsCache, types.NamespacedName{Name: DefaultCluster}, helmRepo, log)
263+
chartsCache = helm.NewHelmIndexFileReader(index)
280264
}
281265

282266
templateResources, err := server.GetFiles(

cmd/gitops/app/create/templates/cmd_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"encoding/base64"
55
"errors"
66
"io"
7-
"io/ioutil"
87
"os"
98
"path/filepath"
109
"strings"
@@ -244,7 +243,7 @@ func TestRunWithProfiles(t *testing.T) {
244243
}
245244

246245
// check that the profiles.yaml file contains the expected values
247-
profilesFile, err := ioutil.ReadFile(filepath.Join(tmpDir, "test-namespace/test-resource/profiles.yaml"))
246+
profilesFile, err := os.ReadFile(filepath.Join(tmpDir, "test-namespace/test-resource/profiles.yaml"))
248247
assert.NoError(t, err)
249248
assert.Contains(t, string(profilesFile), "version: '>0.1'")
250249
assert.Contains(t, string(profilesFile), "cert-manager")

pkg/helm/charts_cache.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package helm
2+
3+
import (
4+
"context"
5+
6+
_ "github.com/mattn/go-sqlite3"
7+
"k8s.io/apimachinery/pkg/types"
8+
)
9+
10+
// LayerAnnotation specifies profile application order.
11+
// Profiles are sorted by layer and those at a higher "layer" are only installed after
12+
// lower layers have successfully installed and started.
13+
const LayerAnnotation = "weave.works/layer"
14+
15+
// ChartsCacheWriter is the "writing" interface to the cache, used by the reconciler etc
16+
type ChartsCacheWriter interface {
17+
AddChart(ctx context.Context, name, version, kind, layer string, clusterRef types.NamespacedName, repoRef ObjectReference) error
18+
Delete(ctx context.Context, repoRef ObjectReference, clusterRef types.NamespacedName) error
19+
DeleteAllChartsForCluster(ctx context.Context, clusterRef types.NamespacedName) error
20+
}
21+
22+
// ChartsCacheReader is the "reading" interface to the cache, used by api etc
23+
type ChartsCacheReader interface {
24+
ListChartsByRepositoryAndCluster(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, kind string) ([]Chart, error)
25+
IsKnownChart(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart) (bool, error)
26+
GetChartValues(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart) ([]byte, error)
27+
UpdateValuesYaml(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart, valuesYaml []byte) error
28+
GetLatestVersion(ctx context.Context, clusterRef, repoRef types.NamespacedName, name string) (string, error)
29+
GetLayer(ctx context.Context, clusterRef, repoRef types.NamespacedName, name, version string) (string, error)
30+
}
31+
32+
type ChartsCache interface {
33+
ChartsCacheReader
34+
ChartsCacheWriter
35+
}
36+
37+
// ObjectReference points to a resource.
38+
type ObjectReference struct {
39+
Kind string
40+
APIVersion string
41+
Name string
42+
Namespace string
43+
}
44+
45+
// Chart holds the name and version of a chart.
46+
type Chart struct {
47+
Name string
48+
Version string
49+
Kind string
50+
Layer string
51+
}
52+
53+
// Implementation of ChartsCache that does nothing.
54+
type NilCache struct{}
55+
56+
func (n NilCache) AddChart(ctx context.Context, name, version, kind, layer string, clusterRef types.NamespacedName, repoRef ObjectReference) error {
57+
return nil
58+
}
59+
60+
func (n NilCache) Delete(ctx context.Context, repoRef ObjectReference, clusterRef types.NamespacedName) error {
61+
return nil
62+
}
63+
64+
func (n NilCache) DeleteAllChartsForCluster(ctx context.Context, clusterRef types.NamespacedName) error {
65+
return nil
66+
}
67+
68+
func (n NilCache) ListChartsByRepositoryAndCluster(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, kind string) ([]Chart, error) {
69+
return nil, nil
70+
}
71+
72+
func (n NilCache) IsKnownChart(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart) (bool, error) {
73+
return false, nil
74+
}
75+
76+
func (n NilCache) GetChartValues(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart) ([]byte, error) {
77+
return nil, nil
78+
}
79+
80+
func (n NilCache) UpdateValuesYaml(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart, valuesYaml []byte) error {
81+
return nil
82+
}
83+
84+
func (n NilCache) GetLatestVersion(ctx context.Context, clusterRef, repoRef types.NamespacedName, name string) (string, error) {
85+
return "", nil
86+
}
87+
88+
func (n NilCache) GetLayer(ctx context.Context, clusterRef, repoRef types.NamespacedName, name, version string) (string, error) {
89+
return "", nil
90+
}

pkg/helm/helm_chart_indexer.go

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,42 +16,6 @@ import (
1616
// dbFile is the name of the sqlite3 database file
1717
const dbFile = "charts.db"
1818

19-
type ChartsCacherWriter interface {
20-
AddChart(ctx context.Context, name, version, kind, layer string, clusterRef types.NamespacedName, repoRef ObjectReference) error
21-
Delete(ctx context.Context, repoRef ObjectReference, clusterRef types.NamespacedName) error
22-
DeleteAllChartsForCluster(ctx context.Context, clusterRef types.NamespacedName) error
23-
}
24-
25-
type ChartsCacheReader interface {
26-
ListChartsByRepositoryAndCluster(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, kind string) ([]Chart, error)
27-
IsKnownChart(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart) (bool, error)
28-
GetChartValues(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart) ([]byte, error)
29-
UpdateValuesYaml(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart, valuesYaml []byte) error
30-
GetLatestVersion(ctx context.Context, clusterRef, repoRef types.NamespacedName, name string) (string, error)
31-
GetLayer(ctx context.Context, clusterRef, repoRef types.NamespacedName, name, version string) (string, error)
32-
}
33-
34-
type ChartsCache interface {
35-
ChartsCacheReader
36-
ChartsCacherWriter
37-
}
38-
39-
// ObjectReference points to a resource.
40-
type ObjectReference struct {
41-
Kind string
42-
APIVersion string
43-
Name string
44-
Namespace string
45-
}
46-
47-
// Chart holds the name and version of a chart.
48-
type Chart struct {
49-
Name string
50-
Version string
51-
Kind string
52-
Layer string
53-
}
54-
5519
// HelmChartIndexer indexs details of Helm charts that have been seen in Helm
5620
// repositories.
5721
type HelmChartIndexer struct {

pkg/helm/helm_index_file_reader.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package helm
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"sort"
8+
9+
"helm.sh/helm/v3/pkg/repo"
10+
"k8s.io/apimachinery/pkg/types"
11+
)
12+
13+
type HelmIndexFileReader struct {
14+
index *repo.IndexFile
15+
}
16+
17+
// Returns a new HelmIndexFileReader a ChartsCacheReader implementation
18+
// that reads from a Helm IndexFile. As we're only reading from a single
19+
// index file, we ignore the repoRef and clusterRef parameters provided
20+
// to most methods.
21+
func NewHelmIndexFileReader(index *repo.IndexFile) *HelmIndexFileReader {
22+
return &HelmIndexFileReader{
23+
index: index,
24+
}
25+
}
26+
27+
// ListChartsByRepositoryAndCluster returns a list of charts from the
28+
// index file. The repoRef and clusterRef parameters are ignored.
29+
func (c HelmIndexFileReader) ListChartsByRepositoryAndCluster(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, kind string) ([]Chart, error) {
30+
var charts []Chart
31+
32+
for _, chart := range c.index.Entries {
33+
for _, version := range chart {
34+
charts = append(charts, Chart{
35+
Name: version.Name,
36+
Version: version.Version,
37+
Layer: version.Annotations[LayerAnnotation],
38+
})
39+
}
40+
}
41+
42+
sort.Slice(charts, func(i, j int) bool {
43+
return charts[i].Name < charts[j].Name
44+
})
45+
46+
return charts, nil
47+
}
48+
49+
// IsKnownChart returns true if the chart is in the index file. The repoRef
50+
// and clusterRef parameters are ignored.
51+
func (c HelmIndexFileReader) IsKnownChart(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart) (bool, error) {
52+
for _, version := range c.index.Entries[chart.Name] {
53+
if version.Version == chart.Version {
54+
return true, nil
55+
}
56+
}
57+
58+
return false, nil
59+
}
60+
61+
// GetLatestVersion returns the latest version of the chart. The repoRef
62+
// and clusterRef parameters are ignored.
63+
func (c HelmIndexFileReader) GetLatestVersion(ctx context.Context, clusterRef, repoRef types.NamespacedName, name string) (string, error) {
64+
versions := []string{}
65+
for _, v := range c.index.Entries[name] {
66+
versions = append(versions, v.Version)
67+
}
68+
69+
sorted, err := ReverseSemVerSort(versions)
70+
if err != nil {
71+
return "", fmt.Errorf("retrieving latest version %s: %w", name, err)
72+
}
73+
74+
return sorted[0], nil
75+
}
76+
77+
// GetLayer returns the layer of the chart. The repoRef
78+
// and clusterRef parameters are ignored.
79+
func (c HelmIndexFileReader) GetLayer(ctx context.Context, clusterRef, repoRef types.NamespacedName, name, version string) (string, error) {
80+
versions := c.index.Entries[name]
81+
82+
for _, v := range versions {
83+
if v.Version == version {
84+
return v.Annotations[LayerAnnotation], nil
85+
}
86+
}
87+
88+
return "", nil
89+
}
90+
91+
// not implmented, this does not support reading values.yaml
92+
func (c HelmIndexFileReader) GetChartValues(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart) ([]byte, error) {
93+
return nil, errors.New("not implemented")
94+
}
95+
96+
// not implmented, this does not support reading values.yaml
97+
func (c HelmIndexFileReader) UpdateValuesYaml(ctx context.Context, clusterRef types.NamespacedName, repoRef ObjectReference, chart Chart, valuesYaml []byte) error {
98+
return errors.New("not implemented")
99+
}

0 commit comments

Comments
 (0)