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  }