Merge pull request #4561 from adamreese/dev-v3-requirements

ref(*): merge requirement.yaml into Chart.yaml
pull/4566/head
Adam Reese 6 years ago committed by GitHub
commit 0d9a226fd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -136,14 +136,14 @@ func (o *dependencyLisOptions) run(out io.Writer) error {
return err
}
if c.Requirements == nil {
if c.Metadata.Requirements == nil {
fmt.Fprintf(out, "WARNING: no requirements at %s/charts\n", o.chartpath)
return nil
}
o.printRequirements(out, c.Requirements)
o.printRequirements(out, c.Metadata.Requirements)
fmt.Fprintln(out)
o.printMissing(out, c.Requirements)
o.printMissing(out, c.Metadata.Requirements)
return nil
}
@ -222,18 +222,18 @@ func (o *dependencyLisOptions) dependencyStatus(dep *chart.Dependency) string {
}
// printRequirements prints all of the requirements in the yaml file.
func (o *dependencyLisOptions) printRequirements(out io.Writer, reqs *chart.Requirements) {
func (o *dependencyLisOptions) printRequirements(out io.Writer, reqs []*chart.Dependency) {
table := uitable.New()
table.MaxColWidth = 80
table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS")
for _, row := range reqs.Dependencies {
for _, row := range reqs {
table.AddRow(row.Name, row.Version, row.Repository, o.dependencyStatus(row))
}
fmt.Fprintln(out, table)
}
// printMissing prints warnings about charts that are present on disk, but are not in the requirements.
func (o *dependencyLisOptions) printMissing(out io.Writer, reqs *chart.Requirements) {
func (o *dependencyLisOptions) printMissing(out io.Writer, reqs []*chart.Dependency) {
folder := filepath.Join(o.chartpath, "charts/*")
files, err := filepath.Glob(folder)
if err != nil {
@ -256,14 +256,14 @@ func (o *dependencyLisOptions) printMissing(out io.Writer, reqs *chart.Requireme
continue
}
found := false
for _, d := range reqs.Dependencies {
for _, d := range reqs {
if d.Name == c.Name() {
found = true
break
}
}
if !found {
fmt.Fprintf(out, "WARNING: %q is not in requirements.yaml.\n", f)
fmt.Fprintf(out, "WARNING: %q is not in Chart.yaml.\n", f)
}
}

@ -28,18 +28,18 @@ import (
)
const dependencyUpDesc = `
Update the on-disk dependencies to mirror the requirements.yaml file.
Update the on-disk dependencies to mirror Chart.yaml.
This command verifies that the required charts, as expressed in 'requirements.yaml',
This command verifies that the required charts, as expressed in 'Chart.yaml',
are present in 'charts/' and are at an acceptable version. It will pull down
the latest charts that satisfy the dependencies, and clean up old dependencies.
On successful update, this will generate a lock file that can be used to
rebuild the requirements to an exact version.
Dependencies are not required to be represented in 'requirements.yaml'. For that
Dependencies are not required to be represented in 'Chart.yaml'. For that
reason, an update command will not remove charts unless they are (a) present
in the requirements.yaml file, but (b) at the wrong version.
in the Chart.yaml file, but (b) at the wrong version.
`
// dependencyUpdateOptions describes a 'helm dependency update'
@ -63,7 +63,7 @@ func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "update CHART",
Aliases: []string{"up"},
Short: "update charts/ based on the contents of requirements.yaml",
Short: "update charts/ based on the contents of Chart.yaml",
Long: dependencyUpDesc,
Args: require.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {

@ -24,8 +24,6 @@ import (
"strings"
"testing"
"github.com/ghodss/yaml"
"k8s.io/helm/pkg/chart"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/provenance"
@ -49,7 +47,8 @@ func TestDependencyUpdateCmd(t *testing.T) {
t.Logf("Listening on directory %s", srv.Root())
chartname := "depup"
if err := createTestingChart(hh.String(), chartname, srv.URL()); err != nil {
md := createTestingMetadata(chartname, srv.URL())
if _, err := chartutil.Create(md, hh.String()); err != nil {
t.Fatal(err)
}
@ -87,14 +86,12 @@ func TestDependencyUpdateCmd(t *testing.T) {
// Now change the dependencies and update. This verifies that on update,
// old dependencies are cleansed and new dependencies are added.
reqfile := &chart.Requirements{
Dependencies: []*chart.Dependency{
{Name: "reqtest", Version: "0.1.0", Repository: srv.URL()},
{Name: "compressedchart", Version: "0.3.0", Repository: srv.URL()},
},
md.Requirements = []*chart.Dependency{
{Name: "reqtest", Version: "0.1.0", Repository: srv.URL()},
{Name: "compressedchart", Version: "0.3.0", Repository: srv.URL()},
}
dir := hh.Path(chartname)
if err := writeRequirements(dir, reqfile); err != nil {
dir := hh.Path(chartname, "Chart.yaml")
if err := chartutil.SaveChartfile(dir, md); err != nil {
t.Fatal(err)
}
@ -209,33 +206,25 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
}
}
// createTestingChart creates a basic chart that depends on reqtest-0.1.0
// createTestingMetadata creates a basic chart that depends on reqtest-0.1.0
//
// The baseURL can be used to point to a particular repository server.
func createTestingChart(dest, name, baseURL string) error {
cfile := &chart.Metadata{
func createTestingMetadata(name, baseURL string) *chart.Metadata {
return &chart.Metadata{
Name: name,
Version: "1.2.3",
}
dir := filepath.Join(dest, name)
_, err := chartutil.Create(cfile, dest)
if err != nil {
return err
}
req := &chart.Requirements{
Dependencies: []*chart.Dependency{
Requirements: []*chart.Dependency{
{Name: "reqtest", Version: "0.1.0", Repository: baseURL},
{Name: "compressedchart", Version: "0.1.0", Repository: baseURL},
},
}
return writeRequirements(dir, req)
}
func writeRequirements(dir string, req *chart.Requirements) error {
data, err := yaml.Marshal(req)
if err != nil {
return err
}
return ioutil.WriteFile(filepath.Join(dir, "requirements.yaml"), data, 0655)
// createTestingChart creates a basic chart that depends on reqtest-0.1.0
//
// The baseURL can be used to point to a particular repository server.
func createTestingChart(dest, name, baseURL string) error {
cfile := createTestingMetadata(name, baseURL)
_, err := chartutil.Create(cfile, dest)
return err
}

@ -141,12 +141,10 @@ func ensureTestHome(t *testing.T, home helmpath.Home) {
}
}
if r, err := repo.LoadRepositoriesFile(repoFile); err == repo.ErrRepoOutOfDate {
t.Log("Updating repository file format...")
if err := r.WriteFile(repoFile, 0644); err != nil {
t.Fatal(err)
}
}
t.Logf("$HELM_HOME has been configured at %s.\n", home)
}
// testHelmHome sets up a Helm Home in a temp dir.

@ -181,7 +181,7 @@ func (o *installOptions) run(out io.Writer) error {
return err
}
if req := chartRequested.Requirements; req != nil {
if req := chartRequested.Metadata.Requirements; req != nil {
// If checkDependencies returns an error, we have unfulfilled dependencies.
// As of Helm 2.4.0, this is treated as a stopping condition:
// https://github.com/kubernetes/helm/issues/2209
@ -286,11 +286,11 @@ func generateName(nameTemplate string) (string, error) {
return b.String(), err
}
func checkDependencies(ch *chart.Chart, reqs *chart.Requirements) error {
func checkDependencies(ch *chart.Chart, reqs []*chart.Dependency) error {
var missing []string
OUTER:
for _, r := range reqs.Dependencies {
for _, r := range reqs {
for _, d := range ch.Dependencies() {
if d.Name() == r.Name {
continue OUTER

@ -112,9 +112,9 @@ func TestInstall(t *testing.T) {
cmd: "install testdata/testcharts/chart-missing-deps",
wantError: true,
},
// Install, chart with bad requirements.yaml in /charts
// Install, chart with bad dependencies in Chart.yaml in /charts
{
name: "install chart with bad requirements.yaml",
name: "install chart with bad dependencies in Chart.yaml",
cmd: "install testdata/testcharts/chart-bad-requirements",
wantError: true,
},

@ -102,7 +102,7 @@ func newPackageCmd(out io.Writer) *cobra.Command {
f.StringVar(&o.version, "version", "", "set the version on the chart to this semver version")
f.StringVar(&o.appVersion, "app-version", "", "set the appVersion on the chart to this version")
f.StringVarP(&o.destination, "destination", "d", ".", "location to write the chart.")
f.BoolVarP(&o.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "requirements.yaml" to dir "charts/" before packaging`)
f.BoolVarP(&o.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "Chart.yaml" to dir "charts/" before packaging`)
o.valuesOptions.addFlags(f)
return cmd
@ -161,7 +161,7 @@ func (o *packageOptions) run(out io.Writer) error {
return errors.Errorf("directory name (%s) and Chart.yaml name (%s) must match", filepath.Base(path), ch.Name())
}
if reqs := ch.Requirements; reqs != nil {
if reqs := ch.Metadata.Requirements; reqs != nil {
if err := checkDependencies(ch, reqs); err != nil {
return err
}

@ -158,7 +158,7 @@ func (o *templateOptions) run(out io.Writer) error {
return err
}
if req := c.Requirements; req != nil {
if req := c.Metadata.Requirements; req != nil {
if err := checkDependencies(c, req); err != nil {
return err
}

@ -1,4 +1,5 @@
NAME VERSION REPOSITORY STATUS
reqsubchart 0.1.0 https://example.com/charts missing
reqsubchart2 0.2.0 https://example.com/charts missing
reqsubchart3 >=0.1.0 https://example.com/charts missing

@ -1 +1 @@
Error: cannot load requirements.yaml: error converting YAML to JSON: yaml: line 2: did not find expected '-' indicator
Error: cannot load Chart.yaml: error converting YAML to JSON: yaml: line 5: did not find expected '-' indicator

@ -1,3 +1,7 @@
description: A Helm chart for Kubernetes
name: chart-missing-deps
version: 0.1.0
dependencies:
- name: reqsubchart
version: 0.1.0
repository: "https://example.com/charts"

@ -1,3 +1,10 @@
description: A Helm chart for Kubernetes
name: chart-missing-deps
version: 0.1.0
dependencies:
- name: reqsubchart
version: 0.1.0
repository: "https://example.com/charts"
- name: reqsubchart2
version: 0.2.0
repository: "https://example.com/charts"

Binary file not shown.

@ -1,3 +1,13 @@
description: A Helm chart for Kubernetes
name: reqtest
version: 0.1.0
dependencies:
- name: reqsubchart
version: 0.1.0
repository: "https://example.com/charts"
- name: reqsubchart2
version: 0.2.0
repository: "https://example.com/charts"
- name: reqsubchart3
version: ">=0.1.0"
repository: "https://example.com/charts"

@ -154,7 +154,7 @@ func (o *upgradeOptions) run(out io.Writer) error {
if err != nil {
return err
}
if req := ch.Requirements; req != nil {
if req := ch.Metadata.Requirements; req != nil {
if err := checkDependencies(ch, req); err != nil {
return err
}

@ -20,8 +20,6 @@ package chart
type Chart struct {
// Metadata is the contents of the Chartfile.
Metadata *Metadata
// Requirements is the contents of requirements.yaml.
Requirements *Requirements
// RequirementsLock is the contents of requirements.lock.
RequirementsLock *RequirementsLock
// Templates for this chart.

@ -77,11 +77,6 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil {
return c, errors.Wrap(err, "cannot load Chart.yaml")
}
case f.Name == "requirements.yaml":
c.Requirements = new(chart.Requirements)
if err := yaml.Unmarshal(f.Data, c.Requirements); err != nil {
return c, errors.Wrap(err, "cannot load requirements.yaml")
}
case f.Name == "requirements.lock":
c.RequirementsLock = new(chart.RequirementsLock)
if err := yaml.Unmarshal(f.Data, &c.RequirementsLock); err != nil {

@ -176,15 +176,15 @@ func verifyChart(t *testing.T, c *chart.Chart) {
}
func verifyRequirements(t *testing.T, c *chart.Chart) {
if len(c.Requirements.Dependencies) != 2 {
t.Errorf("Expected 2 requirements, got %d", len(c.Requirements.Dependencies))
if len(c.Metadata.Requirements) != 2 {
t.Errorf("Expected 2 requirements, got %d", len(c.Metadata.Requirements))
}
tests := []*chart.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"},
{Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"},
}
for i, tt := range tests {
d := c.Requirements.Dependencies[i]
d := c.Metadata.Requirements[i]
if d.Name != tt.Name {
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
}
@ -198,15 +198,15 @@ func verifyRequirements(t *testing.T, c *chart.Chart) {
}
func verifyRequirementsLock(t *testing.T, c *chart.Chart) {
if len(c.Requirements.Dependencies) != 2 {
t.Errorf("Expected 2 requirements, got %d", len(c.Requirements.Dependencies))
if len(c.Metadata.Requirements) != 2 {
t.Errorf("Expected 2 requirements, got %d", len(c.Metadata.Requirements))
}
tests := []*chart.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"},
{Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"},
}
for i, tt := range tests {
d := c.Requirements.Dependencies[i]
d := c.Metadata.Requirements[i]
if d.Name != tt.Name {
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
}
@ -224,7 +224,6 @@ func verifyFrobnitz(t *testing.T, c *chart.Chart) {
}
func verifyChartFileAndTemplate(t *testing.T, c *chart.Chart, name string) {
if c.Metadata == nil {
t.Fatal("Metadata is nil")
}
@ -246,8 +245,8 @@ func verifyChartFileAndTemplate(t *testing.T, c *chart.Chart, name string) {
if len(c.Dependencies()) != 2 {
t.Fatalf("Expected 2 Dependency, got %d", len(c.Dependencies()))
}
if len(c.Requirements.Dependencies) != 2 {
t.Fatalf("Expected 2 Requirements.Dependency, got %d", len(c.Requirements.Dependencies))
if len(c.Metadata.Requirements) != 2 {
t.Fatalf("Expected 2 Requirements.Dependency, got %d", len(c.Metadata.Requirements))
}
if len(c.RequirementsLock.Dependencies) != 2 {
t.Fatalf("Expected 2 RequirementsLock.Dependency, got %d", len(c.RequirementsLock.Dependencies))

@ -0,0 +1,4 @@
name: albatross
description: A Helm chart for Kubernetes
version: 0.1.0
home: ""

@ -0,0 +1,4 @@
albatross: "true"
global:
author: Coleridge

Binary file not shown.

@ -18,3 +18,10 @@ icon: https://example.com/64x64.png
annotations:
extrakey: extravalue
anotherkey: anothervalue
dependencies:
- name: alpine
version: "0.1.0"
repository: https://example.com/charts
- name: mariner
version: "4.3.2"
repository: https://example.com/charts

@ -1,7 +0,0 @@
dependencies:
- name: alpine
version: "0.1.0"
repository: https://example.com/charts
- name: mariner
version: "4.3.2"
repository: https://example.com/charts

@ -18,3 +18,10 @@ icon: https://example.com/64x64.png
annotations:
extrakey: extravalue
anotherkey: anothervalue
dependencies:
- name: alpine
version: "0.1.0"
repository: https://example.com/charts
- name: mariner
version: "4.3.2"
repository: https://example.com/charts

@ -1,7 +0,0 @@
dependencies:
- name: alpine
version: "0.1.0"
repository: https://example.com/charts
- name: mariner
version: "4.3.2"
repository: https://example.com/charts

@ -0,0 +1,12 @@
#!/bin/sh
# Pack the albatross chart into the mariner chart.
echo "Packing albatross into mariner"
tar -zcvf mariner/charts/albatross-0.1.0.tgz albatross
echo "Packing mariner into frobnitz"
tar -zcvf frobnitz/charts/mariner-4.3.2.tgz mariner
# Pack the frobnitz chart.
echo "Packing frobnitz"
tar --exclude=ignore/* -zcvf frobnitz-1.2.3.tgz frobnitz

@ -0,0 +1,8 @@
name: mariner
description: A Helm chart for Kubernetes
version: 4.3.2
home: ""
dependencies:
- name: albatross
repository: https://example.com/mariner/charts
version: "0.1.0"

@ -0,0 +1,7 @@
# Default values for <CHARTNAME>.
# This is a YAML-formatted file. https://github.com/toml-lang/toml
# Declare name/value pairs to be passed into your templates.
# name: "value"
<CHARTNAME>:
test: true

@ -65,4 +65,6 @@ type Metadata struct {
Annotations map[string]string `json:"annotations,omitempty"`
// KubeVersion is a SemVer constraint specifying the version of Kubernetes required.
KubeVersion string `json:"kubeVersion,omitempty"`
// Requirements are a list of requirements for a chart.
Requirements []*Dependency `json:"dependencies,omitempty"`
}

@ -49,14 +49,6 @@ type Dependency struct {
Alias string `json:"alias,omitempty"`
}
// Requirements is a list of requirements for a chart.
//
// Requirements are charts upon which this chart depends. This expresses
// developer intent.
type Requirements struct {
Dependencies []*Dependency `json:"dependencies"`
}
// RequirementsLock is a lock file for requirements.
//
// It represents the state that the dependencies should be in.

@ -318,7 +318,7 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error {
}
var m map[string]interface{}
if err := yaml.Unmarshal([]byte(transform(string(b), schart.Name())), &m); err != nil {
if err := yaml.Unmarshal(transform(string(b), schart.Name()), &m); err != nil {
return err
}
schart.Values = m

@ -26,11 +26,11 @@ import (
)
// ProcessRequirementsConditions disables charts based on condition path value in values
func ProcessRequirementsConditions(reqs *chart.Requirements, cvals Values) {
func ProcessRequirementsConditions(reqs []*chart.Dependency, cvals Values) {
if reqs == nil {
return
}
for _, r := range reqs.Dependencies {
for _, r := range reqs {
var hasTrue, hasFalse bool
for _, c := range strings.Split(strings.TrimSpace(r.Condition), ",") {
if len(c) > 0 {
@ -67,7 +67,7 @@ func ProcessRequirementsConditions(reqs *chart.Requirements, cvals Values) {
}
// ProcessRequirementsTags disables charts based on tags in values
func ProcessRequirementsTags(reqs *chart.Requirements, cvals Values) {
func ProcessRequirementsTags(reqs []*chart.Dependency, cvals Values) {
if reqs == nil {
return
}
@ -75,7 +75,7 @@ func ProcessRequirementsTags(reqs *chart.Requirements, cvals Values) {
if err != nil {
return
}
for _, r := range reqs.Dependencies {
for _, r := range reqs {
var hasTrue, hasFalse bool
for _, k := range r.Tags {
if b, ok := vt[k]; ok {
@ -127,19 +127,19 @@ func getAliasDependency(charts []*chart.Chart, aliasChart *chart.Dependency) *ch
// ProcessRequirementsEnabled removes disabled charts from dependencies
func ProcessRequirementsEnabled(c *chart.Chart, v map[string]interface{}) error {
if c.Requirements == nil {
if c.Metadata.Requirements == nil {
return nil
}
var chartDependencies []*chart.Chart
// If any dependency is not a part of requirements.yaml
// If any dependency is not a part of Chart.yaml
// then this should be added to chartDependencies.
// However, if the dependency is already specified in requirements.yaml
// we should not add it, as it would be anyways processed from requirements.yaml
// However, if the dependency is already specified in Chart.yaml
// we should not add it, as it would be anyways processed from Chart.yaml
for _, existingDependency := range c.Dependencies() {
var dependencyFound bool
for _, req := range c.Requirements.Dependencies {
for _, req := range c.Metadata.Requirements {
if existingDependency.Metadata.Name == req.Name && version.IsCompatibleRange(req.Version, existingDependency.Metadata.Version) {
dependencyFound = true
break
@ -150,7 +150,7 @@ func ProcessRequirementsEnabled(c *chart.Chart, v map[string]interface{}) error
}
}
for _, req := range c.Requirements.Dependencies {
for _, req := range c.Metadata.Requirements {
if chartDependency := getAliasDependency(c.Dependencies(), req); chartDependency != nil {
chartDependencies = append(chartDependencies, chartDependency)
}
@ -161,7 +161,7 @@ func ProcessRequirementsEnabled(c *chart.Chart, v map[string]interface{}) error
c.SetDependencies(chartDependencies...)
// set all to true
for _, lr := range c.Requirements.Dependencies {
for _, lr := range c.Metadata.Requirements {
lr.Enabled = true
}
b, _ := yaml.Marshal(v)
@ -170,11 +170,11 @@ func ProcessRequirementsEnabled(c *chart.Chart, v map[string]interface{}) error
return err
}
// flag dependencies as enabled/disabled
ProcessRequirementsTags(c.Requirements, cvals)
ProcessRequirementsConditions(c.Requirements, cvals)
ProcessRequirementsTags(c.Metadata.Requirements, cvals)
ProcessRequirementsConditions(c.Metadata.Requirements, cvals)
// make a map of charts to remove
rm := map[string]struct{}{}
for _, r := range c.Requirements.Dependencies {
for _, r := range c.Metadata.Requirements {
if !r.Enabled {
// remove disabled chart
rm[r.Name] = struct{}{}
@ -232,7 +232,7 @@ func pathToMap(path string, data map[string]interface{}) map[string]interface{}
// processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field.
func processImportValues(c *chart.Chart) error {
if c.Requirements == nil {
if c.Metadata.Requirements == nil {
return nil
}
// combine chart values and empty config to get Values
@ -242,7 +242,7 @@ func processImportValues(c *chart.Chart) error {
}
b := make(map[string]interface{})
// import values from each dependency if specified in import-values
for _, r := range c.Requirements.Dependencies {
for _, r := range c.Metadata.Requirements {
var outiv []interface{}
for _, riv := range r.ImportValues {
switch iv := riv.(type) {

@ -256,39 +256,39 @@ func TestGetAliasDependency(t *testing.T) {
t.Fatalf("Failed to load testdata: %s", err)
}
req := c.Requirements
req := c.Metadata.Requirements
if len(req.Dependencies) == 0 {
if len(req) == 0 {
t.Fatalf("There are no requirements to test")
}
// Success case
aliasChart := getAliasDependency(c.Dependencies(), req.Dependencies[0])
aliasChart := getAliasDependency(c.Dependencies(), req[0])
if aliasChart == nil {
t.Fatalf("Failed to get dependency chart for alias %s", req.Dependencies[0].Name)
t.Fatalf("Failed to get dependency chart for alias %s", req[0].Name)
}
if req.Dependencies[0].Alias != "" {
if aliasChart.Name() != req.Dependencies[0].Alias {
t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Alias, aliasChart.Name())
if req[0].Alias != "" {
if aliasChart.Name() != req[0].Alias {
t.Fatalf("Dependency chart name should be %s but got %s", req[0].Alias, aliasChart.Name())
}
} else if aliasChart.Name() != req.Dependencies[0].Name {
t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Name, aliasChart.Name())
} else if aliasChart.Name() != req[0].Name {
t.Fatalf("Dependency chart name should be %s but got %s", req[0].Name, aliasChart.Name())
}
if req.Dependencies[0].Version != "" {
if !version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) {
if req[0].Version != "" {
if !version.IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) {
t.Fatalf("Dependency chart version is not in the compatible range")
}
}
// Failure case
req.Dependencies[0].Name = "something-else"
if aliasChart := getAliasDependency(c.Dependencies(), req.Dependencies[0]); aliasChart != nil {
req[0].Name = "something-else"
if aliasChart := getAliasDependency(c.Dependencies(), req[0]); aliasChart != nil {
t.Fatalf("expected no chart but got %s", aliasChart.Name())
}
req.Dependencies[0].Version = "something else which is not in the compatible range"
if version.IsCompatibleRange(req.Dependencies[0].Version, aliasChart.Metadata.Version) {
req[0].Version = "something else which is not in the compatible range"
if version.IsCompatibleRange(req[0].Version, aliasChart.Metadata.Version) {
t.Fatalf("Dependency chart version which is not in the compatible range should cause a failure other than a success ")
}
}
@ -312,8 +312,8 @@ func TestDependentChartAliases(t *testing.T) {
t.Fatal("Expected alias dependencies to be added, but did not got that")
}
if len(c.Dependencies()) != len(c.Requirements.Dependencies) {
t.Fatalf("Expected number of chart dependencies %d, but got %d", len(c.Requirements.Dependencies), len(c.Dependencies()))
if len(c.Dependencies()) != len(c.Metadata.Requirements) {
t.Fatalf("Expected number of chart dependencies %d, but got %d", len(c.Metadata.Requirements), len(c.Dependencies()))
}
}
@ -375,8 +375,8 @@ func TestDependentChartsWithSubchartsAllSpecifiedInRequirements(t *testing.T) {
t.Fatal("Expected no changes in dependencies to be, but did something got changed")
}
if len(c.Dependencies()) != len(c.Requirements.Dependencies) {
t.Fatalf("Expected number of chart dependencies %d, but got %d", len(c.Requirements.Dependencies), len(c.Dependencies()))
if len(c.Dependencies()) != len(c.Metadata.Requirements) {
t.Fatalf("Expected number of chart dependencies %d, but got %d", len(c.Metadata.Requirements), len(c.Dependencies()))
}
}
@ -399,21 +399,21 @@ func TestDependentChartsWithSomeSubchartsSpecifiedInRequirements(t *testing.T) {
t.Fatal("Expected no changes in dependencies to be, but did something got changed")
}
if len(c.Dependencies()) <= len(c.Requirements.Dependencies) {
t.Fatalf("Expected more dependencies than specified in requirements.yaml(%d), but got %d", len(c.Requirements.Dependencies), len(c.Dependencies()))
if len(c.Dependencies()) <= len(c.Metadata.Requirements) {
t.Fatalf("Expected more dependencies than specified in requirements.yaml(%d), but got %d", len(c.Metadata.Requirements), len(c.Dependencies()))
}
}
func verifyRequirements(t *testing.T, c *chart.Chart) {
if len(c.Requirements.Dependencies) != 2 {
t.Errorf("Expected 2 requirements, got %d", len(c.Requirements.Dependencies))
if len(c.Metadata.Requirements) != 2 {
t.Errorf("Expected 2 requirements, got %d", len(c.Metadata.Requirements))
}
tests := []*chart.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"},
{Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"},
}
for i, tt := range tests {
d := c.Requirements.Dependencies[i]
d := c.Metadata.Requirements[i]
if d.Name != tt.Name {
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
}
@ -427,15 +427,15 @@ func verifyRequirements(t *testing.T, c *chart.Chart) {
}
func verifyRequirementsLock(t *testing.T, c *chart.Chart) {
if len(c.Requirements.Dependencies) != 2 {
t.Errorf("Expected 2 requirements, got %d", len(c.Requirements.Dependencies))
if len(c.Metadata.Requirements) != 2 {
t.Errorf("Expected 2 requirements, got %d", len(c.Metadata.Requirements))
}
tests := []*chart.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "https://example.com/charts"},
{Name: "mariner", Version: "4.3.2", Repository: "https://example.com/charts"},
}
for i, tt := range tests {
d := c.Requirements.Dependencies[i]
d := c.Metadata.Requirements[i]
if d.Name != tt.Name {
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
}

@ -15,3 +15,15 @@ sources:
- https://example.com/foo/bar
home: http://example.com
icon: https://example.com/64x64.png
dependencies:
- name: alpine
version: "0.1.0"
repository: https://example.com/charts
- name: mariner
version: "4.3.2"
repository: https://example.com/charts
alias: mariners2
- name: mariner
version: "4.3.2"
repository: https://example.com/charts
alias: mariners1

@ -15,3 +15,10 @@ sources:
- https://example.com/foo/bar
home: http://example.com
icon: https://example.com/64x64.png
dependencies:
- name: alpine
version: "0.1.0"
repository: https://example.com/charts
- name: mariner
version: "4.3.2"
repository: https://example.com/charts

@ -15,3 +15,7 @@ sources:
- https://example.com/foo/bar
home: http://example.com
icon: https://example.com/64x64.png
dependencies:
- name: alpine
version: "0.1.0"
repository: https://example.com/charts

Binary file not shown.

@ -18,3 +18,10 @@ icon: https://example.com/64x64.png
annotations:
extrakey: extravalue
anotherkey: anothervalue
dependencies:
- name: alpine
version: "0.1.0"
repository: https://example.com/charts
- name: mariner
version: "4.3.2"
repository: https://example.com/charts

@ -18,3 +18,10 @@ icon: https://example.com/64x64.png
annotations:
extrakey: extravalue
anotherkey: anothervalue
dependencies:
- name: alpine
version: "0.1.0"
repository: https://example.com/charts
- name: mariner
version: "4.3.2"
repository: https://example.com/charts

@ -2,3 +2,7 @@ name: mariner
description: A Helm chart for Kubernetes
version: 4.3.2
home: ""
dependencies:
- name: albatross
repository: https://example.com/mariner/charts
version: "0.1.0"

@ -2,3 +2,34 @@ apiVersion: v1
description: A Helm chart for Kubernetes
name: parentchart
version: 0.1.0
dependencies:
- name: subchart1
repository: http://localhost:10191
version: 0.1.0
condition: subchart1.enabled
tags:
- front-end
- subchart1
import-values:
- child: SC1data
parent: imported-chart1
- child: SC1data
parent: overridden-chart1
- child: imported-chartA
parent: imported-chartA
- child: imported-chartA-B
parent: imported-chartA-B
- child: overridden-chartA-B
parent: overridden-chartA-B
- child: SCBexported1A
parent: .
- SCBexported2
- SC1exported1
- name: subchart2
repository: http://localhost:10191
version: 0.1.0
condition: subchart2.enabled
tags:
- back-end
- subchart2

@ -2,3 +2,35 @@ apiVersion: v1
description: A Helm chart for Kubernetes
name: subchart1
version: 0.1.0
dependencies:
- name: subcharta
repository: http://localhost:10191
version: 0.1.0
condition: subcharta.enabled,subchart1.subcharta.enabled
tags:
- front-end
- subcharta
import-values:
- child: SCAdata
parent: imported-chartA
- child: SCAdata
parent: overridden-chartA
- child: SCAdata
parent: imported-chartA-B
- name: subchartb
repository: http://localhost:10191
version: 0.1.0
condition: subchartb.enabled
import-values:
- child: SCBdata
parent: imported-chartB
- child: SCBdata
parent: imported-chartA-B
- child: exports.SCBexported2
parent: exports.SCBexported2
- SCBexported1
tags:
- front-end
- subchartb

@ -2,3 +2,18 @@ apiVersion: v1
description: A Helm chart for Kubernetes
name: subchart2
version: 0.1.0
dependencies:
- name: subchartb
repository: http://localhost:10191
version: 0.1.0
condition: subchartb.enabled,subchart2.subchartb.enabled
tags:
- back-end
- subchartb
- name: subchartc
repository: http://localhost:10191
version: 0.1.0
condition: subchartc.enabled
tags:
- back-end
- subchartc

@ -151,10 +151,7 @@ func CoalesceValues(chrt *chart.Chart, vals []byte) (Values, error) {
}
}
cvals, err = coalesce(chrt, cvals)
if err != nil {
return cvals, err
}
coalesce(chrt, cvals)
return coalesceDeps(chrt, cvals)
}
@ -162,14 +159,10 @@ func CoalesceValues(chrt *chart.Chart, vals []byte) (Values, error) {
// coalesce coalesces the dest values and the chart values, giving priority to the dest values.
//
// This is a helper function for CoalesceValues.
func coalesce(ch *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) {
var err error
dest, err = coalesceValues(ch, dest)
if err != nil {
return dest, err
}
func coalesce(ch *chart.Chart, dest map[string]interface{}) map[string]interface{} {
coalesceValues(ch, dest)
coalesceDeps(ch, dest)
return dest, nil
return dest
}
// coalesceDeps coalesces the dependencies of the given chart.
@ -187,12 +180,8 @@ func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]in
// Get globals out of dest and merge them into dvmap.
coalesceGlobals(dvmap, dest)
var err error
// Now coalesce the rest of the values.
dest[subchart.Name()], err = coalesce(subchart, dvmap)
if err != nil {
return dest, err
}
dest[subchart.Name()] = coalesce(subchart, dvmap)
}
}
return dest, nil
@ -261,7 +250,7 @@ func copyMap(src map[string]interface{}) map[string]interface{} {
// coalesceValues builds up a values map for a particular chart.
//
// Values in v will override the values in the chart.
func coalesceValues(c *chart.Chart, v map[string]interface{}) (map[string]interface{}, error) {
func coalesceValues(c *chart.Chart, v map[string]interface{}) {
for key, val := range c.Values {
if value, ok := v[key]; ok {
if value == nil {
@ -285,7 +274,6 @@ func coalesceValues(c *chart.Chart, v map[string]interface{}) (map[string]interf
v[key] = val
}
}
return v, nil
}
// coalesceTables merges a source map into a destination map.

@ -78,10 +78,9 @@ func (m *Manager) Build() error {
return m.Update()
}
// A lock must accompany a requirements.yaml file.
req := c.Requirements
req := c.Metadata.Requirements
if sum, err := resolver.HashReq(req); err != nil || sum != lock.Digest {
return errors.New("requirements.lock is out of sync with requirements.yaml")
return errors.New("requirements.lock is out of sync with Chart.yaml")
}
// Check that all of the repos we're dependent on actually exist.
@ -106,7 +105,7 @@ func (m *Manager) Build() error {
// Update updates a local charts directory.
//
// It first reads the requirements.yaml file, and then attempts to
// It first reads the Chart.yaml file, and then attempts to
// negotiate versions based on that. It will download the versions
// from remote chart repositories unless SkipUpdate is true.
func (m *Manager) Update() error {
@ -117,12 +116,13 @@ func (m *Manager) Update() error {
// If no requirements file is found, we consider this a successful
// completion.
req := c.Requirements
req := c.Metadata.Requirements
if req == nil {
return nil
}
// Hash requirements.yaml
// FIXME should this hash all of Chart.yaml
hash, err := resolver.HashReq(req)
if err != nil {
return err
@ -130,7 +130,7 @@ func (m *Manager) Update() error {
// Check that all of the repos we're dependent on actually exist and
// the repo index names.
repoNames, err := m.getRepoNames(req.Dependencies)
repoNames, err := m.getRepoNames(req)
if err != nil {
return err
}
@ -143,7 +143,7 @@ func (m *Manager) Update() error {
}
// Now we need to find out which version of a chart best satisfies the
// requirements the requirements.yaml
// requirements in the Chart.yaml
lock, err := m.resolve(req, repoNames, hash)
if err != nil {
return err
@ -176,7 +176,7 @@ func (m *Manager) loadChartDir() (*chart.Chart, error) {
// resolve takes a list of requirements and translates them into an exact version to download.
//
// This returns a lock file, which has all of the requirements normalized to a specific version.
func (m *Manager) resolve(req *chart.Requirements, repoNames map[string]string, hash string) (*chart.RequirementsLock, error) {
func (m *Manager) resolve(req []*chart.Dependency, repoNames map[string]string, hash string) (*chart.RequirementsLock, error) {
res := resolver.New(m.ChartPath, m.HelmHome)
return res.Resolve(req, repoNames, hash)
}

@ -47,12 +47,12 @@ func New(chartpath string, helmhome helmpath.Home) *Resolver {
}
// Resolve resolves dependencies and returns a lock file with the resolution.
func (r *Resolver) Resolve(reqs *chart.Requirements, repoNames map[string]string, d string) (*chart.RequirementsLock, error) {
func (r *Resolver) Resolve(reqs []*chart.Dependency, repoNames map[string]string, d string) (*chart.RequirementsLock, error) {
// Now we clone the dependencies, locking as we go.
locked := make([]*chart.Dependency, len(reqs.Dependencies))
locked := make([]*chart.Dependency, len(reqs))
missing := []string{}
for i, d := range reqs.Dependencies {
for i, d := range reqs {
if strings.HasPrefix(d.Repository, "file://") {
if _, err := GetLocalPath(d.Repository, r.chartpath); err != nil {
@ -105,7 +105,7 @@ func (r *Resolver) Resolve(reqs *chart.Requirements, repoNames map[string]string
}
}
if len(missing) > 0 {
return nil, errors.Errorf("can't get a valid version for repositories %s. Try changing the version constraint in requirements.yaml", strings.Join(missing, ", "))
return nil, errors.Errorf("can't get a valid version for repositories %s. Try changing the version constraint in Chart.yaml", strings.Join(missing, ", "))
}
return &chart.RequirementsLock{
Generated: time.Now(),
@ -118,7 +118,7 @@ func (r *Resolver) Resolve(reqs *chart.Requirements, repoNames map[string]string
//
// This should be used only to compare against another hash generated by this
// function.
func HashReq(req *chart.Requirements) (string, error) {
func HashReq(req []*chart.Dependency) (string, error) {
data, err := json.Marshal(req)
if err != nil {
return "", err

@ -24,52 +24,42 @@ import (
func TestResolve(t *testing.T) {
tests := []struct {
name string
req *chart.Requirements
req []*chart.Dependency
expect *chart.RequirementsLock
err bool
}{
{
name: "version failure",
req: &chart.Requirements{
Dependencies: []*chart.Dependency{
{Name: "oedipus-rex", Repository: "http://example.com", Version: ">a1"},
},
req: []*chart.Dependency{
{Name: "oedipus-rex", Repository: "http://example.com", Version: ">a1"},
},
err: true,
},
{
name: "cache index failure",
req: &chart.Requirements{
Dependencies: []*chart.Dependency{
{Name: "oedipus-rex", Repository: "http://example.com", Version: "1.0.0"},
},
req: []*chart.Dependency{
{Name: "oedipus-rex", Repository: "http://example.com", Version: "1.0.0"},
},
err: true,
},
{
name: "chart not found failure",
req: &chart.Requirements{
Dependencies: []*chart.Dependency{
{Name: "redis", Repository: "http://example.com", Version: "1.0.0"},
},
req: []*chart.Dependency{
{Name: "redis", Repository: "http://example.com", Version: "1.0.0"},
},
err: true,
},
{
name: "constraint not satisfied failure",
req: &chart.Requirements{
Dependencies: []*chart.Dependency{
{Name: "alpine", Repository: "http://example.com", Version: ">=1.0.0"},
},
req: []*chart.Dependency{
{Name: "alpine", Repository: "http://example.com", Version: ">=1.0.0"},
},
err: true,
},
{
name: "valid lock",
req: &chart.Requirements{
Dependencies: []*chart.Dependency{
{Name: "alpine", Repository: "http://example.com", Version: ">=0.1.0"},
},
req: []*chart.Dependency{
{Name: "alpine", Repository: "http://example.com", Version: ">=0.1.0"},
},
expect: &chart.RequirementsLock{
Dependencies: []*chart.Dependency{
@ -79,10 +69,8 @@ func TestResolve(t *testing.T) {
},
{
name: "repo from valid local path",
req: &chart.Requirements{
Dependencies: []*chart.Dependency{
{Name: "signtest", Repository: "file://../../../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"},
},
req: []*chart.Dependency{
{Name: "signtest", Repository: "file://../../../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"},
},
expect: &chart.RequirementsLock{
Dependencies: []*chart.Dependency{
@ -92,10 +80,8 @@ func TestResolve(t *testing.T) {
},
{
name: "repo from invalid local path",
req: &chart.Requirements{
Dependencies: []*chart.Dependency{
{Name: "notexist", Repository: "file://../testdata/notexist", Version: "0.1.0"},
},
req: []*chart.Dependency{
{Name: "notexist", Repository: "file://../testdata/notexist", Version: "0.1.0"},
},
err: true,
},
@ -128,7 +114,7 @@ func TestResolve(t *testing.T) {
}
// Check fields.
if len(l.Dependencies) != len(tt.req.Dependencies) {
if len(l.Dependencies) != len(tt.req) {
t.Errorf("%s: wrong number of dependencies in lock", tt.name)
}
d0 := l.Dependencies[0]
@ -146,11 +132,9 @@ func TestResolve(t *testing.T) {
}
func TestHashReq(t *testing.T) {
expect := "sha256:e70e41f8922e19558a8bf62f591a8b70c8e4622e3c03e5415f09aba881f13885"
req := &chart.Requirements{
Dependencies: []*chart.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "http://localhost:8879/charts"},
},
expect := "sha256:d661820b01ed7bcf26eed8f01cf16380e0a76326ba33058d3150f919d9b15bc0"
req := []*chart.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "http://localhost:8879/charts"},
}
h, err := HashReq(req)
if err != nil {
@ -160,7 +144,7 @@ func TestHashReq(t *testing.T) {
t.Errorf("Expected %q, got %q", expect, h)
}
req = &chart.Requirements{Dependencies: []*chart.Dependency{}}
req = []*chart.Dependency{}
h, err = HashReq(req)
if err != nil {
t.Fatal(err)

@ -65,8 +65,9 @@ func TestEqual(t *testing.T) {
func TestExtractHostname(t *testing.T) {
tests := map[string]string{
"http://example.com": "example.com",
"https://example.com/foo": "example.com",
"http://example.com": "example.com",
"https://example.com/foo": "example.com",
"https://example.com:31337/not/with/a/bang/but/a/whimper": "example.com",
}
for start, expect := range tests {

Loading…
Cancel
Save