From 72127c391cee9ab8f8cfa14306ab2cf9b1445a14 Mon Sep 17 00:00:00 2001 From: Jacob LeGrone Date: Sat, 20 Jul 2019 12:05:02 -0400 Subject: [PATCH] feat(test): define tests as Jobs and allow arbitrary supporting resources This updates commands install, upgrade, delete, and test to share the same implementation for hook execution. BREAKING CHANGES: - The `test-failure` hook annotation is removed. Signed-off-by: Jacob LeGrone --- cmd/helm/release_testing_run.go | 33 +-- pkg/action/action_test.go | 4 +- pkg/action/hooks.go | 106 ++++++++++ pkg/action/install.go | 88 +------- pkg/action/release_testing.go | 45 +--- pkg/action/rollback.go | 63 +----- pkg/action/uninstall.go | 53 +---- pkg/action/upgrade.go | 53 +---- pkg/hooks/hooks.go | 67 ------ pkg/kube/client.go | 27 --- pkg/kube/fake/fake.go | 26 +-- pkg/kube/fake/printer.go | 6 - pkg/kube/interface.go | 6 - pkg/release/hook.go | 39 ++-- pkg/releasetesting/environment.go | 120 ----------- pkg/releasetesting/environment_test.go | 71 ------- pkg/releasetesting/test_suite.go | 187 ----------------- pkg/releasetesting/test_suite_test.go | 259 ------------------------ pkg/releaseutil/manifest_sorter.go | 27 ++- pkg/releaseutil/manifest_sorter_test.go | 4 +- pkg/releaseutil/manifest_test.go | 4 +- 21 files changed, 179 insertions(+), 1109 deletions(-) create mode 100644 pkg/action/hooks.go delete mode 100644 pkg/hooks/hooks.go delete mode 100644 pkg/releasetesting/environment.go delete mode 100644 pkg/releasetesting/environment_test.go delete mode 100644 pkg/releasetesting/test_suite.go delete mode 100644 pkg/releasetesting/test_suite_test.go diff --git a/cmd/helm/release_testing_run.go b/cmd/helm/release_testing_run.go index 9608ba374..d300a65e6 100644 --- a/cmd/helm/release_testing_run.go +++ b/cmd/helm/release_testing_run.go @@ -16,16 +16,13 @@ limitations under the License. package main import ( - "fmt" "io" "time" - "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 = ` @@ -44,27 +41,7 @@ func newReleaseTestRunCmd(cfg *action.Configuration, out io.Writer) *cobra.Comma 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") - } - } + return client.Run(args[0]) }, } @@ -74,11 +51,3 @@ func newReleaseTestRunCmd(cfg *action.Configuration, out io.Writer) *cobra.Comma return cmd } - -type testErr struct { - failed int -} - -func (err *testErr) Error() error { - return errors.Errorf("%v test(s) failed", err.failed) -} diff --git a/pkg/action/action_test.go b/pkg/action/action_test.go index 83346ea58..33fd98c28 100644 --- a/pkg/action/action_test.go +++ b/pkg/action/action_test.go @@ -89,7 +89,7 @@ var manifestWithTestHook = `kind: Pod metadata: name: finding-nemo, annotations: - "helm.sh/hook": test-success + "helm.sh/hook": test spec: containers: - name: nemo-test @@ -231,7 +231,7 @@ func namedReleaseStub(name string, status release.Status) *release.Release { Path: "finding-nemo", Manifest: manifestWithTestHook, Events: []release.HookEvent{ - release.HookReleaseTestSuccess, + release.HookTest, }, }, }, diff --git a/pkg/action/hooks.go b/pkg/action/hooks.go new file mode 100644 index 000000000..dd768d112 --- /dev/null +++ b/pkg/action/hooks.go @@ -0,0 +1,106 @@ +/* +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 action + +import ( + "bytes" + "sort" + "time" + + "github.com/pkg/errors" + + "helm.sh/helm/pkg/release" +) + +// execHook executes all of the hooks for the given hook event. +func (cfg *Configuration) execHook(hs []*release.Hook, hook release.HookEvent, timeout time.Duration) error { + executingHooks := []*release.Hook{} + + for _, h := range hs { + for _, e := range h.Events { + if e == hook { + executingHooks = append(executingHooks, h) + } + } + } + + sort.Sort(hookByWeight(executingHooks)) + + for _, h := range executingHooks { + if err := deleteHookByPolicy(cfg, h, release.HookBeforeHookCreation); err != nil { + return err + } + + b := bytes.NewBufferString(h.Manifest) + if err := cfg.KubeClient.Create(b); err != nil { + return errors.Wrapf(err, "warning: Hook %s %s failed", hook, h.Path) + } + b.Reset() + b.WriteString(h.Manifest) + + if err := cfg.KubeClient.WatchUntilReady(b, timeout); err != nil { + // If a hook is failed, checkout the annotation of the hook to determine whether the hook should be deleted + // under failed condition. If so, then clear the corresponding resource object in the hook + if err := deleteHookByPolicy(cfg, h, release.HookFailed); err != nil { + return err + } + return err + } + } + + // If all hooks are succeeded, checkout the annotation of each hook to determine whether the hook should be deleted + // under succeeded condition. If so, then clear the corresponding resource object in each hook + for _, h := range executingHooks { + if err := deleteHookByPolicy(cfg, h, release.HookSucceeded); err != nil { + return err + } + h.LastRun = time.Now() + } + + return nil +} + +// hookByWeight is a sorter for hooks +type hookByWeight []*release.Hook + +func (x hookByWeight) Len() int { return len(x) } +func (x hookByWeight) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x hookByWeight) Less(i, j int) bool { + if x[i].Weight == x[j].Weight { + return x[i].Name < x[j].Name + } + return x[i].Weight < x[j].Weight +} + +// deleteHookByPolicy deletes a hook if the hook policy instructs it to +func deleteHookByPolicy(cfg *Configuration, h *release.Hook, policy release.HookDeletePolicy) error { + if hookHasDeletePolicy(h, policy) { + b := bytes.NewBufferString(h.Manifest) + return cfg.KubeClient.Delete(b) + } + return nil +} + +// hookHasDeletePolicy determines whether the defined hook deletion policy matches the hook deletion polices +// supported by helm. If so, mark the hook as one should be deleted. +func hookHasDeletePolicy(h *release.Hook, policy release.HookDeletePolicy) bool { + for _, v := range h.DeletePolicies { + if policy == v { + return true + } + } + return false +} diff --git a/pkg/action/install.go b/pkg/action/install.go index 52eff5262..3969cb26d 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -25,7 +25,6 @@ import ( "os" "path" "path/filepath" - "sort" "strings" "text/template" "time" @@ -40,7 +39,6 @@ import ( "helm.sh/helm/pkg/downloader" "helm.sh/helm/pkg/engine" "helm.sh/helm/pkg/getter" - "helm.sh/helm/pkg/hooks" kubefake "helm.sh/helm/pkg/kube/fake" "helm.sh/helm/pkg/release" "helm.sh/helm/pkg/releaseutil" @@ -198,7 +196,7 @@ func (i *Install) Run(chrt *chart.Chart) (*release.Release, error) { // pre-install hooks if !i.DisableHooks { - if err := i.execHook(rel.Hooks, hooks.PreInstall); err != nil { + if err := i.execHook(rel.Hooks, release.HookPreInstall); err != nil { return i.failRelease(rel, fmt.Errorf("failed pre-install: %s", err)) } } @@ -220,7 +218,7 @@ func (i *Install) Run(chrt *chart.Chart) (*release.Release, error) { } if !i.DisableHooks { - if err := i.execHook(rel.Hooks, hooks.PostInstall); err != nil { + if err := i.execHook(rel.Hooks, release.HookPostInstall); err != nil { return i.failRelease(rel, fmt.Errorf("failed post-install: %s", err)) } } @@ -466,86 +464,8 @@ func (i *Install) validateManifest(manifest io.Reader) error { } // execHook executes all of the hooks for the given hook event. -func (i *Install) execHook(hs []*release.Hook, hook string) error { - executingHooks := []*release.Hook{} - - for _, h := range hs { - for _, e := range h.Events { - if string(e) == hook { - executingHooks = append(executingHooks, h) - } - } - } - - sort.Sort(hookByWeight(executingHooks)) - - for _, h := range executingHooks { - if err := deleteHookByPolicy(i.cfg, h, hooks.BeforeHookCreation); err != nil { - return err - } - - b := bytes.NewBufferString(h.Manifest) - if err := i.cfg.KubeClient.Create(b); err != nil { - return errors.Wrapf(err, "warning: Release %s %s %s failed", i.ReleaseName, hook, h.Path) - } - b.Reset() - b.WriteString(h.Manifest) - - if err := i.cfg.KubeClient.WatchUntilReady(b, i.Timeout); err != nil { - // If a hook is failed, checkout the annotation of the hook to determine whether the hook should be deleted - // under failed condition. If so, then clear the corresponding resource object in the hook - if err := deleteHookByPolicy(i.cfg, h, hooks.HookFailed); err != nil { - return err - } - return err - } - } - - // If all hooks are succeeded, checkout the annotation of each hook to determine whether the hook should be deleted - // under succeeded condition. If so, then clear the corresponding resource object in each hook - for _, h := range executingHooks { - if err := deleteHookByPolicy(i.cfg, h, hooks.HookSucceeded); err != nil { - return err - } - h.LastRun = time.Now() - } - - return nil -} - -// deletePolices represents a mapping between the key in the annotation for label deleting policy and its real meaning -// FIXME: Can we refactor this out? -var deletePolices = map[string]release.HookDeletePolicy{ - hooks.HookSucceeded: release.HookSucceeded, - hooks.HookFailed: release.HookFailed, - hooks.BeforeHookCreation: release.HookBeforeHookCreation, -} - -// hookHasDeletePolicy determines whether the defined hook deletion policy matches the hook deletion polices -// supported by helm. If so, mark the hook as one should be deleted. -func hookHasDeletePolicy(h *release.Hook, policy string) bool { - dp, ok := deletePolices[policy] - if !ok { - return false - } - for _, v := range h.DeletePolicies { - if dp == v { - return true - } - } - return false -} - -// hookByWeight is a sorter for hooks -type hookByWeight []*release.Hook - -func (x hookByWeight) Len() int { return len(x) } -func (x hookByWeight) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x hookByWeight) Less(i, j int) bool { - if x[i].Weight == x[j].Weight { - return x[i].Name < x[j].Name - } - return x[i].Weight < x[j].Weight +func (i *Install) execHook(hs []*release.Hook, hook release.HookEvent) error { + return i.cfg.execHook(hs, hook, i.Timeout) } // NameAndChart returns the name and chart that should be used. diff --git a/pkg/action/release_testing.go b/pkg/action/release_testing.go index 6aeb8b5b1..eb7e1ccc7 100644 --- a/pkg/action/release_testing.go +++ b/pkg/action/release_testing.go @@ -22,7 +22,6 @@ import ( "github.com/pkg/errors" "helm.sh/helm/pkg/release" - reltesting "helm.sh/helm/pkg/releasetesting" ) // ReleaseTesting is the action for testing a release. @@ -43,52 +42,16 @@ func NewReleaseTesting(cfg *Configuration) *ReleaseTesting { } // Run executes 'helm test' against the given release. -func (r *ReleaseTesting) Run(name string) (<-chan *release.TestReleaseResponse, <-chan error) { - errc := make(chan error, 1) +func (r *ReleaseTesting) Run(name string) error { if err := validateReleaseName(name); err != nil { - errc <- errors.Errorf("releaseTest: Release name is invalid: %s", name) - return nil, errc + return errors.Errorf("releaseTest: Release name is invalid: %s", name) } // finds the non-deleted release with the given name rel, err := r.cfg.Releases.Last(name) if err != nil { - errc <- err - return nil, errc + return err } - ch := make(chan *release.TestReleaseResponse, 1) - testEnv := &reltesting.Environment{ - Namespace: rel.Namespace, - KubeClient: r.cfg.KubeClient, - Timeout: r.Timeout, - Messages: ch, - } - r.cfg.Log("running tests for release %s", rel.Name) - tSuite := reltesting.NewTestSuite(rel) - - go func() { - defer close(errc) - defer close(ch) - - if err := tSuite.Run(testEnv); err != nil { - errc <- errors.Wrapf(err, "error running test suite for %s", rel.Name) - return - } - - rel.Info.LastTestSuiteRun = &release.TestSuite{ - StartedAt: tSuite.StartedAt, - CompletedAt: tSuite.CompletedAt, - Results: tSuite.Results, - } - - if r.Cleanup { - testEnv.DeleteTestPods(tSuite.TestManifests) - } - - if err := r.cfg.Releases.Update(rel); err != nil { - r.cfg.Log("test: Failed to store updated release: %s", err) - } - }() - return ch, errc + return r.cfg.execHook(rel.Hooks, release.HookTest, r.Timeout) } diff --git a/pkg/action/rollback.go b/pkg/action/rollback.go index 2db6ed7a9..c709ff20c 100644 --- a/pkg/action/rollback.go +++ b/pkg/action/rollback.go @@ -19,12 +19,10 @@ package action import ( "bytes" "fmt" - "sort" "time" "github.com/pkg/errors" - "helm.sh/helm/pkg/hooks" "helm.sh/helm/pkg/release" ) @@ -140,7 +138,7 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas // pre-rollback hooks if !r.DisableHooks { - if err := r.execHook(targetRelease.Hooks, hooks.PreRollback); err != nil { + if err := r.execHook(targetRelease.Hooks, release.HookPreRollback); err != nil { return targetRelease, err } } else { @@ -173,7 +171,7 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas // post-rollback hooks if !r.DisableHooks { - if err := r.execHook(targetRelease.Hooks, hooks.PostRollback); err != nil { + if err := r.execHook(targetRelease.Hooks, release.HookPostRollback); err != nil { return targetRelease, err } } @@ -195,59 +193,6 @@ func (r *Rollback) performRollback(currentRelease, targetRelease *release.Releas } // execHook executes all of the hooks for the given hook event. -func (r *Rollback) execHook(hs []*release.Hook, hook string) error { - timeout := r.Timeout - executingHooks := []*release.Hook{} - - for _, h := range hs { - for _, e := range h.Events { - if string(e) == hook { - executingHooks = append(executingHooks, h) - } - } - } - - sort.Sort(hookByWeight(executingHooks)) - - for _, h := range executingHooks { - if err := deleteHookByPolicy(r.cfg, h, hooks.BeforeHookCreation); err != nil { - return err - } - - b := bytes.NewBufferString(h.Manifest) - if err := r.cfg.KubeClient.Create(b); err != nil { - return errors.Wrapf(err, "warning: Hook %s %s failed", hook, h.Path) - } - b.Reset() - b.WriteString(h.Manifest) - - if err := r.cfg.KubeClient.WatchUntilReady(b, timeout); err != nil { - // If a hook is failed, checkout the annotation of the hook to determine whether the hook should be deleted - // under failed condition. If so, then clear the corresponding resource object in the hook - if err := deleteHookByPolicy(r.cfg, h, hooks.HookFailed); err != nil { - return err - } - return err - } - } - - // If all hooks are succeeded, checkout the annotation of each hook to determine whether the hook should be deleted - // under succeeded condition. If so, then clear the corresponding resource object in each hook - for _, h := range executingHooks { - if err := deleteHookByPolicy(r.cfg, h, hooks.HookSucceeded); err != nil { - return err - } - h.LastRun = time.Now() - } - - return nil -} - -// deleteHookByPolicy deletes a hook if the hook policy instructs it to -func deleteHookByPolicy(cfg *Configuration, h *release.Hook, policy string) error { - if hookHasDeletePolicy(h, policy) { - b := bytes.NewBufferString(h.Manifest) - return cfg.KubeClient.Delete(b) - } - return nil +func (r *Rollback) execHook(hs []*release.Hook, hook release.HookEvent) error { + return r.cfg.execHook(hs, hook, r.Timeout) } diff --git a/pkg/action/uninstall.go b/pkg/action/uninstall.go index cbe3f49dc..4090e8241 100644 --- a/pkg/action/uninstall.go +++ b/pkg/action/uninstall.go @@ -18,13 +18,11 @@ package action import ( "bytes" - "sort" "strings" "time" "github.com/pkg/errors" - "helm.sh/helm/pkg/hooks" "helm.sh/helm/pkg/kube" "helm.sh/helm/pkg/release" "helm.sh/helm/pkg/releaseutil" @@ -94,7 +92,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error) res := &release.UninstallReleaseResponse{Release: rel} if !u.DisableHooks { - if err := u.execHook(rel.Hooks, hooks.PreDelete); err != nil { + if err := u.execHook(rel.Hooks, release.HookPreDelete); err != nil { return res, err } } else { @@ -111,7 +109,7 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error) res.Info = kept if !u.DisableHooks { - if err := u.execHook(rel.Hooks, hooks.PostDelete); err != nil { + if err := u.execHook(rel.Hooks, release.HookPostDelete); err != nil { errs = append(errs, err) } } @@ -162,51 +160,8 @@ func joinErrors(errs []error) string { } // execHook executes all of the hooks for the given hook event. -func (u *Uninstall) execHook(hs []*release.Hook, hook string) error { - executingHooks := []*release.Hook{} - - for _, h := range hs { - for _, e := range h.Events { - if string(e) == hook { - executingHooks = append(executingHooks, h) - } - } - } - - sort.Sort(hookByWeight(executingHooks)) - - for _, h := range executingHooks { - if err := deleteHookByPolicy(u.cfg, h, hooks.BeforeHookCreation); err != nil { - return err - } - - b := bytes.NewBufferString(h.Manifest) - if err := u.cfg.KubeClient.Create(b); err != nil { - return errors.Wrapf(err, "warning: Hook %s %s failed", hook, h.Path) - } - b.Reset() - b.WriteString(h.Manifest) - - if err := u.cfg.KubeClient.WatchUntilReady(b, u.Timeout); err != nil { - // If a hook is failed, checkout the annotation of the hook to determine whether the hook should be deleted - // under failed condition. If so, then clear the corresponding resource object in the hook - if err := deleteHookByPolicy(u.cfg, h, hooks.HookFailed); err != nil { - return err - } - return err - } - } - - // If all hooks are succeeded, checkout the annotation of each hook to determine whether the hook should be deleted - // under succeeded condition. If so, then clear the corresponding resource object in each hook - for _, h := range executingHooks { - if err := deleteHookByPolicy(u.cfg, h, hooks.HookSucceeded); err != nil { - return err - } - h.LastRun = time.Now() - } - - return nil +func (u *Uninstall) execHook(hs []*release.Hook, hook release.HookEvent) error { + return u.cfg.execHook(hs, hook, u.Timeout) } // deleteRelease deletes the release and returns manifests that were kept in the deletion process diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index 43f05beac..6de69a842 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -19,14 +19,12 @@ package action import ( "bytes" "fmt" - "sort" "time" "github.com/pkg/errors" "helm.sh/helm/pkg/chart" "helm.sh/helm/pkg/chartutil" - "helm.sh/helm/pkg/hooks" "helm.sh/helm/pkg/kube" "helm.sh/helm/pkg/release" "helm.sh/helm/pkg/releaseutil" @@ -201,7 +199,7 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea // pre-upgrade hooks if !u.DisableHooks { - if err := u.execHook(upgradedRelease.Hooks, hooks.PreUpgrade); err != nil { + if err := u.execHook(upgradedRelease.Hooks, release.HookPreUpgrade); err != nil { return u.failRelease(upgradedRelease, fmt.Errorf("pre-upgrade hooks failed: %s", err)) } } else { @@ -222,7 +220,7 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea // post-upgrade hooks if !u.DisableHooks { - if err := u.execHook(upgradedRelease.Hooks, hooks.PostUpgrade); err != nil { + if err := u.execHook(upgradedRelease.Hooks, release.HookPostUpgrade); err != nil { return u.failRelease(upgradedRelease, fmt.Errorf("post-upgrade hooks failed: %s", err)) } } @@ -335,49 +333,6 @@ func validateManifest(c kube.Interface, manifest []byte) error { } // execHook executes all of the hooks for the given hook event. -func (u *Upgrade) execHook(hs []*release.Hook, hook string) error { - timeout := u.Timeout - executingHooks := []*release.Hook{} - - for _, h := range hs { - for _, e := range h.Events { - if string(e) == hook { - executingHooks = append(executingHooks, h) - } - } - } - - sort.Sort(hookByWeight(executingHooks)) - for _, h := range executingHooks { - if err := deleteHookByPolicy(u.cfg, h, hooks.BeforeHookCreation); err != nil { - return err - } - - b := bytes.NewBufferString(h.Manifest) - if err := u.cfg.KubeClient.Create(b); err != nil { - return errors.Wrapf(err, "warning: Hook %s %s failed", hook, h.Path) - } - b.Reset() - b.WriteString(h.Manifest) - - if err := u.cfg.KubeClient.WatchUntilReady(b, timeout); err != nil { - // If a hook is failed, checkout the annotation of the hook to determine whether the hook should be deleted - // under failed condition. If so, then clear the corresponding resource object in the hook - if err := deleteHookByPolicy(u.cfg, h, hooks.HookFailed); err != nil { - return err - } - return err - } - } - - // If all hooks are succeeded, checkout the annotation of each hook to determine whether the hook should be deleted - // under succeeded condition. If so, then clear the corresponding resource object in each hook - for _, h := range executingHooks { - if err := deleteHookByPolicy(u.cfg, h, hooks.HookSucceeded); err != nil { - return err - } - h.LastRun = time.Now() - } - - return nil +func (u *Upgrade) execHook(hs []*release.Hook, hook release.HookEvent) error { + return u.cfg.execHook(hs, hook, u.Timeout) } diff --git a/pkg/hooks/hooks.go b/pkg/hooks/hooks.go deleted file mode 100644 index 6b6c6fdcc..000000000 --- a/pkg/hooks/hooks.go +++ /dev/null @@ -1,67 +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 hooks - -import ( - "helm.sh/helm/pkg/release" -) - -// HookAnno is the label name for a hook -const HookAnno = "helm.sh/hook" - -// HookWeightAnno is the label name for a hook weight -const HookWeightAnno = "helm.sh/hook-weight" - -// HookDeleteAnno is the label name for the delete policy for a hook -const HookDeleteAnno = "helm.sh/hook-delete-policy" - -// Types of hooks -const ( - PreInstall = "pre-install" - PostInstall = "post-install" - PreDelete = "pre-delete" - PostDelete = "post-delete" - PreUpgrade = "pre-upgrade" - PostUpgrade = "post-upgrade" - PreRollback = "pre-rollback" - PostRollback = "post-rollback" - ReleaseTestSuccess = "test-success" - ReleaseTestFailure = "test-failure" -) - -// Type of policy for deleting the hook -const ( - HookSucceeded = "hook-succeeded" - HookFailed = "hook-failed" - BeforeHookCreation = "before-hook-creation" -) - -// FilterTestHooks filters the list of hooks are returns only testing hooks. -func FilterTestHooks(hooks []*release.Hook) []*release.Hook { - testHooks := []*release.Hook{} - - for _, h := range hooks { - for _, e := range h.Events { - if e == release.HookReleaseTestSuccess || e == release.HookReleaseTestFailure { - testHooks = append(testHooks, h) - continue - } - } - } - - return testHooks -} diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 2af142787..4aa817af3 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -19,7 +19,6 @@ package kube // import "helm.sh/helm/pkg/kube" import ( "context" "encoding/json" - "fmt" "io" "log" "strings" @@ -487,29 +486,3 @@ func scrubValidationError(err error) error { } return err } - -// 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(name string, timeout time.Duration) (v1.PodPhase, error) { - client, _ := c.KubernetesClientSet() - to := int64(timeout) - watcher, err := client.CoreV1().Pods(c.namespace()).Watch(metav1.ListOptions{ - FieldSelector: fmt.Sprintf("metadata.name=%s", name), - TimeoutSeconds: &to, - }) - - for event := range watcher.ResultChan() { - p, ok := event.Object.(*v1.Pod) - if !ok { - return v1.PodUnknown, fmt.Errorf("%s not a pod", name) - } - switch p.Status.Phase { - case v1.PodFailed: - return v1.PodFailed, nil - case v1.PodSucceeded: - return v1.PodSucceeded, nil - } - } - - return v1.PodUnknown, err -} diff --git a/pkg/kube/fake/fake.go b/pkg/kube/fake/fake.go index d22f0acf8..9d3a62069 100644 --- a/pkg/kube/fake/fake.go +++ b/pkg/kube/fake/fake.go @@ -21,7 +21,6 @@ import ( "io" "time" - v1 "k8s.io/api/core/v1" "k8s.io/cli-runtime/pkg/resource" "helm.sh/helm/pkg/kube" @@ -32,15 +31,14 @@ import ( // delegates all its calls to `PrintingKubeClient` type FailingKubeClient struct { PrintingKubeClient - CreateError error - WaitError error - GetError error - DeleteError error - WatchUntilReadyError error - UpdateError error - BuildError error - BuildUnstructuredError error - WaitAndGetCompletedPodPhaseError error + DeleteError error + WatchUntilReadyError error + UpdateError error + BuildError error + BuildUnstructuredError error + CreateError error + WaitError error + GetError error } // Create returns the configured error if set or prints @@ -106,11 +104,3 @@ func (f *FailingKubeClient) BuildUnstructured(r io.Reader) (kube.Result, error) } return f.PrintingKubeClient.Build(r) } - -// WaitAndGetCompletedPodPhase returns the configured error if set or prints -func (f *FailingKubeClient) WaitAndGetCompletedPodPhase(s string, d time.Duration) (v1.PodPhase, error) { - if f.WaitAndGetCompletedPodPhaseError != nil { - return v1.PodSucceeded, f.WaitAndGetCompletedPodPhaseError - } - return f.PrintingKubeClient.WaitAndGetCompletedPodPhase(s, d) -} diff --git a/pkg/kube/fake/printer.go b/pkg/kube/fake/printer.go index b8d927e8d..cc21358f5 100644 --- a/pkg/kube/fake/printer.go +++ b/pkg/kube/fake/printer.go @@ -20,7 +20,6 @@ import ( "io" "time" - v1 "k8s.io/api/core/v1" "k8s.io/cli-runtime/pkg/resource" "helm.sh/helm/pkg/kube" @@ -77,8 +76,3 @@ func (p *PrintingKubeClient) Build(_ io.Reader) (kube.Result, error) { func (p *PrintingKubeClient) BuildUnstructured(_ io.Reader) (kube.Result, error) { return p.Build(nil) } - -// WaitAndGetCompletedPodPhase implements KubeClient WaitAndGetCompletedPodPhase. -func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(_ string, _ time.Duration) (v1.PodPhase, error) { - return v1.PodSucceeded, nil -} diff --git a/pkg/kube/interface.go b/pkg/kube/interface.go index 9256f5e1c..72d7a0ea9 100644 --- a/pkg/kube/interface.go +++ b/pkg/kube/interface.go @@ -19,8 +19,6 @@ package kube import ( "io" "time" - - v1 "k8s.io/api/core/v1" ) // KubernetesClient represents a client capable of communicating with the Kubernetes API. @@ -57,10 +55,6 @@ type Interface interface { Build(reader io.Reader) (Result, error) BuildUnstructured(reader io.Reader) (Result, error) - - // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase - // and returns said phase (PodSucceeded or PodFailed qualify). - WaitAndGetCompletedPodPhase(name string, timeout time.Duration) (v1.PodPhase, error) } var _ Interface = (*Client)(nil) diff --git a/pkg/release/hook.go b/pkg/release/hook.go index d4cb73d54..a36412555 100644 --- a/pkg/release/hook.go +++ b/pkg/release/hook.go @@ -1,10 +1,11 @@ /* 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 + 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, @@ -15,23 +16,24 @@ limitations under the License. package release -import "time" +import ( + "time" +) // HookEvent specifies the hook event type HookEvent string // Hook event types const ( - HookPreInstall HookEvent = "pre-install" - HookPostInstall HookEvent = "post-install" - HookPreDelete HookEvent = "pre-delete" - HookPostDelete HookEvent = "post-delete" - HookPreUpgrade HookEvent = "pre-upgrade" - HookPostUpgrade HookEvent = "post-upgrade" - HookPreRollback HookEvent = "pre-rollback" - HookPostRollback HookEvent = "post-rollback" - HookReleaseTestSuccess HookEvent = "release-test-success" - HookReleaseTestFailure HookEvent = "release-test-failure" + HookPreInstall HookEvent = "pre-install" + HookPostInstall HookEvent = "post-install" + HookPreDelete HookEvent = "pre-delete" + HookPostDelete HookEvent = "post-delete" + HookPreUpgrade HookEvent = "pre-upgrade" + HookPostUpgrade HookEvent = "post-upgrade" + HookPreRollback HookEvent = "pre-rollback" + HookPostRollback HookEvent = "post-rollback" + HookTest HookEvent = "test" ) func (x HookEvent) String() string { return string(x) } @@ -41,13 +43,22 @@ type HookDeletePolicy string // Hook delete policy types const ( - HookSucceeded HookDeletePolicy = "succeeded" - HookFailed HookDeletePolicy = "failed" + HookSucceeded HookDeletePolicy = "hook-succeeded" + HookFailed HookDeletePolicy = "hook-failed" HookBeforeHookCreation HookDeletePolicy = "before-hook-creation" ) func (x HookDeletePolicy) String() string { return string(x) } +// HookAnnotation is the label name for a hook +const HookAnnotation = "helm.sh/hook" + +// HookWeightAnnotation is the label name for a hook weight +const HookWeightAnnotation = "helm.sh/hook-weight" + +// HookDeleteAnnotation is the label name for the delete policy for a hook +const HookDeleteAnnotation = "helm.sh/hook-delete-policy" + // Hook defines a hook object. type Hook struct { Name string `json:"name,omitempty"` diff --git a/pkg/releasetesting/environment.go b/pkg/releasetesting/environment.go deleted file mode 100644 index 7bff936b8..000000000 --- a/pkg/releasetesting/environment.go +++ /dev/null @@ -1,120 +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 releasetesting - -import ( - "bytes" - "fmt" - "log" - "time" - - v1 "k8s.io/api/core/v1" - - "helm.sh/helm/pkg/kube" - "helm.sh/helm/pkg/release" -) - -// Environment encapsulates information about where test suite executes and returns results -type Environment struct { - Namespace string - KubeClient kube.Interface - Messages chan *release.TestReleaseResponse - Timeout time.Duration -} - -func (env *Environment) createTestPod(test *test) error { - b := bytes.NewBufferString(test.manifest) - if err := env.KubeClient.Create(b); err != nil { - test.result.Info = err.Error() - test.result.Status = release.TestRunFailure - return err - } - - return nil -} - -func (env *Environment) getTestPodStatus(test *test) (v1.PodPhase, error) { - status, err := env.KubeClient.WaitAndGetCompletedPodPhase(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() - test.result.Status = release.TestRunUnknown - return status, err - } - - return status, err -} - -func (env *Environment) streamResult(r *release.TestRun) error { - switch r.Status { - case release.TestRunSuccess: - if err := env.streamSuccess(r.Name); err != nil { - return err - } - case release.TestRunFailure: - if err := env.streamFailed(r.Name); err != nil { - return err - } - - default: - if err := env.streamUnknown(r.Name, r.Info); err != nil { - return err - } - } - return nil -} - -func (env *Environment) streamRunning(name string) error { - msg := "RUNNING: " + name - return env.streamMessage(msg, release.TestRunRunning) -} - -func (env *Environment) streamError(info string) error { - msg := "ERROR: " + info - return env.streamMessage(msg, release.TestRunFailure) -} - -func (env *Environment) streamFailed(name string) error { - msg := fmt.Sprintf("FAILED: %s, run `kubectl logs %s --namespace %s` for more info", name, name, env.Namespace) - return env.streamMessage(msg, release.TestRunFailure) -} - -func (env *Environment) streamSuccess(name string) error { - msg := fmt.Sprintf("PASSED: %s", name) - return env.streamMessage(msg, release.TestRunSuccess) -} - -func (env *Environment) streamUnknown(name, info string) error { - msg := fmt.Sprintf("UNKNOWN: %s: %s", name, info) - return env.streamMessage(msg, release.TestRunUnknown) -} - -func (env *Environment) streamMessage(msg string, status release.TestRunStatus) error { - resp := &release.TestReleaseResponse{Msg: msg, Status: status} - env.Messages <- resp - return nil -} - -// DeleteTestPods deletes resources given in testManifests -func (env *Environment) DeleteTestPods(testManifests []string) { - for _, testManifest := range testManifests { - err := env.KubeClient.Delete(bytes.NewBufferString(testManifest)) - if err != nil { - env.streamError(err.Error()) - } - } -} diff --git a/pkg/releasetesting/environment_test.go b/pkg/releasetesting/environment_test.go deleted file mode 100644 index fbff19d3b..000000000 --- a/pkg/releasetesting/environment_test.go +++ /dev/null @@ -1,71 +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 releasetesting - -import ( - "testing" - - "github.com/pkg/errors" - - "helm.sh/helm/pkg/release" -) - -func TestCreateTestPodSuccess(t *testing.T) { - env := testEnvFixture() - test := testFixture() - - if err := env.createTestPod(test); err != nil { - t.Errorf("Expected no error, got an error: %s", err) - } -} - -func TestCreateTestPodFailure(t *testing.T) { - env := testEnvFixture() - env.KubeClient = &mockKubeClient{ - err: errors.New("We ran out of budget and couldn't create finding-nemo"), - } - test := testFixture() - - if err := env.createTestPod(test); err == nil { - t.Errorf("Expected error, got no error") - } - if test.result.Info == "" { - t.Errorf("Expected error to be saved in test result info but found empty string") - } - if test.result.Status != release.TestRunFailure { - t.Errorf("Expected test result status to be failure but got: %v", test.result.Status) - } -} - -func TestStreamMessage(t *testing.T) { - env := testEnvFixture() - defer close(env.Messages) - - expectedMessage := "testing streamMessage" - expectedStatus := release.TestRunSuccess - if err := env.streamMessage(expectedMessage, expectedStatus); err != nil { - t.Errorf("Expected no errors, got: %s", err) - } - - got := <-env.Messages - if got.Msg != expectedMessage { - t.Errorf("Expected message: %s, got: %s", expectedMessage, got.Msg) - } - if got.Status != expectedStatus { - t.Errorf("Expected status: %v, got: %v", expectedStatus, got.Status) - } -} diff --git a/pkg/releasetesting/test_suite.go b/pkg/releasetesting/test_suite.go deleted file mode 100644 index 05500881c..000000000 --- a/pkg/releasetesting/test_suite.go +++ /dev/null @@ -1,187 +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 releasetesting - -import ( - "strings" - "time" - - "github.com/pkg/errors" - v1 "k8s.io/api/core/v1" - "sigs.k8s.io/yaml" - - "helm.sh/helm/pkg/hooks" - "helm.sh/helm/pkg/release" - util "helm.sh/helm/pkg/releaseutil" -) - -// TestSuite what tests are run, results, and metadata -type TestSuite struct { - StartedAt time.Time - CompletedAt time.Time - TestManifests []string - Results []*release.TestRun -} - -type test struct { - name string - manifest string - expectedSuccess bool - result *release.TestRun -} - -// NewTestSuite takes a release object and returns a TestSuite object with test definitions -// extracted from the release -func NewTestSuite(rel *release.Release) *TestSuite { - return &TestSuite{ - TestManifests: extractTestManifestsFromHooks(rel.Hooks), - Results: []*release.TestRun{}, - } -} - -// Run executes tests in a test suite and stores a result within a given environment -func (ts *TestSuite) Run(env *Environment) error { - ts.StartedAt = time.Now() - - if len(ts.TestManifests) == 0 { - // TODO: make this better, adding test run status on test suite is weird - env.streamMessage("No Tests Found", release.TestRunUnknown) - } - - for _, testManifest := range ts.TestManifests { - test, err := newTest(testManifest) - if err != nil { - return err - } - - test.result.StartedAt = time.Now() - if err := env.streamRunning(test.name); err != nil { - return err - } - test.result.Status = release.TestRunRunning - - resourceCreated := true - if err := env.createTestPod(test); err != nil { - resourceCreated = false - if streamErr := env.streamError(test.result.Info); streamErr != nil { - return err - } - } - - resourceCleanExit := true - status := v1.PodUnknown - if resourceCreated { - status, err = env.getTestPodStatus(test) - if err != nil { - resourceCleanExit = false - if streamErr := env.streamError(test.result.Info); streamErr != nil { - return streamErr - } - } - } - - if resourceCreated && resourceCleanExit { - if err := test.assignTestResult(status); err != nil { - return err - } - - if err := env.streamResult(test.result); err != nil { - return err - } - } - - test.result.CompletedAt = time.Now() - ts.Results = append(ts.Results, test.result) - } - - ts.CompletedAt = time.Now() - return nil -} - -func (t *test) assignTestResult(podStatus v1.PodPhase) error { - switch podStatus { - case v1.PodSucceeded: - if t.expectedSuccess { - t.result.Status = release.TestRunSuccess - } else { - t.result.Status = release.TestRunFailure - } - case v1.PodFailed: - if !t.expectedSuccess { - t.result.Status = release.TestRunSuccess - } else { - t.result.Status = release.TestRunFailure - } - default: - t.result.Status = release.TestRunUnknown - } - - return nil -} - -func expectedSuccess(hookTypes []string) (bool, error) { - for _, hookType := range hookTypes { - hookType = strings.ToLower(strings.TrimSpace(hookType)) - if hookType == hooks.ReleaseTestSuccess { - return true, nil - } else if hookType == hooks.ReleaseTestFailure { - return false, nil - } - } - return false, errors.Errorf("no %s or %s hook found", hooks.ReleaseTestSuccess, hooks.ReleaseTestFailure) -} - -func extractTestManifestsFromHooks(h []*release.Hook) []string { - testHooks := hooks.FilterTestHooks(h) - - tests := []string{} - for _, h := range testHooks { - individualTests := util.SplitManifests(h.Manifest) - for _, t := range individualTests { - tests = append(tests, t) - } - } - return tests -} - -func newTest(testManifest string) (*test, error) { - var sh util.SimpleHead - err := yaml.Unmarshal([]byte(testManifest), &sh) - if err != nil { - return nil, err - } - - if sh.Kind != "Pod" { - return nil, errors.Errorf("%s is not a pod", sh.Metadata.Name) - } - - hookTypes := sh.Metadata.Annotations[hooks.HookAnno] - expected, err := expectedSuccess(strings.Split(hookTypes, ",")) - if err != nil { - return nil, err - } - - name := strings.TrimSuffix(sh.Metadata.Name, ",") - return &test{ - name: name, - manifest: testManifest, - expectedSuccess: expected, - result: &release.TestRun{ - Name: name, - }, - }, nil -} diff --git a/pkg/releasetesting/test_suite_test.go b/pkg/releasetesting/test_suite_test.go deleted file mode 100644 index 9256df467..000000000 --- a/pkg/releasetesting/test_suite_test.go +++ /dev/null @@ -1,259 +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 releasetesting - -import ( - "io" - "testing" - "time" - - v1 "k8s.io/api/core/v1" - - "helm.sh/helm/pkg/kube" - "helm.sh/helm/pkg/release" -) - -const manifestWithTestSuccessHook = ` -apiVersion: v1 -kind: Pod -metadata: - name: finding-nemo, - annotations: - "helm.sh/hook": test-success -spec: - containers: - - name: nemo-test - image: fake-image - cmd: fake-command -` - -const manifestWithTestFailureHook = ` -apiVersion: v1 -kind: Pod -metadata: - name: gold-rush, - annotations: - "helm.sh/hook": test-failure -spec: - containers: - - name: gold-finding-test - image: fake-gold-finding-image - cmd: fake-gold-finding-command -` -const manifestWithInstallHooks = `apiVersion: v1 -kind: ConfigMap -metadata: - name: test-cm - annotations: - "helm.sh/hook": post-install,pre-delete -data: - name: value -` - -func TestRun(t *testing.T) { - testManifests := []string{manifestWithTestSuccessHook, manifestWithTestFailureHook} - ts := testSuiteFixture(testManifests) - env := testEnvFixture() - - go func() { - defer close(env.Messages) - if err := ts.Run(env); err != nil { - t.Error(err) - } - }() - - for i := 0; i <= 4; i++ { - <-env.Messages - } - if _, ok := <-env.Messages; ok { - t.Errorf("Expected 4 messages streamed") - } - - if ts.StartedAt.IsZero() { - t.Errorf("Expected StartedAt to not be nil. Got: %v", ts.StartedAt) - } - if ts.CompletedAt.IsZero() { - t.Errorf("Expected CompletedAt to not be nil. Got: %v", ts.CompletedAt) - } - if len(ts.Results) != 2 { - t.Errorf("Expected 2 test result. Got %v", len(ts.Results)) - } - - result := ts.Results[0] - if result.StartedAt.IsZero() { - t.Errorf("Expected test StartedAt to not be nil. Got: %v", result.StartedAt) - } - if result.CompletedAt.IsZero() { - t.Errorf("Expected test CompletedAt to not be nil. Got: %v", result.CompletedAt) - } - if result.Name != "finding-nemo" { - t.Errorf("Expected test name to be finding-nemo. Got: %v", result.Name) - } - if result.Status != release.TestRunSuccess { - t.Errorf("Expected test result to be successful, got: %v", result.Status) - } - result2 := ts.Results[1] - if result2.StartedAt.IsZero() { - t.Errorf("Expected test StartedAt to not be nil. Got: %v", result2.StartedAt) - } - if result2.CompletedAt.IsZero() { - t.Errorf("Expected test CompletedAt to not be nil. Got: %v", result2.CompletedAt) - } - if result2.Name != "gold-rush" { - t.Errorf("Expected test name to be gold-rush, Got: %v", result2.Name) - } - if result2.Status != release.TestRunFailure { - t.Errorf("Expected test result to be successful, got: %v", result2.Status) - } -} - -func TestRunEmptyTestSuite(t *testing.T) { - ts := testSuiteFixture([]string{}) - env := testEnvFixture() - - go func() { - defer close(env.Messages) - if err := ts.Run(env); err != nil { - t.Error(err) - } - }() - - msg := <-env.Messages - if msg.Msg != "No Tests Found" { - t.Errorf("Expected message 'No Tests Found', Got: %v", msg.Msg) - } - - for range env.Messages { - } - - if ts.StartedAt.IsZero() { - t.Errorf("Expected StartedAt to not be nil. Got: %v", ts.StartedAt) - } - if ts.CompletedAt.IsZero() { - t.Errorf("Expected CompletedAt to not be nil. Got: %v", ts.CompletedAt) - } - if len(ts.Results) != 0 { - t.Errorf("Expected 0 test result. Got %v", len(ts.Results)) - } -} - -func TestRunSuccessWithTestFailureHook(t *testing.T) { - ts := testSuiteFixture([]string{manifestWithTestFailureHook}) - env := testEnvFixture() - env.KubeClient = &mockKubeClient{podFail: true} - - go func() { - defer close(env.Messages) - if err := ts.Run(env); err != nil { - t.Error(err) - } - }() - - for i := 0; i <= 4; i++ { - <-env.Messages - } - if _, ok := <-env.Messages; ok { - t.Errorf("Expected 4 messages streamed") - } - - if ts.StartedAt.IsZero() { - t.Errorf("Expected StartedAt to not be nil. Got: %v", ts.StartedAt) - } - if ts.CompletedAt.IsZero() { - t.Errorf("Expected CompletedAt to not be nil. Got: %v", ts.CompletedAt) - } - if len(ts.Results) != 1 { - t.Errorf("Expected 1 test result. Got %v", len(ts.Results)) - } - - result := ts.Results[0] - if result.StartedAt.IsZero() { - t.Errorf("Expected test StartedAt to not be nil. Got: %v", result.StartedAt) - } - if result.CompletedAt.IsZero() { - t.Errorf("Expected test CompletedAt to not be nil. Got: %v", result.CompletedAt) - } - if result.Name != "gold-rush" { - t.Errorf("Expected test name to be gold-rush, Got: %v", result.Name) - } - if result.Status != release.TestRunSuccess { - t.Errorf("Expected test result to be successful, got: %v", result.Status) - } -} - -func TestExtractTestManifestsFromHooks(t *testing.T) { - testManifests := extractTestManifestsFromHooks(hooksStub) - - if len(testManifests) != 1 { - t.Errorf("Expected 1 test manifest, Got: %v", len(testManifests)) - } -} - -var hooksStub = []*release.Hook{ - { - Manifest: manifestWithTestSuccessHook, - Events: []release.HookEvent{ - release.HookReleaseTestSuccess, - }, - }, - { - Manifest: manifestWithInstallHooks, - Events: []release.HookEvent{ - release.HookPostInstall, - }, - }, -} - -func testFixture() *test { - return &test{ - manifest: manifestWithTestSuccessHook, - result: &release.TestRun{}, - } -} - -func testSuiteFixture(testManifests []string) *TestSuite { - testResults := []*release.TestRun{} - ts := &TestSuite{ - TestManifests: testManifests, - Results: testResults, - } - return ts -} - -func testEnvFixture() *Environment { - return &Environment{ - Namespace: "default", - KubeClient: &mockKubeClient{}, - Timeout: 1, - Messages: make(chan *release.TestReleaseResponse, 1), - } -} - -type mockKubeClient struct { - kube.Interface - podFail bool - err error -} - -func (c *mockKubeClient) WaitAndGetCompletedPodPhase(_ string, _ time.Duration) (v1.PodPhase, error) { - if c.podFail { - return v1.PodFailed, nil - } - return v1.PodSucceeded, nil -} -func (c *mockKubeClient) Create(_ io.Reader) error { return c.err } -func (c *mockKubeClient) Delete(_ io.Reader) error { return nil } diff --git a/pkg/releaseutil/manifest_sorter.go b/pkg/releaseutil/manifest_sorter.go index c4e5e255a..eb6016094 100644 --- a/pkg/releaseutil/manifest_sorter.go +++ b/pkg/releaseutil/manifest_sorter.go @@ -26,7 +26,6 @@ import ( "sigs.k8s.io/yaml" "helm.sh/helm/pkg/chartutil" - "helm.sh/helm/pkg/hooks" "helm.sh/helm/pkg/release" ) @@ -53,16 +52,16 @@ type result struct { // TODO: Refactor this out. It's here because naming conventions were not followed through. // So fix the Test hook names and then remove this. var events = map[string]release.HookEvent{ - hooks.PreInstall: release.HookPreInstall, - hooks.PostInstall: release.HookPostInstall, - hooks.PreDelete: release.HookPreDelete, - hooks.PostDelete: release.HookPostDelete, - hooks.PreUpgrade: release.HookPreUpgrade, - hooks.PostUpgrade: release.HookPostUpgrade, - hooks.PreRollback: release.HookPreRollback, - hooks.PostRollback: release.HookPostRollback, - hooks.ReleaseTestSuccess: release.HookReleaseTestSuccess, - hooks.ReleaseTestFailure: release.HookReleaseTestFailure, + release.HookPreInstall.String(): release.HookPreInstall, + release.HookPostInstall.String(): release.HookPostInstall, + release.HookPreDelete.String(): release.HookPreDelete, + release.HookPostDelete.String(): release.HookPostDelete, + release.HookPreUpgrade.String(): release.HookPreUpgrade, + release.HookPostUpgrade.String(): release.HookPostUpgrade, + release.HookPreRollback.String(): release.HookPreRollback, + release.HookPostRollback.String(): release.HookPostRollback, + release.HookTest.String(): release.HookTest, + "test-success": release.HookTest, } // SortManifests takes a map of filename/YAML contents, splits the file @@ -142,7 +141,7 @@ func (file *manifestFile) sort(result *result) error { continue } - hookTypes, ok := entry.Metadata.Annotations[hooks.HookAnno] + hookTypes, ok := entry.Metadata.Annotations[release.HookAnnotation] if !ok { result.generic = append(result.generic, Manifest{ Name: file.path, @@ -182,7 +181,7 @@ func (file *manifestFile) sort(result *result) error { result.hooks = append(result.hooks, h) - operateAnnotationValues(entry, hooks.HookDeleteAnno, func(value string) { + operateAnnotationValues(entry, release.HookDeleteAnnotation, func(value string) { h.DeletePolicies = append(h.DeletePolicies, release.HookDeletePolicy(value)) }) } @@ -201,7 +200,7 @@ func hasAnyAnnotation(entry SimpleHead) bool { // // If no weight is found, the assigned weight is 0 func calculateHookWeight(entry SimpleHead) int { - hws := entry.Metadata.Annotations[hooks.HookWeightAnno] + hws := entry.Metadata.Annotations[release.HookWeightAnnotation] hw, err := strconv.Atoi(hws) if err != nil { hw = 0 diff --git a/pkg/releaseutil/manifest_sorter_test.go b/pkg/releaseutil/manifest_sorter_test.go index 4fa22a192..a82a1941e 100644 --- a/pkg/releaseutil/manifest_sorter_test.go +++ b/pkg/releaseutil/manifest_sorter_test.go @@ -116,7 +116,7 @@ metadata: name: []string{"eighth", "example-test"}, path: "eight", kind: []string{"ConfigMap", "Pod"}, - hooks: map[string][]release.HookEvent{"eighth": nil, "example-test": {release.HookReleaseTestSuccess}}, + hooks: map[string][]release.HookEvent{"eighth": nil, "example-test": {release.HookTest}}, manifest: `kind: ConfigMap apiVersion: v1 metadata: @@ -129,7 +129,7 @@ kind: Pod metadata: name: example-test annotations: - "helm.sh/hook": test-success + "helm.sh/hook": test `, }, } diff --git a/pkg/releaseutil/manifest_test.go b/pkg/releaseutil/manifest_test.go index e8a8a7262..b667f796f 100644 --- a/pkg/releaseutil/manifest_test.go +++ b/pkg/releaseutil/manifest_test.go @@ -29,7 +29,7 @@ kind: Pod metadata: name: finding-nemo, annotations: - "helm.sh/hook": test-success + "helm.sh/hook": test spec: containers: - name: nemo-test @@ -42,7 +42,7 @@ kind: Pod metadata: name: finding-nemo, annotations: - "helm.sh/hook": test-success + "helm.sh/hook": test spec: containers: - name: nemo-test