github.com/umeshredd/helm@v3.0.0-alpha.1+incompatible/pkg/releasetesting/test_suite.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package releasetesting 18 19 import ( 20 "strings" 21 "time" 22 23 "github.com/ghodss/yaml" 24 "github.com/pkg/errors" 25 v1 "k8s.io/api/core/v1" 26 27 "helm.sh/helm/pkg/hooks" 28 "helm.sh/helm/pkg/release" 29 util "helm.sh/helm/pkg/releaseutil" 30 ) 31 32 // TestSuite what tests are run, results, and metadata 33 type TestSuite struct { 34 StartedAt time.Time 35 CompletedAt time.Time 36 TestManifests []string 37 Results []*release.TestRun 38 } 39 40 type test struct { 41 name string 42 manifest string 43 expectedSuccess bool 44 result *release.TestRun 45 } 46 47 // NewTestSuite takes a release object and returns a TestSuite object with test definitions 48 // extracted from the release 49 func NewTestSuite(rel *release.Release) *TestSuite { 50 return &TestSuite{ 51 TestManifests: extractTestManifestsFromHooks(rel.Hooks), 52 Results: []*release.TestRun{}, 53 } 54 } 55 56 // Run executes tests in a test suite and stores a result within a given environment 57 func (ts *TestSuite) Run(env *Environment) error { 58 ts.StartedAt = time.Now() 59 60 if len(ts.TestManifests) == 0 { 61 // TODO: make this better, adding test run status on test suite is weird 62 env.streamMessage("No Tests Found", release.TestRunUnknown) 63 } 64 65 for _, testManifest := range ts.TestManifests { 66 test, err := newTest(testManifest) 67 if err != nil { 68 return err 69 } 70 71 test.result.StartedAt = time.Now() 72 if err := env.streamRunning(test.name); err != nil { 73 return err 74 } 75 test.result.Status = release.TestRunRunning 76 77 resourceCreated := true 78 if err := env.createTestPod(test); err != nil { 79 resourceCreated = false 80 if streamErr := env.streamError(test.result.Info); streamErr != nil { 81 return err 82 } 83 } 84 85 resourceCleanExit := true 86 status := v1.PodUnknown 87 if resourceCreated { 88 status, err = env.getTestPodStatus(test) 89 if err != nil { 90 resourceCleanExit = false 91 if streamErr := env.streamError(test.result.Info); streamErr != nil { 92 return streamErr 93 } 94 } 95 } 96 97 if resourceCreated && resourceCleanExit { 98 if err := test.assignTestResult(status); err != nil { 99 return err 100 } 101 102 if err := env.streamResult(test.result); err != nil { 103 return err 104 } 105 } 106 107 test.result.CompletedAt = time.Now() 108 ts.Results = append(ts.Results, test.result) 109 } 110 111 ts.CompletedAt = time.Now() 112 return nil 113 } 114 115 func (t *test) assignTestResult(podStatus v1.PodPhase) error { 116 switch podStatus { 117 case v1.PodSucceeded: 118 if t.expectedSuccess { 119 t.result.Status = release.TestRunSuccess 120 } else { 121 t.result.Status = release.TestRunFailure 122 } 123 case v1.PodFailed: 124 if !t.expectedSuccess { 125 t.result.Status = release.TestRunSuccess 126 } else { 127 t.result.Status = release.TestRunFailure 128 } 129 default: 130 t.result.Status = release.TestRunUnknown 131 } 132 133 return nil 134 } 135 136 func expectedSuccess(hookTypes []string) (bool, error) { 137 for _, hookType := range hookTypes { 138 hookType = strings.ToLower(strings.TrimSpace(hookType)) 139 if hookType == hooks.ReleaseTestSuccess { 140 return true, nil 141 } else if hookType == hooks.ReleaseTestFailure { 142 return false, nil 143 } 144 } 145 return false, errors.Errorf("no %s or %s hook found", hooks.ReleaseTestSuccess, hooks.ReleaseTestFailure) 146 } 147 148 func extractTestManifestsFromHooks(h []*release.Hook) []string { 149 testHooks := hooks.FilterTestHooks(h) 150 151 tests := []string{} 152 for _, h := range testHooks { 153 individualTests := util.SplitManifests(h.Manifest) 154 for _, t := range individualTests { 155 tests = append(tests, t) 156 } 157 } 158 return tests 159 } 160 161 func newTest(testManifest string) (*test, error) { 162 var sh util.SimpleHead 163 err := yaml.Unmarshal([]byte(testManifest), &sh) 164 if err != nil { 165 return nil, err 166 } 167 168 if sh.Kind != "Pod" { 169 return nil, errors.Errorf("%s is not a pod", sh.Metadata.Name) 170 } 171 172 hookTypes := sh.Metadata.Annotations[hooks.HookAnno] 173 expected, err := expectedSuccess(strings.Split(hookTypes, ",")) 174 if err != nil { 175 return nil, err 176 } 177 178 name := strings.TrimSuffix(sh.Metadata.Name, ",") 179 return &test{ 180 name: name, 181 manifest: testManifest, 182 expectedSuccess: expected, 183 result: &release.TestRun{ 184 Name: name, 185 }, 186 }, nil 187 }