Skip to content

Commit

Permalink
Provide additional information in version check return. Return map of…
Browse files Browse the repository at this point in the history
… requirements rather than list
  • Loading branch information
SimKev2 committed May 2, 2024
1 parent d0cc4c1 commit c7b4727
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 47 deletions.
8 changes: 5 additions & 3 deletions cmd/tk/toolCharts.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"gopkg.in/yaml.v2"
)

const repoConfigFlagUsage = "specify a local helm repository config file to use instead of the repositories in the chartfile.yaml. For use with private repositories"

func chartsCmd() *cli.Command {
cmd := &cli.Command{
Use: "charts",
Expand All @@ -37,7 +39,7 @@ 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")
repoConfigPath := cmd.Flags().String("repository-config", "", repoConfigFlagUsage)

cmd.Run = func(cmd *cli.Command, args []string) error {
c, err := loadChartfile()
Expand All @@ -56,7 +58,7 @@ 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")
repoConfigPath := cmd.Flags().String("repository-config", "", repoConfigFlagUsage)

cmd.Run = func(cmd *cli.Command, args []string) error {
c, err := loadChartfile()
Expand Down Expand Up @@ -150,7 +152,7 @@ func chartsVersionCheckCmd() *cli.Command {
Use: "version-check",
Short: "Check required charts for updated versions",
}
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")
repoConfigPath := cmd.Flags().String("repository-config", "", repoConfigFlagUsage)

cmd.Run = func(cmd *cli.Command, args []string) error {
c, err := loadChartfile()
Expand Down
35 changes: 18 additions & 17 deletions pkg/helm/charts.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,11 @@ func (c *Charts) AddRepos(repos ...Repo) error {
return nil
}

// VersionCheck checks each of the charts in the requires section and returns if there are any
// newer versions of the chart available on the repository.
func (c *Charts) VersionCheck(repoConfigPath string) (ChartSearchVersions, error) {
chartVersions := ChartSearchVersions{}
// VersionCheck checks each of the charts in the requires section and returns information regarding
// related to version upgrades. This includes if the current version is latest as well as the
// latest matching versions of the major and minor version the chart is currently on.
func (c *Charts) VersionCheck(repoConfigPath string) (map[string]RequiresVersionInfo, error) {
requiresVersionInfo := make(map[string]RequiresVersionInfo)

if repoConfigPath != "" {
repoConfig, err := LoadHelmRepoConfig(repoConfigPath)
Expand All @@ -320,23 +321,23 @@ func (c *Charts) VersionCheck(repoConfigPath string) (ChartSearchVersions, error
if err != nil {
return nil, err
}
usingLatestVersion := false
if r.Version == searchVersions[0].Version {
usingLatestVersion = true
}

// Since we can store multiple versions of the same chart in the chartfile we dedup here.
for _, sv := range searchVersions {
exists := false
for _, cv := range chartVersions {
if sv == cv {
exists = true
break
}
}
if !exists {
chartVersions = append(chartVersions, sv)
}
requiresVersionInfo[fmt.Sprintf("%s@%s", r.Chart, r.Version)] = RequiresVersionInfo{
Name: r.Chart,
Directory: r.Directory,
CurrentVersion: r.Version,
UsingLatestVersion: usingLatestVersion,
LatestVersion: searchVersions[0],
LatestMatchingMajorVersion: searchVersions[1],
LatestMatchingMinorVersion: searchVersions[2],
}
}

return chartVersions, nil
return requiresVersionInfo, nil
}

func InitChartfile(path string) (*Charts, error) {
Expand Down
57 changes: 50 additions & 7 deletions pkg/helm/charts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,10 +294,39 @@ func TestChartsVersionCheck(t *testing.T) {
chartVersions, err := c.VersionCheck("")
assert.NoError(t, err)

// stable/prometheus is deprecated so only the 11.12.1 should ever be returned
assert.Equal(t, 1, len(chartVersions))
assert.Equal(t, "stable/prometheus", chartVersions[0].Name)
assert.Equal(t, "11.12.1", chartVersions[0].Version)
// stable/prometheus is deprecated so only the 11.12.1 should ever be returned as latest
latestPrometheusChartVersion := ChartSearchVersion{
Name: "stable/prometheus",
Version: "11.12.1",
AppVersion: "2.20.1",
Description: "DEPRECATED Prometheus is a monitoring system and time series database.",
}
stableExpected := RequiresVersionInfo{
Name: "stable/prometheus",
Directory: "",
CurrentVersion: "11.12.0",
UsingLatestVersion: false,
LatestVersion: latestPrometheusChartVersion,
LatestMatchingMajorVersion: latestPrometheusChartVersion,
LatestMatchingMinorVersion: latestPrometheusChartVersion,
}
oldExpected := RequiresVersionInfo{
Name: "stable/prometheus",
Directory: "old",
CurrentVersion: "11.11.0",
UsingLatestVersion: false,
LatestVersion: latestPrometheusChartVersion,
LatestMatchingMajorVersion: latestPrometheusChartVersion,
LatestMatchingMinorVersion: ChartSearchVersion{
Name: "stable/prometheus",
Version: "11.11.1",
AppVersion: "2.19.0",
Description: "Prometheus is a monitoring system and time series database.",
},
}
assert.Equal(t, 2, len(chartVersions))
assert.Equal(t, stableExpected, chartVersions["stable/[email protected]"])
assert.Equal(t, oldExpected, chartVersions["stable/[email protected]"])
}

func TestVersionCheckWithConfig(t *testing.T) {
Expand Down Expand Up @@ -329,8 +358,22 @@ repositories:
chartVersions, err := c.VersionCheck(filepath.Join(tempDir, "helmConfig.yaml"))
assert.NoError(t, err)

// stable/prometheus is deprecated so only the 11.12.1 should ever be returned
// stable/prometheus is deprecated so only the 11.12.1 should ever be returned as latest
latestPrometheusChartVersion := ChartSearchVersion{
Name: "private/prometheus",
Version: "11.12.1",
AppVersion: "2.20.1",
Description: "DEPRECATED Prometheus is a monitoring system and time series database.",
}
expected := RequiresVersionInfo{
Name: "private/prometheus",
Directory: "",
CurrentVersion: "11.12.0",
UsingLatestVersion: false,
LatestVersion: latestPrometheusChartVersion,
LatestMatchingMajorVersion: latestPrometheusChartVersion,
LatestMatchingMinorVersion: latestPrometheusChartVersion,
}
assert.Equal(t, 1, len(chartVersions))
assert.Equal(t, "private/prometheus", chartVersions[0].Name)
assert.Equal(t, "11.12.1", chartVersions[0].Version)
assert.Equal(t, expected, chartVersions["private/[email protected]"])
}
88 changes: 68 additions & 20 deletions pkg/helm/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,31 @@ type ChartSearchVersion struct {

type ChartSearchVersions []ChartSearchVersion

// RequiresVersionInfo represents a specific required chart and the information around the current
// version and any upgrade information.
type RequiresVersionInfo struct {
// Name of the required chart in the form of repo/chartName
Name string `json:"name,omitempty"`

// Directory information for the chart.
Directory string `json:"directory,omitempty"`

// The current version information of the required helm chart.
CurrentVersion string `json:"current_version,omitempty"`

// Boolean representing if the required chart is already up to date.
UsingLatestVersion bool `json:"using_latest_version"`

// The most up-to-date version information of the required helm chart.
LatestVersion ChartSearchVersion `json:"latest_version,omitempty"`

// The latest version information of the required helm chart that matches the current major version.
LatestMatchingMajorVersion ChartSearchVersion `json:"latest_matching_major_version,omitempty"`

// The latest version information of the required helm chart that matches the current minor version.
LatestMatchingMinorVersion ChartSearchVersion `json:"latest_matching_minor_version,omitempty"`
}

// Opts are additional, non-required options that all Helm operations accept
type Opts struct {
Repositories []Repo
Expand Down Expand Up @@ -148,35 +173,58 @@ func (e ExecHelm) ChartExists(chart string, opts *JsonnetOpts) (string, error) {
return chart, nil
}

// Searches the helm repositories for the latest, the latest matching major, and the latest
// matching minor versions for the given chart.
func (e ExecHelm) SearchRepo(chart, currVersion string, opts Opts) (ChartSearchVersions, error) {
searchVersions := []string{
fmt.Sprintf(">=%s", currVersion), // Latest version X.X.X
fmt.Sprintf("^%s", currVersion), // Latest matching major version 1.X.X
fmt.Sprintf("~%s", currVersion), // Latest matching minor version 1.1.X
}

repoFile, err := writeRepoTmpFile(opts.Repositories)
if err != nil {
return nil, err
}
defer os.Remove(repoFile)

// Vertical tabs are used as deliminators in table so \v is used to match exactly the chart.
// By default the helm search repo command only returns the latest version for the chart.
cmd := e.cmd("search", "repo",
"--repository-config", repoFile,
"--regexp",
fmt.Sprintf("\v%s\v", chart),
"--version", fmt.Sprintf(">%s", currVersion),
"-o", "json",
)
var errBuf bytes.Buffer
var outBuf bytes.Buffer
cmd.Stderr = &errBuf
cmd.Stdout = &outBuf
var chartVersions ChartSearchVersions
for _, versionRegex := range searchVersions {
var chartVersion ChartSearchVersions

// Vertical tabs are used as deliminators in table so \v is used to match exactly the chart.
// Helm search by default only returns the latest version matching the given version regex.
cmd := e.cmd("search", "repo",
"--repository-config", repoFile,
"--regexp", fmt.Sprintf("\v%s\v", chart),
"--version", versionRegex,
"-o", "json",
)
var errBuf bytes.Buffer
var outBuf bytes.Buffer
cmd.Stderr = &errBuf
cmd.Stdout = &outBuf

if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("%s\n%s", errBuf.String(), err)
}

if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("%s\n%s", errBuf.String(), err)
}
err = json.Unmarshal(outBuf.Bytes(), &chartVersion)
if err != nil {
return nil, err
}

var chartVersions ChartSearchVersions
err = json.Unmarshal(outBuf.Bytes(), &chartVersions)
if err != nil {
return nil, err
if len(chartVersion) != 1 {
log.Debug().Msgf("helm search repo for %s did not return 1 version : %+v", chart, chartVersion)
chartVersions = append(chartVersions, ChartSearchVersion{
Name: chart,
Version: currVersion,
AppVersion: "",
Description: "search did not return 1 version",
})
} else {
chartVersions = append(chartVersions, chartVersion...)
}
}

return chartVersions, nil
Expand Down

0 comments on commit c7b4727

Please sign in to comment.