Commit 58c05f87 authored by Michelle Noorali's avatar Michelle Noorali

feat(*): stream helm test messages to client

parent d46d63a8
......@@ -21,7 +21,7 @@ import "google/protobuf/timestamp.proto";
option go_package = "release";
message TestResult {
message TestRun {
enum Status {
UNKNOWN = 0;
SUCCESS = 1;
......@@ -31,5 +31,6 @@ message TestResult {
string name = 1;
Status status = 2;
string info = 3;
google.protobuf.Timestamp last_run = 4;
google.protobuf.Timestamp started_at = 4;
google.protobuf.Timestamp completed_at = 5;
}
......@@ -17,15 +17,18 @@ syntax = "proto3";
package hapi.release;
import "google/protobuf/timestamp.proto";
import "hapi/release/test_result.proto";
import "hapi/release/test_run.proto";
option go_package = "release";
// TestSuite comprises of the last run of the pre-defined test suite of a release version
message TestSuite {
// LastRun indicates the date/time this test was last run.
google.protobuf.Timestamp last_run = 1;
// StartedAt indicates the date/time this test suite was kicked off
google.protobuf.Timestamp started_at = 1;
// CompletedAt indicates the date/time this test suite was completed
google.protobuf.Timestamp completed_at = 2;
// Results are the results of each segment of the test
repeated hapi.release.TestResult results = 2;
repeated hapi.release.TestRun results = 3;
}
......@@ -22,7 +22,6 @@ import "hapi/release/release.proto";
import "hapi/release/info.proto";
import "hapi/release/status.proto";
import "hapi/version/version.proto";
import "hapi/release/test_suite.proto";
option go_package = "services";
......@@ -82,7 +81,7 @@ service ReleaseService {
//TODO: move this to a test release service or rename to RunReleaseTest
// TestRelease runs the tests for a given release
rpc RunReleaseTest(TestReleaseRequest) returns (TestReleaseResponse) {
rpc RunReleaseTest(TestReleaseRequest) returns (stream TestReleaseResponse) {
}
}
......@@ -322,5 +321,5 @@ message TestReleaseRequest {
// TestReleaseResponse
message TestReleaseResponse {
// TODO: change to repeated hapi.release.Release.Test results = 1; (for stream)
hapi.release.TestSuite result = 1;
string msg = 1;
}
......@@ -20,11 +20,9 @@ import (
"fmt"
"io"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
"k8s.io/helm/pkg/helm"
//"k8s.io/helm/pkg/proto/hapi/release"
)
const releaseTestDesc = `
......@@ -69,20 +67,20 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
return cmd
}
func (t *releaseTestCmd) run() error {
res, err := t.client.ReleaseTest(t.name, helm.ReleaseTestTimeout(t.timeout))
if err != nil {
func (t *releaseTestCmd) run() (err error) {
c, errc := t.client.RunReleaseTest(t.name, helm.ReleaseTestTimeout(t.timeout))
for {
select {
case err := <-errc:
return prettyError(err)
case res, ok := <-c:
if !ok {
break
}
fmt.Fprintf(t.out, res.Msg+"\n")
}
table := uitable.New()
table.MaxColWidth = 50
table.AddRow("NAME", "Result", "Info")
//TODO: change Result to Suite
for _, r := range res.Result.Results {
table.AddRow(r.Name, r.Status, r.Info)
}
fmt.Fprintln(t.out, table.String()) //TODO: or no tests found
return nil
}
......@@ -17,6 +17,8 @@ limitations under the License.
package helm // import "k8s.io/helm/pkg/helm"
import (
"io"
"golang.org/x/net/context"
"google.golang.org/grpc"
......@@ -244,8 +246,8 @@ func (h *Client) ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.Get
return h.history(ctx, req)
}
// ReleaseTest executes a pre-defined test on a release
func (h *Client) ReleaseTest(rlsName string, opts ...ReleaseTestOption) (*rls.TestReleaseResponse, error) {
//ReleaseTest executes a pre-defined test on a release
func (h *Client) RunReleaseTest(rlsName string, opts ...ReleaseTestOption) (<-chan *rls.TestReleaseResponse, <-chan error) {
for _, opt := range opts {
opt(&h.opts)
}
......@@ -371,13 +373,39 @@ func (h *Client) history(ctx context.Context, req *rls.GetHistoryRequest) (*rls.
}
// Executes tiller.TestRelease RPC.
func (h *Client) test(ctx context.Context, req *rls.TestReleaseRequest) (*rls.TestReleaseResponse, error) {
func (h *Client) test(ctx context.Context, req *rls.TestReleaseRequest) (<-chan *rls.TestReleaseResponse, <-chan error) {
errc := make(chan error, 1)
c, err := grpc.Dial(h.opts.host, grpc.WithInsecure())
if err != nil {
return nil, err
errc <- err
return nil, errc
}
ch := make(chan *rls.TestReleaseResponse, 1)
go func() {
defer close(errc)
defer close(ch)
defer c.Close()
rlc := rls.NewReleaseServiceClient(c)
return rlc.RunReleaseTest(ctx, req)
s, err := rlc.RunReleaseTest(ctx, req)
if err != nil {
errc <- err
return
}
for {
msg, err := s.Recv()
if err == io.EOF {
return
}
if err != nil {
errc <- err
return
}
ch <- msg
}
}()
return ch, errc
}
......@@ -34,5 +34,5 @@ type Interface interface {
ReleaseContent(rlsName string, opts ...ContentOption) (*rls.GetReleaseContentResponse, error)
ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.GetHistoryResponse, error)
GetVersion(opts ...VersionOption) (*rls.GetVersionResponse, error)
ReleaseTest(rlsName string, opts ...ReleaseTestOption) (*rls.TestReleaseResponse, error)
RunReleaseTest(rlsName string, opts ...ReleaseTestOption) (<-chan *rls.TestReleaseResponse, <-chan error)
}
......@@ -10,7 +10,7 @@ It is generated from these files:
hapi/release/info.proto
hapi/release/release.proto
hapi/release/status.proto
hapi/release/test_result.proto
hapi/release/test_run.proto
hapi/release/test_suite.proto
It has these top-level messages:
......@@ -18,7 +18,7 @@ It has these top-level messages:
Info
Release
Status
TestResult
TestRun
TestSuite
*/
package release
......
// Code generated by protoc-gen-go.
// source: hapi/release/test_result.proto
// DO NOT EDIT!
package release
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import google_protobuf "github.com/golang/protobuf/ptypes/timestamp"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type TestResult_Status int32
const (
TestResult_UNKNOWN TestResult_Status = 0
TestResult_SUCCESS TestResult_Status = 1
TestResult_FAILURE TestResult_Status = 2
)
var TestResult_Status_name = map[int32]string{
0: "UNKNOWN",
1: "SUCCESS",
2: "FAILURE",
}
var TestResult_Status_value = map[string]int32{
"UNKNOWN": 0,
"SUCCESS": 1,
"FAILURE": 2,
}
func (x TestResult_Status) String() string {
return proto.EnumName(TestResult_Status_name, int32(x))
}
func (TestResult_Status) EnumDescriptor() ([]byte, []int) { return fileDescriptor4, []int{0, 0} }
type TestResult struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
Status TestResult_Status `protobuf:"varint,2,opt,name=status,enum=hapi.release.TestResult_Status" json:"status,omitempty"`
Info string `protobuf:"bytes,3,opt,name=info" json:"info,omitempty"`
LastRun *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=last_run,json=lastRun" json:"last_run,omitempty"`
}
func (m *TestResult) Reset() { *m = TestResult{} }
func (m *TestResult) String() string { return proto.CompactTextString(m) }
func (*TestResult) ProtoMessage() {}
func (*TestResult) Descriptor() ([]byte, []int) { return fileDescriptor4, []int{0} }
func (m *TestResult) GetLastRun() *google_protobuf.Timestamp {
if m != nil {
return m.LastRun
}
return nil
}
func init() {
proto.RegisterType((*TestResult)(nil), "hapi.release.TestResult")
proto.RegisterEnum("hapi.release.TestResult_Status", TestResult_Status_name, TestResult_Status_value)
}
func init() { proto.RegisterFile("hapi/release/test_result.proto", fileDescriptor4) }
var fileDescriptor4 = []byte{
// 244 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x8e, 0x41, 0x4b, 0xc3, 0x30,
0x18, 0x86, 0xcd, 0x1c, 0xad, 0xcb, 0x44, 0x4a, 0x4e, 0x65, 0x07, 0x57, 0x76, 0xea, 0x29, 0x81,
0x89, 0x78, 0xd6, 0x31, 0x41, 0x94, 0x0a, 0xe9, 0x8a, 0xe0, 0x45, 0x32, 0xf8, 0x36, 0x0b, 0x6d,
0x53, 0x9a, 0x2f, 0x3f, 0xd5, 0xff, 0x23, 0x49, 0x5a, 0xf4, 0xf6, 0xbd, 0xbc, 0x6f, 0x9e, 0x3c,
0xf4, 0xf6, 0x5b, 0xf5, 0xb5, 0x18, 0xa0, 0x01, 0x65, 0x40, 0x20, 0x18, 0xfc, 0x1a, 0xc0, 0xd8,
0x06, 0x79, 0x3f, 0x68, 0xd4, 0xec, 0xda, 0xf5, 0x7c, 0xec, 0x57, 0xeb, 0xb3, 0xd6, 0xe7, 0x06,
0x84, 0xef, 0x8e, 0xf6, 0x24, 0xb0, 0x6e, 0xc1, 0xa0, 0x6a, 0xfb, 0x30, 0xdf, 0xfc, 0x10, 0x4a,
0x0f, 0x60, 0x50, 0x7a, 0x06, 0x63, 0x74, 0xde, 0xa9, 0x16, 0x52, 0x92, 0x91, 0x7c, 0x21, 0xfd,
0xcd, 0x1e, 0x68, 0x64, 0x50, 0xa1, 0x35, 0xe9, 0x2c, 0x23, 0xf9, 0xcd, 0x76, 0xcd, 0xff, 0x7f,
0xc1, 0xff, 0x5e, 0xf3, 0xd2, 0xcf, 0xe4, 0x38, 0x77, 0xb0, 0xba, 0x3b, 0xe9, 0xf4, 0x32, 0xc0,
0xdc, 0xcd, 0xee, 0xe9, 0x55, 0xa3, 0x9c, 0xb3, 0xed, 0xd2, 0x79, 0x46, 0xf2, 0xe5, 0x76, 0xc5,
0x83, 0x23, 0x9f, 0x1c, 0xf9, 0x61, 0x72, 0x94, 0xb1, 0xdb, 0x4a, 0xdb, 0x6d, 0x04, 0x8d, 0x02,
0x9c, 0x2d, 0x69, 0x5c, 0x15, 0xaf, 0xc5, 0xfb, 0x47, 0x91, 0x5c, 0xb8, 0x50, 0x56, 0xbb, 0xdd,
0xbe, 0x2c, 0x13, 0xe2, 0xc2, 0xf3, 0xe3, 0xcb, 0x5b, 0x25, 0xf7, 0xc9, 0xec, 0x69, 0xf1, 0x19,
0x8f, 0x82, 0xc7, 0xc8, 0x83, 0xef, 0x7e, 0x03, 0x00, 0x00, 0xff, 0xff, 0x4c, 0x44, 0x22, 0xbb,
0x3a, 0x01, 0x00, 0x00,
}
// Code generated by protoc-gen-go.
// source: hapi/release/test_run.proto
// DO NOT EDIT!
package release
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import google_protobuf "github.com/golang/protobuf/ptypes/timestamp"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type TestRun_Status int32
const (
TestRun_UNKNOWN TestRun_Status = 0
TestRun_SUCCESS TestRun_Status = 1
TestRun_FAILURE TestRun_Status = 2
)
var TestRun_Status_name = map[int32]string{
0: "UNKNOWN",
1: "SUCCESS",
2: "FAILURE",
}
var TestRun_Status_value = map[string]int32{
"UNKNOWN": 0,
"SUCCESS": 1,
"FAILURE": 2,
}
func (x TestRun_Status) String() string {
return proto.EnumName(TestRun_Status_name, int32(x))
}
func (TestRun_Status) EnumDescriptor() ([]byte, []int) { return fileDescriptor4, []int{0, 0} }
type TestRun struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
Status TestRun_Status `protobuf:"varint,2,opt,name=status,enum=hapi.release.TestRun_Status" json:"status,omitempty"`
Info string `protobuf:"bytes,3,opt,name=info" json:"info,omitempty"`
StartedAt *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=started_at,json=startedAt" json:"started_at,omitempty"`
CompletedAt *google_protobuf.Timestamp `protobuf:"bytes,5,opt,name=completed_at,json=completedAt" json:"completed_at,omitempty"`
}
func (m *TestRun) Reset() { *m = TestRun{} }
func (m *TestRun) String() string { return proto.CompactTextString(m) }
func (*TestRun) ProtoMessage() {}
func (*TestRun) Descriptor() ([]byte, []int) { return fileDescriptor4, []int{0} }
func (m *TestRun) GetStartedAt() *google_protobuf.Timestamp {
if m != nil {
return m.StartedAt
}
return nil
}
func (m *TestRun) GetCompletedAt() *google_protobuf.Timestamp {
if m != nil {
return m.CompletedAt
}
return nil
}
func init() {
proto.RegisterType((*TestRun)(nil), "hapi.release.TestRun")
proto.RegisterEnum("hapi.release.TestRun_Status", TestRun_Status_name, TestRun_Status_value)
}
func init() { proto.RegisterFile("hapi/release/test_run.proto", fileDescriptor4) }
var fileDescriptor4 = []byte{
// 265 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x8f, 0x41, 0x4b, 0xfb, 0x40,
0x14, 0xc4, 0xff, 0xc9, 0xbf, 0x26, 0x64, 0x53, 0x24, 0xec, 0x29, 0x54, 0xc1, 0xd0, 0x53, 0x4e,
0xbb, 0x50, 0xbd, 0x78, 0xf0, 0x10, 0x4b, 0x05, 0x51, 0x22, 0x6c, 0x1a, 0x04, 0x2f, 0x65, 0xab,
0xaf, 0x35, 0x90, 0x64, 0x43, 0xf6, 0xe5, 0x8b, 0xf8, 0x89, 0x65, 0x93, 0xad, 0x78, 0xf3, 0xf6,
0x86, 0xf9, 0xcd, 0x30, 0x8f, 0x5c, 0x7c, 0xca, 0xae, 0xe2, 0x3d, 0xd4, 0x20, 0x35, 0x70, 0x04,
0x8d, 0xbb, 0x7e, 0x68, 0x59, 0xd7, 0x2b, 0x54, 0x74, 0x6e, 0x4c, 0x66, 0xcd, 0xc5, 0xd5, 0x51,
0xa9, 0x63, 0x0d, 0x7c, 0xf4, 0xf6, 0xc3, 0x81, 0x63, 0xd5, 0x80, 0x46, 0xd9, 0x74, 0x13, 0xbe,
0xfc, 0x72, 0x89, 0xbf, 0x05, 0x8d, 0x62, 0x68, 0x29, 0x25, 0xb3, 0x56, 0x36, 0x10, 0x3b, 0x89,
0x93, 0x06, 0x62, 0xbc, 0xe9, 0x0d, 0xf1, 0x34, 0x4a, 0x1c, 0x74, 0xec, 0x26, 0x4e, 0x7a, 0xbe,
0xba, 0x64, 0xbf, 0xfb, 0x99, 0x8d, 0xb2, 0x62, 0x64, 0x84, 0x65, 0x4d, 0x53, 0xd5, 0x1e, 0x54,
0xfc, 0x7f, 0x6a, 0x32, 0x37, 0xbd, 0x25, 0x44, 0xa3, 0xec, 0x11, 0x3e, 0x76, 0x12, 0xe3, 0x59,
0xe2, 0xa4, 0xe1, 0x6a, 0xc1, 0xa6, 0x7d, 0xec, 0xb4, 0x8f, 0x6d, 0x4f, 0xfb, 0x44, 0x60, 0xe9,
0x0c, 0xe9, 0x1d, 0x99, 0xbf, 0xab, 0xa6, 0xab, 0xc1, 0x86, 0xcf, 0xfe, 0x0c, 0x87, 0x3f, 0x7c,
0x86, 0x4b, 0x4e, 0xbc, 0x69, 0x1f, 0x0d, 0x89, 0x5f, 0xe6, 0x4f, 0xf9, 0xcb, 0x6b, 0x1e, 0xfd,
0x33, 0xa2, 0x28, 0xd7, 0xeb, 0x4d, 0x51, 0x44, 0x8e, 0x11, 0x0f, 0xd9, 0xe3, 0x73, 0x29, 0x36,
0x91, 0x7b, 0x1f, 0xbc, 0xf9, 0xf6, 0xc1, 0xbd, 0x37, 0x96, 0x5f, 0x7f, 0x07, 0x00, 0x00, 0xff,
0xff, 0x8d, 0xb9, 0xce, 0x57, 0x74, 0x01, 0x00, 0x00,
}
......@@ -16,10 +16,12 @@ var _ = math.Inf
// TestSuite comprises of the last run of the pre-defined test suite of a release version
type TestSuite struct {
// LastRun indicates the date/time this test was last run.
LastRun *google_protobuf.Timestamp `protobuf:"bytes,1,opt,name=last_run,json=lastRun" json:"last_run,omitempty"`
// StartedAt indicates the date/time this test suite was kicked off
StartedAt *google_protobuf.Timestamp `protobuf:"bytes,1,opt,name=started_at,json=startedAt" json:"started_at,omitempty"`
// CompletedAt indicates the date/time this test suite was completed
CompletedAt *google_protobuf.Timestamp `protobuf:"bytes,2,opt,name=completed_at,json=completedAt" json:"completed_at,omitempty"`
// Results are the results of each segment of the test
Results []*TestResult `protobuf:"bytes,2,rep,name=results" json:"results,omitempty"`
Results []*TestRun `protobuf:"bytes,3,rep,name=results" json:"results,omitempty"`
}
func (m *TestSuite) Reset() { *m = TestSuite{} }
......@@ -27,14 +29,21 @@ func (m *TestSuite) String() string { return proto.CompactTextString(
func (*TestSuite) ProtoMessage() {}
func (*TestSuite) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{0} }
func (m *TestSuite) GetLastRun() *google_protobuf.Timestamp {
func (m *TestSuite) GetStartedAt() *google_protobuf.Timestamp {
if m != nil {
return m.LastRun
return m.StartedAt
}
return nil
}
func (m *TestSuite) GetResults() []*TestResult {
func (m *TestSuite) GetCompletedAt() *google_protobuf.Timestamp {
if m != nil {
return m.CompletedAt
}
return nil
}
func (m *TestSuite) GetResults() []*TestRun {
if m != nil {
return m.Results
}
......@@ -48,17 +57,18 @@ func init() {
func init() { proto.RegisterFile("hapi/release/test_suite.proto", fileDescriptor5) }
var fileDescriptor5 = []byte{
// 183 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x8e, 0xc1, 0x8a, 0x83, 0x30,
0x14, 0x45, 0x71, 0x06, 0xc6, 0x31, 0xce, 0xca, 0x95, 0x08, 0xd3, 0x4a, 0x57, 0xae, 0x5e, 0xc0,
0xd2, 0x1f, 0xe8, 0x27, 0xa4, 0xae, 0xba, 0x29, 0x11, 0x5e, 0xad, 0x10, 0x8d, 0xf8, 0x5e, 0xfa,
0xfd, 0x25, 0x46, 0xa1, 0xd0, 0xf5, 0x39, 0xdc, 0x73, 0xc5, 0xff, 0x43, 0x4f, 0xbd, 0x9c, 0xd1,
0xa0, 0x26, 0x94, 0x8c, 0xc4, 0x37, 0x72, 0x3d, 0x23, 0x4c, 0xb3, 0x65, 0x9b, 0xfd, 0x79, 0x0c,
0x2b, 0x2e, 0xf6, 0x9d, 0xb5, 0x9d, 0x41, 0xb9, 0xb0, 0xd6, 0xdd, 0x25, 0xf7, 0x03, 0x12, 0xeb,
0x61, 0x0a, 0x7a, 0xb1, 0xfb, 0x5c, 0x9b, 0x91, 0x9c, 0xe1, 0xc0, 0x0f, 0x4f, 0x91, 0x34, 0x48,
0x7c, 0xf1, 0x85, 0xec, 0x24, 0x7e, 0x8d, 0xf6, 0x86, 0x1b, 0xf3, 0xa8, 0x8c, 0xaa, 0xb4, 0x2e,
0x20, 0x04, 0x60, 0x0b, 0x40, 0xb3, 0x05, 0x54, 0xec, 0x5d, 0xe5, 0xc6, 0xac, 0x16, 0x71, 0xd8,
0xa4, 0xfc, 0xab, 0xfc, 0xae, 0xd2, 0x3a, 0x87, 0xf7, 0x93, 0xe0, 0x03, 0x6a, 0x11, 0xd4, 0x26,
0x9e, 0x93, 0x6b, 0xbc, 0xe2, 0xf6, 0x67, 0xd9, 0x3e, 0xbe, 0x02, 0x00, 0x00, 0xff, 0xff, 0x05,
0x00, 0xf5, 0xbb, 0xf9, 0x00, 0x00, 0x00,
// 207 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x8f, 0xc1, 0x4a, 0x86, 0x40,
0x14, 0x85, 0x31, 0x21, 0x71, 0x74, 0x35, 0x10, 0x88, 0x11, 0x49, 0x2b, 0x57, 0x33, 0x60, 0xab,
0x16, 0x2d, 0xec, 0x11, 0xcc, 0x55, 0x1b, 0x19, 0xeb, 0x66, 0xc2, 0xe8, 0x0c, 0x73, 0xef, 0xbc,
0x5a, 0xcf, 0x17, 0xea, 0x18, 0x41, 0x8b, 0x7f, 0xfd, 0x7d, 0xe7, 0x9c, 0x7b, 0xd9, 0xdd, 0x97,
0xb2, 0xb3, 0x74, 0xa0, 0x41, 0x21, 0x48, 0x02, 0xa4, 0x01, 0xfd, 0x4c, 0x20, 0xac, 0x33, 0x64,
0x78, 0xbe, 0x61, 0x11, 0x70, 0x79, 0x3f, 0x19, 0x33, 0x69, 0x90, 0x3b, 0x1b, 0xfd, 0xa7, 0xa4,
0x79, 0x01, 0x24, 0xb5, 0xd8, 0x43, 0x2f, 0x6f, 0xff, 0xb7, 0x39, 0xbf, 0x1e, 0xf0, 0xe1, 0x3b,
0x62, 0x69, 0x0f, 0x48, 0xaf, 0x5b, 0x3f, 0x7f, 0x62, 0x0c, 0x49, 0x39, 0x82, 0x8f, 0x41, 0x51,
0x11, 0x55, 0x51, 0x9d, 0x35, 0xa5, 0x38, 0x06, 0xc4, 0x39, 0x20, 0xfa, 0x73, 0xa0, 0x4b, 0x83,
0xdd, 0x12, 0x7f, 0x66, 0xf9, 0xbb, 0x59, 0xac, 0x86, 0x10, 0xbe, 0xba, 0x18, 0xce, 0x7e, 0xfd,
0x96, 0xb8, 0x64, 0x89, 0x03, 0xf4, 0x9a, 0xb0, 0x88, 0xab, 0xb8, 0xce, 0x9a, 0x1b, 0xf1, 0xf7,
0x4b, 0xb1, 0xdd, 0xd8, 0xf9, 0xb5, 0x3b, 0xad, 0x97, 0xf4, 0x2d, 0x09, 0x6c, 0xbc, 0xde, 0xcb,
0x1f, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x8c, 0x59, 0x65, 0x4f, 0x37, 0x01, 0x00, 0x00,
}
......@@ -42,7 +42,6 @@ import hapi_release5 "k8s.io/helm/pkg/proto/hapi/release"
import hapi_release2 "k8s.io/helm/pkg/proto/hapi/release"
import hapi_release1 "k8s.io/helm/pkg/proto/hapi/release"
import hapi_version "k8s.io/helm/pkg/proto/hapi/version"
import hapi_release4 "k8s.io/helm/pkg/proto/hapi/release"
import (
context "golang.org/x/net/context"
......@@ -507,7 +506,7 @@ func (*TestReleaseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0
// TestReleaseResponse
type TestReleaseResponse struct {
// TODO: change to repeated hapi.release.Release.Test results = 1; (for stream)
Result *hapi_release4.TestSuite `protobuf:"bytes,1,opt,name=result" json:"result,omitempty"`
Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"`
}
func (m *TestReleaseResponse) Reset() { *m = TestReleaseResponse{} }
......@@ -515,13 +514,6 @@ func (m *TestReleaseResponse) String() string { return proto.CompactT
func (*TestReleaseResponse) ProtoMessage() {}
func (*TestReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} }
func (m *TestReleaseResponse) GetResult() *hapi_release4.TestSuite {
if m != nil {
return m.Result
}
return nil
}
func init() {
proto.RegisterType((*ListReleasesRequest)(nil), "hapi.services.tiller.ListReleasesRequest")
proto.RegisterType((*ListSort)(nil), "hapi.services.tiller.ListSort")
......@@ -582,7 +574,7 @@ type ReleaseServiceClient interface {
GetHistory(ctx context.Context, in *GetHistoryRequest, opts ...grpc.CallOption) (*GetHistoryResponse, error)
// TODO: move this to a test release service or rename to RunReleaseTest
// TestRelease runs the tests for a given release
RunReleaseTest(ctx context.Context, in *TestReleaseRequest, opts ...grpc.CallOption) (*TestReleaseResponse, error)
RunReleaseTest(ctx context.Context, in *TestReleaseRequest, opts ...grpc.CallOption) (ReleaseService_RunReleaseTestClient, error)
}
type releaseServiceClient struct {
......@@ -697,13 +689,36 @@ func (c *releaseServiceClient) GetHistory(ctx context.Context, in *GetHistoryReq
return out, nil
}
func (c *releaseServiceClient) RunReleaseTest(ctx context.Context, in *TestReleaseRequest, opts ...grpc.CallOption) (*TestReleaseResponse, error) {
out := new(TestReleaseResponse)
err := grpc.Invoke(ctx, "/hapi.services.tiller.ReleaseService/RunReleaseTest", in, out, c.cc, opts...)
func (c *releaseServiceClient) RunReleaseTest(ctx context.Context, in *TestReleaseRequest, opts ...grpc.CallOption) (ReleaseService_RunReleaseTestClient, error) {
stream, err := grpc.NewClientStream(ctx, &_ReleaseService_serviceDesc.Streams[1], c.cc, "/hapi.services.tiller.ReleaseService/RunReleaseTest", opts...)
if err != nil {
return nil, err
}
return out, nil
x := &releaseServiceRunReleaseTestClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type ReleaseService_RunReleaseTestClient interface {
Recv() (*TestReleaseResponse, error)
grpc.ClientStream
}
type releaseServiceRunReleaseTestClient struct {
grpc.ClientStream
}
func (x *releaseServiceRunReleaseTestClient) Recv() (*TestReleaseResponse, error) {
m := new(TestReleaseResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Server API for ReleaseService service
......@@ -732,7 +747,7 @@ type ReleaseServiceServer interface {
GetHistory(context.Context, *GetHistoryRequest) (*GetHistoryResponse, error)
// TODO: move this to a test release service or rename to RunReleaseTest
// TestRelease runs the tests for a given release
RunReleaseTest(context.Context, *TestReleaseRequest) (*TestReleaseResponse, error)
RunReleaseTest(*TestReleaseRequest, ReleaseService_RunReleaseTestServer) error
}
func RegisterReleaseServiceServer(s *grpc.Server, srv ReleaseServiceServer) {
......@@ -904,22 +919,25 @@ func _ReleaseService_GetHistory_Handler(srv interface{}, ctx context.Context, de
return interceptor(ctx, in, info, handler)
}
func _ReleaseService_RunReleaseTest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TestReleaseRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ReleaseServiceServer).RunReleaseTest(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hapi.services.tiller.ReleaseService/RunReleaseTest",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ReleaseServiceServer).RunReleaseTest(ctx, req.(*TestReleaseRequest))
func _ReleaseService_RunReleaseTest_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(TestReleaseRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return interceptor(ctx, in, info, handler)
return srv.(ReleaseServiceServer).RunReleaseTest(m, &releaseServiceRunReleaseTestServer{stream})
}
type ReleaseService_RunReleaseTestServer interface {
Send(*TestReleaseResponse) error
grpc.ServerStream
}
type releaseServiceRunReleaseTestServer struct {
grpc.ServerStream
}
func (x *releaseServiceRunReleaseTestServer) Send(m *TestReleaseResponse) error {
return x.ServerStream.SendMsg(m)
}
var _ReleaseService_serviceDesc = grpc.ServiceDesc{
......@@ -958,10 +976,6 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
MethodName: "GetHistory",
Handler: _ReleaseService_GetHistory_Handler,
},
{
MethodName: "RunReleaseTest",
Handler: _ReleaseService_RunReleaseTest_Handler,
},
},
Streams: []grpc.StreamDesc{
{
......@@ -969,6 +983,11 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
Handler: _ReleaseService_ListReleases_Handler,
ServerStreams: true,
},
{
StreamName: "RunReleaseTest",
Handler: _ReleaseService_RunReleaseTest_Handler,
ServerStreams: true,
},
},
Metadata: fileDescriptor0,
}
......
......@@ -137,6 +137,7 @@ type KubeClient interface {
Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error
Build(namespace string, reader io.Reader) (kube.Result, error)
//TODO: insert description
WaitAndGetCompletedPodStatus(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error)
}
......@@ -184,6 +185,10 @@ func (p *PrintingKubeClient) Build(ns string, reader io.Reader) (kube.Result, er
return []*resource.Info{}, nil
}
func (p *PrintingKubeClient) WaitAndGetCompletedPodStatus(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error) {
return "", nil
}
// Environment provides the context for executing a client request.
//
// All services in a context are concurrency safe.
......
......@@ -20,10 +20,12 @@ import (
"bytes"
"io"
"testing"
"time"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/kube"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
......@@ -56,6 +58,10 @@ func (k *mockKubeClient) Build(ns string, reader io.Reader) (kube.Result, error)
return []*resource.Info{}, nil
}
func (k *mockKubeClient) WaitAndGetCompletedPodStatus(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error) {
return "", nil
}
var _ Engine = &mockEngine{}
var _ KubeClient = &mockKubeClient{}
var _ KubeClient = &PrintingKubeClient{}
......
......@@ -1066,27 +1066,27 @@ func validateManifest(c environment.KubeClient, ns string, manifest []byte) erro
}
// RunTestRelease runs a pre-defined test on a given release
func (s *ReleaseServer) RunReleaseTest(c ctx.Context, req *services.TestReleaseRequest) (*services.TestReleaseResponse, error) {
func (s *ReleaseServer) RunReleaseTest(req *services.TestReleaseRequest, stream services.ReleaseService_RunReleaseTestServer) error {
res := &services.TestReleaseResponse{}
if !ValidName.MatchString(req.Name) {
return nil, errMissingRelease
return errMissingRelease
}
// finds the non-deleted release with the given name
r, err := s.env.Releases.Last(req.Name)
rel, err := s.env.Releases.Last(req.Name)
if err != nil {
return nil, err
return err
}
tests, err := prepareTests(rel.Hooks, rel.Name)
kubeCli := s.env.KubeClient
testSuite, err := runReleaseTestSuite(r.Hooks, kubeCli, r.Name, r.Namespace, req.Timeout)
testSuite, err := runReleaseTests(tests, rel, kubeCli, stream, req.Timeout)
if err != nil {
return nil, err
return err
}
r.TestSuite = testSuite
res.Result = testSuite
rel.TestSuite = testSuite
return res, nil
return nil
}
......@@ -26,43 +26,55 @@ import (
"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"
"k8s.io/helm/pkg/timeconv"
)
// change name to runReleaseTestSuite
func runReleaseTestSuite(hooks []*release.Hook, kube environment.KubeClient, name, namespace string, timeout int64) (*release.TestSuite, error) {
//TODO: testSuiteRunner.Run()
//struct testSuiteRunner {
//suite *release.TestSuite,
//tests []string,
//kube environemtn.KubeClient,
//timeout int64
////stream or output channel
//}
suite := &release.TestSuite{}
suite.LastRun = timeconv.Now()
results := []*release.TestResult{}
func runReleaseTests(tests []string, rel *release.Release, kube environment.KubeClient, stream services.ReleaseService_RunReleaseTestServer, timeout int64) (*release.TestSuite, error) {
results := []*release.TestRun{}
tests, err := prepareTests(hooks, name)
if err != nil {
return suite, err
}
//TODO: add results to test suite
suite := &release.TestSuite{}
suite.StartedAt = timeconv.Now()
for _, h := range tests {
var sh simpleHead
err := yaml.Unmarshal([]byte(h), &sh)
if err != nil {
//handle err better
return nil, err
}
ts := &release.TestResult{Name: sh.Metadata.Name}
// should this be lower? should we even be saving time to hook?
// TODO: should be start time really
ts.LastRun = timeconv.Now()
if sh.Kind != "Pod" {
return nil, fmt.Errorf("%s is not a pod", sh.Metadata.Name)
}
ts := &release.TestRun{Name: sh.Metadata.Name}
ts.StartedAt = timeconv.Now()
if err := streamRunning(ts.Name, stream); err != nil {
return nil, err
}
resourceCreated := true
b := bytes.NewBufferString(h)
if err := kube.Create(namespace, b); err != nil {
log.Printf("Could not create %s(%s): %v", ts.Name, sh.Kind, err)
ts.Info = err.Error()
//TODO: status option should be constant not random int
ts.Status = 2
if err := kube.Create(rel.Namespace, b); 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
}
}
status := api.PodUnknown
......@@ -70,31 +82,41 @@ func runReleaseTestSuite(hooks []*release.Hook, kube environment.KubeClient, nam
if resourceCreated {
b.Reset()
b.WriteString(h)
status, err = kube.WaitAndGetCompletedPodStatus(namespace, b, time.Duration(timeout)*time.Second)
status, err = kube.WaitAndGetCompletedPodStatus(rel.Namespace, b, time.Duration(timeout)*time.Second)
if err != nil {
log.Printf("Error getting status for %s(%s): %s", ts.Name, sh.Kind, err)
ts.Info = err.Error()
ts.Status = 0
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
}
}
}
// TODO: maybe better suited as a switch statement and include
// PodUnknown, PodFailed, PodRunning, and PodPending scenarios
if resourceCreated && resourceCleanExit && status == api.PodSucceeded {
ts.Status = 1
ts.Status = release.TestRun_SUCCESS
if streamErr := streamSuccess(ts.Name, stream); streamErr != nil {
return nil, streamErr
}
} else if resourceCreated && resourceCleanExit && status == api.PodFailed {
ts.Status = 2
ts.Status = release.TestRun_FAILURE
if streamErr := streamFailed(ts.Name, stream); streamErr != nil {
return nil, err
}
}
results = append(results, ts)
log.Printf("Test %s(%s) complete", ts.Name, sh.Kind)
log.Printf("Test %s completed", ts.Name)
//TODO: recordTests() - add test results to configmap with standardized name
}
suite.Results = results
log.Printf("Finished running test suite for %s", name)
//TODO: delete flag
log.Printf("Finished running test suite for %s", rel.Name)
return suite, nil
}
......@@ -145,3 +167,37 @@ func prepareTests(hooks []*release.Hook, releaseName string) ([]string, error) {
}
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
}
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