Commit e1321912 authored by Michelle Noorali's avatar Michelle Noorali

ref(pkg/): refactor helm test logic

pulled logic out in pkg/releasetesting
parent 9bd12953
......@@ -52,6 +52,6 @@ message Release {
// Namespace is the kubernetes namespace of the release.
string namespace = 8;
// TestSuite provides results on the last test run on a release
hapi.release.TestSuite test_suite = 9;
// LastTestSuiteRun provides results on the last test run on a release
hapi.release.TestSuite last_test_suite_run = 9;
}
......@@ -103,6 +103,7 @@ func PrintStatus(out io.Writer, res *services.GetReleaseStatusResponse) {
fmt.Fprintf(w, "RESOURCES:\n%s\n", re.ReplaceAllString(res.Info.Status.Resources, "\t"))
w.Flush()
}
if len(res.Info.Status.Notes) > 0 {
fmt.Fprintf(out, "NOTES:\n%s\n", res.Info.Status.Notes)
}
......
......@@ -307,9 +307,6 @@ func perform(c *Client, namespace string, infos Result, fn ResourceActorFunc) er
return ErrNoObjectsVisited
}
if err != nil {
return err
}
for _, info := range infos {
if err := fn(info); err != nil {
return err
......
......@@ -22,7 +22,7 @@ type Info struct {
// Deleted tracks when this object was deleted.
Deleted *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=deleted" json:"deleted,omitempty"`
// Description is human-friendly "log entry" about this release.
Description string `protobuf:"bytes,5,opt,name=Description" json:"Description,omitempty"`
Description string `protobuf:"bytes,5,opt,name=Description,json=description" json:"Description,omitempty"`
}
func (m *Info) Reset() { *m = Info{} }
......@@ -65,20 +65,20 @@ func init() {
func init() { proto.RegisterFile("hapi/release/info.proto", fileDescriptor1) }
var fileDescriptor1 = []byte{
// 235 bytes of a gzipped FileDescriptorProto
// 236 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x8f, 0x31, 0x4f, 0xc3, 0x30,
0x10, 0x85, 0x95, 0x52, 0x5a, 0xd5, 0x6d, 0x19, 0x2c, 0x24, 0x42, 0x16, 0x22, 0xa6, 0x0e, 0xc8,
0x10, 0x85, 0x95, 0x52, 0x5a, 0xd5, 0x69, 0x19, 0x2c, 0x24, 0x42, 0x16, 0x22, 0xa6, 0x0e, 0xc8,
0x91, 0x80, 0x1d, 0x81, 0xba, 0xb0, 0x06, 0x26, 0x16, 0xe4, 0xe2, 0x73, 0xb1, 0xe4, 0xe6, 0x2c,
0xfb, 0x3a, 0xf0, 0x2f, 0xf8, 0xc9, 0xa8, 0xb6, 0x83, 0xd2, 0xa9, 0xab, 0xbf, 0xf7, 0x3e, 0xbf,
0x63, 0x57, 0xdf, 0xd2, 0x99, 0xc6, 0x83, 0x05, 0x19, 0xa0, 0x31, 0x9d, 0x46, 0xe1, 0x3c, 0x12,
0xf2, 0xc5, 0x01, 0x88, 0x0c, 0xaa, 0x9b, 0x2d, 0xe2, 0xd6, 0x42, 0x13, 0xd9, 0x66, 0xaf, 0x1b,
0x32, 0x3b, 0x08, 0x24, 0x77, 0x2e, 0xc5, 0xab, 0xeb, 0x23, 0x4f, 0x20, 0x49, 0xfb, 0x90, 0xd0,
0xed, 0xef, 0x88, 0x8d, 0x5f, 0x3b, 0x8d, 0xfc, 0x8e, 0x4d, 0x12, 0x28, 0x8b, 0xba, 0x58, 0xcd,
0xef, 0x2f, 0xc5, 0xf0, 0x0f, 0xf1, 0x16, 0x59, 0x9b, 0x33, 0xfc, 0x99, 0x5d, 0x68, 0xe3, 0x03,
0x7d, 0x2a, 0x70, 0x16, 0x7f, 0x40, 0x95, 0xa3, 0xd8, 0xaa, 0x44, 0xda, 0x22, 0xfa, 0x2d, 0xe2,
0xbd, 0xdf, 0xd2, 0x2e, 0x63, 0x63, 0x9d, 0x0b, 0xfc, 0x89, 0x2d, 0xad, 0x1c, 0x1a, 0xce, 0x4e,
0x1a, 0x16, 0x87, 0xc2, 0xbf, 0xe0, 0x91, 0x4d, 0x15, 0x58, 0x20, 0x50, 0xe5, 0xf8, 0x64, 0xb5,
0x8f, 0xf2, 0x9a, 0xcd, 0xd7, 0x10, 0xbe, 0xbc, 0x71, 0x64, 0xb0, 0x2b, 0xcf, 0xeb, 0x62, 0x35,
0x6b, 0x87, 0x4f, 0x2f, 0xb3, 0x8f, 0x69, 0xbe, 0x7a, 0x33, 0x89, 0xa6, 0x87, 0xbf, 0x00, 0x00,
0x00, 0xff, 0xff, 0x1a, 0x52, 0x8f, 0x9c, 0x89, 0x01, 0x00, 0x00,
0xfb, 0x3a, 0xf0, 0x2f, 0xf8, 0xc9, 0xa8, 0xb6, 0x03, 0x65, 0xea, 0xea, 0xef, 0xbd, 0xcf, 0xef,
0xd8, 0xc5, 0xa7, 0x74, 0xa6, 0xf5, 0x60, 0x41, 0x06, 0x68, 0x4d, 0xaf, 0x51, 0x38, 0x8f, 0x84,
0x7c, 0xbe, 0x07, 0x22, 0x83, 0xfa, 0x6a, 0x83, 0xb8, 0xb1, 0xd0, 0x46, 0xb6, 0xde, 0xe9, 0x96,
0xcc, 0x16, 0x02, 0xc9, 0xad, 0x4b, 0xf1, 0xfa, 0xf2, 0x9f, 0x27, 0x90, 0xa4, 0x5d, 0x48, 0xe8,
0xfa, 0x7b, 0xc4, 0xc6, 0xcf, 0xbd, 0x46, 0x7e, 0xc3, 0x26, 0x09, 0x54, 0x45, 0x53, 0x2c, 0xcb,
0xdb, 0x73, 0x71, 0xf8, 0x87, 0x78, 0x89, 0xac, 0xcb, 0x19, 0xfe, 0xc8, 0xce, 0xb4, 0xf1, 0x81,
0xde, 0x15, 0x38, 0x8b, 0x5f, 0xa0, 0xaa, 0x51, 0x6c, 0xd5, 0x22, 0x6d, 0x11, 0xc3, 0x16, 0xf1,
0x3a, 0x6c, 0xe9, 0x16, 0xb1, 0xb1, 0xca, 0x05, 0xfe, 0xc0, 0x16, 0x56, 0x1e, 0x1a, 0x4e, 0x8e,
0x1a, 0xe6, 0xfb, 0xc2, 0xaf, 0xe0, 0x9e, 0x4d, 0x15, 0x58, 0x20, 0x50, 0xd5, 0xf8, 0x68, 0x75,
0x88, 0xf2, 0x86, 0x95, 0x2b, 0x08, 0x1f, 0xde, 0x38, 0x32, 0xd8, 0x57, 0xa7, 0x4d, 0xb1, 0x9c,
0x75, 0xa5, 0xfa, 0x7b, 0x7a, 0x9a, 0xbd, 0x4d, 0xf3, 0xd5, 0xeb, 0x49, 0x34, 0xdd, 0xfd, 0x04,
0x00, 0x00, 0xff, 0xff, 0x1e, 0x2a, 0x57, 0x7d, 0x89, 0x01, 0x00, 0x00,
}
......@@ -35,8 +35,8 @@ type Release struct {
Version int32 `protobuf:"varint,7,opt,name=version" json:"version,omitempty"`
// Namespace is the kubernetes namespace of the release.
Namespace string `protobuf:"bytes,8,opt,name=namespace" json:"namespace,omitempty"`
// TestSuite provides results on the last test run on a release
TestSuite *TestSuite `protobuf:"bytes,9,opt,name=test_suite,json=testSuite" json:"test_suite,omitempty"`
// LastTestSuiteRun provides results on the last test run on a release
LastTestSuiteRun *TestSuite `protobuf:"bytes,9,opt,name=last_test_suite_run,json=lastTestSuiteRun" json:"last_test_suite_run,omitempty"`
}
func (m *Release) Reset() { *m = Release{} }
......@@ -72,9 +72,9 @@ func (m *Release) GetHooks() []*Hook {
return nil
}
func (m *Release) GetTestSuite() *TestSuite {
func (m *Release) GetLastTestSuiteRun() *TestSuite {
if m != nil {
return m.TestSuite
return m.LastTestSuiteRun
}
return nil
}
......@@ -86,24 +86,24 @@ func init() {
func init() { proto.RegisterFile("hapi/release/release.proto", fileDescriptor2) }
var fileDescriptor2 = []byte{
// 290 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0xbf, 0x4e, 0xc3, 0x30,
0x10, 0xc6, 0x95, 0x36, 0x7f, 0x9a, 0x83, 0x85, 0x1b, 0xa8, 0x15, 0x81, 0x14, 0x31, 0x40, 0xc4,
0x90, 0x4a, 0x20, 0xf1, 0x00, 0xb0, 0xc0, 0x6a, 0x98, 0x58, 0x90, 0x89, 0x1c, 0x62, 0x95, 0xda,
0x51, 0x6c, 0x78, 0x4e, 0x1e, 0x09, 0xf9, 0x4f, 0x68, 0x42, 0x17, 0xc7, 0x77, 0xbf, 0x2f, 0xf7,
0x7d, 0x3e, 0x28, 0x3a, 0xd6, 0x8b, 0xcd, 0xc0, 0x3f, 0x39, 0xd3, 0x7c, 0xfc, 0xd6, 0xfd, 0xa0,
0x8c, 0xc2, 0x63, 0xcb, 0xea, 0xd0, 0x2b, 0xd6, 0x33, 0x65, 0xa7, 0xd4, 0xd6, 0xcb, 0xfe, 0x01,
0x21, 0x5b, 0x15, 0xc0, 0xf9, 0x0c, 0x18, 0xae, 0xcd, 0x9b, 0xfe, 0x12, 0x86, 0xcf, 0xfe, 0x6b,
0x3a, 0x36, 0x98, 0x4d, 0xa3, 0x64, 0x2b, 0x3e, 0x02, 0x38, 0x9d, 0x02, 0x7b, 0xfa, 0xfe, 0xc5,
0xcf, 0x02, 0x32, 0xea, 0xa7, 0x21, 0x42, 0x2c, 0xd9, 0x8e, 0x93, 0xa8, 0x8c, 0xaa, 0x9c, 0xba,
0x3b, 0x5e, 0x42, 0x6c, 0xdd, 0xc9, 0xa2, 0x8c, 0xaa, 0xa3, 0x1b, 0xac, 0xa7, 0xf1, 0xeb, 0x27,
0xd9, 0x2a, 0xea, 0x38, 0x5e, 0x41, 0xe2, 0xc6, 0x92, 0xa5, 0x13, 0x9e, 0x78, 0xa1, 0x77, 0x7a,
0xb0, 0x27, 0xf5, 0x1c, 0xaf, 0x21, 0xf5, 0xc1, 0x48, 0x3c, 0x1d, 0x19, 0x94, 0x8e, 0xd0, 0xa0,
0xc0, 0x02, 0x56, 0x3b, 0x26, 0x45, 0xcb, 0xb5, 0x21, 0x89, 0x0b, 0xf5, 0x57, 0x63, 0x05, 0x89,
0xdd, 0x97, 0x26, 0x69, 0xb9, 0x3c, 0x4c, 0xf6, 0xa8, 0xd4, 0x96, 0x7a, 0x01, 0x12, 0xc8, 0xbe,
0xf9, 0xa0, 0x85, 0x92, 0x24, 0x2b, 0xa3, 0x2a, 0xa1, 0x63, 0x89, 0x67, 0x90, 0xdb, 0x47, 0xea,
0x9e, 0x35, 0x9c, 0xac, 0x9c, 0xc1, 0xbe, 0x81, 0x77, 0x00, 0xfb, 0xfd, 0x92, 0xdc, 0xa5, 0x5d,
0xcf, 0x6d, 0x5e, 0xb8, 0x36, 0xcf, 0x16, 0xd3, 0xdc, 0x8c, 0xd7, 0xfb, 0xfc, 0x35, 0x0b, 0xfc,
0x3d, 0x75, 0x4b, 0xbe, 0xfd, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xea, 0xe6, 0xdb, 0x71, 0x12, 0x02,
0x00, 0x00,
// 300 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0xcd, 0x4e, 0x84, 0x30,
0x10, 0x80, 0xc3, 0x2e, 0x3f, 0x4b, 0xf5, 0xa0, 0x63, 0xe2, 0x36, 0x44, 0x13, 0xe2, 0x41, 0x89,
0x07, 0x36, 0xd1, 0x37, 0xd0, 0xc4, 0xe8, 0xb5, 0x7a, 0xf2, 0x42, 0x2a, 0x29, 0xd2, 0xec, 0x6e,
0x4b, 0x68, 0xf1, 0x79, 0x7d, 0x14, 0xd3, 0x9f, 0x15, 0xd0, 0x4b, 0x61, 0xe6, 0xfb, 0x3a, 0x33,
0x1d, 0x94, 0xb5, 0xb4, 0xe3, 0x9b, 0x9e, 0xed, 0x18, 0x55, 0xec, 0xf0, 0x2d, 0xbb, 0x5e, 0x6a,
0x09, 0xc7, 0x86, 0x95, 0x3e, 0x97, 0xad, 0x67, 0x66, 0x2b, 0xe5, 0xd6, 0x69, 0x7f, 0x00, 0x17,
0x8d, 0xf4, 0xe0, 0x72, 0x06, 0x34, 0x53, 0xba, 0x52, 0x03, 0xd7, 0x6c, 0x76, 0xaf, 0x6e, 0x69,
0xaf, 0x37, 0xb5, 0x14, 0x0d, 0xff, 0xf4, 0xe0, 0x7c, 0x0a, 0xcc, 0xe9, 0xf2, 0x57, 0xdf, 0x0b,
0x94, 0x10, 0x57, 0x0d, 0x00, 0x85, 0x82, 0xee, 0x19, 0x0e, 0xf2, 0xa0, 0x48, 0x89, 0xfd, 0x87,
0x6b, 0x14, 0x9a, 0xee, 0x78, 0x91, 0x07, 0xc5, 0xd1, 0x1d, 0x94, 0xd3, 0xf1, 0xcb, 0x17, 0xd1,
0x48, 0x62, 0x39, 0xdc, 0xa0, 0xc8, 0x96, 0xc5, 0x4b, 0x2b, 0x9e, 0x3a, 0xd1, 0x75, 0x7a, 0x34,
0x27, 0x71, 0x1c, 0x6e, 0x51, 0xec, 0x06, 0xc3, 0xe1, 0xb4, 0xa4, 0x37, 0x2d, 0x21, 0xde, 0x80,
0x0c, 0xad, 0xf6, 0x54, 0xf0, 0x86, 0x29, 0x8d, 0x23, 0x3b, 0xd4, 0x6f, 0x0c, 0x05, 0x8a, 0xcc,
0xbe, 0x14, 0x8e, 0xf3, 0xe5, 0xff, 0xc9, 0x9e, 0xa5, 0xdc, 0x12, 0x27, 0x00, 0x46, 0xc9, 0x17,
0xeb, 0x15, 0x97, 0x02, 0x27, 0x79, 0x50, 0x44, 0xe4, 0x10, 0xc2, 0x05, 0x4a, 0xcd, 0x23, 0x55,
0x47, 0x6b, 0x86, 0x57, 0xb6, 0xc1, 0x98, 0x80, 0x27, 0x74, 0xb6, 0xa3, 0x4a, 0x57, 0xe3, 0x92,
0xab, 0x7e, 0x10, 0x38, 0xb5, 0x63, 0xaf, 0xe7, 0xfd, 0xde, 0x98, 0xd2, 0xaf, 0x46, 0x21, 0x27,
0xe6, 0xce, 0x18, 0x0e, 0xe2, 0x21, 0x7d, 0x4f, 0xbc, 0xf6, 0x11, 0xdb, 0xa5, 0xdf, 0xff, 0x04,
0x00, 0x00, 0xff, 0xff, 0x85, 0x06, 0x87, 0x2b, 0x22, 0x02, 0x00, 0x00,
}
This diff is collapsed.
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package releasetesting
import (
"fmt"
"k8s.io/helm/pkg/proto/hapi/services"
"k8s.io/helm/pkg/tiller/environment"
)
type Environment struct {
Namespace string
KubeClient environment.KubeClient
Stream services.ReleaseService_RunReleaseTestServer
Timeout int64
}
func streamRunning(name string, stream services.ReleaseService_RunReleaseTestServer) error {
msg := "RUNNING: " + name
if err := streamMessage(msg, stream); err != nil {
return err
}
return nil
}
func streamError(info string, stream services.ReleaseService_RunReleaseTestServer) error {
msg := "ERROR: " + info
if err := streamMessage(msg, stream); err != nil {
return err
}
return nil
}
func streamFailed(name string, stream services.ReleaseService_RunReleaseTestServer) error {
msg := fmt.Sprintf("FAILED: %s, run `kubectl logs %s` for more info", name, name)
if err := streamMessage(msg, stream); err != nil {
return err
}
return nil
}
func streamSuccess(name string, stream services.ReleaseService_RunReleaseTestServer) error {
msg := fmt.Sprintf("PASSED: %s", name)
if err := streamMessage(msg, stream); err != nil {
return err
}
return nil
}
func streamUnknown(name, info string, stream services.ReleaseService_RunReleaseTestServer) error {
msg := fmt.Sprintf("UNKNOWN: %s: %s", name, info)
if err := streamMessage(msg, stream); err != nil {
return err
}
return nil
}
func streamMessage(msg string, stream services.ReleaseService_RunReleaseTestServer) error {
resp := &services.TestReleaseResponse{Msg: msg}
// TODO: handle err better
if err := stream.Send(resp); err != nil {
return err
}
return nil
}
......@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package tiller
package releasetesting
import (
"bytes"
......@@ -23,181 +23,173 @@ import (
"time"
"github.com/ghodss/yaml"
"github.com/golang/protobuf/ptypes/timestamp"
"k8s.io/kubernetes/pkg/api"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/proto/hapi/services"
"k8s.io/helm/pkg/tiller/environment"
util "k8s.io/helm/pkg/releaseutil"
"k8s.io/helm/pkg/timeconv"
)
//TODO: testSuiteRunner.Run()
//struct testSuiteRunner {
//suite *release.TestSuite,
//tests []string,
//kube environemtn.KubeClient,
//timeout int64
////stream or output channel
//}
type TestSuite struct {
StartedAt *timestamp.Timestamp
CompletedAt *timestamp.Timestamp
TestManifests []string
Results []*release.TestRun
}
type test struct {
manifest string
result *release.TestRun
}
func NewTestSuite(rel *release.Release, env *Environment) (*TestSuite, error) {
testManifests, err := prepareTestManifests(rel.Hooks, rel.Name)
if err != nil {
return nil, err
}
func runReleaseTests(tests []string, rel *release.Release, kube environment.KubeClient, stream services.ReleaseService_RunReleaseTestServer, timeout int64) (*release.TestSuite, error) {
results := []*release.TestRun{}
//TODO: add results to test suite
suite := &release.TestSuite{}
suite.StartedAt = timeconv.Now()
return &TestSuite{
TestManifests: testManifests,
Results: results,
}, nil
}
for _, h := range tests {
var sh simpleHead
err := yaml.Unmarshal([]byte(h), &sh)
if err != nil {
return nil, err
}
func newTest(testManifest string) (*test, error) {
var sh util.SimpleHead
err := yaml.Unmarshal([]byte(testManifest), &sh)
if err != nil {
return nil, err
}
if sh.Kind != "Pod" {
return nil, fmt.Errorf("%s is not a pod", sh.Metadata.Name)
}
if sh.Kind != "Pod" {
return nil, fmt.Errorf("%s is not a pod", sh.Metadata.Name)
return &test{
manifest: testManifest,
result: &release.TestRun{
Name: sh.Metadata.Name,
},
}, nil
}
func (t *TestSuite) createTestPod(test *test, env *Environment) error {
b := bytes.NewBufferString(test.manifest)
if err := env.KubeClient.Create(env.Namespace, b); err != nil {
log.Printf(err.Error())
test.result.Info = err.Error()
test.result.Status = release.TestRun_FAILURE
return err
}
return nil
}
func (t *TestSuite) getPodExitStatus(test *test, env *Environment) (api.PodPhase, error) {
b := bytes.NewBufferString(test.manifest)
status, err := env.KubeClient.WaitAndGetCompletedPodPhase(env.Namespace, b, time.Duration(env.Timeout)*time.Second)
if err != nil {
log.Printf("Error getting status for pod %s: %s", test.result.Name, err)
test.result.Info = err.Error()
test.result.Status = release.TestRun_UNKNOWN
return status, err
}
return status, err
}
func (t *TestSuite) Run(env *Environment) error {
t.StartedAt = timeconv.Now()
for _, testManifest := range t.TestManifests {
test, err := newTest(testManifest)
if err != nil {
return err
}
ts := &release.TestRun{Name: sh.Metadata.Name}
ts.StartedAt = timeconv.Now()
if err := streamRunning(ts.Name, stream); err != nil {
return nil, err
test.result.StartedAt = timeconv.Now()
if err := streamRunning(test.result.Name, env.Stream); err != nil {
return err
}
resourceCreated := true
b := bytes.NewBufferString(h)
if err := kube.Create(rel.Namespace, b); err != nil {
if err := t.createTestPod(test, env); err != nil {
resourceCreated = false
msg := fmt.Sprintf("ERROR: %s", err)
log.Printf(msg)
ts.Info = err.Error()
ts.Status = release.TestRun_FAILURE
if streamErr := streamMessage(msg, stream); streamErr != nil {
return nil, err
if streamErr := streamError(test.result.Info, env.Stream); streamErr != nil {
return err
}
}
status := api.PodUnknown
resourceCleanExit := true
status := api.PodUnknown
if resourceCreated {
b.Reset()
b.WriteString(h)
status, err = kube.WaitAndGetCompletedPodPhase(rel.Namespace, b, time.Duration(timeout)*time.Second)
status, err = t.getPodExitStatus(test, env)
if err != nil {
resourceCleanExit = false
log.Printf("Error getting status for pod %s: %s", ts.Name, err)
ts.Info = err.Error()
ts.Status = release.TestRun_UNKNOWN
if streamErr := streamFailed(ts.Name, stream); streamErr != nil {
return nil, err
if streamErr := streamUnknown(test.result.Name, test.result.Info, env.Stream); streamErr != nil {
return streamErr
}
}
}
// TODO: maybe better suited as a switch statement and include
// PodUnknown, PodFailed, PodRunning, and PodPending scenarios
if resourceCreated && resourceCleanExit && status == api.PodSucceeded {
ts.Status = release.TestRun_SUCCESS
if streamErr := streamSuccess(ts.Name, stream); streamErr != nil {
return nil, streamErr
test.result.Status = release.TestRun_SUCCESS
if streamErr := streamSuccess(test.result.Name, env.Stream); streamErr != nil {
return streamErr
}
} else if resourceCreated && resourceCleanExit && status == api.PodFailed {
ts.Status = release.TestRun_FAILURE
if streamErr := streamFailed(ts.Name, stream); streamErr != nil {
return nil, err
test.result.Status = release.TestRun_FAILURE
if streamErr := streamFailed(test.result.Name, env.Stream); streamErr != nil {
return err
}
}
results = append(results, ts)
log.Printf("Test %s completed", ts.Name)
} //else if resourceCreated && resourceCleanExit && status == api.PodUnkown {
//TODO: recordTests() - add test results to configmap with standardized name
_ = append(t.Results, test.result)
}
suite.Results = results
//TODO: delete flag
log.Printf("Finished running test suite for %s", rel.Name)
return suite, nil
t.CompletedAt = timeconv.Now()
return nil
}
func filterTests(hooks []*release.Hook, releaseName string) ([]*release.Hook, error) {
func filterTestHooks(hooks []*release.Hook, releaseName string) ([]*release.Hook, error) {
testHooks := []*release.Hook{}
notFoundErr := fmt.Errorf("no tests found for release %s", releaseName)
if len(hooks) == 0 {
return nil, notFoundErr
}
code, ok := events[releaseTest]
if !ok {
return nil, fmt.Errorf("unknown hook %q", releaseTest)
}
found := false
for _, h := range hooks {
for _, e := range h.Events {
if e == code {
found = true
if e == release.Hook_RELEASE_TEST {
testHooks = append(testHooks, h)
continue
}
}
}
//TODO: probably don't need to check found
if !found && len(testHooks) == 0 {
if len(testHooks) == 0 {
return nil, notFoundErr
}
return testHooks, nil
}
func prepareTests(hooks []*release.Hook, releaseName string) ([]string, error) {
testHooks, err := filterTests(hooks, releaseName)
func prepareTestManifests(hooks []*release.Hook, releaseName string) ([]string, error) {
testHooks, err := filterTestHooks(hooks, releaseName)
if err != nil {
return nil, err
}
tests := []string{}
for _, h := range testHooks {
individualTests := splitManifests(h.Manifest)
individualTests := util.SplitManifests(h.Manifest)
for _, t := range individualTests {
tests = append(tests, t)
}
}
return tests, nil
}
func streamRunning(name string, stream services.ReleaseService_RunReleaseTestServer) error {
msg := "RUNNING: " + name
if err := streamMessage(msg, stream); err != nil {
return err
}
return nil
}
func streamFailed(name string, stream services.ReleaseService_RunReleaseTestServer) error {
msg := fmt.Sprintf("FAILED: %s, run `kubectl logs %s` for more info", name, name)
if err := streamMessage(msg, stream); err != nil {
return err
}
return nil
}
func streamSuccess(name string, stream services.ReleaseService_RunReleaseTestServer) error {
msg := fmt.Sprintf("PASSED: %s", name)
if err := streamMessage(msg, stream); err != nil {
return err
}
return nil
}
func streamMessage(msg string, stream services.ReleaseService_RunReleaseTestServer) error {
resp := &services.TestReleaseResponse{Msg: msg}
// TODO: handle err better
if err := stream.Send(resp); err != nil {
return err
}
return nil
}
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package releaseutil
import (
"fmt"
"strings"
)
type SimpleHead struct {
Version string `json:"apiVersion"`
Kind string `json:"kind,omitempty"`
Metadata *struct {
Name string `json:"name"`
Annotations map[string]string `json:"annotations"`
} `json:"metadata,omitempty"`
}
func SplitManifests(bigfile string) map[string]string {
// This is not the best way of doing things, but it's how k8s itself does it.
// Basically, we're quickly splitting a stream of YAML documents into an
// array of YAML docs. In the current implementation, the file name is just
// a place holder, and doesn't have any further meaning.
sep := "\n---\n"
tpl := "manifest-%d"
res := map[string]string{}
tmp := strings.Split(bigfile, sep)
for i, d := range tmp {
res[fmt.Sprintf(tpl, i)] = d
}
return res
}
......@@ -26,6 +26,7 @@ import (
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/release"
util "k8s.io/helm/pkg/releaseutil"
)
// hookAnno is the label name for a hook
......@@ -55,20 +56,11 @@ var events = map[string]release.Hook_Event{
releaseTest: release.Hook_RELEASE_TEST,
}
type simpleHead struct {
Version string `json:"apiVersion"`
Kind string `json:"kind,omitempty"`
Metadata *struct {
Name string `json:"name"`
Annotations map[string]string `json:"annotations"`
} `json:"metadata,omitempty"`
}
// manifest represents a manifest file, which has a name and some content.
type manifest struct {
name string
content string
head *simpleHead
head *util.SimpleHead
}
// sortManifests takes a map of filename/YAML contents and sorts them into hook types.
......@@ -108,7 +100,7 @@ func sortManifests(files map[string]string, apis chartutil.VersionSet, sort Sort
continue
}
var sh simpleHead
var sh util.SimpleHead
err := yaml.Unmarshal([]byte(c), &sh)
if err != nil {
......
......@@ -22,6 +22,7 @@ import (
"github.com/ghodss/yaml"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/hooks"
"k8s.io/helm/pkg/proto/hapi/release"
)
......@@ -162,7 +163,7 @@ metadata:
// Verify the sort order
sorted := make([]manifest, len(data))
for i, s := range data {
var sh simpleHead
var sh util.SimpleHead
err := yaml.Unmarshal([]byte(s.manifest), &sh)
if err != nil {
// This is expected for manifests that are corrupt or empty.
......
......@@ -18,6 +18,8 @@ package tiller
import (
"testing"
"k8s.io/helm/pkg/hooks"
)
func TestKindSorter(t *testing.T) {
......@@ -25,27 +27,27 @@ func TestKindSorter(t *testing.T) {
{
name: "m",
content: "",
head: &simpleHead{Kind: "Deployment"},
head: &util.SimpleHead{Kind: "Deployment"},
},
{
name: "l",
content: "",
head: &simpleHead{Kind: "Service"},
head: &util.SimpleHead{Kind: "Service"},
},
{
name: "!",
content: "",
head: &simpleHead{Kind: "HonkyTonkSet"},
head: &util.SimpleHead{Kind: "HonkyTonkSet"},
},
{
name: "h",
content: "",
head: &simpleHead{Kind: "Namespace"},
head: &util.SimpleHead{Kind: "Namespace"},
},
{
name: "e",
content: "",
head: &simpleHead{Kind: "ConfigMap"},
head: &util.SimpleHead{Kind: "ConfigMap"},
},
}
......
......@@ -36,6 +36,7 @@ import (
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/proto/hapi/services"
reltesting "k8s.io/helm/pkg/releasetesting"
relutil "k8s.io/helm/pkg/releaseutil"
"k8s.io/helm/pkg/storage/driver"
"k8s.io/helm/pkg/tiller/environment"
......@@ -986,7 +987,7 @@ func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
log.Printf("uninstall: Failed to store updated release: %s", err)
}
manifests := splitManifests(rel.Manifest)
manifests := relutil.SplitManifests(rel.Manifest)
_, files, err := sortManifests(manifests, vs, UninstallOrder)
if err != nil {
// We could instead just delete everything in no particular order.
......@@ -1044,21 +1045,6 @@ func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
return res, errs
}
func splitManifests(bigfile string) map[string]string {
// This is not the best way of doing things, but it's how k8s itself does it.
// Basically, we're quickly splitting a stream of YAML documents into an
// array of YAML docs. In the current implementation, the file name is just
// a place holder, and doesn't have any further meaning.
sep := "\n---\n"
tpl := "manifest-%d"
res := map[string]string{}
tmp := strings.Split(bigfile, sep)
for i, d := range tmp {
res[fmt.Sprintf(tpl, i)] = d
}
return res
}
func validateManifest(c environment.KubeClient, ns string, manifest []byte) error {
r := bytes.NewReader(manifest)
_, err := c.Build(ns, r)
......@@ -1078,15 +1064,23 @@ func (s *ReleaseServer) RunReleaseTest(req *services.TestReleaseRequest, stream
return err
}
tests, err := prepareTests(rel.Hooks, rel.Name)
kubeCli := s.env.KubeClient
testEnv := &reltesting.Environment{
Namespace: rel.Namespace,
KubeClient: s.env.KubeClient,
Timeout: req.Timeout,
Stream: stream,
}
testSuite, err := runReleaseTests(tests, rel, kubeCli, stream, req.Timeout)
if err != nil {
tSuite, err := reltesting.NewTestSuite(rel, testEnv)
if err := tSuite.Run(testEnv); err != nil {
return err
}
rel.TestSuite = testSuite
rel.LastTestSuiteRun = &release.TestSuite{
StartedAt: tSuite.StartedAt,
CompletedAt: tSuite.CompletedAt,
Results: tSuite.Results,
}
return nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment