diff --git a/_proto/hapi/release/hook.proto b/_proto/hapi/release/hook.proto index 388c34535..6ae2a71e5 100644 --- a/_proto/hapi/release/hook.proto +++ b/_proto/hapi/release/hook.proto @@ -32,6 +32,7 @@ message Hook { POST_UPGRADE = 6; PRE_ROLLBACK = 7; POST_ROLLBACK = 8; + RELEASE_TEST_SUCCESS = 9; } string name = 1; // Kind is the Kubernetes kind. diff --git a/_proto/hapi/release/release.proto b/_proto/hapi/release/release.proto index 4a6afa0fe..efa2298d1 100644 --- a/_proto/hapi/release/release.proto +++ b/_proto/hapi/release/release.proto @@ -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; + + // LastTestSuiteRun provides results on the last test run on a release + hapi.release.TestSuite last_test_suite_run = 9; } diff --git a/_proto/hapi/release/test_run.proto b/_proto/hapi/release/test_run.proto new file mode 100644 index 000000000..a441e729f --- /dev/null +++ b/_proto/hapi/release/test_run.proto @@ -0,0 +1,36 @@ + +// 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 TestRun { + enum Status { + UNKNOWN = 0; + SUCCESS = 1; + FAILURE = 2; + } + + string name = 1; + Status status = 2; + string info = 3; + google.protobuf.Timestamp started_at = 4; + google.protobuf.Timestamp completed_at = 5; +} diff --git a/_proto/hapi/release/test_suite.proto b/_proto/hapi/release/test_suite.proto new file mode 100644 index 000000000..2f6feb08c --- /dev/null +++ b/_proto/hapi/release/test_suite.proto @@ -0,0 +1,34 @@ +// 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_run.proto"; + +option go_package = "release"; + +// TestSuite comprises of the last run of the pre-defined test suite of a release version +message TestSuite { + // StartedAt indicates the date/time this test suite was kicked off + google.protobuf.Timestamp started_at = 1; + + // CompletedAt indicates the date/time this test suite was completed + google.protobuf.Timestamp completed_at = 2; + + // Results are the results of each segment of the test + repeated hapi.release.TestRun results = 3; +} diff --git a/_proto/hapi/services/tiller.proto b/_proto/hapi/services/tiller.proto index a47ab035b..f835106f0 100644 --- a/_proto/hapi/services/tiller.proto +++ b/_proto/hapi/services/tiller.proto @@ -78,6 +78,10 @@ service ReleaseService { // ReleaseHistory retrieves a releasse's history. rpc GetHistory(GetHistoryRequest) returns (GetHistoryResponse) { } + + // RunReleaseTest executes the tests defined of a named release + rpc RunReleaseTest(TestReleaseRequest) returns (stream TestReleaseResponse) { + } } // ListReleasesRequest requests a list of releases. @@ -108,6 +112,8 @@ message ListReleasesRequest { ListSort.SortOrder sort_order = 5; repeated hapi.release.Status.Code status_codes = 6; + // Namespace is the filter to select releases only from a specific namespace. + string namespace = 7; } // ListSort defines sorting fields on a release list. @@ -304,3 +310,16 @@ 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 represents a message from executing a test +message TestReleaseResponse { + string msg = 1; +} diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index e71c0da8f..93f81eb4a 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -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), diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index 1ce04d0fc..b0a5075a7 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -197,6 +197,10 @@ func (c *fakeReleaseClient) ReleaseHistory(rlsName string, opts ...helm.HistoryO return &rls.GetHistoryResponse{Releases: c.rels}, c.err } +func (c *fakeReleaseClient) RunReleaseTest(rlsName string, opts ...helm.ReleaseTestOption) (<-chan *rls.TestReleaseResponse, <-chan error) { + return nil, nil +} + func (c *fakeReleaseClient) Option(opt ...helm.Option) helm.Interface { return c } diff --git a/cmd/helm/release_testing.go b/cmd/helm/release_testing.go new file mode 100644 index 000000000..0a2ce683d --- /dev/null +++ b/cmd/helm/release_testing.go @@ -0,0 +1,84 @@ +/* +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/spf13/cobra" + + "k8s.io/helm/pkg/helm" +) + +const releaseTestDesc = ` +The 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() (err error) { + c, errc := t.client.RunReleaseTest(t.name, helm.ReleaseTestTimeout(t.timeout)) + + for { + select { + case err := <-errc: + return prettyError(err) + case res, ok := <-c: + if !ok { + break + } + fmt.Fprintf(t.out, res.Msg+"\n") + } + } +} diff --git a/cmd/helm/search/search_test.go b/cmd/helm/search/search_test.go index 5f09f5876..db1e83a74 100644 --- a/cmd/helm/search/search_test.go +++ b/cmd/helm/search/search_test.go @@ -139,6 +139,7 @@ func TestAddRepo_Sort(t *testing.T) { if err != nil { t.Fatal(err) } + SortScore(sr) ch := sr[0] expect := "1.2.3" diff --git a/cmd/helm/status.go b/cmd/helm/status.go index 4e57b8768..f37682a20 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -100,6 +100,7 @@ func PrintStatus(out io.Writer, res *services.GetReleaseStatusResponse) { fmt.Fprintf(w, "RESOURCES:\n%s\n", re.ReplaceAllString(res.Info.Status.Resources, "\t")) w.Flush() } + if len(res.Info.Status.Notes) > 0 { fmt.Fprintf(out, "NOTES:\n%s\n", res.Info.Status.Notes) } diff --git a/docs/examples/nginx/templates/service-test.yaml b/docs/examples/nginx/templates/service-test.yaml new file mode 100644 index 000000000..0accd4c2a --- /dev/null +++ b/docs/examples/nginx/templates/service-test.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{.Release.Name}}-service-test" + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: curl + image: radial/busyboxplus:curl + command: ['curl'] + args: [ '{{ template "fullname" .}}:{{default 80 .Values.httpPort}}' ] + restartPolicy: Never diff --git a/docs/install.md b/docs/install.md old mode 100644 new mode 100755 index 7486904d0..b935fa4f8 --- a/docs/install.md +++ b/docs/install.md @@ -189,7 +189,7 @@ Setting `TILLER_TAG=canary` will get the latest snapshot of master. Because Tiller stores its data in Kubernetes ConfigMaps, you can safely delete and re-install Tiller without worrying about losing any data. The recommended way of deleting Tiller is with `kubectl delete deployment -tiller-deploy --namespace kube-system` +tiller-deploy --namespace kube-system`, or more concisely `helm reset`. Tiller can then be re-installed from the client with: diff --git a/docs/using_helm.md b/docs/using_helm.md old mode 100644 new mode 100755 index 27ea04adb..2ecbee0b6 --- a/docs/using_helm.md +++ b/docs/using_helm.md @@ -324,16 +324,17 @@ cluster. And as we can see above, it shows that our new values from `panda.yaml` were deployed to the cluster. Now, if something does not go as planned during a release, it is easy to -roll back to a previous release. +roll back to a previous release using `helm rollback [RELEASE] [REVISION]`. ```console -$ helm rollback happy-panda --version 1 +$ helm rollback happy-panda 1 ``` The above rolls back our happy-panda to its very first release version. A release version is an incremental revision. Every time an install, upgrade, or rollback happens, the revision number is incremented by 1. -The first revision number is always 1. +The first revision number is always 1. And we can use `helm history [RELEASE]` +to see revision numbers for a certain release. ## Helpful Options for Install/Upgrade/Rollback There are several other helpful options you can specify for customizing the diff --git a/pkg/helm/client.go b/pkg/helm/client.go index 049c6af60..c8ade3467 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -17,6 +17,8 @@ limitations under the License. package helm // import "k8s.io/helm/pkg/helm" import ( + "io" + "golang.org/x/net/context" "google.golang.org/grpc" @@ -244,6 +246,19 @@ func (h *Client) ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.Get return h.history(ctx, req) } +// RunReleaseTest executes a pre-defined test on a release +func (h *Client) RunReleaseTest(rlsName string, opts ...ReleaseTestOption) (<-chan *rls.TestReleaseResponse, <-chan error) { + for _, opt := range opts { + opt(&h.opts) + } + + 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 +371,41 @@ 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) (<-chan *rls.TestReleaseResponse, <-chan error) { + errc := make(chan error, 1) + c, err := grpc.Dial(h.opts.host, grpc.WithInsecure()) + if err != nil { + errc <- err + return nil, errc + } + + ch := make(chan *rls.TestReleaseResponse, 1) + go func() { + defer close(errc) + defer close(ch) + defer c.Close() + + rlc := rls.NewReleaseServiceClient(c) + s, err := rlc.RunReleaseTest(ctx, req) + if err != nil { + errc <- err + return + } + + for { + msg, err := s.Recv() + if err == io.EOF { + return + } + if err != nil { + errc <- err + return + } + ch <- msg + } + }() + + return ch, errc +} diff --git a/pkg/helm/interface.go b/pkg/helm/interface.go index 6b88463b7..bff110b34 100644 --- a/pkg/helm/interface.go +++ b/pkg/helm/interface.go @@ -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) + RunReleaseTest(rlsName string, opts ...ReleaseTestOption) (<-chan *rls.TestReleaseResponse, <-chan error) } diff --git a/pkg/helm/option.go b/pkg/helm/option.go index 6ffb3e12f..422783cda 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -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"). @@ -181,6 +183,13 @@ func DeleteTimeout(timeout int64) DeleteOption { } } +// ReleaseTestTimeout 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) { @@ -371,3 +380,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) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 0022bb71f..850dfe1a9 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -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,7 @@ func perform(c *Client, namespace string, infos Result, fn ResourceActorFunc) er if len(infos) == 0 { return ErrNoObjectsVisited } + for _, info := range infos { if err := fn(info); err != nil { return err @@ -610,3 +612,43 @@ func scrubValidationError(err error) error { } return err } + +// WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase +// and returns said phase (PodSucceeded or PodFailed qualify) +func (c *Client) WaitAndGetCompletedPodPhase(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] + + 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 +} diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index 080e9882c..0c9cf788b 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -18,6 +18,7 @@ package kube import ( "bytes" + "encoding/json" "io" "io/ioutil" "net/http" @@ -37,6 +38,8 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/watch" + watchjson "k8s.io/kubernetes/pkg/watch/json" ) func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser { @@ -44,10 +47,18 @@ func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser { } func newPod(name string) api.Pod { + return newPodWithStatus(name, api.PodStatus{}, "") +} + +func newPodWithStatus(name string, status api.PodStatus, namespace string) api.Pod { + ns := api.NamespaceDefault + if namespace != "" { + ns = namespace + } return api.Pod{ ObjectMeta: api.ObjectMeta{ Name: name, - Namespace: api.NamespaceDefault, + Namespace: ns, }, Spec: api.PodSpec{ Containers: []api.Container{{ @@ -56,6 +67,7 @@ func newPod(name string) api.Pod { Ports: []api.ContainerPort{{Name: "http", ContainerPort: 80}}, }}, }, + Status: status, } } @@ -102,6 +114,32 @@ func (f *fakeReaperFactory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, e return f.reaper, nil } +func newEventResponse(code int, e *watch.Event) (*http.Response, error) { + dispatchedEvent, err := encodeAndMarshalEvent(e) + if err != nil { + return nil, err + } + + header := http.Header{} + header.Set("Content-Type", runtime.ContentTypeJSON) + body := ioutil.NopCloser(bytes.NewReader(dispatchedEvent)) + return &http.Response{StatusCode: 200, Header: header, Body: body}, nil +} + +func encodeAndMarshalEvent(e *watch.Event) ([]byte, error) { + encodedEvent, err := watchjson.Object(testapi.Default.Codec(), e) + if err != nil { + return nil, err + } + + marshaledEvent, err := json.Marshal(encodedEvent) + if err != nil { + return nil, err + } + + return marshaledEvent, nil +} + func TestUpdate(t *testing.T) { listA := newPodList("starfish", "otter", "squid") listB := newPodList("starfish", "otter", "dolphin") @@ -305,6 +343,72 @@ func TestPerform(t *testing.T) { } } +func TestWaitAndGetCompletedPodPhase(t *testing.T) { + tests := []struct { + podPhase api.PodPhase + expectedPhase api.PodPhase + err bool + errMessage string + }{ + { + podPhase: api.PodPending, + expectedPhase: api.PodUnknown, + err: true, + errMessage: "timed out waiting for the condition", + }, { + podPhase: api.PodRunning, + expectedPhase: api.PodUnknown, + err: true, + errMessage: "timed out waiting for the condition", + }, { + podPhase: api.PodSucceeded, + expectedPhase: api.PodSucceeded, + }, { + podPhase: api.PodFailed, + expectedPhase: api.PodFailed, + }, + } + + for _, tt := range tests { + f, tf, codec, ns := cmdtesting.NewAPIFactory() + actions := make(map[string]string) + + var testPodList api.PodList + testPodList.Items = append(testPodList.Items, newPodWithStatus("bestpod", api.PodStatus{Phase: tt.podPhase}, "test")) + + 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 + switch { + case p == "/namespaces/test/pods/bestpod" && m == "GET": + return newResponse(200, &testPodList.Items[0]) + case p == "/watch/namespaces/test/pods/bestpod" && m == "GET": + event := watch.Event{Type: watch.Added, Object: &testPodList.Items[0]} + return newEventResponse(200, &event) + default: + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + return nil, nil + } + }), + } + + c := &Client{Factory: f} + + phase, err := c.WaitAndGetCompletedPodPhase("test", objBody(codec, &testPodList), 1*time.Second) + if (err != nil) != tt.err { + t.Fatalf("Expected error but there was none.") + } + if err != nil && err.Error() != tt.errMessage { + t.Fatalf("Expected error %s, got %s", tt.errMessage, err.Error()) + } + if phase != tt.expectedPhase { + t.Fatalf("Expected pod phase %s, got %s", tt.expectedPhase, phase) + } + } +} + func TestReal(t *testing.T) { t.Skip("This is a live test, comment this line to run") c := New(nil) diff --git a/pkg/proto/hapi/release/hook.pb.go b/pkg/proto/hapi/release/hook.pb.go index 57581b14e..810df99ff 100644 --- a/pkg/proto/hapi/release/hook.pb.go +++ b/pkg/proto/hapi/release/hook.pb.go @@ -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_run.proto + hapi/release/test_suite.proto It has these top-level messages: Hook Info Release Status + TestRun + TestSuite */ package release @@ -38,15 +42,16 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type Hook_Event int32 const ( - Hook_UNKNOWN Hook_Event = 0 - Hook_PRE_INSTALL Hook_Event = 1 - Hook_POST_INSTALL Hook_Event = 2 - Hook_PRE_DELETE Hook_Event = 3 - Hook_POST_DELETE Hook_Event = 4 - Hook_PRE_UPGRADE Hook_Event = 5 - Hook_POST_UPGRADE Hook_Event = 6 - Hook_PRE_ROLLBACK Hook_Event = 7 - Hook_POST_ROLLBACK Hook_Event = 8 + Hook_UNKNOWN Hook_Event = 0 + Hook_PRE_INSTALL Hook_Event = 1 + Hook_POST_INSTALL Hook_Event = 2 + Hook_PRE_DELETE Hook_Event = 3 + Hook_POST_DELETE Hook_Event = 4 + Hook_PRE_UPGRADE Hook_Event = 5 + Hook_POST_UPGRADE Hook_Event = 6 + Hook_PRE_ROLLBACK Hook_Event = 7 + Hook_POST_ROLLBACK Hook_Event = 8 + Hook_RELEASE_TEST_SUCCESS Hook_Event = 9 ) var Hook_Event_name = map[int32]string{ @@ -59,17 +64,19 @@ var Hook_Event_name = map[int32]string{ 6: "POST_UPGRADE", 7: "PRE_ROLLBACK", 8: "POST_ROLLBACK", + 9: "RELEASE_TEST_SUCCESS", } var Hook_Event_value = map[string]int32{ - "UNKNOWN": 0, - "PRE_INSTALL": 1, - "POST_INSTALL": 2, - "PRE_DELETE": 3, - "POST_DELETE": 4, - "PRE_UPGRADE": 5, - "POST_UPGRADE": 6, - "PRE_ROLLBACK": 7, - "POST_ROLLBACK": 8, + "UNKNOWN": 0, + "PRE_INSTALL": 1, + "POST_INSTALL": 2, + "PRE_DELETE": 3, + "POST_DELETE": 4, + "PRE_UPGRADE": 5, + "POST_UPGRADE": 6, + "PRE_ROLLBACK": 7, + "POST_ROLLBACK": 8, + "RELEASE_TEST_SUCCESS": 9, } func (x Hook_Event) String() string { @@ -112,26 +119,27 @@ 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, + // 343 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x4c, 0x90, 0xdf, 0x6e, 0xa2, 0x40, + 0x14, 0xc6, 0x17, 0x41, 0xd0, 0xa3, 0xeb, 0xb2, 0x93, 0x4d, 0x76, 0xe2, 0x4d, 0x8d, 0x57, 0x5e, + 0x0d, 0x8d, 0x4d, 0x1f, 0x00, 0x75, 0xd2, 0x36, 0x12, 0x34, 0x03, 0xa6, 0x49, 0x6f, 0x08, 0xa6, + 0xa3, 0x12, 0x85, 0x21, 0x82, 0x7d, 0x82, 0x3e, 0x55, 0x9f, 0xae, 0x99, 0xe1, 0x4f, 0x7a, 0x77, + 0xf8, 0x9d, 0x1f, 0xdf, 0xcc, 0x37, 0xf0, 0xff, 0x14, 0xe7, 0x89, 0x73, 0xe5, 0x17, 0x1e, 0x17, + 0xdc, 0x39, 0x09, 0x71, 0x26, 0xf9, 0x55, 0x94, 0x02, 0x0d, 0xe5, 0x82, 0xd4, 0x8b, 0xf1, 0xdd, + 0x51, 0x88, 0xe3, 0x85, 0x3b, 0x6a, 0xb7, 0xbf, 0x1d, 0x9c, 0x32, 0x49, 0x79, 0x51, 0xc6, 0x69, + 0x5e, 0xe9, 0xd3, 0x4f, 0x1d, 0x8c, 0x67, 0x21, 0xce, 0x08, 0x81, 0x91, 0xc5, 0x29, 0xc7, 0xda, + 0x44, 0x9b, 0xf5, 0x99, 0x9a, 0x25, 0x3b, 0x27, 0xd9, 0x3b, 0xee, 0x54, 0x4c, 0xce, 0x92, 0xe5, + 0x71, 0x79, 0xc2, 0x7a, 0xc5, 0xe4, 0x8c, 0xc6, 0xd0, 0x4b, 0xe3, 0x2c, 0x39, 0xf0, 0xa2, 0xc4, + 0x86, 0xe2, 0xed, 0x37, 0xba, 0x07, 0x93, 0x7f, 0xf0, 0xac, 0x2c, 0x70, 0x77, 0xa2, 0xcf, 0x46, + 0x73, 0x4c, 0x7e, 0x5e, 0x90, 0xc8, 0xb3, 0x09, 0x95, 0x02, 0xab, 0x3d, 0xf4, 0x08, 0xbd, 0x4b, + 0x5c, 0x94, 0xd1, 0xf5, 0x96, 0x61, 0x73, 0xa2, 0xcd, 0x06, 0xf3, 0x31, 0xa9, 0x6a, 0x90, 0xa6, + 0x06, 0x09, 0x9b, 0x1a, 0xcc, 0x92, 0x2e, 0xbb, 0x65, 0xd3, 0x2f, 0x0d, 0xba, 0x2a, 0x08, 0x0d, + 0xc0, 0xda, 0xf9, 0x6b, 0x7f, 0xf3, 0xea, 0xdb, 0xbf, 0xd0, 0x1f, 0x18, 0x6c, 0x19, 0x8d, 0x5e, + 0xfc, 0x20, 0x74, 0x3d, 0xcf, 0xd6, 0x90, 0x0d, 0xc3, 0xed, 0x26, 0x08, 0x5b, 0xd2, 0x41, 0x23, + 0x00, 0xa9, 0xac, 0xa8, 0x47, 0x43, 0x6a, 0xeb, 0xea, 0x17, 0x69, 0xd4, 0xc0, 0x68, 0x32, 0x76, + 0xdb, 0x27, 0xe6, 0xae, 0xa8, 0xdd, 0x6d, 0x33, 0x1a, 0x62, 0x2a, 0xc2, 0x68, 0xc4, 0x36, 0x9e, + 0xb7, 0x70, 0x97, 0x6b, 0xdb, 0x42, 0x7f, 0xe1, 0xb7, 0x72, 0x5a, 0xd4, 0x43, 0x18, 0xfe, 0x31, + 0xea, 0x51, 0x37, 0xa0, 0x51, 0x48, 0x83, 0x30, 0x0a, 0x76, 0xcb, 0x25, 0x0d, 0x02, 0xbb, 0xbf, + 0xe8, 0xbf, 0x59, 0xf5, 0x8b, 0xec, 0x4d, 0x55, 0xf2, 0xe1, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xdf, + 0xef, 0x1c, 0xfd, 0xe2, 0x01, 0x00, 0x00, } diff --git a/pkg/proto/hapi/release/info.pb.go b/pkg/proto/hapi/release/info.pb.go index a73dcab2f..2a0ec863d 100644 --- a/pkg/proto/hapi/release/info.pb.go +++ b/pkg/proto/hapi/release/info.pb.go @@ -22,7 +22,7 @@ type Info struct { // Deleted tracks when this object was deleted. Deleted *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=deleted" json:"deleted,omitempty"` // Description is human-friendly "log entry" about this release. - Description string `protobuf:"bytes,5,opt,name=Description" json:"Description,omitempty"` + Description string `protobuf:"bytes,5,opt,name=Description,json=description" json:"Description,omitempty"` } func (m *Info) Reset() { *m = Info{} } @@ -65,20 +65,20 @@ func init() { func init() { proto.RegisterFile("hapi/release/info.proto", fileDescriptor1) } var fileDescriptor1 = []byte{ - // 235 bytes of a gzipped FileDescriptorProto + // 236 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x8f, 0x31, 0x4f, 0xc3, 0x30, - 0x10, 0x85, 0x95, 0x52, 0x5a, 0xd5, 0x6d, 0x19, 0x2c, 0x24, 0x42, 0x16, 0x22, 0xa6, 0x0e, 0xc8, + 0x10, 0x85, 0x95, 0x52, 0x5a, 0xd5, 0x69, 0x19, 0x2c, 0x24, 0x42, 0x16, 0x22, 0xa6, 0x0e, 0xc8, 0x91, 0x80, 0x1d, 0x81, 0xba, 0xb0, 0x06, 0x26, 0x16, 0xe4, 0xe2, 0x73, 0xb1, 0xe4, 0xe6, 0x2c, - 0xfb, 0x3a, 0xf0, 0x2f, 0xf8, 0xc9, 0xa8, 0xb6, 0x83, 0xd2, 0xa9, 0xab, 0xbf, 0xf7, 0x3e, 0xbf, - 0x63, 0x57, 0xdf, 0xd2, 0x99, 0xc6, 0x83, 0x05, 0x19, 0xa0, 0x31, 0x9d, 0x46, 0xe1, 0x3c, 0x12, - 0xf2, 0xc5, 0x01, 0x88, 0x0c, 0xaa, 0x9b, 0x2d, 0xe2, 0xd6, 0x42, 0x13, 0xd9, 0x66, 0xaf, 0x1b, - 0x32, 0x3b, 0x08, 0x24, 0x77, 0x2e, 0xc5, 0xab, 0xeb, 0x23, 0x4f, 0x20, 0x49, 0xfb, 0x90, 0xd0, - 0xed, 0xef, 0x88, 0x8d, 0x5f, 0x3b, 0x8d, 0xfc, 0x8e, 0x4d, 0x12, 0x28, 0x8b, 0xba, 0x58, 0xcd, - 0xef, 0x2f, 0xc5, 0xf0, 0x0f, 0xf1, 0x16, 0x59, 0x9b, 0x33, 0xfc, 0x99, 0x5d, 0x68, 0xe3, 0x03, - 0x7d, 0x2a, 0x70, 0x16, 0x7f, 0x40, 0x95, 0xa3, 0xd8, 0xaa, 0x44, 0xda, 0x22, 0xfa, 0x2d, 0xe2, - 0xbd, 0xdf, 0xd2, 0x2e, 0x63, 0x63, 0x9d, 0x0b, 0xfc, 0x89, 0x2d, 0xad, 0x1c, 0x1a, 0xce, 0x4e, - 0x1a, 0x16, 0x87, 0xc2, 0xbf, 0xe0, 0x91, 0x4d, 0x15, 0x58, 0x20, 0x50, 0xe5, 0xf8, 0x64, 0xb5, - 0x8f, 0xf2, 0x9a, 0xcd, 0xd7, 0x10, 0xbe, 0xbc, 0x71, 0x64, 0xb0, 0x2b, 0xcf, 0xeb, 0x62, 0x35, - 0x6b, 0x87, 0x4f, 0x2f, 0xb3, 0x8f, 0x69, 0xbe, 0x7a, 0x33, 0x89, 0xa6, 0x87, 0xbf, 0x00, 0x00, - 0x00, 0xff, 0xff, 0x1a, 0x52, 0x8f, 0x9c, 0x89, 0x01, 0x00, 0x00, + 0xfb, 0x3a, 0xf0, 0x2f, 0xf8, 0xc9, 0xa8, 0xb6, 0x03, 0x65, 0xea, 0xea, 0xef, 0xbd, 0xcf, 0xef, + 0xd8, 0xc5, 0xa7, 0x74, 0xa6, 0xf5, 0x60, 0x41, 0x06, 0x68, 0x4d, 0xaf, 0x51, 0x38, 0x8f, 0x84, + 0x7c, 0xbe, 0x07, 0x22, 0x83, 0xfa, 0x6a, 0x83, 0xb8, 0xb1, 0xd0, 0x46, 0xb6, 0xde, 0xe9, 0x96, + 0xcc, 0x16, 0x02, 0xc9, 0xad, 0x4b, 0xf1, 0xfa, 0xf2, 0x9f, 0x27, 0x90, 0xa4, 0x5d, 0x48, 0xe8, + 0xfa, 0x7b, 0xc4, 0xc6, 0xcf, 0xbd, 0x46, 0x7e, 0xc3, 0x26, 0x09, 0x54, 0x45, 0x53, 0x2c, 0xcb, + 0xdb, 0x73, 0x71, 0xf8, 0x87, 0x78, 0x89, 0xac, 0xcb, 0x19, 0xfe, 0xc8, 0xce, 0xb4, 0xf1, 0x81, + 0xde, 0x15, 0x38, 0x8b, 0x5f, 0xa0, 0xaa, 0x51, 0x6c, 0xd5, 0x22, 0x6d, 0x11, 0xc3, 0x16, 0xf1, + 0x3a, 0x6c, 0xe9, 0x16, 0xb1, 0xb1, 0xca, 0x05, 0xfe, 0xc0, 0x16, 0x56, 0x1e, 0x1a, 0x4e, 0x8e, + 0x1a, 0xe6, 0xfb, 0xc2, 0xaf, 0xe0, 0x9e, 0x4d, 0x15, 0x58, 0x20, 0x50, 0xd5, 0xf8, 0x68, 0x75, + 0x88, 0xf2, 0x86, 0x95, 0x2b, 0x08, 0x1f, 0xde, 0x38, 0x32, 0xd8, 0x57, 0xa7, 0x4d, 0xb1, 0x9c, + 0x75, 0xa5, 0xfa, 0x7b, 0x7a, 0x9a, 0xbd, 0x4d, 0xf3, 0xd5, 0xeb, 0x49, 0x34, 0xdd, 0xfd, 0x04, + 0x00, 0x00, 0xff, 0xff, 0x1e, 0x2a, 0x57, 0x7d, 0x89, 0x01, 0x00, 0x00, } diff --git a/pkg/proto/hapi/release/release.pb.go b/pkg/proto/hapi/release/release.pb.go index 72255e3e2..471a03c79 100644 --- a/pkg/proto/hapi/release/release.pb.go +++ b/pkg/proto/hapi/release/release.pb.go @@ -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"` + // LastTestSuiteRun provides results on the last test run on a release + LastTestSuiteRun *TestSuite `protobuf:"bytes,9,opt,name=last_test_suite_run,json=lastTestSuiteRun" json:"last_test_suite_run,omitempty"` } func (m *Release) Reset() { *m = Release{} } @@ -70,6 +72,13 @@ func (m *Release) GetHooks() []*Hook { return nil } +func (m *Release) GetLastTestSuiteRun() *TestSuite { + if m != nil { + return m.LastTestSuiteRun + } + 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, + // 300 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0xcd, 0x4e, 0x84, 0x30, + 0x10, 0x80, 0xc3, 0x2e, 0x3f, 0x4b, 0xf5, 0xa0, 0x63, 0xe2, 0x36, 0x44, 0x13, 0xe2, 0x41, 0x89, + 0x07, 0x36, 0xd1, 0x37, 0xd0, 0xc4, 0xe8, 0xb5, 0x7a, 0xf2, 0x42, 0x2a, 0x29, 0xd2, 0xec, 0x6e, + 0x4b, 0x68, 0xf1, 0x79, 0x7d, 0x14, 0xd3, 0x9f, 0x15, 0xd0, 0x4b, 0x61, 0xe6, 0xfb, 0x3a, 0x33, + 0x1d, 0x94, 0xb5, 0xb4, 0xe3, 0x9b, 0x9e, 0xed, 0x18, 0x55, 0xec, 0xf0, 0x2d, 0xbb, 0x5e, 0x6a, + 0x09, 0xc7, 0x86, 0x95, 0x3e, 0x97, 0xad, 0x67, 0x66, 0x2b, 0xe5, 0xd6, 0x69, 0x7f, 0x00, 0x17, + 0x8d, 0xf4, 0xe0, 0x72, 0x06, 0x34, 0x53, 0xba, 0x52, 0x03, 0xd7, 0x6c, 0x76, 0xaf, 0x6e, 0x69, + 0xaf, 0x37, 0xb5, 0x14, 0x0d, 0xff, 0xf4, 0xe0, 0x7c, 0x0a, 0xcc, 0xe9, 0xf2, 0x57, 0xdf, 0x0b, + 0x94, 0x10, 0x57, 0x0d, 0x00, 0x85, 0x82, 0xee, 0x19, 0x0e, 0xf2, 0xa0, 0x48, 0x89, 0xfd, 0x87, + 0x6b, 0x14, 0x9a, 0xee, 0x78, 0x91, 0x07, 0xc5, 0xd1, 0x1d, 0x94, 0xd3, 0xf1, 0xcb, 0x17, 0xd1, + 0x48, 0x62, 0x39, 0xdc, 0xa0, 0xc8, 0x96, 0xc5, 0x4b, 0x2b, 0x9e, 0x3a, 0xd1, 0x75, 0x7a, 0x34, + 0x27, 0x71, 0x1c, 0x6e, 0x51, 0xec, 0x06, 0xc3, 0xe1, 0xb4, 0xa4, 0x37, 0x2d, 0x21, 0xde, 0x80, + 0x0c, 0xad, 0xf6, 0x54, 0xf0, 0x86, 0x29, 0x8d, 0x23, 0x3b, 0xd4, 0x6f, 0x0c, 0x05, 0x8a, 0xcc, + 0xbe, 0x14, 0x8e, 0xf3, 0xe5, 0xff, 0xc9, 0x9e, 0xa5, 0xdc, 0x12, 0x27, 0x00, 0x46, 0xc9, 0x17, + 0xeb, 0x15, 0x97, 0x02, 0x27, 0x79, 0x50, 0x44, 0xe4, 0x10, 0xc2, 0x05, 0x4a, 0xcd, 0x23, 0x55, + 0x47, 0x6b, 0x86, 0x57, 0xb6, 0xc1, 0x98, 0x80, 0x27, 0x74, 0xb6, 0xa3, 0x4a, 0x57, 0xe3, 0x92, + 0xab, 0x7e, 0x10, 0x38, 0xb5, 0x63, 0xaf, 0xe7, 0xfd, 0xde, 0x98, 0xd2, 0xaf, 0x46, 0x21, 0x27, + 0xe6, 0xce, 0x18, 0x0e, 0xe2, 0x21, 0x7d, 0x4f, 0xbc, 0xf6, 0x11, 0xdb, 0xa5, 0xdf, 0xff, 0x04, + 0x00, 0x00, 0xff, 0xff, 0x85, 0x06, 0x87, 0x2b, 0x22, 0x02, 0x00, 0x00, } diff --git a/pkg/proto/hapi/release/test_run.pb.go b/pkg/proto/hapi/release/test_run.pb.go new file mode 100644 index 000000000..51b3e72f9 --- /dev/null +++ b/pkg/proto/hapi/release/test_run.pb.go @@ -0,0 +1,94 @@ +// Code generated by protoc-gen-go. +// source: hapi/release/test_run.proto +// DO NOT EDIT! + +package release + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import google_protobuf "github.com/golang/protobuf/ptypes/timestamp" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type TestRun_Status int32 + +const ( + TestRun_UNKNOWN TestRun_Status = 0 + TestRun_SUCCESS TestRun_Status = 1 + TestRun_FAILURE TestRun_Status = 2 +) + +var TestRun_Status_name = map[int32]string{ + 0: "UNKNOWN", + 1: "SUCCESS", + 2: "FAILURE", +} +var TestRun_Status_value = map[string]int32{ + "UNKNOWN": 0, + "SUCCESS": 1, + "FAILURE": 2, +} + +func (x TestRun_Status) String() string { + return proto.EnumName(TestRun_Status_name, int32(x)) +} +func (TestRun_Status) EnumDescriptor() ([]byte, []int) { return fileDescriptor4, []int{0, 0} } + +type TestRun struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Status TestRun_Status `protobuf:"varint,2,opt,name=status,enum=hapi.release.TestRun_Status" json:"status,omitempty"` + Info string `protobuf:"bytes,3,opt,name=info" json:"info,omitempty"` + StartedAt *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=started_at,json=startedAt" json:"started_at,omitempty"` + CompletedAt *google_protobuf.Timestamp `protobuf:"bytes,5,opt,name=completed_at,json=completedAt" json:"completed_at,omitempty"` +} + +func (m *TestRun) Reset() { *m = TestRun{} } +func (m *TestRun) String() string { return proto.CompactTextString(m) } +func (*TestRun) ProtoMessage() {} +func (*TestRun) Descriptor() ([]byte, []int) { return fileDescriptor4, []int{0} } + +func (m *TestRun) GetStartedAt() *google_protobuf.Timestamp { + if m != nil { + return m.StartedAt + } + return nil +} + +func (m *TestRun) GetCompletedAt() *google_protobuf.Timestamp { + if m != nil { + return m.CompletedAt + } + return nil +} + +func init() { + proto.RegisterType((*TestRun)(nil), "hapi.release.TestRun") + proto.RegisterEnum("hapi.release.TestRun_Status", TestRun_Status_name, TestRun_Status_value) +} + +func init() { proto.RegisterFile("hapi/release/test_run.proto", fileDescriptor4) } + +var fileDescriptor4 = []byte{ + // 265 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x8f, 0x41, 0x4b, 0xfb, 0x40, + 0x14, 0xc4, 0xff, 0xc9, 0xbf, 0x26, 0x64, 0x53, 0x24, 0xec, 0x29, 0x54, 0xc1, 0xd0, 0x53, 0x4e, + 0xbb, 0x50, 0xbd, 0x78, 0xf0, 0x10, 0x4b, 0x05, 0x51, 0x22, 0x6c, 0x1a, 0x04, 0x2f, 0x65, 0xab, + 0xaf, 0x35, 0x90, 0x64, 0x43, 0xf6, 0xe5, 0x8b, 0xf8, 0x89, 0x65, 0x93, 0xad, 0x78, 0xf3, 0xf6, + 0x86, 0xf9, 0xcd, 0x30, 0x8f, 0x5c, 0x7c, 0xca, 0xae, 0xe2, 0x3d, 0xd4, 0x20, 0x35, 0x70, 0x04, + 0x8d, 0xbb, 0x7e, 0x68, 0x59, 0xd7, 0x2b, 0x54, 0x74, 0x6e, 0x4c, 0x66, 0xcd, 0xc5, 0xd5, 0x51, + 0xa9, 0x63, 0x0d, 0x7c, 0xf4, 0xf6, 0xc3, 0x81, 0x63, 0xd5, 0x80, 0x46, 0xd9, 0x74, 0x13, 0xbe, + 0xfc, 0x72, 0x89, 0xbf, 0x05, 0x8d, 0x62, 0x68, 0x29, 0x25, 0xb3, 0x56, 0x36, 0x10, 0x3b, 0x89, + 0x93, 0x06, 0x62, 0xbc, 0xe9, 0x0d, 0xf1, 0x34, 0x4a, 0x1c, 0x74, 0xec, 0x26, 0x4e, 0x7a, 0xbe, + 0xba, 0x64, 0xbf, 0xfb, 0x99, 0x8d, 0xb2, 0x62, 0x64, 0x84, 0x65, 0x4d, 0x53, 0xd5, 0x1e, 0x54, + 0xfc, 0x7f, 0x6a, 0x32, 0x37, 0xbd, 0x25, 0x44, 0xa3, 0xec, 0x11, 0x3e, 0x76, 0x12, 0xe3, 0x59, + 0xe2, 0xa4, 0xe1, 0x6a, 0xc1, 0xa6, 0x7d, 0xec, 0xb4, 0x8f, 0x6d, 0x4f, 0xfb, 0x44, 0x60, 0xe9, + 0x0c, 0xe9, 0x1d, 0x99, 0xbf, 0xab, 0xa6, 0xab, 0xc1, 0x86, 0xcf, 0xfe, 0x0c, 0x87, 0x3f, 0x7c, + 0x86, 0x4b, 0x4e, 0xbc, 0x69, 0x1f, 0x0d, 0x89, 0x5f, 0xe6, 0x4f, 0xf9, 0xcb, 0x6b, 0x1e, 0xfd, + 0x33, 0xa2, 0x28, 0xd7, 0xeb, 0x4d, 0x51, 0x44, 0x8e, 0x11, 0x0f, 0xd9, 0xe3, 0x73, 0x29, 0x36, + 0x91, 0x7b, 0x1f, 0xbc, 0xf9, 0xf6, 0xc1, 0xbd, 0x37, 0x96, 0x5f, 0x7f, 0x07, 0x00, 0x00, 0xff, + 0xff, 0x8d, 0xb9, 0xce, 0x57, 0x74, 0x01, 0x00, 0x00, +} diff --git a/pkg/proto/hapi/release/test_suite.pb.go b/pkg/proto/hapi/release/test_suite.pb.go new file mode 100644 index 000000000..27fe45ac5 --- /dev/null +++ b/pkg/proto/hapi/release/test_suite.pb.go @@ -0,0 +1,74 @@ +// 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 { + // StartedAt indicates the date/time this test suite was kicked off + StartedAt *google_protobuf.Timestamp `protobuf:"bytes,1,opt,name=started_at,json=startedAt" json:"started_at,omitempty"` + // CompletedAt indicates the date/time this test suite was completed + CompletedAt *google_protobuf.Timestamp `protobuf:"bytes,2,opt,name=completed_at,json=completedAt" json:"completed_at,omitempty"` + // Results are the results of each segment of the test + Results []*TestRun `protobuf:"bytes,3,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) GetStartedAt() *google_protobuf.Timestamp { + if m != nil { + return m.StartedAt + } + return nil +} + +func (m *TestSuite) GetCompletedAt() *google_protobuf.Timestamp { + if m != nil { + return m.CompletedAt + } + return nil +} + +func (m *TestSuite) GetResults() []*TestRun { + if m != nil { + return m.Results + } + return nil +} + +func init() { + proto.RegisterType((*TestSuite)(nil), "hapi.release.TestSuite") +} + +func init() { proto.RegisterFile("hapi/release/test_suite.proto", fileDescriptor5) } + +var fileDescriptor5 = []byte{ + // 207 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x8f, 0xc1, 0x4a, 0x86, 0x40, + 0x14, 0x85, 0x31, 0x21, 0x71, 0x74, 0x35, 0x10, 0x88, 0x11, 0x49, 0x2b, 0x57, 0x33, 0x60, 0xab, + 0x16, 0x2d, 0xec, 0x11, 0xcc, 0x55, 0x1b, 0x19, 0xeb, 0x66, 0xc2, 0xe8, 0x0c, 0x73, 0xef, 0xbc, + 0x5a, 0xcf, 0x17, 0xea, 0x18, 0x41, 0x8b, 0x7f, 0xfd, 0x7d, 0xe7, 0x9c, 0x7b, 0xd9, 0xdd, 0x97, + 0xb2, 0xb3, 0x74, 0xa0, 0x41, 0x21, 0x48, 0x02, 0xa4, 0x01, 0xfd, 0x4c, 0x20, 0xac, 0x33, 0x64, + 0x78, 0xbe, 0x61, 0x11, 0x70, 0x79, 0x3f, 0x19, 0x33, 0x69, 0x90, 0x3b, 0x1b, 0xfd, 0xa7, 0xa4, + 0x79, 0x01, 0x24, 0xb5, 0xd8, 0x43, 0x2f, 0x6f, 0xff, 0xb7, 0x39, 0xbf, 0x1e, 0xf0, 0xe1, 0x3b, + 0x62, 0x69, 0x0f, 0x48, 0xaf, 0x5b, 0x3f, 0x7f, 0x62, 0x0c, 0x49, 0x39, 0x82, 0x8f, 0x41, 0x51, + 0x11, 0x55, 0x51, 0x9d, 0x35, 0xa5, 0x38, 0x06, 0xc4, 0x39, 0x20, 0xfa, 0x73, 0xa0, 0x4b, 0x83, + 0xdd, 0x12, 0x7f, 0x66, 0xf9, 0xbb, 0x59, 0xac, 0x86, 0x10, 0xbe, 0xba, 0x18, 0xce, 0x7e, 0xfd, + 0x96, 0xb8, 0x64, 0x89, 0x03, 0xf4, 0x9a, 0xb0, 0x88, 0xab, 0xb8, 0xce, 0x9a, 0x1b, 0xf1, 0xf7, + 0x4b, 0xb1, 0xdd, 0xd8, 0xf9, 0xb5, 0x3b, 0xad, 0x97, 0xf4, 0x2d, 0x09, 0x6c, 0xbc, 0xde, 0xcb, + 0x1f, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x8c, 0x59, 0x65, 0x4f, 0x37, 0x01, 0x00, 0x00, +} diff --git a/pkg/proto/hapi/services/tiller.pb.go b/pkg/proto/hapi/services/tiller.pb.go index f6601f216..701c0b626 100644 --- a/pkg/proto/hapi/services/tiller.pb.go +++ b/pkg/proto/hapi/services/tiller.pb.go @@ -28,6 +28,8 @@ It has these top-level messages: GetVersionResponse GetHistoryRequest GetHistoryResponse + TestReleaseRequest + TestReleaseResponse */ package services @@ -36,7 +38,7 @@ 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" @@ -155,7 +157,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{} } @@ -163,7 +165,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 } @@ -221,7 +223,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{} } @@ -229,7 +231,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 } @@ -280,7 +282,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{} } @@ -288,7 +290,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 } @@ -320,7 +322,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{} } @@ -328,7 +330,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 } @@ -383,7 +385,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{} } @@ -391,7 +393,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 } @@ -418,7 +420,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"` } @@ -428,7 +430,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 } @@ -445,7 +447,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{} } @@ -475,7 +477,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{} } @@ -483,13 +485,37 @@ 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) + Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,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 init() { proto.RegisterType((*ListReleasesRequest)(nil), "hapi.services.tiller.ListReleasesRequest") proto.RegisterType((*ListSort)(nil), "hapi.services.tiller.ListSort") @@ -510,6 +536,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) } @@ -546,6 +574,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) (ReleaseService_RunReleaseTestClient, error) } type releaseServiceClient struct { @@ -660,6 +691,38 @@ func (c *releaseServiceClient) GetHistory(ctx context.Context, in *GetHistoryReq return out, nil } +func (c *releaseServiceClient) RunReleaseTest(ctx context.Context, in *TestReleaseRequest, opts ...grpc.CallOption) (ReleaseService_RunReleaseTestClient, error) { + stream, err := grpc.NewClientStream(ctx, &_ReleaseService_serviceDesc.Streams[1], c.cc, "/hapi.services.tiller.ReleaseService/RunReleaseTest", opts...) + if err != nil { + return nil, err + } + x := &releaseServiceRunReleaseTestClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type ReleaseService_RunReleaseTestClient interface { + Recv() (*TestReleaseResponse, error) + grpc.ClientStream +} + +type releaseServiceRunReleaseTestClient struct { + grpc.ClientStream +} + +func (x *releaseServiceRunReleaseTestClient) Recv() (*TestReleaseResponse, error) { + m := new(TestReleaseResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + // Server API for ReleaseService service type ReleaseServiceServer interface { @@ -684,6 +747,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(*TestReleaseRequest, ReleaseService_RunReleaseTestServer) error } func RegisterReleaseServiceServer(s *grpc.Server, srv ReleaseServiceServer) { @@ -855,6 +921,27 @@ func _ReleaseService_GetHistory_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } +func _ReleaseService_RunReleaseTest_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(TestReleaseRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ReleaseServiceServer).RunReleaseTest(m, &releaseServiceRunReleaseTestServer{stream}) +} + +type ReleaseService_RunReleaseTestServer interface { + Send(*TestReleaseResponse) error + grpc.ServerStream +} + +type releaseServiceRunReleaseTestServer struct { + grpc.ServerStream +} + +func (x *releaseServiceRunReleaseTestServer) Send(m *TestReleaseResponse) error { + return x.ServerStream.SendMsg(m) +} + var _ReleaseService_serviceDesc = grpc.ServiceDesc{ ServiceName: "hapi.services.tiller.ReleaseService", HandlerType: (*ReleaseServiceServer)(nil), @@ -898,6 +985,11 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{ Handler: _ReleaseService_ListReleases_Handler, ServerStreams: true, }, + { + StreamName: "RunReleaseTest", + Handler: _ReleaseService_RunReleaseTest_Handler, + ServerStreams: true, + }, }, Metadata: fileDescriptor0, } @@ -905,74 +997,77 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("hapi/services/tiller.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 1092 bytes of a gzipped FileDescriptorProto + // 1141 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x57, 0xdd, 0x6e, 0xe3, 0x44, - 0x14, 0xae, 0xf3, 0xe3, 0x24, 0xa7, 0x3f, 0xa4, 0xb3, 0x6d, 0xe3, 0x5a, 0x80, 0x82, 0x11, 0x6c, - 0x58, 0xd8, 0x14, 0xc2, 0x15, 0x12, 0x42, 0xea, 0x66, 0xa3, 0xb4, 0x50, 0xb2, 0x92, 0x43, 0x17, - 0x89, 0x0b, 0x22, 0x37, 0x99, 0x6c, 0xcd, 0x3a, 0x9e, 0xe0, 0x99, 0x94, 0xcd, 0x2d, 0x77, 0xbc, - 0x06, 0x77, 0xf0, 0x30, 0x3c, 0x0b, 0x8f, 0x80, 0x3c, 0x3f, 0xae, 0xed, 0xda, 0x59, 0x93, 0x9b, - 0xd8, 0x33, 0xe7, 0xcc, 0x77, 0xce, 0xf9, 0xe6, 0xfc, 0x38, 0x60, 0xde, 0x3a, 0x4b, 0xf7, 0x8c, - 0xe2, 0xe0, 0xce, 0x9d, 0x62, 0x7a, 0xc6, 0x5c, 0xcf, 0xc3, 0x41, 0x77, 0x19, 0x10, 0x46, 0xd0, - 0x51, 0x28, 0xeb, 0x2a, 0x59, 0x57, 0xc8, 0xcc, 0x13, 0x7e, 0x62, 0x7a, 0xeb, 0x04, 0x4c, 0xfc, - 0x0a, 0x6d, 0xb3, 0x15, 0xdf, 0x27, 0xfe, 0xdc, 0x7d, 0x25, 0x05, 0xc2, 0x44, 0x80, 0x3d, 0xec, - 0x50, 0xac, 0x9e, 0x89, 0x43, 0x4a, 0xe6, 0xfa, 0x73, 0x22, 0x05, 0xa7, 0x09, 0x01, 0x65, 0x0e, - 0x5b, 0xd1, 0x04, 0xde, 0x1d, 0x0e, 0xa8, 0x4b, 0x7c, 0xf5, 0x14, 0x32, 0xeb, 0xcf, 0x12, 0x3c, - 0xba, 0x72, 0x29, 0xb3, 0xc5, 0x41, 0x6a, 0xe3, 0x5f, 0x57, 0x98, 0x32, 0x74, 0x04, 0x55, 0xcf, - 0x5d, 0xb8, 0xcc, 0xd0, 0xda, 0x5a, 0xa7, 0x6c, 0x8b, 0x05, 0x3a, 0x01, 0x9d, 0xcc, 0xe7, 0x14, - 0x33, 0xa3, 0xd4, 0xd6, 0x3a, 0x0d, 0x5b, 0xae, 0xd0, 0x37, 0x50, 0xa3, 0x24, 0x60, 0x93, 0x9b, - 0xb5, 0x51, 0x6e, 0x6b, 0x9d, 0x83, 0xde, 0x47, 0xdd, 0x2c, 0x2a, 0xba, 0xa1, 0xa5, 0x31, 0x09, - 0x58, 0x37, 0xfc, 0x79, 0xb6, 0xb6, 0x75, 0xca, 0x9f, 0x21, 0xee, 0xdc, 0xf5, 0x18, 0x0e, 0x8c, - 0x8a, 0xc0, 0x15, 0x2b, 0x34, 0x04, 0xe0, 0xb8, 0x24, 0x98, 0xe1, 0xc0, 0xa8, 0x72, 0xe8, 0x4e, - 0x01, 0xe8, 0x17, 0xa1, 0xbe, 0xdd, 0xa0, 0xea, 0x15, 0x7d, 0x0d, 0x7b, 0x82, 0x92, 0xc9, 0x94, - 0xcc, 0x30, 0x35, 0xf4, 0x76, 0xb9, 0x73, 0xd0, 0x3b, 0x15, 0x50, 0x8a, 0xe1, 0xb1, 0x20, 0xad, - 0x4f, 0x66, 0xd8, 0xde, 0x15, 0xea, 0xe1, 0x3b, 0xb5, 0x7e, 0x86, 0xba, 0x82, 0xb7, 0x7a, 0xa0, - 0x0b, 0xe7, 0xd1, 0x2e, 0xd4, 0xae, 0x47, 0xdf, 0x8d, 0x5e, 0xfc, 0x38, 0x6a, 0xee, 0xa0, 0x3a, - 0x54, 0x46, 0xe7, 0xdf, 0x0f, 0x9a, 0x1a, 0x3a, 0x84, 0xfd, 0xab, 0xf3, 0xf1, 0x0f, 0x13, 0x7b, - 0x70, 0x35, 0x38, 0x1f, 0x0f, 0x9e, 0x37, 0x4b, 0xd6, 0xfb, 0xd0, 0x88, 0xbc, 0x42, 0x35, 0x28, - 0x9f, 0x8f, 0xfb, 0xe2, 0xc8, 0xf3, 0xc1, 0xb8, 0xdf, 0xd4, 0xac, 0x3f, 0x34, 0x38, 0x4a, 0x5e, - 0x02, 0x5d, 0x12, 0x9f, 0xe2, 0xf0, 0x16, 0xa6, 0x64, 0xe5, 0x47, 0xb7, 0xc0, 0x17, 0x08, 0x41, - 0xc5, 0xc7, 0x6f, 0xd4, 0x1d, 0xf0, 0xf7, 0x50, 0x93, 0x11, 0xe6, 0x78, 0x9c, 0xff, 0xb2, 0x2d, - 0x16, 0xe8, 0x0b, 0xa8, 0xcb, 0xe0, 0xa8, 0x51, 0x69, 0x97, 0x3b, 0xbb, 0xbd, 0xe3, 0x64, 0xc8, - 0xd2, 0xa2, 0x1d, 0xa9, 0x59, 0x43, 0x68, 0x0d, 0xb1, 0xf2, 0x44, 0x30, 0xa2, 0x72, 0x22, 0xb4, - 0xeb, 0x2c, 0x30, 0x77, 0x26, 0xb4, 0xeb, 0x2c, 0x30, 0x32, 0xa0, 0x26, 0x13, 0x8a, 0xbb, 0x53, - 0xb5, 0xd5, 0xd2, 0x62, 0x60, 0x3c, 0x04, 0x92, 0x71, 0x65, 0x21, 0x7d, 0x0c, 0x95, 0x30, 0x9d, - 0x39, 0xcc, 0x6e, 0x0f, 0x25, 0xfd, 0xbc, 0xf4, 0xe7, 0xc4, 0xe6, 0x72, 0xf4, 0x2e, 0x34, 0x42, - 0x7d, 0xba, 0x74, 0xa6, 0x98, 0x47, 0xdb, 0xb0, 0xef, 0x37, 0xac, 0x8b, 0xb8, 0xd5, 0x3e, 0xf1, - 0x19, 0xf6, 0xd9, 0x76, 0xfe, 0x5f, 0xc1, 0x69, 0x06, 0x92, 0x0c, 0xe0, 0x0c, 0x6a, 0xd2, 0x35, - 0x8e, 0x96, 0xcb, 0xab, 0xd2, 0xb2, 0xfe, 0x2e, 0xc1, 0xd1, 0xf5, 0x72, 0xe6, 0x30, 0xac, 0x44, - 0x1b, 0x9c, 0x7a, 0x0c, 0x55, 0xde, 0x16, 0x24, 0x17, 0x87, 0x02, 0x5b, 0xf4, 0x8e, 0x7e, 0xf8, - 0x6b, 0x0b, 0x39, 0x7a, 0x02, 0xfa, 0x9d, 0xe3, 0xad, 0x30, 0xe5, 0x44, 0x44, 0xac, 0x49, 0x4d, - 0xde, 0x53, 0x6c, 0xa9, 0x81, 0x5a, 0x50, 0x9b, 0x05, 0xeb, 0x49, 0xb0, 0xf2, 0x79, 0x91, 0xd5, - 0x6d, 0x7d, 0x16, 0xac, 0xed, 0x95, 0x8f, 0x3e, 0x84, 0xfd, 0x99, 0x4b, 0x9d, 0x1b, 0x0f, 0x4f, - 0x6e, 0x09, 0x79, 0x4d, 0x79, 0x9d, 0xd5, 0xed, 0x3d, 0xb9, 0x79, 0x11, 0xee, 0x21, 0x33, 0xcc, - 0xa4, 0x69, 0x80, 0x1d, 0x86, 0x0d, 0x9d, 0xcb, 0xa3, 0x75, 0xc8, 0x21, 0x73, 0x17, 0x98, 0xac, - 0x98, 0x51, 0xe3, 0xd9, 0xa7, 0x96, 0xe8, 0x03, 0xd8, 0x0b, 0x30, 0xc5, 0x6c, 0x22, 0xbd, 0xac, - 0xf3, 0x93, 0xbb, 0x7c, 0xef, 0xa5, 0x70, 0x0b, 0x41, 0xe5, 0x37, 0xc7, 0x65, 0x46, 0x83, 0x8b, - 0xf8, 0xbb, 0x75, 0x01, 0xc7, 0x29, 0xae, 0xb6, 0xa5, 0xfd, 0x1f, 0x0d, 0x4e, 0x6c, 0xe2, 0x79, - 0x37, 0xce, 0xf4, 0x75, 0x01, 0xe2, 0x63, 0x1c, 0x95, 0x36, 0x73, 0x54, 0xce, 0xe0, 0x28, 0x96, - 0x4b, 0x95, 0x44, 0x2e, 0x25, 0xd8, 0xab, 0xe6, 0xb3, 0xa7, 0x27, 0xd9, 0x53, 0xd4, 0xd4, 0x62, - 0xd4, 0x7c, 0x0b, 0xad, 0x07, 0xf1, 0x6c, 0x4b, 0xce, 0x5f, 0x25, 0x38, 0xbe, 0xf4, 0x29, 0x73, - 0x3c, 0x2f, 0xc5, 0x4d, 0x94, 0x80, 0x5a, 0xe1, 0x04, 0x2c, 0xfd, 0x9f, 0x04, 0x2c, 0x27, 0xc8, - 0x55, 0x37, 0x51, 0x89, 0xdd, 0x44, 0xa1, 0xa4, 0x4c, 0xb4, 0x02, 0x3d, 0xd5, 0x0a, 0xd0, 0x7b, - 0x00, 0x01, 0x5e, 0x51, 0x3c, 0xe1, 0xe0, 0x82, 0xc4, 0x06, 0xdf, 0x19, 0xc9, 0xca, 0x57, 0xbc, - 0xd7, 0xb3, 0x79, 0x8f, 0xa7, 0xe4, 0x25, 0x9c, 0xa4, 0xa9, 0xda, 0x96, 0xf6, 0xdf, 0x35, 0x68, - 0x5d, 0xfb, 0x6e, 0x26, 0xf1, 0x59, 0x49, 0xf9, 0x80, 0x8a, 0x52, 0x06, 0x15, 0x47, 0x50, 0x5d, - 0xae, 0x82, 0x57, 0x58, 0x52, 0x2b, 0x16, 0xf1, 0x18, 0x2b, 0x89, 0x18, 0xad, 0x09, 0x18, 0x0f, - 0x7d, 0xd8, 0x32, 0xa2, 0xd0, 0xeb, 0xa8, 0x75, 0x37, 0x44, 0x9b, 0xb6, 0x1e, 0xc1, 0xe1, 0x10, - 0xb3, 0x97, 0xa2, 0x00, 0x64, 0x78, 0xd6, 0x00, 0x50, 0x7c, 0xf3, 0xde, 0x9e, 0xdc, 0x4a, 0xda, - 0x53, 0x5f, 0x2a, 0x4a, 0x5f, 0x69, 0x59, 0x5f, 0x71, 0xec, 0x0b, 0x97, 0x32, 0x12, 0xac, 0x37, - 0x51, 0xd7, 0x84, 0xf2, 0xc2, 0x79, 0x23, 0x3b, 0x7b, 0xf8, 0x6a, 0x0d, 0xb9, 0x07, 0xd1, 0x51, - 0xe9, 0x41, 0x7c, 0x4e, 0x6a, 0x85, 0xe6, 0x64, 0xef, 0xdf, 0x1a, 0x1c, 0xa8, 0xe1, 0x26, 0x3e, - 0x45, 0x90, 0x0b, 0x7b, 0xf1, 0x29, 0x8e, 0x3e, 0xc9, 0xff, 0x52, 0x49, 0x7d, 0x6e, 0x99, 0x4f, - 0x8a, 0xa8, 0x0a, 0x67, 0xad, 0x9d, 0xcf, 0x35, 0x44, 0xa1, 0x99, 0x1e, 0xae, 0xe8, 0x69, 0x36, - 0x46, 0xce, 0x34, 0x37, 0xbb, 0x45, 0xd5, 0x95, 0x59, 0x74, 0xc7, 0x69, 0x4f, 0x4e, 0x44, 0xf4, - 0x56, 0x98, 0xe4, 0x10, 0x36, 0xcf, 0x0a, 0xeb, 0x47, 0x76, 0x7f, 0x81, 0xfd, 0xc4, 0x38, 0x40, - 0x39, 0x6c, 0x65, 0xcd, 0x57, 0xf3, 0xd3, 0x42, 0xba, 0x91, 0xad, 0x05, 0x1c, 0x24, 0xeb, 0x1c, - 0xe5, 0x00, 0x64, 0x36, 0x4e, 0xf3, 0xb3, 0x62, 0xca, 0x91, 0x39, 0x0a, 0xcd, 0x74, 0x19, 0xe6, - 0xdd, 0x63, 0x4e, 0xcb, 0xc8, 0xbb, 0xc7, 0xbc, 0xea, 0xb6, 0x76, 0x90, 0x03, 0x70, 0x5f, 0x85, - 0xe8, 0x71, 0xee, 0x85, 0x24, 0x8b, 0xd7, 0xec, 0xbc, 0x5d, 0x31, 0x32, 0xb1, 0x84, 0x77, 0x52, - 0x63, 0x0a, 0xe5, 0x50, 0x93, 0x3d, 0x9d, 0xcd, 0xa7, 0x05, 0xb5, 0x53, 0x41, 0xc9, 0xc2, 0xde, - 0x10, 0x54, 0xb2, 0x6b, 0x6c, 0x08, 0x2a, 0xd5, 0x23, 0xac, 0x9d, 0x67, 0xf0, 0x53, 0x5d, 0xe9, - 0xdd, 0xe8, 0xfc, 0xef, 0xd3, 0x97, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0xbd, 0x3d, 0xae, 0x84, - 0x0f, 0x0e, 0x00, 0x00, + 0x14, 0xae, 0xf3, 0x9f, 0xd3, 0x1f, 0xd2, 0xe9, 0x9f, 0x6b, 0x01, 0x2a, 0x46, 0xd0, 0xec, 0xc2, + 0xa6, 0x10, 0xae, 0x90, 0x10, 0x52, 0xdb, 0x8d, 0xda, 0x42, 0xe9, 0x4a, 0xce, 0x76, 0x91, 0xb8, + 0x20, 0x72, 0x93, 0x49, 0x6b, 0xd6, 0xf1, 0x04, 0xcf, 0xa4, 0x6c, 0x6f, 0xb9, 0xe3, 0x35, 0xb8, + 0x83, 0x87, 0xe1, 0x05, 0x78, 0x19, 0x34, 0x7f, 0xae, 0x27, 0xb5, 0x5b, 0x93, 0x9b, 0x78, 0x66, + 0xce, 0x99, 0xef, 0x9c, 0xf3, 0x9d, 0x33, 0x67, 0x26, 0xe0, 0xdc, 0xf8, 0xd3, 0xe0, 0x80, 0xe2, + 0xf8, 0x36, 0x18, 0x62, 0x7a, 0xc0, 0x82, 0x30, 0xc4, 0x71, 0x67, 0x1a, 0x13, 0x46, 0xd0, 0x26, + 0x97, 0x75, 0xb4, 0xac, 0x23, 0x65, 0xce, 0xb6, 0xd8, 0x31, 0xbc, 0xf1, 0x63, 0x26, 0x7f, 0xa5, + 0xb6, 0xb3, 0x93, 0x5e, 0x27, 0xd1, 0x38, 0xb8, 0x56, 0x02, 0x69, 0x22, 0xc6, 0x21, 0xf6, 0x29, + 0xd6, 0x5f, 0x63, 0x93, 0x96, 0x05, 0xd1, 0x98, 0x28, 0xc1, 0xae, 0x21, 0xa0, 0xcc, 0x67, 0x33, + 0x6a, 0xe0, 0xdd, 0xe2, 0x98, 0x06, 0x24, 0xd2, 0x5f, 0x29, 0x73, 0xff, 0x2c, 0xc1, 0xc6, 0x79, + 0x40, 0x99, 0x27, 0x37, 0x52, 0x0f, 0xff, 0x3a, 0xc3, 0x94, 0xa1, 0x4d, 0xa8, 0x86, 0xc1, 0x24, + 0x60, 0xb6, 0xb5, 0x67, 0xb5, 0xcb, 0x9e, 0x9c, 0xa0, 0x6d, 0xa8, 0x91, 0xf1, 0x98, 0x62, 0x66, + 0x97, 0xf6, 0xac, 0x76, 0xd3, 0x53, 0x33, 0xf4, 0x2d, 0xd4, 0x29, 0x89, 0xd9, 0xe0, 0xea, 0xce, + 0x2e, 0xef, 0x59, 0xed, 0xb5, 0xee, 0x27, 0x9d, 0x2c, 0x2a, 0x3a, 0xdc, 0x52, 0x9f, 0xc4, 0xac, + 0xc3, 0x7f, 0x8e, 0xee, 0xbc, 0x1a, 0x15, 0x5f, 0x8e, 0x3b, 0x0e, 0x42, 0x86, 0x63, 0xbb, 0x22, + 0x71, 0xe5, 0x0c, 0x9d, 0x00, 0x08, 0x5c, 0x12, 0x8f, 0x70, 0x6c, 0x57, 0x05, 0x74, 0xbb, 0x00, + 0xf4, 0x2b, 0xae, 0xef, 0x35, 0xa9, 0x1e, 0xa2, 0x6f, 0x60, 0x45, 0x52, 0x32, 0x18, 0x92, 0x11, + 0xa6, 0x76, 0x6d, 0xaf, 0xdc, 0x5e, 0xeb, 0xee, 0x4a, 0x28, 0xcd, 0x70, 0x5f, 0x92, 0x76, 0x4c, + 0x46, 0xd8, 0x5b, 0x96, 0xea, 0x7c, 0x4c, 0xdd, 0x9f, 0xa1, 0xa1, 0xe1, 0xdd, 0x2e, 0xd4, 0xa4, + 0xf3, 0x68, 0x19, 0xea, 0x97, 0x17, 0xdf, 0x5f, 0xbc, 0xfa, 0xf1, 0xa2, 0xb5, 0x84, 0x1a, 0x50, + 0xb9, 0x38, 0xfc, 0xa1, 0xd7, 0xb2, 0xd0, 0x3a, 0xac, 0x9e, 0x1f, 0xf6, 0x5f, 0x0f, 0xbc, 0xde, + 0x79, 0xef, 0xb0, 0xdf, 0x7b, 0xd9, 0x2a, 0xb9, 0x1f, 0x42, 0x33, 0xf1, 0x0a, 0xd5, 0xa1, 0x7c, + 0xd8, 0x3f, 0x96, 0x5b, 0x5e, 0xf6, 0xfa, 0xc7, 0x2d, 0xcb, 0xfd, 0xc3, 0x82, 0x4d, 0x33, 0x09, + 0x74, 0x4a, 0x22, 0x8a, 0x79, 0x16, 0x86, 0x64, 0x16, 0x25, 0x59, 0x10, 0x13, 0x84, 0xa0, 0x12, + 0xe1, 0x77, 0x3a, 0x07, 0x62, 0xcc, 0x35, 0x19, 0x61, 0x7e, 0x28, 0xf8, 0x2f, 0x7b, 0x72, 0x82, + 0xbe, 0x84, 0x86, 0x0a, 0x8e, 0xda, 0x95, 0xbd, 0x72, 0x7b, 0xb9, 0xbb, 0x65, 0x86, 0xac, 0x2c, + 0x7a, 0x89, 0x9a, 0x7b, 0x02, 0x3b, 0x27, 0x58, 0x7b, 0x22, 0x19, 0xd1, 0x35, 0xc1, 0xed, 0xfa, + 0x13, 0x2c, 0x9c, 0xe1, 0x76, 0xfd, 0x09, 0x46, 0x36, 0xd4, 0x55, 0x41, 0x09, 0x77, 0xaa, 0x9e, + 0x9e, 0xba, 0x0c, 0xec, 0x87, 0x40, 0x2a, 0xae, 0x2c, 0xa4, 0x4f, 0xa1, 0xc2, 0xcb, 0x59, 0xc0, + 0x2c, 0x77, 0x91, 0xe9, 0xe7, 0x59, 0x34, 0x26, 0x9e, 0x90, 0xa3, 0xf7, 0xa1, 0xc9, 0xf5, 0xe9, + 0xd4, 0x1f, 0x62, 0x11, 0x6d, 0xd3, 0xbb, 0x5f, 0x70, 0x4f, 0xd3, 0x56, 0x8f, 0x49, 0xc4, 0x70, + 0xc4, 0x16, 0xf3, 0xff, 0x1c, 0x76, 0x33, 0x90, 0x54, 0x00, 0x07, 0x50, 0x57, 0xae, 0x09, 0xb4, + 0x5c, 0x5e, 0xb5, 0x96, 0xfb, 0x77, 0x09, 0x36, 0x2f, 0xa7, 0x23, 0x9f, 0x61, 0x2d, 0x7a, 0xc4, + 0xa9, 0x7d, 0xa8, 0x8a, 0xb6, 0xa0, 0xb8, 0x58, 0x97, 0xd8, 0xb2, 0x77, 0x1c, 0xf3, 0x5f, 0x4f, + 0xca, 0xd1, 0x73, 0xa8, 0xdd, 0xfa, 0xe1, 0x0c, 0x53, 0x41, 0x44, 0xc2, 0x9a, 0xd2, 0x14, 0x3d, + 0xc5, 0x53, 0x1a, 0x68, 0x07, 0xea, 0xa3, 0xf8, 0x6e, 0x10, 0xcf, 0x22, 0x71, 0xc8, 0x1a, 0x5e, + 0x6d, 0x14, 0xdf, 0x79, 0xb3, 0x08, 0x7d, 0x0c, 0xab, 0xa3, 0x80, 0xfa, 0x57, 0x21, 0x1e, 0xdc, + 0x10, 0xf2, 0x96, 0x8a, 0x73, 0xd6, 0xf0, 0x56, 0xd4, 0xe2, 0x29, 0x5f, 0x43, 0x0e, 0xaf, 0xa4, + 0x61, 0x8c, 0x7d, 0x86, 0xed, 0x9a, 0x90, 0x27, 0x73, 0xce, 0x21, 0x0b, 0x26, 0x98, 0xcc, 0x98, + 0x5d, 0x17, 0xd5, 0xa7, 0xa7, 0xe8, 0x23, 0x58, 0x89, 0x31, 0xc5, 0x6c, 0xa0, 0xbc, 0x6c, 0x88, + 0x9d, 0xcb, 0x62, 0xed, 0x8d, 0x74, 0x0b, 0x41, 0xe5, 0x37, 0x3f, 0x60, 0x76, 0x53, 0x88, 0xc4, + 0xd8, 0x3d, 0x85, 0xad, 0x39, 0xae, 0x16, 0xa5, 0xfd, 0x1f, 0x0b, 0xb6, 0x3d, 0x12, 0x86, 0x57, + 0xfe, 0xf0, 0x6d, 0x01, 0xe2, 0x53, 0x1c, 0x95, 0x1e, 0xe7, 0xa8, 0x9c, 0xc1, 0x51, 0xaa, 0x96, + 0x2a, 0x46, 0x2d, 0x19, 0xec, 0x55, 0xf3, 0xd9, 0xab, 0x99, 0xec, 0x69, 0x6a, 0xea, 0x29, 0x6a, + 0xbe, 0x83, 0x9d, 0x07, 0xf1, 0x2c, 0x4a, 0xce, 0x5f, 0x25, 0xd8, 0x3a, 0x8b, 0x28, 0xf3, 0xc3, + 0x70, 0x8e, 0x9b, 0xa4, 0x00, 0xad, 0xc2, 0x05, 0x58, 0xfa, 0x3f, 0x05, 0x58, 0x36, 0xc8, 0xd5, + 0x99, 0xa8, 0xa4, 0x32, 0x51, 0xa8, 0x28, 0x8d, 0x56, 0x50, 0x9b, 0x6b, 0x05, 0xe8, 0x03, 0x80, + 0x18, 0xcf, 0x28, 0x1e, 0x08, 0x70, 0x49, 0x62, 0x53, 0xac, 0x5c, 0xa8, 0x93, 0xaf, 0x79, 0x6f, + 0x64, 0xf3, 0x9e, 0x2e, 0xc9, 0x33, 0xd8, 0x9e, 0xa7, 0x6a, 0x51, 0xda, 0x7f, 0xb7, 0x60, 0xe7, + 0x32, 0x0a, 0x32, 0x89, 0xcf, 0x2a, 0xca, 0x07, 0x54, 0x94, 0x32, 0xa8, 0xd8, 0x84, 0xea, 0x74, + 0x16, 0x5f, 0x63, 0x45, 0xad, 0x9c, 0xa4, 0x63, 0xac, 0x18, 0x31, 0xba, 0x03, 0xb0, 0x1f, 0xfa, + 0xb0, 0x60, 0x44, 0xdc, 0xeb, 0xa4, 0x75, 0x37, 0x65, 0x9b, 0x76, 0x37, 0x60, 0xfd, 0x04, 0xb3, + 0x37, 0xf2, 0x00, 0xa8, 0xf0, 0xdc, 0x1e, 0xa0, 0xf4, 0xe2, 0xbd, 0x3d, 0xb5, 0x64, 0xda, 0xd3, + 0x2f, 0x15, 0xad, 0x9f, 0xb4, 0xe6, 0xaf, 0x05, 0xf6, 0x69, 0x40, 0x19, 0x89, 0xef, 0x1e, 0xa3, + 0xae, 0x05, 0xe5, 0x89, 0xff, 0x4e, 0x75, 0x76, 0x3e, 0x74, 0x4f, 0x84, 0x07, 0xc9, 0x56, 0xe5, + 0x41, 0xfa, 0x9e, 0xb4, 0x8a, 0xdd, 0x93, 0x47, 0x80, 0x5e, 0xe3, 0xe4, 0xca, 0x7e, 0xe2, 0x8a, + 0xd1, 0x49, 0x28, 0x99, 0x49, 0xd8, 0x87, 0x0d, 0x03, 0x43, 0x79, 0xc3, 0xbd, 0xa6, 0xd7, 0x0a, + 0x83, 0x0f, 0xbb, 0xff, 0x36, 0x60, 0x4d, 0xdf, 0xa4, 0xf2, 0xdd, 0x83, 0x02, 0x58, 0x49, 0x3f, + 0x19, 0xd0, 0xb3, 0xfc, 0x67, 0xd1, 0xdc, 0xdb, 0xce, 0x79, 0x5e, 0x44, 0x55, 0xfa, 0xe2, 0x2e, + 0x7d, 0x61, 0x21, 0x0a, 0xad, 0xf9, 0x9b, 0x1c, 0xbd, 0xc8, 0xc6, 0xc8, 0x79, 0x3a, 0x38, 0x9d, + 0xa2, 0xea, 0xda, 0x2c, 0xba, 0x15, 0x39, 0x36, 0xaf, 0x5f, 0xf4, 0x24, 0x8c, 0x79, 0xe3, 0x3b, + 0x07, 0x85, 0xf5, 0x13, 0xbb, 0xbf, 0xc0, 0xaa, 0x71, 0xf7, 0xa0, 0x1c, 0xb6, 0xb2, 0x2e, 0x73, + 0xe7, 0xb3, 0x42, 0xba, 0x89, 0xad, 0x09, 0xac, 0x99, 0x4d, 0x05, 0xe5, 0x00, 0x64, 0x76, 0x69, + 0xe7, 0xf3, 0x62, 0xca, 0x89, 0x39, 0x0a, 0xad, 0xf9, 0x33, 0x9f, 0x97, 0xc7, 0x9c, 0xfe, 0x94, + 0x97, 0xc7, 0xbc, 0x56, 0xe2, 0x2e, 0x21, 0x1f, 0xe0, 0xfe, 0xc8, 0xa3, 0xfd, 0xdc, 0x84, 0x98, + 0x9d, 0xc2, 0x69, 0x3f, 0xad, 0x98, 0x98, 0x98, 0xc2, 0x7b, 0x73, 0x77, 0x22, 0xca, 0xa1, 0x26, + 0xfb, 0x29, 0xe0, 0xbc, 0x28, 0xa8, 0x3d, 0x17, 0x94, 0xea, 0x22, 0x8f, 0x04, 0x65, 0xb6, 0xa8, + 0x47, 0x82, 0x9a, 0x6b, 0x48, 0xee, 0x12, 0x0a, 0x60, 0xcd, 0x9b, 0x45, 0xca, 0x34, 0xef, 0x12, + 0x28, 0x67, 0xf7, 0xc3, 0x2e, 0xe4, 0x3c, 0x2b, 0xa0, 0x79, 0x7f, 0xbe, 0x8f, 0xe0, 0xa7, 0x86, + 0x56, 0xbd, 0xaa, 0x89, 0xbf, 0x85, 0x5f, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0xe7, 0xd5, 0x3c, + 0xdf, 0xe7, 0x0e, 0x00, 0x00, } diff --git a/pkg/releasetesting/environment.go b/pkg/releasetesting/environment.go new file mode 100644 index 000000000..0b409856f --- /dev/null +++ b/pkg/releasetesting/environment.go @@ -0,0 +1,68 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package releasetesting + +import ( + "fmt" + + "k8s.io/helm/pkg/proto/hapi/services" + "k8s.io/helm/pkg/tiller/environment" +) + +// Environment encapsulates information about where test suite executes and returns results +type Environment struct { + Namespace string + KubeClient environment.KubeClient + Stream services.ReleaseService_RunReleaseTestServer + Timeout int64 +} + +func streamRunning(name string, stream services.ReleaseService_RunReleaseTestServer) error { + msg := "RUNNING: " + name + err := streamMessage(msg, stream) + return err +} + +func streamError(info string, stream services.ReleaseService_RunReleaseTestServer) error { + msg := "ERROR: " + info + err := streamMessage(msg, stream) + return err +} + +func streamFailed(name, namespace string, stream services.ReleaseService_RunReleaseTestServer) error { + msg := fmt.Sprintf("FAILED: %s, run `kubectl logs %s --namespace %s` for more info", name, name, namespace) + err := streamMessage(msg, stream) + return err +} + +func streamSuccess(name string, stream services.ReleaseService_RunReleaseTestServer) error { + msg := fmt.Sprintf("PASSED: %s", name) + err := streamMessage(msg, stream) + return err +} + +func streamUnknown(name, info string, stream services.ReleaseService_RunReleaseTestServer) error { + msg := fmt.Sprintf("UNKNOWN: %s: %s", name, info) + err := streamMessage(msg, stream) + return err +} + +func streamMessage(msg string, stream services.ReleaseService_RunReleaseTestServer) error { + resp := &services.TestReleaseResponse{Msg: msg} + err := stream.Send(resp) + return err +} diff --git a/pkg/releasetesting/test_suite.go b/pkg/releasetesting/test_suite.go new file mode 100644 index 000000000..6bcf1c96d --- /dev/null +++ b/pkg/releasetesting/test_suite.go @@ -0,0 +1,204 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package releasetesting + +import ( + "bytes" + "fmt" + "log" + "strings" + "time" + + "github.com/ghodss/yaml" + "github.com/golang/protobuf/ptypes/timestamp" + "k8s.io/kubernetes/pkg/api" + + "k8s.io/helm/pkg/proto/hapi/release" + util "k8s.io/helm/pkg/releaseutil" + "k8s.io/helm/pkg/timeconv" +) + +// TestSuite what tests are run, results, and metadata +type TestSuite struct { + StartedAt *timestamp.Timestamp + CompletedAt *timestamp.Timestamp + TestManifests []string + Results []*release.TestRun +} + +type test struct { + manifest string + result *release.TestRun +} + +// NewTestSuite takes a release object and returns a TestSuite object with test definitions +// extracted from the release +func NewTestSuite(rel *release.Release) (*TestSuite, error) { + testManifests, err := extractTestManifestsFromHooks(rel.Hooks, rel.Name) + if err != nil { + return nil, err + } + + results := []*release.TestRun{} + + return &TestSuite{ + TestManifests: testManifests, + Results: results, + }, nil +} + +// Run executes tests in a test suite and stores a result within the context of a given environment +func (t *TestSuite) Run(env *Environment) error { + t.StartedAt = timeconv.Now() + + for _, testManifest := range t.TestManifests { + test, err := newTest(testManifest) + if err != nil { + return err + } + + test.result.StartedAt = timeconv.Now() + if err := streamRunning(test.result.Name, env.Stream); err != nil { + return err + } + + resourceCreated := true + if err := t.createTestPod(test, env); err != nil { + resourceCreated = false + if streamErr := streamError(test.result.Info, env.Stream); streamErr != nil { + return err + } + } + + resourceCleanExit := true + status := api.PodUnknown + if resourceCreated { + status, err = t.getTestPodStatus(test, env) + if err != nil { + resourceCleanExit = false + if streamErr := streamUnknown(test.result.Name, test.result.Info, env.Stream); streamErr != nil { + return streamErr + } + } + } + + if resourceCreated && resourceCleanExit && status == api.PodSucceeded { + test.result.Status = release.TestRun_SUCCESS + if streamErr := streamSuccess(test.result.Name, env.Stream); streamErr != nil { + return streamErr + } + } else if resourceCreated && resourceCleanExit && status == api.PodFailed { + test.result.Status = release.TestRun_FAILURE + if streamErr := streamFailed(test.result.Name, env.Namespace, env.Stream); streamErr != nil { + return err + } + } + + test.result.CompletedAt = timeconv.Now() + t.Results = append(t.Results, test.result) + } + + t.CompletedAt = timeconv.Now() + return nil +} + +// NOTE: may want to move this function to pkg/tiller in the future +func filterHooksForTestHooks(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 + } + + for _, h := range hooks { + for _, e := range h.Events { + if e == release.Hook_RELEASE_TEST_SUCCESS { + testHooks = append(testHooks, h) + continue + } + } + } + + if len(testHooks) == 0 { + return nil, notFoundErr + } + + return testHooks, nil +} + +// NOTE: may want to move this function to pkg/tiller in the future +func extractTestManifestsFromHooks(hooks []*release.Hook, releaseName string) ([]string, error) { + testHooks, err := filterHooksForTestHooks(hooks, releaseName) + if err != nil { + return nil, err + } + + tests := []string{} + for _, h := range testHooks { + individualTests := util.SplitManifests(h.Manifest) + for _, t := range individualTests { + tests = append(tests, t) + } + } + return tests, nil +} + +func newTest(testManifest string) (*test, error) { + var sh util.SimpleHead + err := yaml.Unmarshal([]byte(testManifest), &sh) + if err != nil { + return nil, err + } + + if sh.Kind != "Pod" { + return nil, fmt.Errorf("%s is not a pod", sh.Metadata.Name) + } + + name := strings.TrimSuffix(sh.Metadata.Name, ",") + return &test{ + manifest: testManifest, + result: &release.TestRun{ + Name: name, + }, + }, nil +} + +func (t *TestSuite) createTestPod(test *test, env *Environment) error { + b := bytes.NewBufferString(test.manifest) + if err := env.KubeClient.Create(env.Namespace, b, env.Timeout, false); err != nil { + log.Printf(err.Error()) + test.result.Info = err.Error() + test.result.Status = release.TestRun_FAILURE + return err + } + + return nil +} + +func (t *TestSuite) getTestPodStatus(test *test, env *Environment) (api.PodPhase, error) { + b := bytes.NewBufferString(test.manifest) + status, err := env.KubeClient.WaitAndGetCompletedPodPhase(env.Namespace, b, time.Duration(env.Timeout)*time.Second) + if err != nil { + log.Printf("Error getting status for pod %s: %s", test.result.Name, err) + test.result.Info = err.Error() + test.result.Status = release.TestRun_UNKNOWN + return status, err + } + + return status, err +} diff --git a/pkg/releasetesting/test_suite_test.go b/pkg/releasetesting/test_suite_test.go new file mode 100644 index 000000000..5f1151e5f --- /dev/null +++ b/pkg/releasetesting/test_suite_test.go @@ -0,0 +1,247 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package releasetesting + +import ( + "io" + "os" + "testing" + "time" + + "github.com/golang/protobuf/ptypes/timestamp" + "golang.org/x/net/context" + grpc "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + "k8s.io/kubernetes/pkg/api" + + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + "k8s.io/helm/pkg/storage" + "k8s.io/helm/pkg/storage/driver" + tillerEnv "k8s.io/helm/pkg/tiller/environment" +) + +func TestNewTestSuite(t *testing.T) { + rel := releaseStub() + + _, err := NewTestSuite(rel) + if err != nil { + t.Errorf("%s", err) + } +} + +func TestRun(t *testing.T) { + + ts := testSuiteFixture() + if err := ts.Run(testEnvFixture()); err != nil { + t.Errorf("%s", err) + } + + if ts.StartedAt == nil { + t.Errorf("Expected StartedAt to not be nil. Got: %v", ts.StartedAt) + } + + if ts.CompletedAt == nil { + t.Errorf("Expected CompletedAt to not be nil. Got: %v", ts.CompletedAt) + } + + if len(ts.Results) != 1 { + t.Errorf("Expected 1 test result. Got %v", len(ts.Results)) + } + + result := ts.Results[0] + if result.StartedAt == nil { + t.Errorf("Expected test StartedAt to not be nil. Got: %v", result.StartedAt) + } + + if result.CompletedAt == nil { + t.Errorf("Expected test CompletedAt to not be nil. Got: %v", result.CompletedAt) + } + + if result.Name != "finding-nemo" { + t.Errorf("Expected test name to be finding-nemo. Got: %v", result.Name) + } + + if result.Status != release.TestRun_SUCCESS { + t.Errorf("Expected test result to be successful, got: %v", result.Status) + } + +} + +func TestGetTestPodStatus(t *testing.T) { + ts := testSuiteFixture() + + status, err := ts.getTestPodStatus(testFixture(), testEnvFixture()) + if err != nil { + t.Errorf("Expected getTestPodStatus not to return err, Got: %s", err) + } + + if status != api.PodSucceeded { + t.Errorf("Expected pod status to be succeeded, Got: %s ", status) + } + +} + +func TestExtractTestManifestsFromHooks(t *testing.T) { + rel := releaseStub() + testManifests, err := extractTestManifestsFromHooks(rel.Hooks, rel.Name) + if err != nil { + t.Errorf("Expected no error, Got: %s", err) + } + + if len(testManifests) != 1 { + t.Errorf("Expected 1 test manifest, Got: %v", len(testManifests)) + } +} + +func chartStub() *chart.Chart { + return &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "nemo", + }, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithTestHook)}, + }, + } +} + +var manifestWithTestHook = ` +apiVersion: v1 +kind: Pod +metadata: + name: finding-nemo, + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: nemo-test + image: fake-image + cmd: fake-command +` +var manifestWithInstallHooks = `apiVersion: v1 +kind: ConfigMap +metadata: + name: test-cm + annotations: + "helm.sh/hook": post-install,pre-delete +data: + name: value +` + +func releaseStub() *release.Release { + date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} + return &release.Release{ + Name: "lost-fish", + Info: &release.Info{ + FirstDeployed: &date, + LastDeployed: &date, + Status: &release.Status{Code: release.Status_DEPLOYED}, + Description: "a release stub", + }, + Chart: chartStub(), + Config: &chart.Config{Raw: `name: value`}, + Version: 1, + Hooks: []*release.Hook{ + { + Name: "finding-nemo", + Kind: "Pod", + Path: "finding-nemo", + Manifest: manifestWithTestHook, + Events: []release.Hook_Event{ + release.Hook_RELEASE_TEST_SUCCESS, + }, + }, + { + Name: "test-cm", + Kind: "ConfigMap", + Path: "test-cm", + Manifest: manifestWithInstallHooks, + Events: []release.Hook_Event{ + release.Hook_POST_INSTALL, + release.Hook_PRE_DELETE, + }, + }, + }, + } +} + +func testFixture() *test { + return &test{ + manifest: manifestWithTestHook, + result: &release.TestRun{}, + } +} + +func testSuiteFixture() *TestSuite { + testManifests := []string{manifestWithTestHook} + testResults := []*release.TestRun{} + ts := &TestSuite{ + TestManifests: testManifests, + Results: testResults, + } + + return ts +} + +func testEnvFixture() *Environment { + tillerEnv := mockTillerEnvironment() + + return &Environment{ + Namespace: "default", + KubeClient: tillerEnv.KubeClient, + Timeout: 5, + Stream: mockStream{}, + } +} + +func mockTillerEnvironment() *tillerEnv.Environment { + e := tillerEnv.New() + e.Releases = storage.Init(driver.NewMemory()) + e.KubeClient = newPodSucceededKubeClient() + return e +} + +type mockStream struct { + stream grpc.ServerStream +} + +func (rs mockStream) Send(m *services.TestReleaseResponse) error { + return nil +} +func (rs mockStream) SetHeader(m metadata.MD) error { return nil } +func (rs mockStream) SendHeader(m metadata.MD) error { return nil } +func (rs mockStream) SetTrailer(m metadata.MD) {} +func (rs mockStream) SendMsg(v interface{}) error { return nil } +func (rs mockStream) RecvMsg(v interface{}) error { return nil } +func (rs mockStream) Context() context.Context { return helm.NewContext() } + +func newPodSucceededKubeClient() *podSucceededKubeClient { + return &podSucceededKubeClient{ + PrintingKubeClient: tillerEnv.PrintingKubeClient{Out: os.Stdout}, + } +} + +type podSucceededKubeClient struct { + tillerEnv.PrintingKubeClient +} + +func (p *podSucceededKubeClient) WaitAndGetCompletedPodPhase(ns string, r io.Reader, timeout time.Duration) (api.PodPhase, error) { + return api.PodSucceeded, nil +} diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go new file mode 100644 index 000000000..b6bb87b0a --- /dev/null +++ b/pkg/releaseutil/manifest.go @@ -0,0 +1,48 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package releaseutil + +import ( + "fmt" + "strings" +) + +// SimpleHead defines what the structure of the head of a manifest file +type SimpleHead struct { + Version string `json:"apiVersion"` + Kind string `json:"kind,omitempty"` + Metadata *struct { + Name string `json:"name"` + Annotations map[string]string `json:"annotations"` + } `json:"metadata,omitempty"` +} + +// SplitManifests takes a string of manifest and returns a map contains individual manifests +func SplitManifests(bigfile string) map[string]string { + // This is not the best way of doing things, but it's how k8s itself does it. + // Basically, we're quickly splitting a stream of YAML documents into an + // array of YAML docs. In the current implementation, the file name is just + // a place holder, and doesn't have any further meaning. + sep := "\n---\n" + tpl := "manifest-%d" + res := map[string]string{} + tmp := strings.Split(bigfile, sep) + for i, d := range tmp { + res[fmt.Sprintf(tpl, i)] = d + } + return res +} diff --git a/pkg/tiller/environment/environment.go b/pkg/tiller/environment/environment.go index 7e8e94ab6..fa5d1ecab 100644 --- a/pkg/tiller/environment/environment.go +++ b/pkg/tiller/environment/environment.go @@ -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,10 @@ 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) + + // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase + // and returns said phase (PodSucceeded or PodFailed qualify) + WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error) } // PrintingKubeClient implements KubeClient, but simply prints the reader to @@ -180,6 +186,12 @@ func (p *PrintingKubeClient) Build(ns string, reader io.Reader) (kube.Result, er return []*resource.Info{}, nil } +// WaitAndGetCompletedPodPhase implements KubeClient WaitAndGetCompletedPodPhase +func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error) { + _, err := io.Copy(p.Out, reader) + return api.PodUnknown, err +} + // Environment provides the context for executing a client request. // // All services in a context are concurrency safe. diff --git a/pkg/tiller/environment/environment_test.go b/pkg/tiller/environment/environment_test.go index a6621e5e7..cb36de356 100644 --- a/pkg/tiller/environment/environment_test.go +++ b/pkg/tiller/environment/environment_test.go @@ -20,10 +20,12 @@ import ( "bytes" "io" "testing" + "time" "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/kube" "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/kubectl/resource" ) @@ -55,6 +57,13 @@ func (k *mockKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, func (k *mockKubeClient) Build(ns string, reader io.Reader) (kube.Result, error) { return []*resource.Info{}, nil } +func (k *mockKubeClient) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error) { + return api.PodUnknown, nil +} + +func (k *mockKubeClient) WaitAndGetCompletedPodStatus(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error) { + return "", nil +} var _ Engine = &mockEngine{} var _ KubeClient = &mockKubeClient{} diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go index 1687f2f3b..ed151602b 100644 --- a/pkg/tiller/hooks.go +++ b/pkg/tiller/hooks.go @@ -26,47 +26,41 @@ import ( "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/proto/hapi/release" + util "k8s.io/helm/pkg/releaseutil" ) // hookAnno is the label name for a hook const hookAnno = "helm.sh/hook" const ( - preInstall = "pre-install" - postInstall = "post-install" - preDelete = "pre-delete" - postDelete = "post-delete" - preUpgrade = "pre-upgrade" - postUpgrade = "post-upgrade" - preRollback = "pre-rollback" - postRollback = "post-rollback" + preInstall = "pre-install" + postInstall = "post-install" + preDelete = "pre-delete" + postDelete = "post-delete" + preUpgrade = "pre-upgrade" + postUpgrade = "post-upgrade" + preRollback = "pre-rollback" + postRollback = "post-rollback" + releaseTestSuccess = "test-success" ) var events = map[string]release.Hook_Event{ - preInstall: release.Hook_PRE_INSTALL, - postInstall: release.Hook_POST_INSTALL, - preDelete: release.Hook_PRE_DELETE, - postDelete: release.Hook_POST_DELETE, - preUpgrade: release.Hook_PRE_UPGRADE, - postUpgrade: release.Hook_POST_UPGRADE, - preRollback: release.Hook_PRE_ROLLBACK, - postRollback: release.Hook_POST_ROLLBACK, -} - -type simpleHead struct { - Version string `json:"apiVersion"` - Kind string `json:"kind,omitempty"` - Metadata *struct { - Name string `json:"name"` - Annotations map[string]string `json:"annotations"` - } `json:"metadata,omitempty"` + preInstall: release.Hook_PRE_INSTALL, + postInstall: release.Hook_POST_INSTALL, + preDelete: release.Hook_PRE_DELETE, + postDelete: release.Hook_POST_DELETE, + preUpgrade: release.Hook_PRE_UPGRADE, + postUpgrade: release.Hook_POST_UPGRADE, + preRollback: release.Hook_PRE_ROLLBACK, + postRollback: release.Hook_POST_ROLLBACK, + releaseTestSuccess: release.Hook_RELEASE_TEST_SUCCESS, } // manifest represents a manifest file, which has a name and some content. type manifest struct { name string content string - head *simpleHead + head *util.SimpleHead } // sortManifests takes a map of filename/YAML contents and sorts them into hook types. @@ -106,7 +100,7 @@ func sortManifests(files map[string]string, apis chartutil.VersionSet, sort Sort continue } - var sh simpleHead + var sh util.SimpleHead err := yaml.Unmarshal([]byte(c), &sh) if err != nil { diff --git a/pkg/tiller/hooks_test.go b/pkg/tiller/hooks_test.go index b9bfed71a..823e7469c 100644 --- a/pkg/tiller/hooks_test.go +++ b/pkg/tiller/hooks_test.go @@ -23,6 +23,7 @@ import ( "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/proto/hapi/release" + util "k8s.io/helm/pkg/releaseutil" ) func TestSortManifests(t *testing.T) { @@ -162,7 +163,7 @@ metadata: // Verify the sort order sorted := make([]manifest, len(data)) for i, s := range data { - var sh simpleHead + var sh util.SimpleHead err := yaml.Unmarshal([]byte(s.manifest), &sh) if err != nil { // This is expected for manifests that are corrupt or empty. diff --git a/pkg/tiller/kind_sorter_test.go b/pkg/tiller/kind_sorter_test.go index 7b7e5f679..212b8aefc 100644 --- a/pkg/tiller/kind_sorter_test.go +++ b/pkg/tiller/kind_sorter_test.go @@ -18,6 +18,8 @@ package tiller import ( "testing" + + util "k8s.io/helm/pkg/releaseutil" ) func TestKindSorter(t *testing.T) { @@ -25,27 +27,27 @@ func TestKindSorter(t *testing.T) { { name: "m", content: "", - head: &simpleHead{Kind: "Deployment"}, + head: &util.SimpleHead{Kind: "Deployment"}, }, { name: "l", content: "", - head: &simpleHead{Kind: "Service"}, + head: &util.SimpleHead{Kind: "Service"}, }, { name: "!", content: "", - head: &simpleHead{Kind: "HonkyTonkSet"}, + head: &util.SimpleHead{Kind: "HonkyTonkSet"}, }, { name: "h", content: "", - head: &simpleHead{Kind: "Namespace"}, + head: &util.SimpleHead{Kind: "Namespace"}, }, { name: "e", content: "", - head: &simpleHead{Kind: "ConfigMap"}, + head: &util.SimpleHead{Kind: "ConfigMap"}, }, } diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index b1ae7b182..6f09638a2 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -36,6 +36,7 @@ import ( "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/services" + reltesting "k8s.io/helm/pkg/releasetesting" relutil "k8s.io/helm/pkg/releaseutil" "k8s.io/helm/pkg/storage/driver" "k8s.io/helm/pkg/tiller/environment" @@ -1003,7 +1004,7 @@ func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR log.Printf("uninstall: Failed to store updated release: %s", err) } - manifests := splitManifests(rel.Manifest) + manifests := relutil.SplitManifests(rel.Manifest) _, files, err := sortManifests(manifests, vs, UninstallOrder) if err != nil { // We could instead just delete everything in no particular order. @@ -1061,23 +1062,48 @@ func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR return res, errs } -func splitManifests(bigfile string) map[string]string { - // This is not the best way of doing things, but it's how k8s itself does it. - // Basically, we're quickly splitting a stream of YAML documents into an - // array of YAML docs. In the current implementation, the file name is just - // a place holder, and doesn't have any further meaning. - sep := "\n---\n" - tpl := "manifest-%d" - res := map[string]string{} - tmp := strings.Split(bigfile, sep) - for i, d := range tmp { - res[fmt.Sprintf(tpl, i)] = d - } - return res -} - func validateManifest(c environment.KubeClient, ns string, manifest []byte) error { r := bytes.NewReader(manifest) _, err := c.Build(ns, r) return err } + +// RunReleaseTest runs pre-defined tests stored as hooks on a given release +func (s *ReleaseServer) RunReleaseTest(req *services.TestReleaseRequest, stream services.ReleaseService_RunReleaseTestServer) error { + + if !ValidName.MatchString(req.Name) { + return errMissingRelease + } + + // finds the non-deleted release with the given name + rel, err := s.env.Releases.Last(req.Name) + if err != nil { + return err + } + + testEnv := &reltesting.Environment{ + Namespace: rel.Namespace, + KubeClient: s.env.KubeClient, + Timeout: req.Timeout, + Stream: stream, + } + + tSuite, err := reltesting.NewTestSuite(rel) + if err != nil { + log.Printf("Error creating test suite for %s", rel.Name) + return err + } + + if err := tSuite.Run(testEnv); err != nil { + log.Printf("Error running test suite for %s", rel.Name) + return err + } + + rel.LastTestSuiteRun = &release.TestSuite{ + StartedAt: tSuite.StartedAt, + CompletedAt: tSuite.CompletedAt, + Results: tSuite.Results, + } + + return nil +} diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go index 942e1e0b9..185ef05e7 100644 --- a/pkg/tiller/release_server_test.go +++ b/pkg/tiller/release_server_test.go @@ -27,6 +27,7 @@ import ( "github.com/golang/protobuf/ptypes/timestamp" "golang.org/x/net/context" + grpc "google.golang.org/grpc" "google.golang.org/grpc/metadata" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" @@ -51,6 +52,20 @@ data: name: value ` +var manifestWithTestHook = ` +apiVersion: v1 +kind: Pod +metadata: + name: finding-nemo, + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: nemo-test + image: fake-image + cmd: fake-command +` + var manifestWithKeep = `apiVersion: v1 kind: ConfigMap metadata: @@ -83,7 +98,7 @@ data: func rsFixture() *ReleaseServer { return &ReleaseServer{ - env: mockEnvironment(), + env: MockEnvironment(), clientset: fake.NewSimpleClientset(), } } @@ -136,6 +151,15 @@ func namedReleaseStub(name string, status release.Status_Code) *release.Release release.Hook_PRE_DELETE, }, }, + { + Name: "finding-nemo", + Kind: "Pod", + Path: "finding-nemo", + Manifest: manifestWithTestHook, + Events: []release.Hook_Event{ + release.Hook_RELEASE_TEST_SUCCESS, + }, + }, }, } } @@ -950,8 +974,8 @@ func TestRollbackRelease(t *testing.T) { t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) } - if len(updated.Hooks) != 1 { - t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) + if len(updated.Hooks) != 2 { + t.Fatalf("Expected 2 hooks, got %d", len(updated.Hooks)) } if updated.Hooks[0].Manifest != manifestWithHook { @@ -1453,7 +1477,19 @@ func TestReleasesNamespace(t *testing.T) { } } -func mockEnvironment() *environment.Environment { +func TestRunReleaseTest(t *testing.T) { + rs := rsFixture() + rel := namedReleaseStub("nemo", release.Status_DEPLOYED) + rs.env.Releases.Create(rel) + + req := &services.TestReleaseRequest{Name: "nemo", Timeout: 2} + err := rs.RunReleaseTest(req, mockRunReleaseTestServer{}) + if err != nil { + t.Fatalf("failed to run release tests on %s: %s", rel.Name, err) + } +} + +func MockEnvironment() *environment.Environment { e := environment.New() e.Releases = storage.Init(driver.NewMemory()) e.KubeClient = &environment.PrintingKubeClient{Out: os.Stdout} @@ -1504,3 +1540,17 @@ func (l *mockListServer) RecvMsg(v interface{}) error { return nil } func (l *mockListServer) SendHeader(m metadata.MD) error { return nil } func (l *mockListServer) SetTrailer(m metadata.MD) {} func (l *mockListServer) SetHeader(m metadata.MD) error { return nil } + +type mockRunReleaseTestServer struct { + stream grpc.ServerStream +} + +func (rs mockRunReleaseTestServer) Send(m *services.TestReleaseResponse) error { + return nil +} +func (rs mockRunReleaseTestServer) SetHeader(m metadata.MD) error { return nil } +func (rs mockRunReleaseTestServer) SendHeader(m metadata.MD) error { return nil } +func (rs mockRunReleaseTestServer) SetTrailer(m metadata.MD) {} +func (rs mockRunReleaseTestServer) SendMsg(v interface{}) error { return nil } +func (rs mockRunReleaseTestServer) RecvMsg(v interface{}) error { return nil } +func (rs mockRunReleaseTestServer) Context() context.Context { return helm.NewContext() }