diff --git a/Gopkg.lock b/Gopkg.lock index a77aab51c..4edbe1b21 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -929,6 +929,30 @@ revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" version = "v1.3.0" +[[projects]] + branch = "master" + digest = "1:f4e5276a3b356f4692107047fd2890f2fe534f4feeb6b1fd2f6dfbd87f1ccf54" + name = "github.com/xeipuuv/gojsonpointer" + packages = ["."] + pruneopts = "UT" + revision = "4e3ac2762d5f479393488629ee9370b50873b3a6" + +[[projects]] + branch = "master" + digest = "1:dc6a6c28ca45d38cfce9f7cb61681ee38c5b99ec1425339bfc1e1a7ba769c807" + name = "github.com/xeipuuv/gojsonreference" + packages = ["."] + pruneopts = "UT" + revision = "bd5ef7bd5415a7ac448318e64f11a24cd21e594b" + +[[projects]] + digest = "1:1c898ea6c30c16e8d55fdb6fe44c4bee5f9b7d68aa260cfdfc3024491dcc7bea" + name = "github.com/xeipuuv/gojsonschema" + packages = ["."] + pruneopts = "UT" + revision = "f971f3cd73b2899de6923801c147f075263e0c50" + version = "v1.1.0" + [[projects]] digest = "1:340553b2fdaab7d53e63fd40f8ed82203bdd3274253055bdb80a46828482ef81" name = "github.com/xenolf/lego" @@ -1777,6 +1801,7 @@ "github.com/spf13/pflag", "github.com/stretchr/testify/assert", "github.com/stretchr/testify/suite", + "github.com/xeipuuv/gojsonschema", "golang.org/x/crypto/bcrypt", "golang.org/x/crypto/openpgp", "golang.org/x/crypto/openpgp/clearsign", diff --git a/Gopkg.toml b/Gopkg.toml index 7ea36e668..94736bf3c 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -102,3 +102,6 @@ go-tests = true unused-packages = true +[[constraint]] + name = "github.com/xeipuuv/gojsonschema" + version = "1.1.0" diff --git a/cmd/helm/create.go b/cmd/helm/create.go index 0caf11d42..4c26e4ca8 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -80,8 +80,8 @@ func (o *createOptions) run(out io.Writer) error { Description: "A Helm chart for Kubernetes", Type: "application", Version: "0.1.0", - AppVersion: "1.0", - APIVersion: chart.APIVersionv1, + AppVersion: "0.1.0", + APIVersion: chart.APIVersionV1, } if o.starter != "" { @@ -90,6 +90,6 @@ func (o *createOptions) run(out io.Writer) error { return chartutil.CreateFrom(cfile, filepath.Dir(o.name), lstarter) } - _, err := chartutil.Create(cfile, filepath.Dir(o.name)) + _, err := chartutil.Create(chartname, filepath.Dir(o.name)) return err } diff --git a/cmd/helm/create_test.go b/cmd/helm/create_test.go index 33d3b8eee..bf36be68e 100644 --- a/cmd/helm/create_test.go +++ b/cmd/helm/create_test.go @@ -55,7 +55,7 @@ func TestCreateCmd(t *testing.T) { if c.Name() != cname { t.Errorf("Expected %q name, got %q", cname, c.Name()) } - if c.Metadata.APIVersion != chart.APIVersionv1 { + if c.Metadata.APIVersion != chart.APIVersionV1 { t.Errorf("Wrong API version: %q", c.Metadata.APIVersion) } } @@ -73,7 +73,7 @@ func TestCreateStarterCmd(t *testing.T) { // Create a starter. starterchart := hh.Starters() os.Mkdir(starterchart, 0755) - if dest, err := chartutil.Create(&chart.Metadata{Name: "starterchart"}, starterchart); err != nil { + if dest, err := chartutil.Create("starterchart", starterchart); err != nil { t.Fatalf("Could not create chart: %s", err) } else { t.Logf("Created %s", dest) @@ -106,7 +106,7 @@ func TestCreateStarterCmd(t *testing.T) { if c.Name() != cname { t.Errorf("Expected %q name, got %q", cname, c.Name()) } - if c.Metadata.APIVersion != chart.APIVersionv1 { + if c.Metadata.APIVersion != chart.APIVersionV1 { t.Errorf("Wrong API version: %q", c.Metadata.APIVersion) } diff --git a/cmd/helm/dependency_update_test.go b/cmd/helm/dependency_update_test.go index 7e366bde2..bbe108973 100644 --- a/cmd/helm/dependency_update_test.go +++ b/cmd/helm/dependency_update_test.go @@ -46,8 +46,9 @@ func TestDependencyUpdateCmd(t *testing.T) { t.Logf("Listening on directory %s", srv.Root()) chartname := "depup" - md := createTestingMetadata(chartname, srv.URL()) - if _, err := chartutil.Create(md, hh.String()); err != nil { + ch := createTestingMetadata(chartname, srv.URL()) + md := ch.Metadata + if err := chartutil.SaveDir(ch, hh.String()); err != nil { t.Fatal(err) } @@ -203,13 +204,16 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) { // createTestingMetadata creates a basic chart that depends on reqtest-0.1.0 // // The baseURL can be used to point to a particular repository server. -func createTestingMetadata(name, baseURL string) *chart.Metadata { - return &chart.Metadata{ - Name: name, - Version: "1.2.3", - Dependencies: []*chart.Dependency{ - {Name: "reqtest", Version: "0.1.0", Repository: baseURL}, - {Name: "compressedchart", Version: "0.1.0", Repository: baseURL}, +func createTestingMetadata(name, baseURL string) *chart.Chart { + return &chart.Chart{ + Metadata: &chart.Metadata{ + APIVersion: chart.APIVersionV1, + Name: name, + Version: "1.2.3", + Dependencies: []*chart.Dependency{ + {Name: "reqtest", Version: "0.1.0", Repository: baseURL}, + {Name: "compressedchart", Version: "0.1.0", Repository: baseURL}, + }, }, } } @@ -219,6 +223,5 @@ func createTestingMetadata(name, baseURL string) *chart.Metadata { // The baseURL can be used to point to a particular repository server. func createTestingChart(dest, name, baseURL string) error { cfile := createTestingMetadata(name, baseURL) - _, err := chartutil.Create(cfile, dest) - return err + return chartutil.SaveDir(cfile, dest) } diff --git a/cmd/helm/history.go b/cmd/helm/history.go index 699cf38ca..7ba4eb7b3 100644 --- a/cmd/helm/history.go +++ b/cmd/helm/history.go @@ -35,11 +35,11 @@ configures the maximum length of the revision list returned. The historical release set is printed as a formatted table, e.g: $ helm history angry-bird --max=4 - REVISION UPDATED STATUS CHART DESCRIPTION - 1 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 Initial install - 2 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 Upgraded successfully - 3 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 Rolled back to 2 - 4 Mon Oct 3 10:15:13 2016 deployed alpine-0.1.0 Upgraded successfully + REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION + 1 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Initial install + 2 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Upgraded successfully + 3 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Rolled back to 2 + 4 Mon Oct 3 10:15:13 2016 deployed alpine-0.1.0 1.0 Upgraded successfully ` func newHistoryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 38464b585..f6da14496 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -113,7 +113,7 @@ func TestInstall(t *testing.T) { }, // Install, chart with bad dependencies in Chart.yaml in /charts { - name: "install chart with bad dependencies in Chart.yaml", + name: "install chart with bad dependencies in Chart.yaml", cmd: "install badreq testdata/testcharts/chart-bad-requirements", wantError: true, }, @@ -136,6 +136,53 @@ func TestInstall(t *testing.T) { wantError: true, golden: "output/install-chart-bad-type.txt", }, + // Install, values from yaml, schematized + { + name: "install with schema file", + cmd: "install schema testdata/testcharts/chart-with-schema", + golden: "output/schema.txt", + }, + // Install, values from yaml, schematized with errors + { + name: "install with schema file, with errors", + cmd: "install schema testdata/testcharts/chart-with-schema-negative", + wantError: true, + golden: "output/schema-negative.txt", + }, + // Install, values from yaml, extra values from yaml, schematized with errors + { + name: "install with schema file, extra values from yaml, with errors", + cmd: "install schema testdata/testcharts/chart-with-schema -f testdata/testcharts/chart-with-schema/extra-values.yaml", + wantError: true, + golden: "output/schema-negative.txt", + }, + // Install, values from yaml, extra values from cli, schematized with errors + { + name: "install with schema file, extra values from cli, with errors", + cmd: "install schema testdata/testcharts/chart-with-schema --set age=-5", + wantError: true, + golden: "output/schema-negative-cli.txt", + }, + // Install with subchart, values from yaml, schematized with errors + { + name: "install with schema file and schematized subchart, with errors", + cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart", + wantError: true, + golden: "output/subchart-schema-negative.txt", + }, + // Install with subchart, values from yaml, extra values from cli, schematized with errors + { + name: "install with schema file and schematized subchart, extra values from cli", + cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart --set lastname=doe --set subchart-with-schema.age=25", + golden: "output/subchart-schema-cli.txt", + }, + // Install with subchart, values from yaml, extra values from cli, schematized with errors + { + name: "install with schema file and schematized subchart, extra values from cli, with errors", + cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart --set lastname=doe --set subchart-with-schema.age=-25", + wantError: true, + golden: "output/subchart-schema-cli-negative.txt", + }, } runTestActionCmd(t, tests) diff --git a/cmd/helm/release_testing.go b/cmd/helm/release_testing.go index f6cf0a14a..ab89c7173 100644 --- a/cmd/helm/release_testing.go +++ b/cmd/helm/release_testing.go @@ -17,68 +17,29 @@ limitations under the License. package main import ( - "fmt" "io" - "github.com/pkg/errors" "github.com/spf13/cobra" - "helm.sh/helm/cmd/helm/require" "helm.sh/helm/pkg/action" - "helm.sh/helm/pkg/release" ) -const releaseTestDesc = ` -The test command runs the tests for a release. +const releaseTestHelp = ` +The test command consists of multiple subcommands around running tests on a release. + +Example usage: + $ helm test run [RELEASE] -The argument this command takes is the name of a deployed release. -The tests to be run are defined in the chart that was installed. ` func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { - client := action.NewReleaseTesting(cfg) - cmd := &cobra.Command{ - Use: "test [RELEASE]", - Short: "test a release", - Long: releaseTestDesc, - Args: require.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - c, errc := client.Run(args[0]) - testErr := &testErr{} - - for { - select { - case err := <-errc: - if err == nil && testErr.failed > 0 { - return testErr.Error() - } - return err - case res, ok := <-c: - if !ok { - break - } - - if res.Status == release.TestRunFailure { - testErr.failed++ - } - fmt.Fprintf(out, res.Msg+"\n") - } - } - }, + Use: "test", + Short: "test a release or cleanup test artifacts", + Long: releaseTestHelp, } - - f := cmd.Flags() - f.Int64Var(&client.Timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") - f.BoolVar(&client.Cleanup, "cleanup", false, "delete test pods upon completion") - + cmd.AddCommand( + newReleaseTestRunCmd(cfg, out), + ) return cmd } - -type testErr struct { - failed int -} - -func (err *testErr) Error() error { - return errors.Errorf("%v test(s) failed", err.failed) -} diff --git a/cmd/helm/release_testing_run.go b/cmd/helm/release_testing_run.go new file mode 100644 index 000000000..4ede32256 --- /dev/null +++ b/cmd/helm/release_testing_run.go @@ -0,0 +1,83 @@ +/* +Copyright The Helm Authors. +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 main + +import ( + "fmt" + "io" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "helm.sh/helm/cmd/helm/require" + "helm.sh/helm/pkg/action" + "helm.sh/helm/pkg/release" +) + +const releaseTestRunHelp = ` +The test command runs the tests for a release. + +The argument this command takes is the name of a deployed release. +The tests to be run are defined in the chart that was installed. +` + +func newReleaseTestRunCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { + client := action.NewReleaseTesting(cfg) + + cmd := &cobra.Command{ + Use: "run [RELEASE]", + Short: "run tests for a release", + Long: releaseTestRunHelp, + Args: require.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + c, errc := client.Run(args[0]) + testErr := &testErr{} + + for { + select { + case err := <-errc: + if err != nil && testErr.failed > 0 { + return testErr.Error() + } + return err + case res, ok := <-c: + if !ok { + break + } + + if res.Status == release.TestRunFailure { + testErr.failed++ + } + fmt.Fprintf(out, res.Msg+"\n") + } + } + }, + } + + f := cmd.Flags() + f.Int64Var(&client.Timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") + f.BoolVar(&client.Cleanup, "cleanup", false, "delete test pods upon completion") + + return cmd +} + +type testErr struct { + failed int +} + +func (err *testErr) Error() error { + return errors.Errorf("%v test(s) failed", err.failed) +} diff --git a/cmd/helm/release_testing_test.go b/cmd/helm/release_testing_test.go deleted file mode 100644 index a9ab5d76e..000000000 --- a/cmd/helm/release_testing_test.go +++ /dev/null @@ -1,96 +0,0 @@ -/* -Copyright The Helm Authors. - -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 main - -import ( - "testing" - "time" - - "helm.sh/helm/pkg/release" -) - -func TestReleaseTesting(t *testing.T) { - timestamp := time.Unix(1452902400, 0).UTC() - - tests := []cmdTestCase{{ - name: "successful test", - cmd: "status test-success", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{ - Name: "test-success", - TestSuiteResults: []*release.TestRun{ - { - Name: "test-success", - Status: release.TestRunSuccess, - StartedAt: timestamp, - CompletedAt: timestamp, - }, - }, - })}, - golden: "output/test-success.txt", - }, { - name: "test failure", - cmd: "status test-failure", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{ - Name: "test-failure", - TestSuiteResults: []*release.TestRun{ - { - Name: "test-failure", - Status: release.TestRunFailure, - StartedAt: timestamp, - CompletedAt: timestamp, - }, - }, - })}, - golden: "output/test-failure.txt", - }, { - name: "test unknown", - cmd: "status test-unknown", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{ - Name: "test-unknown", - TestSuiteResults: []*release.TestRun{ - { - Name: "test-unknown", - Status: release.TestRunUnknown, - StartedAt: timestamp, - CompletedAt: timestamp, - }, - }, - })}, - golden: "output/test-unknown.txt", - }, { - name: "test running", - cmd: "status test-running", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{ - Name: "test-running", - TestSuiteResults: []*release.TestRun{ - { - Name: "test-running", - Status: release.TestRunRunning, - StartedAt: timestamp, - CompletedAt: timestamp, - }, - }, - })}, - golden: "output/test-running.txt", - }, { - name: "test with no tests", - cmd: "test no-tests", - rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "no-tests"})}, - golden: "output/test-no-tests.txt", - }} - runTestCmd(t, tests) -} diff --git a/cmd/helm/testdata/output/history-limit.txt b/cmd/helm/testdata/output/history-limit.txt index 48c900c86..92ce86edf 100644 --- a/cmd/helm/testdata/output/history-limit.txt +++ b/cmd/helm/testdata/output/history-limit.txt @@ -1,3 +1,3 @@ -REVISION UPDATED STATUS CHART DESCRIPTION -3 1977-09-02 22:04:05 +0000 UTC superseded foo-0.1.0-beta.1 Release mock -4 1977-09-02 22:04:05 +0000 UTC deployed foo-0.1.0-beta.1 Release mock +REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION +3 1977-09-02 22:04:05 +0000 UTC superseded foo-0.1.0-beta.1 1.0 Release mock +4 1977-09-02 22:04:05 +0000 UTC deployed foo-0.1.0-beta.1 1.0 Release mock diff --git a/cmd/helm/testdata/output/history.json b/cmd/helm/testdata/output/history.json index e16b5670e..364007f3c 100644 --- a/cmd/helm/testdata/output/history.json +++ b/cmd/helm/testdata/output/history.json @@ -1 +1 @@ -[{"revision":3,"updated":"1977-09-02 22:04:05 +0000 UTC","status":"superseded","chart":"foo-0.1.0-beta.1","description":"Release mock"},{"revision":4,"updated":"1977-09-02 22:04:05 +0000 UTC","status":"deployed","chart":"foo-0.1.0-beta.1","description":"Release mock"}] +[{"revision":3,"updated":"1977-09-02 22:04:05 +0000 UTC","status":"superseded","chart":"foo-0.1.0-beta.1","app_version":"1.0","description":"Release mock"},{"revision":4,"updated":"1977-09-02 22:04:05 +0000 UTC","status":"deployed","chart":"foo-0.1.0-beta.1","app_version":"1.0","description":"Release mock"}] diff --git a/cmd/helm/testdata/output/history.txt b/cmd/helm/testdata/output/history.txt index e2379f721..a6161b524 100644 --- a/cmd/helm/testdata/output/history.txt +++ b/cmd/helm/testdata/output/history.txt @@ -1,5 +1,5 @@ -REVISION UPDATED STATUS CHART DESCRIPTION -1 1977-09-02 22:04:05 +0000 UTC superseded foo-0.1.0-beta.1 Release mock -2 1977-09-02 22:04:05 +0000 UTC superseded foo-0.1.0-beta.1 Release mock -3 1977-09-02 22:04:05 +0000 UTC superseded foo-0.1.0-beta.1 Release mock -4 1977-09-02 22:04:05 +0000 UTC deployed foo-0.1.0-beta.1 Release mock +REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION +1 1977-09-02 22:04:05 +0000 UTC superseded foo-0.1.0-beta.1 1.0 Release mock +2 1977-09-02 22:04:05 +0000 UTC superseded foo-0.1.0-beta.1 1.0 Release mock +3 1977-09-02 22:04:05 +0000 UTC superseded foo-0.1.0-beta.1 1.0 Release mock +4 1977-09-02 22:04:05 +0000 UTC deployed foo-0.1.0-beta.1 1.0 Release mock diff --git a/cmd/helm/testdata/output/history.yaml b/cmd/helm/testdata/output/history.yaml index 042b87118..fc2887068 100644 --- a/cmd/helm/testdata/output/history.yaml +++ b/cmd/helm/testdata/output/history.yaml @@ -1,9 +1,11 @@ -- chart: foo-0.1.0-beta.1 +- app_version: "1.0" + chart: foo-0.1.0-beta.1 description: Release mock revision: 3 status: superseded updated: 1977-09-02 22:04:05 +0000 UTC -- chart: foo-0.1.0-beta.1 +- app_version: "1.0" + chart: foo-0.1.0-beta.1 description: Release mock revision: 4 status: deployed diff --git a/cmd/helm/testdata/output/schema-negative-cli.txt b/cmd/helm/testdata/output/schema-negative-cli.txt new file mode 100644 index 000000000..26bc92b1b --- /dev/null +++ b/cmd/helm/testdata/output/schema-negative-cli.txt @@ -0,0 +1,4 @@ +Error: values don't meet the specifications of the schema(s) in the following chart(s): +empty: +- age: Must be greater than or equal to 0/1 + diff --git a/cmd/helm/testdata/output/schema-negative.txt b/cmd/helm/testdata/output/schema-negative.txt new file mode 100644 index 000000000..2ea97b7d0 --- /dev/null +++ b/cmd/helm/testdata/output/schema-negative.txt @@ -0,0 +1,5 @@ +Error: values don't meet the specifications of the schema(s) in the following chart(s): +empty: +- (root): employmentInfo is required +- age: Must be greater than or equal to 0/1 + diff --git a/cmd/helm/testdata/output/schema.txt b/cmd/helm/testdata/output/schema.txt new file mode 100644 index 000000000..f694bfdf1 --- /dev/null +++ b/cmd/helm/testdata/output/schema.txt @@ -0,0 +1,5 @@ +NAME: schema +LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC +NAMESPACE: default +STATUS: deployed + diff --git a/cmd/helm/testdata/output/subchart-schema-cli-negative.txt b/cmd/helm/testdata/output/subchart-schema-cli-negative.txt new file mode 100644 index 000000000..86f6e87a2 --- /dev/null +++ b/cmd/helm/testdata/output/subchart-schema-cli-negative.txt @@ -0,0 +1,4 @@ +Error: values don't meet the specifications of the schema(s) in the following chart(s): +subchart-with-schema: +- age: Must be greater than or equal to 0/1 + diff --git a/cmd/helm/testdata/output/subchart-schema-cli.txt b/cmd/helm/testdata/output/subchart-schema-cli.txt new file mode 100644 index 000000000..f694bfdf1 --- /dev/null +++ b/cmd/helm/testdata/output/subchart-schema-cli.txt @@ -0,0 +1,5 @@ +NAME: schema +LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC +NAMESPACE: default +STATUS: deployed + diff --git a/cmd/helm/testdata/output/subchart-schema-negative.txt b/cmd/helm/testdata/output/subchart-schema-negative.txt new file mode 100644 index 000000000..5a84170fd --- /dev/null +++ b/cmd/helm/testdata/output/subchart-schema-negative.txt @@ -0,0 +1,6 @@ +Error: values don't meet the specifications of the schema(s) in the following chart(s): +chart-without-schema: +- (root): lastname is required +subchart-with-schema: +- (root): age is required + diff --git a/cmd/helm/testdata/output/uninstall-purge.txt b/cmd/helm/testdata/output/uninstall-keep-history.txt similarity index 100% rename from cmd/helm/testdata/output/uninstall-purge.txt rename to cmd/helm/testdata/output/uninstall-keep-history.txt diff --git a/cmd/helm/testdata/output/uninstall-multiple.txt b/cmd/helm/testdata/output/uninstall-multiple.txt new file mode 100644 index 000000000..ee1c67d2f --- /dev/null +++ b/cmd/helm/testdata/output/uninstall-multiple.txt @@ -0,0 +1,2 @@ +release "aeneas" uninstalled +release "aeneas2" uninstalled diff --git a/cmd/helm/testdata/output/upgrade-with-bad-dependencies.txt b/cmd/helm/testdata/output/upgrade-with-bad-dependencies.txt index 5652097a6..6dddc7344 100644 --- a/cmd/helm/testdata/output/upgrade-with-bad-dependencies.txt +++ b/cmd/helm/testdata/output/upgrade-with-bad-dependencies.txt @@ -1 +1 @@ -Error: cannot load Chart.yaml: error converting YAML to JSON: yaml: line 5: did not find expected '-' indicator +Error: cannot load Chart.yaml: error converting YAML to JSON: yaml: line 6: did not find expected '-' indicator diff --git a/cmd/helm/testdata/output/upgrade-with-install-timeout.txt b/cmd/helm/testdata/output/upgrade-with-install-timeout.txt index fb64f1f86..753979405 100644 --- a/cmd/helm/testdata/output/upgrade-with-install-timeout.txt +++ b/cmd/helm/testdata/output/upgrade-with-install-timeout.txt @@ -4,8 +4,3 @@ LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC NAMESPACE: default STATUS: deployed -NOTES: -1. Get the application URL by running these commands: - export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=crazy-bunny" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 diff --git a/cmd/helm/testdata/output/upgrade-with-install.txt b/cmd/helm/testdata/output/upgrade-with-install.txt index 8aba923a8..d08b923e8 100644 --- a/cmd/helm/testdata/output/upgrade-with-install.txt +++ b/cmd/helm/testdata/output/upgrade-with-install.txt @@ -4,8 +4,3 @@ LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC NAMESPACE: default STATUS: deployed -NOTES: -1. Get the application URL by running these commands: - export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=zany-bunny" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 diff --git a/cmd/helm/testdata/output/upgrade-with-reset-values.txt b/cmd/helm/testdata/output/upgrade-with-reset-values.txt index 4b0f9c309..1cfe36a34 100644 --- a/cmd/helm/testdata/output/upgrade-with-reset-values.txt +++ b/cmd/helm/testdata/output/upgrade-with-reset-values.txt @@ -4,8 +4,3 @@ LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC NAMESPACE: default STATUS: deployed -NOTES: -1. Get the application URL by running these commands: - export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=funny-bunny" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 diff --git a/cmd/helm/testdata/output/upgrade-with-reset-values2.txt b/cmd/helm/testdata/output/upgrade-with-reset-values2.txt index 4b0f9c309..1cfe36a34 100644 --- a/cmd/helm/testdata/output/upgrade-with-reset-values2.txt +++ b/cmd/helm/testdata/output/upgrade-with-reset-values2.txt @@ -4,8 +4,3 @@ LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC NAMESPACE: default STATUS: deployed -NOTES: -1. Get the application URL by running these commands: - export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=funny-bunny" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 diff --git a/cmd/helm/testdata/output/upgrade-with-timeout.txt b/cmd/helm/testdata/output/upgrade-with-timeout.txt index 4b0f9c309..1cfe36a34 100644 --- a/cmd/helm/testdata/output/upgrade-with-timeout.txt +++ b/cmd/helm/testdata/output/upgrade-with-timeout.txt @@ -4,8 +4,3 @@ LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC NAMESPACE: default STATUS: deployed -NOTES: -1. Get the application URL by running these commands: - export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=funny-bunny" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 diff --git a/cmd/helm/testdata/output/upgrade-with-wait.txt b/cmd/helm/testdata/output/upgrade-with-wait.txt index fb64f1f86..753979405 100644 --- a/cmd/helm/testdata/output/upgrade-with-wait.txt +++ b/cmd/helm/testdata/output/upgrade-with-wait.txt @@ -4,8 +4,3 @@ LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC NAMESPACE: default STATUS: deployed -NOTES: -1. Get the application URL by running these commands: - export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=crazy-bunny" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 diff --git a/cmd/helm/testdata/output/upgrade.txt b/cmd/helm/testdata/output/upgrade.txt index 4b0f9c309..1cfe36a34 100644 --- a/cmd/helm/testdata/output/upgrade.txt +++ b/cmd/helm/testdata/output/upgrade.txt @@ -4,8 +4,3 @@ LAST DEPLOYED: 1977-09-02 22:04:05 +0000 UTC NAMESPACE: default STATUS: deployed -NOTES: -1. Get the application URL by running these commands: - export POD_NAME=$(kubectl get pods -l "app=testUpgradeChart,release=funny-bunny" -o jsonpath="{.items[0].metadata.name}") - echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:80 diff --git a/cmd/helm/testdata/testcharts/alpine/Chart.yaml b/cmd/helm/testdata/testcharts/alpine/Chart.yaml index e45d7326a..eec261220 100644 --- a/cmd/helm/testdata/testcharts/alpine/Chart.yaml +++ b/cmd/helm/testdata/testcharts/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: Deploy a basic Alpine Linux pod home: https://helm.sh/helm name: alpine diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml index ba72c77bf..1f445ee11 100644 --- a/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml +++ b/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: chart-missing-deps version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/Chart.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/Chart.yaml index c3813bc8c..356135537 100644 --- a/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/Chart.yaml +++ b/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: reqsubchart version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/Chart.yaml b/cmd/helm/testdata/testcharts/chart-bad-type/Chart.yaml index 75767a62c..e77b5afaa 100644 --- a/cmd/helm/testdata/testcharts/chart-bad-type/Chart.yaml +++ b/cmd/helm/testdata/testcharts/chart-bad-type/Chart.yaml @@ -1,7 +1,8 @@ +apiVersion: v1 description: Deploy a basic Alpine Linux pod home: https://helm.sh/helm name: chart-bad-type sources: -- https://github.com/helm/helm + - https://github.com/helm/helm version: 0.1.0 type: foobar diff --git a/cmd/helm/testdata/testcharts/chart-missing-deps/Chart.yaml b/cmd/helm/testdata/testcharts/chart-missing-deps/Chart.yaml index 8cd47d20c..9605636db 100644 --- a/cmd/helm/testdata/testcharts/chart-missing-deps/Chart.yaml +++ b/cmd/helm/testdata/testcharts/chart-missing-deps/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: chart-missing-deps version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/Chart.yaml b/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/Chart.yaml index c3813bc8c..356135537 100644 --- a/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/Chart.yaml +++ b/cmd/helm/testdata/testcharts/chart-missing-deps/charts/reqsubchart/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: reqsubchart version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/Chart.yaml new file mode 100644 index 000000000..4e24c2ebb --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +name: chart-without-schema +description: A Helm chart for Kubernetes +type: application +version: 0.1.0 +appVersion: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/Chart.yaml new file mode 100644 index 000000000..b5a77c5db --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +name: subchart-with-schema +description: A Helm chart for Kubernetes +type: application +version: 0.1.0 +appVersion: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/templates/empty.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/templates/empty.yaml new file mode 100644 index 000000000..c80812f6e --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/templates/empty.yaml @@ -0,0 +1 @@ +# This file is intentionally blank diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.schema.json b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.schema.json new file mode 100644 index 000000000..4ff791844 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.schema.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Values", + "type": "object", + "properties": { + "age": { + "description": "Age", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "age" + ] +} diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/charts/subchart-with-schema/values.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/templates/empty.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/templates/empty.yaml new file mode 100644 index 000000000..c80812f6e --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/templates/empty.yaml @@ -0,0 +1 @@ +# This file is intentionally blank diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.schema.json b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.schema.json new file mode 100644 index 000000000..f30948038 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.schema.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Values", + "type": "object", + "properties": { + "firstname": { + "description": "First name", + "type": "string" + }, + "lastname": { + "type": "string" + } + }, + "required": [ + "firstname", + "lastname" + ] +} diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.yaml new file mode 100644 index 000000000..c9deafc00 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-and-subchart/values.yaml @@ -0,0 +1 @@ +firstname: "John" diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-negative/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-negative/Chart.yaml new file mode 100644 index 000000000..395d24f6a --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-negative/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +description: Empty testing chart +home: https://k8s.io/helm +name: empty +sources: +- https://github.com/kubernetes/helm +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-negative/templates/empty.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-negative/templates/empty.yaml new file mode 100644 index 000000000..c80812f6e --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-negative/templates/empty.yaml @@ -0,0 +1 @@ +# This file is intentionally blank diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.schema.json b/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.schema.json new file mode 100644 index 000000000..4df89bbe8 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "addresses": { + "description": "List of addresses", + "items": { + "properties": { + "city": { + "type": "string" + }, + "number": { + "type": "number" + }, + "street": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "age": { + "description": "Age", + "minimum": 0, + "type": "integer" + }, + "employmentInfo": { + "properties": { + "salary": { + "minimum": 0, + "type": "number" + }, + "title": { + "type": "string" + } + }, + "required": [ + "salary" + ], + "type": "object" + }, + "firstname": { + "description": "First name", + "type": "string" + }, + "lastname": { + "type": "string" + }, + "likesCoffee": { + "type": "boolean" + }, + "phoneNumbers": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "firstname", + "lastname", + "addresses", + "employmentInfo" + ], + "title": "Values", + "type": "object" +} diff --git a/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.yaml new file mode 100644 index 000000000..5a1250bff --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema-negative/values.yaml @@ -0,0 +1,14 @@ +firstname: John +lastname: Doe +age: -5 +likesCoffee: true +addresses: + - city: Springfield + street: Main + number: 12345 + - city: New York + street: Broadway + number: 67890 +phoneNumbers: + - "(888) 888-8888" + - "(555) 555-5555" diff --git a/cmd/helm/testdata/testcharts/chart-with-schema/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-schema/Chart.yaml new file mode 100644 index 000000000..395d24f6a --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +description: Empty testing chart +home: https://k8s.io/helm +name: empty +sources: +- https://github.com/kubernetes/helm +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-schema/extra-values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema/extra-values.yaml new file mode 100644 index 000000000..76c290c4f --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema/extra-values.yaml @@ -0,0 +1,2 @@ +age: -5 +employmentInfo: null diff --git a/cmd/helm/testdata/testcharts/chart-with-schema/templates/empty.yaml b/cmd/helm/testdata/testcharts/chart-with-schema/templates/empty.yaml new file mode 100644 index 000000000..c80812f6e --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema/templates/empty.yaml @@ -0,0 +1 @@ +# This file is intentionally blank diff --git a/cmd/helm/testdata/testcharts/chart-with-schema/values.schema.json b/cmd/helm/testdata/testcharts/chart-with-schema/values.schema.json new file mode 100644 index 000000000..4df89bbe8 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema/values.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "addresses": { + "description": "List of addresses", + "items": { + "properties": { + "city": { + "type": "string" + }, + "number": { + "type": "number" + }, + "street": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "age": { + "description": "Age", + "minimum": 0, + "type": "integer" + }, + "employmentInfo": { + "properties": { + "salary": { + "minimum": 0, + "type": "number" + }, + "title": { + "type": "string" + } + }, + "required": [ + "salary" + ], + "type": "object" + }, + "firstname": { + "description": "First name", + "type": "string" + }, + "lastname": { + "type": "string" + }, + "likesCoffee": { + "type": "boolean" + }, + "phoneNumbers": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "firstname", + "lastname", + "addresses", + "employmentInfo" + ], + "title": "Values", + "type": "object" +} diff --git a/cmd/helm/testdata/testcharts/chart-with-schema/values.yaml b/cmd/helm/testdata/testcharts/chart-with-schema/values.yaml new file mode 100644 index 000000000..042dea664 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-schema/values.yaml @@ -0,0 +1,17 @@ +firstname: John +lastname: Doe +age: 25 +likesCoffee: true +employmentInfo: + title: Software Developer + salary: 100000 +addresses: + - city: Springfield + street: Main + number: 12345 + - city: New York + street: Broadway + number: 67890 +phoneNumbers: + - "(888) 888-8888" + - "(555) 555-5555" diff --git a/cmd/helm/testdata/testcharts/compressedchart-0.1.0.tgz b/cmd/helm/testdata/testcharts/compressedchart-0.1.0.tgz index 575b27128..3c9c24d76 100644 Binary files a/cmd/helm/testdata/testcharts/compressedchart-0.1.0.tgz and b/cmd/helm/testdata/testcharts/compressedchart-0.1.0.tgz differ diff --git a/cmd/helm/testdata/testcharts/compressedchart-0.2.0.tgz b/cmd/helm/testdata/testcharts/compressedchart-0.2.0.tgz index ba96a80c9..16a644a79 100644 Binary files a/cmd/helm/testdata/testcharts/compressedchart-0.2.0.tgz and b/cmd/helm/testdata/testcharts/compressedchart-0.2.0.tgz differ diff --git a/cmd/helm/testdata/testcharts/compressedchart-0.3.0.tgz b/cmd/helm/testdata/testcharts/compressedchart-0.3.0.tgz index 89776bfa8..051bd6fd9 100644 Binary files a/cmd/helm/testdata/testcharts/compressedchart-0.3.0.tgz and b/cmd/helm/testdata/testcharts/compressedchart-0.3.0.tgz differ diff --git a/cmd/helm/testdata/testcharts/decompressedchart/Chart.yaml b/cmd/helm/testdata/testcharts/decompressedchart/Chart.yaml index 3e65afdfa..92ba4d88f 100644 --- a/cmd/helm/testdata/testcharts/decompressedchart/Chart.yaml +++ b/cmd/helm/testdata/testcharts/decompressedchart/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: decompressedchart version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/empty/Chart.yaml b/cmd/helm/testdata/testcharts/empty/Chart.yaml index 8bdba0330..4f1dc0012 100644 --- a/cmd/helm/testdata/testcharts/empty/Chart.yaml +++ b/cmd/helm/testdata/testcharts/empty/Chart.yaml @@ -1,6 +1,7 @@ +apiVersion: v1 description: Empty testing chart home: https://helm.sh/helm name: empty sources: -- https://github.com/helm/helm + - https://github.com/helm/helm version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/issue1979/Chart.yaml b/cmd/helm/testdata/testcharts/issue1979/Chart.yaml index e45d7326a..5269b5cf6 100644 --- a/cmd/helm/testdata/testcharts/issue1979/Chart.yaml +++ b/cmd/helm/testdata/testcharts/issue1979/Chart.yaml @@ -1,6 +1,7 @@ +apiVersion: v1 description: Deploy a basic Alpine Linux pod home: https://helm.sh/helm name: alpine sources: -- https://github.com/helm/helm + - https://github.com/helm/helm version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/novals/Chart.yaml b/cmd/helm/testdata/testcharts/novals/Chart.yaml index 905258117..ada85a4c4 100644 --- a/cmd/helm/testdata/testcharts/novals/Chart.yaml +++ b/cmd/helm/testdata/testcharts/novals/Chart.yaml @@ -1,6 +1,7 @@ +apiVersion: v1 description: Deploy a basic Alpine Linux pod home: https://helm.sh/helm name: novals sources: -- https://github.com/helm/helm + - https://github.com/helm/helm version: 0.2.0 diff --git a/cmd/helm/testdata/testcharts/reqtest-0.1.0.tgz b/cmd/helm/testdata/testcharts/reqtest-0.1.0.tgz index 8618e91d7..5d8e46a50 100644 Binary files a/cmd/helm/testdata/testcharts/reqtest-0.1.0.tgz and b/cmd/helm/testdata/testcharts/reqtest-0.1.0.tgz differ diff --git a/cmd/helm/testdata/testcharts/reqtest/Chart.yaml b/cmd/helm/testdata/testcharts/reqtest/Chart.yaml index e38826af3..07b6e2c97 100644 --- a/cmd/helm/testdata/testcharts/reqtest/Chart.yaml +++ b/cmd/helm/testdata/testcharts/reqtest/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: reqtest version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/Chart.yaml b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/Chart.yaml index c3813bc8c..356135537 100644 --- a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/Chart.yaml +++ b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: reqsubchart version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/Chart.yaml b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/Chart.yaml index 9f7c22a71..5b9277370 100644 --- a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/Chart.yaml +++ b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart2/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: reqsubchart2 version: 0.2.0 diff --git a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart3-0.2.0.tgz b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart3-0.2.0.tgz index 84b0fb65e..37962b0ab 100644 Binary files a/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart3-0.2.0.tgz and b/cmd/helm/testdata/testcharts/reqtest/charts/reqsubchart3-0.2.0.tgz differ diff --git a/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz b/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz index 6de9d988d..c74e5b0ef 100644 Binary files a/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz and b/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz differ diff --git a/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz.prov b/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz.prov index 94235399a..d325bb266 100644 --- a/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz.prov +++ b/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz.prov @@ -1,20 +1,21 @@ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 +apiVersion: v1 description: A Helm chart for Kubernetes name: signtest version: 0.1.0 ... files: - signtest-0.1.0.tgz: sha256:dee72947753628425b82814516bdaa37aef49f25e8820dd2a6e15a33a007823b + signtest-0.1.0.tgz: sha256:e5ef611620fb97704d8751c16bab17fedb68883bfb0edc76f78a70e9173f9b55 -----BEGIN PGP SIGNATURE----- -wsBcBAEBCgAQBQJXomNHCRCEO7+YH8GHYgAALywIAG1Me852Fpn1GYu8Q1GCcw4g -l2k7vOFchdDwDhdSVbkh4YyvTaIO3iE2Jtk1rxw+RIJiUr0eLO/rnIJuxZS8WKki -DR1LI9J1VD4dxN3uDETtWDWq7ScoPsRY5mJvYZXC8whrWEt/H2kfqmoA9LloRPWp -flOE0iktA4UciZOblTj6nAk3iDyjh/4HYL4a6tT0LjjKI7OTw4YyHfjHad1ywVCz -9dMUc1rPgTnl+fnRiSPSrlZIWKOt1mcQ4fVrU3nwtRUwTId2k8FtygL0G6M+Y6t0 -S6yaU7qfk9uTxkdkUF7Bf1X3ukxfe+cNBC32vf4m8LY4NkcYfSqK2fGtQsnVr6s= -=NyOM +wsBcBAEBCgAQBQJcoosfCRCEO7+YH8GHYgAA220IALAs8T8NPgkcLvHu+5109cAN +BOCNPSZDNsqLZW/2Dc9cKoBG7Jen4Qad+i5l9351kqn3D9Gm6eRfAWcjfggRobV/ +9daZ19h0nl4O1muQNAkjvdgZt8MOP3+PB3I3/Tu2QCYjI579SLUmuXlcZR5BCFPR +PJy+e3QpV2PcdeU2KZLG4tjtlrq+3QC9ZHHEJLs+BVN9d46Dwo6CxJdHJrrrAkTw +M8MhA92vbiTTPRSCZI9x5qDAwJYhoq0oxLflpuL2tIlo3qVoCsaTSURwMESEHO32 +XwYG7BaVDMELWhAorBAGBGBwWFbJ1677qQ2gd9CN0COiVhekWlFRcnn60800r84= +=k9Y9 -----END PGP SIGNATURE----- \ No newline at end of file diff --git a/cmd/helm/testdata/testcharts/signtest/Chart.yaml b/cmd/helm/testdata/testcharts/signtest/Chart.yaml index 90964b44a..f1f73723a 100644 --- a/cmd/helm/testdata/testcharts/signtest/Chart.yaml +++ b/cmd/helm/testdata/testcharts/signtest/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: signtest version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/signtest/alpine/Chart.yaml b/cmd/helm/testdata/testcharts/signtest/alpine/Chart.yaml index e45d7326a..eec261220 100644 --- a/cmd/helm/testdata/testcharts/signtest/alpine/Chart.yaml +++ b/cmd/helm/testdata/testcharts/signtest/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: Deploy a basic Alpine Linux pod home: https://helm.sh/helm name: alpine diff --git a/cmd/helm/uninstall.go b/cmd/helm/uninstall.go index 7d0bb09ff..37da40666 100644 --- a/cmd/helm/uninstall.go +++ b/cmd/helm/uninstall.go @@ -27,8 +27,10 @@ import ( ) const uninstallDesc = ` -This command takes a release name, and then uninstalls the release from Kubernetes. -It removes all of the resources associated with the last release of the chart. +This command takes a release name and uninstalls the release. + +It removes all of the resources associated with the last release of the chart +as well as the release history, freeing it up for future use. Use the '--dry-run' flag to see which releases will be uninstalled without actually uninstalling them. @@ -41,13 +43,13 @@ func newUninstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { Use: "uninstall RELEASE_NAME [...]", Aliases: []string{"del", "delete", "un"}, SuggestFor: []string{"remove", "rm"}, - Short: "given a release name, uninstall the release from Kubernetes", + Short: "uninstall a release", Long: uninstallDesc, Args: require.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { for i := 0; i < len(args); i++ { - res, err := client.Run(args[0]) + res, err := client.Run(args[i]) if err != nil { return err } @@ -64,7 +66,7 @@ func newUninstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f := cmd.Flags() f.BoolVar(&client.DryRun, "dry-run", false, "simulate a uninstall") f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during uninstallation") - f.BoolVar(&client.Purge, "purge", false, "remove the release from the store and make its name free for later use") + f.BoolVar(&client.KeepHistory, "keep-history", false, "remove all associated resources and mark the release as deleted, but retain the release history") f.Int64Var(&client.Timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") return cmd diff --git a/cmd/helm/uninstall_test.go b/cmd/helm/uninstall_test.go index 5ebb20ead..409019a3c 100644 --- a/cmd/helm/uninstall_test.go +++ b/cmd/helm/uninstall_test.go @@ -30,6 +30,15 @@ func TestUninstall(t *testing.T) { golden: "output/uninstall.txt", rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "aeneas"})}, }, + { + name: "multiple uninstall", + cmd: "uninstall aeneas aeneas2", + golden: "output/uninstall-multiple.txt", + rels: []*release.Release{ + release.Mock(&release.MockReleaseOptions{Name: "aeneas"}), + release.Mock(&release.MockReleaseOptions{Name: "aeneas2"}), + }, + }, { name: "uninstall with timeout", cmd: "uninstall aeneas --timeout 120", @@ -43,9 +52,9 @@ func TestUninstall(t *testing.T) { rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "aeneas"})}, }, { - name: "purge", - cmd: "uninstall aeneas --purge", - golden: "output/uninstall-purge.txt", + name: "keep history", + cmd: "uninstall aeneas --keep-history", + golden: "output/uninstall-keep-history.txt", rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "aeneas"})}, }, { diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index bba9256fc..d8f9d5bf0 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -18,6 +18,7 @@ package main import ( "fmt" + "path/filepath" "testing" "helm.sh/helm/pkg/chart" @@ -28,13 +29,16 @@ import ( func TestUpgradeCmd(t *testing.T) { tmpChart := testTempDir(t) - cfile := &chart.Metadata{ - Name: "testUpgradeChart", - Description: "A Helm chart for Kubernetes", - Version: "0.1.0", + cfile := &chart.Chart{ + Metadata: &chart.Metadata{ + APIVersion: chart.APIVersionV1, + Name: "testUpgradeChart", + Description: "A Helm chart for Kubernetes", + Version: "0.1.0", + }, } - chartPath, err := chartutil.Create(cfile, tmpChart) - if err != nil { + chartPath := filepath.Join(tmpChart, cfile.Metadata.Name) + if err := chartutil.SaveDir(cfile, tmpChart); err != nil { t.Fatalf("Error creating chart for upgrade: %v", err) } ch, err := loader.Load(chartPath) @@ -47,14 +51,9 @@ func TestUpgradeCmd(t *testing.T) { }) // update chart version - cfile = &chart.Metadata{ - Name: "testUpgradeChart", - Description: "A Helm chart for Kubernetes", - Version: "0.1.2", - } + cfile.Metadata.Version = "0.1.2" - chartPath, err = chartutil.Create(cfile, tmpChart) - if err != nil { + if err := chartutil.SaveDir(cfile, tmpChart); err != nil { t.Fatalf("Error creating chart: %v", err) } ch, err = loader.Load(chartPath) @@ -63,14 +62,9 @@ func TestUpgradeCmd(t *testing.T) { } // update chart version again - cfile = &chart.Metadata{ - Name: "testUpgradeChart", - Description: "A Helm chart for Kubernetes", - Version: "0.1.3", - } + cfile.Metadata.Version = "0.1.3" - chartPath, err = chartutil.Create(cfile, tmpChart) - if err != nil { + if err := chartutil.SaveDir(cfile, tmpChart); err != nil { t.Fatalf("Error creating chart: %v", err) } var ch2 *chart.Chart diff --git a/docs/charts.md b/docs/charts.md index 096b8ae6f..442269e55 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -26,6 +26,7 @@ wordpress/ LICENSE # OPTIONAL: A plain text file containing the license for the chart README.md # OPTIONAL: A human-readable README file values.yaml # The default configuration values for this chart + values.schema.json # OPTIONAL: A JSON Schema for imposing a structure on the values.yaml file charts/ # A directory containing any charts upon which this chart depends. templates/ # A directory of templates that, when combined with values, # will generate valid Kubernetes manifest files. @@ -763,14 +764,98 @@ parent chart. Also, global variables of parent charts take precedence over the global variables from subcharts. +### Schema Files + +Sometimes, a chart maintainer might want to define a structure on their values. +This can be done by defining a schema in the `values.schema.json` file. A +schema is represented as a [JSON Schema](https://json-schema.org/). +It might look something like this: + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "image": { + "description": "Container Image", + "properties": { + "repo": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "type": "object" + }, + "name": { + "description": "Service name", + "type": "string" + }, + "port": { + "description": "Port", + "minimum": 0, + "type": "integer" + }, + "protocol": { + "type": "string" + } + }, + "required": [ + "protocol", + "port" + ], + "title": "Values", + "type": "object" +} +``` + +This schema will be applied to the values to validate it. Validation occurs +when any of the following commands are invoked: + +* `helm install` +* `helm upgrade` +* `helm lint` +* `helm template` + +An example of a +`values.yaml` file that meets the requirements of this schema might look +something like this: + +```yaml +name: frontend +protocol: https +port: 443 +``` + +Note that the schema is applied to the final `.Values` object, and not just to +the `values.yaml` file. This means that the following `yaml` file is valid, +given that the chart is installed with the appropriate `--set` option shown +below. + +```yaml +name: frontend +protocol: https +``` + +```` +helm install --set port=443 +```` + +Furthermore, the final `.Values` object is checked against *all* subchart +schemas. This means that restrictions on a subchart can't be circumvented by a +parent chart. This also works backwards - if a subchart has a requirement that +is not met in the subchart's `values.yaml` file, the parent chart *must* +satisfy those restrictions in order to be valid. + ### References -When it comes to writing templates and values files, there are several +When it comes to writing templates, values, and schema files, there are several standard references that will help you out. - [Go templates](https://godoc.org/text/template) - [Extra template functions](https://godoc.org/github.com/Masterminds/sprig) - [The YAML format](http://yaml.org/spec/) +- [JSON Schema](https://json-schema.org/) ## Using Helm to Manage Charts diff --git a/docs/developers.md b/docs/developers.md index c87a38a96..47ce2132a 100644 --- a/docs/developers.md +++ b/docs/developers.md @@ -19,7 +19,7 @@ $ make bootstrap build ``` NOTE: This will fail if not running from the path `$GOPATH/src/helm.sh/helm`. The -directory `k8s.io` should not be a symlink or `build` will not find the relevant +directory `helm.sh` should not be a symlink or `build` will not find the relevant packages. This will build both Helm and the Helm library. `make bootstrap` will attempt to @@ -94,7 +94,7 @@ home of the current development candidate. Releases are tagged. We accept changes to the code via GitHub Pull Requests (PRs). One workflow for doing this is as follows: -1. Go to your `$GOPATH/src/k8s.io` directory and `git clone` the +1. Go to your `$GOPATH/src` directory, then `mkdir helm.sh; cd helm.sh` and `git clone` the `github.com/helm/helm` repository. 2. Fork that repository into your GitHub account 3. Add your repository as a remote for `$GOPATH/src/helm.sh/helm` diff --git a/docs/examples/alpine/Chart.yaml b/docs/examples/alpine/Chart.yaml index e56f8a469..a2403f594 100644 --- a/docs/examples/alpine/Chart.yaml +++ b/docs/examples/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: alpine description: Deploy a basic Alpine Linux pod version: 0.1.0 diff --git a/docs/examples/nginx/Chart.yaml b/docs/examples/nginx/Chart.yaml index 3d6f5751b..a6a942435 100644 --- a/docs/examples/nginx/Chart.yaml +++ b/docs/examples/nginx/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: nginx description: A basic NGINX HTTP server version: 0.1.0 diff --git a/docs/examples/nginx/charts/alpine/Chart.yaml b/docs/examples/nginx/charts/alpine/Chart.yaml index e56f8a469..a2403f594 100644 --- a/docs/examples/nginx/charts/alpine/Chart.yaml +++ b/docs/examples/nginx/charts/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: alpine description: Deploy a basic Alpine Linux pod version: 0.1.0 diff --git a/docs/faq.md b/docs/faq.md index 4d37e8a24..85bfc5350 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -15,6 +15,16 @@ Here's an exhaustive list of all the major changes introduced in Helm 3. In Helm 3, Helm switched the Go import path over from `k8s.io/helm` to `helm.sh/helm`. If you intend to upgrade to the Helm 3 Go client libraries, make sure to change your import paths. +### Helm delete + +In order to better align the verbiage from other package managers, `helm delete` was re-named to +`helm uninstall`. `helm delete` is still retained as an alias to `helm uninstall`, so either form +can be used. + +In Helm 2, in order to purge the release ledger, the `--purge` flag had to be provided. This +functionality is now enabled by default. To retain the previous behaviour, use +`helm uninstall --keep-history`. + ## Installing diff --git a/pkg/action/history.go b/pkg/action/history.go index 290154986..36a11710e 100644 --- a/pkg/action/history.go +++ b/pkg/action/history.go @@ -34,6 +34,7 @@ type releaseInfo struct { Updated string `json:"updated"` Status string `json:"status"` Chart string `json:"chart"` + AppVersion string `json:"app_version"` Description string `json:"description"` } @@ -142,11 +143,13 @@ func getReleaseHistory(rls []*release.Release) (history releaseHistory) { s := r.Info.Status.String() v := r.Version d := r.Info.Description + a := formatAppVersion(r.Chart) rInfo := releaseInfo{ Revision: v, Status: s, Chart: c, + AppVersion: a, Description: d, } if !r.Info.LastDeployed.IsZero() { @@ -162,10 +165,10 @@ func getReleaseHistory(rls []*release.Release) (history releaseHistory) { func formatAsTable(releases releaseHistory) []byte { tbl := uitable.New() - tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "DESCRIPTION") + tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION", "DESCRIPTION") for i := 0; i <= len(releases)-1; i++ { r := releases[i] - tbl.AddRow(r.Revision, r.Updated, r.Status, r.Chart, r.Description) + tbl.AddRow(r.Revision, r.Updated, r.Status, r.Chart, r.AppVersion, r.Description) } return tbl.Bytes() } @@ -178,3 +181,12 @@ func formatChartname(c *chart.Chart) string { } return fmt.Sprintf("%s-%s", c.Name(), c.Metadata.Version) } + +func formatAppVersion(c *chart.Chart) string { + if c == nil || c.Metadata == nil { + // This is an edge case that has happened in prod, though we don't + // know how: https://github.com/helm/helm/issues/1347 + return "MISSING" + } + return c.AppVersion() +} diff --git a/pkg/action/lint_test.go b/pkg/action/lint_test.go index c442be344..eec9f9533 100644 --- a/pkg/action/lint_test.go +++ b/pkg/action/lint_test.go @@ -29,6 +29,8 @@ var ( invalidArchivedChartPath = "../../cmd/helm/testdata/testcharts/invalidcompressedchart0.1.0.tgz" chartDirPath = "../../cmd/helm/testdata/testcharts/decompressedchart/" chartMissingManifest = "../../cmd/helm/testdata/testcharts/chart-missing-manifest" + chartSchema = "../../cmd/helm/testdata/testcharts/chart-with-schema" + chartSchemaNegative = "../../cmd/helm/testdata/testcharts/chart-with-schema-negative" ) func TestLintChart(t *testing.T) { @@ -47,4 +49,10 @@ func TestLintChart(t *testing.T) { if _, err := lintChart(chartMissingManifest, values, namespace, strict); err == nil { t.Error("Expected a chart parsing error") } + if _, err := lintChart(chartSchema, values, namespace, strict); err != nil { + t.Error(err) + } + if _, err := lintChart(chartSchemaNegative, values, namespace, strict); err != nil { + t.Error(err) + } } diff --git a/pkg/action/uninstall.go b/pkg/action/uninstall.go index e44b15583..11d1b5225 100644 --- a/pkg/action/uninstall.go +++ b/pkg/action/uninstall.go @@ -38,7 +38,7 @@ type Uninstall struct { DisableHooks bool DryRun bool - Purge bool + KeepHistory bool Timeout int64 } @@ -78,7 +78,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error) // TODO: Are there any cases where we want to force a delete even if it's // already marked deleted? if rel.Info.Status == release.StatusUninstalled { - if u.Purge { + if !u.KeepHistory { if err := u.purgeReleases(rels...); err != nil { return nil, errors.Wrap(err, "uninstall: Failed to purge the release") } @@ -119,7 +119,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error) rel.Info.Status = release.StatusUninstalled rel.Info.Description = "Uninstallation complete" - if u.Purge { + if !u.KeepHistory { u.cfg.Log("purge requested for %s", name) err := u.purgeReleases(rels...) return res, errors.Wrap(err, "uninstall: Failed to purge the release") diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go index ac2bf8b6b..c016d3e33 100644 --- a/pkg/chart/chart.go +++ b/pkg/chart/chart.go @@ -15,8 +15,8 @@ limitations under the License. package chart -// APIVersionv1 is the API version number for version 1. -const APIVersionv1 = "v1" +// APIVersionV1 is the API version number for version 1. +const APIVersionV1 = "v1" // Chart is a helm package that contains metadata, a default config, zero or more // optionally parameterizable templates, and zero or more charts (dependencies). @@ -31,6 +31,8 @@ type Chart struct { RawValues []byte // Values are default config for this template. Values map[string]interface{} + // Schema is an optional JSON schema for imposing structure on Values + Schema []byte // Files are miscellaneous files in a chart archive, // e.g. README, LICENSE, etc. Files []*File @@ -96,3 +98,15 @@ func (ch *Chart) ChartFullPath() string { } return ch.Name() } + +func (ch *Chart) Validate() error { + return ch.Metadata.Validate() +} + +// AppVersion returns the appversion of the chart. +func (ch *Chart) AppVersion() string { + if ch.Metadata == nil { + return "" + } + return ch.Metadata.AppVersion +} diff --git a/pkg/chart/loader/load.go b/pkg/chart/loader/load.go index 0782a964e..cd886d8c7 100644 --- a/pkg/chart/loader/load.go +++ b/pkg/chart/loader/load.go @@ -90,6 +90,8 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { return c, errors.Wrap(err, "cannot load values.yaml") } c.RawValues = f.Data + case f.Name == "values.schema.json": + c.Schema = 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/"): @@ -106,12 +108,8 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { } } - // 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") + if err := c.Validate(); err != nil { + return c, err } for n, files := range subcharts { diff --git a/pkg/chart/loader/load_test.go b/pkg/chart/loader/load_test.go index 686f50dcf..c5ee1e5a1 100644 --- a/pkg/chart/loader/load_test.go +++ b/pkg/chart/loader/load_test.go @@ -17,6 +17,7 @@ limitations under the License. package loader import ( + "bytes" "testing" "helm.sh/helm/pkg/chart" @@ -78,6 +79,10 @@ icon: https://example.com/64x64.png Name: "values.yaml", Data: []byte("var: some values"), }, + { + Name: "values.schema.json", + Data: []byte("type: Values"), + }, { Name: "templates/deployment.yaml", Data: []byte("some deployment"), @@ -101,6 +106,10 @@ icon: https://example.com/64x64.png t.Error("Expected chart values to be populated with default values") } + if !bytes.Equal(c.Schema, []byte("type: Values")) { + t.Error("Expected chart schema to be populated with default values") + } + if len(c.Templates) != 2 { t.Errorf("Expected number of templates == 2, got %d", len(c.Templates)) } @@ -109,7 +118,7 @@ icon: https://example.com/64x64.png if err == nil { t.Fatal("Expected err to be non-nil") } - if err.Error() != "chart metadata (Chart.yaml) missing" { + if err.Error() != "metadata is required" { t.Errorf("Expected chart metadata missing error, got '%s'", err.Error()) } } diff --git a/pkg/chart/loader/testdata/albatross/Chart.yaml b/pkg/chart/loader/testdata/albatross/Chart.yaml index eeef737ff..b5188fde0 100644 --- a/pkg/chart/loader/testdata/albatross/Chart.yaml +++ b/pkg/chart/loader/testdata/albatross/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: albatross description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chart/loader/testdata/frobnitz-1.2.3.tgz b/pkg/chart/loader/testdata/frobnitz-1.2.3.tgz index 983820e06..b2b76a83c 100644 Binary files a/pkg/chart/loader/testdata/frobnitz-1.2.3.tgz and b/pkg/chart/loader/testdata/frobnitz-1.2.3.tgz differ diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml index 4cc36dca5..79e0d65db 100644 --- a/pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml +++ b/pkg/chart/loader/testdata/frobnitz/charts/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: alpine description: Deploy a basic Alpine Linux pod version: 0.1.0 diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml index 171e36156..1c9dd5fa4 100644 --- a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml +++ b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: mast1 description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz index ced5a4a6a..61cb62051 100644 Binary files a/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz and b/pkg/chart/loader/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz b/pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz index 88c24d822..3190136b0 100644 Binary files a/pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz and b/pkg/chart/loader/testdata/frobnitz/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz b/pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz index 513bfca1a..a9d4c11d8 100644 Binary files a/pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz and b/pkg/chart/loader/testdata/frobnitz_backslash-1.2.3.tgz differ diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml index 4cc36dca5..79e0d65db 100755 --- a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml +++ b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: alpine description: Deploy a basic Alpine Linux pod version: 0.1.0 diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml index 171e36156..1c9dd5fa4 100755 --- a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml +++ b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: mast1 description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz index ced5a4a6a..61cb62051 100755 Binary files a/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz and b/pkg/chart/loader/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz b/pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz index 3af333e76..3190136b0 100755 Binary files a/pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz and b/pkg/chart/loader/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chart/loader/testdata/genfrob.sh b/pkg/chart/loader/testdata/genfrob.sh index 8f2cddec1..35fdd59f2 100755 --- a/pkg/chart/loader/testdata/genfrob.sh +++ b/pkg/chart/loader/testdata/genfrob.sh @@ -6,7 +6,9 @@ tar -zcvf mariner/charts/albatross-0.1.0.tgz albatross echo "Packing mariner into frobnitz" tar -zcvf frobnitz/charts/mariner-4.3.2.tgz mariner +tar -zcvf frobnitz_backslash/charts/mariner-4.3.2.tgz mariner # Pack the frobnitz chart. echo "Packing frobnitz" tar --exclude=ignore/* -zcvf frobnitz-1.2.3.tgz frobnitz +tar --exclude=ignore/* -zcvf frobnitz_backslash-1.2.3.tgz frobnitz_backslash diff --git a/pkg/chart/loader/testdata/mariner/Chart.yaml b/pkg/chart/loader/testdata/mariner/Chart.yaml index e2efb7f99..92dc4b390 100644 --- a/pkg/chart/loader/testdata/mariner/Chart.yaml +++ b/pkg/chart/loader/testdata/mariner/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: mariner description: A Helm chart for Kubernetes version: 4.3.2 diff --git a/pkg/chart/loader/testdata/mariner/charts/albatross-0.1.0.tgz b/pkg/chart/loader/testdata/mariner/charts/albatross-0.1.0.tgz index fa8ef5aca..128ef82f7 100644 Binary files a/pkg/chart/loader/testdata/mariner/charts/albatross-0.1.0.tgz and b/pkg/chart/loader/testdata/mariner/charts/albatross-0.1.0.tgz differ diff --git a/pkg/chart/metadata.go b/pkg/chart/metadata.go index d0f03a61a..4a4139d1b 100644 --- a/pkg/chart/metadata.go +++ b/pkg/chart/metadata.go @@ -15,6 +15,8 @@ limitations under the License. package chart +import "errors" + // Maintainer describes a Chart maintainer. type Maintainer struct { // Name is a user name or organization name @@ -65,3 +67,20 @@ type Metadata struct { // Specifies the chart type: application or library Type string `json:"type,omitempty"` } + +func (md *Metadata) Validate() error { + if md == nil { + return errors.New("metadata is required") + } + if md.APIVersion == "" { + return errors.New("metadata apiVersion is required") + } + if md.Name == "" { + return errors.New("metadata name is required") + } + if md.Version == "" { + return errors.New("metadata version is required") + } + // TODO validate valid semver here? + return nil +} diff --git a/pkg/chartutil/chartfile_test.go b/pkg/chartutil/chartfile_test.go index 358c8231d..33d149501 100644 --- a/pkg/chartutil/chartfile_test.go +++ b/pkg/chartutil/chartfile_test.go @@ -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 != chart.APIVersionv1 { - t.Errorf("Expected API Version %q, got %q", chart.APIVersionv1, f.APIVersion) + if f.APIVersion != chart.APIVersionV1 { + t.Errorf("Expected API Version %q, got %q", chart.APIVersionV1, f.APIVersion) } if f.Name != name { diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index dde89a752..e9e5e12e1 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -53,6 +53,29 @@ const ( HelpersName = "_helpers.tpl" ) +const defaultChartfile = `apiVersion: v1 +name: %s +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. +appVersion: 0.1.0 +` + const defaultValues = `# Default values for %s. # This is a YAML-formatted file. # Declare variables to be passed into your templates. @@ -61,7 +84,6 @@ replicaCount: 1 image: repository: nginx - tag: stable pullPolicy: IfNotPresent nameOverride: "" @@ -189,7 +211,7 @@ spec: spec: containers: - name: {{ .Chart.Name }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + image: "{{ .Values.image.repository }}:{{ .Release.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - name: http @@ -339,7 +361,7 @@ func CreateFrom(chartfile *chart.Metadata, dest, src string) error { // If Chart.yaml or any directories cannot be created, this will return an // error. In such a case, this will attempt to clean up by removing the // new chart directory. -func Create(chartfile *chart.Metadata, dir string) (string, error) { +func Create(name, dir string) (string, error) { path, err := filepath.Abs(dir) if err != nil { return path, err @@ -351,8 +373,7 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) { return path, errors.Errorf("no such directory %s", path) } - n := chartfile.Name - cdir := filepath.Join(path, n) + cdir := filepath.Join(path, name) if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() { return cdir, errors.Errorf("file %s already exists and is not a directory", cdir) } @@ -360,13 +381,6 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) { return cdir, err } - cf := filepath.Join(cdir, ChartfileName) - if _, err := os.Stat(cf); err != nil { - if err := SaveChartfile(cf, chartfile); err != nil { - return cdir, err - } - } - for _, d := range []string{TemplatesDir, ChartsDir} { if err := os.MkdirAll(filepath.Join(cdir, d), 0755); err != nil { return cdir, err @@ -377,10 +391,15 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) { path string content []byte }{ + { + // Chart.yaml + path: filepath.Join(cdir, ChartfileName), + content: []byte(fmt.Sprintf(defaultChartfile, name)), + }, { // values.yaml path: filepath.Join(cdir, ValuesfileName), - content: []byte(fmt.Sprintf(defaultValues, chartfile.Name)), + content: []byte(fmt.Sprintf(defaultValues, name)), }, { // .helmignore @@ -390,27 +409,27 @@ func Create(chartfile *chart.Metadata, dir string) (string, error) { { // ingress.yaml path: filepath.Join(cdir, TemplatesDir, IngressFileName), - content: transform(defaultIngress, chartfile.Name), + content: transform(defaultIngress, name), }, { // deployment.yaml path: filepath.Join(cdir, TemplatesDir, DeploymentName), - content: transform(defaultDeployment, chartfile.Name), + content: transform(defaultDeployment, name), }, { // service.yaml path: filepath.Join(cdir, TemplatesDir, ServiceName), - content: transform(defaultService, chartfile.Name), + content: transform(defaultService, name), }, { // NOTES.txt path: filepath.Join(cdir, TemplatesDir, NotesName), - content: transform(defaultNotes, chartfile.Name), + content: transform(defaultNotes, name), }, { // _helpers.tpl path: filepath.Join(cdir, TemplatesDir, HelpersName), - content: transform(defaultHelpers, chartfile.Name), + content: transform(defaultHelpers, name), }, } diff --git a/pkg/chartutil/create_test.go b/pkg/chartutil/create_test.go index ab8b43e96..256360327 100644 --- a/pkg/chartutil/create_test.go +++ b/pkg/chartutil/create_test.go @@ -34,9 +34,7 @@ func TestCreate(t *testing.T) { } defer os.RemoveAll(tdir) - cf := &chart.Metadata{Name: "foo"} - - c, err := Create(cf, tdir) + c, err := Create("foo", tdir) if err != nil { t.Fatal(err) } @@ -85,7 +83,11 @@ func TestCreateFrom(t *testing.T) { } defer os.RemoveAll(tdir) - cf := &chart.Metadata{Name: "foo"} + cf := &chart.Metadata{ + APIVersion: chart.APIVersionV1, + Name: "foo", + Version: "0.1.0", + } srcdir := "./testdata/mariner" if err := CreateFrom(cf, tdir, srcdir); err != nil { diff --git a/pkg/chartutil/dependencies_test.go b/pkg/chartutil/dependencies_test.go index 10f7d250f..629856ccd 100644 --- a/pkg/chartutil/dependencies_test.go +++ b/pkg/chartutil/dependencies_test.go @@ -27,6 +27,7 @@ import ( ) func loadChart(t *testing.T, path string) *chart.Chart { + t.Helper() c, err := loader.Load(path) if err != nil { t.Fatalf("failed to load testdata: %s", err) diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go index a456b206e..bde902c98 100644 --- a/pkg/chartutil/save.go +++ b/pkg/chartutil/save.go @@ -36,7 +36,10 @@ var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") func SaveDir(c *chart.Chart, dest string) error { // Create the chart directory outdir := filepath.Join(dest, c.Name()) - if err := os.Mkdir(outdir, 0755); err != nil { + if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() { + return errors.Errorf("file %s already exists and is not a directory", outdir) + } + if err := os.MkdirAll(outdir, 0755); err != nil { return err } @@ -109,18 +112,11 @@ func Save(c *chart.Chart, outDir string) (string, error) { return "", errors.Errorf("location %s is not a directory", outDir) } - if c.Metadata == nil { - return "", errors.New("no Chart.yaml data") - } - - cfile := c.Metadata - if cfile.Name == "" { - return "", errors.New("no chart name specified (Chart.yaml)") - } else if cfile.Version == "" { - return "", errors.New("no chart version specified (Chart.yaml)") + if err := c.Validate(); err != nil { + return "", errors.Wrap(err, "chart validation") } - filename := fmt.Sprintf("%s-%s.tgz", cfile.Name, cfile.Version) + filename := fmt.Sprintf("%s-%s.tgz", c.Name(), c.Metadata.Version) filename = filepath.Join(outDir, filename) if stat, err := os.Stat(filepath.Dir(filename)); os.IsNotExist(err) { if err := os.MkdirAll(filepath.Dir(filename), 0755); !os.IsExist(err) { diff --git a/pkg/chartutil/save_test.go b/pkg/chartutil/save_test.go index 72b804c81..3ac13b476 100644 --- a/pkg/chartutil/save_test.go +++ b/pkg/chartutil/save_test.go @@ -35,8 +35,9 @@ func TestSave(t *testing.T) { c := &chart.Chart{ Metadata: &chart.Metadata{ - Name: "ahab", - Version: "1.2.3.4", + APIVersion: chart.APIVersionV1, + Name: "ahab", + Version: "1.2.3", }, Files: []*chart.File{ {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, @@ -80,8 +81,9 @@ func TestSaveDir(t *testing.T) { c := &chart.Chart{ Metadata: &chart.Metadata{ - Name: "ahab", - Version: "1.2.3.4", + APIVersion: chart.APIVersionV1, + Name: "ahab", + Version: "1.2.3", }, Files: []*chart.File{ {Name: "scheherazade/shahryar.txt", Data: []byte("1,001 Nights")}, diff --git a/pkg/chartutil/testdata/albatross/Chart.yaml b/pkg/chartutil/testdata/albatross/Chart.yaml index eeef737ff..b5188fde0 100644 --- a/pkg/chartutil/testdata/albatross/Chart.yaml +++ b/pkg/chartutil/testdata/albatross/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: albatross description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml index 4cc36dca5..79e0d65db 100644 --- a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: alpine description: Deploy a basic Alpine Linux pod version: 0.1.0 diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml index 171e36156..1c9dd5fa4 100644 --- a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: mast1 description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz index ced5a4a6a..61cb62051 100644 Binary files a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz and b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz index 3af333e76..3190136b0 100644 Binary files a/pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz and b/pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/Chart.yaml index 4cc36dca5..79e0d65db 100644 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: alpine description: Deploy a basic Alpine Linux pod version: 0.1.0 diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/Chart.yaml index 171e36156..1c9dd5fa4 100644 --- a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast1/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: mast1 description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast2-0.1.0.tgz index ced5a4a6a..61cb62051 100644 Binary files a/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast2-0.1.0.tgz and b/pkg/chartutil/testdata/dependent-chart-helmignore/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/Chart.yaml index 4cc36dca5..79e0d65db 100644 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: alpine description: Deploy a basic Alpine Linux pod version: 0.1.0 diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml index 171e36156..1c9dd5fa4 100644 --- a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: mast1 description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz index ced5a4a6a..61cb62051 100644 Binary files a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz and b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/mariner-4.3.2.tgz index 3af333e76..3190136b0 100644 Binary files a/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/mariner-4.3.2.tgz and b/pkg/chartutil/testdata/dependent-chart-no-requirements-yaml/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/Chart.yaml index 4cc36dca5..79e0d65db 100644 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: alpine description: Deploy a basic Alpine Linux pod version: 0.1.0 diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml index 171e36156..1c9dd5fa4 100644 --- a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: mast1 description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz index ced5a4a6a..61cb62051 100644 Binary files a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz and b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/mariner-4.3.2.tgz index 3af333e76..3190136b0 100644 Binary files a/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/mariner-4.3.2.tgz and b/pkg/chartutil/testdata/dependent-chart-with-all-in-requirements-yaml/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/Chart.yaml index 4cc36dca5..79e0d65db 100644 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: alpine description: Deploy a basic Alpine Linux pod version: 0.1.0 diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml index 171e36156..1c9dd5fa4 100644 --- a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml +++ b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast1/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: mast1 description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz index ced5a4a6a..61cb62051 100644 Binary files a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz and b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/mariner-4.3.2.tgz index 3af333e76..3190136b0 100644 Binary files a/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/mariner-4.3.2.tgz and b/pkg/chartutil/testdata/dependent-chart-with-mixed-requirements-yaml/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chartutil/testdata/frobnitz-1.2.3.tgz b/pkg/chartutil/testdata/frobnitz-1.2.3.tgz index a8ba8ec6f..8731dce02 100644 Binary files a/pkg/chartutil/testdata/frobnitz-1.2.3.tgz and b/pkg/chartutil/testdata/frobnitz-1.2.3.tgz differ diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/frobnitz/charts/alpine/Chart.yaml index 4cc36dca5..79e0d65db 100644 --- a/pkg/chartutil/testdata/frobnitz/charts/alpine/Chart.yaml +++ b/pkg/chartutil/testdata/frobnitz/charts/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: alpine description: Deploy a basic Alpine Linux pod version: 0.1.0 diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml index 171e36156..1c9dd5fa4 100644 --- a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml +++ b/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast1/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: mast1 description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz index ced5a4a6a..61cb62051 100644 Binary files a/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz and b/pkg/chartutil/testdata/frobnitz/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz index ea6e2a160..5648f6f6d 100644 Binary files a/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz and b/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz b/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz index bc6be27aa..692965951 100644 Binary files a/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz and b/pkg/chartutil/testdata/frobnitz_backslash-1.2.3.tgz differ diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/Chart.yaml index 4cc36dca5..79e0d65db 100755 --- a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/Chart.yaml +++ b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: alpine description: Deploy a basic Alpine Linux pod version: 0.1.0 diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml index 171e36156..1c9dd5fa4 100755 --- a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml +++ b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast1/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: mast1 description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz index ced5a4a6a..61cb62051 100755 Binary files a/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz and b/pkg/chartutil/testdata/frobnitz_backslash/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chartutil/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz index 3af333e76..5648f6f6d 100755 Binary files a/pkg/chartutil/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz and b/pkg/chartutil/testdata/frobnitz_backslash/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chartutil/testdata/genfrob.sh b/pkg/chartutil/testdata/genfrob.sh index 8f2cddec1..35fdd59f2 100755 --- a/pkg/chartutil/testdata/genfrob.sh +++ b/pkg/chartutil/testdata/genfrob.sh @@ -6,7 +6,9 @@ tar -zcvf mariner/charts/albatross-0.1.0.tgz albatross echo "Packing mariner into frobnitz" tar -zcvf frobnitz/charts/mariner-4.3.2.tgz mariner +tar -zcvf frobnitz_backslash/charts/mariner-4.3.2.tgz mariner # Pack the frobnitz chart. echo "Packing frobnitz" tar --exclude=ignore/* -zcvf frobnitz-1.2.3.tgz frobnitz +tar --exclude=ignore/* -zcvf frobnitz_backslash-1.2.3.tgz frobnitz_backslash diff --git a/pkg/chartutil/testdata/mariner/Chart.yaml b/pkg/chartutil/testdata/mariner/Chart.yaml index e2efb7f99..92dc4b390 100644 --- a/pkg/chartutil/testdata/mariner/Chart.yaml +++ b/pkg/chartutil/testdata/mariner/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: mariner description: A Helm chart for Kubernetes version: 4.3.2 diff --git a/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz b/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz index 72bb6b36f..22c1fe572 100644 Binary files a/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz and b/pkg/chartutil/testdata/mariner/charts/albatross-0.1.0.tgz differ diff --git a/pkg/chartutil/testdata/moby/Chart.yaml b/pkg/chartutil/testdata/moby/Chart.yaml index b725af916..a5f992c61 100644 --- a/pkg/chartutil/testdata/moby/Chart.yaml +++ b/pkg/chartutil/testdata/moby/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: moby version: 0.1.0 diff --git a/pkg/chartutil/testdata/moby/charts/pequod/Chart.yaml b/pkg/chartutil/testdata/moby/charts/pequod/Chart.yaml index d9a3bfd5f..f1a8ef76b 100644 --- a/pkg/chartutil/testdata/moby/charts/pequod/Chart.yaml +++ b/pkg/chartutil/testdata/moby/charts/pequod/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: pequod version: 0.1.0 diff --git a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/Chart.yaml b/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/Chart.yaml index c3cdf397d..a7ee7bf90 100644 --- a/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/Chart.yaml +++ b/pkg/chartutil/testdata/moby/charts/pequod/charts/ahab/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: ahab version: 0.1.0 diff --git a/pkg/chartutil/testdata/moby/charts/spouter/Chart.yaml b/pkg/chartutil/testdata/moby/charts/spouter/Chart.yaml index f6819c06d..0525085b6 100644 --- a/pkg/chartutil/testdata/moby/charts/spouter/Chart.yaml +++ b/pkg/chartutil/testdata/moby/charts/spouter/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: spouter version: 0.1.0 diff --git a/pkg/chartutil/testdata/test-values-negative.yaml b/pkg/chartutil/testdata/test-values-negative.yaml new file mode 100644 index 000000000..5a1250bff --- /dev/null +++ b/pkg/chartutil/testdata/test-values-negative.yaml @@ -0,0 +1,14 @@ +firstname: John +lastname: Doe +age: -5 +likesCoffee: true +addresses: + - city: Springfield + street: Main + number: 12345 + - city: New York + street: Broadway + number: 67890 +phoneNumbers: + - "(888) 888-8888" + - "(555) 555-5555" diff --git a/pkg/chartutil/testdata/test-values.schema.json b/pkg/chartutil/testdata/test-values.schema.json new file mode 100644 index 000000000..4df89bbe8 --- /dev/null +++ b/pkg/chartutil/testdata/test-values.schema.json @@ -0,0 +1,67 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "addresses": { + "description": "List of addresses", + "items": { + "properties": { + "city": { + "type": "string" + }, + "number": { + "type": "number" + }, + "street": { + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + }, + "age": { + "description": "Age", + "minimum": 0, + "type": "integer" + }, + "employmentInfo": { + "properties": { + "salary": { + "minimum": 0, + "type": "number" + }, + "title": { + "type": "string" + } + }, + "required": [ + "salary" + ], + "type": "object" + }, + "firstname": { + "description": "First name", + "type": "string" + }, + "lastname": { + "type": "string" + }, + "likesCoffee": { + "type": "boolean" + }, + "phoneNumbers": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "firstname", + "lastname", + "addresses", + "employmentInfo" + ], + "title": "Values", + "type": "object" +} diff --git a/pkg/chartutil/testdata/test-values.yaml b/pkg/chartutil/testdata/test-values.yaml new file mode 100644 index 000000000..042dea664 --- /dev/null +++ b/pkg/chartutil/testdata/test-values.yaml @@ -0,0 +1,17 @@ +firstname: John +lastname: Doe +age: 25 +likesCoffee: true +employmentInfo: + title: Software Developer + salary: 100000 +addresses: + - city: Springfield + street: Main + number: 12345 + - city: New York + street: Broadway + number: 67890 +phoneNumbers: + - "(888) 888-8888" + - "(555) 555-5555" diff --git a/pkg/chartutil/values.go b/pkg/chartutil/values.go index 644cdd49b..7edc75233 100644 --- a/pkg/chartutil/values.go +++ b/pkg/chartutil/values.go @@ -17,6 +17,7 @@ limitations under the License. package chartutil import ( + "bytes" "fmt" "io" "io/ioutil" @@ -25,6 +26,7 @@ import ( "github.com/ghodss/yaml" "github.com/pkg/errors" + "github.com/xeipuuv/gojsonschema" "helm.sh/helm/pkg/chart" ) @@ -133,6 +135,64 @@ func ReadValuesFile(filename string) (Values, error) { return ReadValues(data) } +// ValidateAgainstSchema checks that values does not violate the structure laid out in schema +func ValidateAgainstSchema(chrt *chart.Chart, values map[string]interface{}) error { + var sb strings.Builder + if chrt.Schema != nil { + err := ValidateAgainstSingleSchema(values, chrt.Schema) + if err != nil { + sb.WriteString(fmt.Sprintf("%s:\n", chrt.Name())) + sb.WriteString(err.Error()) + } + } + + // For each dependency, recurively call this function with the coalesced values + for _, subchrt := range chrt.Dependencies() { + subchrtValues := values[subchrt.Name()].(map[string]interface{}) + if err := ValidateAgainstSchema(subchrt, subchrtValues); err != nil { + sb.WriteString(err.Error()) + } + } + + if sb.Len() > 0 { + return errors.New(sb.String()) + } + + return nil +} + +// ValidateAgainstSingleSchema checks that values does not violate the structure laid out in this schema +func ValidateAgainstSingleSchema(values Values, schemaJSON []byte) error { + valuesData, err := yaml.Marshal(values) + if err != nil { + return err + } + valuesJSON, err := yaml.YAMLToJSON(valuesData) + if err != nil { + return err + } + if bytes.Equal(valuesJSON, []byte("null")) { + valuesJSON = []byte("{}") + } + schemaLoader := gojsonschema.NewBytesLoader(schemaJSON) + valuesLoader := gojsonschema.NewBytesLoader(valuesJSON) + + result, err := gojsonschema.Validate(schemaLoader, valuesLoader) + if err != nil { + return err + } + + if !result.Valid() { + var sb strings.Builder + for _, desc := range result.Errors() { + sb.WriteString(fmt.Sprintf("- %s\n", desc)) + } + return errors.New(sb.String()) + } + + return nil +} + // CoalesceValues coalesces all of the values in a chart (and its subcharts). // // Values are coalesced together using the following rules: @@ -331,6 +391,11 @@ func ToRenderValues(chrt *chart.Chart, chrtVals map[string]interface{}, options return top, err } + if err := ValidateAgainstSchema(chrt, vals); err != nil { + errFmt := "values don't meet the specifications of the schema(s) in the following chart(s):\n%s" + return top, fmt.Errorf(errFmt, err.Error()) + } + top["Values"] = vals return top, nil } diff --git a/pkg/chartutil/values_test.go b/pkg/chartutil/values_test.go index 2faaf7828..43aa72189 100644 --- a/pkg/chartutil/values_test.go +++ b/pkg/chartutil/values_test.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/json" "fmt" + "io/ioutil" "testing" "text/template" @@ -160,6 +161,125 @@ func TestReadValuesFile(t *testing.T) { matchValues(t, data) } +func TestValidateAgainstSingleSchema(t *testing.T) { + values, err := ReadValuesFile("./testdata/test-values.yaml") + if err != nil { + t.Fatalf("Error reading YAML file: %s", err) + } + schema, err := ioutil.ReadFile("./testdata/test-values.schema.json") + if err != nil { + t.Fatalf("Error reading YAML file: %s", err) + } + + if err := ValidateAgainstSingleSchema(values, schema); err != nil { + t.Errorf("Error validating Values against Schema: %s", err) + } +} + +func TestValidateAgainstSingleSchemaNegative(t *testing.T) { + values, err := ReadValuesFile("./testdata/test-values-negative.yaml") + if err != nil { + t.Fatalf("Error reading YAML file: %s", err) + } + schema, err := ioutil.ReadFile("./testdata/test-values.schema.json") + if err != nil { + t.Fatalf("Error reading YAML file: %s", err) + } + + var errString string + if err := ValidateAgainstSingleSchema(values, schema); err == nil { + t.Fatalf("Expected an error, but got nil") + } else { + errString = err.Error() + } + + expectedErrString := `- (root): employmentInfo is required +- age: Must be greater than or equal to 0/1 +` + if errString != expectedErrString { + t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString) + } +} + +const subchrtSchema = `{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Values", + "type": "object", + "properties": { + "age": { + "description": "Age", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "age" + ] +} +` + +func TestValidateAgainstSchema(t *testing.T) { + subchrtJSON := []byte(subchrtSchema) + subchrt := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "subchrt", + }, + Schema: subchrtJSON, + } + chrt := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "chrt", + }, + } + chrt.AddDependency(subchrt) + + vals := map[string]interface{}{ + "name": "John", + "subchrt": map[string]interface{}{ + "age": 25, + }, + } + + if err := ValidateAgainstSchema(chrt, vals); err != nil { + t.Errorf("Error validating Values against Schema: %s", err) + } +} + +func TestValidateAgainstSchemaNegative(t *testing.T) { + subchrtJSON := []byte(subchrtSchema) + subchrt := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "subchrt", + }, + Schema: subchrtJSON, + } + chrt := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "chrt", + }, + } + chrt.AddDependency(subchrt) + + vals := map[string]interface{}{ + "name": "John", + "subchrt": map[string]interface{}{}, + } + + var errString string + if err := ValidateAgainstSchema(chrt, vals); err == nil { + t.Fatalf("Expected an error, but got nil") + } else { + errString = err.Error() + } + + expectedErrString := `subchrt: +- (root): age is required +` + if errString != expectedErrString { + t.Errorf("Error string :\n`%s`\ndoes not match expected\n`%s`", errString, expectedErrString) + } +} + func ExampleValues() { doc := ` title: "Moby Dick" @@ -399,6 +519,7 @@ func TestCoalesceTables(t *testing.T) { t.Errorf("Expected boat string, got %v", dst["boat"]) } } + func TestPathValue(t *testing.T) { doc := ` title: "Moby Dick" diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 6cb917915..b71d35793 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -282,11 +282,12 @@ func (c *ChartDownloader) getRepoCredentials(r *repo.ChartRepository) (username, // name is the archive file name plus the ".prov" extension. func VerifyChart(path, keyring string) (*provenance.Verification, error) { // For now, error out if it's not a tar file. - if fi, err := os.Stat(path); err != nil { + switch fi, err := os.Stat(path); { + case err != nil: return nil, err - } else if fi.IsDir() { + case fi.IsDir(): return nil, errors.New("unpacked charts cannot be verified") - } else if !isTar(path) { + case !isTar(path): return nil, errors.New("chart must be a tgz file") } @@ -307,7 +308,7 @@ func VerifyChart(path, keyring string) (*provenance.Verification, error) { // Currently, this simply checks extension, since a subsequent function will // untar the file and validate its binary format. func isTar(filename string) bool { - return strings.ToLower(filepath.Ext(filename)) == ".tgz" + return strings.EqualFold(filepath.Ext(filename), ".tgz") } func pickChartRepositoryConfigByName(name string, cfgs []*repo.Entry) (*repo.Entry, error) { diff --git a/pkg/downloader/testdata/signtest-0.1.0.tgz b/pkg/downloader/testdata/signtest-0.1.0.tgz index 6de9d988d..c74e5b0ef 100644 Binary files a/pkg/downloader/testdata/signtest-0.1.0.tgz and b/pkg/downloader/testdata/signtest-0.1.0.tgz differ diff --git a/pkg/downloader/testdata/signtest-0.1.0.tgz.prov b/pkg/downloader/testdata/signtest-0.1.0.tgz.prov index 94235399a..d325bb266 100644 --- a/pkg/downloader/testdata/signtest-0.1.0.tgz.prov +++ b/pkg/downloader/testdata/signtest-0.1.0.tgz.prov @@ -1,20 +1,21 @@ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 +apiVersion: v1 description: A Helm chart for Kubernetes name: signtest version: 0.1.0 ... files: - signtest-0.1.0.tgz: sha256:dee72947753628425b82814516bdaa37aef49f25e8820dd2a6e15a33a007823b + signtest-0.1.0.tgz: sha256:e5ef611620fb97704d8751c16bab17fedb68883bfb0edc76f78a70e9173f9b55 -----BEGIN PGP SIGNATURE----- -wsBcBAEBCgAQBQJXomNHCRCEO7+YH8GHYgAALywIAG1Me852Fpn1GYu8Q1GCcw4g -l2k7vOFchdDwDhdSVbkh4YyvTaIO3iE2Jtk1rxw+RIJiUr0eLO/rnIJuxZS8WKki -DR1LI9J1VD4dxN3uDETtWDWq7ScoPsRY5mJvYZXC8whrWEt/H2kfqmoA9LloRPWp -flOE0iktA4UciZOblTj6nAk3iDyjh/4HYL4a6tT0LjjKI7OTw4YyHfjHad1ywVCz -9dMUc1rPgTnl+fnRiSPSrlZIWKOt1mcQ4fVrU3nwtRUwTId2k8FtygL0G6M+Y6t0 -S6yaU7qfk9uTxkdkUF7Bf1X3ukxfe+cNBC32vf4m8LY4NkcYfSqK2fGtQsnVr6s= -=NyOM +wsBcBAEBCgAQBQJcoosfCRCEO7+YH8GHYgAA220IALAs8T8NPgkcLvHu+5109cAN +BOCNPSZDNsqLZW/2Dc9cKoBG7Jen4Qad+i5l9351kqn3D9Gm6eRfAWcjfggRobV/ +9daZ19h0nl4O1muQNAkjvdgZt8MOP3+PB3I3/Tu2QCYjI579SLUmuXlcZR5BCFPR +PJy+e3QpV2PcdeU2KZLG4tjtlrq+3QC9ZHHEJLs+BVN9d46Dwo6CxJdHJrrrAkTw +M8MhA92vbiTTPRSCZI9x5qDAwJYhoq0oxLflpuL2tIlo3qVoCsaTSURwMESEHO32 +XwYG7BaVDMELWhAorBAGBGBwWFbJ1677qQ2gd9CN0COiVhekWlFRcnn60800r84= +=k9Y9 -----END PGP SIGNATURE----- \ No newline at end of file diff --git a/pkg/downloader/testdata/signtest/Chart.yaml b/pkg/downloader/testdata/signtest/Chart.yaml index 90964b44a..f1f73723a 100644 --- a/pkg/downloader/testdata/signtest/Chart.yaml +++ b/pkg/downloader/testdata/signtest/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: signtest version: 0.1.0 diff --git a/pkg/downloader/testdata/signtest/alpine/Chart.yaml b/pkg/downloader/testdata/signtest/alpine/Chart.yaml index e45d7326a..eec261220 100644 --- a/pkg/downloader/testdata/signtest/alpine/Chart.yaml +++ b/pkg/downloader/testdata/signtest/alpine/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: Deploy a basic Alpine Linux pod home: https://helm.sh/helm name: alpine diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 25f1e6fad..b1bc93040 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -17,6 +17,7 @@ limitations under the License. package engine import ( + "fmt" "path" "sort" "strings" @@ -150,51 +151,65 @@ func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable) // higher-level (in file system) templates over deeply nested templates. keys := sortTemplates(tpls) - for _, fname := range keys { - r := tpls[fname] - if _, err := t.New(fname).Parse(r.tpl); err != nil { - return map[string]string{}, errors.Wrapf(err, "parse error in %q", fname) + for _, filename := range keys { + r := tpls[filename] + if _, err := t.New(filename).Parse(r.tpl); err != nil { + return map[string]string{}, parseTemplateError(filename, err) } } // Adding the reference templates to the template context // so they can be referenced in the tpl function - for fname, r := range referenceTpls { - if t.Lookup(fname) == nil { - if _, err := t.New(fname).Parse(r.tpl); err != nil { - return map[string]string{}, errors.Wrapf(err, "parse error in %q", fname) + for filename, r := range referenceTpls { + if t.Lookup(filename) == nil { + if _, err := t.New(filename).Parse(r.tpl); err != nil { + return map[string]string{}, parseTemplateError(filename, err) } } } rendered = make(map[string]string, len(keys)) - for _, file := range keys { + for _, filename := range keys { // Don't render partials. We don't care out the direct output of partials. // They are only included from other templates. - if strings.HasPrefix(path.Base(file), "_") { + if strings.HasPrefix(path.Base(filename), "_") { continue } // At render time, add information about the template that is being rendered. - vals := tpls[file].vals - vals["Template"] = chartutil.Values{"Name": file, "BasePath": tpls[file].basePath} + vals := tpls[filename].vals + vals["Template"] = chartutil.Values{"Name": filename, "BasePath": tpls[filename].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) + if err := t.ExecuteTemplate(&buf, filename, vals); err != nil { + return map[string]string{}, parseTemplateError(filename, err) } // Work around the issue where Go will emit "" even if Options(missing=zero) // is set. Since missing=error will never get here, we do not need to handle // the Strict case. f := &chart.File{ - Name: strings.ReplaceAll(file, "/templates", "/manifests"), + Name: strings.ReplaceAll(filename, "/templates", "/manifests"), Data: []byte(strings.ReplaceAll(buf.String(), "", "")), } - rendered[file] = string(f.Data) + rendered[filename] = string(f.Data) } return rendered, nil } +func parseTemplateError(filename string, err error) error { + tokens := strings.Split(err.Error(), ": ") + if len(tokens) == 1 { + // This might happen if a non-templating error occurs + return fmt.Errorf("render error in (%s): %s", filename, err) + } + // The first token is "template" + // The second token is either "filename:lineno" or "filename:lineNo:columnNo" + location := tokens[1] + // The remaining tokens make up a stacktrace-like chain, ending with the relevant error + errMsg := tokens[len(tokens)-1] + return fmt.Errorf("render error at (%s): %s", string(location), errMsg) +} + func sortTemplates(tpls map[string]renderable) []string { keys := make([]string, len(tpls)) i := 0 diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index aebb2ad09..9d708f193 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -187,6 +187,33 @@ func TestParallelRenderInternals(t *testing.T) { wg.Wait() } +func TestRenderErrors(t *testing.T) { + vals := chartutil.Values{"Values": map[string]interface{}{}} + + tplsMissingRequired := map[string]renderable{ + "missing_required": {tpl: `{{required "foo is required" .Values.foo}}`, vals: vals}, + } + _, err := new(Engine).render(tplsMissingRequired) + if err == nil { + t.Fatalf("Expected failures while rendering: %s", err) + } + expected := `render error at (missing_required:1:2): foo is required` + if err.Error() != expected { + t.Errorf("Expected '%s', got %q", expected, err.Error()) + } + + tplsUndefinedFunction := map[string]renderable{ + "undefined_function": {tpl: `{{foo}}`, vals: vals}, + } + _, err = new(Engine).render(tplsUndefinedFunction) + if err == nil { + t.Fatalf("Expected failures while rendering: %s", err) + } + expected = `render error at (undefined_function:1): function "foo" not defined` + if err.Error() != expected { + t.Errorf("Expected '%s', got %q", expected, err.Error()) + } +} func TestAllTemplates(t *testing.T) { ch1 := &chart.Chart{ Metadata: &chart.Metadata{Name: "ch1"}, diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 519f6bcde..6f2e98e49 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -40,7 +40,6 @@ import ( "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/watch" @@ -621,55 +620,28 @@ func scrubValidationError(err error) error { // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase // and returns said phase (PodSucceeded or PodFailed qualify). -func (c *Client) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) { - infos, err := c.Build(namespace, reader) - if err != nil { - return v1.PodUnknown, err - } - info := infos[0] - - kind := info.Mapping.GroupVersionKind.Kind - if kind != "Pod" { - return v1.PodUnknown, goerrors.Errorf("%s is not a Pod", info.Name) - } - - if err := c.watchPodUntilComplete(timeout, info); err != nil { - return v1.PodUnknown, err - } - - if err := info.Get(); err != nil { - return v1.PodUnknown, err - } - status := info.Object.(*v1.Pod).Status.Phase - - return status, nil -} +func (c *Client) WaitAndGetCompletedPodPhase(namespace, name string, timeout int64) (v1.PodPhase, error) { + client, _ := c.KubernetesClientSet() -func (c *Client) watchPodUntilComplete(timeout time.Duration, info *resource.Info) error { - w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion) - if err != nil { - return err - } + watcher, err := client.CoreV1().Pods(namespace).Watch(metav1.ListOptions{ + FieldSelector: fmt.Sprintf("metadata.name=%s", name), + TimeoutSeconds: &timeout, + }) - c.Log("Watching pod %s for completion with timeout of %v", info.Name, timeout) - ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout) - defer cancel() - _, err = watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) { - switch e.Type { - case watch.Deleted: - return false, errors.NewNotFound(schema.GroupResource{Resource: "pods"}, "") + for event := range watcher.ResultChan() { + p, ok := event.Object.(*v1.Pod) + if !ok { + return v1.PodUnknown, fmt.Errorf("%s not a pod", name) } - switch t := e.Object.(type) { - case *v1.Pod: - switch t.Status.Phase { - case v1.PodFailed, v1.PodSucceeded: - return true, nil - } + switch p.Status.Phase { + case v1.PodFailed: + return v1.PodFailed, nil + case v1.PodSucceeded: + return v1.PodSucceeded, nil } - return false, nil - }) + } - return err + return v1.PodUnknown, err } //get a kubernetes resources' relation pods diff --git a/pkg/kube/environment.go b/pkg/kube/environment.go index 94e4b62fe..dd205967f 100644 --- a/pkg/kube/environment.go +++ b/pkg/kube/environment.go @@ -18,7 +18,6 @@ package kube import ( "io" - "time" v1 "k8s.io/api/core/v1" "k8s.io/cli-runtime/pkg/resource" @@ -74,7 +73,7 @@ type KubernetesClient interface { // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase // and returns said phase (PodSucceeded or PodFailed qualify). - WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) + WaitAndGetCompletedPodPhase(namespace, name string, timeout int64) (v1.PodPhase, error) } // PrintingKubeClient implements KubeClient, but simply prints the reader to @@ -126,7 +125,6 @@ func (p *PrintingKubeClient) BuildUnstructured(ns string, reader io.Reader) (Res } // WaitAndGetCompletedPodPhase implements KubeClient WaitAndGetCompletedPodPhase. -func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) { - _, err := io.Copy(p.Out, reader) - return v1.PodUnknown, err +func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(namespace, name string, timeout int64) (v1.PodPhase, error) { + return v1.PodSucceeded, nil } diff --git a/pkg/kube/environment_test.go b/pkg/kube/environment_test.go index 4a0a51ad9..bb31e85b9 100644 --- a/pkg/kube/environment_test.go +++ b/pkg/kube/environment_test.go @@ -49,7 +49,7 @@ func (k *mockKubeClient) Build(ns string, reader io.Reader) (Result, error) { func (k *mockKubeClient) BuildUnstructured(ns string, reader io.Reader) (Result, error) { return []*resource.Info{}, nil } -func (k *mockKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) { +func (k *mockKubeClient) WaitAndGetCompletedPodPhase(namespace, name string, timeout int64) (v1.PodPhase, error) { return v1.PodUnknown, nil } diff --git a/pkg/lint/rules/testdata/albatross/Chart.yaml b/pkg/lint/rules/testdata/albatross/Chart.yaml index c108fa5e5..21124acfc 100644 --- a/pkg/lint/rules/testdata/albatross/Chart.yaml +++ b/pkg/lint/rules/testdata/albatross/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: albatross description: testing chart version: 199.44.12345-Alpha.1+cafe009 diff --git a/pkg/lint/rules/testdata/badchartfile/Chart.yaml b/pkg/lint/rules/testdata/badchartfile/Chart.yaml index dbb4a1501..b64052eb9 100644 --- a/pkg/lint/rules/testdata/badchartfile/Chart.yaml +++ b/pkg/lint/rules/testdata/badchartfile/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes version: 0.0.0 home: "" diff --git a/pkg/lint/rules/testdata/badvaluesfile/Chart.yaml b/pkg/lint/rules/testdata/badvaluesfile/Chart.yaml index bed845249..632919d03 100644 --- a/pkg/lint/rules/testdata/badvaluesfile/Chart.yaml +++ b/pkg/lint/rules/testdata/badvaluesfile/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: badvaluesfile description: A Helm chart for Kubernetes version: 0.0.1 diff --git a/pkg/lint/rules/testdata/goodone/Chart.yaml b/pkg/lint/rules/testdata/goodone/Chart.yaml index de05463ca..cb7a4bf20 100644 --- a/pkg/lint/rules/testdata/goodone/Chart.yaml +++ b/pkg/lint/rules/testdata/goodone/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 name: goodone description: good testing chart version: 199.44.12345-Alpha.1+cafe009 diff --git a/pkg/lint/rules/values.go b/pkg/lint/rules/values.go index f3d9a7317..a0310e934 100644 --- a/pkg/lint/rules/values.go +++ b/pkg/lint/rules/values.go @@ -17,6 +17,7 @@ limitations under the License. package rules import ( + "io/ioutil" "os" "path/filepath" @@ -48,6 +49,19 @@ func validateValuesFileExistence(valuesPath string) error { } func validateValuesFile(valuesPath string) error { - _, err := chartutil.ReadValuesFile(valuesPath) - return errors.Wrap(err, "unable to parse YAML") + values, err := chartutil.ReadValuesFile(valuesPath) + if err != nil { + return errors.Wrap(err, "unable to parse YAML") + } + + ext := filepath.Ext(valuesPath) + schemaPath := valuesPath[:len(valuesPath)-len(ext)] + ".schema.json" + schema, err := ioutil.ReadFile(schemaPath) + if len(schema) == 0 { + return nil + } + if err != nil { + return err + } + return chartutil.ValidateAgainstSingleSchema(values, schema) } diff --git a/pkg/provenance/sign_test.go b/pkg/provenance/sign_test.go index d74e23887..1f4d2d232 100644 --- a/pkg/provenance/sign_test.go +++ b/pkg/provenance/sign_test.go @@ -63,13 +63,14 @@ const ( ) // testMessageBlock represents the expected message block for the testdata/hashtest chart. -const testMessageBlock = `description: Test chart versioning +const testMessageBlock = `apiVersion: v1 +description: Test chart versioning name: hashtest version: 1.2.3 ... files: - hashtest-1.2.3.tgz: sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75 + hashtest-1.2.3.tgz: sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888 ` func TestMessageBlock(t *testing.T) { @@ -100,7 +101,7 @@ func TestParseMessageBlock(t *testing.T) { if hash, ok := sc.Files["hashtest-1.2.3.tgz"]; !ok { t.Errorf("hashtest file not found in Files") - } else if hash != "sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75" { + } else if hash != "sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888" { t.Errorf("Unexpected hash: %q", hash) } } diff --git a/pkg/provenance/testdata/hashtest-1.2.3.tgz b/pkg/provenance/testdata/hashtest-1.2.3.tgz index 1e89b524f..7bbc533ca 100644 Binary files a/pkg/provenance/testdata/hashtest-1.2.3.tgz and b/pkg/provenance/testdata/hashtest-1.2.3.tgz differ diff --git a/pkg/provenance/testdata/hashtest-1.2.3.tgz.prov b/pkg/provenance/testdata/hashtest-1.2.3.tgz.prov new file mode 100755 index 000000000..3a788cd2e --- /dev/null +++ b/pkg/provenance/testdata/hashtest-1.2.3.tgz.prov @@ -0,0 +1,21 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +apiVersion: v1 +description: Test chart versioning +name: hashtest +version: 1.2.3 + +... +files: + hashtest-1.2.3.tgz: sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888 +-----BEGIN PGP SIGNATURE----- + +wsBcBAEBCgAQBQJcon2ICRCEO7+YH8GHYgAASEAIAHD4Rad+LF47qNydI+k7x3aC +/qkdsqxE9kCUHtTJkZObE/Zmj2w3Opq0gcQftz4aJ2G9raqPDvwOzxnTxOkGfUdK +qIye48gFHzr2a7HnMTWr+HLQc4Gg+9kysIwkW4TM8wYV10osysYjBrhcafrHzFSK +791dBHhXP/aOrJQbFRob0GRFQ4pXdaSww1+kVaZLiKSPkkMKt9uk9Po1ggJYSIDX +uzXNcr78jTWACqkAtwx8+CJ8yzcGeuXSVNABDgbmAgpY0YT+Bz/UOWq4Q7tyuWnS +x9BKrvcb+Gc/6S0oK0Ffp8K4iSWYp79uH1bZ2oBS1yajA0c5h5i7qI3N4cabREw= +=YgnR +-----END PGP SIGNATURE----- \ No newline at end of file diff --git a/pkg/provenance/testdata/hashtest.sha256 b/pkg/provenance/testdata/hashtest.sha256 index 829031f9d..05173edf8 100644 --- a/pkg/provenance/testdata/hashtest.sha256 +++ b/pkg/provenance/testdata/hashtest.sha256 @@ -1 +1 @@ -8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75 hashtest-1.2.3.tgz +c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888 hashtest-1.2.3.tgz diff --git a/pkg/provenance/testdata/hashtest/Chart.yaml b/pkg/provenance/testdata/hashtest/Chart.yaml index 342631ef8..6edf5f8b6 100644 --- a/pkg/provenance/testdata/hashtest/Chart.yaml +++ b/pkg/provenance/testdata/hashtest/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: Test chart versioning name: hashtest version: 1.2.3 diff --git a/pkg/provenance/testdata/msgblock.yaml b/pkg/provenance/testdata/msgblock.yaml index 0fdbda8ce..c16293ffc 100644 --- a/pkg/provenance/testdata/msgblock.yaml +++ b/pkg/provenance/testdata/msgblock.yaml @@ -1,7 +1,8 @@ +apiVersion: v1 description: Test chart versioning name: hashtest version: 1.2.3 ... files: - hashtest-1.2.3.tgz: sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75 + hashtest-1.2.3.tgz: sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888 diff --git a/pkg/provenance/testdata/msgblock.yaml.asc b/pkg/provenance/testdata/msgblock.yaml.asc index 5a34d6c52..b4187b742 100644 --- a/pkg/provenance/testdata/msgblock.yaml.asc +++ b/pkg/provenance/testdata/msgblock.yaml.asc @@ -1,21 +1,22 @@ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 +apiVersion: v1 description: Test chart versioning name: hashtest version: 1.2.3 ... files: - hashtest-1.2.3.tgz: sha256:8e90e879e2a04b1900570e1c198755e46e4706d70b0e79f5edabfac7900e4e75 + hashtest-1.2.3.tgz: sha256:c6841b3a895f1444a6738b5d04564a57e860ce42f8519c3be807fb6d9bee7888 -----BEGIN PGP SIGNATURE----- -Comment: GPGTools - https://gpgtools.org -iQEcBAEBCgAGBQJXlp8KAAoJEIQ7v5gfwYdiE7sIAJYDiza+asekeooSXLvQiK+G -PKnveqQpx49EZ6L7Y7UlW25SyH8EjXXHeJysDywCXF3w4luxN9n56ffU0KEW11IY -F+JSjmgIWLS6ti7ZAGEi6JInQ/30rOAIpTEBRBL2IueW3m63mezrGK6XkBlGqpor -C9WKeqLi+DWlMoBtsEy3Uk0XP6pn/qBFICYAbLQQU0sCCUT8CBA8f8aidxi7aw9t -i404yYF+Dvc6i4JlSG77SV0ZJBWllUvsWoCd9Jli0NAuaMqmE7mzcEt/dE+Fm2Ql -Bx3tr1WS4xTRiFQdcOttOl93H+OaHTh+Y0qqLTzzpCvqmttG0HfI6lMeCs7LeyA= -=vEK+ +iQFJBAEBCgAzFiEEXmFTibU8o38O5gvThDu/mB/Bh2IFAlyiiDcVHGhlbG0tdGVz +dGluZ0BoZWxtLnNoAAoJEIQ7v5gfwYdiILAH/2f3GMVh+ZY5a+szOBudcuivjTcz +0Im1MwWQZfB1po3Yu7smWZbf5tJCzvVpYtvRlfa0nguuIh763MwOh9Q7dBXOLAxm +VCxqHm3svnNenBNfOpIygaMTgMZKxI4RrsKBgwPOTmlNtKg2lVaCiJAI30TXE6bB +/DwEYX0wmTssrAcSpTzOOSC+zHnPKew+5A3SY3ms+gAtVAcLepmJjI7RS7RhQxDl +AG+rWYis5gpDrk3U9OG1EOxqbftOAMqUl/kwI9eu5cPouN85rWwMe5pvHAvuyr/y +caYdlXDHTZsXmBuvfiUX6gqXtrpPCyKTCP+RzNf3+bXJM8m3u3gbMjGvKjU= +=vHcU -----END PGP SIGNATURE----- diff --git a/pkg/registry/cache.go b/pkg/registry/cache.go index b61b0adee..ccedd1e54 100644 --- a/pkg/registry/cache.go +++ b/pkg/registry/cache.go @@ -74,6 +74,7 @@ func (cache *filesystemCache) LayersToChart(layers []ocispec.Descriptor) (*chart if err != nil { return nil, err } + metadata.APIVersion = chart.APIVersionV1 metadata.Name = name metadata.Version = version @@ -96,8 +97,8 @@ func (cache *filesystemCache) LayersToChart(layers []ocispec.Descriptor) (*chart func (cache *filesystemCache) ChartToLayers(ch *chart.Chart) ([]ocispec.Descriptor, error) { // extract/separate the name and version from other metadata - if ch.Metadata == nil { - return nil, errors.New("chart does not contain metadata") + if err := ch.Validate(); err != nil { + return nil, err } name := ch.Metadata.Name version := ch.Metadata.Version @@ -115,7 +116,11 @@ func (cache *filesystemCache) ChartToLayers(ch *chart.Chart) ([]ocispec.Descript // TODO: something better than this hack. Currently needed for chartutil.Save() // If metadata does not contain Name or Version, an error is returned // such as "no chart name specified (Chart.yaml)" - ch.Metadata = &chart.Metadata{Name: "-", Version: "-"} + ch.Metadata = &chart.Metadata{ + APIVersion: chart.APIVersionV1, + Name: "-", + Version: "0.1.0", + } destDir := mkdir(filepath.Join(cache.rootDir, "blobs", ".build")) tmpFile, err := chartutil.Save(ch, destDir) defer os.Remove(tmpFile) diff --git a/pkg/registry/client_test.go b/pkg/registry/client_test.go index 13cd0238b..5b2f06eb5 100644 --- a/pkg/registry/client_test.go +++ b/pkg/registry/client_test.go @@ -132,8 +132,9 @@ func (suite *RegistryClientTestSuite) Test_1_SaveChart() { // valid chart ch := &chart.Chart{} ch.Metadata = &chart.Metadata{ - Name: "testchart", - Version: "1.2.3", + APIVersion: "v1", + Name: "testchart", + Version: "1.2.3", } err = suite.RegistryClient.SaveChart(ch, ref) suite.Nil(err) diff --git a/pkg/release/mock.go b/pkg/release/mock.go index 0a3656a3b..3e8a8e361 100644 --- a/pkg/release/mock.go +++ b/pkg/release/mock.go @@ -71,8 +71,9 @@ func Mock(opts *MockReleaseOptions) *Release { if opts.Chart == nil { ch = &chart.Chart{ Metadata: &chart.Metadata{ - Name: "foo", - Version: "0.1.0-beta.1", + Name: "foo", + Version: "0.1.0-beta.1", + AppVersion: "1.0", }, Templates: []*chart.File{ {Name: "templates/foo.tpl", Data: []byte(MockManifest)}, diff --git a/pkg/releasetesting/environment.go b/pkg/releasetesting/environment.go index aa3517fb3..09727636e 100644 --- a/pkg/releasetesting/environment.go +++ b/pkg/releasetesting/environment.go @@ -20,7 +20,6 @@ import ( "bytes" "fmt" "log" - "time" v1 "k8s.io/api/core/v1" @@ -48,8 +47,7 @@ func (env *Environment) createTestPod(test *test) error { } func (env *Environment) getTestPodStatus(test *test) (v1.PodPhase, error) { - b := bytes.NewBufferString(test.manifest) - status, err := env.KubeClient.WaitAndGetCompletedPodPhase(env.Namespace, b, time.Duration(env.Timeout)*time.Second) + status, err := env.KubeClient.WaitAndGetCompletedPodPhase(env.Namespace, test.name, env.Timeout) if err != nil { log.Printf("Error getting status for pod %s: %s", test.result.Name, err) test.result.Info = err.Error() diff --git a/pkg/releasetesting/test_suite.go b/pkg/releasetesting/test_suite.go index cbe50f78b..62c6fb157 100644 --- a/pkg/releasetesting/test_suite.go +++ b/pkg/releasetesting/test_suite.go @@ -38,6 +38,7 @@ type TestSuite struct { } type test struct { + name string manifest string expectedSuccess bool result *release.TestRun @@ -68,7 +69,7 @@ func (ts *TestSuite) Run(env *Environment) error { } test.result.StartedAt = time.Now() - if err := env.streamRunning(test.result.Name); err != nil { + if err := env.streamRunning(test.name); err != nil { return err } test.result.Status = release.TestRunRunning @@ -176,6 +177,7 @@ func newTest(testManifest string) (*test, error) { name := strings.TrimSuffix(sh.Metadata.Name, ",") return &test{ + name: name, manifest: testManifest, expectedSuccess: expected, result: &release.TestRun{ diff --git a/pkg/releasetesting/test_suite_test.go b/pkg/releasetesting/test_suite_test.go index 21f3eabe6..2e17e2945 100644 --- a/pkg/releasetesting/test_suite_test.go +++ b/pkg/releasetesting/test_suite_test.go @@ -19,7 +19,6 @@ package releasetesting import ( "io" "testing" - "time" v1 "k8s.io/api/core/v1" @@ -249,7 +248,7 @@ type mockKubeClient struct { err error } -func (c *mockKubeClient) WaitAndGetCompletedPodPhase(_ string, _ io.Reader, _ time.Duration) (v1.PodPhase, error) { +func (c *mockKubeClient) WaitAndGetCompletedPodPhase(_ string, _ string, _ int64) (v1.PodPhase, error) { if c.podFail { return v1.PodFailed, nil } diff --git a/pkg/repo/repotest/testdata/examplechart-0.1.0.tgz b/pkg/repo/repotest/testdata/examplechart-0.1.0.tgz index aec86c640..c5ea741eb 100644 Binary files a/pkg/repo/repotest/testdata/examplechart-0.1.0.tgz and b/pkg/repo/repotest/testdata/examplechart-0.1.0.tgz differ diff --git a/pkg/repo/repotest/testdata/examplechart/Chart.yaml b/pkg/repo/repotest/testdata/examplechart/Chart.yaml index 8e06de648..a7d297285 100644 --- a/pkg/repo/repotest/testdata/examplechart/Chart.yaml +++ b/pkg/repo/repotest/testdata/examplechart/Chart.yaml @@ -1,3 +1,4 @@ +apiVersion: v1 description: A Helm chart for Kubernetes name: examplechart version: 0.1.0 diff --git a/pkg/repo/testdata/repository/frobnitz-1.2.3.tgz b/pkg/repo/testdata/repository/frobnitz-1.2.3.tgz index fc8cacec2..8731dce02 100644 Binary files a/pkg/repo/testdata/repository/frobnitz-1.2.3.tgz and b/pkg/repo/testdata/repository/frobnitz-1.2.3.tgz differ diff --git a/pkg/repo/testdata/repository/sprocket-1.1.0.tgz b/pkg/repo/testdata/repository/sprocket-1.1.0.tgz index 595e9cc03..48d65f491 100644 Binary files a/pkg/repo/testdata/repository/sprocket-1.1.0.tgz and b/pkg/repo/testdata/repository/sprocket-1.1.0.tgz differ diff --git a/pkg/repo/testdata/repository/sprocket-1.2.0.tgz b/pkg/repo/testdata/repository/sprocket-1.2.0.tgz index 82188a99b..6fdc73c2b 100644 Binary files a/pkg/repo/testdata/repository/sprocket-1.2.0.tgz and b/pkg/repo/testdata/repository/sprocket-1.2.0.tgz differ diff --git a/pkg/repo/testdata/repository/universe/zarthal-1.0.0.tgz b/pkg/repo/testdata/repository/universe/zarthal-1.0.0.tgz index 90cb34bd5..6f1e8564c 100644 Binary files a/pkg/repo/testdata/repository/universe/zarthal-1.0.0.tgz and b/pkg/repo/testdata/repository/universe/zarthal-1.0.0.tgz differ diff --git a/scripts/completions.bash b/scripts/completions.bash index ededbb791..6c05963b4 100644 --- a/scripts/completions.bash +++ b/scripts/completions.bash @@ -287,8 +287,8 @@ _helm_delete() local_nonpersistent_flags+=("--dry-run") flags+=("--no-hooks") local_nonpersistent_flags+=("--no-hooks") - flags+=("--purge") - local_nonpersistent_flags+=("--purge") + flags+=("--keep-history") + local_nonpersistent_flags+=("--keep-history") flags+=("--timeout=") local_nonpersistent_flags+=("--timeout=") flags+=("--tls")