Skip to content

Commit

Permalink
Add repository config option for private repositories (#1017)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimKev2 authored Apr 19, 2024
1 parent f73b937 commit e2ac5a4
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 20 deletions.
6 changes: 4 additions & 2 deletions cmd/tk/toolCharts.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@ func chartsVendorCmd() *cli.Command {
Short: "Download Charts to a local folder",
}
prune := cmd.Flags().Bool("prune", false, "also remove non-vendored files from the destination directory")
repoConfigPath := cmd.Flags().String("repository-config", "", "specify a local helm repository config file to use instead of the repositories in the chartfile.yaml. For use with private repositories")

cmd.Run = func(cmd *cli.Command, args []string) error {
c, err := loadChartfile()
if err != nil {
return err
}

return c.Vendor(*prune)
return c.Vendor(*prune, *repoConfigPath)
}

return cmd
Expand All @@ -53,14 +54,15 @@ func chartsAddCmd() *cli.Command {
Use: "add [chart@version] [...]",
Short: "Adds Charts to the chartfile",
}
repoConfigPath := cmd.Flags().String("repository-config", "", "specify a local helm repository config file to use instead of the repositories in the chartfile.yaml. For use with private repositories")

cmd.Run = func(cmd *cli.Command, args []string) error {
c, err := loadChartfile()
if err != nil {
return err
}

return c.Add(args)
return c.Add(args, *repoConfigPath)
}

return cmd
Expand Down
37 changes: 34 additions & 3 deletions pkg/helm/charts.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,29 @@ func LoadChartfile(projectRoot string) (*Charts, error) {
return charts, nil
}

// LoadHelmRepoConfig reads in a helm config file
func LoadHelmRepoConfig(repoConfigPath string) (*ConfigFile, error) {
// make sure path is valid
repoPath, err := filepath.Abs(repoConfigPath)
if err != nil {
return nil, err
}

// open repo config file
data, err := os.ReadFile(repoPath)
if err != nil {
return nil, err
}

// parse the file non-strictly to account for any minor config changes
rc := &ConfigFile{}
if err := yaml.Unmarshal(data, rc); err != nil {
return nil, err
}

return rc, nil
}

// Charts exposes the central Chartfile management functions
type Charts struct {
// Manifest are the chartfile.yaml contents. It holds data about the developers intentions
Expand Down Expand Up @@ -89,12 +112,20 @@ func (c Charts) ManifestFile() string {

// Vendor pulls all Charts specified in the manifest into the local charts
// directory. It fetches the repository index before doing so.
func (c Charts) Vendor(prune bool) error {
func (c Charts) Vendor(prune bool, repoConfigPath string) error {
dir := c.ChartDir()
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return err
}

if repoConfigPath != "" {
repoConfig, err := LoadHelmRepoConfig(repoConfigPath)
if err != nil {
return err
}
c.Manifest.Repositories = repoConfig.Repositories
}

// Check that there are no output conflicts before vendoring
if err := c.Manifest.Requires.Validate(); err != nil {
return err
Expand Down Expand Up @@ -199,7 +230,7 @@ func (c Charts) Vendor(prune bool) error {

// Add adds every Chart in reqs to the Manifest after validation, and runs
// Vendor afterwards
func (c *Charts) Add(reqs []string) error {
func (c *Charts) Add(reqs []string, repoConfigPath string) error {
log.Info().Msgf("Adding %v Charts ...", len(reqs))

// parse new charts, append in memory
Expand Down Expand Up @@ -238,7 +269,7 @@ func (c *Charts) Add(reqs []string) error {

// worked fine? vendor it
log.Info().Msgf("Added %v Charts to helmfile.yaml. Vendoring ...", added)
return c.Vendor(false)
return c.Vendor(false, repoConfigPath)
}

func (c *Charts) AddRepos(repos ...Repo) error {
Expand Down
87 changes: 72 additions & 15 deletions pkg/helm/charts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,28 +88,47 @@ func TestAdd(t *testing.T) {
c, err := InitChartfile(filepath.Join(tempDir, Filename))
require.NoError(t, err)

err = c.Add([]string{"stable/[email protected]"})
err = c.Add([]string{"stable/[email protected]"}, "")
assert.NoError(t, err)

// Adding again the same chart
err = c.Add([]string{"stable/[email protected]"})
err = c.Add([]string{"stable/[email protected]"}, "")
assert.EqualError(t, err, "1 Chart(s) were skipped. Please check above logs for details")

// Adding a chart with a different version to the same path, causes a conflict
err = c.Add([]string{"stable/[email protected]"})
err = c.Add([]string{"stable/[email protected]"}, "")
assert.EqualError(t, err, `Validation errors:
- output directory "prometheus" is used twice, by charts "stable/[email protected]" and "stable/[email protected]"`)

// Add a chart with a specific extract directory
err = c.Add([]string{"stable/[email protected]:prometheus-11.12.0"})
err = c.Add([]string{"stable/[email protected]:prometheus-11.12.0"}, "")
assert.NoError(t, err)

// Add a chart while specifying a helm repo config file
require.NoError(t, os.WriteFile(filepath.Join(tempDir, "helmConfig.yaml"), []byte(`
apiVersion: ""
generated: "0001-01-01T00:00:00Z"
repositories:
- caFile: ""
certFile: ""
insecure_skip_tls_verify: false
keyFile: ""
name: private
pass_credentials_all: false
password: ""
url: https://charts.helm.sh/stable
username: ""
`), 0644))
err = c.Add([]string{"private/[email protected]:private-11.12.1"}, filepath.Join(tempDir, "helmConfig.yaml"))
assert.NoError(t, err)

// Check file contents
listResult, err := os.ReadDir(filepath.Join(tempDir, "charts"))
assert.NoError(t, err)
assert.Equal(t, 2, len(listResult))
assert.Equal(t, "prometheus", listResult[0].Name())
assert.Equal(t, "prometheus-11.12.0", listResult[1].Name())
assert.Equal(t, 3, len(listResult))
assert.Equal(t, "private-11.12.1", listResult[0].Name())
assert.Equal(t, "prometheus", listResult[1].Name())
assert.Equal(t, "prometheus-11.12.0", listResult[2].Name())

chartContent, err := os.ReadFile(filepath.Join(tempDir, "charts", "prometheus", "Chart.yaml"))
assert.NoError(t, err)
Expand All @@ -118,6 +137,10 @@ func TestAdd(t *testing.T) {
chartContent, err = os.ReadFile(filepath.Join(tempDir, "charts", "prometheus-11.12.0", "Chart.yaml"))
assert.NoError(t, err)
assert.Contains(t, string(chartContent), `version: 11.12.0`)

chartContent, err = os.ReadFile(filepath.Join(tempDir, "charts", "private-11.12.1", "Chart.yaml"))
assert.NoError(t, err)
assert.Contains(t, string(chartContent), `version: 11.12.1`)
}

func TestAddOCI(t *testing.T) {
Expand All @@ -128,7 +151,7 @@ func TestAddOCI(t *testing.T) {
err = c.AddRepos(Repo{Name: "karpenter", URL: "oci://public.ecr.aws/karpenter"})
assert.NoError(t, err)

err = c.Add([]string{"karpenter/[email protected]"})
err = c.Add([]string{"karpenter/[email protected]"}, "")
assert.NoError(t, err)

// Check file contents
Expand All @@ -143,7 +166,7 @@ func TestRevendorDeletedFiles(t *testing.T) {
c, err := InitChartfile(filepath.Join(tempDir, Filename))
require.NoError(t, err)

err = c.Add([]string{"stable/[email protected]"})
err = c.Add([]string{"stable/[email protected]"}, "")
assert.NoError(t, err)

// Check file contents
Expand All @@ -153,7 +176,7 @@ func TestRevendorDeletedFiles(t *testing.T) {

// Delete the whole dir and revendor
require.NoError(t, os.RemoveAll(filepath.Join(tempDir, "charts", "prometheus")))
assert.NoError(t, c.Vendor(true))
assert.NoError(t, c.Vendor(true, ""))

// Check file contents
chartContent, err = os.ReadFile(filepath.Join(tempDir, "charts", "prometheus", "Chart.yaml"))
Expand All @@ -162,7 +185,7 @@ func TestRevendorDeletedFiles(t *testing.T) {

// Delete just the Chart.yaml and revendor
require.NoError(t, os.Remove(filepath.Join(tempDir, "charts", "prometheus", "Chart.yaml")))
assert.NoError(t, c.Vendor(true))
assert.NoError(t, c.Vendor(true, ""))

// Check file contents
chartContent, err = os.ReadFile(filepath.Join(tempDir, "charts", "prometheus", "Chart.yaml"))
Expand All @@ -178,17 +201,17 @@ func TestPrune(t *testing.T) {
require.NoError(t, err)

// Add a chart
require.NoError(t, c.Add([]string{"stable/[email protected]"}))
require.NoError(t, c.Add([]string{"stable/[email protected]"}, ""))

// Add a chart with a directory
require.NoError(t, c.Add([]string{"stable/[email protected]:custom-dir"}))
require.NoError(t, c.Add([]string{"stable/[email protected]:custom-dir"}, ""))

// Add unrelated files and folders
require.NoError(t, os.WriteFile(filepath.Join(tempDir, "charts", "foo.txt"), []byte("foo"), 0644))
require.NoError(t, os.Mkdir(filepath.Join(tempDir, "charts", "foo"), 0755))
require.NoError(t, os.WriteFile(filepath.Join(tempDir, "charts", "foo", "Chart.yaml"), []byte("foo"), 0644))

require.NoError(t, c.Vendor(prune))
require.NoError(t, c.Vendor(prune, ""))

// Check if files are pruned
listResult, err := os.ReadDir(filepath.Join(tempDir, "charts"))
Expand Down Expand Up @@ -217,7 +240,41 @@ func TestInvalidChartName(t *testing.T) {
Version: "1.0.0",
})

err = c.Vendor(false)
err = c.Vendor(false, "")
assert.EqualError(t, err, `Validation errors:
- Chart name "noslash" is not valid. Expecting a repo/name format.`)
}

func TestConfigFileOption(t *testing.T) {
tempDir := t.TempDir()
c, err := InitChartfile(filepath.Join(tempDir, Filename))
require.NoError(t, err)

// Don't want to commit credentials so we just verify the "private" repo reference will make
// use of this helm config since the InitChartfile does not have a reference to it.
require.NoError(t, os.WriteFile(filepath.Join(tempDir, "helmConfig.yaml"), []byte(`
apiVersion: ""
generated: "0001-01-01T00:00:00Z"
repositories:
- caFile: ""
certFile: ""
insecure_skip_tls_verify: false
keyFile: ""
name: private
pass_credentials_all: false
password: ""
url: https://charts.helm.sh/stable
username: ""
`), 0644))
c.Manifest.Requires = append(c.Manifest.Requires, Requirement{
Chart: "private/prometheus",
Version: "11.12.1",
})

err = c.Vendor(false, filepath.Join(tempDir, "helmConfig.yaml"))
assert.NoError(t, err)

chartContent, err := os.ReadFile(filepath.Join(tempDir, "charts", "prometheus", "Chart.yaml"))
assert.NoError(t, err)
assert.Contains(t, string(chartContent), `version: 11.12.1`)
}
13 changes: 13 additions & 0 deletions pkg/helm/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ type Chartfile struct {
Directory string `json:"directory,omitempty"`
}

// ConfigFile represents the default Helm config structure to be used in place of the chartfile
// Repositories if supplied.
type ConfigFile struct {
// Version of the Helm repo config schema
APIVersion string `json:"apiVersion"`

// The datetime of when this repo config was generated
Generated string `json:"generated"`

// Repositories to source from
Repositories Repos `json:"repositories"`
}

// Repo describes a single Helm repository
type Repo struct {
Name string `json:"name,omitempty"`
Expand Down

0 comments on commit e2ac5a4

Please sign in to comment.