Skip to content

Add repository config option for private repositories #1017

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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