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  }