mirror of https://github.com/helm/helm
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 <git@jacob.work>pull/6054/head
parent
533a369464
commit
72127c391c
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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 }
|
Loading…
Reference in new issue