github.com/Beeketing/helm@v2.12.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 "fmt" 21 "strings" 22 23 "github.com/ghodss/yaml" 24 "github.com/golang/protobuf/ptypes/timestamp" 25 "k8s.io/api/core/v1" 26 27 "k8s.io/helm/pkg/hooks" 28 "k8s.io/helm/pkg/proto/hapi/release" 29 util "k8s.io/helm/pkg/releaseutil" 30 "k8s.io/helm/pkg/timeconv" 31 ) 32 33 // TestSuite what tests are run, results, and metadata 34 type TestSuite struct { 35 StartedAt *timestamp.Timestamp 36 CompletedAt *timestamp.Timestamp 37 TestManifests []string 38 Results []*release.TestRun 39 } 40 41 type test struct { 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, error) { 50 testManifests, err := extractTestManifestsFromHooks(rel.Hooks) 51 if err != nil { 52 return nil, err 53 } 54 55 results := []*release.TestRun{} 56 57 return &TestSuite{ 58 TestManifests: testManifests, 59 Results: results, 60 }, nil 61 } 62 63 // Run executes tests in a test suite and stores a result within a given environment 64 func (ts *TestSuite) Run(env *Environment) error { 65 ts.StartedAt = timeconv.Now() 66 67 if len(ts.TestManifests) == 0 { 68 // TODO: make this better, adding test run status on test suite is weird 69 env.streamMessage("No Tests Found", release.TestRun_UNKNOWN) 70 } 71 72 for _, testManifest := range ts.TestManifests { 73 test, err := newTest(testManifest) 74 if err != nil { 75 return err 76 } 77 78 test.result.StartedAt = timeconv.Now() 79 if err := env.streamRunning(test.result.Name); err != nil { 80 return err 81 } 82 test.result.Status = release.TestRun_RUNNING 83 84 resourceCreated := true 85 if err := env.createTestPod(test); err != nil { 86 resourceCreated = false 87 if streamErr := env.streamError(test.result.Info); streamErr != nil { 88 return err 89 } 90 } 91 92 resourceCleanExit := true 93 status := v1.PodUnknown 94 if resourceCreated { 95 status, err = env.getTestPodStatus(test) 96 if err != nil { 97 resourceCleanExit = false 98 if streamErr := env.streamError(test.result.Info); streamErr != nil { 99 return streamErr 100 } 101 } 102 } 103 104 if resourceCreated && resourceCleanExit { 105 if err := test.assignTestResult(status); err != nil { 106 return err 107 } 108 109 if err := env.streamResult(test.result); err != nil { 110 return err 111 } 112 } 113 114 test.result.CompletedAt = timeconv.Now() 115 ts.Results = append(ts.Results, test.result) 116 } 117 118 ts.CompletedAt = timeconv.Now() 119 return nil 120 } 121 122 func (t *test) assignTestResult(podStatus v1.PodPhase) error { 123 switch podStatus { 124 case v1.PodSucceeded: 125 if t.expectedSuccess { 126 t.result.Status = release.TestRun_SUCCESS 127 } else { 128 t.result.Status = release.TestRun_FAILURE 129 } 130 case v1.PodFailed: 131 if !t.expectedSuccess { 132 t.result.Status = release.TestRun_SUCCESS 133 } else { 134 t.result.Status = release.TestRun_FAILURE 135 } 136 default: 137 t.result.Status = release.TestRun_UNKNOWN 138 } 139 140 return nil 141 } 142 143 func expectedSuccess(hookTypes []string) (bool, error) { 144 for _, hookType := range hookTypes { 145 hookType = strings.ToLower(strings.TrimSpace(hookType)) 146 if hookType == hooks.ReleaseTestSuccess { 147 return true, nil 148 } else if hookType == hooks.ReleaseTestFailure { 149 return false, nil 150 } 151 } 152 return false, fmt.Errorf("No %s or %s hook found", hooks.ReleaseTestSuccess, hooks.ReleaseTestFailure) 153 } 154 155 func extractTestManifestsFromHooks(h []*release.Hook) ([]string, error) { 156 testHooks := hooks.FilterTestHooks(h) 157 158 tests := []string{} 159 for _, h := range testHooks { 160 individualTests := util.SplitManifests(h.Manifest) 161 for _, t := range individualTests { 162 tests = append(tests, t) 163 } 164 } 165 return tests, nil 166 } 167 168 func newTest(testManifest string) (*test, error) { 169 var sh util.SimpleHead 170 err := yaml.Unmarshal([]byte(testManifest), &sh) 171 if err != nil { 172 return nil, err 173 } 174 175 if sh.Kind != "Pod" { 176 return nil, fmt.Errorf("%s is not a pod", sh.Metadata.Name) 177 } 178 179 hookTypes := sh.Metadata.Annotations[hooks.HookAnno] 180 expected, err := expectedSuccess(strings.Split(hookTypes, ",")) 181 if err != nil { 182 return nil, err 183 } 184 185 name := strings.TrimSuffix(sh.Metadata.Name, ",") 186 return &test{ 187 manifest: testManifest, 188 expectedSuccess: expected, 189 result: &release.TestRun{ 190 Name: name, 191 }, 192 }, nil 193 }