Commit d46d63a8 authored by Michelle Noorali's avatar Michelle Noorali

feat(*): add helm test command mvp

* This is a simple mvp which processes a test definition with the
hook annotation for test when you run `helm test [release]`
* helm client cmd, proto def, tiller logic
parent 8d75b0c0
...@@ -32,6 +32,7 @@ message Hook { ...@@ -32,6 +32,7 @@ message Hook {
POST_UPGRADE = 6; POST_UPGRADE = 6;
PRE_ROLLBACK = 7; PRE_ROLLBACK = 7;
POST_ROLLBACK = 8; POST_ROLLBACK = 8;
RELEASE_TEST = 9;
} }
string name = 1; string name = 1;
// Kind is the Kubernetes kind. // Kind is the Kubernetes kind.
......
...@@ -18,6 +18,7 @@ package hapi.release; ...@@ -18,6 +18,7 @@ package hapi.release;
import "hapi/release/hook.proto"; import "hapi/release/hook.proto";
import "hapi/release/info.proto"; import "hapi/release/info.proto";
import "hapi/release/test_suite.proto";
import "hapi/chart/config.proto"; import "hapi/chart/config.proto";
import "hapi/chart/chart.proto"; import "hapi/chart/chart.proto";
...@@ -50,4 +51,7 @@ message Release { ...@@ -50,4 +51,7 @@ 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
hapi.release.TestSuite test_suite = 9;
} }
// 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.
syntax = "proto3";
package hapi.release;
import "google/protobuf/timestamp.proto";
option go_package = "release";
message TestResult {
enum Status {
UNKNOWN = 0;
SUCCESS = 1;
FAILURE = 2;
}
string name = 1;
Status status = 2;
string info = 3;
google.protobuf.Timestamp last_run = 4;
}
// 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.
syntax = "proto3";
package hapi.release;
import "google/protobuf/timestamp.proto";
import "hapi/release/test_result.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;
// Results are the results of each segment of the test
repeated hapi.release.TestResult results = 2;
}
...@@ -22,6 +22,7 @@ import "hapi/release/release.proto"; ...@@ -22,6 +22,7 @@ import "hapi/release/release.proto";
import "hapi/release/info.proto"; import "hapi/release/info.proto";
import "hapi/release/status.proto"; import "hapi/release/status.proto";
import "hapi/version/version.proto"; import "hapi/version/version.proto";
import "hapi/release/test_suite.proto";
option go_package = "services"; option go_package = "services";
...@@ -78,6 +79,11 @@ service ReleaseService { ...@@ -78,6 +79,11 @@ service ReleaseService {
// ReleaseHistory retrieves a releasse's history. // ReleaseHistory retrieves a releasse's history.
rpc GetHistory(GetHistoryRequest) returns (GetHistoryResponse) { rpc GetHistory(GetHistoryRequest) returns (GetHistoryResponse) {
} }
//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) {
}
} }
// ListReleasesRequest requests a list of releases. // ListReleasesRequest requests a list of releases.
...@@ -304,3 +310,17 @@ message GetHistoryRequest { ...@@ -304,3 +310,17 @@ message GetHistoryRequest {
message GetHistoryResponse { message GetHistoryResponse {
repeated hapi.release.Release releases = 1; repeated hapi.release.Release releases = 1;
} }
// TestReleaseRequest is a request to get the status of a release.
message TestReleaseRequest {
// Name is the name of the release
string name = 1;
// timeout specifies the max amount of time any kubernetes client command can run.
int64 timeout = 2;
}
// TestReleaseResponse
message TestReleaseResponse {
// TODO: change to repeated hapi.release.Release.Test results = 1; (for stream)
hapi.release.TestSuite result = 1;
}
...@@ -122,6 +122,7 @@ func newRootCmd(out io.Writer) *cobra.Command { ...@@ -122,6 +122,7 @@ func newRootCmd(out io.Writer) *cobra.Command {
newInitCmd(out), newInitCmd(out),
newResetCmd(nil, out), newResetCmd(nil, out),
newVersionCmd(nil, out), newVersionCmd(nil, out),
newReleaseTestCmd(nil, out),
// Hidden documentation generator command: 'helm docs' // Hidden documentation generator command: 'helm docs'
newDocsCmd(out), newDocsCmd(out),
......
/*
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 main
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 = `
Th test command runs the tests for a release.
The argument this command takes is the name of a deployed release.
The tests to be run are defined in the chart that was installed.
`
type releaseTestCmd struct {
name string
out io.Writer
client helm.Interface
timeout int64
}
func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
rlsTest := &releaseTestCmd{
out: out,
client: c,
}
cmd := &cobra.Command{
Use: "test [RELEASE]",
Short: "test a release",
Long: releaseTestDesc,
PersistentPreRunE: setupConnection,
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkArgsLength(len(args), "release name"); err != nil {
return err
}
rlsTest.name = args[0]
rlsTest.client = ensureHelmClient(rlsTest.client)
return rlsTest.run()
},
}
f := cmd.Flags()
f.Int64Var(&rlsTest.timeout, "timeout", 300, "time in seconds to wait for any individual kubernetes operation (like Jobs for hooks)")
return cmd
}
func (t *releaseTestCmd) run() error {
res, err := t.client.ReleaseTest(t.name, helm.ReleaseTestTimeout(t.timeout))
if err != nil {
return prettyError(err)
}
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
}
apiVersion: v1
kind: Pod
metadata:
name: "{{.Release.Name}}-service-test"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: curl
image: radial/busyboxplus:curl
command: ['curl']
args: [ '{{ template "fullname" .}}:{{default 80 .Values.httpPort}}' ]
restartPolicy: Never
---
apiVersion: v1
kind: Pod
metadata:
name: "{{.Release.Name}}-service-failing-test"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: curl
image: radial/busyboxplus:curl
command: ['curl']
args: [ '{{ template "fullname" .}}-service:{{default 80 .Values.httpPort}}' ]
restartPolicy: Never
...@@ -244,6 +244,19 @@ func (h *Client) ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.Get ...@@ -244,6 +244,19 @@ func (h *Client) ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.Get
return h.history(ctx, req) 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) {
for _, opt := range opts {
opt(&h.opts)
}
req := &h.opts.testReq
req.Name = rlsName
ctx := NewContext()
return h.test(ctx, req)
}
// Executes tiller.ListReleases RPC. // Executes tiller.ListReleases RPC.
func (h *Client) list(ctx context.Context, req *rls.ListReleasesRequest) (*rls.ListReleasesResponse, error) { func (h *Client) list(ctx context.Context, req *rls.ListReleasesRequest) (*rls.ListReleasesResponse, error) {
c, err := grpc.Dial(h.opts.host, grpc.WithInsecure()) c, err := grpc.Dial(h.opts.host, grpc.WithInsecure())
...@@ -356,3 +369,15 @@ func (h *Client) history(ctx context.Context, req *rls.GetHistoryRequest) (*rls. ...@@ -356,3 +369,15 @@ func (h *Client) history(ctx context.Context, req *rls.GetHistoryRequest) (*rls.
rlc := rls.NewReleaseServiceClient(c) rlc := rls.NewReleaseServiceClient(c)
return rlc.GetHistory(ctx, req) return rlc.GetHistory(ctx, req)
} }
// Executes tiller.TestRelease RPC.
func (h *Client) test(ctx context.Context, req *rls.TestReleaseRequest) (*rls.TestReleaseResponse, error) {
c, err := grpc.Dial(h.opts.host, grpc.WithInsecure())
if err != nil {
return nil, err
}
defer c.Close()
rlc := rls.NewReleaseServiceClient(c)
return rlc.RunReleaseTest(ctx, req)
}
...@@ -34,4 +34,5 @@ type Interface interface { ...@@ -34,4 +34,5 @@ type Interface interface {
ReleaseContent(rlsName string, opts ...ContentOption) (*rls.GetReleaseContentResponse, error) ReleaseContent(rlsName string, opts ...ContentOption) (*rls.GetReleaseContentResponse, error)
ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.GetHistoryResponse, error) ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.GetHistoryResponse, error)
GetVersion(opts ...VersionOption) (*rls.GetVersionResponse, error) GetVersion(opts ...VersionOption) (*rls.GetVersionResponse, error)
ReleaseTest(rlsName string, opts ...ReleaseTestOption) (*rls.TestReleaseResponse, error)
} }
...@@ -66,6 +66,8 @@ type options struct { ...@@ -66,6 +66,8 @@ type options struct {
histReq rls.GetHistoryRequest histReq rls.GetHistoryRequest
// resetValues instructs Tiller to reset values to their defaults. // resetValues instructs Tiller to reset values to their defaults.
resetValues bool resetValues bool
// release test options are applied directly to the test release history request
testReq rls.TestReleaseRequest
} }
// Host specifies the host address of the Tiller release server, (default = ":44134"). // Host specifies the host address of the Tiller release server, (default = ":44134").
...@@ -174,6 +176,13 @@ func DeleteTimeout(timeout int64) DeleteOption { ...@@ -174,6 +176,13 @@ func DeleteTimeout(timeout int64) DeleteOption {
} }
} }
// TestTimeout specifies the number of seconds before kubernetes calls timeout
func ReleaseTestTimeout(timeout int64) ReleaseTestOption {
return func(opts *options) {
opts.testReq.Timeout = timeout
}
}
// RollbackTimeout specifies the number of seconds before kubernetes calls timeout // RollbackTimeout specifies the number of seconds before kubernetes calls timeout
func RollbackTimeout(timeout int64) RollbackOption { func RollbackTimeout(timeout int64) RollbackOption {
return func(opts *options) { return func(opts *options) {
...@@ -364,3 +373,7 @@ func NewContext() context.Context { ...@@ -364,3 +373,7 @@ func NewContext() context.Context {
md := metadata.Pairs("x-helm-api-client", version.Version) md := metadata.Pairs("x-helm-api-client", version.Version)
return metadata.NewContext(context.TODO(), md) return metadata.NewContext(context.TODO(), md)
} }
// ReleaseTestOption allows configuring optional request data for
// issuing a TestRelease rpc.
type ReleaseTestOption func(*options)
...@@ -33,6 +33,7 @@ import ( ...@@ -33,6 +33,7 @@ import (
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1" "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
conditions "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
...@@ -305,6 +306,10 @@ func perform(c *Client, namespace string, infos Result, fn ResourceActorFunc) er ...@@ -305,6 +306,10 @@ func perform(c *Client, namespace string, infos Result, fn ResourceActorFunc) er
if len(infos) == 0 { if len(infos) == 0 {
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
...@@ -610,3 +615,43 @@ func scrubValidationError(err error) error { ...@@ -610,3 +615,43 @@ func scrubValidationError(err error) error {
} }
return err return err
} }
func (c *Client) WaitAndGetCompletedPodStatus(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error) {
infos, err := c.Build(namespace, reader)
if err != nil {
return api.PodUnknown, err
}
info := infos[0]
// TODO: should we be checking kind before hand? probably yes.
// TODO: add validation to linter: any manifest with a test hook has to be a pod kind?
kind := info.Mapping.GroupVersionKind.Kind
if kind != "Pod" {
return api.PodUnknown, fmt.Errorf("%s is not a Pod", info.Name)
}
if err := watchPodUntilComplete(timeout, info); err != nil {
return api.PodUnknown, err
}
if err := info.Get(); err != nil {
return api.PodUnknown, err
}
status := info.Object.(*api.Pod).Status.Phase
return status, nil
}
func watchPodUntilComplete(timeout time.Duration, info *resource.Info) error {
w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion)
if err != nil {
return err
}
log.Printf("Watching pod %s for completion with timeout of %v", info.Name, timeout)
_, err = watch.Until(timeout, w, func(e watch.Event) (bool, error) {
return conditions.PodCompleted(e)
})
return err
}
...@@ -305,6 +305,51 @@ func TestPerform(t *testing.T) { ...@@ -305,6 +305,51 @@ func TestPerform(t *testing.T) {
} }
} }
func TestWaitAndGetCompletedPodStatus(t *testing.T) {
f, tf, codec, ns := cmdtesting.NewAPIFactory()
actions := make(map[string]string)
testPodList := newPodList("bestpod")
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
p, m := req.URL.Path, req.Method
actions[p] = m
count := 0
switch {
case p == "/namespaces/test/pods/bestpod" && m == "GET":
return newResponse(200, &testPodList.Items[0])
case p == "/watch/namespaces/test/pods/bestpod" && m == "GET":
//TODO: fix response
count = count + 1
if count == 1 {
//returns event running
return newResponse(200, &testPodList.Items[0])
}
if count == 2 {
//return event succeeded
return newResponse(200, &testPodList.Items[0])
}
default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil
}
}),
}
c := &Client{Factory: f}
//stub watchUntil to return no error
status, err := c.WaitAndGetCompletedPodStatus("test", objBody(codec, &testPodList), 30*time.Second)
if err != nil {
t.Fatal(err)
}
if status != api.PodSucceeded {
t.Fatal("Expected %s, got %s", api.PodSucceeded, status)
}
}
func TestReal(t *testing.T) { func TestReal(t *testing.T) {
t.Skip("This is a live test, comment this line to run") t.Skip("This is a live test, comment this line to run")
c := New(nil) c := New(nil)
......
...@@ -10,12 +10,16 @@ It is generated from these files: ...@@ -10,12 +10,16 @@ It is generated from these files:
hapi/release/info.proto hapi/release/info.proto
hapi/release/release.proto hapi/release/release.proto
hapi/release/status.proto hapi/release/status.proto
hapi/release/test_result.proto
hapi/release/test_suite.proto
It has these top-level messages: It has these top-level messages:
Hook Hook
Info Info
Release Release
Status Status
TestResult
TestSuite
*/ */
package release package release
...@@ -47,6 +51,7 @@ const ( ...@@ -47,6 +51,7 @@ const (
Hook_POST_UPGRADE Hook_Event = 6 Hook_POST_UPGRADE Hook_Event = 6
Hook_PRE_ROLLBACK Hook_Event = 7 Hook_PRE_ROLLBACK Hook_Event = 7
Hook_POST_ROLLBACK Hook_Event = 8 Hook_POST_ROLLBACK Hook_Event = 8
Hook_RELEASE_TEST Hook_Event = 9
) )
var Hook_Event_name = map[int32]string{ var Hook_Event_name = map[int32]string{
...@@ -59,6 +64,7 @@ var Hook_Event_name = map[int32]string{ ...@@ -59,6 +64,7 @@ var Hook_Event_name = map[int32]string{
6: "POST_UPGRADE", 6: "POST_UPGRADE",
7: "PRE_ROLLBACK", 7: "PRE_ROLLBACK",
8: "POST_ROLLBACK", 8: "POST_ROLLBACK",
9: "RELEASE_TEST",
} }
var Hook_Event_value = map[string]int32{ var Hook_Event_value = map[string]int32{
"UNKNOWN": 0, "UNKNOWN": 0,
...@@ -70,6 +76,7 @@ var Hook_Event_value = map[string]int32{ ...@@ -70,6 +76,7 @@ var Hook_Event_value = map[string]int32{
"POST_UPGRADE": 6, "POST_UPGRADE": 6,
"PRE_ROLLBACK": 7, "PRE_ROLLBACK": 7,
"POST_ROLLBACK": 8, "POST_ROLLBACK": 8,
"RELEASE_TEST": 9,
} }
func (x Hook_Event) String() string { func (x Hook_Event) String() string {
...@@ -112,26 +119,26 @@ func init() { ...@@ -112,26 +119,26 @@ func init() {
func init() { proto.RegisterFile("hapi/release/hook.proto", fileDescriptor0) } func init() { proto.RegisterFile("hapi/release/hook.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 321 bytes of a gzipped FileDescriptorProto // 334 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x8f, 0x4d, 0x6e, 0xea, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x90, 0xcd, 0x6e, 0xe2, 0x30,
0x14, 0x85, 0x5f, 0x20, 0x24, 0x70, 0xe1, 0xd1, 0xd4, 0x93, 0x5a, 0x4c, 0x8a, 0x18, 0x31, 0x72, 0x14, 0x85, 0x27, 0x10, 0x12, 0x30, 0x0c, 0xe3, 0xf1, 0x66, 0x2c, 0x36, 0x83, 0x58, 0xb1, 0x72,
0x2a, 0xaa, 0x2e, 0x00, 0x8a, 0xd5, 0x56, 0x44, 0x01, 0x99, 0xa0, 0x4a, 0x9d, 0x20, 0xa3, 0x1a, 0x2a, 0xaa, 0x3e, 0x40, 0x28, 0x56, 0x5b, 0x11, 0x05, 0xe4, 0x04, 0x55, 0xea, 0x26, 0x32, 0xaa,
0x88, 0x20, 0x71, 0x44, 0x4c, 0xd7, 0xd3, 0xf5, 0x75, 0x15, 0x95, 0x9d, 0x1f, 0x75, 0x76, 0xfd, 0x81, 0x08, 0x12, 0x47, 0xc4, 0xf4, 0xc1, 0xfa, 0x7c, 0x5d, 0x54, 0x76, 0x7e, 0xd4, 0xdd, 0xcd,
0xdd, 0xcf, 0xc7, 0x3e, 0x70, 0x77, 0xe4, 0x59, 0xec, 0x5f, 0xc4, 0x59, 0xf0, 0x5c, 0xf8, 0x47, 0x77, 0xbf, 0x1c, 0xfb, 0x18, 0xfc, 0x3b, 0xf1, 0x22, 0xf5, 0xae, 0xe2, 0x22, 0x78, 0x29, 0xbc,
0x29, 0x4f, 0x24, 0xbb, 0x48, 0x25, 0x51, 0x4f, 0x2f, 0x48, 0xb9, 0x18, 0xdc, 0x1f, 0xa4, 0x3c, 0x93, 0x94, 0x67, 0x52, 0x5c, 0xa5, 0x92, 0x68, 0xa4, 0x17, 0xa4, 0x5e, 0x4c, 0xfe, 0x1f, 0xa5,
0x9c, 0x85, 0x6f, 0x76, 0xbb, 0xeb, 0xde, 0x57, 0x71, 0x22, 0x72, 0xc5, 0x93, 0xac, 0xd0, 0x47, 0x3c, 0x5e, 0x84, 0x67, 0x76, 0xfb, 0xdb, 0xc1, 0x53, 0x69, 0x26, 0x4a, 0xc5, 0xb3, 0xa2, 0xd2,
0x3f, 0x0d, 0xb0, 0x5f, 0xa5, 0x3c, 0x21, 0x04, 0x76, 0xca, 0x13, 0x81, 0xad, 0xa1, 0x35, 0xee, 0x67, 0x5f, 0x1d, 0x60, 0x3f, 0x4b, 0x79, 0x46, 0x08, 0xd8, 0x39, 0xcf, 0x04, 0xb6, 0xa6, 0xd6,
0x30, 0x33, 0x6b, 0x76, 0x8a, 0xd3, 0x4f, 0xdc, 0x28, 0x98, 0x9e, 0x35, 0xcb, 0xb8, 0x3a, 0xe2, 0x7c, 0xc0, 0xcc, 0xac, 0xd9, 0x39, 0xcd, 0xdf, 0x71, 0xa7, 0x62, 0x7a, 0xd6, 0xac, 0xe0, 0xea,
0x66, 0xc1, 0xf4, 0x8c, 0x06, 0xd0, 0x4e, 0x78, 0x1a, 0xef, 0x45, 0xae, 0xb0, 0x6d, 0x78, 0x7d, 0x84, 0xbb, 0x15, 0xd3, 0x33, 0x9a, 0x80, 0x7e, 0xc6, 0xf3, 0xf4, 0x20, 0x4a, 0x85, 0x6d, 0xc3,
0x46, 0x0f, 0xe0, 0x88, 0x2f, 0x91, 0xaa, 0x1c, 0xb7, 0x86, 0xcd, 0x71, 0x7f, 0x82, 0xc9, 0xdf, 0xdb, 0x6f, 0x74, 0x07, 0x1c, 0xf1, 0x21, 0x72, 0x55, 0xe2, 0xde, 0xb4, 0x3b, 0x1f, 0x2f, 0x30,
0x0f, 0x12, 0xfd, 0x36, 0xa1, 0x5a, 0x60, 0xa5, 0x87, 0x9e, 0xa0, 0x7d, 0xe6, 0xb9, 0xda, 0x5e, 0xf9, 0x79, 0x41, 0xa2, 0xcf, 0x26, 0x54, 0x0b, 0xac, 0xf6, 0xd0, 0x03, 0xe8, 0x5f, 0x78, 0xa9,
0xae, 0x29, 0x76, 0x86, 0xd6, 0xb8, 0x3b, 0x19, 0x90, 0xa2, 0x06, 0xa9, 0x6a, 0x90, 0xa8, 0xaa, 0x92, 0xeb, 0x2d, 0xc7, 0xce, 0xd4, 0x9a, 0x0f, 0x17, 0x13, 0x52, 0xd5, 0x20, 0x4d, 0x0d, 0x12,
0xc1, 0x5c, 0xed, 0xb2, 0x6b, 0x3a, 0xfa, 0xb6, 0xa0, 0x65, 0x82, 0x50, 0x17, 0xdc, 0x4d, 0xb8, 0x37, 0x35, 0x98, 0xab, 0x5d, 0x76, 0xcb, 0x67, 0x9f, 0x16, 0xe8, 0x99, 0x20, 0x34, 0x04, 0xee,
0x08, 0x97, 0xef, 0xa1, 0xf7, 0x0f, 0xdd, 0x40, 0x77, 0xc5, 0xe8, 0xf6, 0x2d, 0x5c, 0x47, 0xd3, 0x2e, 0x5c, 0x87, 0x9b, 0xd7, 0x10, 0xfe, 0x42, 0x7f, 0xc0, 0x70, 0xcb, 0x68, 0xf2, 0x12, 0x46,
0x20, 0xf0, 0x2c, 0xe4, 0x41, 0x6f, 0xb5, 0x5c, 0x47, 0x35, 0x69, 0xa0, 0x3e, 0x80, 0x56, 0xe6, 0xb1, 0x1f, 0x04, 0xd0, 0x42, 0x10, 0x8c, 0xb6, 0x9b, 0x28, 0x6e, 0x49, 0x07, 0x8d, 0x01, 0xd0,
0x34, 0xa0, 0x11, 0xf5, 0x9a, 0xe6, 0x8a, 0x36, 0x4a, 0x60, 0x57, 0x19, 0x9b, 0xd5, 0x0b, 0x9b, 0xca, 0x8a, 0x06, 0x34, 0xa6, 0xb0, 0x6b, 0x7e, 0xd1, 0x46, 0x0d, 0xec, 0x26, 0x63, 0xb7, 0x7d,
0xce, 0xa9, 0xd7, 0xaa, 0x33, 0x2a, 0xe2, 0x18, 0xc2, 0xe8, 0x96, 0x2d, 0x83, 0x60, 0x36, 0x7d, 0x62, 0xfe, 0x8a, 0xc2, 0x5e, 0x9b, 0xd1, 0x10, 0xc7, 0x10, 0x46, 0x13, 0xb6, 0x09, 0x82, 0xa5,
0x5e, 0x78, 0x2e, 0xba, 0x85, 0xff, 0xc6, 0xa9, 0x51, 0x7b, 0xd6, 0xf9, 0x70, 0xcb, 0xde, 0x3b, 0xff, 0xb8, 0x86, 0x2e, 0xfa, 0x0b, 0x7e, 0x1b, 0xa7, 0x45, 0x7d, 0x2d, 0x31, 0x1a, 0x50, 0x3f,
0xc7, 0x54, 0x79, 0xfc, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xa4, 0x2e, 0x6f, 0xbd, 0xc8, 0x01, 0x00, 0xa2, 0x49, 0x4c, 0xa3, 0x18, 0x0e, 0x96, 0x83, 0x37, 0xb7, 0x7e, 0x89, 0xbd, 0x63, 0xca, 0xdd,
0x00, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x35, 0xb7, 0x2a, 0x22, 0xda, 0x01, 0x00, 0x00,
} }
...@@ -35,6 +35,8 @@ type Release struct { ...@@ -35,6 +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
TestSuite *TestSuite `protobuf:"bytes,9,opt,name=test_suite,json=testSuite" json:"test_suite,omitempty"`
} }
func (m *Release) Reset() { *m = Release{} } func (m *Release) Reset() { *m = Release{} }
...@@ -70,6 +72,13 @@ func (m *Release) GetHooks() []*Hook { ...@@ -70,6 +72,13 @@ func (m *Release) GetHooks() []*Hook {
return nil return nil
} }
func (m *Release) GetTestSuite() *TestSuite {
if m != nil {
return m.TestSuite
}
return nil
}
func init() { func init() {
proto.RegisterType((*Release)(nil), "hapi.release.Release") proto.RegisterType((*Release)(nil), "hapi.release.Release")
} }
...@@ -77,21 +86,24 @@ func init() { ...@@ -77,21 +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{
// 256 bytes of a gzipped FileDescriptorProto // 290 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0xbf, 0x4e, 0xc3, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0xbf, 0x4e, 0xc3, 0x30,
0x0c, 0xc6, 0x95, 0x36, 0x7f, 0x1a, 0xc3, 0x82, 0x07, 0xb0, 0x22, 0x86, 0x88, 0x01, 0x22, 0x86, 0x10, 0xc6, 0x95, 0x36, 0x7f, 0x9a, 0x83, 0x85, 0x1b, 0xa8, 0x15, 0x81, 0x14, 0x31, 0x40, 0xc4,
0x54, 0x82, 0x37, 0x80, 0x05, 0xd6, 0x1b, 0xd9, 0x8e, 0xe8, 0x42, 0x4e, 0xa5, 0xe7, 0x28, 0x17, 0x90, 0x4a, 0x20, 0xf1, 0x00, 0xb0, 0xc0, 0x6a, 0x98, 0x58, 0x90, 0x89, 0x1c, 0x62, 0x95, 0xda,
0xf1, 0x2c, 0x3c, 0x2e, 0xba, 0x3f, 0x85, 0x94, 0x2e, 0x4e, 0xec, 0xdf, 0xa7, 0xcf, 0xdf, 0x19, 0x51, 0x6c, 0x78, 0x4e, 0x1e, 0x09, 0xf9, 0x4f, 0x68, 0x42, 0x17, 0xc7, 0x77, 0xbf, 0x2f, 0xf7,
0xaa, 0x41, 0x8e, 0x7a, 0x3b, 0xa9, 0x4f, 0x25, 0xad, 0x3a, 0x7c, 0xdb, 0x71, 0xe2, 0x99, 0xf1, 0x7d, 0x3e, 0x28, 0x3a, 0xd6, 0x8b, 0xcd, 0xc0, 0x3f, 0x39, 0xd3, 0x7c, 0xfc, 0xd6, 0xfd, 0xa0,
0xdc, 0xb1, 0x36, 0xce, 0xaa, 0xab, 0x23, 0xe5, 0xc0, 0xbc, 0x0b, 0xb2, 0x7f, 0x40, 0x9b, 0x9e, 0x8c, 0xc2, 0x63, 0xcb, 0xea, 0xd0, 0x2b, 0xd6, 0x33, 0x65, 0xa7, 0xd4, 0xd6, 0xcb, 0xfe, 0x01,
0x8f, 0x40, 0x37, 0xc8, 0x69, 0xde, 0x76, 0x6c, 0x7a, 0xfd, 0x11, 0xc1, 0xe5, 0x12, 0xb8, 0x1a, 0x21, 0x5b, 0x15, 0xc0, 0xf9, 0x0c, 0x18, 0xae, 0xcd, 0x9b, 0xfe, 0x12, 0x86, 0xcf, 0xfe, 0x6b,
0xe6, 0x37, 0xdf, 0x2b, 0x28, 0x44, 0xf0, 0x41, 0x84, 0xd4, 0xc8, 0xbd, 0xa2, 0xa4, 0x4e, 0x9a, 0x3a, 0x36, 0x98, 0x4d, 0xa3, 0x64, 0x2b, 0x3e, 0x02, 0x38, 0x9d, 0x02, 0x7b, 0xfa, 0xfe, 0xc5,
0x52, 0xf8, 0x7f, 0xbc, 0x85, 0xd4, 0xd9, 0xd3, 0xaa, 0x4e, 0x9a, 0xb3, 0x07, 0x6c, 0x97, 0xf9, 0xcf, 0x02, 0x32, 0xea, 0xa7, 0x21, 0x42, 0x2c, 0xd9, 0x8e, 0x93, 0xa8, 0x8c, 0xaa, 0x9c, 0xba,
0xda, 0x57, 0xd3, 0xb3, 0xf0, 0x1c, 0xef, 0x20, 0xf3, 0xb6, 0xb4, 0xf6, 0xc2, 0x8b, 0x20, 0x0c, 0x3b, 0x5e, 0x42, 0x6c, 0xdd, 0xc9, 0xa2, 0x8c, 0xaa, 0xa3, 0x1b, 0xac, 0xa7, 0xf1, 0xeb, 0x27,
0x9b, 0x9e, 0x5d, 0x15, 0x81, 0xe3, 0x3d, 0xe4, 0x21, 0x18, 0xa5, 0x4b, 0xcb, 0xa8, 0xf4, 0x44, 0xd9, 0x2a, 0xea, 0x38, 0x5e, 0x41, 0xe2, 0xc6, 0x92, 0xa5, 0x13, 0x9e, 0x78, 0xa1, 0x77, 0x7a,
0x44, 0x05, 0x56, 0xb0, 0xd9, 0x4b, 0xa3, 0x7b, 0x65, 0x67, 0xca, 0x7c, 0xa8, 0xdf, 0x1e, 0x1b, 0xb0, 0x27, 0xf5, 0x1c, 0xaf, 0x21, 0xf5, 0xc1, 0x48, 0x3c, 0x1d, 0x19, 0x94, 0x8e, 0xd0, 0xa0,
0xc8, 0xdc, 0x41, 0x2c, 0xe5, 0xf5, 0xfa, 0x34, 0xd9, 0x0b, 0xf3, 0x4e, 0x04, 0x01, 0x12, 0x14, 0xc0, 0x02, 0x56, 0x3b, 0x26, 0x45, 0xcb, 0xb5, 0x21, 0x89, 0x0b, 0xf5, 0x57, 0x63, 0x05, 0x89,
0x5f, 0x6a, 0xb2, 0x9a, 0x0d, 0x15, 0x75, 0xd2, 0x64, 0xe2, 0xd0, 0xe2, 0x35, 0x94, 0xee, 0x91, 0xdd, 0x97, 0x26, 0x69, 0xb9, 0x3c, 0x4c, 0xf6, 0xa8, 0xd4, 0x96, 0x7a, 0x01, 0x12, 0xc8, 0xbe,
0x76, 0x94, 0x9d, 0xa2, 0x8d, 0x5f, 0xf0, 0x37, 0x78, 0x2a, 0xdf, 0x8a, 0x68, 0xf7, 0x9e, 0xfb, 0xf9, 0xa0, 0x85, 0x92, 0x24, 0x2b, 0xa3, 0x2a, 0xa1, 0x63, 0x89, 0x67, 0x90, 0xdb, 0x47, 0xea,
0x63, 0x3d, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0xc8, 0x8f, 0xec, 0x97, 0xbb, 0x01, 0x00, 0x00, 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,
} }
// 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_suite.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
// 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"`
// Results are the results of each segment of the test
Results []*TestResult `protobuf:"bytes,2,rep,name=results" json:"results,omitempty"`
}
func (m *TestSuite) Reset() { *m = TestSuite{} }
func (m *TestSuite) String() string { return proto.CompactTextString(m) }
func (*TestSuite) ProtoMessage() {}
func (*TestSuite) Descriptor() ([]byte, []int) { return fileDescriptor5, []int{0} }
func (m *TestSuite) GetLastRun() *google_protobuf.Timestamp {
if m != nil {
return m.LastRun
}
return nil
}
func (m *TestSuite) GetResults() []*TestResult {
if m != nil {
return m.Results
}
return nil
}
func init() {
proto.RegisterType((*TestSuite)(nil), "hapi.release.TestSuite")
}
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,
}
...@@ -28,6 +28,8 @@ It has these top-level messages: ...@@ -28,6 +28,8 @@ It has these top-level messages:
GetVersionResponse GetVersionResponse
GetHistoryRequest GetHistoryRequest
GetHistoryResponse GetHistoryResponse
TestReleaseRequest
TestReleaseResponse
*/ */
package services package services
...@@ -36,10 +38,11 @@ import fmt "fmt" ...@@ -36,10 +38,11 @@ import fmt "fmt"
import math "math" import math "math"
import hapi_chart3 "k8s.io/helm/pkg/proto/hapi/chart" import hapi_chart3 "k8s.io/helm/pkg/proto/hapi/chart"
import hapi_chart "k8s.io/helm/pkg/proto/hapi/chart" import hapi_chart "k8s.io/helm/pkg/proto/hapi/chart"
import hapi_release3 "k8s.io/helm/pkg/proto/hapi/release" import hapi_release5 "k8s.io/helm/pkg/proto/hapi/release"
import hapi_release2 "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_release1 "k8s.io/helm/pkg/proto/hapi/release"
import hapi_version "k8s.io/helm/pkg/proto/hapi/version" import hapi_version "k8s.io/helm/pkg/proto/hapi/version"
import hapi_release4 "k8s.io/helm/pkg/proto/hapi/release"
import ( import (
context "golang.org/x/net/context" context "golang.org/x/net/context"
...@@ -153,7 +156,7 @@ type ListReleasesResponse struct { ...@@ -153,7 +156,7 @@ type ListReleasesResponse struct {
// Total is the total number of queryable releases. // Total is the total number of queryable releases.
Total int64 `protobuf:"varint,3,opt,name=total" json:"total,omitempty"` Total int64 `protobuf:"varint,3,opt,name=total" json:"total,omitempty"`
// Releases is the list of found release objects. // Releases is the list of found release objects.
Releases []*hapi_release3.Release `protobuf:"bytes,4,rep,name=releases" json:"releases,omitempty"` Releases []*hapi_release5.Release `protobuf:"bytes,4,rep,name=releases" json:"releases,omitempty"`
} }
func (m *ListReleasesResponse) Reset() { *m = ListReleasesResponse{} } func (m *ListReleasesResponse) Reset() { *m = ListReleasesResponse{} }
...@@ -161,7 +164,7 @@ func (m *ListReleasesResponse) String() string { return proto.Compact ...@@ -161,7 +164,7 @@ func (m *ListReleasesResponse) String() string { return proto.Compact
func (*ListReleasesResponse) ProtoMessage() {} func (*ListReleasesResponse) ProtoMessage() {}
func (*ListReleasesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } func (*ListReleasesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *ListReleasesResponse) GetReleases() []*hapi_release3.Release { func (m *ListReleasesResponse) GetReleases() []*hapi_release5.Release {
if m != nil { if m != nil {
return m.Releases return m.Releases
} }
...@@ -219,7 +222,7 @@ func (*GetReleaseContentRequest) Descriptor() ([]byte, []int) { return fileDescr ...@@ -219,7 +222,7 @@ func (*GetReleaseContentRequest) Descriptor() ([]byte, []int) { return fileDescr
// GetReleaseContentResponse is a response containing the contents of a release. // GetReleaseContentResponse is a response containing the contents of a release.
type GetReleaseContentResponse struct { type GetReleaseContentResponse struct {
// The release content // The release content
Release *hapi_release3.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
} }
func (m *GetReleaseContentResponse) Reset() { *m = GetReleaseContentResponse{} } func (m *GetReleaseContentResponse) Reset() { *m = GetReleaseContentResponse{} }
...@@ -227,7 +230,7 @@ func (m *GetReleaseContentResponse) String() string { return proto.Co ...@@ -227,7 +230,7 @@ func (m *GetReleaseContentResponse) String() string { return proto.Co
func (*GetReleaseContentResponse) ProtoMessage() {} func (*GetReleaseContentResponse) ProtoMessage() {}
func (*GetReleaseContentResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } func (*GetReleaseContentResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
func (m *GetReleaseContentResponse) GetRelease() *hapi_release3.Release { func (m *GetReleaseContentResponse) GetRelease() *hapi_release5.Release {
if m != nil { if m != nil {
return m.Release return m.Release
} }
...@@ -278,7 +281,7 @@ func (m *UpdateReleaseRequest) GetValues() *hapi_chart.Config { ...@@ -278,7 +281,7 @@ func (m *UpdateReleaseRequest) GetValues() *hapi_chart.Config {
// UpdateReleaseResponse is the response to an update request. // UpdateReleaseResponse is the response to an update request.
type UpdateReleaseResponse struct { type UpdateReleaseResponse struct {
Release *hapi_release3.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
} }
func (m *UpdateReleaseResponse) Reset() { *m = UpdateReleaseResponse{} } func (m *UpdateReleaseResponse) Reset() { *m = UpdateReleaseResponse{} }
...@@ -286,7 +289,7 @@ func (m *UpdateReleaseResponse) String() string { return proto.Compac ...@@ -286,7 +289,7 @@ func (m *UpdateReleaseResponse) String() string { return proto.Compac
func (*UpdateReleaseResponse) ProtoMessage() {} func (*UpdateReleaseResponse) ProtoMessage() {}
func (*UpdateReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } func (*UpdateReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
func (m *UpdateReleaseResponse) GetRelease() *hapi_release3.Release { func (m *UpdateReleaseResponse) GetRelease() *hapi_release5.Release {
if m != nil { if m != nil {
return m.Release return m.Release
} }
...@@ -318,7 +321,7 @@ func (*RollbackReleaseRequest) Descriptor() ([]byte, []int) { return fileDescrip ...@@ -318,7 +321,7 @@ func (*RollbackReleaseRequest) Descriptor() ([]byte, []int) { return fileDescrip
// RollbackReleaseResponse is the response to an update request. // RollbackReleaseResponse is the response to an update request.
type RollbackReleaseResponse struct { type RollbackReleaseResponse struct {
Release *hapi_release3.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
} }
func (m *RollbackReleaseResponse) Reset() { *m = RollbackReleaseResponse{} } func (m *RollbackReleaseResponse) Reset() { *m = RollbackReleaseResponse{} }
...@@ -326,7 +329,7 @@ func (m *RollbackReleaseResponse) String() string { return proto.Comp ...@@ -326,7 +329,7 @@ func (m *RollbackReleaseResponse) String() string { return proto.Comp
func (*RollbackReleaseResponse) ProtoMessage() {} func (*RollbackReleaseResponse) ProtoMessage() {}
func (*RollbackReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } func (*RollbackReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
func (m *RollbackReleaseResponse) GetRelease() *hapi_release3.Release { func (m *RollbackReleaseResponse) GetRelease() *hapi_release5.Release {
if m != nil { if m != nil {
return m.Release return m.Release
} }
...@@ -381,7 +384,7 @@ func (m *InstallReleaseRequest) GetValues() *hapi_chart.Config { ...@@ -381,7 +384,7 @@ func (m *InstallReleaseRequest) GetValues() *hapi_chart.Config {
// InstallReleaseResponse is the response from a release installation. // InstallReleaseResponse is the response from a release installation.
type InstallReleaseResponse struct { type InstallReleaseResponse struct {
Release *hapi_release3.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
} }
func (m *InstallReleaseResponse) Reset() { *m = InstallReleaseResponse{} } func (m *InstallReleaseResponse) Reset() { *m = InstallReleaseResponse{} }
...@@ -389,7 +392,7 @@ func (m *InstallReleaseResponse) String() string { return proto.Compa ...@@ -389,7 +392,7 @@ func (m *InstallReleaseResponse) String() string { return proto.Compa
func (*InstallReleaseResponse) ProtoMessage() {} func (*InstallReleaseResponse) ProtoMessage() {}
func (*InstallReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } func (*InstallReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} }
func (m *InstallReleaseResponse) GetRelease() *hapi_release3.Release { func (m *InstallReleaseResponse) GetRelease() *hapi_release5.Release {
if m != nil { if m != nil {
return m.Release return m.Release
} }
...@@ -416,7 +419,7 @@ func (*UninstallReleaseRequest) Descriptor() ([]byte, []int) { return fileDescri ...@@ -416,7 +419,7 @@ func (*UninstallReleaseRequest) Descriptor() ([]byte, []int) { return fileDescri
// UninstallReleaseResponse represents a successful response to an uninstall request. // UninstallReleaseResponse represents a successful response to an uninstall request.
type UninstallReleaseResponse struct { type UninstallReleaseResponse struct {
// Release is the release that was marked deleted. // Release is the release that was marked deleted.
Release *hapi_release3.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
// Info is an uninstall message // Info is an uninstall message
Info string `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"` Info string `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"`
} }
...@@ -426,7 +429,7 @@ func (m *UninstallReleaseResponse) String() string { return proto.Com ...@@ -426,7 +429,7 @@ func (m *UninstallReleaseResponse) String() string { return proto.Com
func (*UninstallReleaseResponse) ProtoMessage() {} func (*UninstallReleaseResponse) ProtoMessage() {}
func (*UninstallReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } func (*UninstallReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} }
func (m *UninstallReleaseResponse) GetRelease() *hapi_release3.Release { func (m *UninstallReleaseResponse) GetRelease() *hapi_release5.Release {
if m != nil { if m != nil {
return m.Release return m.Release
} }
...@@ -443,7 +446,7 @@ func (*GetVersionRequest) ProtoMessage() {} ...@@ -443,7 +446,7 @@ func (*GetVersionRequest) ProtoMessage() {}
func (*GetVersionRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } func (*GetVersionRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
type GetVersionResponse struct { type GetVersionResponse struct {
Version *hapi_version.Version `protobuf:"bytes,1,opt,name=Version" json:"Version,omitempty"` Version *hapi_version.Version `protobuf:"bytes,1,opt,name=Version,json=version" json:"Version,omitempty"`
} }
func (m *GetVersionResponse) Reset() { *m = GetVersionResponse{} } func (m *GetVersionResponse) Reset() { *m = GetVersionResponse{} }
...@@ -473,7 +476,7 @@ func (*GetHistoryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, ...@@ -473,7 +476,7 @@ func (*GetHistoryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0,
// GetHistoryResponse is received in response to a GetHistory rpc. // GetHistoryResponse is received in response to a GetHistory rpc.
type GetHistoryResponse struct { type GetHistoryResponse struct {
Releases []*hapi_release3.Release `protobuf:"bytes,1,rep,name=releases" json:"releases,omitempty"` Releases []*hapi_release5.Release `protobuf:"bytes,1,rep,name=releases" json:"releases,omitempty"`
} }
func (m *GetHistoryResponse) Reset() { *m = GetHistoryResponse{} } func (m *GetHistoryResponse) Reset() { *m = GetHistoryResponse{} }
...@@ -481,13 +484,44 @@ func (m *GetHistoryResponse) String() string { return proto.CompactTe ...@@ -481,13 +484,44 @@ func (m *GetHistoryResponse) String() string { return proto.CompactTe
func (*GetHistoryResponse) ProtoMessage() {} func (*GetHistoryResponse) ProtoMessage() {}
func (*GetHistoryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } func (*GetHistoryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} }
func (m *GetHistoryResponse) GetReleases() []*hapi_release3.Release { func (m *GetHistoryResponse) GetReleases() []*hapi_release5.Release {
if m != nil { if m != nil {
return m.Releases return m.Releases
} }
return nil return nil
} }
// TestReleaseRequest is a request to get the status of a release.
type TestReleaseRequest struct {
// Name is the name of the release
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// timeout specifies the max amount of time any kubernetes client command can run.
Timeout int64 `protobuf:"varint,2,opt,name=timeout" json:"timeout,omitempty"`
}
func (m *TestReleaseRequest) Reset() { *m = TestReleaseRequest{} }
func (m *TestReleaseRequest) String() string { return proto.CompactTextString(m) }
func (*TestReleaseRequest) ProtoMessage() {}
func (*TestReleaseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} }
// 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"`
}
func (m *TestReleaseResponse) Reset() { *m = TestReleaseResponse{} }
func (m *TestReleaseResponse) String() string { return proto.CompactTextString(m) }
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() { func init() {
proto.RegisterType((*ListReleasesRequest)(nil), "hapi.services.tiller.ListReleasesRequest") proto.RegisterType((*ListReleasesRequest)(nil), "hapi.services.tiller.ListReleasesRequest")
proto.RegisterType((*ListSort)(nil), "hapi.services.tiller.ListSort") proto.RegisterType((*ListSort)(nil), "hapi.services.tiller.ListSort")
...@@ -508,6 +542,8 @@ func init() { ...@@ -508,6 +542,8 @@ func init() {
proto.RegisterType((*GetVersionResponse)(nil), "hapi.services.tiller.GetVersionResponse") proto.RegisterType((*GetVersionResponse)(nil), "hapi.services.tiller.GetVersionResponse")
proto.RegisterType((*GetHistoryRequest)(nil), "hapi.services.tiller.GetHistoryRequest") proto.RegisterType((*GetHistoryRequest)(nil), "hapi.services.tiller.GetHistoryRequest")
proto.RegisterType((*GetHistoryResponse)(nil), "hapi.services.tiller.GetHistoryResponse") proto.RegisterType((*GetHistoryResponse)(nil), "hapi.services.tiller.GetHistoryResponse")
proto.RegisterType((*TestReleaseRequest)(nil), "hapi.services.tiller.TestReleaseRequest")
proto.RegisterType((*TestReleaseResponse)(nil), "hapi.services.tiller.TestReleaseResponse")
proto.RegisterEnum("hapi.services.tiller.ListSort_SortBy", ListSort_SortBy_name, ListSort_SortBy_value) proto.RegisterEnum("hapi.services.tiller.ListSort_SortBy", ListSort_SortBy_name, ListSort_SortBy_value)
proto.RegisterEnum("hapi.services.tiller.ListSort_SortOrder", ListSort_SortOrder_name, ListSort_SortOrder_value) proto.RegisterEnum("hapi.services.tiller.ListSort_SortOrder", ListSort_SortOrder_name, ListSort_SortOrder_value)
} }
...@@ -544,6 +580,9 @@ type ReleaseServiceClient interface { ...@@ -544,6 +580,9 @@ type ReleaseServiceClient interface {
RollbackRelease(ctx context.Context, in *RollbackReleaseRequest, opts ...grpc.CallOption) (*RollbackReleaseResponse, error) RollbackRelease(ctx context.Context, in *RollbackReleaseRequest, opts ...grpc.CallOption) (*RollbackReleaseResponse, error)
// ReleaseHistory retrieves a releasse's history. // ReleaseHistory retrieves a releasse's history.
GetHistory(ctx context.Context, in *GetHistoryRequest, opts ...grpc.CallOption) (*GetHistoryResponse, error) 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)
} }
type releaseServiceClient struct { type releaseServiceClient struct {
...@@ -658,6 +697,15 @@ func (c *releaseServiceClient) GetHistory(ctx context.Context, in *GetHistoryReq ...@@ -658,6 +697,15 @@ func (c *releaseServiceClient) GetHistory(ctx context.Context, in *GetHistoryReq
return out, nil 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...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for ReleaseService service // Server API for ReleaseService service
type ReleaseServiceServer interface { type ReleaseServiceServer interface {
...@@ -682,6 +730,9 @@ type ReleaseServiceServer interface { ...@@ -682,6 +730,9 @@ type ReleaseServiceServer interface {
RollbackRelease(context.Context, *RollbackReleaseRequest) (*RollbackReleaseResponse, error) RollbackRelease(context.Context, *RollbackReleaseRequest) (*RollbackReleaseResponse, error)
// ReleaseHistory retrieves a releasse's history. // ReleaseHistory retrieves a releasse's history.
GetHistory(context.Context, *GetHistoryRequest) (*GetHistoryResponse, error) 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)
} }
func RegisterReleaseServiceServer(s *grpc.Server, srv ReleaseServiceServer) { func RegisterReleaseServiceServer(s *grpc.Server, srv ReleaseServiceServer) {
...@@ -853,6 +904,24 @@ func _ReleaseService_GetHistory_Handler(srv interface{}, ctx context.Context, de ...@@ -853,6 +904,24 @@ func _ReleaseService_GetHistory_Handler(srv interface{}, ctx context.Context, de
return interceptor(ctx, in, info, handler) 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))
}
return interceptor(ctx, in, info, handler)
}
var _ReleaseService_serviceDesc = grpc.ServiceDesc{ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
ServiceName: "hapi.services.tiller.ReleaseService", ServiceName: "hapi.services.tiller.ReleaseService",
HandlerType: (*ReleaseServiceServer)(nil), HandlerType: (*ReleaseServiceServer)(nil),
...@@ -889,6 +958,10 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{ ...@@ -889,6 +958,10 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
MethodName: "GetHistory", MethodName: "GetHistory",
Handler: _ReleaseService_GetHistory_Handler, Handler: _ReleaseService_GetHistory_Handler,
}, },
{
MethodName: "RunReleaseTest",
Handler: _ReleaseService_RunReleaseTest_Handler,
},
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
{ {
......
...@@ -24,6 +24,7 @@ package environment ...@@ -24,6 +24,7 @@ package environment
import ( import (
"io" "io"
"time"
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/engine" "k8s.io/helm/pkg/engine"
...@@ -31,6 +32,7 @@ import ( ...@@ -31,6 +32,7 @@ import (
"k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/storage" "k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/storage/driver" "k8s.io/helm/pkg/storage/driver"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
) )
...@@ -135,6 +137,8 @@ type KubeClient interface { ...@@ -135,6 +137,8 @@ type KubeClient interface {
Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error
Build(namespace string, reader io.Reader) (kube.Result, 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)
} }
// PrintingKubeClient implements KubeClient, but simply prints the reader to // PrintingKubeClient implements KubeClient, but simply prints the reader to
......
...@@ -40,6 +40,7 @@ const ( ...@@ -40,6 +40,7 @@ const (
postUpgrade = "post-upgrade" postUpgrade = "post-upgrade"
preRollback = "pre-rollback" preRollback = "pre-rollback"
postRollback = "post-rollback" postRollback = "post-rollback"
releaseTest = "test"
) )
var events = map[string]release.Hook_Event{ var events = map[string]release.Hook_Event{
...@@ -51,6 +52,7 @@ var events = map[string]release.Hook_Event{ ...@@ -51,6 +52,7 @@ var events = map[string]release.Hook_Event{
postUpgrade: release.Hook_POST_UPGRADE, postUpgrade: release.Hook_POST_UPGRADE,
preRollback: release.Hook_PRE_ROLLBACK, preRollback: release.Hook_PRE_ROLLBACK,
postRollback: release.Hook_POST_ROLLBACK, postRollback: release.Hook_POST_ROLLBACK,
releaseTest: release.Hook_RELEASE_TEST,
} }
type simpleHead struct { type simpleHead struct {
......
...@@ -1064,3 +1064,29 @@ func validateManifest(c environment.KubeClient, ns string, manifest []byte) erro ...@@ -1064,3 +1064,29 @@ func validateManifest(c environment.KubeClient, ns string, manifest []byte) erro
_, err := c.Build(ns, r) _, err := c.Build(ns, r)
return err return err
} }
// RunTestRelease runs a pre-defined test on a given release
func (s *ReleaseServer) RunReleaseTest(c ctx.Context, req *services.TestReleaseRequest) (*services.TestReleaseResponse, error) {
res := &services.TestReleaseResponse{}
if !ValidName.MatchString(req.Name) {
return nil, errMissingRelease
}
// finds the non-deleted release with the given name
r, err := s.env.Releases.Last(req.Name)
if err != nil {
return nil, err
}
kubeCli := s.env.KubeClient
testSuite, err := runReleaseTestSuite(r.Hooks, kubeCli, r.Name, r.Namespace, req.Timeout)
if err != nil {
return nil, err
}
r.TestSuite = testSuite
res.Result = testSuite
return res, 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 tiller
import (
"bytes"
"fmt"
"log"
"time"
"github.com/ghodss/yaml"
"k8s.io/kubernetes/pkg/api"
"k8s.io/helm/pkg/proto/hapi/release"
"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) {
suite := &release.TestSuite{}
suite.LastRun = timeconv.Now()
results := []*release.TestResult{}
tests, err := prepareTests(hooks, name)
if err != nil {
return suite, err
}
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()
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
resourceCreated = false
}
status := api.PodUnknown
resourceCleanExit := true
if resourceCreated {
b.Reset()
b.WriteString(h)
status, err = kube.WaitAndGetCompletedPodStatus(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
}
}
// 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
} else if resourceCreated && resourceCleanExit && status == api.PodFailed {
ts.Status = 2
}
results = append(results, ts)
log.Printf("Test %s(%s) complete", ts.Name, sh.Kind)
//TODO: recordTests() - add test results to configmap with standardized name
}
suite.Results = results
log.Printf("Finished running test suite for %s", name)
return suite, nil
}
func filterTests(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
testHooks = append(testHooks, h)
continue
}
}
}
//TODO: probably don't need to check found
if found == false && len(testHooks) == 0 {
return nil, notFoundErr
}
return testHooks, nil
}
func prepareTests(hooks []*release.Hook, releaseName string) ([]string, error) {
testHooks, err := filterTests(hooks, releaseName)
if err != nil {
return nil, err
}
tests := []string{}
for _, h := range testHooks {
individualTests := splitManifests(h.Manifest)
for _, t := range individualTests {
tests = append(tests, t)
}
}
return tests, 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