diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 2d26615cc..22937a842 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -146,7 +146,7 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release") f.StringVar(&client.Description, "description", "", "add a custom description") f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") - f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "run helm dependency update before installing the chart") + f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart") f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema") f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used") f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present") diff --git a/cmd/helm/testdata/output/upgrade-with-dependency-update.txt b/cmd/helm/testdata/output/upgrade-with-dependency-update.txt new file mode 100644 index 000000000..0e7e5842e --- /dev/null +++ b/cmd/helm/testdata/output/upgrade-with-dependency-update.txt @@ -0,0 +1,9 @@ +Release "funny-bunny" has been upgraded. Happy Helming! +NAME: funny-bunny +LAST DEPLOYED: Fri Sep 2 22:04:05 1977 +NAMESPACE: default +STATUS: deployed +REVISION: 3 +TEST SUITE: None +NOTES: +PARENT NOTES diff --git a/cmd/helm/testdata/testcharts/chart-with-subchart-update/Chart.lock b/cmd/helm/testdata/testcharts/chart-with-subchart-update/Chart.lock new file mode 100644 index 000000000..31cda6bd6 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-subchart-update/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: subchart-with-notes + repository: file://../chart-with-subchart-notes/charts/subchart-with-notes + version: 0.0.1 +digest: sha256:8ca45f73ae3f6170a09b64a967006e98e13cd91eb51e5ab0599bb87296c7df0a +generated: "2021-05-02T15:07:22.1099921+02:00" diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index cd5acd85d..9c893d77f 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -31,6 +31,7 @@ import ( "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/cli/output" "helm.sh/helm/v3/pkg/cli/values" + "helm.sh/helm/v3/pkg/downloader" "helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/storage/driver" ) @@ -139,7 +140,8 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { return err } - vals, err := valueOpts.MergeValues(getter.All(settings)) + p := getter.All(settings) + vals, err := valueOpts.MergeValues(p) if err != nil { return err } @@ -151,7 +153,27 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { } if req := ch.Metadata.Dependencies; req != nil { if err := action.CheckDependencies(ch, req); err != nil { - return err + if client.DependencyUpdate { + man := &downloader.Manager{ + Out: out, + ChartPath: chartPath, + Keyring: client.ChartPathOptions.Keyring, + SkipUpdate: false, + Getters: p, + RepositoryConfig: settings.RepositoryConfig, + RepositoryCache: settings.RepositoryCache, + Debug: settings.Debug, + } + if err := man.Update(); err != nil { + return err + } + // Reload the chart with the updated Chart.lock file. + if ch, err = loader.Load(chartPath); err != nil { + return errors.Wrap(err, "failed reloading chart after repo update") + } + } else { + return err + } } } @@ -193,6 +215,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails") f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") f.StringVar(&client.Description, "description", "", "add a custom description") + f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart") addChartPathOptionsFlags(f, &client.ChartPathOptions) addValueOptionsFlags(f, valueOpts) bindOutputFlag(cmd, &outfmt) diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index e952a5933..fc2a22d7d 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -32,6 +32,7 @@ import ( ) func TestUpgradeCmd(t *testing.T) { + tmpChart := ensure.TempDir(t) cfile := &chart.Chart{ Metadata: &chart.Metadata{ @@ -79,6 +80,7 @@ func TestUpgradeCmd(t *testing.T) { missingDepsPath := "testdata/testcharts/chart-missing-deps" badDepsPath := "testdata/testcharts/chart-bad-requirements" + presentDepsPath := "testdata/testcharts/chart-with-subchart-update" relWithStatusMock := func(n string, v int, ch *chart.Chart, status release.Status) *release.Release { return release.Mock(&release.MockReleaseOptions{Name: n, Version: v, Chart: ch, Status: status}) @@ -149,6 +151,12 @@ func TestUpgradeCmd(t *testing.T) { golden: "output/upgrade-with-bad-dependencies.txt", wantError: true, }, + { + name: "upgrade a release with resolving missing dependencies", + cmd: fmt.Sprintf("upgrade --dependency-update funny-bunny %s", presentDepsPath), + golden: "output/upgrade-with-dependency-update.txt", + rels: []*release.Release{relMock("funny-bunny", 2, ch2)}, + }, { name: "upgrade a non-existent release", cmd: fmt.Sprintf("upgrade funny-bunny '%s'", chartPath), diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 3b3dd3f1c..07d9cb40e 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -98,6 +98,8 @@ type Upgrade struct { PostRenderer postrender.PostRenderer // DisableOpenAPIValidation controls whether OpenAPI validation is enforced. DisableOpenAPIValidation bool + // Get missing dependencies + DependencyUpdate bool } // NewUpgrade creates a new Upgrade object with the given configuration. diff --git a/pkg/storage/driver/sql.go b/pkg/storage/driver/sql.go index 13538aba0..c8a6ae04f 100644 --- a/pkg/storage/driver/sql.go +++ b/pkg/storage/driver/sql.go @@ -310,6 +310,10 @@ func (s *SQL) Query(labels map[string]string) ([]*rspb.Release, error) { return nil, err } + if len(records) == 0 { + return nil, ErrReleaseNotFound + } + var releases []*rspb.Release for _, record := range records { release, err := decodeRelease(record.Body) diff --git a/pkg/storage/driver/sql_test.go b/pkg/storage/driver/sql_test.go index 1562a90aa..87b6315b8 100644 --- a/pkg/storage/driver/sql_test.go +++ b/pkg/storage/driver/sql_test.go @@ -292,6 +292,11 @@ func TestSqlUpdate(t *testing.T) { func TestSqlQuery(t *testing.T) { // Reflect actual use cases in ../storage.go + labelSetUnknown := map[string]string{ + "name": "smug-pigeon", + "owner": sqlReleaseDefaultOwner, + "status": "unknown", + } labelSetDeployed := map[string]string{ "name": "smug-pigeon", "owner": sqlReleaseDefaultOwner, @@ -320,6 +325,15 @@ func TestSqlQuery(t *testing.T) { sqlReleaseTableNamespaceColumn, ) + mock. + ExpectQuery(regexp.QuoteMeta(query)). + WithArgs("smug-pigeon", sqlReleaseDefaultOwner, "unknown", "default"). + WillReturnRows( + mock.NewRows([]string{ + sqlReleaseTableBodyColumn, + }), + ).RowsWillBeClosed() + mock. ExpectQuery(regexp.QuoteMeta(query)). WithArgs("smug-pigeon", sqlReleaseDefaultOwner, "deployed", "default"). @@ -353,6 +367,13 @@ func TestSqlQuery(t *testing.T) { ), ).RowsWillBeClosed() + _, err := sqlDriver.Query(labelSetUnknown) + if err == nil { + t.Errorf("Expected error {%v}, got nil", ErrReleaseNotFound) + } else if err != ErrReleaseNotFound { + t.Fatalf("failed to query for unknown smug-pigeon release: %v", err) + } + results, err := sqlDriver.Query(labelSetDeployed) if err != nil { t.Fatalf("failed to query for deployed smug-pigeon release: %v", err)