diff --git a/internal/action/install.go b/internal/action/install.go index 355689a..96fe49a 100644 --- a/internal/action/install.go +++ b/internal/action/install.go @@ -30,6 +30,11 @@ import ( "github.com/fluxcd/helm-controller/internal/postrender" ) +// InstallOption can be used to modify Helm's action.Install after the instructions +// from the v2beta2.HelmRelease have been applied. This is for example useful to +// enable the dry-run setting as a CLI. +type InstallOption func(action *helmaction.Install) + // Install runs the Helm install action with the provided config, using the // v2beta2.HelmReleaseSpec of the given object to determine the target release // and rollback configuration. @@ -42,12 +47,8 @@ import ( // action result. The caller is expected to listen to this using a // storage.ObserveFunc, which provides superior access to Helm storage writes. func Install(ctx context.Context, config *helmaction.Configuration, obj *v2.HelmRelease, - chrt *helmchart.Chart, vals helmchartutil.Values) (*helmrelease.Release, error) { - - install, err := newInstall(config, obj) - if err != nil { - return nil, err - } + chrt *helmchart.Chart, vals helmchartutil.Values, opts ...InstallOption) (*helmrelease.Release, error) { + install := newInstall(config, obj, opts) policy, err := crdPolicyOrDefault(obj.Spec.GetInstall().CRDs) if err != nil { @@ -60,7 +61,7 @@ func Install(ctx context.Context, config *helmaction.Configuration, obj *v2.Helm return install.RunWithContext(ctx, chrt, vals.AsMap()) } -func newInstall(config *helmaction.Configuration, obj *v2.HelmRelease) (*helmaction.Install, error) { +func newInstall(config *helmaction.Configuration, obj *v2.HelmRelease, opts []InstallOption) *helmaction.Install { install := helmaction.NewInstall(config) install.ReleaseName = obj.GetReleaseName() @@ -83,11 +84,11 @@ func newInstall(config *helmaction.Configuration, obj *v2.HelmRelease) (*helmact install.EnableDNS = allowDNS } - renderer, err := postrender.BuildPostRenderers(obj) - if err != nil { - return nil, err - } - install.PostRenderer = renderer + install.PostRenderer = postrender.BuildPostRenderers(obj) - return install, nil + for _, opt := range opts { + opt(install) + } + + return install } diff --git a/internal/action/install_test.go b/internal/action/install_test.go new file mode 100644 index 0000000..64e5166 --- /dev/null +++ b/internal/action/install_test.go @@ -0,0 +1,97 @@ +/* +Copyright 2022 The Flux 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 action + +import ( + "testing" + "time" + + . "github.com/onsi/gomega" + helmaction "helm.sh/helm/v3/pkg/action" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v2 "github.com/fluxcd/helm-controller/api/v2beta2" +) + +func Test_newInstall(t *testing.T) { + t.Run("new install", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "install", + Namespace: "install-ns", + }, + Spec: v2.HelmReleaseSpec{ + Timeout: &metav1.Duration{Duration: time.Minute}, + Install: &v2.Install{ + Timeout: &metav1.Duration{Duration: 10 * time.Second}, + Replace: true, + }, + }, + } + + got := newInstall(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Namespace).To(Equal(obj.Namespace)) + g.Expect(got.Timeout).To(Equal(obj.Spec.Install.Timeout.Duration)) + g.Expect(got.Replace).To(Equal(obj.Spec.Install.Replace)) + }) + + t.Run("timeout fallback", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "install", + Namespace: "install-ns", + }, + Spec: v2.HelmReleaseSpec{ + Timeout: &metav1.Duration{Duration: time.Minute}, + }, + } + + got := newInstall(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Namespace).To(Equal(obj.Namespace)) + g.Expect(got.Timeout).To(Equal(obj.Spec.Timeout.Duration)) + }) + + t.Run("applies options", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "install", + Namespace: "install-ns", + }, + Spec: v2.HelmReleaseSpec{}, + } + + got := newInstall(&helmaction.Configuration{}, obj, []InstallOption{ + func(install *helmaction.Install) { + install.Atomic = true + }, + func(install *helmaction.Install) { + install.DryRun = true + }, + }) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Atomic).To(BeTrue()) + g.Expect(got.DryRun).To(BeTrue()) + }) +} diff --git a/internal/action/rollback.go b/internal/action/rollback.go index 74daa35..0af54bd 100644 --- a/internal/action/rollback.go +++ b/internal/action/rollback.go @@ -22,6 +22,11 @@ import ( v2 "github.com/fluxcd/helm-controller/api/v2beta2" ) +// RollbackOption can be used to modify Helm's action.Rollback after the +// instructions from the v2beta2.HelmRelease have been applied. This is for +// example useful to enable the dry-run setting as a CLI. +type RollbackOption func(*helmaction.Rollback) + // Rollback runs the Helm rollback action with the provided config, using the // v2beta2.HelmReleaseSpec of the given object to determine the target release // and rollback configuration. @@ -30,25 +35,29 @@ import ( // expected to be done by the caller. In addition, it does not take note of the // action result. The caller is expected to listen to this using a // storage.ObserveFunc, which provides superior access to Helm storage writes. -func Rollback(config *helmaction.Configuration, obj *v2.HelmRelease) error { - rollback := newRollback(config, obj) +func Rollback(config *helmaction.Configuration, obj *v2.HelmRelease, opts ...RollbackOption) error { + rollback := newRollback(config, obj, opts) return rollback.Run(obj.GetReleaseName()) } -func newRollback(config *helmaction.Configuration, rel *v2.HelmRelease) *helmaction.Rollback { +func newRollback(config *helmaction.Configuration, obj *v2.HelmRelease, opts []RollbackOption) *helmaction.Rollback { rollback := helmaction.NewRollback(config) - rollback.Timeout = rel.Spec.GetRollback().GetTimeout(rel.GetTimeout()).Duration - rollback.Wait = !rel.Spec.GetRollback().DisableWait - rollback.WaitForJobs = !rel.Spec.GetRollback().DisableWaitForJobs - rollback.DisableHooks = rel.Spec.GetRollback().DisableHooks - rollback.Force = rel.Spec.GetRollback().Force - rollback.Recreate = rel.Spec.GetRollback().Recreate - rollback.CleanupOnFail = rel.Spec.GetRollback().CleanupOnFail + rollback.Timeout = obj.Spec.GetRollback().GetTimeout(obj.GetTimeout()).Duration + rollback.Wait = !obj.Spec.GetRollback().DisableWait + rollback.WaitForJobs = !obj.Spec.GetRollback().DisableWaitForJobs + rollback.DisableHooks = obj.Spec.GetRollback().DisableHooks + rollback.Force = obj.Spec.GetRollback().Force + rollback.Recreate = obj.Spec.GetRollback().Recreate + rollback.CleanupOnFail = obj.Spec.GetRollback().CleanupOnFail - if prev := rel.Status.Previous; prev != nil && prev.Name == rel.GetReleaseName() && prev.Namespace == rel.GetReleaseNamespace() { + if prev := obj.Status.Previous; prev != nil && prev.Name == obj.GetReleaseName() && prev.Namespace == obj.GetReleaseNamespace() { rollback.Version = prev.Version } + for _, opt := range opts { + opt(rollback) + } + return rollback } diff --git a/internal/action/rollback_test.go b/internal/action/rollback_test.go new file mode 100644 index 0000000..34d880b --- /dev/null +++ b/internal/action/rollback_test.go @@ -0,0 +1,139 @@ +/* +Copyright 2022 The Flux 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 action + +import ( + "testing" + "time" + + . "github.com/onsi/gomega" + helmaction "helm.sh/helm/v3/pkg/action" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v2 "github.com/fluxcd/helm-controller/api/v2beta2" +) + +func Test_newRollback(t *testing.T) { + t.Run("new rollback", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rollback", + Namespace: "rollback-ns", + }, + Spec: v2.HelmReleaseSpec{ + Timeout: &metav1.Duration{Duration: time.Minute}, + Rollback: &v2.Rollback{ + Timeout: &metav1.Duration{Duration: 10 * time.Second}, + Force: true, + }, + }, + } + + got := newRollback(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Timeout).To(Equal(obj.Spec.Rollback.Timeout.Duration)) + g.Expect(got.Force).To(Equal(obj.Spec.Rollback.Force)) + }) + + t.Run("rollback with previous", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rollback", + Namespace: "rollback-ns", + }, + Status: v2.HelmReleaseStatus{ + Previous: &v2.HelmReleaseInfo{ + Name: "rollback", + Namespace: "rollback-ns", + Version: 3, + }, + }, + } + + got := newRollback(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Version).To(Equal(obj.Status.Previous.Version)) + }) + + t.Run("rollback with stale previous", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rollback", + Namespace: "rollback-ns", + }, + Status: v2.HelmReleaseStatus{ + Previous: &v2.HelmReleaseInfo{ + Name: "rollback", + Namespace: "other-ns", + Version: 3, + }, + }, + } + + got := newRollback(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Version).To(BeZero()) + }) + + t.Run("timeout fallback", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rollback", + Namespace: "rollback-ns", + }, + Spec: v2.HelmReleaseSpec{ + Timeout: &metav1.Duration{Duration: time.Minute}, + }, + } + + got := newRollback(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Timeout).To(Equal(obj.Spec.Timeout.Duration)) + }) + + t.Run("applies options", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "rollback", + Namespace: "rollback-ns", + }, + Spec: v2.HelmReleaseSpec{}, + } + + got := newRollback(&helmaction.Configuration{}, obj, []RollbackOption{ + func(rollback *helmaction.Rollback) { + rollback.CleanupOnFail = true + }, + func(rollback *helmaction.Rollback) { + rollback.DryRun = true + }, + }) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.CleanupOnFail).To(BeTrue()) + g.Expect(got.DryRun).To(BeTrue()) + }) +} diff --git a/internal/action/test.go b/internal/action/test.go index 3bdcba8..4a59bd7 100644 --- a/internal/action/test.go +++ b/internal/action/test.go @@ -25,6 +25,11 @@ import ( v2 "github.com/fluxcd/helm-controller/api/v2beta2" ) +// TestOption can be used to modify Helm's action.ReleaseTesting after the +// instructions from the v2beta2.HelmRelease have been applied. This is for +// example useful to enable the dry-run setting as a CLI. +type TestOption func(action *helmaction.ReleaseTesting) + // Test runs the Helm test action with the provided config, using the // v2beta2.HelmReleaseSpec of the given object to determine the target release // and test configuration. @@ -33,16 +38,20 @@ import ( // expected to be done by the caller. In addition, it does not take note of the // action result. The caller is expected to listen to this using a // storage.ObserveFunc, which provides superior access to Helm storage writes. -func Test(_ context.Context, config *helmaction.Configuration, obj *v2.HelmRelease) (*helmrelease.Release, error) { - test := newTest(config, obj) +func Test(_ context.Context, config *helmaction.Configuration, obj *v2.HelmRelease, opts ...TestOption) (*helmrelease.Release, error) { + test := newTest(config, obj, opts) return test.Run(obj.GetReleaseName()) } -func newTest(config *helmaction.Configuration, obj *v2.HelmRelease) *helmaction.ReleaseTesting { +func newTest(config *helmaction.Configuration, obj *v2.HelmRelease, opts []TestOption) *helmaction.ReleaseTesting { test := helmaction.NewReleaseTesting(config) test.Namespace = obj.GetReleaseNamespace() test.Timeout = obj.Spec.GetTest().GetTimeout(obj.GetTimeout()).Duration + for _, opt := range opts { + opt(test) + } + return test } diff --git a/internal/action/test_test.go b/internal/action/test_test.go new file mode 100644 index 0000000..b9dd718 --- /dev/null +++ b/internal/action/test_test.go @@ -0,0 +1,96 @@ +/* +Copyright 2022 The Flux 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 action + +import ( + "testing" + "time" + + . "github.com/onsi/gomega" + helmaction "helm.sh/helm/v3/pkg/action" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v2 "github.com/fluxcd/helm-controller/api/v2beta2" +) + +func Test_newTest(t *testing.T) { + t.Run("new test", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-ns", + }, + Spec: v2.HelmReleaseSpec{ + Timeout: &metav1.Duration{Duration: time.Minute}, + Test: &v2.Test{ + Timeout: &metav1.Duration{Duration: 10 * time.Second}, + }, + }, + } + + got := newTest(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Namespace).To(Equal(obj.Namespace)) + g.Expect(got.Timeout).To(Equal(obj.Spec.Test.Timeout.Duration)) + }) + + t.Run("timeout fallback", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-ns", + }, + Spec: v2.HelmReleaseSpec{ + Timeout: &metav1.Duration{Duration: time.Minute}, + }, + } + + got := newTest(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Namespace).To(Equal(obj.Namespace)) + g.Expect(got.Timeout).To(Equal(obj.Spec.Timeout.Duration)) + }) + + t.Run("applies options", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test-ns", + }, + Spec: v2.HelmReleaseSpec{}, + } + + got := newTest(&helmaction.Configuration{}, obj, []TestOption{ + func(test *helmaction.ReleaseTesting) { + test.Filters = map[string][]string{ + "test": {"test"}, + } + }, + func(test *helmaction.ReleaseTesting) { + test.Filters["test2"] = []string{"test2"} + }, + }) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Filters).To(HaveLen(2)) + }) +} diff --git a/internal/action/uninstall.go b/internal/action/uninstall.go index 5a9959f..bfbafd2 100644 --- a/internal/action/uninstall.go +++ b/internal/action/uninstall.go @@ -25,6 +25,11 @@ import ( v2 "github.com/fluxcd/helm-controller/api/v2beta2" ) +// UninstallOption can be used to modify Helm's action.Uninstall after the +// instructions from the v2beta2.HelmRelease have been applied. This is for +// example useful to enable the dry-run setting as a CLI. +type UninstallOption func(*helmaction.Uninstall) + // Uninstall runs the Helm uninstall action with the provided config, using the // v2beta2.HelmReleaseSpec of the given object to determine the target release // and uninstall configuration. @@ -33,12 +38,12 @@ import ( // expected to be done by the caller. In addition, it does not take note of the // action result. The caller is expected to listen to this using a // storage.ObserveFunc, which provides superior access to Helm storage writes. -func Uninstall(ctx context.Context, config *helmaction.Configuration, obj *v2.HelmRelease) (*helmrelease.UninstallReleaseResponse, error) { - uninstall := newUninstall(config, obj) +func Uninstall(_ context.Context, config *helmaction.Configuration, obj *v2.HelmRelease, opts ...UninstallOption) (*helmrelease.UninstallReleaseResponse, error) { + uninstall := newUninstall(config, obj, opts) return uninstall.Run(obj.GetReleaseName()) } -func newUninstall(config *helmaction.Configuration, obj *v2.HelmRelease) *helmaction.Uninstall { +func newUninstall(config *helmaction.Configuration, obj *v2.HelmRelease, opts []UninstallOption) *helmaction.Uninstall { uninstall := helmaction.NewUninstall(config) uninstall.Timeout = obj.Spec.GetUninstall().GetTimeout(obj.GetTimeout()).Duration @@ -46,5 +51,9 @@ func newUninstall(config *helmaction.Configuration, obj *v2.HelmRelease) *helmac uninstall.KeepHistory = obj.Spec.GetUninstall().KeepHistory uninstall.Wait = !obj.Spec.GetUninstall().DisableWait + for _, opt := range opts { + opt(uninstall) + } + return uninstall } diff --git a/internal/action/uninstall_test.go b/internal/action/uninstall_test.go new file mode 100644 index 0000000..dcb3efe --- /dev/null +++ b/internal/action/uninstall_test.go @@ -0,0 +1,95 @@ +/* +Copyright 2022 The Flux 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 action + +import ( + "testing" + "time" + + . "github.com/onsi/gomega" + helmaction "helm.sh/helm/v3/pkg/action" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v2 "github.com/fluxcd/helm-controller/api/v2beta2" +) + +func Test_newUninstall(t *testing.T) { + t.Run("new uninstall", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "uninstall", + Namespace: "uninstall-ns", + }, + Spec: v2.HelmReleaseSpec{ + Timeout: &metav1.Duration{Duration: time.Minute}, + Uninstall: &v2.Uninstall{ + Timeout: &metav1.Duration{Duration: 10 * time.Second}, + KeepHistory: true, + }, + }, + } + + got := newUninstall(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Timeout).To(Equal(obj.Spec.Uninstall.Timeout.Duration)) + g.Expect(got.KeepHistory).To(Equal(obj.Spec.Uninstall.KeepHistory)) + }) + + t.Run("timeout fallback", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "uninstall", + Namespace: "uninstall-ns", + }, + Spec: v2.HelmReleaseSpec{ + Timeout: &metav1.Duration{Duration: time.Minute}, + }, + } + + got := newUninstall(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Timeout).To(Equal(obj.Spec.Timeout.Duration)) + }) + + t.Run("applies options", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "uninstall", + Namespace: "uninstall-ns", + }, + Spec: v2.HelmReleaseSpec{}, + } + + got := newUninstall(&helmaction.Configuration{}, obj, []UninstallOption{ + func(uninstall *helmaction.Uninstall) { + uninstall.Wait = true + }, + func(uninstall *helmaction.Uninstall) { + uninstall.DisableHooks = true + }, + }) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Wait).To(BeTrue()) + g.Expect(got.DisableHooks).To(BeTrue()) + }) +} diff --git a/internal/action/upgrade.go b/internal/action/upgrade.go index fcc0a84..9901542 100644 --- a/internal/action/upgrade.go +++ b/internal/action/upgrade.go @@ -30,6 +30,11 @@ import ( "github.com/fluxcd/helm-controller/internal/postrender" ) +// UpgradeOption can be used to modify Helm's action.Upgrade after the instructions +// from the v2beta2.HelmRelease have been applied. This is for example useful to +// enable the dry-run setting as a CLI. +type UpgradeOption func(upgrade *helmaction.Upgrade) + // Upgrade runs the Helm upgrade action with the provided config, using the // v2beta2.HelmReleaseSpec of the given object to determine the target release // and upgrade configuration. @@ -42,11 +47,8 @@ import ( // action result. The caller is expected to listen to this using a // storage.ObserveFunc, which provides superior access to Helm storage writes. func Upgrade(ctx context.Context, config *helmaction.Configuration, obj *v2.HelmRelease, chrt *helmchart.Chart, - vals helmchartutil.Values) (*helmrelease.Release, error) { - upgrade, err := newUpgrade(config, obj) - if err != nil { - return nil, err - } + vals helmchartutil.Values, opts ...UpgradeOption) (*helmrelease.Release, error) { + upgrade := newUpgrade(config, obj, opts) policy, err := crdPolicyOrDefault(obj.Spec.GetInstall().CRDs) if err != nil { @@ -59,20 +61,20 @@ func Upgrade(ctx context.Context, config *helmaction.Configuration, obj *v2.Helm return upgrade.RunWithContext(ctx, obj.GetReleaseName(), chrt, vals.AsMap()) } -func newUpgrade(config *helmaction.Configuration, rel *v2.HelmRelease) (*helmaction.Upgrade, error) { +func newUpgrade(config *helmaction.Configuration, obj *v2.HelmRelease, opts []UpgradeOption) *helmaction.Upgrade { upgrade := helmaction.NewUpgrade(config) - upgrade.Namespace = rel.GetReleaseNamespace() - upgrade.ResetValues = !rel.Spec.GetUpgrade().PreserveValues - upgrade.ReuseValues = rel.Spec.GetUpgrade().PreserveValues - upgrade.MaxHistory = rel.GetMaxHistory() - upgrade.Timeout = rel.Spec.GetUpgrade().GetTimeout(rel.GetTimeout()).Duration - upgrade.Wait = !rel.Spec.GetUpgrade().DisableWait - upgrade.WaitForJobs = !rel.Spec.GetUpgrade().DisableWaitForJobs - upgrade.DisableHooks = rel.Spec.GetUpgrade().DisableHooks - upgrade.DisableOpenAPIValidation = rel.Spec.GetUpgrade().DisableOpenAPIValidation - upgrade.Force = rel.Spec.GetUpgrade().Force - upgrade.CleanupOnFail = rel.Spec.GetUpgrade().CleanupOnFail + upgrade.Namespace = obj.GetReleaseNamespace() + upgrade.ResetValues = !obj.Spec.GetUpgrade().PreserveValues + upgrade.ReuseValues = obj.Spec.GetUpgrade().PreserveValues + upgrade.MaxHistory = obj.GetMaxHistory() + upgrade.Timeout = obj.Spec.GetUpgrade().GetTimeout(obj.GetTimeout()).Duration + upgrade.Wait = !obj.Spec.GetUpgrade().DisableWait + upgrade.WaitForJobs = !obj.Spec.GetUpgrade().DisableWaitForJobs + upgrade.DisableHooks = obj.Spec.GetUpgrade().DisableHooks + upgrade.DisableOpenAPIValidation = obj.Spec.GetUpgrade().DisableOpenAPIValidation + upgrade.Force = obj.Spec.GetUpgrade().Force + upgrade.CleanupOnFail = obj.Spec.GetUpgrade().CleanupOnFail upgrade.Devel = true // If the user opted-in to allow DNS lookups, enable it. @@ -80,11 +82,11 @@ func newUpgrade(config *helmaction.Configuration, rel *v2.HelmRelease) (*helmact upgrade.EnableDNS = allowDNS } - renderer, err := postrender.BuildPostRenderers(rel) - if err != nil { - return nil, err - } - upgrade.PostRenderer = renderer + upgrade.PostRenderer = postrender.BuildPostRenderers(obj) - return upgrade, err + for _, opt := range opts { + opt(upgrade) + } + + return upgrade } diff --git a/internal/action/upgrade_test.go b/internal/action/upgrade_test.go new file mode 100644 index 0000000..da62479 --- /dev/null +++ b/internal/action/upgrade_test.go @@ -0,0 +1,97 @@ +/* +Copyright 2022 The Flux 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 action + +import ( + "testing" + "time" + + . "github.com/onsi/gomega" + helmaction "helm.sh/helm/v3/pkg/action" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + v2 "github.com/fluxcd/helm-controller/api/v2beta2" +) + +func Test_newUpgrade(t *testing.T) { + t.Run("new upgrade", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "upgrade", + Namespace: "upgrade-ns", + }, + Spec: v2.HelmReleaseSpec{ + Timeout: &metav1.Duration{Duration: time.Minute}, + Upgrade: &v2.Upgrade{ + Timeout: &metav1.Duration{Duration: 10 * time.Second}, + Force: true, + }, + }, + } + + got := newUpgrade(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Namespace).To(Equal(obj.Namespace)) + g.Expect(got.Timeout).To(Equal(obj.Spec.Upgrade.Timeout.Duration)) + g.Expect(got.Force).To(Equal(obj.Spec.Upgrade.Force)) + }) + + t.Run("timeout fallback", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "upgrade", + Namespace: "upgrade-ns", + }, + Spec: v2.HelmReleaseSpec{ + Timeout: &metav1.Duration{Duration: time.Minute}, + }, + } + + got := newUpgrade(&helmaction.Configuration{}, obj, nil) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Namespace).To(Equal(obj.Namespace)) + g.Expect(got.Timeout).To(Equal(obj.Spec.Timeout.Duration)) + }) + + t.Run("applies options", func(t *testing.T) { + g := NewWithT(t) + + obj := &v2.HelmRelease{ + ObjectMeta: metav1.ObjectMeta{ + Name: "upgrade", + Namespace: "upgrade-ns", + }, + Spec: v2.HelmReleaseSpec{}, + } + + got := newUpgrade(&helmaction.Configuration{}, obj, []UpgradeOption{ + func(upgrade *helmaction.Upgrade) { + upgrade.Install = true + }, + func(upgrade *helmaction.Upgrade) { + upgrade.DryRun = true + }, + }) + g.Expect(got).ToNot(BeNil()) + g.Expect(got.Install).To(BeTrue()) + g.Expect(got.DryRun).To(BeTrue()) + }) +} diff --git a/internal/postrender/build.go b/internal/postrender/build.go index ba771d1..5dc419a 100644 --- a/internal/postrender/build.go +++ b/internal/postrender/build.go @@ -24,9 +24,9 @@ import ( // BuildPostRenderers creates the post-renderer instances from a HelmRelease // and combines them into a single Combined post renderer. -func BuildPostRenderers(rel *v2.HelmRelease) (helmpostrender.PostRenderer, error) { +func BuildPostRenderers(rel *v2.HelmRelease) helmpostrender.PostRenderer { if rel == nil { - return nil, nil + return nil } renderers := make([]helmpostrender.PostRenderer, 0) for _, r := range rel.Spec.PostRenderers { @@ -41,7 +41,7 @@ func BuildPostRenderers(rel *v2.HelmRelease) (helmpostrender.PostRenderer, error } renderers = append(renderers, NewOriginLabels(v2.GroupVersion.Group, rel.Namespace, rel.Name)) if len(renderers) == 0 { - return nil, nil + return nil } - return NewCombined(renderers...), nil + return NewCombined(renderers...) }