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