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 {
POST_UPGRADE = 6;
PRE_ROLLBACK = 7;
POST_ROLLBACK = 8;
RELEASE_TEST = 9;
}
string name = 1;
// Kind is the Kubernetes kind.
......
......@@ -18,6 +18,7 @@ package hapi.release;
import "hapi/release/hook.proto";
import "hapi/release/info.proto";
import "hapi/release/test_suite.proto";
import "hapi/chart/config.proto";
import "hapi/chart/chart.proto";
......@@ -50,4 +51,7 @@ message Release {
// Namespace is the kubernetes namespace of the release.
string namespace = 8;
// TestSuite provides results on the last test run on a release
hapi.release.TestSuite test_suite = 9;
}
// 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";
import "hapi/release/info.proto";
import "hapi/release/status.proto";
import "hapi/version/version.proto";
import "hapi/release/test_suite.proto";
option go_package = "services";
......@@ -78,6 +79,11 @@ service ReleaseService {
// ReleaseHistory retrieves a releasse's history.
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.
......@@ -304,3 +310,17 @@ message GetHistoryRequest {
message GetHistoryResponse {
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 {
newInitCmd(out),
newResetCmd(nil, out),
newVersionCmd(nil, out),
newReleaseTestCmd(nil, out),
// Hidden documentation generator command: 'helm docs'
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
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.
func (h *Client) list(ctx context.Context, req *rls.ListReleasesRequest) (*rls.ListReleasesResponse, error) {
c, err := grpc.Dial(h.opts.host, grpc.WithInsecure())
......@@ -356,3 +369,15 @@ func (h *Client) history(ctx context.Context, req *rls.GetHistoryRequest) (*rls.
rlc := rls.NewReleaseServiceClient(c)
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 {
ReleaseContent(rlsName string, opts ...ContentOption) (*rls.GetReleaseContentResponse, error)
ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.GetHistoryResponse, error)
GetVersion(opts ...VersionOption) (*rls.GetVersionResponse, error)
ReleaseTest(rlsName string, opts ...ReleaseTestOption) (*rls.TestReleaseResponse, error)
}
......@@ -66,6 +66,8 @@ type options struct {
histReq rls.GetHistoryRequest
// resetValues instructs Tiller to reset values to their defaults.
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").
......@@ -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
func RollbackTimeout(timeout int64) RollbackOption {
return func(opts *options) {
......@@ -364,3 +373,7 @@ func NewContext() context.Context {
md := metadata.Pairs("x-helm-api-client", version.Version)
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 (
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
"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/fields"
"k8s.io/kubernetes/pkg/kubectl"
......@@ -305,6 +306,10 @@ func perform(c *Client, namespace string, infos Result, fn ResourceActorFunc) er
if len(infos) == 0 {
return ErrNoObjectsVisited
}
if err != nil {
return err
}
for _, info := range infos {
if err := fn(info); err != nil {
return err
......@@ -610,3 +615,43 @@ func scrubValidationError(err error) error {
}
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) {
}
}
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) {
t.Skip("This is a live test, comment this line to run")
c := New(nil)
......
......@@ -10,12 +10,16 @@ It is generated from these files:
hapi/release/info.proto
hapi/release/release.proto
hapi/release/status.proto
hapi/release/test_result.proto
hapi/release/test_suite.proto
It has these top-level messages:
Hook
Info
Release
Status
TestResult
TestSuite
*/
package release
......@@ -47,6 +51,7 @@ const (
Hook_POST_UPGRADE Hook_Event = 6
Hook_PRE_ROLLBACK Hook_Event = 7
Hook_POST_ROLLBACK Hook_Event = 8
Hook_RELEASE_TEST Hook_Event = 9
)
var Hook_Event_name = map[int32]string{
......@@ -59,6 +64,7 @@ var Hook_Event_name = map[int32]string{
6: "POST_UPGRADE",
7: "PRE_ROLLBACK",
8: "POST_ROLLBACK",
9: "RELEASE_TEST",
}
var Hook_Event_value = map[string]int32{
"UNKNOWN": 0,
......@@ -70,6 +76,7 @@ var Hook_Event_value = map[string]int32{
"POST_UPGRADE": 6,
"PRE_ROLLBACK": 7,
"POST_ROLLBACK": 8,
"RELEASE_TEST": 9,
}
func (x Hook_Event) String() string {
......@@ -112,26 +119,26 @@ func init() {
func init() { proto.RegisterFile("hapi/release/hook.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 321 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x8f, 0x4d, 0x6e, 0xea, 0x30,
0x14, 0x85, 0x5f, 0x20, 0x24, 0x70, 0xe1, 0xd1, 0xd4, 0x93, 0x5a, 0x4c, 0x8a, 0x18, 0x31, 0x72,
0x2a, 0xaa, 0x2e, 0x00, 0x8a, 0xd5, 0x56, 0x44, 0x01, 0x99, 0xa0, 0x4a, 0x9d, 0x20, 0xa3, 0x1a,
0x88, 0x20, 0x71, 0x44, 0x4c, 0xd7, 0xd3, 0xf5, 0x75, 0x15, 0x95, 0x9d, 0x1f, 0x75, 0x76, 0xfd,
0xdd, 0xcf, 0xc7, 0x3e, 0x70, 0x77, 0xe4, 0x59, 0xec, 0x5f, 0xc4, 0x59, 0xf0, 0x5c, 0xf8, 0x47,
0x29, 0x4f, 0x24, 0xbb, 0x48, 0x25, 0x51, 0x4f, 0x2f, 0x48, 0xb9, 0x18, 0xdc, 0x1f, 0xa4, 0x3c,
0x9c, 0x85, 0x6f, 0x76, 0xbb, 0xeb, 0xde, 0x57, 0x71, 0x22, 0x72, 0xc5, 0x93, 0xac, 0xd0, 0x47,
0x3f, 0x0d, 0xb0, 0x5f, 0xa5, 0x3c, 0x21, 0x04, 0x76, 0xca, 0x13, 0x81, 0xad, 0xa1, 0x35, 0xee,
0x30, 0x33, 0x6b, 0x76, 0x8a, 0xd3, 0x4f, 0xdc, 0x28, 0x98, 0x9e, 0x35, 0xcb, 0xb8, 0x3a, 0xe2,
0x66, 0xc1, 0xf4, 0x8c, 0x06, 0xd0, 0x4e, 0x78, 0x1a, 0xef, 0x45, 0xae, 0xb0, 0x6d, 0x78, 0x7d,
0x46, 0x0f, 0xe0, 0x88, 0x2f, 0x91, 0xaa, 0x1c, 0xb7, 0x86, 0xcd, 0x71, 0x7f, 0x82, 0xc9, 0xdf,
0x0f, 0x12, 0xfd, 0x36, 0xa1, 0x5a, 0x60, 0xa5, 0x87, 0x9e, 0xa0, 0x7d, 0xe6, 0xb9, 0xda, 0x5e,
0xae, 0x29, 0x76, 0x86, 0xd6, 0xb8, 0x3b, 0x19, 0x90, 0xa2, 0x06, 0xa9, 0x6a, 0x90, 0xa8, 0xaa,
0xc1, 0x5c, 0xed, 0xb2, 0x6b, 0x3a, 0xfa, 0xb6, 0xa0, 0x65, 0x82, 0x50, 0x17, 0xdc, 0x4d, 0xb8,
0x08, 0x97, 0xef, 0xa1, 0xf7, 0x0f, 0xdd, 0x40, 0x77, 0xc5, 0xe8, 0xf6, 0x2d, 0x5c, 0x47, 0xd3,
0x20, 0xf0, 0x2c, 0xe4, 0x41, 0x6f, 0xb5, 0x5c, 0x47, 0x35, 0x69, 0xa0, 0x3e, 0x80, 0x56, 0xe6,
0x34, 0xa0, 0x11, 0xf5, 0x9a, 0xe6, 0x8a, 0x36, 0x4a, 0x60, 0x57, 0x19, 0x9b, 0xd5, 0x0b, 0x9b,
0xce, 0xa9, 0xd7, 0xaa, 0x33, 0x2a, 0xe2, 0x18, 0xc2, 0xe8, 0x96, 0x2d, 0x83, 0x60, 0x36, 0x7d,
0x5e, 0x78, 0x2e, 0xba, 0x85, 0xff, 0xc6, 0xa9, 0x51, 0x7b, 0xd6, 0xf9, 0x70, 0xcb, 0xde, 0x3b,
0xc7, 0x54, 0x79, 0xfc, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xa4, 0x2e, 0x6f, 0xbd, 0xc8, 0x01, 0x00,
0x00,
// 334 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x90, 0xcd, 0x6e, 0xe2, 0x30,
0x14, 0x85, 0x27, 0x10, 0x12, 0x30, 0x0c, 0xe3, 0xf1, 0x66, 0x2c, 0x36, 0x83, 0x58, 0xb1, 0x72,
0x2a, 0xaa, 0x3e, 0x40, 0x28, 0x56, 0x5b, 0x11, 0x05, 0xe4, 0x04, 0x55, 0xea, 0x26, 0x32, 0xaa,
0x81, 0x08, 0x12, 0x47, 0xc4, 0xf4, 0xc1, 0xfa, 0x7c, 0x5d, 0x54, 0x76, 0x7e, 0xd4, 0xdd, 0xcd,
0x77, 0xbf, 0x1c, 0xfb, 0x18, 0xfc, 0x3b, 0xf1, 0x22, 0xf5, 0xae, 0xe2, 0x22, 0x78, 0x29, 0xbc,
0x93, 0x94, 0x67, 0x52, 0x5c, 0xa5, 0x92, 0x68, 0xa4, 0x17, 0xa4, 0x5e, 0x4c, 0xfe, 0x1f, 0xa5,
0x3c, 0x5e, 0x84, 0x67, 0x76, 0xfb, 0xdb, 0xc1, 0x53, 0x69, 0x26, 0x4a, 0xc5, 0xb3, 0xa2, 0xd2,
0x67, 0x5f, 0x1d, 0x60, 0x3f, 0x4b, 0x79, 0x46, 0x08, 0xd8, 0x39, 0xcf, 0x04, 0xb6, 0xa6, 0xd6,
0x7c, 0xc0, 0xcc, 0xac, 0xd9, 0x39, 0xcd, 0xdf, 0x71, 0xa7, 0x62, 0x7a, 0xd6, 0xac, 0xe0, 0xea,
0x84, 0xbb, 0x15, 0xd3, 0x33, 0x9a, 0x80, 0x7e, 0xc6, 0xf3, 0xf4, 0x20, 0x4a, 0x85, 0x6d, 0xc3,
0xdb, 0x6f, 0x74, 0x07, 0x1c, 0xf1, 0x21, 0x72, 0x55, 0xe2, 0xde, 0xb4, 0x3b, 0x1f, 0x2f, 0x30,
0xf9, 0x79, 0x41, 0xa2, 0xcf, 0x26, 0x54, 0x0b, 0xac, 0xf6, 0xd0, 0x03, 0xe8, 0x5f, 0x78, 0xa9,
0x92, 0xeb, 0x2d, 0xc7, 0xce, 0xd4, 0x9a, 0x0f, 0x17, 0x13, 0x52, 0xd5, 0x20, 0x4d, 0x0d, 0x12,
0x37, 0x35, 0x98, 0xab, 0x5d, 0x76, 0xcb, 0x67, 0x9f, 0x16, 0xe8, 0x99, 0x20, 0x34, 0x04, 0xee,
0x2e, 0x5c, 0x87, 0x9b, 0xd7, 0x10, 0xfe, 0x42, 0x7f, 0xc0, 0x70, 0xcb, 0x68, 0xf2, 0x12, 0x46,
0xb1, 0x1f, 0x04, 0xd0, 0x42, 0x10, 0x8c, 0xb6, 0x9b, 0x28, 0x6e, 0x49, 0x07, 0x8d, 0x01, 0xd0,
0xca, 0x8a, 0x06, 0x34, 0xa6, 0xb0, 0x6b, 0x7e, 0xd1, 0x46, 0x0d, 0xec, 0x26, 0x63, 0xb7, 0x7d,
0x62, 0xfe, 0x8a, 0xc2, 0x5e, 0x9b, 0xd1, 0x10, 0xc7, 0x10, 0x46, 0x13, 0xb6, 0x09, 0x82, 0xa5,
0xff, 0xb8, 0x86, 0x2e, 0xfa, 0x0b, 0x7e, 0x1b, 0xa7, 0x45, 0x7d, 0x2d, 0x31, 0x1a, 0x50, 0x3f,
0xa2, 0x49, 0x4c, 0xa3, 0x18, 0x0e, 0x96, 0x83, 0x37, 0xb7, 0x7e, 0x89, 0xbd, 0x63, 0xca, 0xdd,
0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x35, 0xb7, 0x2a, 0x22, 0xda, 0x01, 0x00, 0x00,
}
......@@ -35,6 +35,8 @@ type Release struct {
Version int32 `protobuf:"varint,7,opt,name=version" json:"version,omitempty"`
// Namespace is the kubernetes namespace of the release.
Namespace string `protobuf:"bytes,8,opt,name=namespace" json:"namespace,omitempty"`
// TestSuite provides results on the last test run on a release
TestSuite *TestSuite `protobuf:"bytes,9,opt,name=test_suite,json=testSuite" json:"test_suite,omitempty"`
}
func (m *Release) Reset() { *m = Release{} }
......@@ -70,6 +72,13 @@ func (m *Release) GetHooks() []*Hook {
return nil
}
func (m *Release) GetTestSuite() *TestSuite {
if m != nil {
return m.TestSuite
}
return nil
}
func init() {
proto.RegisterType((*Release)(nil), "hapi.release.Release")
}
......@@ -77,21 +86,24 @@ func init() {
func init() { proto.RegisterFile("hapi/release/release.proto", fileDescriptor2) }
var fileDescriptor2 = []byte{
// 256 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0xbf, 0x4e, 0xc3, 0x40,
0x0c, 0xc6, 0x95, 0x36, 0x7f, 0x1a, 0xc3, 0x82, 0x07, 0xb0, 0x22, 0x86, 0x88, 0x01, 0x22, 0x86,
0x54, 0x82, 0x37, 0x80, 0x05, 0xd6, 0x1b, 0xd9, 0x8e, 0xe8, 0x42, 0x4e, 0xa5, 0xe7, 0x28, 0x17,
0xf1, 0x2c, 0x3c, 0x2e, 0xba, 0x3f, 0x85, 0x94, 0x2e, 0x4e, 0xec, 0xdf, 0xa7, 0xcf, 0xdf, 0x19,
0xaa, 0x41, 0x8e, 0x7a, 0x3b, 0xa9, 0x4f, 0x25, 0xad, 0x3a, 0x7c, 0xdb, 0x71, 0xe2, 0x99, 0xf1,
0xdc, 0xb1, 0x36, 0xce, 0xaa, 0xab, 0x23, 0xe5, 0xc0, 0xbc, 0x0b, 0xb2, 0x7f, 0x40, 0x9b, 0x9e,
0x8f, 0x40, 0x37, 0xc8, 0x69, 0xde, 0x76, 0x6c, 0x7a, 0xfd, 0x11, 0xc1, 0xe5, 0x12, 0xb8, 0x1a,
0xe6, 0x37, 0xdf, 0x2b, 0x28, 0x44, 0xf0, 0x41, 0x84, 0xd4, 0xc8, 0xbd, 0xa2, 0xa4, 0x4e, 0x9a,
0x52, 0xf8, 0x7f, 0xbc, 0x85, 0xd4, 0xd9, 0xd3, 0xaa, 0x4e, 0x9a, 0xb3, 0x07, 0x6c, 0x97, 0xf9,
0xda, 0x57, 0xd3, 0xb3, 0xf0, 0x1c, 0xef, 0x20, 0xf3, 0xb6, 0xb4, 0xf6, 0xc2, 0x8b, 0x20, 0x0c,
0x9b, 0x9e, 0x5d, 0x15, 0x81, 0xe3, 0x3d, 0xe4, 0x21, 0x18, 0xa5, 0x4b, 0xcb, 0xa8, 0xf4, 0x44,
0x44, 0x05, 0x56, 0xb0, 0xd9, 0x4b, 0xa3, 0x7b, 0x65, 0x67, 0xca, 0x7c, 0xa8, 0xdf, 0x1e, 0x1b,
0xc8, 0xdc, 0x41, 0x2c, 0xe5, 0xf5, 0xfa, 0x34, 0xd9, 0x0b, 0xf3, 0x4e, 0x04, 0x01, 0x12, 0x14,
0x5f, 0x6a, 0xb2, 0x9a, 0x0d, 0x15, 0x75, 0xd2, 0x64, 0xe2, 0xd0, 0xe2, 0x35, 0x94, 0xee, 0x91,
0x76, 0x94, 0x9d, 0xa2, 0x8d, 0x5f, 0xf0, 0x37, 0x78, 0x2a, 0xdf, 0x8a, 0x68, 0xf7, 0x9e, 0xfb,
0x63, 0x3d, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0xc8, 0x8f, 0xec, 0x97, 0xbb, 0x01, 0x00, 0x00,
// 290 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0xbf, 0x4e, 0xc3, 0x30,
0x10, 0xc6, 0x95, 0x36, 0x7f, 0x9a, 0x83, 0x85, 0x1b, 0xa8, 0x15, 0x81, 0x14, 0x31, 0x40, 0xc4,
0x90, 0x4a, 0x20, 0xf1, 0x00, 0xb0, 0xc0, 0x6a, 0x98, 0x58, 0x90, 0x89, 0x1c, 0x62, 0x95, 0xda,
0x51, 0x6c, 0x78, 0x4e, 0x1e, 0x09, 0xf9, 0x4f, 0x68, 0x42, 0x17, 0xc7, 0x77, 0xbf, 0x2f, 0xf7,
0x7d, 0x3e, 0x28, 0x3a, 0xd6, 0x8b, 0xcd, 0xc0, 0x3f, 0x39, 0xd3, 0x7c, 0xfc, 0xd6, 0xfd, 0xa0,
0x8c, 0xc2, 0x63, 0xcb, 0xea, 0xd0, 0x2b, 0xd6, 0x33, 0x65, 0xa7, 0xd4, 0xd6, 0xcb, 0xfe, 0x01,
0x21, 0x5b, 0x15, 0xc0, 0xf9, 0x0c, 0x18, 0xae, 0xcd, 0x9b, 0xfe, 0x12, 0x86, 0xcf, 0xfe, 0x6b,
0x3a, 0x36, 0x98, 0x4d, 0xa3, 0x64, 0x2b, 0x3e, 0x02, 0x38, 0x9d, 0x02, 0x7b, 0xfa, 0xfe, 0xc5,
0xcf, 0x02, 0x32, 0xea, 0xa7, 0x21, 0x42, 0x2c, 0xd9, 0x8e, 0x93, 0xa8, 0x8c, 0xaa, 0x9c, 0xba,
0x3b, 0x5e, 0x42, 0x6c, 0xdd, 0xc9, 0xa2, 0x8c, 0xaa, 0xa3, 0x1b, 0xac, 0xa7, 0xf1, 0xeb, 0x27,
0xd9, 0x2a, 0xea, 0x38, 0x5e, 0x41, 0xe2, 0xc6, 0x92, 0xa5, 0x13, 0x9e, 0x78, 0xa1, 0x77, 0x7a,
0xb0, 0x27, 0xf5, 0x1c, 0xaf, 0x21, 0xf5, 0xc1, 0x48, 0x3c, 0x1d, 0x19, 0x94, 0x8e, 0xd0, 0xa0,
0xc0, 0x02, 0x56, 0x3b, 0x26, 0x45, 0xcb, 0xb5, 0x21, 0x89, 0x0b, 0xf5, 0x57, 0x63, 0x05, 0x89,
0xdd, 0x97, 0x26, 0x69, 0xb9, 0x3c, 0x4c, 0xf6, 0xa8, 0xd4, 0x96, 0x7a, 0x01, 0x12, 0xc8, 0xbe,
0xf9, 0xa0, 0x85, 0x92, 0x24, 0x2b, 0xa3, 0x2a, 0xa1, 0x63, 0x89, 0x67, 0x90, 0xdb, 0x47, 0xea,
0x9e, 0x35, 0x9c, 0xac, 0x9c, 0xc1, 0xbe, 0x81, 0x77, 0x00, 0xfb, 0xfd, 0x92, 0xdc, 0xa5, 0x5d,
0xcf, 0x6d, 0x5e, 0xb8, 0x36, 0xcf, 0x16, 0xd3, 0xdc, 0x8c, 0xd7, 0xfb, 0xfc, 0x35, 0x0b, 0xfc,
0x3d, 0x75, 0x4b, 0xbe, 0xfd, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xea, 0xe6, 0xdb, 0x71, 0x12, 0x02,
0x00, 0x00,
}
// 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:
GetVersionResponse
GetHistoryRequest
GetHistoryResponse
TestReleaseRequest
TestReleaseResponse
*/
package services
......@@ -36,10 +38,11 @@ import fmt "fmt"
import math "math"
import hapi_chart3 "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_release1 "k8s.io/helm/pkg/proto/hapi/release"
import hapi_version "k8s.io/helm/pkg/proto/hapi/version"
import hapi_release4 "k8s.io/helm/pkg/proto/hapi/release"
import (
context "golang.org/x/net/context"
......@@ -153,7 +156,7 @@ type ListReleasesResponse struct {
// Total is the total number of queryable releases.
Total int64 `protobuf:"varint,3,opt,name=total" json:"total,omitempty"`
// 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{} }
......@@ -161,7 +164,7 @@ func (m *ListReleasesResponse) String() string { return proto.Compact
func (*ListReleasesResponse) ProtoMessage() {}
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 {
return m.Releases
}
......@@ -219,7 +222,7 @@ func (*GetReleaseContentRequest) Descriptor() ([]byte, []int) { return fileDescr
// GetReleaseContentResponse is a response containing the contents of a release.
type GetReleaseContentResponse struct {
// 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{} }
......@@ -227,7 +230,7 @@ func (m *GetReleaseContentResponse) String() string { return proto.Co
func (*GetReleaseContentResponse) ProtoMessage() {}
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 {
return m.Release
}
......@@ -278,7 +281,7 @@ func (m *UpdateReleaseRequest) GetValues() *hapi_chart.Config {
// UpdateReleaseResponse is the response to an update request.
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{} }
......@@ -286,7 +289,7 @@ func (m *UpdateReleaseResponse) String() string { return proto.Compac
func (*UpdateReleaseResponse) ProtoMessage() {}
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 {
return m.Release
}
......@@ -318,7 +321,7 @@ func (*RollbackReleaseRequest) Descriptor() ([]byte, []int) { return fileDescrip
// RollbackReleaseResponse is the response to an update request.
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{} }
......@@ -326,7 +329,7 @@ func (m *RollbackReleaseResponse) String() string { return proto.Comp
func (*RollbackReleaseResponse) ProtoMessage() {}
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 {
return m.Release
}
......@@ -381,7 +384,7 @@ func (m *InstallReleaseRequest) GetValues() *hapi_chart.Config {
// InstallReleaseResponse is the response from a release installation.
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{} }
......@@ -389,7 +392,7 @@ func (m *InstallReleaseResponse) String() string { return proto.Compa
func (*InstallReleaseResponse) ProtoMessage() {}
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 {
return m.Release
}
......@@ -416,7 +419,7 @@ func (*UninstallReleaseRequest) Descriptor() ([]byte, []int) { return fileDescri
// UninstallReleaseResponse represents a successful response to an uninstall request.
type UninstallReleaseResponse struct {
// 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 string `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"`
}
......@@ -426,7 +429,7 @@ func (m *UninstallReleaseResponse) String() string { return proto.Com
func (*UninstallReleaseResponse) ProtoMessage() {}
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 {
return m.Release
}
......@@ -443,7 +446,7 @@ func (*GetVersionRequest) ProtoMessage() {}
func (*GetVersionRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} }
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{} }
......@@ -473,7 +476,7 @@ func (*GetHistoryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0,
// GetHistoryResponse is received in response to a GetHistory rpc.
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{} }
......@@ -481,13 +484,44 @@ func (m *GetHistoryResponse) String() string { return proto.CompactTe
func (*GetHistoryResponse) ProtoMessage() {}
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 {
return m.Releases
}
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() {
proto.RegisterType((*ListReleasesRequest)(nil), "hapi.services.tiller.ListReleasesRequest")
proto.RegisterType((*ListSort)(nil), "hapi.services.tiller.ListSort")
......@@ -508,6 +542,8 @@ func init() {
proto.RegisterType((*GetVersionResponse)(nil), "hapi.services.tiller.GetVersionResponse")
proto.RegisterType((*GetHistoryRequest)(nil), "hapi.services.tiller.GetHistoryRequest")
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_SortOrder", ListSort_SortOrder_name, ListSort_SortOrder_value)
}
......@@ -544,6 +580,9 @@ type ReleaseServiceClient interface {
RollbackRelease(ctx context.Context, in *RollbackReleaseRequest, opts ...grpc.CallOption) (*RollbackReleaseResponse, error)
// ReleaseHistory retrieves a releasse's history.
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 {
......@@ -658,6 +697,15 @@ func (c *releaseServiceClient) GetHistory(ctx context.Context, in *GetHistoryReq
return out, nil
}
func (c *releaseServiceClient) RunReleaseTest(ctx context.Context, in *TestReleaseRequest, opts ...grpc.CallOption) (*TestReleaseResponse, error) {
out := new(TestReleaseResponse)
err := grpc.Invoke(ctx, "/hapi.services.tiller.ReleaseService/RunReleaseTest", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for ReleaseService service
type ReleaseServiceServer interface {
......@@ -682,6 +730,9 @@ type ReleaseServiceServer interface {
RollbackRelease(context.Context, *RollbackReleaseRequest) (*RollbackReleaseResponse, error)
// ReleaseHistory retrieves a releasse's history.
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) {
......@@ -853,6 +904,24 @@ func _ReleaseService_GetHistory_Handler(srv interface{}, ctx context.Context, de
return interceptor(ctx, in, info, handler)
}
func _ReleaseService_RunReleaseTest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TestReleaseRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ReleaseServiceServer).RunReleaseTest(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/hapi.services.tiller.ReleaseService/RunReleaseTest",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ReleaseServiceServer).RunReleaseTest(ctx, req.(*TestReleaseRequest))
}
return interceptor(ctx, in, info, handler)
}
var _ReleaseService_serviceDesc = grpc.ServiceDesc{
ServiceName: "hapi.services.tiller.ReleaseService",
HandlerType: (*ReleaseServiceServer)(nil),
......@@ -889,6 +958,10 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
MethodName: "GetHistory",
Handler: _ReleaseService_GetHistory_Handler,
},
{
MethodName: "RunReleaseTest",
Handler: _ReleaseService_RunReleaseTest_Handler,
},
},
Streams: []grpc.StreamDesc{
{
......
......@@ -24,6 +24,7 @@ package environment
import (
"io"
"time"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/engine"
......@@ -31,6 +32,7 @@ import (
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/storage/driver"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
......@@ -135,6 +137,8 @@ type KubeClient interface {
Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error
Build(namespace string, reader io.Reader) (kube.Result, error)
//TODO: insert description
WaitAndGetCompletedPodStatus(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error)
}
// PrintingKubeClient implements KubeClient, but simply prints the reader to
......
......@@ -40,6 +40,7 @@ const (
postUpgrade = "post-upgrade"
preRollback = "pre-rollback"
postRollback = "post-rollback"
releaseTest = "test"
)
var events = map[string]release.Hook_Event{
......@@ -51,6 +52,7 @@ var events = map[string]release.Hook_Event{
postUpgrade: release.Hook_POST_UPGRADE,
preRollback: release.Hook_PRE_ROLLBACK,
postRollback: release.Hook_POST_ROLLBACK,
releaseTest: release.Hook_RELEASE_TEST,
}
type simpleHead struct {
......
......@@ -1064,3 +1064,29 @@ func validateManifest(c environment.KubeClient, ns string, manifest []byte) erro
_, err := c.Build(ns, r)
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