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  }