github.com/cloudposse/helm@v2.2.3+incompatible/pkg/releasetesting/test_suite.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors All rights reserved. 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/kubernetes/pkg/api" 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 env.streamMessage("No Tests Found") 69 } 70 71 for _, testManifest := range ts.TestManifests { 72 test, err := newTest(testManifest) 73 if err != nil { 74 return err 75 } 76 77 test.result.StartedAt = timeconv.Now() 78 if err := env.streamRunning(test.result.Name); err != nil { 79 return err 80 } 81 82 resourceCreated := true 83 if err := env.createTestPod(test); err != nil { 84 resourceCreated = false 85 if streamErr := env.streamError(test.result.Info); streamErr != nil { 86 return err 87 } 88 } 89 90 resourceCleanExit := true 91 status := api.PodUnknown 92 if resourceCreated { 93 status, err = env.getTestPodStatus(test) 94 if err != nil { 95 resourceCleanExit = false 96 if streamErr := env.streamUnknown(test.result.Name, test.result.Info); streamErr != nil { 97 return streamErr 98 } 99 } 100 } 101 102 if resourceCreated && resourceCleanExit { 103 if err := test.assignTestResult(status); err != nil { 104 return err 105 } 106 107 if err := env.streamResult(test.result); err != nil { 108 return err 109 } 110 } 111 112 test.result.CompletedAt = timeconv.Now() 113 ts.Results = append(ts.Results, test.result) 114 } 115 116 ts.CompletedAt = timeconv.Now() 117 return nil 118 } 119 120 func (t *test) assignTestResult(podStatus api.PodPhase) error { 121 switch podStatus { 122 case api.PodSucceeded: 123 if t.expectedSuccess { 124 t.result.Status = release.TestRun_SUCCESS 125 } else { 126 t.result.Status = release.TestRun_FAILURE 127 } 128 case api.PodFailed: 129 if !t.expectedSuccess { 130 t.result.Status = release.TestRun_SUCCESS 131 } else { 132 t.result.Status = release.TestRun_FAILURE 133 } 134 default: 135 t.result.Status = release.TestRun_UNKNOWN 136 } 137 138 return nil 139 } 140 141 func expectedSuccess(hookTypes []string) (bool, error) { 142 for _, hookType := range hookTypes { 143 hookType = strings.ToLower(strings.TrimSpace(hookType)) 144 if hookType == hooks.ReleaseTestSuccess { 145 return true, nil 146 } else if hookType == hooks.ReleaseTestFailure { 147 return false, nil 148 } 149 } 150 return false, fmt.Errorf("No %s or %s hook found", hooks.ReleaseTestSuccess, hooks.ReleaseTestFailure) 151 } 152 153 func extractTestManifestsFromHooks(h []*release.Hook) ([]string, error) { 154 testHooks := hooks.FilterTestHooks(h) 155 156 tests := []string{} 157 for _, h := range testHooks { 158 individualTests := util.SplitManifests(h.Manifest) 159 for _, t := range individualTests { 160 tests = append(tests, t) 161 } 162 } 163 return tests, nil 164 } 165 166 func newTest(testManifest string) (*test, error) { 167 var sh util.SimpleHead 168 err := yaml.Unmarshal([]byte(testManifest), &sh) 169 if err != nil { 170 return nil, err 171 } 172 173 if sh.Kind != "Pod" { 174 return nil, fmt.Errorf("%s is not a pod", sh.Metadata.Name) 175 } 176 177 hookTypes := sh.Metadata.Annotations[hooks.HookAnno] 178 expected, err := expectedSuccess(strings.Split(hookTypes, ",")) 179 if err != nil { 180 return nil, err 181 } 182 183 name := strings.TrimSuffix(sh.Metadata.Name, ",") 184 return &test{ 185 manifest: testManifest, 186 expectedSuccess: expected, 187 result: &release.TestRun{ 188 Name: name, 189 }, 190 }, nil 191 }