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