Skip to content

Commit

Permalink
refactor: add Chart type
Browse files Browse the repository at this point in the history
Signed-off-by: Jacob LeGrone <[email protected]>
  • Loading branch information
jlegrone committed Feb 15, 2019
1 parent c258d19 commit a6dafab
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 70 deletions.
130 changes: 84 additions & 46 deletions pkg/chart/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
"github.com/pkg/errors"
)

const maxNameLength = 63

// Git is the Interface that wraps Git operations.
//
// FileExistsOnBranch checks whether file exists on the specified remote/branch.
Expand Down Expand Up @@ -131,6 +133,58 @@ type AccountValidator interface {
Validate(repoDomain string, account string) error
}

// Chart represents a Helm chart, and can be initalized with the NewChart method.
type Chart struct {
path string
yaml *util.ChartYaml
ciValuesPaths []string
}

func (c *Chart) Yaml() *util.ChartYaml {
return c.yaml
}

func (c *Chart) Path() string {
return c.path
}

func (c *Chart) String() string {
return c.Path()
}

// ValuesFilePathsForCI returns all file paths in the 'ci' subfolder of the chart directory matching the pattern '*-values.yaml'
func (c *Chart) ValuesFilePathsForCI() []string {
return c.ciValuesPaths
}

// CreateInstallParams generates a randomized release name and namespace based on the chart path
// and optional buildID. If a buildID is specified, it will be part of the generated namespace.
func (c *Chart) CreateInstallParams(buildID string) (release string, namespace string) {
release = path.Base(c.Path())
if release == "." || release == "/" {
yaml := c.Yaml()
release = yaml.Name
}
namespace = release
if buildID != "" {
namespace = fmt.Sprintf("%s-%s", namespace, buildID)
}
randomSuffix := util.RandomString(10)
release = util.TruncateLeft(fmt.Sprintf("%s-%s", release, randomSuffix), maxNameLength)
namespace = util.TruncateLeft(fmt.Sprintf("%s-%s", namespace, randomSuffix), maxNameLength)
return
}

func NewChart(chartPath string) (*Chart, error) {
yaml, err := util.ReadChartYaml(chartPath)
if err != nil {
return nil, err
}
ciDir := path.Join(chartPath, "ci/*-values.yaml")
matches, _ := filepath.Glob(ciDir)
return &Chart{chartPath, yaml, matches}, nil
}

type Testing struct {
config config.Configuration
helm Helm
Expand All @@ -150,7 +204,7 @@ type TestResults struct {

// TestResult holds test results for a specific chart
type TestResult struct {
Chart string
Chart *Chart
Error error
}

Expand All @@ -171,7 +225,7 @@ func NewTesting(config config.Configuration) Testing {
return testing
}

func (t *Testing) processCharts(action func(chart string, valuesFiles []string) TestResult) ([]TestResult, error) {
func (t *Testing) processCharts(action func(chart *Chart) TestResult) ([]TestResult, error) {
var results []TestResult
charts, err := t.FindChartsToBeProcessed()
if err != nil {
Expand Down Expand Up @@ -219,14 +273,17 @@ func (t *Testing) processCharts(action func(chart string, valuesFiles []string)
TestResults: results,
}

for _, chart := range charts {
valuesFiles := t.FindValuesFilesForCI(chart)
for _, dir := range charts {
chart, err := NewChart(dir)
if err != nil {
return nil, err
}

if err := t.helm.BuildDependencies(chart); err != nil {
if err := t.helm.BuildDependencies(chart.Path()); err != nil {
return nil, errors.Wrapf(err, "Error building dependencies for chart '%s'", chart)
}

result := action(chart, valuesFiles)
result := action(chart)
if result.Error != nil {
testResults.OverallSuccess = false
}
Expand Down Expand Up @@ -273,7 +330,7 @@ func (t *Testing) PrintResults(results []TestResult) {
}

// LintChart lints the specified chart.
func (t *Testing) LintChart(chart string, valuesFiles []string) TestResult {
func (t *Testing) LintChart(chart *Chart) TestResult {
fmt.Printf("Linting chart '%s'\n", chart)

result := TestResult{Chart: chart}
Expand All @@ -285,8 +342,9 @@ func (t *Testing) LintChart(chart string, valuesFiles []string) TestResult {
}
}

chartYaml := path.Join(chart, "Chart.yaml")
valuesYaml := path.Join(chart, "values.yaml")
chartYaml := path.Join(chart.Path(), "Chart.yaml")
valuesYaml := path.Join(chart.Path(), "values.yaml")
valuesFiles := chart.ValuesFilePathsForCI()

if t.config.ValidateChartSchema {
if err := t.linter.Yamale(chartYaml, t.config.ChartYamlSchema); err != nil {
Expand Down Expand Up @@ -321,7 +379,7 @@ func (t *Testing) LintChart(chart string, valuesFiles []string) TestResult {
if valuesFile != "" {
fmt.Printf("\nLinting chart with values file '%s'...\n\n", valuesFile)
}
if err := t.helm.LintWithValues(chart, valuesFile); err != nil {
if err := t.helm.LintWithValues(chart.Path(), valuesFile); err != nil {
result.Error = err
break
}
Expand All @@ -332,10 +390,11 @@ func (t *Testing) LintChart(chart string, valuesFiles []string) TestResult {

// InstallChart installs the specified chart into a new namespace, waits for resources to become ready, and eventually
// uninstalls it and deletes the namespace again.
func (t *Testing) InstallChart(chart string, valuesFiles []string) TestResult {
func (t *Testing) InstallChart(chart *Chart) TestResult {
fmt.Printf("Installing chart '%s'...\n", chart)

result := TestResult{Chart: chart}
valuesFiles := chart.ValuesFilePathsForCI()

// Test with defaults if no values files are specified.
if len(valuesFiles) == 0 {
Expand All @@ -353,17 +412,17 @@ func (t *Testing) InstallChart(chart string, valuesFiles []string) TestResult {
fun := func() error {
if t.config.Namespace != "" {
namespace = t.config.Namespace
release, _ = util.CreateInstallParams(chart, t.config.BuildId)
release, _ = chart.CreateInstallParams(t.config.BuildId)
releaseSelector = fmt.Sprintf("%s=%s", t.config.ReleaseLabel, release)
} else {
release, namespace = util.CreateInstallParams(chart, t.config.BuildId)
release, namespace = chart.CreateInstallParams(t.config.BuildId)
defer t.kubectl.DeleteNamespace(namespace)
}

defer t.helm.DeleteRelease(release)
defer t.PrintPodDetailsAndLogs(namespace, releaseSelector)

if err := t.helm.InstallWithValues(chart, valuesFile, namespace, release); err != nil {
if err := t.helm.InstallWithValues(chart.Path(), valuesFile, namespace, release); err != nil {
result.Error = err
return err
}
Expand All @@ -388,12 +447,12 @@ func (t *Testing) InstallChart(chart string, valuesFiles []string) TestResult {
}

// LintAndInstallChart first lints and then installs the specified chart.
func (t *Testing) LintAndInstallChart(chart string, valuesFiles []string) TestResult {
result := t.LintChart(chart, valuesFiles)
func (t *Testing) LintAndInstallChart(chart *Chart) TestResult {
result := t.LintChart(chart)
if result.Error != nil {
return result
}
return t.InstallChart(chart, valuesFiles)
return t.InstallChart(chart)
}

// FindChartsToBeProcessed identifies charts to be processed depending on the configuration
Expand All @@ -408,13 +467,6 @@ func (t *Testing) FindChartsToBeProcessed() ([]string, error) {
return t.ComputeChangedChartDirectories()
}

// FindValuesFilesForCI returns all files in the 'ci' subfolder of the chart directory matching the pattern '*-values.yaml'
func (t *Testing) FindValuesFilesForCI(chart string) []string {
ciDir := path.Join(chart, "ci/*-values.yaml")
matches, _ := filepath.Glob(ciDir)
return matches
}

// ComputeChangedChartDirectories takes the merge base of HEAD and the configured remote and target branch and computes a
// slice of changed charts from that in the configured chart directories excluding those configured to be excluded.
func (t *Testing) ComputeChangedChartDirectories() ([]string, error) {
Expand Down Expand Up @@ -473,10 +525,10 @@ func (t *Testing) ReadAllChartDirectories() ([]string, error) {
}

// CheckVersionIncrement checks that the new chart version is greater than the old one using semantic version comparison.
func (t *Testing) CheckVersionIncrement(chart string) error {
func (t *Testing) CheckVersionIncrement(chart *Chart) error {
fmt.Printf("Checking chart '%s' for a version bump...\n", chart)

oldVersion, err := t.GetOldChartVersion(chart)
oldVersion, err := t.GetOldChartVersion(chart.Path())
if err != nil {
return err
}
Expand All @@ -487,10 +539,8 @@ func (t *Testing) CheckVersionIncrement(chart string) error {

fmt.Println("Old chart version:", oldVersion)

newVersion, err := t.GetNewChartVersion(chart)
if err != nil {
return err
}
chartYaml := chart.Yaml()
newVersion := chartYaml.Version
fmt.Println("New chart version:", newVersion)

result, err := util.CompareVersions(oldVersion, newVersion)
Expand All @@ -507,10 +557,10 @@ func (t *Testing) CheckVersionIncrement(chart string) error {
}

// GetOldChartVersion gets the version of the old Chart.yaml file from the target branch.
func (t *Testing) GetOldChartVersion(chart string) (string, error) {
func (t *Testing) GetOldChartVersion(chartPath string) (string, error) {
cfg := t.config

chartYamlFile := path.Join(chart, "Chart.yaml")
chartYamlFile := path.Join(chartPath, "Chart.yaml")
if !t.git.FileExistsOnBranch(chartYamlFile, cfg.Remote, cfg.TargetBranch) {
fmt.Printf("Unable to find chart on %s. New chart detected.\n", cfg.TargetBranch)
return "", nil
Expand All @@ -529,24 +579,12 @@ func (t *Testing) GetOldChartVersion(chart string) (string, error) {
return chartYaml.Version, nil
}

// GetNewChartVersion gets the new version from the currently checked out Chart.yaml file.
func (t *Testing) GetNewChartVersion(chart string) (string, error) {
chartYaml, err := util.ReadChartYaml(chart)
if err != nil {
return "", errors.Wrap(err, "Error reading new chart version")
}
return chartYaml.Version, nil
}

// ValidateMaintainers validates maintainers in the Chart.yaml file. Maintainer names must be valid accounts
// (GitHub, Bitbucket, GitLab) names. Deprecated charts must not have maintainers.
func (t *Testing) ValidateMaintainers(chart string) error {
func (t *Testing) ValidateMaintainers(chart *Chart) error {
fmt.Println("Validating maintainers...")

chartYaml, err := util.ReadChartYaml(chart)
if err != nil {
return err
}
chartYaml := chart.Yaml()

if chartYaml.Deprecated {
if len(chartYaml.Maintainers) > 0 {
Expand Down
18 changes: 13 additions & 5 deletions pkg/chart/chart_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,10 @@ func TestValidateMaintainers(t *testing.T) {

for _, testData := range testDataSlice {
t.Run(testData.name, func(t *testing.T) {
err := ct.ValidateMaintainers(testData.chartDir)
assert.Equal(t, testData.expected, err == nil)
chart, err := NewChart(testData.chartDir)
assert.Nil(t, err)
validationErr := ct.ValidateMaintainers(chart)
assert.Equal(t, testData.expected, validationErr == nil)
})
}
}
Expand Down Expand Up @@ -178,7 +180,9 @@ func TestLintChartMaintainerValidation(t *testing.T) {

for _, testData := range testCases {
t.Run(testData.name, func(t *testing.T) {
result := ct.LintChart(testData.chartDir, []string{})
chart, err := NewChart(testData.chartDir)
assert.Nil(t, err)
result := ct.LintChart(chart)
assert.Equal(t, testData.expected, result.Error == nil)
})
}
Expand Down Expand Up @@ -219,7 +223,9 @@ func TestLintChartSchemaValidation(t *testing.T) {

for _, testData := range testCases {
t.Run(testData.name, func(t *testing.T) {
result := ct.LintChart(testData.chartDir, []string{})
chart, err := NewChart(testData.chartDir)
assert.Nil(t, err)
result := ct.LintChart(chart)
assert.Equal(t, testData.expected, result.Error == nil)
fakeMockLinter.AssertNumberOfCalls(t, "Yamale", callsYamale)
fakeMockLinter.AssertNumberOfCalls(t, "YamlLint", callsYamlLint)
Expand Down Expand Up @@ -263,7 +269,9 @@ func TestLintYamlValidation(t *testing.T) {

for _, testData := range testCases {
t.Run(testData.name, func(t *testing.T) {
result := ct.LintChart(testData.chartDir, []string{})
chart, err := NewChart(testData.chartDir)
assert.Nil(t, err)
result := ct.LintChart(chart)
assert.Equal(t, testData.expected, result.Error == nil)
fakeMockLinter.AssertNumberOfCalls(t, "Yamale", callsYamale)
fakeMockLinter.AssertNumberOfCalls(t, "YamlLint", callsYamlLint)
Expand Down
19 changes: 0 additions & 19 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import (
)

const chars = "1234567890abcdefghijklmnopqrstuvwxyz"
const maxNameLength = 63

type Maintainer struct {
Name string `yaml:"name"`
Expand Down Expand Up @@ -180,24 +179,6 @@ func CompareVersions(left string, right string) (int, error) {
return leftVersion.Compare(rightVersion), nil
}

// CreateInstallParams generates a randomized release name and namespace based on the chart path
// and optional buildID. If a buildID is specified, it will be part of the generated namespace.
func CreateInstallParams(chart string, buildID string) (release string, namespace string) {
release = path.Base(chart)
if release == "." || release == "/" {
yaml, _ := ReadChartYaml(chart)
release = yaml.Name
}
namespace = release
if buildID != "" {
namespace = fmt.Sprintf("%s-%s", namespace, buildID)
}
randomSuffix := RandomString(10)
release = TruncateLeft(fmt.Sprintf("%s-%s", release, randomSuffix), maxNameLength)
namespace = TruncateLeft(fmt.Sprintf("%s-%s", namespace, randomSuffix), maxNameLength)
return
}

func PrintDelimiterLine(delimiterChar string) {
delim := make([]string, 120)
for i := 0; i < 120; i++ {
Expand Down

0 comments on commit a6dafab

Please sign in to comment.