mirror of
https://github.com/helm/helm.git
synced 2026-05-28 04:35:48 -04:00
ref(*): refactor chart/chartutil
ref(chartutil): move chart loading out of chartutil into new package
add chart loader interface to allow lazy loading
feat(chart): create chart accessors
ref(*): cleanup requirements
ref(tiller): remove optional template engines
ref(tiller): simplify sorting releases and hooks
ref(*): code simplification
ref(hapi): move chart package out of hapi
ref(chart): add requirements and lock to Chart struct
This commit is contained in:
parent
56c4b9b48d
commit
f012940d9c
130 changed files with 1765 additions and 1577 deletions
|
|
@ -24,8 +24,8 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
)
|
||||
|
||||
const createDesc = `
|
||||
|
|
@ -80,7 +80,7 @@ func (o *createOptions) run(out io.Writer) error {
|
|||
Description: "A Helm chart for Kubernetes",
|
||||
Version: "0.1.0",
|
||||
AppVersion: "1.0",
|
||||
APIVersion: chartutil.APIVersionv1,
|
||||
APIVersion: chart.APIVersionv1,
|
||||
}
|
||||
|
||||
if o.starter != "" {
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
)
|
||||
|
||||
func TestCreateCmd(t *testing.T) {
|
||||
|
|
@ -46,15 +47,15 @@ func TestCreateCmd(t *testing.T) {
|
|||
t.Fatalf("chart is not directory")
|
||||
}
|
||||
|
||||
c, err := chartutil.LoadDir(cname)
|
||||
c, err := loader.LoadDir(cname)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c.Metadata.Name != cname {
|
||||
t.Errorf("Expected %q name, got %q", cname, c.Metadata.Name)
|
||||
if c.Name() != cname {
|
||||
t.Errorf("Expected %q name, got %q", cname, c.Name())
|
||||
}
|
||||
if c.Metadata.APIVersion != chartutil.APIVersionv1 {
|
||||
if c.Metadata.APIVersion != chart.APIVersionv1 {
|
||||
t.Errorf("Wrong API version: %q", c.Metadata.APIVersion)
|
||||
}
|
||||
}
|
||||
|
|
@ -97,15 +98,15 @@ func TestCreateStarterCmd(t *testing.T) {
|
|||
t.Fatalf("chart is not directory")
|
||||
}
|
||||
|
||||
c, err := chartutil.LoadDir(cname)
|
||||
c, err := loader.LoadDir(cname)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c.Metadata.Name != cname {
|
||||
t.Errorf("Expected %q name, got %q", cname, c.Metadata.Name)
|
||||
if c.Name() != cname {
|
||||
t.Errorf("Expected %q name, got %q", cname, c.Name())
|
||||
}
|
||||
if c.Metadata.APIVersion != chartutil.APIVersionv1 {
|
||||
if c.Metadata.APIVersion != chart.APIVersionv1 {
|
||||
t.Errorf("Wrong API version: %q", c.Metadata.APIVersion)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
)
|
||||
|
||||
const dependencyDesc = `
|
||||
|
|
@ -130,27 +131,23 @@ func newDependencyListCmd(out io.Writer) *cobra.Command {
|
|||
}
|
||||
|
||||
func (o *dependencyLisOptions) run(out io.Writer) error {
|
||||
c, err := chartutil.Load(o.chartpath)
|
||||
c, err := loader.Load(o.chartpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := chartutil.LoadRequirements(c)
|
||||
if err != nil {
|
||||
if err == chartutil.ErrRequirementsNotFound {
|
||||
fmt.Fprintf(out, "WARNING: no requirements at %s/charts\n", o.chartpath)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
if c.Requirements == nil {
|
||||
fmt.Fprintf(out, "WARNING: no requirements at %s/charts\n", o.chartpath)
|
||||
return nil
|
||||
}
|
||||
|
||||
o.printRequirements(out, r)
|
||||
o.printRequirements(out, c.Requirements)
|
||||
fmt.Fprintln(out)
|
||||
o.printMissing(out, r)
|
||||
o.printMissing(out, c.Requirements)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *dependencyLisOptions) dependencyStatus(dep *chartutil.Dependency) string {
|
||||
func (o *dependencyLisOptions) dependencyStatus(dep *chart.Dependency) string {
|
||||
filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*")
|
||||
archives, err := filepath.Glob(filepath.Join(o.chartpath, "charts", filename))
|
||||
if err != nil {
|
||||
|
|
@ -160,11 +157,11 @@ func (o *dependencyLisOptions) dependencyStatus(dep *chartutil.Dependency) strin
|
|||
} else if len(archives) == 1 {
|
||||
archive := archives[0]
|
||||
if _, err := os.Stat(archive); err == nil {
|
||||
c, err := chartutil.Load(archive)
|
||||
c, err := loader.Load(archive)
|
||||
if err != nil {
|
||||
return "corrupt"
|
||||
}
|
||||
if c.Metadata.Name != dep.Name {
|
||||
if c.Name() != dep.Name {
|
||||
return "misnamed"
|
||||
}
|
||||
|
||||
|
|
@ -195,12 +192,12 @@ func (o *dependencyLisOptions) dependencyStatus(dep *chartutil.Dependency) strin
|
|||
return "mispackaged"
|
||||
}
|
||||
|
||||
c, err := chartutil.Load(folder)
|
||||
c, err := loader.Load(folder)
|
||||
if err != nil {
|
||||
return "corrupt"
|
||||
}
|
||||
|
||||
if c.Metadata.Name != dep.Name {
|
||||
if c.Name() != dep.Name {
|
||||
return "misnamed"
|
||||
}
|
||||
|
||||
|
|
@ -225,7 +222,7 @@ func (o *dependencyLisOptions) dependencyStatus(dep *chartutil.Dependency) strin
|
|||
}
|
||||
|
||||
// printRequirements prints all of the requirements in the yaml file.
|
||||
func (o *dependencyLisOptions) printRequirements(out io.Writer, reqs *chartutil.Requirements) {
|
||||
func (o *dependencyLisOptions) printRequirements(out io.Writer, reqs *chart.Requirements) {
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = 80
|
||||
table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS")
|
||||
|
|
@ -236,7 +233,7 @@ func (o *dependencyLisOptions) printRequirements(out io.Writer, reqs *chartutil.
|
|||
}
|
||||
|
||||
// printMissing prints warnings about charts that are present on disk, but are not in the requirements.
|
||||
func (o *dependencyLisOptions) printMissing(out io.Writer, reqs *chartutil.Requirements) {
|
||||
func (o *dependencyLisOptions) printMissing(out io.Writer, reqs *chart.Requirements) {
|
||||
folder := filepath.Join(o.chartpath, "charts/*")
|
||||
files, err := filepath.Glob(folder)
|
||||
if err != nil {
|
||||
|
|
@ -253,14 +250,14 @@ func (o *dependencyLisOptions) printMissing(out io.Writer, reqs *chartutil.Requi
|
|||
if !fi.IsDir() && filepath.Ext(f) != ".tgz" {
|
||||
continue
|
||||
}
|
||||
c, err := chartutil.Load(f)
|
||||
c, err := loader.Load(f)
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "WARNING: %q is not a chart.\n", f)
|
||||
continue
|
||||
}
|
||||
found := false
|
||||
for _, d := range reqs.Dependencies {
|
||||
if d.Name == c.Metadata.Name {
|
||||
if d.Name == c.Name() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,8 @@ import (
|
|||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/provenance"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/repo/repotest"
|
||||
|
|
@ -88,8 +87,8 @@ 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 := &chartutil.Requirements{
|
||||
Dependencies: []*chartutil.Dependency{
|
||||
reqfile := &chart.Requirements{
|
||||
Dependencies: []*chart.Dependency{
|
||||
{Name: "reqtest", Version: "0.1.0", Repository: srv.URL()},
|
||||
{Name: "compressedchart", Version: "0.3.0", Repository: srv.URL()},
|
||||
},
|
||||
|
|
@ -170,7 +169,7 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
|
|||
|
||||
out := bytes.NewBuffer(nil)
|
||||
o := &dependencyUpdateOptions{}
|
||||
o.helmhome = helmpath.Home(hh)
|
||||
o.helmhome = hh
|
||||
o.chartpath = hh.Path(chartname)
|
||||
|
||||
if err := o.run(out); err != nil {
|
||||
|
|
@ -223,8 +222,8 @@ func createTestingChart(dest, name, baseURL string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req := &chartutil.Requirements{
|
||||
Dependencies: []*chartutil.Dependency{
|
||||
req := &chart.Requirements{
|
||||
Dependencies: []*chart.Dependency{
|
||||
{Name: "reqtest", Version: "0.1.0", Repository: baseURL},
|
||||
{Name: "compressedchart", Version: "0.1.0", Repository: baseURL},
|
||||
},
|
||||
|
|
@ -232,7 +231,7 @@ func createTestingChart(dest, name, baseURL string) error {
|
|||
return writeRequirements(dir, req)
|
||||
}
|
||||
|
||||
func writeRequirements(dir string, req *chartutil.Requirements) error {
|
||||
func writeRequirements(dir string, req *chart.Requirements) error {
|
||||
data, err := yaml.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
)
|
||||
|
|
@ -167,5 +167,5 @@ func formatChartname(c *chart.Chart) string {
|
|||
// know how: https://github.com/kubernetes/helm/issues/1347
|
||||
return "MISSING"
|
||||
}
|
||||
return fmt.Sprintf("%s-%s", c.Metadata.Name, c.Metadata.Version)
|
||||
return fmt.Sprintf("%s-%s", c.Name(), c.Metadata.Version)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
)
|
||||
|
||||
const inspectDesc = `
|
||||
|
|
@ -146,7 +146,7 @@ func newInspectCmd(out io.Writer) *cobra.Command {
|
|||
}
|
||||
|
||||
func (i *inspectOptions) run(out io.Writer) error {
|
||||
chrt, err := chartutil.Load(i.chartpath)
|
||||
chrt, err := loader.Load(i.chartpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
)
|
||||
|
|
@ -176,12 +176,12 @@ func (o *installOptions) run(out io.Writer) error {
|
|||
}
|
||||
|
||||
// Check chart requirements to make sure all dependencies are present in /charts
|
||||
chartRequested, err := chartutil.Load(o.chartPath)
|
||||
chartRequested, err := loader.Load(o.chartPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if req, err := chartutil.LoadRequirements(chartRequested); err == nil {
|
||||
if req := chartRequested.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
|
||||
|
|
@ -203,8 +203,6 @@ func (o *installOptions) run(out io.Writer) error {
|
|||
}
|
||||
|
||||
}
|
||||
} else if err != chartutil.ErrRequirementsNotFound {
|
||||
return errors.Wrap(err, "cannot load requirements")
|
||||
}
|
||||
|
||||
rel, err := o.client.InstallReleaseFromChart(
|
||||
|
|
@ -272,7 +270,6 @@ func (o *installOptions) printRelease(out io.Writer, rel *release.Release) {
|
|||
if rel == nil {
|
||||
return
|
||||
}
|
||||
// TODO: Switch to text/template like everything else.
|
||||
fmt.Fprintf(out, "NAME: %s\n", rel.Name)
|
||||
if settings.Debug {
|
||||
printRelease(out, rel)
|
||||
|
|
@ -286,27 +283,20 @@ func generateName(nameTemplate string) (string, error) {
|
|||
}
|
||||
var b bytes.Buffer
|
||||
err = t.Execute(&b, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return b.String(), nil
|
||||
return b.String(), err
|
||||
}
|
||||
|
||||
func checkDependencies(ch *chart.Chart, reqs *chartutil.Requirements) error {
|
||||
missing := []string{}
|
||||
func checkDependencies(ch *chart.Chart, reqs *chart.Requirements) error {
|
||||
var missing []string
|
||||
|
||||
deps := ch.Dependencies
|
||||
OUTER:
|
||||
for _, r := range reqs.Dependencies {
|
||||
found := false
|
||||
for _, d := range deps {
|
||||
if d.Metadata.Name == r.Name {
|
||||
found = true
|
||||
break
|
||||
for _, d := range ch.Dependencies() {
|
||||
if d.Name() == r.Name {
|
||||
continue OUTER
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
missing = append(missing, r.Name)
|
||||
}
|
||||
missing = append(missing, r.Name)
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
|
|
|
|||
|
|
@ -30,10 +30,11 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/downloader"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/provenance"
|
||||
)
|
||||
|
|
@ -129,7 +130,7 @@ func (o *packageOptions) run(out io.Writer) error {
|
|||
}
|
||||
}
|
||||
|
||||
ch, err := chartutil.LoadDir(path)
|
||||
ch, err := loader.LoadDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -161,18 +162,14 @@ func (o *packageOptions) run(out io.Writer) error {
|
|||
debug("Setting appVersion to %s", o.appVersion)
|
||||
}
|
||||
|
||||
if filepath.Base(path) != ch.Metadata.Name {
|
||||
return errors.Errorf("directory name (%s) and Chart.yaml name (%s) must match", filepath.Base(path), ch.Metadata.Name)
|
||||
if filepath.Base(path) != ch.Name() {
|
||||
return errors.Errorf("directory name (%s) and Chart.yaml name (%s) must match", filepath.Base(path), ch.Name())
|
||||
}
|
||||
|
||||
if reqs, err := chartutil.LoadRequirements(ch); err == nil {
|
||||
if reqs := ch.Requirements; reqs != nil {
|
||||
if err := checkDependencies(ch, reqs); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err != chartutil.ErrRequirementsNotFound {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var dest string
|
||||
|
|
|
|||
|
|
@ -26,8 +26,9 @@ import (
|
|||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
)
|
||||
|
||||
|
|
@ -206,7 +207,7 @@ func TestSetAppVersion(t *testing.T) {
|
|||
tmp := testTempDir(t)
|
||||
|
||||
hh := testHelmHome(t)
|
||||
settings.Home = helmpath.Home(hh)
|
||||
settings.Home = hh
|
||||
|
||||
c := newPackageCmd(&bytes.Buffer{})
|
||||
flags := map[string]string{
|
||||
|
|
@ -224,7 +225,7 @@ func TestSetAppVersion(t *testing.T) {
|
|||
} else if fi.Size() == 0 {
|
||||
t.Errorf("file %q has zero bytes.", chartPath)
|
||||
}
|
||||
ch, err := chartutil.Load(chartPath)
|
||||
ch, err := loader.Load(chartPath)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error loading packaged chart: %v", err)
|
||||
}
|
||||
|
|
@ -332,7 +333,7 @@ func createValuesFile(t *testing.T, data string) string {
|
|||
|
||||
func getChartValues(chartPath string) (chartutil.Values, error) {
|
||||
|
||||
chart, err := chartutil.Load(chartPath)
|
||||
chart, err := loader.Load(chartPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func TestUpdateCmd(t *testing.T) {
|
|||
}
|
||||
o := &repoUpdateOptions{
|
||||
update: updater,
|
||||
home: helmpath.Home(hh),
|
||||
home: hh,
|
||||
}
|
||||
if err := o.run(out); err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/engine"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
util "k8s.io/helm/pkg/releaseutil"
|
||||
"k8s.io/helm/pkg/tiller"
|
||||
tversion "k8s.io/helm/pkg/version"
|
||||
)
|
||||
|
||||
const defaultDirectoryPermission = 0755
|
||||
|
|
@ -152,17 +152,15 @@ func (o *templateOptions) run(out io.Writer) error {
|
|||
}
|
||||
|
||||
// Check chart requirements to make sure all dependencies are present in /charts
|
||||
c, err := chartutil.Load(o.chartPath)
|
||||
c, err := loader.Load(o.chartPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if req, err := chartutil.LoadRequirements(c); err == nil {
|
||||
if req := c.Requirements; req != nil {
|
||||
if err := checkDependencies(c, req); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != chartutil.ErrRequirementsNotFound {
|
||||
return errors.Wrap(err, "cannot load requirements")
|
||||
}
|
||||
options := chartutil.ReleaseOptions{
|
||||
Name: o.releaseName,
|
||||
|
|
@ -178,22 +176,18 @@ func (o *templateOptions) run(out io.Writer) error {
|
|||
// Set up engine.
|
||||
renderer := engine.New()
|
||||
|
||||
caps := &chartutil.Capabilities{
|
||||
APIVersions: chartutil.DefaultVersionSet,
|
||||
KubeVersion: chartutil.DefaultKubeVersion,
|
||||
HelmVersion: tversion.GetBuildInfo(),
|
||||
}
|
||||
|
||||
// kubernetes version
|
||||
kv, err := semver.NewVersion(o.kubeVersion)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not parse a kubernetes version")
|
||||
}
|
||||
|
||||
caps := chartutil.DefaultCapabilities
|
||||
caps.KubeVersion.Major = fmt.Sprint(kv.Major())
|
||||
caps.KubeVersion.Minor = fmt.Sprint(kv.Minor())
|
||||
caps.KubeVersion.GitVersion = fmt.Sprintf("v%d.%d.0", kv.Major(), kv.Minor())
|
||||
|
||||
vals, err := chartutil.ToRenderValuesCaps(c, config, options, caps)
|
||||
vals, err := chartutil.ToRenderValues(c, config, options, caps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Error: cannot load requirements: error converting YAML to JSON: yaml: line 2: did not find expected '-' indicator
|
||||
Error: cannot load requirements.yaml: error converting YAML to JSON: yaml: line 2: did not find expected '-' indicator
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/helm/cmd/helm/require"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
"k8s.io/helm/pkg/storage/driver"
|
||||
)
|
||||
|
|
@ -150,17 +150,15 @@ func (o *upgradeOptions) run(out io.Writer) error {
|
|||
}
|
||||
|
||||
// Check chart requirements to make sure all dependencies are present in /charts
|
||||
if ch, err := chartutil.Load(chartPath); err == nil {
|
||||
if req, err := chartutil.LoadRequirements(ch); err == nil {
|
||||
if err := checkDependencies(ch, req); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != chartutil.ErrRequirementsNotFound {
|
||||
return errors.Wrap(err, "cannot load requirements")
|
||||
}
|
||||
} else {
|
||||
ch, err := loader.Load(chartPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if req := ch.Requirements; req != nil {
|
||||
if err := checkDependencies(ch, req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := o.client.UpdateRelease(
|
||||
o.release,
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ package main
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/helm"
|
||||
)
|
||||
|
|
@ -36,7 +37,7 @@ func TestUpgradeCmd(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Error creating chart for upgrade: %v", err)
|
||||
}
|
||||
ch, err := chartutil.Load(chartPath)
|
||||
ch, err := loader.Load(chartPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Error loading chart: %v", err)
|
||||
}
|
||||
|
|
@ -56,7 +57,7 @@ func TestUpgradeCmd(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Error creating chart: %v", err)
|
||||
}
|
||||
ch, err = chartutil.Load(chartPath)
|
||||
ch, err = loader.Load(chartPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Error loading updated chart: %v", err)
|
||||
}
|
||||
|
|
@ -73,7 +74,7 @@ func TestUpgradeCmd(t *testing.T) {
|
|||
t.Fatalf("Error creating chart: %v", err)
|
||||
}
|
||||
var ch2 *chart.Chart
|
||||
ch2, err = chartutil.Load(chartPath)
|
||||
ch2, err = loader.Load(chartPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Error loading updated chart: %v", err)
|
||||
}
|
||||
|
|
|
|||
7
docs/examples/nginx/charts/alpine/Chart.yaml
Normal file
7
docs/examples/nginx/charts/alpine/Chart.yaml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
name: alpine
|
||||
description: Deploy a basic Alpine Linux pod
|
||||
version: 0.1.0
|
||||
home: https://github.com/kubernetes/helm
|
||||
sources:
|
||||
- https://github.com/kubernetes/helm
|
||||
appVersion: 3.3
|
||||
11
docs/examples/nginx/charts/alpine/README.md
Normal file
11
docs/examples/nginx/charts/alpine/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Alpine: A simple Helm chart
|
||||
|
||||
Run a single pod of Alpine Linux.
|
||||
|
||||
The `templates/` directory contains a very simple pod resource with a
|
||||
couple of parameters.
|
||||
|
||||
The `values.yaml` file contains the default values for the
|
||||
`alpine-pod.yaml` template.
|
||||
|
||||
You can install this example using `helm install docs/examples/alpine`.
|
||||
16
docs/examples/nginx/charts/alpine/templates/_helpers.tpl
Normal file
16
docs/examples/nginx/charts/alpine/templates/_helpers.tpl
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{{/* vim: set filetype=mustache: */}}
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "alpine.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
*/}}
|
||||
{{- define "alpine.fullname" -}}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride -}}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end -}}
|
||||
23
docs/examples/nginx/charts/alpine/templates/alpine-pod.yaml
Normal file
23
docs/examples/nginx/charts/alpine/templates/alpine-pod.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: {{ template "alpine.fullname" . }}
|
||||
labels:
|
||||
# The "heritage" label is used to track which tool deployed a given chart.
|
||||
# It is useful for admins who want to see what releases a particular tool
|
||||
# is responsible for.
|
||||
heritage: {{ .Release.Service }}
|
||||
# The "release" convention makes it easy to tie a release to all of the
|
||||
# Kubernetes resources that were created as part of that release.
|
||||
release: {{ .Release.Name }}
|
||||
# This makes it easy to audit chart usage.
|
||||
chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
app: {{ template "alpine.name" . }}
|
||||
spec:
|
||||
# This shows how to use a simple value. This will look for a passed-in value called restartPolicy.
|
||||
restartPolicy: {{ .Values.restartPolicy }}
|
||||
containers:
|
||||
- name: waiter
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
command: ["/bin/sleep", "9000"]
|
||||
6
docs/examples/nginx/charts/alpine/values.yaml
Normal file
6
docs/examples/nginx/charts/alpine/values.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
image:
|
||||
repository: alpine
|
||||
tag: 3.3
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
restartPolicy: Never
|
||||
1
docs/examples/nginx/templates/NOTES.txt
Normal file
1
docs/examples/nginx/templates/NOTES.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
Sample notes for {{ .Chart.Name }}
|
||||
95
pkg/chart/chart.go
Normal file
95
pkg/chart/chart.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package chart
|
||||
|
||||
// Chart is a helm package that contains metadata, a default config, zero or more
|
||||
// optionally parameterizable templates, and zero or more charts (dependencies).
|
||||
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.
|
||||
Templates []*File
|
||||
// Values are default config for this template.
|
||||
Values []byte
|
||||
// Files are miscellaneous files in a chart archive,
|
||||
// e.g. README, LICENSE, etc.
|
||||
Files []*File
|
||||
|
||||
parent *Chart
|
||||
dependencies []*Chart
|
||||
}
|
||||
|
||||
// SetDependencies replaces the chart dependencies.
|
||||
func (ch *Chart) SetDependencies(charts ...*Chart) {
|
||||
ch.dependencies = nil
|
||||
ch.AddDependency(charts...)
|
||||
}
|
||||
|
||||
// Name returns the name of the chart.
|
||||
func (ch *Chart) Name() string {
|
||||
if ch.Metadata == nil {
|
||||
return ""
|
||||
}
|
||||
return ch.Metadata.Name
|
||||
}
|
||||
|
||||
// AddDependency determines if the chart is a subchart.
|
||||
func (ch *Chart) AddDependency(charts ...*Chart) {
|
||||
for i, x := range charts {
|
||||
charts[i].parent = ch
|
||||
ch.dependencies = append(ch.dependencies, x)
|
||||
}
|
||||
}
|
||||
|
||||
// Root finds the root chart.
|
||||
func (ch *Chart) Root() *Chart {
|
||||
if ch.IsRoot() {
|
||||
return ch
|
||||
}
|
||||
return ch.Parent().Root()
|
||||
}
|
||||
|
||||
// Dependencies are the charts that this chart depends on.
|
||||
func (ch *Chart) Dependencies() []*Chart { return ch.dependencies }
|
||||
|
||||
// IsRoot determines if the chart is the root chart.
|
||||
func (ch *Chart) IsRoot() bool { return ch.parent == nil }
|
||||
|
||||
// Parent returns a subchart's parent chart.
|
||||
func (ch *Chart) Parent() *Chart { return ch.parent }
|
||||
|
||||
// Parent sets a subchart's parent chart.
|
||||
func (ch *Chart) SetParent(chart *Chart) { ch.parent = chart }
|
||||
|
||||
// ChartPath returns the full path to this chart in dot notation.
|
||||
func (ch *Chart) ChartPath() string {
|
||||
if !ch.IsRoot() {
|
||||
return ch.Parent().ChartPath() + "." + ch.Name()
|
||||
}
|
||||
return ch.Name()
|
||||
}
|
||||
|
||||
// ChartFullPath returns the full path to this chart.
|
||||
func (ch *Chart) ChartFullPath() string {
|
||||
if !ch.IsRoot() {
|
||||
return ch.Parent().ChartFullPath() + "/charts/" + ch.Name()
|
||||
}
|
||||
return ch.Name()
|
||||
}
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Copyright 2018 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
|
@ -14,12 +13,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package chartutil
|
||||
package chart
|
||||
|
||||
import "strings"
|
||||
|
||||
// Transform performs a string replacement of the specified source for
|
||||
// a given key with the replacement string
|
||||
func Transform(src, key, replacement string) []byte {
|
||||
return []byte(strings.Replace(src, key, replacement, -1))
|
||||
}
|
||||
// APIVersionv1 is the API version number for version 1.
|
||||
const APIVersionv1 = "v1"
|
||||
|
|
@ -21,7 +21,7 @@ package chart
|
|||
// base directory.
|
||||
type File struct {
|
||||
// Name is the path-like name of the template.
|
||||
Name string `json:"name,omitempty"`
|
||||
Name string
|
||||
// Data is the template as byte data.
|
||||
Data []byte `json:"data,omitempty"`
|
||||
Data []byte
|
||||
}
|
||||
110
pkg/chart/loader/archive.go
Normal file
110
pkg/chart/loader/archive.go
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
)
|
||||
|
||||
type FileLoader string
|
||||
|
||||
func (l FileLoader) Load() (*chart.Chart, error) {
|
||||
return LoadFile(string(l))
|
||||
}
|
||||
|
||||
// LoadFile loads from an archive file.
|
||||
func LoadFile(name string) (*chart.Chart, error) {
|
||||
if fi, err := os.Stat(name); err != nil {
|
||||
return nil, err
|
||||
} else if fi.IsDir() {
|
||||
return nil, errors.New("cannot load a directory")
|
||||
}
|
||||
|
||||
raw, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer raw.Close()
|
||||
|
||||
return LoadArchive(raw)
|
||||
}
|
||||
|
||||
// LoadArchive loads from a reader containing a compressed tar archive.
|
||||
func LoadArchive(in io.Reader) (*chart.Chart, error) {
|
||||
unzipped, err := gzip.NewReader(in)
|
||||
if err != nil {
|
||||
return &chart.Chart{}, err
|
||||
}
|
||||
defer unzipped.Close()
|
||||
|
||||
files := []*BufferedFile{}
|
||||
tr := tar.NewReader(unzipped)
|
||||
for {
|
||||
b := bytes.NewBuffer(nil)
|
||||
hd, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return &chart.Chart{}, err
|
||||
}
|
||||
|
||||
if hd.FileInfo().IsDir() {
|
||||
// Use this instead of hd.Typeflag because we don't have to do any
|
||||
// inference chasing.
|
||||
continue
|
||||
}
|
||||
|
||||
// Archive could contain \ if generated on Windows
|
||||
delimiter := "/"
|
||||
if strings.ContainsRune(hd.Name, '\\') {
|
||||
delimiter = "\\"
|
||||
}
|
||||
|
||||
parts := strings.Split(hd.Name, delimiter)
|
||||
n := strings.Join(parts[1:], delimiter)
|
||||
|
||||
// Normalize the path to the / delimiter
|
||||
n = strings.Replace(n, delimiter, "/", -1)
|
||||
|
||||
if parts[0] == "Chart.yaml" {
|
||||
return nil, errors.New("chart yaml not in base directory")
|
||||
}
|
||||
|
||||
if _, err := io.Copy(b, tr); err != nil {
|
||||
return &chart.Chart{}, err
|
||||
}
|
||||
|
||||
files = append(files, &BufferedFile{Name: n, Data: b.Bytes()})
|
||||
b.Reset()
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
return nil, errors.New("no files in chart archive")
|
||||
}
|
||||
|
||||
return LoadFiles(files)
|
||||
}
|
||||
105
pkg/chart/loader/directory.go
Normal file
105
pkg/chart/loader/directory.go
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/ignore"
|
||||
"k8s.io/helm/pkg/sympath"
|
||||
)
|
||||
|
||||
type DirLoader string
|
||||
|
||||
func (l DirLoader) Load() (*chart.Chart, error) {
|
||||
return LoadDir(string(l))
|
||||
}
|
||||
|
||||
// LoadDir loads from a directory.
|
||||
//
|
||||
// This loads charts only from directories.
|
||||
func LoadDir(dir string) (*chart.Chart, error) {
|
||||
topdir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Just used for errors.
|
||||
c := &chart.Chart{}
|
||||
|
||||
rules := ignore.Empty()
|
||||
ifile := filepath.Join(topdir, ignore.HelmIgnore)
|
||||
if _, err := os.Stat(ifile); err == nil {
|
||||
r, err := ignore.ParseFile(ifile)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
rules = r
|
||||
}
|
||||
rules.AddDefaults()
|
||||
|
||||
files := []*BufferedFile{}
|
||||
topdir += string(filepath.Separator)
|
||||
|
||||
walk := func(name string, fi os.FileInfo, err error) error {
|
||||
n := strings.TrimPrefix(name, topdir)
|
||||
if n == "" {
|
||||
// No need to process top level. Avoid bug with helmignore .* matching
|
||||
// empty names. See issue 1779.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Normalize to / since it will also work on Windows
|
||||
n = filepath.ToSlash(n)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
// Directory-based ignore rules should involve skipping the entire
|
||||
// contents of that directory.
|
||||
if rules.Ignore(n, fi) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// If a .helmignore file matches, skip this file.
|
||||
if rules.Ignore(n, fi) {
|
||||
return nil
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error reading %s", n)
|
||||
}
|
||||
|
||||
files = append(files, &BufferedFile{Name: n, Data: data})
|
||||
return nil
|
||||
}
|
||||
if err = sympath.Walk(topdir, walk); err != nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
return LoadFiles(files)
|
||||
}
|
||||
151
pkg/chart/loader/load.go
Normal file
151
pkg/chart/loader/load.go
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package loader
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
)
|
||||
|
||||
type ChartLoader interface {
|
||||
Load() (*chart.Chart, error)
|
||||
}
|
||||
|
||||
func Loader(name string) (ChartLoader, error) {
|
||||
fi, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
return DirLoader(name), nil
|
||||
}
|
||||
return FileLoader(name), nil
|
||||
|
||||
}
|
||||
|
||||
// Load takes a string name, tries to resolve it to a file or directory, and then loads it.
|
||||
//
|
||||
// This is the preferred way to load a chart. It will discover the chart encoding
|
||||
// and hand off to the appropriate chart reader.
|
||||
//
|
||||
// If a .helmignore file is present, the directory loader will skip loading any files
|
||||
// matching it. But .helmignore is not evaluated when reading out of an archive.
|
||||
func Load(name string) (*chart.Chart, error) {
|
||||
l, err := Loader(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.Load()
|
||||
}
|
||||
|
||||
// BufferedFile represents an archive file buffered for later processing.
|
||||
type BufferedFile struct {
|
||||
Name string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// LoadFiles loads from in-memory files.
|
||||
func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
|
||||
c := new(chart.Chart)
|
||||
subcharts := make(map[string][]*BufferedFile)
|
||||
|
||||
for _, f := range files {
|
||||
switch {
|
||||
case f.Name == "Chart.yaml":
|
||||
c.Metadata = new(chart.Metadata)
|
||||
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 {
|
||||
return c, errors.Wrap(err, "cannot load requirements.lock")
|
||||
}
|
||||
case f.Name == "values.yaml":
|
||||
c.Values = f.Data
|
||||
case strings.HasPrefix(f.Name, "templates/"):
|
||||
c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data})
|
||||
case strings.HasPrefix(f.Name, "charts/"):
|
||||
if filepath.Ext(f.Name) == ".prov" {
|
||||
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
|
||||
continue
|
||||
}
|
||||
|
||||
fname := strings.TrimPrefix(f.Name, "charts/")
|
||||
cname := strings.SplitN(fname, "/", 2)[0]
|
||||
subcharts[cname] = append(subcharts[cname], &BufferedFile{Name: fname, Data: f.Data})
|
||||
default:
|
||||
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that we got a Chart.yaml file
|
||||
if c.Metadata == nil {
|
||||
return c, errors.New("chart metadata (Chart.yaml) missing")
|
||||
}
|
||||
if c.Name() == "" {
|
||||
return c, errors.New("invalid chart (Chart.yaml): name must not be empty")
|
||||
}
|
||||
|
||||
for n, files := range subcharts {
|
||||
var sc *chart.Chart
|
||||
var err error
|
||||
switch {
|
||||
case strings.IndexAny(n, "_.") == 0:
|
||||
continue
|
||||
case filepath.Ext(n) == ".tgz":
|
||||
file := files[0]
|
||||
if file.Name != n {
|
||||
return c, errors.Errorf("error unpacking tar in %s: expected %s, got %s", c.Name(), n, file.Name)
|
||||
}
|
||||
// Untar the chart and add to c.Dependencies
|
||||
sc, err = LoadArchive(bytes.NewBuffer(file.Data))
|
||||
default:
|
||||
// We have to trim the prefix off of every file, and ignore any file
|
||||
// that is in charts/, but isn't actually a chart.
|
||||
buff := make([]*BufferedFile, 0, len(files))
|
||||
for _, f := range files {
|
||||
parts := strings.SplitN(f.Name, "/", 2)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
f.Name = parts[1]
|
||||
buff = append(buff, f)
|
||||
}
|
||||
sc, err = LoadFiles(buff)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return c, errors.Wrapf(err, "error unpacking %s in %s", n, c.Name())
|
||||
}
|
||||
c.AddDependency(sc)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
|
@ -14,27 +14,35 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package chartutil
|
||||
package loader
|
||||
|
||||
import (
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
)
|
||||
|
||||
func TestLoadDir(t *testing.T) {
|
||||
c, err := Load("testdata/frobnitz")
|
||||
l, err := Loader("testdata/frobnitz")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
c, err := l.Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
verifyFrobnitz(t, c)
|
||||
verifyChart(t, c)
|
||||
verifyRequirements(t, c)
|
||||
verifyRequirementsLock(t, c)
|
||||
}
|
||||
|
||||
func TestLoadFile(t *testing.T) {
|
||||
c, err := Load("testdata/frobnitz-1.2.3.tgz")
|
||||
l, err := Loader("testdata/frobnitz-1.2.3.tgz")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
c, err := l.Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
|
|
@ -46,7 +54,7 @@ func TestLoadFile(t *testing.T) {
|
|||
func TestLoadFiles(t *testing.T) {
|
||||
goodFiles := []*BufferedFile{
|
||||
{
|
||||
Name: ChartfileName,
|
||||
Name: "Chart.yaml",
|
||||
Data: []byte(`apiVersion: v1
|
||||
name: frobnitz
|
||||
description: This is a frobnitz.
|
||||
|
|
@ -67,16 +75,16 @@ icon: https://example.com/64x64.png
|
|||
`),
|
||||
},
|
||||
{
|
||||
Name: ValuesfileName,
|
||||
Data: []byte(defaultValues),
|
||||
Name: "values.yaml",
|
||||
Data: []byte("some values"),
|
||||
},
|
||||
{
|
||||
Name: path.Join("templates", DeploymentName),
|
||||
Data: []byte(defaultDeployment),
|
||||
Name: "templates/deployment.yaml",
|
||||
Data: []byte("some deployment"),
|
||||
},
|
||||
{
|
||||
Name: path.Join("templates", ServiceName),
|
||||
Data: []byte(defaultService),
|
||||
Name: "templates/service.yaml",
|
||||
Data: []byte("some service"),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -85,11 +93,11 @@ icon: https://example.com/64x64.png
|
|||
t.Errorf("Expected good files to be loaded, got %v", err)
|
||||
}
|
||||
|
||||
if c.Metadata.Name != "frobnitz" {
|
||||
t.Errorf("Expected chart name to be 'frobnitz', got %s", c.Metadata.Name)
|
||||
if c.Name() != "frobnitz" {
|
||||
t.Errorf("Expected chart name to be 'frobnitz', got %s", c.Name())
|
||||
}
|
||||
|
||||
if string(c.Values) != defaultValues {
|
||||
if string(c.Values) != "some values" {
|
||||
t.Error("Expected chart values to be populated with default values")
|
||||
}
|
||||
|
||||
|
|
@ -119,15 +127,16 @@ func TestLoadFileBackslash(t *testing.T) {
|
|||
}
|
||||
|
||||
func verifyChart(t *testing.T, c *chart.Chart) {
|
||||
if c.Metadata.Name == "" {
|
||||
t.Helper()
|
||||
if c.Name() == "" {
|
||||
t.Fatalf("No chart metadata found on %v", c)
|
||||
}
|
||||
t.Logf("Verifying chart %s", c.Metadata.Name)
|
||||
t.Logf("Verifying chart %s", c.Name())
|
||||
if len(c.Templates) != 1 {
|
||||
t.Errorf("Expected 1 template, got %d", len(c.Templates))
|
||||
}
|
||||
|
||||
numfiles := 8
|
||||
numfiles := 6
|
||||
if len(c.Files) != numfiles {
|
||||
t.Errorf("Expected %d extra files, got %d", numfiles, len(c.Files))
|
||||
for _, n := range c.Files {
|
||||
|
|
@ -135,10 +144,10 @@ func verifyChart(t *testing.T, c *chart.Chart) {
|
|||
}
|
||||
}
|
||||
|
||||
if len(c.Dependencies) != 2 {
|
||||
t.Errorf("Expected 2 dependencies, got %d (%v)", len(c.Dependencies), c.Dependencies)
|
||||
for _, d := range c.Dependencies {
|
||||
t.Logf("\tSubchart: %s\n", d.Metadata.Name)
|
||||
if len(c.Dependencies()) != 2 {
|
||||
t.Errorf("Expected 2 dependencies, got %d (%v)", len(c.Dependencies()), c.Dependencies())
|
||||
for _, d := range c.Dependencies() {
|
||||
t.Logf("\tSubchart: %s\n", d.Name())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -151,35 +160,31 @@ func verifyChart(t *testing.T, c *chart.Chart) {
|
|||
},
|
||||
}
|
||||
|
||||
for _, dep := range c.Dependencies {
|
||||
for _, dep := range c.Dependencies() {
|
||||
if dep.Metadata == nil {
|
||||
t.Fatalf("expected metadata on dependency: %v", dep)
|
||||
}
|
||||
exp, ok := expect[dep.Metadata.Name]
|
||||
exp, ok := expect[dep.Name()]
|
||||
if !ok {
|
||||
t.Fatalf("Unknown dependency %s", dep.Metadata.Name)
|
||||
t.Fatalf("Unknown dependency %s", dep.Name())
|
||||
}
|
||||
if exp["version"] != dep.Metadata.Version {
|
||||
t.Errorf("Expected %s version %s, got %s", dep.Metadata.Name, exp["version"], dep.Metadata.Version)
|
||||
t.Errorf("Expected %s version %s, got %s", dep.Name(), exp["version"], dep.Metadata.Version)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func verifyRequirements(t *testing.T, c *chart.Chart) {
|
||||
r, err := LoadRequirements(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if len(c.Requirements.Dependencies) != 2 {
|
||||
t.Errorf("Expected 2 requirements, got %d", len(c.Requirements.Dependencies))
|
||||
}
|
||||
if len(r.Dependencies) != 2 {
|
||||
t.Errorf("Expected 2 requirements, got %d", len(r.Dependencies))
|
||||
}
|
||||
tests := []*Dependency{
|
||||
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 := r.Dependencies[i]
|
||||
d := c.Requirements.Dependencies[i]
|
||||
if d.Name != tt.Name {
|
||||
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
|
||||
}
|
||||
|
|
@ -191,20 +196,17 @@ func verifyRequirements(t *testing.T, c *chart.Chart) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyRequirementsLock(t *testing.T, c *chart.Chart) {
|
||||
r, err := LoadRequirementsLock(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if len(c.Requirements.Dependencies) != 2 {
|
||||
t.Errorf("Expected 2 requirements, got %d", len(c.Requirements.Dependencies))
|
||||
}
|
||||
if len(r.Dependencies) != 2 {
|
||||
t.Errorf("Expected 2 requirements, got %d", len(r.Dependencies))
|
||||
}
|
||||
tests := []*Dependency{
|
||||
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 := r.Dependencies[i]
|
||||
d := c.Requirements.Dependencies[i]
|
||||
if d.Name != tt.Name {
|
||||
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
|
||||
}
|
||||
|
|
@ -223,17 +225,55 @@ func verifyFrobnitz(t *testing.T, c *chart.Chart) {
|
|||
|
||||
func verifyChartFileAndTemplate(t *testing.T, c *chart.Chart, name string) {
|
||||
|
||||
verifyChartfile(t, c.Metadata, name)
|
||||
|
||||
if c.Metadata == nil {
|
||||
t.Fatal("Metadata is nil")
|
||||
}
|
||||
if c.Name() != name {
|
||||
t.Errorf("Expected %s, got %s", name, c.Name())
|
||||
}
|
||||
if len(c.Templates) != 1 {
|
||||
t.Fatalf("Expected 1 template, got %d", len(c.Templates))
|
||||
}
|
||||
|
||||
if c.Templates[0].Name != "templates/template.tpl" {
|
||||
t.Errorf("Unexpected template: %s", c.Templates[0].Name)
|
||||
}
|
||||
|
||||
if len(c.Templates[0].Data) == 0 {
|
||||
t.Error("No template data.")
|
||||
}
|
||||
if len(c.Files) != 6 {
|
||||
t.Fatalf("Expected 6 Files, got %d", len(c.Files))
|
||||
}
|
||||
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.RequirementsLock.Dependencies) != 2 {
|
||||
t.Fatalf("Expected 2 RequirementsLock.Dependency, got %d", len(c.RequirementsLock.Dependencies))
|
||||
}
|
||||
|
||||
for _, dep := range c.Dependencies() {
|
||||
switch dep.Name() {
|
||||
case "mariner":
|
||||
case "alpine":
|
||||
if len(dep.Templates) != 1 {
|
||||
t.Fatalf("Expected 1 template, got %d", len(dep.Templates))
|
||||
}
|
||||
if dep.Templates[0].Name != "templates/alpine-pod.yaml" {
|
||||
t.Errorf("Unexpected template: %s", dep.Templates[0].Name)
|
||||
}
|
||||
if len(dep.Templates[0].Data) == 0 {
|
||||
t.Error("No template data.")
|
||||
}
|
||||
if len(dep.Files) != 1 {
|
||||
t.Fatalf("Expected 1 Files, got %d", len(dep.Files))
|
||||
}
|
||||
if len(dep.Dependencies()) != 2 {
|
||||
t.Fatalf("Expected 2 Dependency, got %d", len(dep.Dependencies()))
|
||||
}
|
||||
default:
|
||||
t.Errorf("Unexpected dependeny %s", dep.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
pkg/chart/loader/testdata/frobnitz-1.2.3.tgz
vendored
Normal file
BIN
pkg/chart/loader/testdata/frobnitz-1.2.3.tgz
vendored
Normal file
Binary file not shown.
1
pkg/chart/loader/testdata/frobnitz/.helmignore
vendored
Normal file
1
pkg/chart/loader/testdata/frobnitz/.helmignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
ignore/
|
||||
20
pkg/chart/loader/testdata/frobnitz/Chart.yaml
vendored
Normal file
20
pkg/chart/loader/testdata/frobnitz/Chart.yaml
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: v1
|
||||
name: frobnitz
|
||||
description: This is a frobnitz.
|
||||
version: "1.2.3"
|
||||
keywords:
|
||||
- frobnitz
|
||||
- sprocket
|
||||
- dodad
|
||||
maintainers:
|
||||
- name: The Helm Team
|
||||
email: helm@example.com
|
||||
- name: Someone Else
|
||||
email: nobody@example.com
|
||||
sources:
|
||||
- https://example.com/foo/bar
|
||||
home: http://example.com
|
||||
icon: https://example.com/64x64.png
|
||||
annotations:
|
||||
extrakey: extravalue
|
||||
anotherkey: anothervalue
|
||||
1
pkg/chart/loader/testdata/frobnitz/INSTALL.txt
vendored
Normal file
1
pkg/chart/loader/testdata/frobnitz/INSTALL.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
This is an install document. The client may display this.
|
||||
1
pkg/chart/loader/testdata/frobnitz/LICENSE
vendored
Normal file
1
pkg/chart/loader/testdata/frobnitz/LICENSE
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
LICENSE placeholder.
|
||||
11
pkg/chart/loader/testdata/frobnitz/README.md
vendored
Normal file
11
pkg/chart/loader/testdata/frobnitz/README.md
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Frobnitz
|
||||
|
||||
This is an example chart.
|
||||
|
||||
## Usage
|
||||
|
||||
This is an example. It has no usage.
|
||||
|
||||
## Development
|
||||
|
||||
For developer info, see the top-level repository.
|
||||
1
pkg/chart/loader/testdata/frobnitz/charts/_ignore_me
vendored
Normal file
1
pkg/chart/loader/testdata/frobnitz/charts/_ignore_me
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
This should be ignored by the loader, but may be included in a chart.
|
||||
4
pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml
vendored
Normal file
4
pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
name: alpine
|
||||
description: Deploy a basic Alpine Linux pod
|
||||
version: 0.1.0
|
||||
home: https://k8s.io/helm
|
||||
9
pkg/chart/loader/testdata/frobnitz/charts/alpine/README.md
vendored
Normal file
9
pkg/chart/loader/testdata/frobnitz/charts/alpine/README.md
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
This example was generated using the command `helm create alpine`.
|
||||
|
||||
The `templates/` directory contains a very simple pod resource with a
|
||||
couple of parameters.
|
||||
|
||||
The `values.toml` file contains the default values for the
|
||||
`alpine-pod.yaml` template.
|
||||
|
||||
You can install this example using `helm install docs/examples/alpine`.
|
||||
4
pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml
vendored
Normal file
4
pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
name: mast1
|
||||
description: A Helm chart for Kubernetes
|
||||
version: 0.1.0
|
||||
home: ""
|
||||
4
pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml
vendored
Normal file
4
pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/values.yaml
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Default values for mast1.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare name/value pairs to be passed into your templates.
|
||||
# name = "value"
|
||||
BIN
pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz
vendored
Normal file
BIN
pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz
vendored
Normal file
Binary file not shown.
14
pkg/chart/loader/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml
vendored
Normal file
14
pkg/chart/loader/testdata/frobnitz/charts/alpine/templates/alpine-pod.yaml
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: {{.Release.Name}}-{{.Chart.Name}}
|
||||
labels:
|
||||
heritage: {{.Release.Service}}
|
||||
chartName: {{.Chart.Name}}
|
||||
chartVersion: {{.Chart.Version | quote}}
|
||||
spec:
|
||||
restartPolicy: {{default "Never" .restart_policy}}
|
||||
containers:
|
||||
- name: waiter
|
||||
image: "alpine:3.3"
|
||||
command: ["/bin/sleep","9000"]
|
||||
2
pkg/chart/loader/testdata/frobnitz/charts/alpine/values.yaml
vendored
Normal file
2
pkg/chart/loader/testdata/frobnitz/charts/alpine/values.yaml
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# The pod name
|
||||
name: "my-alpine"
|
||||
BIN
pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz
vendored
Normal file
BIN
pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz
vendored
Normal file
Binary file not shown.
1
pkg/chart/loader/testdata/frobnitz/docs/README.md
vendored
Normal file
1
pkg/chart/loader/testdata/frobnitz/docs/README.md
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
This is a placeholder for documentation.
|
||||
8
pkg/chart/loader/testdata/frobnitz/icon.svg
vendored
Normal file
8
pkg/chart/loader/testdata/frobnitz/icon.svg
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.0" width="256" height="256" id="test">
|
||||
<desc>Example icon</desc>
|
||||
<rect id="first" x="2" y="2" width="40" height="60" fill="navy"/>
|
||||
<rect id="second" x="15" y="4" width="40" height="60" fill="red"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 374 B |
0
pkg/chart/loader/testdata/frobnitz/ignore/me.txt
vendored
Normal file
0
pkg/chart/loader/testdata/frobnitz/ignore/me.txt
vendored
Normal file
8
pkg/chart/loader/testdata/frobnitz/requirements.lock
vendored
Normal file
8
pkg/chart/loader/testdata/frobnitz/requirements.lock
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
dependencies:
|
||||
- name: alpine
|
||||
version: "0.1.0"
|
||||
repository: https://example.com/charts
|
||||
- name: mariner
|
||||
version: "4.3.2"
|
||||
repository: https://example.com/charts
|
||||
digest: invalid
|
||||
7
pkg/chart/loader/testdata/frobnitz/requirements.yaml
vendored
Normal file
7
pkg/chart/loader/testdata/frobnitz/requirements.yaml
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
dependencies:
|
||||
- name: alpine
|
||||
version: "0.1.0"
|
||||
repository: https://example.com/charts
|
||||
- name: mariner
|
||||
version: "4.3.2"
|
||||
repository: https://example.com/charts
|
||||
1
pkg/chart/loader/testdata/frobnitz/templates/template.tpl
vendored
Normal file
1
pkg/chart/loader/testdata/frobnitz/templates/template.tpl
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
Hello {{.Name | default "world"}}
|
||||
6
pkg/chart/loader/testdata/frobnitz/values.yaml
vendored
Normal file
6
pkg/chart/loader/testdata/frobnitz/values.yaml
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# A values file contains configuration.
|
||||
|
||||
name: "Some Name"
|
||||
|
||||
section:
|
||||
name: "Name in a section"
|
||||
BIN
pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz
vendored
Normal file
BIN
pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz
vendored
Normal file
Binary file not shown.
1
pkg/chart/loader/testdata/frobnitz_backslash/.helmignore
vendored
Executable file
1
pkg/chart/loader/testdata/frobnitz_backslash/.helmignore
vendored
Executable file
|
|
@ -0,0 +1 @@
|
|||
ignore/
|
||||
20
pkg/chart/loader/testdata/frobnitz_backslash/Chart.yaml
vendored
Executable file
20
pkg/chart/loader/testdata/frobnitz_backslash/Chart.yaml
vendored
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: v1
|
||||
name: frobnitz_backslash
|
||||
description: This is a frobnitz.
|
||||
version: "1.2.3"
|
||||
keywords:
|
||||
- frobnitz
|
||||
- sprocket
|
||||
- dodad
|
||||
maintainers:
|
||||
- name: The Helm Team
|
||||
email: helm@example.com
|
||||
- name: Someone Else
|
||||
email: nobody@example.com
|
||||
sources:
|
||||
- https://example.com/foo/bar
|
||||
home: http://example.com
|
||||
icon: https://example.com/64x64.png
|
||||
annotations:
|
||||
extrakey: extravalue
|
||||
anotherkey: anothervalue
|
||||
1
pkg/chart/loader/testdata/frobnitz_backslash/INSTALL.txt
vendored
Executable file
1
pkg/chart/loader/testdata/frobnitz_backslash/INSTALL.txt
vendored
Executable file
|
|
@ -0,0 +1 @@
|
|||
This is an install document. The client may display this.
|
||||
1
pkg/chart/loader/testdata/frobnitz_backslash/LICENSE
vendored
Executable file
1
pkg/chart/loader/testdata/frobnitz_backslash/LICENSE
vendored
Executable file
|
|
@ -0,0 +1 @@
|
|||
LICENSE placeholder.
|
||||
11
pkg/chart/loader/testdata/frobnitz_backslash/README.md
vendored
Executable file
11
pkg/chart/loader/testdata/frobnitz_backslash/README.md
vendored
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
# Frobnitz
|
||||
|
||||
This is an example chart.
|
||||
|
||||
## Usage
|
||||
|
||||
This is an example. It has no usage.
|
||||
|
||||
## Development
|
||||
|
||||
For developer info, see the top-level repository.
|
||||
1
pkg/chart/loader/testdata/frobnitz_backslash/charts/_ignore_me
vendored
Executable file
1
pkg/chart/loader/testdata/frobnitz_backslash/charts/_ignore_me
vendored
Executable file
|
|
@ -0,0 +1 @@
|
|||
This should be ignored by the loader, but may be included in a chart.
|
||||
4
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml
vendored
Executable file
4
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml
vendored
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
name: alpine
|
||||
description: Deploy a basic Alpine Linux pod
|
||||
version: 0.1.0
|
||||
home: https://k8s.io/helm
|
||||
9
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/README.md
vendored
Executable file
9
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/README.md
vendored
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
This example was generated using the command `helm create alpine`.
|
||||
|
||||
The `templates/` directory contains a very simple pod resource with a
|
||||
couple of parameters.
|
||||
|
||||
The `values.toml` file contains the default values for the
|
||||
`alpine-pod.yaml` template.
|
||||
|
||||
You can install this example using `helm install docs/examples/alpine`.
|
||||
4
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml
vendored
Executable file
4
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml
vendored
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
name: mast1
|
||||
description: A Helm chart for Kubernetes
|
||||
version: 0.1.0
|
||||
home: ""
|
||||
4
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml
vendored
Executable file
4
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/values.yaml
vendored
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
# Default values for mast1.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare name/value pairs to be passed into your templates.
|
||||
# name = "value"
|
||||
BIN
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz
vendored
Executable file
BIN
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz
vendored
Executable file
Binary file not shown.
14
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml
vendored
Executable file
14
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/templates/alpine-pod.yaml
vendored
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: {{.Release.Name}}-{{.Chart.Name}}
|
||||
labels:
|
||||
heritage: {{.Release.Service}}
|
||||
chartName: {{.Chart.Name}}
|
||||
chartVersion: {{.Chart.Version | quote}}
|
||||
spec:
|
||||
restartPolicy: {{default "Never" .restart_policy}}
|
||||
containers:
|
||||
- name: waiter
|
||||
image: "alpine:3.3"
|
||||
command: ["/bin/sleep","9000"]
|
||||
2
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/values.yaml
vendored
Executable file
2
pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/values.yaml
vendored
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
# The pod name
|
||||
name: "my-alpine"
|
||||
BIN
pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz
vendored
Executable file
BIN
pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz
vendored
Executable file
Binary file not shown.
1
pkg/chart/loader/testdata/frobnitz_backslash/docs/README.md
vendored
Executable file
1
pkg/chart/loader/testdata/frobnitz_backslash/docs/README.md
vendored
Executable file
|
|
@ -0,0 +1 @@
|
|||
This is a placeholder for documentation.
|
||||
8
pkg/chart/loader/testdata/frobnitz_backslash/icon.svg
vendored
Executable file
8
pkg/chart/loader/testdata/frobnitz_backslash/icon.svg
vendored
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.0" width="256" height="256" id="test">
|
||||
<desc>Example icon</desc>
|
||||
<rect id="first" x="2" y="2" width="40" height="60" fill="navy"/>
|
||||
<rect id="second" x="15" y="4" width="40" height="60" fill="red"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 374 B |
0
pkg/chart/loader/testdata/frobnitz_backslash/ignore/me.txt
vendored
Executable file
0
pkg/chart/loader/testdata/frobnitz_backslash/ignore/me.txt
vendored
Executable file
8
pkg/chart/loader/testdata/frobnitz_backslash/requirements.lock
vendored
Executable file
8
pkg/chart/loader/testdata/frobnitz_backslash/requirements.lock
vendored
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
dependencies:
|
||||
- name: alpine
|
||||
version: "0.1.0"
|
||||
repository: https://example.com/charts
|
||||
- name: mariner
|
||||
version: "4.3.2"
|
||||
repository: https://example.com/charts
|
||||
digest: invalid
|
||||
7
pkg/chart/loader/testdata/frobnitz_backslash/requirements.yaml
vendored
Executable file
7
pkg/chart/loader/testdata/frobnitz_backslash/requirements.yaml
vendored
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
dependencies:
|
||||
- name: alpine
|
||||
version: "0.1.0"
|
||||
repository: https://example.com/charts
|
||||
- name: mariner
|
||||
version: "4.3.2"
|
||||
repository: https://example.com/charts
|
||||
1
pkg/chart/loader/testdata/frobnitz_backslash/templates/template.tpl
vendored
Executable file
1
pkg/chart/loader/testdata/frobnitz_backslash/templates/template.tpl
vendored
Executable file
|
|
@ -0,0 +1 @@
|
|||
Hello {{.Name | default "world"}}
|
||||
6
pkg/chart/loader/testdata/frobnitz_backslash/values.yaml
vendored
Executable file
6
pkg/chart/loader/testdata/frobnitz_backslash/values.yaml
vendored
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
# A values file contains configuration.
|
||||
|
||||
name: "Some Name"
|
||||
|
||||
section:
|
||||
name: "Name in a section"
|
||||
70
pkg/chart/requirements.go
Normal file
70
pkg/chart/requirements.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package chart
|
||||
|
||||
import "time"
|
||||
|
||||
// Dependency describes a chart upon which another chart depends.
|
||||
//
|
||||
// Dependencies can be used to express developer intent, or to capture the state
|
||||
// of a chart.
|
||||
type Dependency struct {
|
||||
// Name is the name of the dependency.
|
||||
//
|
||||
// This must mach the name in the dependency's Chart.yaml.
|
||||
Name string `json:"name"`
|
||||
// Version is the version (range) of this chart.
|
||||
//
|
||||
// A lock file will always produce a single version, while a dependency
|
||||
// may contain a semantic version range.
|
||||
Version string `json:"version,omitempty"`
|
||||
// The URL to the repository.
|
||||
//
|
||||
// Appending `index.yaml` to this string should result in a URL that can be
|
||||
// used to fetch the repository index.
|
||||
Repository string `json:"repository"`
|
||||
// A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled )
|
||||
Condition string `json:"condition,omitempty"`
|
||||
// Tags can be used to group charts for enabling/disabling together
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
// Enabled bool determines if chart should be loaded
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
// ImportValues holds the mapping of source values to parent key to be imported. Each item can be a
|
||||
// string or pair of child/parent sublist items.
|
||||
ImportValues []interface{} `json:"import-values,omitempty"`
|
||||
// Alias usable alias to be used for the chart
|
||||
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.
|
||||
type RequirementsLock struct {
|
||||
// Genderated is the date the lock file was last generated.
|
||||
Generated time.Time `json:"generated"`
|
||||
// Digest is a hash of the requirements file used to generate it.
|
||||
Digest string `json:"digest"`
|
||||
// Dependencies is the list of dependencies that this lock file has locked.
|
||||
Dependencies []*Dependency `json:"dependencies"`
|
||||
}
|
||||
|
|
@ -20,13 +20,14 @@ import (
|
|||
"runtime"
|
||||
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
|
||||
tversion "k8s.io/helm/pkg/version"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultVersionSet is the default version set, which includes only Core V1 ("v1").
|
||||
DefaultVersionSet = NewVersionSet("v1")
|
||||
DefaultVersionSet = allKnownVersions()
|
||||
|
||||
// DefaultKubeVersion is the default kubernetes version
|
||||
DefaultKubeVersion = &version.Info{
|
||||
|
|
@ -37,6 +38,12 @@ var (
|
|||
Compiler: runtime.Compiler,
|
||||
Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
|
||||
}
|
||||
|
||||
// DefaultCapabilities is the default set of capabilities.
|
||||
DefaultCapabilities = &Capabilities{
|
||||
APIVersions: DefaultVersionSet,
|
||||
KubeVersion: DefaultKubeVersion,
|
||||
}
|
||||
)
|
||||
|
||||
// Capabilities describes the capabilities of the Kubernetes cluster that Tiller is attached to.
|
||||
|
|
@ -52,11 +59,11 @@ type Capabilities struct {
|
|||
}
|
||||
|
||||
// VersionSet is a set of Kubernetes API versions.
|
||||
type VersionSet map[string]interface{}
|
||||
type VersionSet map[string]struct{}
|
||||
|
||||
// NewVersionSet creates a new version set from a list of strings.
|
||||
func NewVersionSet(apiVersions ...string) VersionSet {
|
||||
vs := VersionSet{}
|
||||
vs := make(VersionSet)
|
||||
for _, v := range apiVersions {
|
||||
vs[v] = struct{}{}
|
||||
}
|
||||
|
|
@ -70,3 +77,11 @@ func (v VersionSet) Has(apiVersion string) bool {
|
|||
_, ok := v[apiVersion]
|
||||
return ok
|
||||
}
|
||||
|
||||
func allKnownVersions() VersionSet {
|
||||
vs := make(VersionSet)
|
||||
for gvk := range scheme.Scheme.AllKnownTypes() {
|
||||
vs[gvk.GroupVersion().String()] = struct{}{}
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,9 +38,6 @@ func TestDefaultVersionSet(t *testing.T) {
|
|||
if !DefaultVersionSet.Has("v1") {
|
||||
t.Error("Expected core v1 version set")
|
||||
}
|
||||
if d := len(DefaultVersionSet); d != 1 {
|
||||
t.Errorf("Expected only one version, got %d", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCapabilities(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -24,29 +24,18 @@ import (
|
|||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
)
|
||||
|
||||
// APIVersionv1 is the API version number for version 1.
|
||||
const APIVersionv1 = "v1"
|
||||
|
||||
// UnmarshalChartfile takes raw Chart.yaml data and unmarshals it.
|
||||
func UnmarshalChartfile(data []byte) (*chart.Metadata, error) {
|
||||
y := &chart.Metadata{}
|
||||
err := yaml.Unmarshal(data, y)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return y, nil
|
||||
}
|
||||
|
||||
// LoadChartfile loads a Chart.yaml file into a *chart.Metadata.
|
||||
func LoadChartfile(filename string) (*chart.Metadata, error) {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return UnmarshalChartfile(b)
|
||||
y := new(chart.Metadata)
|
||||
err = yaml.Unmarshal(b, y)
|
||||
return y, err
|
||||
}
|
||||
|
||||
// SaveChartfile saves the given metadata as a Chart.yaml file at the given path.
|
||||
|
|
@ -80,8 +69,8 @@ func IsChartDir(dirName string) (bool, error) {
|
|||
return false, errors.Errorf("cannot read Chart.Yaml in directory %q", dirName)
|
||||
}
|
||||
|
||||
chartContent, err := UnmarshalChartfile(chartYamlContent)
|
||||
if err != nil {
|
||||
chartContent := new(chart.Metadata)
|
||||
if err := yaml.Unmarshal(chartYamlContent, &chartContent); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if chartContent == nil {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ package chartutil
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
)
|
||||
|
||||
const testfile = "testdata/chartfiletest.yaml"
|
||||
|
|
@ -40,8 +40,8 @@ func verifyChartfile(t *testing.T, f *chart.Metadata, name string) {
|
|||
}
|
||||
|
||||
// Api instead of API because it was generated via protobuf.
|
||||
if f.APIVersion != APIVersionv1 {
|
||||
t.Errorf("Expected API Version %q, got %q", APIVersionv1, f.APIVersion)
|
||||
if f.APIVersion != chart.APIVersionv1 {
|
||||
t.Errorf("Expected API Version %q, got %q", chart.APIVersionv1, f.APIVersion)
|
||||
}
|
||||
|
||||
if f.Name != name {
|
||||
|
|
|
|||
|
|
@ -21,10 +21,12 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -294,7 +296,7 @@ Create chart name and version as used by the chart label.
|
|||
|
||||
// CreateFrom creates a new chart, but scaffolds it from the src chart.
|
||||
func CreateFrom(chartfile *chart.Metadata, dest, src string) error {
|
||||
schart, err := Load(src)
|
||||
schart, err := loader.Load(src)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not load %s", src)
|
||||
}
|
||||
|
|
@ -304,12 +306,12 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error {
|
|||
var updatedTemplates []*chart.File
|
||||
|
||||
for _, template := range schart.Templates {
|
||||
newData := Transform(string(template.Data), "<CHARTNAME>", schart.Metadata.Name)
|
||||
newData := transform(string(template.Data), schart.Name())
|
||||
updatedTemplates = append(updatedTemplates, &chart.File{Name: template.Name, Data: newData})
|
||||
}
|
||||
|
||||
schart.Templates = updatedTemplates
|
||||
schart.Values = Transform(string(schart.Values), "<CHARTNAME>", schart.Metadata.Name)
|
||||
schart.Values = transform(string(schart.Values), schart.Name())
|
||||
|
||||
return SaveDir(schart, dest)
|
||||
}
|
||||
|
|
@ -378,27 +380,27 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) {
|
|||
{
|
||||
// ingress.yaml
|
||||
path: filepath.Join(cdir, TemplatesDir, IngressFileName),
|
||||
content: Transform(defaultIngress, "<CHARTNAME>", chartfile.Name),
|
||||
content: transform(defaultIngress, chartfile.Name),
|
||||
},
|
||||
{
|
||||
// deployment.yaml
|
||||
path: filepath.Join(cdir, TemplatesDir, DeploymentName),
|
||||
content: Transform(defaultDeployment, "<CHARTNAME>", chartfile.Name),
|
||||
content: transform(defaultDeployment, chartfile.Name),
|
||||
},
|
||||
{
|
||||
// service.yaml
|
||||
path: filepath.Join(cdir, TemplatesDir, ServiceName),
|
||||
content: Transform(defaultService, "<CHARTNAME>", chartfile.Name),
|
||||
content: transform(defaultService, chartfile.Name),
|
||||
},
|
||||
{
|
||||
// NOTES.txt
|
||||
path: filepath.Join(cdir, TemplatesDir, NotesName),
|
||||
content: Transform(defaultNotes, "<CHARTNAME>", chartfile.Name),
|
||||
content: transform(defaultNotes, chartfile.Name),
|
||||
},
|
||||
{
|
||||
// _helpers.tpl
|
||||
path: filepath.Join(cdir, TemplatesDir, HelpersName),
|
||||
content: Transform(defaultHelpers, "<CHARTNAME>", chartfile.Name),
|
||||
content: transform(defaultHelpers, chartfile.Name),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -413,3 +415,9 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) {
|
|||
}
|
||||
return cdir, nil
|
||||
}
|
||||
|
||||
// transform performs a string replacement of the specified source for
|
||||
// a given key with the replacement string
|
||||
func transform(src, replacement string) []byte {
|
||||
return []byte(strings.Replace(src, "<CHARTNAME>", replacement, -1))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
)
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
|
|
@ -42,13 +43,13 @@ func TestCreate(t *testing.T) {
|
|||
|
||||
dir := filepath.Join(tdir, "foo")
|
||||
|
||||
mychart, err := LoadDir(c)
|
||||
mychart, err := loader.LoadDir(c)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load newly created chart %q: %s", c, err)
|
||||
}
|
||||
|
||||
if mychart.Metadata.Name != "foo" {
|
||||
t.Errorf("Expected name to be 'foo', got %q", mychart.Metadata.Name)
|
||||
if mychart.Name() != "foo" {
|
||||
t.Errorf("Expected name to be 'foo', got %q", mychart.Name())
|
||||
}
|
||||
|
||||
for _, d := range []string{TemplatesDir, ChartsDir} {
|
||||
|
|
@ -94,13 +95,13 @@ func TestCreateFrom(t *testing.T) {
|
|||
dir := filepath.Join(tdir, "foo")
|
||||
|
||||
c := filepath.Join(tdir, cf.Name)
|
||||
mychart, err := LoadDir(c)
|
||||
mychart, err := loader.LoadDir(c)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load newly created chart %q: %s", c, err)
|
||||
}
|
||||
|
||||
if mychart.Metadata.Name != "foo" {
|
||||
t.Errorf("Expected name to be 'foo', got %q", mychart.Metadata.Name)
|
||||
if mychart.Name() != "foo" {
|
||||
t.Errorf("Expected name to be 'foo', got %q", mychart.Name())
|
||||
}
|
||||
|
||||
for _, d := range []string{TemplatesDir, ChartsDir} {
|
||||
|
|
@ -111,7 +112,7 @@ func TestCreateFrom(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
for _, f := range []string{ChartfileName, ValuesfileName, "requirements.yaml"} {
|
||||
for _, f := range []string{ChartfileName, ValuesfileName} {
|
||||
if fi, err := os.Stat(filepath.Join(dir, f)); err != nil {
|
||||
t.Errorf("Expected %s file: %s", f, err)
|
||||
} else if fi.IsDir() {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
|
||||
/*Package chartutil contains tools for working with charts.
|
||||
|
||||
Charts are described in the protocol buffer definition (pkg/proto/hapi/charts).
|
||||
Charts are described in the protocol buffer definition (pkg/proto/charts).
|
||||
This packe provides utilities for serializing and deserializing charts.
|
||||
|
||||
A chart can be represented on the file system in one of two ways:
|
||||
|
|
@ -27,18 +27,18 @@ A chart can be represented on the file system in one of two ways:
|
|||
|
||||
This package provides utilitites for working with those file formats.
|
||||
|
||||
The preferred way of loading a chart is using 'chartutil.Load`:
|
||||
The preferred way of loading a chart is using 'loader.Load`:
|
||||
|
||||
chart, err := chartutil.Load(filename)
|
||||
chart, err := loader.Load(filename)
|
||||
|
||||
This will attempt to discover whether the file at 'filename' is a directory or
|
||||
a chart archive. It will then load accordingly.
|
||||
|
||||
For accepting raw compressed tar file data from an io.Reader, the
|
||||
'chartutil.LoadArchive()' will read in the data, uncompress it, and unpack it
|
||||
'loader.LoadArchive()' will read in the data, uncompress it, and unpack it
|
||||
into a Chart.
|
||||
|
||||
When creating charts in memory, use the 'k8s.io/helm/pkg/proto/hapi/chart'
|
||||
When creating charts in memory, use the 'k8s.io/helm/pkg/proto/chart'
|
||||
package directly.
|
||||
*/
|
||||
package chartutil // import "k8s.io/helm/pkg/chartutil"
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ func Expand(dir string, r io.Reader) error {
|
|||
return err
|
||||
}
|
||||
|
||||
//split header name and create missing directories
|
||||
d, _ := filepath.Split(header.Name)
|
||||
// split header name and create missing directories
|
||||
d := filepath.Dir(header.Name)
|
||||
fullDir := filepath.Join(dir, d)
|
||||
_, err = os.Stat(fullDir)
|
||||
if err != nil && d != "" {
|
||||
|
|
@ -63,8 +63,7 @@ func Expand(dir string, r io.Reader) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(file, tr)
|
||||
if err != nil {
|
||||
if _, err = io.Copy(file, tr); err != nil {
|
||||
file.Close()
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import (
|
|||
"github.com/ghodss/yaml"
|
||||
"github.com/gobwas/glob"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
)
|
||||
|
||||
// Files is a map of files in a chart that can be accessed from a template.
|
||||
|
|
@ -35,7 +35,7 @@ type Files map[string][]byte
|
|||
// NewFiles creates a new Files from chart files.
|
||||
// Given an []*any.Any (the format for files in a chart.Chart), extract a map of files.
|
||||
func NewFiles(from []*chart.File) Files {
|
||||
files := map[string][]byte{}
|
||||
files := make(map[string][]byte)
|
||||
for _, f := range from {
|
||||
files[f.Name] = f.Data
|
||||
}
|
||||
|
|
@ -50,11 +50,10 @@ func NewFiles(from []*chart.File) Files {
|
|||
// This is intended to be accessed from within a template, so a missed key returns
|
||||
// an empty []byte.
|
||||
func (f Files) GetBytes(name string) []byte {
|
||||
v, ok := f[name]
|
||||
if !ok {
|
||||
return []byte{}
|
||||
if v, ok := f[name]; ok {
|
||||
return v
|
||||
}
|
||||
return v
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
// Get returns a string representation of the given file.
|
||||
|
|
@ -97,7 +96,7 @@ func (f Files) Glob(pattern string) Files {
|
|||
// (regardless of path) should be unique.
|
||||
//
|
||||
// This is designed to be called from a template, and will return empty string
|
||||
// (via ToYaml function) if it cannot be serialized to YAML, or if the Files
|
||||
// (via ToYAML function) if it cannot be serialized to YAML, or if the Files
|
||||
// object is nil.
|
||||
//
|
||||
// The output will not be indented, so you will want to pipe this to the
|
||||
|
|
@ -110,14 +109,14 @@ func (f Files) AsConfig() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
m := map[string]string{}
|
||||
m := make(map[string]string)
|
||||
|
||||
// Explicitly convert to strings, and file names
|
||||
for k, v := range f {
|
||||
m[path.Base(k)] = string(v)
|
||||
}
|
||||
|
||||
return ToYaml(m)
|
||||
return ToYAML(m)
|
||||
}
|
||||
|
||||
// AsSecrets returns the base64-encoded value of a Files object suitable for
|
||||
|
|
@ -126,7 +125,7 @@ func (f Files) AsConfig() string {
|
|||
// (regardless of path) should be unique.
|
||||
//
|
||||
// This is designed to be called from a template, and will return empty string
|
||||
// (via ToYaml function) if it cannot be serialized to YAML, or if the Files
|
||||
// (via ToYAML function) if it cannot be serialized to YAML, or if the Files
|
||||
// object is nil.
|
||||
//
|
||||
// The output will not be indented, so you will want to pipe this to the
|
||||
|
|
@ -139,13 +138,13 @@ func (f Files) AsSecrets() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
m := map[string]string{}
|
||||
m := make(map[string]string)
|
||||
|
||||
for k, v := range f {
|
||||
m[path.Base(k)] = base64.StdEncoding.EncodeToString(v)
|
||||
}
|
||||
|
||||
return ToYaml(m)
|
||||
return ToYAML(m)
|
||||
}
|
||||
|
||||
// Lines returns each line of a named file (split by "\n") as a slice, so it can
|
||||
|
|
@ -163,11 +162,11 @@ func (f Files) Lines(path string) []string {
|
|||
return strings.Split(string(f[path]), "\n")
|
||||
}
|
||||
|
||||
// ToYaml takes an interface, marshals it to yaml, and returns a string. It will
|
||||
// ToYAML takes an interface, marshals it to yaml, and returns a string. It will
|
||||
// always return a string, even on marshal error (empty string).
|
||||
//
|
||||
// This is designed to be called from a template.
|
||||
func ToYaml(v interface{}) string {
|
||||
func ToYAML(v interface{}) string {
|
||||
data, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
// Swallow errors inside of a template.
|
||||
|
|
@ -176,13 +175,13 @@ func ToYaml(v interface{}) string {
|
|||
return strings.TrimSuffix(string(data), "\n")
|
||||
}
|
||||
|
||||
// FromYaml converts a YAML document into a map[string]interface{}.
|
||||
// FromYAML converts a YAML document into a map[string]interface{}.
|
||||
//
|
||||
// This is not a general-purpose YAML parser, and will not parse all valid
|
||||
// YAML documents. Additionally, because its intended use is within templates
|
||||
// it tolerates errors. It will insert the returned error message string into
|
||||
// m["Error"] in the returned map.
|
||||
func FromYaml(str string) map[string]interface{} {
|
||||
func FromYAML(str string) map[string]interface{} {
|
||||
m := map[string]interface{}{}
|
||||
|
||||
if err := yaml.Unmarshal([]byte(str), &m); err != nil {
|
||||
|
|
@ -191,11 +190,11 @@ func FromYaml(str string) map[string]interface{} {
|
|||
return m
|
||||
}
|
||||
|
||||
// ToToml takes an interface, marshals it to toml, and returns a string. It will
|
||||
// ToTOML takes an interface, marshals it to toml, and returns a string. It will
|
||||
// always return a string, even on marshal error (empty string).
|
||||
//
|
||||
// This is designed to be called from a template.
|
||||
func ToToml(v interface{}) string {
|
||||
func ToTOML(v interface{}) string {
|
||||
b := bytes.NewBuffer(nil)
|
||||
e := toml.NewEncoder(b)
|
||||
err := e.Encode(v)
|
||||
|
|
@ -205,11 +204,11 @@ func ToToml(v interface{}) string {
|
|||
return b.String()
|
||||
}
|
||||
|
||||
// ToJson takes an interface, marshals it to json, and returns a string. It will
|
||||
// ToJSON takes an interface, marshals it to json, and returns a string. It will
|
||||
// always return a string, even on marshal error (empty string).
|
||||
//
|
||||
// This is designed to be called from a template.
|
||||
func ToJson(v interface{}) string {
|
||||
func ToJSON(v interface{}) string {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
// Swallow errors inside of a template.
|
||||
|
|
@ -218,14 +217,14 @@ func ToJson(v interface{}) string {
|
|||
return string(data)
|
||||
}
|
||||
|
||||
// FromJson converts a JSON document into a map[string]interface{}.
|
||||
// FromJSON converts a JSON document into a map[string]interface{}.
|
||||
//
|
||||
// This is not a general-purpose JSON parser, and will not parse all valid
|
||||
// JSON documents. Additionally, because its intended use is within templates
|
||||
// it tolerates errors. It will insert the returned error message string into
|
||||
// m["Error"] in the returned map.
|
||||
func FromJson(str string) map[string]interface{} {
|
||||
m := map[string]interface{}{}
|
||||
func FromJSON(str string) map[string]interface{} {
|
||||
m := make(map[string]interface{})
|
||||
|
||||
if err := json.Unmarshal([]byte(str), &m); err != nil {
|
||||
m["Error"] = err.Error()
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ func TestLines(t *testing.T) {
|
|||
as.Equal("bar", out[0])
|
||||
}
|
||||
|
||||
func TestToYaml(t *testing.T) {
|
||||
func TestToYAML(t *testing.T) {
|
||||
expect := "foo: bar"
|
||||
v := struct {
|
||||
Foo string `json:"foo"`
|
||||
|
|
@ -105,12 +105,12 @@ func TestToYaml(t *testing.T) {
|
|||
Foo: "bar",
|
||||
}
|
||||
|
||||
if got := ToYaml(v); got != expect {
|
||||
if got := ToYAML(v); got != expect {
|
||||
t.Errorf("Expected %q, got %q", expect, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToToml(t *testing.T) {
|
||||
func TestToTOML(t *testing.T) {
|
||||
expect := "foo = \"bar\"\n"
|
||||
v := struct {
|
||||
Foo string `toml:"foo"`
|
||||
|
|
@ -118,7 +118,7 @@ func TestToToml(t *testing.T) {
|
|||
Foo: "bar",
|
||||
}
|
||||
|
||||
if got := ToToml(v); got != expect {
|
||||
if got := ToTOML(v); got != expect {
|
||||
t.Errorf("Expected %q, got %q", expect, got)
|
||||
}
|
||||
|
||||
|
|
@ -128,19 +128,19 @@ func TestToToml(t *testing.T) {
|
|||
"sail": "white",
|
||||
},
|
||||
}
|
||||
got := ToToml(dict)
|
||||
got := ToTOML(dict)
|
||||
expect = "[mast]\n sail = \"white\"\n"
|
||||
if got != expect {
|
||||
t.Errorf("Expected:\n%s\nGot\n%s\n", expect, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromYaml(t *testing.T) {
|
||||
func TestFromYAML(t *testing.T) {
|
||||
doc := `hello: world
|
||||
one:
|
||||
two: three
|
||||
`
|
||||
dict := FromYaml(doc)
|
||||
dict := FromYAML(doc)
|
||||
if err, ok := dict["Error"]; ok {
|
||||
t.Fatalf("Parse error: %s", err)
|
||||
}
|
||||
|
|
@ -160,13 +160,13 @@ one:
|
|||
- two
|
||||
- three
|
||||
`
|
||||
dict = FromYaml(doc2)
|
||||
dict = FromYAML(doc2)
|
||||
if _, ok := dict["Error"]; !ok {
|
||||
t.Fatal("Expected parser error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestToJson(t *testing.T) {
|
||||
func TestToJSON(t *testing.T) {
|
||||
expect := `{"foo":"bar"}`
|
||||
v := struct {
|
||||
Foo string `json:"foo"`
|
||||
|
|
@ -174,12 +174,12 @@ func TestToJson(t *testing.T) {
|
|||
Foo: "bar",
|
||||
}
|
||||
|
||||
if got := ToJson(v); got != expect {
|
||||
if got := ToJSON(v); got != expect {
|
||||
t.Errorf("Expected %q, got %q", expect, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromJson(t *testing.T) {
|
||||
func TestFromJSON(t *testing.T) {
|
||||
doc := `{
|
||||
"hello": "world",
|
||||
"one": {
|
||||
|
|
@ -187,7 +187,7 @@ func TestFromJson(t *testing.T) {
|
|||
}
|
||||
}
|
||||
`
|
||||
dict := FromJson(doc)
|
||||
dict := FromJSON(doc)
|
||||
if err, ok := dict["Error"]; ok {
|
||||
t.Fatalf("Parse error: %s", err)
|
||||
}
|
||||
|
|
@ -209,7 +209,7 @@ func TestFromJson(t *testing.T) {
|
|||
"three"
|
||||
]
|
||||
`
|
||||
dict = FromJson(doc2)
|
||||
dict = FromJSON(doc2)
|
||||
if _, ok := dict["Error"]; !ok {
|
||||
t.Fatal("Expected parser error")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,286 +0,0 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package chartutil
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/ignore"
|
||||
"k8s.io/helm/pkg/sympath"
|
||||
)
|
||||
|
||||
// Load takes a string name, tries to resolve it to a file or directory, and then loads it.
|
||||
//
|
||||
// This is the preferred way to load a chart. It will discover the chart encoding
|
||||
// and hand off to the appropriate chart reader.
|
||||
//
|
||||
// If a .helmignore file is present, the directory loader will skip loading any files
|
||||
// matching it. But .helmignore is not evaluated when reading out of an archive.
|
||||
func Load(name string) (*chart.Chart, error) {
|
||||
fi, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
if validChart, err := IsChartDir(name); !validChart {
|
||||
return nil, err
|
||||
}
|
||||
return LoadDir(name)
|
||||
}
|
||||
return LoadFile(name)
|
||||
}
|
||||
|
||||
// BufferedFile represents an archive file buffered for later processing.
|
||||
type BufferedFile struct {
|
||||
Name string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// LoadArchive loads from a reader containing a compressed tar archive.
|
||||
func LoadArchive(in io.Reader) (*chart.Chart, error) {
|
||||
unzipped, err := gzip.NewReader(in)
|
||||
if err != nil {
|
||||
return &chart.Chart{}, err
|
||||
}
|
||||
defer unzipped.Close()
|
||||
|
||||
files := []*BufferedFile{}
|
||||
tr := tar.NewReader(unzipped)
|
||||
for {
|
||||
b := bytes.NewBuffer(nil)
|
||||
hd, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return &chart.Chart{}, err
|
||||
}
|
||||
|
||||
if hd.FileInfo().IsDir() {
|
||||
// Use this instead of hd.Typeflag because we don't have to do any
|
||||
// inference chasing.
|
||||
continue
|
||||
}
|
||||
|
||||
// Archive could contain \ if generated on Windows
|
||||
delimiter := "/"
|
||||
if strings.ContainsRune(hd.Name, '\\') {
|
||||
delimiter = "\\"
|
||||
}
|
||||
|
||||
parts := strings.Split(hd.Name, delimiter)
|
||||
n := strings.Join(parts[1:], delimiter)
|
||||
|
||||
// Normalize the path to the / delimiter
|
||||
n = strings.Replace(n, delimiter, "/", -1)
|
||||
|
||||
if parts[0] == "Chart.yaml" {
|
||||
return nil, errors.New("chart yaml not in base directory")
|
||||
}
|
||||
|
||||
if _, err := io.Copy(b, tr); err != nil {
|
||||
return &chart.Chart{}, err
|
||||
}
|
||||
|
||||
files = append(files, &BufferedFile{Name: n, Data: b.Bytes()})
|
||||
b.Reset()
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
return nil, errors.New("no files in chart archive")
|
||||
}
|
||||
|
||||
return LoadFiles(files)
|
||||
}
|
||||
|
||||
// LoadFiles loads from in-memory files.
|
||||
func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
|
||||
c := &chart.Chart{}
|
||||
subcharts := map[string][]*BufferedFile{}
|
||||
|
||||
for _, f := range files {
|
||||
if f.Name == "Chart.yaml" {
|
||||
m, err := UnmarshalChartfile(f.Data)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
c.Metadata = m
|
||||
} else if f.Name == "values.toml" {
|
||||
return c, errors.New("values.toml is illegal as of 2.0.0-alpha.2")
|
||||
} else if f.Name == "values.yaml" {
|
||||
c.Values = f.Data
|
||||
} else if strings.HasPrefix(f.Name, "templates/") {
|
||||
c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data})
|
||||
} else if strings.HasPrefix(f.Name, "charts/") {
|
||||
if filepath.Ext(f.Name) == ".prov" {
|
||||
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
|
||||
continue
|
||||
}
|
||||
cname := strings.TrimPrefix(f.Name, "charts/")
|
||||
if strings.IndexAny(cname, "._") == 0 {
|
||||
// Ignore charts/ that start with . or _.
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(cname, "/", 2)
|
||||
scname := parts[0]
|
||||
subcharts[scname] = append(subcharts[scname], &BufferedFile{Name: cname, Data: f.Data})
|
||||
} else {
|
||||
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that we got a Chart.yaml file
|
||||
if c.Metadata == nil {
|
||||
return c, errors.New("chart metadata (Chart.yaml) missing")
|
||||
}
|
||||
if c.Metadata.Name == "" {
|
||||
return c, errors.New("invalid chart (Chart.yaml): name must not be empty")
|
||||
}
|
||||
|
||||
for n, files := range subcharts {
|
||||
var sc *chart.Chart
|
||||
var err error
|
||||
if strings.IndexAny(n, "_.") == 0 {
|
||||
continue
|
||||
} else if filepath.Ext(n) == ".tgz" {
|
||||
file := files[0]
|
||||
if file.Name != n {
|
||||
return c, errors.Errorf("error unpacking tar in %s: expected %s, got %s", c.Metadata.Name, n, file.Name)
|
||||
}
|
||||
// Untar the chart and add to c.Dependencies
|
||||
b := bytes.NewBuffer(file.Data)
|
||||
sc, err = LoadArchive(b)
|
||||
} else {
|
||||
// We have to trim the prefix off of every file, and ignore any file
|
||||
// that is in charts/, but isn't actually a chart.
|
||||
buff := make([]*BufferedFile, 0, len(files))
|
||||
for _, f := range files {
|
||||
parts := strings.SplitN(f.Name, "/", 2)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
f.Name = parts[1]
|
||||
buff = append(buff, f)
|
||||
}
|
||||
sc, err = LoadFiles(buff)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return c, errors.Wrapf(err, "error unpacking %s in %s", n, c.Metadata.Name)
|
||||
}
|
||||
|
||||
c.Dependencies = append(c.Dependencies, sc)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// LoadFile loads from an archive file.
|
||||
func LoadFile(name string) (*chart.Chart, error) {
|
||||
if fi, err := os.Stat(name); err != nil {
|
||||
return nil, err
|
||||
} else if fi.IsDir() {
|
||||
return nil, errors.New("cannot load a directory")
|
||||
}
|
||||
|
||||
raw, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer raw.Close()
|
||||
|
||||
return LoadArchive(raw)
|
||||
}
|
||||
|
||||
// LoadDir loads from a directory.
|
||||
//
|
||||
// This loads charts only from directories.
|
||||
func LoadDir(dir string) (*chart.Chart, error) {
|
||||
topdir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Just used for errors.
|
||||
c := &chart.Chart{}
|
||||
|
||||
rules := ignore.Empty()
|
||||
ifile := filepath.Join(topdir, ignore.HelmIgnore)
|
||||
if _, err := os.Stat(ifile); err == nil {
|
||||
r, err := ignore.ParseFile(ifile)
|
||||
if err != nil {
|
||||
return c, err
|
||||
}
|
||||
rules = r
|
||||
}
|
||||
rules.AddDefaults()
|
||||
|
||||
files := []*BufferedFile{}
|
||||
topdir += string(filepath.Separator)
|
||||
|
||||
walk := func(name string, fi os.FileInfo, err error) error {
|
||||
n := strings.TrimPrefix(name, topdir)
|
||||
if n == "" {
|
||||
// No need to process top level. Avoid bug with helmignore .* matching
|
||||
// empty names. See issue 1779.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Normalize to / since it will also work on Windows
|
||||
n = filepath.ToSlash(n)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
// Directory-based ignore rules should involve skipping the entire
|
||||
// contents of that directory.
|
||||
if rules.Ignore(n, fi) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// If a .helmignore file matches, skip this file.
|
||||
if rules.Ignore(n, fi) {
|
||||
return nil
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error reading %s", n)
|
||||
}
|
||||
|
||||
files = append(files, &BufferedFile{Name: n, Data: data})
|
||||
return nil
|
||||
}
|
||||
if err = sympath.Walk(topdir, walk); err != nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
return LoadFiles(files)
|
||||
}
|
||||
|
|
@ -18,209 +18,88 @@ package chartutil
|
|||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/version"
|
||||
)
|
||||
|
||||
const (
|
||||
requirementsName = "requirements.yaml"
|
||||
lockfileName = "requirements.lock"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrRequirementsNotFound indicates that a requirements.yaml is not found.
|
||||
ErrRequirementsNotFound = errors.New(requirementsName + " not found")
|
||||
// ErrLockfileNotFound indicates that a requirements.lock is not found.
|
||||
ErrLockfileNotFound = errors.New(lockfileName + " not found")
|
||||
)
|
||||
|
||||
// Dependency describes a chart upon which another chart depends.
|
||||
//
|
||||
// Dependencies can be used to express developer intent, or to capture the state
|
||||
// of a chart.
|
||||
type Dependency struct {
|
||||
// Name is the name of the dependency.
|
||||
//
|
||||
// This must mach the name in the dependency's Chart.yaml.
|
||||
Name string `json:"name"`
|
||||
// Version is the version (range) of this chart.
|
||||
//
|
||||
// A lock file will always produce a single version, while a dependency
|
||||
// may contain a semantic version range.
|
||||
Version string `json:"version,omitempty"`
|
||||
// The URL to the repository.
|
||||
//
|
||||
// Appending `index.yaml` to this string should result in a URL that can be
|
||||
// used to fetch the repository index.
|
||||
Repository string `json:"repository"`
|
||||
// A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled )
|
||||
Condition string `json:"condition,omitempty"`
|
||||
// Tags can be used to group charts for enabling/disabling together
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
// Enabled bool determines if chart should be loaded
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
// ImportValues holds the mapping of source values to parent key to be imported. Each item can be a
|
||||
// string or pair of child/parent sublist items.
|
||||
ImportValues []interface{} `json:"import-values,omitempty"`
|
||||
// Alias usable alias to be used for the chart
|
||||
Alias string `json:"alias,omitempty"`
|
||||
}
|
||||
|
||||
// ErrNoRequirementsFile to detect error condition
|
||||
type ErrNoRequirementsFile error
|
||||
|
||||
// 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.
|
||||
type RequirementsLock struct {
|
||||
// Genderated is the date the lock file was last generated.
|
||||
Generated time.Time `json:"generated"`
|
||||
// Digest is a hash of the requirements file used to generate it.
|
||||
Digest string `json:"digest"`
|
||||
// Dependencies is the list of dependencies that this lock file has locked.
|
||||
Dependencies []*Dependency `json:"dependencies"`
|
||||
}
|
||||
|
||||
// LoadRequirements loads a requirements file from an in-memory chart.
|
||||
func LoadRequirements(c *chart.Chart) (*Requirements, error) {
|
||||
var data []byte
|
||||
for _, f := range c.Files {
|
||||
if f.Name == requirementsName {
|
||||
data = f.Data
|
||||
}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, ErrRequirementsNotFound
|
||||
}
|
||||
r := &Requirements{}
|
||||
return r, yaml.Unmarshal(data, r)
|
||||
}
|
||||
|
||||
// LoadRequirementsLock loads a requirements lock file.
|
||||
func LoadRequirementsLock(c *chart.Chart) (*RequirementsLock, error) {
|
||||
var data []byte
|
||||
for _, f := range c.Files {
|
||||
if f.Name == lockfileName {
|
||||
data = f.Data
|
||||
}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, ErrLockfileNotFound
|
||||
}
|
||||
r := &RequirementsLock{}
|
||||
return r, yaml.Unmarshal(data, r)
|
||||
}
|
||||
|
||||
// ProcessRequirementsConditions disables charts based on condition path value in values
|
||||
func ProcessRequirementsConditions(reqs *Requirements, cvals Values) {
|
||||
var cond string
|
||||
var conds []string
|
||||
if reqs == nil || len(reqs.Dependencies) == 0 {
|
||||
func ProcessRequirementsConditions(reqs *chart.Requirements, cvals Values) {
|
||||
if reqs == nil {
|
||||
return
|
||||
}
|
||||
for _, r := range reqs.Dependencies {
|
||||
var hasTrue, hasFalse bool
|
||||
cond = r.Condition
|
||||
// check for list
|
||||
if len(cond) > 0 {
|
||||
if strings.Contains(cond, ",") {
|
||||
conds = strings.Split(strings.TrimSpace(cond), ",")
|
||||
} else {
|
||||
conds = []string{strings.TrimSpace(cond)}
|
||||
}
|
||||
for _, c := range conds {
|
||||
if len(c) > 0 {
|
||||
// retrieve value
|
||||
vv, err := cvals.PathValue(c)
|
||||
if err == nil {
|
||||
// if not bool, warn
|
||||
if bv, ok := vv.(bool); ok {
|
||||
if bv {
|
||||
hasTrue = true
|
||||
} else {
|
||||
hasFalse = true
|
||||
}
|
||||
} else {
|
||||
log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name)
|
||||
}
|
||||
} else if _, ok := err.(ErrNoValue); !ok {
|
||||
// this is a real error
|
||||
log.Printf("Warning: PathValue returned error %v", err)
|
||||
|
||||
}
|
||||
if vv != nil {
|
||||
// got first value, break loop
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasTrue && hasFalse {
|
||||
r.Enabled = false
|
||||
} else if hasTrue {
|
||||
r.Enabled = true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ProcessRequirementsTags disables charts based on tags in values
|
||||
func ProcessRequirementsTags(reqs *Requirements, cvals Values) {
|
||||
vt, err := cvals.Table("tags")
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
}
|
||||
if reqs == nil || len(reqs.Dependencies) == 0 {
|
||||
return
|
||||
}
|
||||
for _, r := range reqs.Dependencies {
|
||||
if len(r.Tags) > 0 {
|
||||
tags := r.Tags
|
||||
|
||||
var hasTrue, hasFalse bool
|
||||
for _, k := range tags {
|
||||
if b, ok := vt[k]; ok {
|
||||
for _, c := range strings.Split(strings.TrimSpace(r.Condition), ",") {
|
||||
if len(c) > 0 {
|
||||
// retrieve value
|
||||
vv, err := cvals.PathValue(c)
|
||||
if err == nil {
|
||||
// if not bool, warn
|
||||
if bv, ok := b.(bool); ok {
|
||||
if bv, ok := vv.(bool); ok {
|
||||
if bv {
|
||||
hasTrue = true
|
||||
} else {
|
||||
hasFalse = true
|
||||
}
|
||||
} else {
|
||||
log.Printf("Warning: Tag '%s' for chart %s returned non-bool value", k, r.Name)
|
||||
log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name)
|
||||
}
|
||||
} else if _, ok := err.(ErrNoValue); !ok {
|
||||
// this is a real error
|
||||
log.Printf("Warning: PathValue returned error %v", err)
|
||||
}
|
||||
if vv != nil {
|
||||
// got first value, break loop
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasTrue && hasFalse {
|
||||
r.Enabled = false
|
||||
} else if hasTrue || !hasTrue && !hasFalse {
|
||||
r.Enabled = true
|
||||
|
||||
}
|
||||
}
|
||||
if !hasTrue && hasFalse {
|
||||
r.Enabled = false
|
||||
} else if hasTrue {
|
||||
r.Enabled = true
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getAliasDependency(charts []*chart.Chart, aliasChart *Dependency) *chart.Chart {
|
||||
// ProcessRequirementsTags disables charts based on tags in values
|
||||
func ProcessRequirementsTags(reqs *chart.Requirements, cvals Values) {
|
||||
if reqs == nil {
|
||||
return
|
||||
}
|
||||
vt, err := cvals.Table("tags")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, r := range reqs.Dependencies {
|
||||
var hasTrue, hasFalse bool
|
||||
for _, k := range r.Tags {
|
||||
if b, ok := vt[k]; ok {
|
||||
// if not bool, warn
|
||||
if bv, ok := b.(bool); ok {
|
||||
if bv {
|
||||
hasTrue = true
|
||||
} else {
|
||||
hasFalse = true
|
||||
}
|
||||
} else {
|
||||
log.Printf("Warning: Tag '%s' for chart %s returned non-bool value", k, r.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasTrue && hasFalse {
|
||||
r.Enabled = false
|
||||
} else if hasTrue || !hasTrue && !hasFalse {
|
||||
r.Enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getAliasDependency(charts []*chart.Chart, aliasChart *chart.Dependency) *chart.Chart {
|
||||
var chartFound chart.Chart
|
||||
for _, existingChart := range charts {
|
||||
if existingChart == nil {
|
||||
|
|
@ -248,14 +127,7 @@ func getAliasDependency(charts []*chart.Chart, aliasChart *Dependency) *chart.Ch
|
|||
|
||||
// ProcessRequirementsEnabled removes disabled charts from dependencies
|
||||
func ProcessRequirementsEnabled(c *chart.Chart, v []byte) error {
|
||||
reqs, err := LoadRequirements(c)
|
||||
if err != nil {
|
||||
// if not just missing requirements file, return error
|
||||
if nerr, ok := err.(ErrNoRequirementsFile); !ok {
|
||||
return nerr
|
||||
}
|
||||
|
||||
// no requirements to process
|
||||
if c.Requirements == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -265,9 +137,9 @@ func ProcessRequirementsEnabled(c *chart.Chart, v []byte) error {
|
|||
// However, if the dependency is already specified in requirements.yaml
|
||||
// we should not add it, as it would be anyways processed from requirements.yaml
|
||||
|
||||
for _, existingDependency := range c.Dependencies {
|
||||
for _, existingDependency := range c.Dependencies() {
|
||||
var dependencyFound bool
|
||||
for _, req := range reqs.Dependencies {
|
||||
for _, req := range c.Requirements.Dependencies {
|
||||
if existingDependency.Metadata.Name == req.Name && version.IsCompatibleRange(req.Version, existingDependency.Metadata.Version) {
|
||||
dependencyFound = true
|
||||
break
|
||||
|
|
@ -278,18 +150,18 @@ func ProcessRequirementsEnabled(c *chart.Chart, v []byte) error {
|
|||
}
|
||||
}
|
||||
|
||||
for _, req := range reqs.Dependencies {
|
||||
if chartDependency := getAliasDependency(c.Dependencies, req); chartDependency != nil {
|
||||
for _, req := range c.Requirements.Dependencies {
|
||||
if chartDependency := getAliasDependency(c.Dependencies(), req); chartDependency != nil {
|
||||
chartDependencies = append(chartDependencies, chartDependency)
|
||||
}
|
||||
if req.Alias != "" {
|
||||
req.Name = req.Alias
|
||||
}
|
||||
}
|
||||
c.Dependencies = chartDependencies
|
||||
c.SetDependencies(chartDependencies...)
|
||||
|
||||
// set all to true
|
||||
for _, lr := range reqs.Dependencies {
|
||||
for _, lr := range c.Requirements.Dependencies {
|
||||
lr.Enabled = true
|
||||
}
|
||||
cvals, err := CoalesceValues(c, v)
|
||||
|
|
@ -302,34 +174,32 @@ func ProcessRequirementsEnabled(c *chart.Chart, v []byte) error {
|
|||
return err
|
||||
}
|
||||
// flag dependencies as enabled/disabled
|
||||
ProcessRequirementsTags(reqs, cvals)
|
||||
ProcessRequirementsConditions(reqs, cvals)
|
||||
ProcessRequirementsTags(c.Requirements, cvals)
|
||||
ProcessRequirementsConditions(c.Requirements, cvals)
|
||||
// make a map of charts to remove
|
||||
rm := map[string]bool{}
|
||||
for _, r := range reqs.Dependencies {
|
||||
rm := map[string]struct{}{}
|
||||
for _, r := range c.Requirements.Dependencies {
|
||||
if !r.Enabled {
|
||||
// remove disabled chart
|
||||
rm[r.Name] = true
|
||||
rm[r.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
// don't keep disabled charts in new slice
|
||||
cd := []*chart.Chart{}
|
||||
copy(cd, c.Dependencies[:0])
|
||||
for _, n := range c.Dependencies {
|
||||
copy(cd, c.Dependencies()[:0])
|
||||
for _, n := range c.Dependencies() {
|
||||
if _, ok := rm[n.Metadata.Name]; !ok {
|
||||
cd = append(cd, n)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// recursively call self to process sub dependencies
|
||||
for _, t := range cd {
|
||||
err := ProcessRequirementsEnabled(t, yvals)
|
||||
// if its not just missing requirements file, return error
|
||||
if nerr, ok := err.(ErrNoRequirementsFile); !ok && err != nil {
|
||||
return nerr
|
||||
if err := ProcessRequirementsEnabled(t, yvals); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.Dependencies = cd
|
||||
c.SetDependencies(cd...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -361,30 +231,13 @@ func pathToMap(path string, data map[string]interface{}) map[string]interface{}
|
|||
n[i][k] = n[z]
|
||||
}
|
||||
}
|
||||
|
||||
return n[0]
|
||||
}
|
||||
|
||||
// getParents returns a slice of parent charts in reverse order.
|
||||
func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart {
|
||||
if len(out) == 0 {
|
||||
out = []*chart.Chart{c}
|
||||
}
|
||||
for _, ch := range c.Dependencies {
|
||||
if len(ch.Dependencies) > 0 {
|
||||
out = append(out, ch)
|
||||
out = getParents(ch, out)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field.
|
||||
func processImportValues(c *chart.Chart) error {
|
||||
reqs, err := LoadRequirements(c)
|
||||
if err != nil {
|
||||
return err
|
||||
if c.Requirements == nil {
|
||||
return nil
|
||||
}
|
||||
// combine chart values and empty config to get Values
|
||||
cvals, err := CoalesceValues(c, []byte{})
|
||||
|
|
@ -393,45 +246,41 @@ func processImportValues(c *chart.Chart) error {
|
|||
}
|
||||
b := make(map[string]interface{})
|
||||
// import values from each dependency if specified in import-values
|
||||
for _, r := range reqs.Dependencies {
|
||||
if len(r.ImportValues) > 0 {
|
||||
var outiv []interface{}
|
||||
for _, riv := range r.ImportValues {
|
||||
switch iv := riv.(type) {
|
||||
case map[string]interface{}:
|
||||
nm := map[string]string{
|
||||
"child": iv["child"].(string),
|
||||
"parent": iv["parent"].(string),
|
||||
}
|
||||
outiv = append(outiv, nm)
|
||||
s := r.Name + "." + nm["child"]
|
||||
// get child table
|
||||
vv, err := cvals.Table(s)
|
||||
if err != nil {
|
||||
log.Printf("Warning: ImportValues missing table: %v", err)
|
||||
continue
|
||||
}
|
||||
// create value map from child to be merged into parent
|
||||
vm := pathToMap(nm["parent"], vv.AsMap())
|
||||
b = coalesceTables(cvals, vm)
|
||||
case string:
|
||||
nm := map[string]string{
|
||||
"child": "exports." + iv,
|
||||
"parent": ".",
|
||||
}
|
||||
outiv = append(outiv, nm)
|
||||
s := r.Name + "." + nm["child"]
|
||||
vm, err := cvals.Table(s)
|
||||
if err != nil {
|
||||
log.Printf("Warning: ImportValues missing table: %v", err)
|
||||
continue
|
||||
}
|
||||
b = coalesceTables(b, vm.AsMap())
|
||||
for _, r := range c.Requirements.Dependencies {
|
||||
var outiv []interface{}
|
||||
for _, riv := range r.ImportValues {
|
||||
switch iv := riv.(type) {
|
||||
case map[string]interface{}:
|
||||
nm := map[string]string{
|
||||
"child": iv["child"].(string),
|
||||
"parent": iv["parent"].(string),
|
||||
}
|
||||
outiv = append(outiv, nm)
|
||||
// get child table
|
||||
vv, err := cvals.Table(r.Name + "." + nm["child"])
|
||||
if err != nil {
|
||||
log.Printf("Warning: ImportValues missing table: %v", err)
|
||||
continue
|
||||
}
|
||||
// create value map from child to be merged into parent
|
||||
vm := pathToMap(nm["parent"], vv.AsMap())
|
||||
b = coalesceTables(cvals, vm)
|
||||
case string:
|
||||
nm := map[string]string{
|
||||
"child": "exports." + iv,
|
||||
"parent": ".",
|
||||
}
|
||||
outiv = append(outiv, nm)
|
||||
vm, err := cvals.Table(r.Name + "." + nm["child"])
|
||||
if err != nil {
|
||||
log.Printf("Warning: ImportValues missing table: %v", err)
|
||||
continue
|
||||
}
|
||||
b = coalesceTables(b, vm.AsMap())
|
||||
}
|
||||
// set our formatted import values
|
||||
r.ImportValues = outiv
|
||||
}
|
||||
// set our formatted import values
|
||||
r.ImportValues = outiv
|
||||
}
|
||||
b = coalesceTables(b, cvals)
|
||||
y, err := yaml.Marshal(b)
|
||||
|
|
@ -447,10 +296,11 @@ func processImportValues(c *chart.Chart) error {
|
|||
|
||||
// ProcessRequirementsImportValues imports specified chart values from child to parent.
|
||||
func ProcessRequirementsImportValues(c *chart.Chart) error {
|
||||
pc := getParents(c, nil)
|
||||
for i := len(pc) - 1; i >= 0; i-- {
|
||||
processImportValues(pc[i])
|
||||
for _, d := range c.Dependencies() {
|
||||
// recurse
|
||||
if err := ProcessRequirementsImportValues(d); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return processImportValues(c)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,13 @@ import (
|
|||
|
||||
"strconv"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/version"
|
||||
)
|
||||
|
||||
func TestLoadRequirements(t *testing.T) {
|
||||
c, err := Load("testdata/frobnitz")
|
||||
c, err := loader.Load("testdata/frobnitz")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
|
|
@ -33,158 +34,89 @@ func TestLoadRequirements(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLoadRequirementsLock(t *testing.T) {
|
||||
c, err := Load("testdata/frobnitz")
|
||||
c, err := loader.Load("testdata/frobnitz")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
verifyRequirementsLock(t, c)
|
||||
}
|
||||
func TestRequirementsTagsNonValue(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
|
||||
func TestRequirementsEnabled(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
v []byte
|
||||
e []string // expected charts including duplicates in alphanumeric order
|
||||
}{{
|
||||
"tags with no effect",
|
||||
[]byte("tags:\n nothinguseful: false\n\n"),
|
||||
[]string{"parentchart", "subchart1", "subcharta", "subchartb"},
|
||||
}, {
|
||||
"tags with no effect",
|
||||
[]byte("tags:\n nothinguseful: false\n\n"),
|
||||
[]string{"parentchart", "subchart1", "subcharta", "subchartb"},
|
||||
}, {
|
||||
"tags disabling a group",
|
||||
[]byte("tags:\n front-end: false\n\n"),
|
||||
[]string{"parentchart"},
|
||||
}, {
|
||||
"tags disabling a group and enabling a different group",
|
||||
[]byte("tags:\n front-end: false\n\n back-end: true\n"),
|
||||
[]string{"parentchart", "subchart2", "subchartb", "subchartc"},
|
||||
}, {
|
||||
"tags disabling only children, children still enabled since tag front-end=true in values.yaml",
|
||||
[]byte("tags:\n subcharta: false\n\n subchartb: false\n"),
|
||||
[]string{"parentchart", "subchart1", "subcharta", "subchartb"},
|
||||
}, {
|
||||
"tags disabling all parents/children with additional tag re-enabling a parent",
|
||||
[]byte("tags:\n front-end: false\n\n subchart1: true\n\n back-end: false\n"),
|
||||
[]string{"parentchart", "subchart1"},
|
||||
}, {
|
||||
"tags with no effect",
|
||||
[]byte("subchart1:\n nothinguseful: false\n\n"),
|
||||
[]string{"parentchart", "subchart1", "subcharta", "subchartb"},
|
||||
}, {
|
||||
"conditions enabling the parent charts, but back-end (b, c) is still disabled via values.yaml",
|
||||
[]byte("subchart1:\n enabled: true\nsubchart2:\n enabled: true\n"),
|
||||
[]string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb"},
|
||||
}, {
|
||||
"conditions disabling the parent charts, effectively disabling children",
|
||||
[]byte("subchart1:\n enabled: false\nsubchart2:\n enabled: false\n"),
|
||||
[]string{"parentchart"},
|
||||
}, {
|
||||
"conditions a child using the second condition path of child's condition",
|
||||
[]byte("subchart1:\n subcharta:\n enabled: false\n"),
|
||||
[]string{"parentchart", "subchart1", "subchartb"},
|
||||
}, {
|
||||
"tags enabling a parent/child group with condition disabling one child",
|
||||
[]byte("subchartc:\n enabled: false\ntags:\n back-end: true\n"),
|
||||
[]string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb", "subchartb"},
|
||||
}, {
|
||||
"tags will not enable a child if parent is explicitly disabled with condition",
|
||||
[]byte("subchart1:\n enabled: false\ntags:\n front-end: true\n"),
|
||||
[]string{"parentchart"},
|
||||
}}
|
||||
|
||||
for _, tc := range tests {
|
||||
c, err := loader.Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
verifyRequirementsEnabled(t, c, tc.v, tc.e)
|
||||
})
|
||||
}
|
||||
// tags with no effect
|
||||
v := []byte("tags:\n nothinguseful: false\n\n")
|
||||
// expected charts including duplicates in alphanumeric order
|
||||
e := []string{"parentchart", "subchart1", "subcharta", "subchartb"}
|
||||
|
||||
verifyRequirementsEnabled(t, c, v, e)
|
||||
}
|
||||
func TestRequirementsTagsDisabledL1(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
// tags disabling a group
|
||||
v := []byte("tags:\n front-end: false\n\n")
|
||||
// expected charts including duplicates in alphanumeric order
|
||||
e := []string{"parentchart"}
|
||||
|
||||
verifyRequirementsEnabled(t, c, v, e)
|
||||
}
|
||||
func TestRequirementsTagsEnabledL1(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
// tags disabling a group and enabling a different group
|
||||
v := []byte("tags:\n front-end: false\n\n back-end: true\n")
|
||||
// expected charts including duplicates in alphanumeric order
|
||||
e := []string{"parentchart", "subchart2", "subchartb", "subchartc"}
|
||||
|
||||
verifyRequirementsEnabled(t, c, v, e)
|
||||
}
|
||||
|
||||
func TestRequirementsTagsDisabledL2(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
// tags disabling only children, children still enabled since tag front-end=true in values.yaml
|
||||
v := []byte("tags:\n subcharta: false\n\n subchartb: false\n")
|
||||
// expected charts including duplicates in alphanumeric order
|
||||
e := []string{"parentchart", "subchart1", "subcharta", "subchartb"}
|
||||
|
||||
verifyRequirementsEnabled(t, c, v, e)
|
||||
}
|
||||
func TestRequirementsTagsDisabledL1Mixed(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
// tags disabling all parents/children with additional tag re-enabling a parent
|
||||
v := []byte("tags:\n front-end: false\n\n subchart1: true\n\n back-end: false\n")
|
||||
// expected charts including duplicates in alphanumeric order
|
||||
e := []string{"parentchart", "subchart1"}
|
||||
|
||||
verifyRequirementsEnabled(t, c, v, e)
|
||||
}
|
||||
func TestRequirementsConditionsNonValue(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
// tags with no effect
|
||||
v := []byte("subchart1:\n nothinguseful: false\n\n")
|
||||
// expected charts including duplicates in alphanumeric order
|
||||
e := []string{"parentchart", "subchart1", "subcharta", "subchartb"}
|
||||
|
||||
verifyRequirementsEnabled(t, c, v, e)
|
||||
}
|
||||
func TestRequirementsConditionsEnabledL1Both(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
// conditions enabling the parent charts, but back-end (b, c) is still disabled via values.yaml
|
||||
v := []byte("subchart1:\n enabled: true\nsubchart2:\n enabled: true\n")
|
||||
// expected charts including duplicates in alphanumeric order
|
||||
e := []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb"}
|
||||
|
||||
verifyRequirementsEnabled(t, c, v, e)
|
||||
}
|
||||
func TestRequirementsConditionsDisabledL1Both(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
// conditions disabling the parent charts, effectively disabling children
|
||||
v := []byte("subchart1:\n enabled: false\nsubchart2:\n enabled: false\n")
|
||||
// expected charts including duplicates in alphanumeric order
|
||||
e := []string{"parentchart"}
|
||||
|
||||
verifyRequirementsEnabled(t, c, v, e)
|
||||
}
|
||||
|
||||
func TestRequirementsConditionsSecond(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
// conditions a child using the second condition path of child's condition
|
||||
v := []byte("subchart1:\n subcharta:\n enabled: false\n")
|
||||
// expected charts including duplicates in alphanumeric order
|
||||
e := []string{"parentchart", "subchart1", "subchartb"}
|
||||
|
||||
verifyRequirementsEnabled(t, c, v, e)
|
||||
}
|
||||
func TestRequirementsCombinedDisabledL2(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
// tags enabling a parent/child group with condition disabling one child
|
||||
v := []byte("subchartc:\n enabled: false\ntags:\n back-end: true\n")
|
||||
// expected charts including duplicates in alphanumeric order
|
||||
e := []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb", "subchartb"}
|
||||
|
||||
verifyRequirementsEnabled(t, c, v, e)
|
||||
}
|
||||
func TestRequirementsCombinedDisabledL1(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
// tags will not enable a child if parent is explicitly disabled with condition
|
||||
v := []byte("subchart1:\n enabled: false\ntags:\n front-end: true\n")
|
||||
// expected charts including duplicates in alphanumeric order
|
||||
e := []string{"parentchart"}
|
||||
|
||||
verifyRequirementsEnabled(t, c, v, e)
|
||||
}
|
||||
|
||||
func verifyRequirementsEnabled(t *testing.T, c *chart.Chart, v []byte, e []string) {
|
||||
out := []*chart.Chart{}
|
||||
err := ProcessRequirementsEnabled(c, v)
|
||||
if err != nil {
|
||||
if err := ProcessRequirementsEnabled(c, v); err != nil {
|
||||
t.Errorf("Error processing enabled requirements %v", err)
|
||||
}
|
||||
out = extractCharts(c, out)
|
||||
|
||||
out := extractCharts(c, nil)
|
||||
// build list of chart names
|
||||
p := []string{}
|
||||
var p []string
|
||||
for _, r := range out {
|
||||
p = append(p, r.Metadata.Name)
|
||||
p = append(p, r.Name())
|
||||
}
|
||||
//sort alphanumeric and compare to expectations
|
||||
sort.Strings(p)
|
||||
|
|
@ -201,23 +133,21 @@ func verifyRequirementsEnabled(t *testing.T, c *chart.Chart, v []byte, e []strin
|
|||
|
||||
// extractCharts recursively searches chart dependencies returning all charts found
|
||||
func extractCharts(c *chart.Chart, out []*chart.Chart) []*chart.Chart {
|
||||
|
||||
if len(c.Metadata.Name) > 0 {
|
||||
if len(c.Name()) > 0 {
|
||||
out = append(out, c)
|
||||
}
|
||||
for _, d := range c.Dependencies {
|
||||
for _, d := range c.Dependencies() {
|
||||
out = extractCharts(d, out)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestProcessRequirementsImportValues(t *testing.T) {
|
||||
c, err := Load("testdata/subpop")
|
||||
c, err := loader.Load("testdata/subpop")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
|
||||
v := []byte{}
|
||||
|
||||
e := make(map[string]string)
|
||||
|
||||
e["imported-chart1.SC1bool"] = "true"
|
||||
|
|
@ -279,17 +209,16 @@ func TestProcessRequirementsImportValues(t *testing.T) {
|
|||
e["SCBexported2A"] = "blaster"
|
||||
e["global.SC1exported2.all.SC1exported3"] = "SC1expstr"
|
||||
|
||||
verifyRequirementsImportValues(t, c, v, e)
|
||||
verifyRequirementsImportValues(t, c, e)
|
||||
}
|
||||
func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v []byte, e map[string]string) {
|
||||
|
||||
err := ProcessRequirementsImportValues(c)
|
||||
if err != nil {
|
||||
t.Errorf("Error processing import values requirements %v", err)
|
||||
func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, e map[string]string) {
|
||||
if err := ProcessRequirementsImportValues(c); err != nil {
|
||||
t.Fatalf("Error processing import values requirements %v", err)
|
||||
}
|
||||
cc, err := ReadValues(c.Values)
|
||||
if err != nil {
|
||||
t.Errorf("Error reading import values %v", err)
|
||||
t.Fatalf("Error reading import values %v", err)
|
||||
}
|
||||
for kk, vv := range e {
|
||||
pv, err := cc.PathValue(kk)
|
||||
|
|
@ -317,182 +246,203 @@ func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v []byte, e ma
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAliasDependency(t *testing.T) {
|
||||
c, err := Load("testdata/frobnitz")
|
||||
c, err := loader.Load("testdata/frobnitz")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
req, err := LoadRequirements(c)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load requirement for testdata: %s", err)
|
||||
}
|
||||
|
||||
req := c.Requirements
|
||||
|
||||
if len(req.Dependencies) == 0 {
|
||||
t.Fatalf("There are no requirements to test")
|
||||
}
|
||||
|
||||
// Success case
|
||||
aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0])
|
||||
aliasChart := getAliasDependency(c.Dependencies(), req.Dependencies[0])
|
||||
if aliasChart == nil {
|
||||
t.Fatalf("Failed to get dependency chart for alias %s", req.Dependencies[0].Name)
|
||||
}
|
||||
if req.Dependencies[0].Alias != "" {
|
||||
if aliasChart.Metadata.Name != req.Dependencies[0].Alias {
|
||||
t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Alias, aliasChart.Metadata.Name)
|
||||
if aliasChart.Name() != req.Dependencies[0].Alias {
|
||||
t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Alias, aliasChart.Name())
|
||||
}
|
||||
} else if aliasChart.Metadata.Name != req.Dependencies[0].Name {
|
||||
t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Name, aliasChart.Metadata.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())
|
||||
}
|
||||
|
||||
if req.Dependencies[0].Version != "" {
|
||||
if !version.IsCompatibleRange(req.Dependencies[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 {
|
||||
t.Fatalf("expected no chart but got %s", aliasChart.Metadata.Name)
|
||||
if aliasChart := getAliasDependency(c.Dependencies(), req.Dependencies[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) {
|
||||
t.Fatalf("Dependency chart version which is not in the compatible range should cause a failure other than a success ")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDependentChartAliases(t *testing.T) {
|
||||
c, err := Load("testdata/dependent-chart-alias")
|
||||
c, err := loader.Load("testdata/dependent-chart-alias")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
|
||||
if len(c.Dependencies) == 0 {
|
||||
if len(c.Dependencies()) == 0 {
|
||||
t.Fatal("There are no dependencies to run this test")
|
||||
}
|
||||
|
||||
origLength := len(c.Dependencies)
|
||||
origLength := len(c.Dependencies())
|
||||
if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
|
||||
t.Fatalf("Expected no errors but got %q", err)
|
||||
}
|
||||
|
||||
if len(c.Dependencies) == origLength {
|
||||
if len(c.Dependencies()) == origLength {
|
||||
t.Fatal("Expected alias dependencies to be added, but did not got that")
|
||||
}
|
||||
|
||||
reqmts, err := LoadRequirements(c)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot load requirements for test chart, %v", err)
|
||||
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(reqmts.Dependencies) {
|
||||
t.Fatalf("Expected number of chart dependencies %d, but got %d", len(reqmts.Dependencies), len(c.Dependencies))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDependentChartWithSubChartsAbsentInRequirements(t *testing.T) {
|
||||
c, err := Load("testdata/dependent-chart-no-requirements-yaml")
|
||||
c, err := loader.Load("testdata/dependent-chart-no-requirements-yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
|
||||
if len(c.Dependencies) != 2 {
|
||||
t.Fatalf("Expected 2 dependencies for this chart, but got %d", len(c.Dependencies))
|
||||
if len(c.Dependencies()) != 2 {
|
||||
t.Fatalf("Expected 2 dependencies for this chart, but got %d", len(c.Dependencies()))
|
||||
}
|
||||
|
||||
origLength := len(c.Dependencies)
|
||||
origLength := len(c.Dependencies())
|
||||
if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
|
||||
t.Fatalf("Expected no errors but got %q", err)
|
||||
}
|
||||
|
||||
if len(c.Dependencies) != origLength {
|
||||
if len(c.Dependencies()) != origLength {
|
||||
t.Fatal("Expected no changes in dependencies to be, but did something got changed")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDependentChartWithSubChartsHelmignore(t *testing.T) {
|
||||
if _, err := Load("testdata/dependent-chart-helmignore"); err != nil {
|
||||
if _, err := loader.Load("testdata/dependent-chart-helmignore"); err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependentChartsWithSubChartsSymlink(t *testing.T) {
|
||||
c, err := Load("testdata/joonix")
|
||||
c, err := loader.Load("testdata/joonix")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
if c.Metadata.Name != "joonix" {
|
||||
t.Fatalf("Unexpected chart name: %s", c.Metadata.Name)
|
||||
if c.Name() != "joonix" {
|
||||
t.Fatalf("Unexpected chart name: %s", c.Name())
|
||||
}
|
||||
if n := len(c.Dependencies); n != 1 {
|
||||
if n := len(c.Dependencies()); n != 1 {
|
||||
t.Fatalf("Expected 1 dependency for this chart, but got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependentChartsWithSubchartsAllSpecifiedInRequirements(t *testing.T) {
|
||||
c, err := Load("testdata/dependent-chart-with-all-in-requirements-yaml")
|
||||
c, err := loader.Load("testdata/dependent-chart-with-all-in-requirements-yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
|
||||
if len(c.Dependencies) == 0 {
|
||||
if len(c.Dependencies()) == 0 {
|
||||
t.Fatal("There are no dependencies to run this test")
|
||||
}
|
||||
|
||||
origLength := len(c.Dependencies)
|
||||
origLength := len(c.Dependencies())
|
||||
if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
|
||||
t.Fatalf("Expected no errors but got %q", err)
|
||||
}
|
||||
|
||||
if len(c.Dependencies) != origLength {
|
||||
if len(c.Dependencies()) != origLength {
|
||||
t.Fatal("Expected no changes in dependencies to be, but did something got changed")
|
||||
}
|
||||
|
||||
reqmts, err := LoadRequirements(c)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot load requirements for test chart, %v", err)
|
||||
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(reqmts.Dependencies) {
|
||||
t.Fatalf("Expected number of chart dependencies %d, but got %d", len(reqmts.Dependencies), len(c.Dependencies))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDependentChartsWithSomeSubchartsSpecifiedInRequirements(t *testing.T) {
|
||||
c, err := Load("testdata/dependent-chart-with-mixed-requirements-yaml")
|
||||
c, err := loader.Load("testdata/dependent-chart-with-mixed-requirements-yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load testdata: %s", err)
|
||||
}
|
||||
|
||||
if len(c.Dependencies) == 0 {
|
||||
if len(c.Dependencies()) == 0 {
|
||||
t.Fatal("There are no dependencies to run this test")
|
||||
}
|
||||
|
||||
origLength := len(c.Dependencies)
|
||||
origLength := len(c.Dependencies())
|
||||
if err := ProcessRequirementsEnabled(c, c.Values); err != nil {
|
||||
t.Fatalf("Expected no errors but got %q", err)
|
||||
}
|
||||
|
||||
if len(c.Dependencies) != origLength {
|
||||
if len(c.Dependencies()) != origLength {
|
||||
t.Fatal("Expected no changes in dependencies to be, but did something got changed")
|
||||
}
|
||||
|
||||
reqmts, err := LoadRequirements(c)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot load requirements for test chart, %v", err)
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
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]
|
||||
if d.Name != tt.Name {
|
||||
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
|
||||
}
|
||||
if d.Version != tt.Version {
|
||||
t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version)
|
||||
}
|
||||
if d.Repository != tt.Repository {
|
||||
t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
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]
|
||||
if d.Name != tt.Name {
|
||||
t.Errorf("Expected dependency named %q, got %q", tt.Name, d.Name)
|
||||
}
|
||||
if d.Version != tt.Version {
|
||||
t.Errorf("Expected dependency named %q to have version %q, got %q", tt.Name, tt.Version, d.Version)
|
||||
}
|
||||
if d.Repository != tt.Repository {
|
||||
t.Errorf("Expected dependency named %q to have repository %q, got %q", tt.Name, tt.Repository, d.Repository)
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.Dependencies) <= len(reqmts.Dependencies) {
|
||||
t.Fatalf("Expected more dependencies than specified in requirements.yaml(%d), but got %d", len(reqmts.Dependencies), len(c.Dependencies))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import (
|
|||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
)
|
||||
|
||||
var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")
|
||||
|
|
@ -35,7 +35,7 @@ var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")
|
|||
// SaveDir saves a chart as files in a directory.
|
||||
func SaveDir(c *chart.Chart, dest string) error {
|
||||
// Create the chart directory
|
||||
outdir := filepath.Join(dest, c.Metadata.Name)
|
||||
outdir := filepath.Join(dest, c.Name())
|
||||
if err := os.Mkdir(outdir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -83,7 +83,7 @@ func SaveDir(c *chart.Chart, dest string) error {
|
|||
|
||||
// Save dependencies
|
||||
base := filepath.Join(outdir, ChartsDir)
|
||||
for _, dep := range c.Dependencies {
|
||||
for _, dep := range c.Dependencies() {
|
||||
// Here, we write each dependency as a tar file.
|
||||
if _, err := Save(dep, base); err != nil {
|
||||
return err
|
||||
|
|
@ -158,7 +158,7 @@ func Save(c *chart.Chart, outDir string) (string, error) {
|
|||
}
|
||||
|
||||
func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
|
||||
base := filepath.Join(prefix, c.Metadata.Name)
|
||||
base := filepath.Join(prefix, c.Name())
|
||||
|
||||
// Save Chart.yaml
|
||||
cdata, err := yaml.Marshal(c.Metadata)
|
||||
|
|
@ -193,7 +193,7 @@ func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
|
|||
}
|
||||
|
||||
// Save dependencies
|
||||
for _, dep := range c.Dependencies {
|
||||
for _, dep := range c.Dependencies() {
|
||||
if err := writeTarContents(out, dep, base+"/charts"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -212,8 +212,6 @@ func writeToTar(out *tar.Writer, name string, body []byte) error {
|
|||
if err := out.WriteHeader(h); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := out.Write(body); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
_, err := out.Write(body)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
)
|
||||
|
||||
func TestSave(t *testing.T) {
|
||||
|
|
@ -55,13 +56,13 @@ func TestSave(t *testing.T) {
|
|||
t.Fatalf("Expected %q to end with .tgz", where)
|
||||
}
|
||||
|
||||
c2, err := LoadFile(where)
|
||||
c2, err := loader.LoadFile(where)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c2.Metadata.Name != c.Metadata.Name {
|
||||
t.Fatalf("Expected chart archive to have %q, got %q", c.Metadata.Name, c2.Metadata.Name)
|
||||
if c2.Name() != c.Name() {
|
||||
t.Fatalf("Expected chart archive to have %q, got %q", c.Name(), c2.Name())
|
||||
}
|
||||
if !bytes.Equal(c2.Values, c.Values) {
|
||||
t.Fatal("Values data did not match")
|
||||
|
|
@ -93,13 +94,13 @@ func TestSaveDir(t *testing.T) {
|
|||
t.Fatalf("Failed to save: %s", err)
|
||||
}
|
||||
|
||||
c2, err := LoadDir(tmp + "/ahab")
|
||||
c2, err := loader.LoadDir(tmp + "/ahab")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if c2.Metadata.Name != c.Metadata.Name {
|
||||
t.Fatalf("Expected chart archive to have %q, got %q", c.Metadata.Name, c2.Metadata.Name)
|
||||
if c2.Name() != c.Name() {
|
||||
t.Fatalf("Expected chart archive to have %q, got %q", c.Name(), c2.Name())
|
||||
}
|
||||
if !bytes.Equal(c2.Values, c.Values) {
|
||||
t.Fatal("Values data did not match")
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
)
|
||||
|
||||
// ErrNoTable indicates that a chart does not have a matching table.
|
||||
|
|
@ -59,11 +59,10 @@ func (v Values) YAML() (string, error) {
|
|||
//
|
||||
// An ErrNoTable is returned if the table does not exist.
|
||||
func (v Values) Table(name string) (Values, error) {
|
||||
names := strings.Split(name, ".")
|
||||
table := v
|
||||
var err error
|
||||
|
||||
for _, n := range names {
|
||||
for _, n := range strings.Split(name, ".") {
|
||||
table, err = tableLookup(table, n)
|
||||
if err != nil {
|
||||
return table, err
|
||||
|
|
@ -110,7 +109,7 @@ func tableLookup(v Values, simple string) (Values, error) {
|
|||
}
|
||||
|
||||
var e ErrNoTable = errors.Errorf("no table named %q", simple)
|
||||
return map[string]interface{}{}, e
|
||||
return Values{}, e
|
||||
}
|
||||
|
||||
// ReadValues will parse YAML byte data into a Values.
|
||||
|
|
@ -141,23 +140,23 @@ func ReadValuesFile(filename string) (Values, error) {
|
|||
// - A chart has access to all of the variables for it, as well as all of
|
||||
// the values destined for its dependencies.
|
||||
func CoalesceValues(chrt *chart.Chart, vals []byte) (Values, error) {
|
||||
var err error
|
||||
cvals := Values{}
|
||||
// Parse values if not nil. We merge these at the top level because
|
||||
// the passed-in values are in the same namespace as the parent chart.
|
||||
if vals != nil {
|
||||
evals, err := ReadValues(vals)
|
||||
if err != nil {
|
||||
return cvals, err
|
||||
}
|
||||
cvals, err = coalesce(chrt, evals)
|
||||
cvals, err = ReadValues(vals)
|
||||
if err != nil {
|
||||
return cvals, err
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
cvals, err = coalesceDeps(chrt, cvals)
|
||||
return cvals, err
|
||||
cvals, err = coalesce(chrt, cvals)
|
||||
if err != nil {
|
||||
return cvals, err
|
||||
}
|
||||
|
||||
return coalesceDeps(chrt, cvals)
|
||||
}
|
||||
|
||||
// coalesce coalesces the dest values and the chart values, giving priority to the dest values.
|
||||
|
|
@ -175,14 +174,14 @@ func coalesce(ch *chart.Chart, dest map[string]interface{}) (map[string]interfac
|
|||
|
||||
// coalesceDeps coalesces the dependencies of the given chart.
|
||||
func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) {
|
||||
for _, subchart := range chrt.Dependencies {
|
||||
if c, ok := dest[subchart.Metadata.Name]; !ok {
|
||||
for _, subchart := range chrt.Dependencies() {
|
||||
if c, ok := dest[subchart.Name()]; !ok {
|
||||
// If dest doesn't already have the key, create it.
|
||||
dest[subchart.Metadata.Name] = map[string]interface{}{}
|
||||
dest[subchart.Name()] = make(map[string]interface{})
|
||||
} else if !istable(c) {
|
||||
return dest, errors.Errorf("type mismatch on %s: %t", subchart.Metadata.Name, c)
|
||||
return dest, errors.Errorf("type mismatch on %s: %t", subchart.Name(), c)
|
||||
}
|
||||
if dv, ok := dest[subchart.Metadata.Name]; ok {
|
||||
if dv, ok := dest[subchart.Name()]; ok {
|
||||
dvmap := dv.(map[string]interface{})
|
||||
|
||||
// Get globals out of dest and merge them into dvmap.
|
||||
|
|
@ -190,7 +189,7 @@ func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]in
|
|||
|
||||
var err error
|
||||
// Now coalesce the rest of the values.
|
||||
dest[subchart.Metadata.Name], err = coalesce(subchart, dvmap)
|
||||
dest[subchart.Name()], err = coalesce(subchart, dvmap)
|
||||
if err != nil {
|
||||
return dest, err
|
||||
}
|
||||
|
|
@ -206,14 +205,14 @@ func coalesceGlobals(dest, src map[string]interface{}) map[string]interface{} {
|
|||
var dg, sg map[string]interface{}
|
||||
|
||||
if destglob, ok := dest[GlobalKey]; !ok {
|
||||
dg = map[string]interface{}{}
|
||||
dg = make(map[string]interface{})
|
||||
} else if dg, ok = destglob.(map[string]interface{}); !ok {
|
||||
log.Printf("warning: skipping globals because destination %s is not a table.", GlobalKey)
|
||||
return dg
|
||||
}
|
||||
|
||||
if srcglob, ok := src[GlobalKey]; !ok {
|
||||
sg = map[string]interface{}{}
|
||||
sg = make(map[string]interface{})
|
||||
} else if sg, ok = srcglob.(map[string]interface{}); !ok {
|
||||
log.Printf("warning: skipping globals because source %s is not a table.", GlobalKey)
|
||||
return dg
|
||||
|
|
@ -340,19 +339,8 @@ type ReleaseOptions struct {
|
|||
|
||||
// ToRenderValues composes the struct from the data coming from the Releases, Charts and Values files
|
||||
//
|
||||
// WARNING: This function is deprecated for Helm > 2.1.99 Use ToRenderValuesCaps() instead. It will
|
||||
// remain in the codebase to stay SemVer compliant.
|
||||
//
|
||||
// In Helm 3.0, this will be changed to accept Capabilities as a fourth parameter.
|
||||
func ToRenderValues(chrt *chart.Chart, chrtVals []byte, options ReleaseOptions) (Values, error) {
|
||||
caps := &Capabilities{APIVersions: DefaultVersionSet}
|
||||
return ToRenderValuesCaps(chrt, chrtVals, options, caps)
|
||||
}
|
||||
|
||||
// ToRenderValuesCaps composes the struct from the data coming from the Releases, Charts and Values files
|
||||
//
|
||||
// This takes both ReleaseOptions and Capabilities to merge into the render values.
|
||||
func ToRenderValuesCaps(chrt *chart.Chart, chrtVals []byte, options ReleaseOptions, caps *Capabilities) (Values, error) {
|
||||
func ToRenderValues(chrt *chart.Chart, chrtVals []byte, options ReleaseOptions, caps *Capabilities) (Values, error) {
|
||||
|
||||
top := map[string]interface{}{
|
||||
"Release": map[string]interface{}{
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ import (
|
|||
|
||||
kversion "k8s.io/apimachinery/pkg/version"
|
||||
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/version"
|
||||
)
|
||||
|
||||
|
|
@ -89,16 +90,14 @@ where:
|
|||
Metadata: &chart.Metadata{Name: "test"},
|
||||
Templates: []*chart.File{},
|
||||
Values: []byte(chartValues),
|
||||
Dependencies: []*chart.Chart{
|
||||
{
|
||||
Metadata: &chart.Metadata{Name: "where"},
|
||||
Values: []byte{},
|
||||
},
|
||||
},
|
||||
Files: []*chart.File{
|
||||
{Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")},
|
||||
},
|
||||
}
|
||||
c.AddDependency(&chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "where"},
|
||||
Values: []byte{},
|
||||
})
|
||||
v := []byte(overideValues)
|
||||
|
||||
o := ReleaseOptions{
|
||||
|
|
@ -112,7 +111,7 @@ where:
|
|||
KubeVersion: &kversion.Info{Major: "1"},
|
||||
}
|
||||
|
||||
res, err := ToRenderValuesCaps(c, v, o, caps)
|
||||
res, err := ToRenderValues(c, v, o, caps)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -259,10 +258,8 @@ func matchValues(t *testing.T, data map[string]interface{}) {
|
|||
func ttpl(tpl string, v map[string]interface{}) (string, error) {
|
||||
var b bytes.Buffer
|
||||
tt := template.Must(template.New("t").Parse(tpl))
|
||||
if err := tt.Execute(&b, v); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return b.String(), nil
|
||||
err := tt.Execute(&b, v)
|
||||
return b.String(), err
|
||||
}
|
||||
|
||||
// ref: http://www.yaml.org/spec/1.2/spec.html#id2803362
|
||||
|
|
@ -293,7 +290,7 @@ pequod:
|
|||
|
||||
func TestCoalesceValues(t *testing.T) {
|
||||
tchart := "testdata/moby"
|
||||
c, err := LoadDir(tchart)
|
||||
c, err := loader.LoadDir(tchart)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,9 +30,10 @@ import (
|
|||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/getter"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
"k8s.io/helm/pkg/repo"
|
||||
"k8s.io/helm/pkg/resolver"
|
||||
|
|
@ -72,16 +73,13 @@ func (m *Manager) Build() error {
|
|||
|
||||
// If a lock file is found, run a build from that. Otherwise, just do
|
||||
// an update.
|
||||
lock, err := chartutil.LoadRequirementsLock(c)
|
||||
if err != nil {
|
||||
lock := c.RequirementsLock
|
||||
if lock == nil {
|
||||
return m.Update()
|
||||
}
|
||||
|
||||
// A lock must accompany a requirements.yaml file.
|
||||
req, err := chartutil.LoadRequirements(c)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "requirements.yaml cannot be opened")
|
||||
}
|
||||
req := c.Requirements
|
||||
if sum, err := resolver.HashReq(req); err != nil || sum != lock.Digest {
|
||||
return errors.New("requirements.lock is out of sync with requirements.yaml")
|
||||
}
|
||||
|
|
@ -119,13 +117,9 @@ func (m *Manager) Update() error {
|
|||
|
||||
// If no requirements file is found, we consider this a successful
|
||||
// completion.
|
||||
req, err := chartutil.LoadRequirements(c)
|
||||
if err != nil {
|
||||
if err == chartutil.ErrRequirementsNotFound {
|
||||
fmt.Fprintf(m.Out, "No requirements found in %s/charts.\n", m.ChartPath)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
req := c.Requirements
|
||||
if req == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash requirements.yaml
|
||||
|
|
@ -161,8 +155,8 @@ func (m *Manager) Update() error {
|
|||
}
|
||||
|
||||
// If the lock file hasn't changed, don't write a new one.
|
||||
oldLock, err := chartutil.LoadRequirementsLock(c)
|
||||
if err == nil && oldLock.Digest == lock.Digest {
|
||||
oldLock := c.RequirementsLock
|
||||
if oldLock != nil && oldLock.Digest == lock.Digest {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -176,13 +170,13 @@ func (m *Manager) loadChartDir() (*chart.Chart, error) {
|
|||
} else if !fi.IsDir() {
|
||||
return nil, errors.New("only unpacked charts can be updated")
|
||||
}
|
||||
return chartutil.LoadDir(m.ChartPath)
|
||||
return loader.LoadDir(m.ChartPath)
|
||||
}
|
||||
|
||||
// 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 *chartutil.Requirements, repoNames map[string]string, hash string) (*chartutil.RequirementsLock, error) {
|
||||
func (m *Manager) resolve(req *chart.Requirements, repoNames map[string]string, hash string) (*chart.RequirementsLock, error) {
|
||||
res := resolver.New(m.ChartPath, m.HelmHome)
|
||||
return res.Resolve(req, repoNames, hash)
|
||||
}
|
||||
|
|
@ -191,7 +185,7 @@ func (m *Manager) resolve(req *chartutil.Requirements, repoNames map[string]stri
|
|||
//
|
||||
// It will delete versions of the chart that exist on disk and might cause
|
||||
// a conflict.
|
||||
func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
|
||||
func (m *Manager) downloadAll(deps []*chart.Dependency) error {
|
||||
repos, err := m.loadChartRepositories()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -307,12 +301,12 @@ func (m *Manager) safeDeleteDep(name, dir string) error {
|
|||
return err
|
||||
}
|
||||
for _, fname := range files {
|
||||
ch, err := chartutil.LoadFile(fname)
|
||||
ch, err := loader.LoadFile(fname)
|
||||
if err != nil {
|
||||
fmt.Fprintf(m.Out, "Could not verify %s for deletion: %s (Skipping)", fname, err)
|
||||
continue
|
||||
}
|
||||
if ch.Metadata.Name != name {
|
||||
if ch.Name() != name {
|
||||
// This is not the file you are looking for.
|
||||
continue
|
||||
}
|
||||
|
|
@ -325,7 +319,7 @@ func (m *Manager) safeDeleteDep(name, dir string) error {
|
|||
}
|
||||
|
||||
// hasAllRepos ensures that all of the referenced deps are in the local repo cache.
|
||||
func (m *Manager) hasAllRepos(deps []*chartutil.Dependency) error {
|
||||
func (m *Manager) hasAllRepos(deps []*chart.Dependency) error {
|
||||
rf, err := repo.LoadRepositoriesFile(m.HelmHome.RepositoryFile())
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -335,25 +329,22 @@ func (m *Manager) hasAllRepos(deps []*chartutil.Dependency) error {
|
|||
// Verify that all repositories referenced in the deps are actually known
|
||||
// by Helm.
|
||||
missing := []string{}
|
||||
Loop:
|
||||
for _, dd := range deps {
|
||||
// If repo is from local path, continue
|
||||
if strings.HasPrefix(dd.Repository, "file://") {
|
||||
continue
|
||||
}
|
||||
|
||||
found := false
|
||||
if dd.Repository == "" {
|
||||
found = true
|
||||
} else {
|
||||
for _, repo := range repos {
|
||||
if urlutil.Equal(repo.URL, strings.TrimSuffix(dd.Repository, "/")) {
|
||||
found = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
for _, repo := range repos {
|
||||
if urlutil.Equal(repo.URL, strings.TrimSuffix(dd.Repository, "/")) {
|
||||
continue Loop
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
missing = append(missing, dd.Repository)
|
||||
}
|
||||
missing = append(missing, dd.Repository)
|
||||
}
|
||||
if len(missing) > 0 {
|
||||
return errors.Errorf("no repository definition for %s. Please add the missing repos via 'helm repo add'", strings.Join(missing, ", "))
|
||||
|
|
@ -362,7 +353,7 @@ func (m *Manager) hasAllRepos(deps []*chartutil.Dependency) error {
|
|||
}
|
||||
|
||||
// getRepoNames returns the repo names of the referenced deps which can be used to fetch the cahced index file.
|
||||
func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string, error) {
|
||||
func (m *Manager) getRepoNames(deps []*chart.Dependency) (map[string]string, error) {
|
||||
rf, err := repo.LoadRepositoriesFile(m.HelmHome.RepositoryFile())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -408,24 +399,22 @@ func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string,
|
|||
}
|
||||
}
|
||||
if len(missing) > 0 {
|
||||
if len(missing) > 0 {
|
||||
errorMessage := fmt.Sprintf("no repository definition for %s. Please add them via 'helm repo add'", strings.Join(missing, ", "))
|
||||
// It is common for people to try to enter "stable" as a repository instead of the actual URL.
|
||||
// For this case, let's give them a suggestion.
|
||||
containsNonURL := false
|
||||
for _, repo := range missing {
|
||||
if !strings.Contains(repo, "//") && !strings.HasPrefix(repo, "@") && !strings.HasPrefix(repo, "alias:") {
|
||||
containsNonURL = true
|
||||
}
|
||||
errorMessage := fmt.Sprintf("no repository definition for %s. Please add them via 'helm repo add'", strings.Join(missing, ", "))
|
||||
// It is common for people to try to enter "stable" as a repository instead of the actual URL.
|
||||
// For this case, let's give them a suggestion.
|
||||
containsNonURL := false
|
||||
for _, repo := range missing {
|
||||
if !strings.Contains(repo, "//") && !strings.HasPrefix(repo, "@") && !strings.HasPrefix(repo, "alias:") {
|
||||
containsNonURL = true
|
||||
}
|
||||
if containsNonURL {
|
||||
errorMessage += `
|
||||
}
|
||||
if containsNonURL {
|
||||
errorMessage += `
|
||||
Note that repositories must be URLs or aliases. For example, to refer to the stable
|
||||
repository, use "https://kubernetes-charts.storage.googleapis.com/" or "@stable" instead of
|
||||
"stable". Don't forget to add the repo, too ('helm repo add').`
|
||||
}
|
||||
return nil, errors.New(errorMessage)
|
||||
}
|
||||
return nil, errors.New(errorMessage)
|
||||
}
|
||||
return reposMap, nil
|
||||
}
|
||||
|
|
@ -596,7 +585,7 @@ func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, err
|
|||
}
|
||||
|
||||
// writeLock writes a lockfile to disk
|
||||
func writeLock(chartpath string, lock *chartutil.RequirementsLock) error {
|
||||
func writeLock(chartpath string, lock *chart.RequirementsLock) error {
|
||||
data, err := yaml.Marshal(lock)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -618,7 +607,7 @@ func tarFromLocalDir(chartpath, name, repo, version string) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
ch, err := chartutil.LoadDir(origPath)
|
||||
ch, err := loader.LoadDir(origPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/helm/helmpath"
|
||||
)
|
||||
|
||||
|
|
@ -100,48 +100,48 @@ func TestGetRepoNames(t *testing.T) {
|
|||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
req []*chartutil.Dependency
|
||||
req []*chart.Dependency
|
||||
expect map[string]string
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
name: "no repo definition failure",
|
||||
req: []*chartutil.Dependency{
|
||||
req: []*chart.Dependency{
|
||||
{Name: "oedipus-rex", Repository: "http://example.com/test"},
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
name: "no repo definition failure -- stable repo",
|
||||
req: []*chartutil.Dependency{
|
||||
req: []*chart.Dependency{
|
||||
{Name: "oedipus-rex", Repository: "stable"},
|
||||
},
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
name: "no repo definition failure",
|
||||
req: []*chartutil.Dependency{
|
||||
req: []*chart.Dependency{
|
||||
{Name: "oedipus-rex", Repository: "http://example.com"},
|
||||
},
|
||||
expect: map[string]string{"oedipus-rex": "testing"},
|
||||
},
|
||||
{
|
||||
name: "repo from local path",
|
||||
req: []*chartutil.Dependency{
|
||||
req: []*chart.Dependency{
|
||||
{Name: "local-dep", Repository: "file://./testdata/signtest"},
|
||||
},
|
||||
expect: map[string]string{"local-dep": "file://./testdata/signtest"},
|
||||
},
|
||||
{
|
||||
name: "repo alias (alias:)",
|
||||
req: []*chartutil.Dependency{
|
||||
req: []*chart.Dependency{
|
||||
{Name: "oedipus-rex", Repository: "alias:testing"},
|
||||
},
|
||||
expect: map[string]string{"oedipus-rex": "testing"},
|
||||
},
|
||||
{
|
||||
name: "repo alias (@)",
|
||||
req: []*chartutil.Dependency{
|
||||
req: []*chart.Dependency{
|
||||
{Name: "oedipus-rex", Repository: "@testing"},
|
||||
},
|
||||
expect: map[string]string{"oedipus-rex": "testing"},
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
|
@ -26,19 +25,19 @@ import (
|
|||
"github.com/Masterminds/sprig"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
)
|
||||
|
||||
// Engine is an implementation of 'cmd/tiller/environment'.Engine that uses Go templates.
|
||||
type Engine struct {
|
||||
// FuncMap contains the template functions that will be passed to each
|
||||
// render call. This may only be modified before the first call to Render.
|
||||
FuncMap template.FuncMap
|
||||
funcMap template.FuncMap
|
||||
// If strict is enabled, template rendering will fail if a template references
|
||||
// a value that was not passed in.
|
||||
Strict bool
|
||||
CurrentTemplates map[string]renderable
|
||||
currentTemplates map[string]renderable
|
||||
}
|
||||
|
||||
// New creates a new Go template Engine instance.
|
||||
|
|
@ -49,10 +48,7 @@ type Engine struct {
|
|||
// The FuncMap sets all of the Sprig functions except for those that provide
|
||||
// access to the underlying OS (env, expandenv).
|
||||
func New() *Engine {
|
||||
f := FuncMap()
|
||||
return &Engine{
|
||||
FuncMap: f,
|
||||
}
|
||||
return &Engine{funcMap: FuncMap()}
|
||||
}
|
||||
|
||||
// FuncMap returns a mapping of all of the functions that Engine has.
|
||||
|
|
@ -76,11 +72,11 @@ func FuncMap() template.FuncMap {
|
|||
|
||||
// Add some extra functionality
|
||||
extra := template.FuncMap{
|
||||
"toToml": chartutil.ToToml,
|
||||
"toYaml": chartutil.ToYaml,
|
||||
"fromYaml": chartutil.FromYaml,
|
||||
"toJson": chartutil.ToJson,
|
||||
"fromJson": chartutil.FromJson,
|
||||
"toToml": chartutil.ToTOML,
|
||||
"toYaml": chartutil.ToYAML,
|
||||
"fromYaml": chartutil.FromYAML,
|
||||
"toJson": chartutil.ToJSON,
|
||||
"fromJson": chartutil.FromJSON,
|
||||
|
||||
// This is a placeholder for the "include" function, which is
|
||||
// late-bound to a template. By declaring it here, we preserve the
|
||||
|
|
@ -119,8 +115,8 @@ func FuncMap() template.FuncMap {
|
|||
func (e *Engine) Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) {
|
||||
// Render the charts
|
||||
tmap := allTemplates(chrt, values)
|
||||
e.CurrentTemplates = tmap
|
||||
return e.render(tmap)
|
||||
e.currentTemplates = tmap
|
||||
return e.render(chrt, tmap)
|
||||
}
|
||||
|
||||
// renderable is an object that can be rendered.
|
||||
|
|
@ -138,18 +134,16 @@ type renderable struct {
|
|||
// The resulting FuncMap is only valid for the passed-in template.
|
||||
func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap {
|
||||
// Clone the func map because we are adding context-specific functions.
|
||||
var funcMap template.FuncMap = map[string]interface{}{}
|
||||
for k, v := range e.FuncMap {
|
||||
funcMap := make(template.FuncMap)
|
||||
for k, v := range e.funcMap {
|
||||
funcMap[k] = v
|
||||
}
|
||||
|
||||
// Add the 'include' function here so we can close over t.
|
||||
funcMap["include"] = func(name string, data interface{}) (string, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
if err := t.ExecuteTemplate(buf, name, data); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
var buf strings.Builder
|
||||
err := t.ExecuteTemplate(&buf, name, data)
|
||||
return buf.String(), err
|
||||
}
|
||||
|
||||
// Add the 'required' function here
|
||||
|
|
@ -177,15 +171,15 @@ func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap {
|
|||
basePath: basePath.(string),
|
||||
}
|
||||
|
||||
templates := map[string]renderable{}
|
||||
templateName, err := vals.PathValue("Template.Name")
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl)
|
||||
}
|
||||
|
||||
templates := make(map[string]renderable)
|
||||
templates[templateName.(string)] = r
|
||||
|
||||
result, err := e.render(templates)
|
||||
result, err := e.render(nil, templates)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl)
|
||||
}
|
||||
|
|
@ -196,7 +190,7 @@ func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap {
|
|||
}
|
||||
|
||||
// render takes a map of templates/values and renders them.
|
||||
func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string, err error) {
|
||||
func (e *Engine) render(ch *chart.Chart, tpls map[string]renderable) (rendered map[string]string, err error) {
|
||||
// Basically, what we do here is start with an empty parent template and then
|
||||
// build up a list of templates -- one for each file. Once all of the templates
|
||||
// have been parsed, we loop through again and execute every template.
|
||||
|
|
@ -228,8 +222,7 @@ func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string,
|
|||
|
||||
for _, fname := range keys {
|
||||
r := tpls[fname]
|
||||
t = t.New(fname).Funcs(funcMap)
|
||||
if _, err := t.Parse(r.tpl); err != nil {
|
||||
if _, err := t.New(fname).Funcs(funcMap).Parse(r.tpl); err != nil {
|
||||
return map[string]string{}, errors.Wrapf(err, "parse error in %q", fname)
|
||||
}
|
||||
files = append(files, fname)
|
||||
|
|
@ -237,17 +230,15 @@ func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string,
|
|||
|
||||
// Adding the engine's currentTemplates to the template context
|
||||
// so they can be referenced in the tpl function
|
||||
for fname, r := range e.CurrentTemplates {
|
||||
for fname, r := range e.currentTemplates {
|
||||
if t.Lookup(fname) == nil {
|
||||
t = t.New(fname).Funcs(funcMap)
|
||||
if _, err := t.Parse(r.tpl); err != nil {
|
||||
if _, err := t.New(fname).Funcs(funcMap).Parse(r.tpl); err != nil {
|
||||
return map[string]string{}, errors.Wrapf(err, "parse error in %q", fname)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rendered = make(map[string]string, len(files))
|
||||
var buf bytes.Buffer
|
||||
for _, file := range files {
|
||||
// Don't render partials. We don't care out the direct output of partials.
|
||||
// They are only included from other templates.
|
||||
|
|
@ -256,7 +247,8 @@ func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string,
|
|||
}
|
||||
// At render time, add information about the template that is being rendered.
|
||||
vals := tpls[file].vals
|
||||
vals["Template"] = map[string]interface{}{"Name": file, "BasePath": tpls[file].basePath}
|
||||
vals["Template"] = chartutil.Values{"Name": file, "BasePath": tpls[file].basePath}
|
||||
var buf strings.Builder
|
||||
if err := t.ExecuteTemplate(&buf, file, vals); err != nil {
|
||||
return map[string]string{}, errors.Wrapf(err, "render error in %q", file)
|
||||
}
|
||||
|
|
@ -264,8 +256,14 @@ func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string,
|
|||
// Work around the issue where Go will emit "<no value>" even if Options(missing=zero)
|
||||
// is set. Since missing=error will never get here, we do not need to handle
|
||||
// the Strict case.
|
||||
rendered[file] = strings.Replace(buf.String(), "<no value>", "", -1)
|
||||
buf.Reset()
|
||||
f := &chart.File{
|
||||
Name: strings.Replace(file, "/templates", "/manifests", -1),
|
||||
Data: []byte(strings.Replace(buf.String(), "<no value>", "", -1)),
|
||||
}
|
||||
rendered[file] = string(f.Data)
|
||||
if ch != nil {
|
||||
ch.Files = append(ch.Files, f)
|
||||
}
|
||||
}
|
||||
|
||||
return rendered, nil
|
||||
|
|
@ -299,8 +297,8 @@ func (p byPathLen) Less(i, j int) bool {
|
|||
//
|
||||
// As it goes, it also prepares the values in a scope-sensitive manner.
|
||||
func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable {
|
||||
templates := map[string]renderable{}
|
||||
recAllTpls(c, templates, vals, true, "")
|
||||
templates := make(map[string]renderable)
|
||||
recAllTpls(c, templates, vals)
|
||||
return templates
|
||||
}
|
||||
|
||||
|
|
@ -308,44 +306,32 @@ func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable {
|
|||
//
|
||||
// As it recurses, it also sets the values to be appropriate for the template
|
||||
// scope.
|
||||
func recAllTpls(c *chart.Chart, templates map[string]renderable, parentVals chartutil.Values, top bool, parentID string) {
|
||||
func recAllTpls(c *chart.Chart, templates map[string]renderable, parentVals chartutil.Values) {
|
||||
// This should never evaluate to a nil map. That will cause problems when
|
||||
// values are appended later.
|
||||
cvals := chartutil.Values{}
|
||||
if top {
|
||||
// If this is the top of the rendering tree, assume that parentVals
|
||||
// is already resolved to the authoritative values.
|
||||
cvals := make(chartutil.Values)
|
||||
if c.IsRoot() {
|
||||
cvals = parentVals
|
||||
} else if c.Metadata != nil && c.Metadata.Name != "" {
|
||||
// If there is a {{.Values.ThisChart}} in the parent metadata,
|
||||
// copy that into the {{.Values}} for this template.
|
||||
newVals := chartutil.Values{}
|
||||
if vs, err := parentVals.Table("Values"); err == nil {
|
||||
if tmp, err := vs.Table(c.Metadata.Name); err == nil {
|
||||
newVals = tmp
|
||||
}
|
||||
}
|
||||
|
||||
} else if c.Name() != "" {
|
||||
cvals = map[string]interface{}{
|
||||
"Values": newVals,
|
||||
"Values": make(chartutil.Values),
|
||||
"Release": parentVals["Release"],
|
||||
"Chart": c.Metadata,
|
||||
"Files": chartutil.NewFiles(c.Files),
|
||||
"Capabilities": parentVals["Capabilities"],
|
||||
}
|
||||
// If there is a {{.Values.ThisChart}} in the parent metadata,
|
||||
// copy that into the {{.Values}} for this template.
|
||||
if vs, err := parentVals.Table("Values." + c.Name()); err == nil {
|
||||
cvals["Values"] = vs
|
||||
}
|
||||
}
|
||||
|
||||
newParentID := c.Metadata.Name
|
||||
if parentID != "" {
|
||||
// We artificially reconstruct the chart path to child templates. This
|
||||
// creates a namespaced filename that can be used to track down the source
|
||||
// of a particular template declaration.
|
||||
newParentID = path.Join(parentID, "charts", newParentID)
|
||||
for _, child := range c.Dependencies() {
|
||||
recAllTpls(child, templates, cvals)
|
||||
}
|
||||
|
||||
for _, child := range c.Dependencies {
|
||||
recAllTpls(child, templates, cvals, false, newParentID)
|
||||
}
|
||||
newParentID := c.ChartFullPath()
|
||||
for _, t := range c.Templates {
|
||||
templates[path.Join(newParentID, t.Name)] = renderable{
|
||||
tpl: string(t.Data),
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import (
|
|||
"sync"
|
||||
"testing"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
)
|
||||
|
||||
func TestSortTemplates(t *testing.T) {
|
||||
|
|
@ -62,7 +62,7 @@ func TestEngine(t *testing.T) {
|
|||
// Forbidden because they allow access to the host OS.
|
||||
forbidden := []string{"env", "expandenv"}
|
||||
for _, f := range forbidden {
|
||||
if _, ok := e.FuncMap[f]; ok {
|
||||
if _, ok := e.funcMap[f]; ok {
|
||||
t.Errorf("Forbidden function %s exists in FuncMap.", f)
|
||||
}
|
||||
}
|
||||
|
|
@ -149,7 +149,7 @@ func TestRenderInternals(t *testing.T) {
|
|||
"three": {tpl: `{{template "two" dict "Value" "three"}}`, vals: vals},
|
||||
}
|
||||
|
||||
out, err := e.render(tpls)
|
||||
out, err := e.render(nil, tpls)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed template rendering: %s", err)
|
||||
}
|
||||
|
|
@ -182,7 +182,7 @@ func TestParallelRenderInternals(t *testing.T) {
|
|||
tt := fmt.Sprintf("expect-%d", i)
|
||||
v := chartutil.Values{"val": tt}
|
||||
tpls := map[string]renderable{fname: {tpl: `{{.val}}`, vals: v}}
|
||||
out, err := e.render(tpls)
|
||||
out, err := e.render(nil, tpls)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to render %s: %s", tt, err)
|
||||
}
|
||||
|
|
@ -202,22 +202,23 @@ func TestAllTemplates(t *testing.T) {
|
|||
{Name: "templates/foo", Data: []byte("foo")},
|
||||
{Name: "templates/bar", Data: []byte("bar")},
|
||||
},
|
||||
Dependencies: []*chart.Chart{
|
||||
{
|
||||
Metadata: &chart.Metadata{Name: "laboratory mice"},
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/pinky", Data: []byte("pinky")},
|
||||
{Name: "templates/brain", Data: []byte("brain")},
|
||||
},
|
||||
Dependencies: []*chart.Chart{{
|
||||
Metadata: &chart.Metadata{Name: "same thing we do every night"},
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/innermost", Data: []byte("innermost")},
|
||||
}},
|
||||
},
|
||||
},
|
||||
}
|
||||
dep1 := &chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "laboratory mice"},
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/pinky", Data: []byte("pinky")},
|
||||
{Name: "templates/brain", Data: []byte("brain")},
|
||||
},
|
||||
}
|
||||
ch1.AddDependency(dep1)
|
||||
|
||||
dep2 := &chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "same thing we do every night"},
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/innermost", Data: []byte("innermost")},
|
||||
},
|
||||
}
|
||||
dep1.AddDependency(dep2)
|
||||
|
||||
var v chartutil.Values
|
||||
tpls := allTemplates(ch1, v)
|
||||
|
|
@ -235,18 +236,15 @@ func TestRenderDependency(t *testing.T) {
|
|||
Templates: []*chart.File{
|
||||
{Name: "templates/outer", Data: []byte(toptpl)},
|
||||
},
|
||||
Dependencies: []*chart.Chart{
|
||||
{
|
||||
Metadata: &chart.Metadata{Name: "innerchart"},
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/inner", Data: []byte(deptpl)},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
ch.AddDependency(&chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "innerchart"},
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/inner", Data: []byte(deptpl)},
|
||||
},
|
||||
})
|
||||
|
||||
out, err := e.Render(ch, map[string]interface{}{})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to render chart: %s", err)
|
||||
}
|
||||
|
|
@ -285,9 +283,9 @@ func TestRenderNestedValues(t *testing.T) {
|
|||
Templates: []*chart.File{
|
||||
{Name: innerpath, Data: []byte(`Old {{.Values.who}} is still a-flyin'`)},
|
||||
},
|
||||
Values: []byte(`who: "Robert"`),
|
||||
Dependencies: []*chart.Chart{deepest},
|
||||
Values: []byte(`who: "Robert"`),
|
||||
}
|
||||
inner.AddDependency(deepest)
|
||||
|
||||
outer := &chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "top"},
|
||||
|
|
@ -299,8 +297,8 @@ what: stinkweed
|
|||
who: me
|
||||
herrick:
|
||||
who: time`),
|
||||
Dependencies: []*chart.Chart{inner},
|
||||
}
|
||||
outer.AddDependency(inner)
|
||||
|
||||
injValues := []byte(`
|
||||
what: rosebuds
|
||||
|
|
@ -358,8 +356,6 @@ func TestRenderBuiltinValues(t *testing.T) {
|
|||
{Name: "templates/Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
|
||||
{Name: "templates/From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.Get "book/title.txt"}}`)},
|
||||
},
|
||||
Values: []byte{},
|
||||
Dependencies: []*chart.Chart{},
|
||||
Files: []*chart.File{
|
||||
{Name: "author", Data: []byte("Virgil")},
|
||||
{Name: "book/title.txt", Data: []byte("Aeneid")},
|
||||
|
|
@ -371,9 +367,8 @@ func TestRenderBuiltinValues(t *testing.T) {
|
|||
Templates: []*chart.File{
|
||||
{Name: "templates/Aeneas", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)},
|
||||
},
|
||||
Values: []byte{},
|
||||
Dependencies: []*chart.Chart{inner},
|
||||
}
|
||||
outer.AddDependency(inner)
|
||||
|
||||
inject := chartutil.Values{
|
||||
"Values": "",
|
||||
|
|
@ -403,15 +398,13 @@ func TestRenderBuiltinValues(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func TestAlterFuncMap(t *testing.T) {
|
||||
func TestAlterFuncMap_include(t *testing.T) {
|
||||
c := &chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "conrad"},
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/quote", Data: []byte(`{{include "conrad/templates/_partial" . | indent 2}} dead.`)},
|
||||
{Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)},
|
||||
},
|
||||
Values: []byte{},
|
||||
Dependencies: []*chart.Chart{},
|
||||
}
|
||||
|
||||
v := chartutil.Values{
|
||||
|
|
@ -431,127 +424,127 @@ func TestAlterFuncMap(t *testing.T) {
|
|||
if got := out["conrad/templates/quote"]; got != expect {
|
||||
t.Errorf("Expected %q, got %q (%v)", expect, got, out)
|
||||
}
|
||||
}
|
||||
|
||||
reqChart := &chart.Chart{
|
||||
func TestAlterFuncMap_require(t *testing.T) {
|
||||
c := &chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "conan"},
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/quote", Data: []byte(`All your base are belong to {{ required "A valid 'who' is required" .Values.who }}`)},
|
||||
{Name: "templates/bases", Data: []byte(`All {{ required "A valid 'bases' is required" .Values.bases }} of them!`)},
|
||||
},
|
||||
Values: []byte{},
|
||||
Dependencies: []*chart.Chart{},
|
||||
}
|
||||
|
||||
reqValues := chartutil.Values{
|
||||
v := chartutil.Values{
|
||||
"Values": chartutil.Values{
|
||||
"who": "us",
|
||||
"bases": 2,
|
||||
},
|
||||
"Chart": reqChart.Metadata,
|
||||
"Chart": c.Metadata,
|
||||
"Release": chartutil.Values{
|
||||
"Name": "That 90s meme",
|
||||
},
|
||||
}
|
||||
|
||||
outReq, err := New().Render(reqChart, reqValues)
|
||||
out, err := New().Render(c, v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectStr := "All your base are belong to us"
|
||||
if gotStr := outReq["conan/templates/quote"]; gotStr != expectStr {
|
||||
t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, outReq)
|
||||
if gotStr := out["conan/templates/quote"]; gotStr != expectStr {
|
||||
t.Errorf("Expected %q, got %q (%v)", expectStr, gotStr, out)
|
||||
}
|
||||
expectNum := "All 2 of them!"
|
||||
if gotNum := outReq["conan/templates/bases"]; gotNum != expectNum {
|
||||
t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, outReq)
|
||||
if gotNum := out["conan/templates/bases"]; gotNum != expectNum {
|
||||
t.Errorf("Expected %q, got %q (%v)", expectNum, gotNum, out)
|
||||
}
|
||||
}
|
||||
|
||||
tplChart := &chart.Chart{
|
||||
func TestAlterFuncMap_tpl(t *testing.T) {
|
||||
c := &chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "TplFunction"},
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value}}" .}}`)},
|
||||
},
|
||||
Values: []byte{},
|
||||
Dependencies: []*chart.Chart{},
|
||||
}
|
||||
|
||||
tplValues := chartutil.Values{
|
||||
v := chartutil.Values{
|
||||
"Values": chartutil.Values{
|
||||
"value": "myvalue",
|
||||
},
|
||||
"Chart": tplChart.Metadata,
|
||||
"Chart": c.Metadata,
|
||||
"Release": chartutil.Values{
|
||||
"Name": "TestRelease",
|
||||
},
|
||||
}
|
||||
|
||||
outTpl, err := New().Render(tplChart, tplValues)
|
||||
out, err := New().Render(c, v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectTplStr := "Evaluate tpl Value: myvalue"
|
||||
if gotStrTpl := outTpl["TplFunction/templates/base"]; gotStrTpl != expectTplStr {
|
||||
t.Errorf("Expected %q, got %q (%v)", expectTplStr, gotStrTpl, outTpl)
|
||||
expect := "Evaluate tpl Value: myvalue"
|
||||
if got := out["TplFunction/templates/base"]; got != expect {
|
||||
t.Errorf("Expected %q, got %q (%v)", expect, got, out)
|
||||
}
|
||||
}
|
||||
|
||||
tplChartWithFunction := &chart.Chart{
|
||||
func TestAlterFuncMap_tplfunc(t *testing.T) {
|
||||
c := &chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "TplFunction"},
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/base", Data: []byte(`Evaluate tpl {{tpl "Value: {{ .Values.value | quote}}" .}}`)},
|
||||
},
|
||||
Values: []byte{},
|
||||
Dependencies: []*chart.Chart{},
|
||||
}
|
||||
|
||||
tplValuesWithFunction := chartutil.Values{
|
||||
v := chartutil.Values{
|
||||
"Values": chartutil.Values{
|
||||
"value": "myvalue",
|
||||
},
|
||||
"Chart": tplChartWithFunction.Metadata,
|
||||
"Chart": c.Metadata,
|
||||
"Release": chartutil.Values{
|
||||
"Name": "TestRelease",
|
||||
},
|
||||
}
|
||||
|
||||
outTplWithFunction, err := New().Render(tplChartWithFunction, tplValuesWithFunction)
|
||||
out, err := New().Render(c, v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectTplStrWithFunction := "Evaluate tpl Value: \"myvalue\""
|
||||
if gotStrTplWithFunction := outTplWithFunction["TplFunction/templates/base"]; gotStrTplWithFunction != expectTplStrWithFunction {
|
||||
t.Errorf("Expected %q, got %q (%v)", expectTplStrWithFunction, gotStrTplWithFunction, outTplWithFunction)
|
||||
expect := "Evaluate tpl Value: \"myvalue\""
|
||||
if got := out["TplFunction/templates/base"]; got != expect {
|
||||
t.Errorf("Expected %q, got %q (%v)", expect, got, out)
|
||||
}
|
||||
}
|
||||
|
||||
tplChartWithInclude := &chart.Chart{
|
||||
func TestAlterFuncMap_tplinclude(t *testing.T) {
|
||||
c := &chart.Chart{
|
||||
Metadata: &chart.Metadata{Name: "TplFunction"},
|
||||
Templates: []*chart.File{
|
||||
{Name: "templates/base", Data: []byte(`{{ tpl "{{include ` + "`" + `TplFunction/templates/_partial` + "`" + ` . | quote }}" .}}`)},
|
||||
{Name: "templates/_partial", Data: []byte(`{{.Template.Name}}`)},
|
||||
},
|
||||
Values: []byte{},
|
||||
Dependencies: []*chart.Chart{},
|
||||
}
|
||||
tplValueWithInclude := chartutil.Values{
|
||||
v := chartutil.Values{
|
||||
"Values": chartutil.Values{
|
||||
"value": "myvalue",
|
||||
},
|
||||
"Chart": tplChartWithInclude.Metadata,
|
||||
"Chart": c.Metadata,
|
||||
"Release": chartutil.Values{
|
||||
"Name": "TestRelease",
|
||||
},
|
||||
}
|
||||
|
||||
outTplWithInclude, err := New().Render(tplChartWithInclude, tplValueWithInclude)
|
||||
out, err := New().Render(c, v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedTplStrWithInclude := "\"TplFunction/templates/base\""
|
||||
if gotStrTplWithInclude := outTplWithInclude["TplFunction/templates/base"]; gotStrTplWithInclude != expectedTplStrWithInclude {
|
||||
t.Errorf("Expected %q, got %q (%v)", expectedTplStrWithInclude, gotStrTplWithInclude, outTplWithInclude)
|
||||
expect := "\"TplFunction/templates/base\""
|
||||
if got := out["TplFunction/templates/base"]; got != expect {
|
||||
t.Errorf("Expected %q, got %q (%v)", expect, got, out)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package chart
|
||||
|
||||
// Chart is a helm package that contains metadata, a default config, zero or more
|
||||
// optionally parameterizable templates, and zero or more charts (dependencies).
|
||||
type Chart struct {
|
||||
// Metadata is the contents of the Chartfile.
|
||||
Metadata *Metadata `json:"metadata,omitempty"`
|
||||
// Templates for this chart.
|
||||
Templates []*File `json:"templates,omitempty"`
|
||||
// Dependencies are the charts that this chart depends on.
|
||||
Dependencies []*Chart `json:"dependencies,omitempty"`
|
||||
// Values are default config for this template.
|
||||
Values []byte `json:"values,omitempty"`
|
||||
// Files are miscellaneous files in a chart archive,
|
||||
// e.g. README, LICENSE, etc.
|
||||
Files []*File `json:"files,omitempty"`
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
|
||||
package release
|
||||
|
||||
import "k8s.io/helm/pkg/hapi/chart"
|
||||
import "k8s.io/helm/pkg/chart"
|
||||
|
||||
// Release describes a deployment of a chart, together with the chart
|
||||
// and the variables used to deploy that chart.
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
package hapi
|
||||
|
||||
import (
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ limitations under the License.
|
|||
package helm // import "k8s.io/helm/pkg/helm"
|
||||
|
||||
import (
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/chart/loader"
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/hapi"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
"k8s.io/helm/pkg/storage"
|
||||
"k8s.io/helm/pkg/tiller"
|
||||
"k8s.io/helm/pkg/tiller/environment"
|
||||
)
|
||||
|
||||
// Client manages client side of the Helm-Tiller protocol.
|
||||
|
|
@ -39,8 +39,7 @@ func NewClient(opts ...Option) *Client {
|
|||
}
|
||||
|
||||
func (c *Client) init() *Client {
|
||||
env := environment.New()
|
||||
c.tiller = tiller.NewReleaseServer(env, c.opts.discovery, c.opts.kubeClient)
|
||||
c.tiller = tiller.NewReleaseServer(c.opts.discovery, c.opts.kubeClient)
|
||||
c.tiller.Releases = storage.Init(c.opts.driver)
|
||||
return c
|
||||
}
|
||||
|
|
@ -69,7 +68,7 @@ func (c *Client) ListReleases(opts ...ReleaseListOption) ([]*release.Release, er
|
|||
// InstallRelease loads a chart from chstr, installs it, and returns the release response.
|
||||
func (c *Client) InstallRelease(chstr, ns string, opts ...InstallOption) (*release.Release, error) {
|
||||
// load the chart to install
|
||||
chart, err := chartutil.Load(chstr)
|
||||
chart, err := loader.Load(chstr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -136,7 +135,7 @@ func (c *Client) UninstallRelease(rlsName string, opts ...UninstallOption) (*hap
|
|||
// UpdateRelease loads a chart from chstr and updates a release to a new/different chart.
|
||||
func (c *Client) UpdateRelease(rlsName, chstr string, opts ...UpdateOption) (*release.Release, error) {
|
||||
// load the chart to update
|
||||
chart, err := chartutil.Load(chstr)
|
||||
chart, err := loader.Load(chstr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ import (
|
|||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"k8s.io/helm/pkg/chart"
|
||||
"k8s.io/helm/pkg/hapi"
|
||||
"k8s.io/helm/pkg/hapi/chart"
|
||||
"k8s.io/helm/pkg/hapi/release"
|
||||
)
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue