From 7ef9bb6f71cc56c0e3044eeaf35d1b975da71559 Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Thu, 12 Jan 2017 16:16:52 -0800 Subject: [PATCH 1/2] feat(*): Add --wait flag Adds `--wait` flag to helm that waits for all pods to reach a ready state, PVCs to be bound, and services to have IP addresses Closes #1805 --- _proto/hapi/services/tiller.proto | 9 + cmd/helm/install.go | 5 +- cmd/helm/install_test.go | 8 + cmd/helm/rollback.go | 5 +- cmd/helm/rollback_test.go | 6 + cmd/helm/upgrade.go | 6 +- cmd/helm/upgrade_test.go | 7 + pkg/helm/option.go | 21 +++ pkg/kube/client.go | 191 +++++++++++++++++++-- pkg/kube/client_test.go | 87 ++++++++-- pkg/proto/hapi/services/tiller.pb.go | 146 ++++++++-------- pkg/tiller/environment/environment.go | 12 +- pkg/tiller/environment/environment_test.go | 8 +- pkg/tiller/release_server.go | 16 +- pkg/tiller/release_server_test.go | 4 +- 15 files changed, 412 insertions(+), 119 deletions(-) diff --git a/_proto/hapi/services/tiller.proto b/_proto/hapi/services/tiller.proto index aa30a6426..a47ab035b 100644 --- a/_proto/hapi/services/tiller.proto +++ b/_proto/hapi/services/tiller.proto @@ -194,6 +194,9 @@ message UpdateReleaseRequest { int64 timeout = 7; // ResetValues will cause Tiller to ignore stored values, resetting to default values. bool reset_values = 8; + // wait, if true, will wait until all Pods, PVCs, and Services are in a ready state + // before marking the release as successful. It will wait for as long as timeout + bool wait = 9; } // UpdateReleaseResponse is the response to an update request. @@ -214,6 +217,9 @@ message RollbackReleaseRequest { bool recreate = 5; // timeout specifies the max amount of time any kubernetes client command can run. int64 timeout = 6; + // wait, if true, will wait until all Pods, PVCs, and Services are in a ready state + // before marking the release as successful. It will wait for as long as timeout + bool wait = 7; } // RollbackReleaseResponse is the response to an update request. @@ -248,6 +254,9 @@ message InstallReleaseRequest { // timeout specifies the max amount of time any kubernetes client command can run. int64 timeout = 8; + // wait, if true, will wait until all Pods, PVCs, and Services are in a ready state + // before marking the release as successful. It will wait for as long as timeout + bool wait = 9; } // InstallReleaseResponse is the response from a release installation. diff --git a/cmd/helm/install.go b/cmd/helm/install.go index f2fc67fe7..6917a6e6a 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -112,6 +112,7 @@ type installCmd struct { nameTemplate string version string timeout int64 + wait bool } type valueFiles []string @@ -169,6 +170,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { f.StringVar(&inst.keyring, "keyring", defaultKeyring(), "location of public keys used for verification") f.StringVar(&inst.version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed") f.Int64Var(&inst.timeout, "timeout", 300, "time in seconds to wait for any individual kubernetes operation (like Jobs for hooks)") + f.BoolVar(&inst.wait, "wait", false, "if set, will wait until all Pods, PVCs, and Services are in a ready state before marking the release as successful. It will wait for as long as --timeout") return cmd } @@ -205,7 +207,8 @@ func (i *installCmd) run() error { helm.InstallDryRun(i.dryRun), helm.InstallReuseName(i.replace), helm.InstallDisableHooks(i.disableHooks), - helm.InstallTimeout(i.timeout)) + helm.InstallTimeout(i.timeout), + helm.InstallWait(i.wait)) if err != nil { return prettyError(err) } diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index e8708789d..3204a2437 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -98,6 +98,14 @@ func TestInstall(t *testing.T) { expected: "foobar", resp: releaseMock(&releaseOptions{name: "foobar"}), }, + // Install, with wait + { + name: "install with a wait", + args: []string{"testdata/testcharts/alpine"}, + flags: strings.Split("--wait", " "), + expected: "apollo", + resp: releaseMock(&releaseOptions{name: "apollo"}), + }, // Install, using the name-template { name: "install with name-template", diff --git a/cmd/helm/rollback.go b/cmd/helm/rollback.go index ee7b17515..267bfa0dd 100644 --- a/cmd/helm/rollback.go +++ b/cmd/helm/rollback.go @@ -43,6 +43,7 @@ type rollbackCmd struct { out io.Writer client helm.Interface timeout int64 + wait bool } func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command { @@ -79,6 +80,7 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command { f.BoolVar(&rollback.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") f.BoolVar(&rollback.disableHooks, "no-hooks", false, "prevent hooks from running during rollback") f.Int64Var(&rollback.timeout, "timeout", 300, "time in seconds to wait for any individual kubernetes operation (like Jobs for hooks)") + f.BoolVar(&rollback.wait, "wait", false, "if set, will wait until all Pods, PVCs, and Services are in a ready state before marking the release as successful. It will wait for as long as --timeout") return cmd } @@ -90,7 +92,8 @@ func (r *rollbackCmd) run() error { helm.RollbackRecreate(r.recreate), helm.RollbackDisableHooks(r.disableHooks), helm.RollbackVersion(r.revision), - helm.RollbackTimeout(r.timeout)) + helm.RollbackTimeout(r.timeout), + helm.RollbackWait(r.wait)) if err != nil { return prettyError(err) } diff --git a/cmd/helm/rollback_test.go b/cmd/helm/rollback_test.go index 0e8bec542..f02068247 100644 --- a/cmd/helm/rollback_test.go +++ b/cmd/helm/rollback_test.go @@ -37,6 +37,12 @@ func TestRollbackCmd(t *testing.T) { flags: []string{"--timeout", "120"}, expected: "Rollback was a success! Happy Helming!", }, + { + name: "rollback a release with wait", + args: []string{"funny-honey", "1"}, + flags: []string{"--wait"}, + expected: "Rollback was a success! Happy Helming!", + }, { name: "rollback a release without revision", args: []string{"funny-honey"}, diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index 4eb228bd3..5f6d36142 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -71,6 +71,7 @@ type upgradeCmd struct { version string timeout int64 resetValues bool + wait bool } func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { @@ -112,6 +113,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { f.StringVar(&upgrade.version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used") f.Int64Var(&upgrade.timeout, "timeout", 300, "time in seconds to wait for any individual kubernetes operation (like Jobs for hooks)") f.BoolVar(&upgrade.resetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart") + f.BoolVar(&upgrade.wait, "wait", false, "if set, will wait until all Pods, PVCs, and Services are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.MarkDeprecated("disable-hooks", "use --no-hooks instead") @@ -147,6 +149,7 @@ func (u *upgradeCmd) run() error { values: u.values, namespace: u.namespace, timeout: u.timeout, + wait: u.wait, } return ic.run() } @@ -165,7 +168,8 @@ func (u *upgradeCmd) run() error { helm.UpgradeRecreate(u.recreate), helm.UpgradeDisableHooks(u.disableHooks), helm.UpgradeTimeout(u.timeout), - helm.ResetValues(u.resetValues)) + helm.ResetValues(u.resetValues), + helm.UpgradeWait(u.wait)) if err != nil { return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err)) } diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index 0b485639a..dadf3fb80 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -114,6 +114,13 @@ func TestUpgradeCmd(t *testing.T) { resp: releaseMock(&releaseOptions{name: "crazy-bunny", version: 1, chart: ch}), expected: "Release \"crazy-bunny\" has been upgraded. Happy Helming!\n", }, + { + name: "upgrade a release with wait", + args: []string{"crazy-bunny", chartPath}, + flags: []string{"--wait"}, + resp: releaseMock(&releaseOptions{name: "crazy-bunny", version: 2, chart: ch2}), + expected: "Release \"crazy-bunny\" has been upgraded. Happy Helming!\n", + }, } cmd := func(c *fakeReleaseClient, out io.Writer) *cobra.Command { diff --git a/pkg/helm/option.go b/pkg/helm/option.go index bb28570a8..42df562cf 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -181,6 +181,27 @@ func RollbackTimeout(timeout int64) RollbackOption { } } +// InstallWait specifies whether or not to wait for all resources to be ready +func InstallWait(wait bool) InstallOption { + return func(opts *options) { + opts.instReq.Wait = wait + } +} + +// UpgradeWait specifies whether or not to wait for all resources to be ready +func UpgradeWait(wait bool) UpdateOption { + return func(opts *options) { + opts.updateReq.Wait = wait + } +} + +// RollbackWait specifies whether or not to wait for all resources to be ready +func RollbackWait(wait bool) RollbackOption { + return func(opts *options) { + opts.rollbackReq.Wait = wait + } +} + // UpdateValueOverrides specifies a list of values to include when upgrading func UpdateValueOverrides(raw []byte) UpdateOption { return func(opts *options) { diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 79aceba78..0022bb71f 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -30,6 +30,7 @@ import ( "k8s.io/kubernetes/pkg/api/v1" apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1" "k8s.io/kubernetes/pkg/apis/batch" + "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" @@ -40,6 +41,7 @@ import ( "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/strategicpatch" + "k8s.io/kubernetes/pkg/util/wait" "k8s.io/kubernetes/pkg/watch" ) @@ -67,7 +69,7 @@ type ResourceActorFunc func(*resource.Info) error // Create creates kubernetes resources from an io.reader // // Namespace will set the namespace -func (c *Client) Create(namespace string, reader io.Reader) error { +func (c *Client) Create(namespace string, reader io.Reader, timeout int64, shouldWait bool) error { client, err := c.ClientSet() if err != nil { return err @@ -75,7 +77,18 @@ func (c *Client) Create(namespace string, reader io.Reader) error { if err := ensureNamespace(client, namespace); err != nil { return err } - return perform(c, namespace, reader, createResource) + infos, buildErr := c.Build(namespace, reader) + if buildErr != nil { + return buildErr + } + err = perform(c, namespace, infos, createResource) + if err != nil { + return err + } + if shouldWait { + err = c.waitForResources(time.Duration(timeout)*time.Second, infos) + } + return err } func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Result { @@ -107,7 +120,11 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { // Since we don't know what order the objects come in, let's group them by the types, so // that when we print them, they come looking good (headers apply to subgroups, etc.) objs := make(map[string][]runtime.Object) - err := perform(c, namespace, reader, func(info *resource.Info) error { + infos, err := c.Build(namespace, reader) + if err != nil { + return "", err + } + err = perform(c, namespace, infos, func(info *resource.Info) error { log.Printf("Doing get for: '%s'", info.Name) obj, err := resource.NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name, info.Export) if err != nil { @@ -159,7 +176,7 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { // not present in the target configuration // // Namespace will set the namespaces -func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, recreate bool) error { +func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, recreate bool, timeout int64, shouldWait bool) error { original, err := c.Build(namespace, originalReader) if err != nil { return fmt.Errorf("failed decoding reader into objects: %s", err) @@ -224,6 +241,12 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader log.Printf("Failed to delete %s, err: %s", info.Name, err) } } + if shouldWait { + err = c.waitForResources(time.Duration(timeout)*time.Second, target) + } + if err != nil { + return err + } return nil } @@ -231,7 +254,11 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader // // Namespace will set the namespace func (c *Client) Delete(namespace string, reader io.Reader) error { - return perform(c, namespace, reader, func(info *resource.Info) error { + infos, err := c.Build(namespace, reader) + if err != nil { + return err + } + return perform(c, namespace, infos, func(info *resource.Info) error { log.Printf("Starting delete for %s %s", info.Name, info.Mapping.GroupVersionKind.Kind) err := deleteResource(c, info) return skipIfNotFound(err) @@ -264,18 +291,18 @@ func watchTimeout(t time.Duration) ResourceActorFunc { // ascertained by watching the Status fields in a job's output. // // Handling for other kinds will be added as necessary. -func (c *Client) WatchUntilReady(namespace string, reader io.Reader, timeout int64) error { +func (c *Client) WatchUntilReady(namespace string, reader io.Reader, timeout int64, shouldWait bool) error { + infos, err := c.Build(namespace, reader) + if err != nil { + return err + } // For jobs, there's also the option to do poll c.Jobs(namespace).Get(): // https://github.com/adamreese/kubernetes/blob/master/test/e2e/job.go#L291-L300 - return perform(c, namespace, reader, watchTimeout(time.Duration(timeout)*time.Second)) + return perform(c, namespace, infos, watchTimeout(time.Duration(timeout)*time.Second)) } -func perform(c *Client, namespace string, reader io.Reader, fn ResourceActorFunc) error { - infos, err := c.Build(namespace, reader) - switch { - case err != nil: - return err - case len(infos) == 0: +func perform(c *Client, namespace string, infos Result, fn ResourceActorFunc) error { + if len(infos) == 0 { return ErrNoObjectsVisited } for _, info := range infos { @@ -287,8 +314,12 @@ func perform(c *Client, namespace string, reader io.Reader, fn ResourceActorFunc } func createResource(info *resource.Info) error { - _, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) - return err + obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object) + if err != nil { + return err + } + info.Refresh(obj, true) + return nil } func deleteResource(c *Client, info *resource.Info) error { @@ -328,7 +359,8 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, // send patch to server helper := resource.NewHelper(target.Client, target.Mapping) - if _, err = helper.Patch(target.Namespace, target.Name, api.StrategicMergePatchType, patch); err != nil { + var obj runtime.Object + if obj, err = helper.Patch(target.Namespace, target.Name, api.StrategicMergePatchType, patch); err != nil { return err } @@ -336,6 +368,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, client, _ := c.ClientSet() return recreatePods(client, target.Namespace, extractSelector(currentObj)) } + target.Refresh(obj, true) return nil } @@ -418,6 +451,132 @@ func watchUntilReady(timeout time.Duration, info *resource.Info) error { return err } +func podsReady(pods []api.Pod) bool { + if len(pods) == 0 { + return true + } + for _, pod := range pods { + if !api.IsPodReady(&pod) { + return false + } + } + return true +} + +func servicesReady(svc []api.Service) bool { + if len(svc) == 0 { + return true + } + for _, s := range svc { + if !api.IsServiceIPSet(&s) { + return false + } + // This checks if the service has a LoadBalancer and that balancer has an Ingress defined + if s.Spec.Type == api.ServiceTypeLoadBalancer && s.Status.LoadBalancer.Ingress == nil { + return false + } + } + return true +} + +func volumesReady(vols []api.PersistentVolumeClaim) bool { + if len(vols) == 0 { + return true + } + for _, v := range vols { + if v.Status.Phase != api.ClaimBound { + return false + } + } + return true +} + +func getPods(client *internalclientset.Clientset, namespace string, selector map[string]string) ([]api.Pod, error) { + list, err := client.Pods(namespace).List(api.ListOptions{ + FieldSelector: fields.Everything(), + LabelSelector: labels.Set(selector).AsSelector(), + }) + if err != nil { + return nil, err + } + return list.Items, nil +} + +// waitForResources polls to get the current status of all pods, PVCs, and Services +// until all are ready or a timeout is reached +func (c *Client) waitForResources(timeout time.Duration, created Result) error { + log.Printf("beginning wait for resources with timeout of %v", timeout) + client, _ := c.ClientSet() + return wait.Poll(2*time.Second, timeout, func() (bool, error) { + pods := []api.Pod{} + services := []api.Service{} + pvc := []api.PersistentVolumeClaim{} + for _, v := range created { + switch value := v.Object.(type) { + case (*api.ReplicationController): + list, err := getPods(client, value.Namespace, value.Spec.Selector) + if err != nil { + return false, err + } + pods = append(pods, list...) + case (*api.Pod): + pod, err := client.Pods(value.Namespace).Get(value.Name) + if err != nil { + return false, err + } + pods = append(pods, *pod) + case (*extensions.Deployment): + // Get the RS children first + rs, err := client.ReplicaSets(value.Namespace).List(api.ListOptions{ + FieldSelector: fields.Everything(), + LabelSelector: labels.Set(value.Spec.Selector.MatchLabels).AsSelector(), + }) + if err != nil { + return false, err + } + for _, r := range rs.Items { + list, err := getPods(client, value.Namespace, r.Spec.Selector.MatchLabels) + if err != nil { + return false, err + } + pods = append(pods, list...) + } + case (*extensions.DaemonSet): + list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels) + if err != nil { + return false, err + } + pods = append(pods, list...) + case (*apps.StatefulSet): + list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels) + if err != nil { + return false, err + } + pods = append(pods, list...) + case (*extensions.ReplicaSet): + list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels) + if err != nil { + return false, err + } + pods = append(pods, list...) + case (*api.PersistentVolumeClaim): + claim, err := client.PersistentVolumeClaims(value.Namespace).Get(value.Name) + if err != nil { + return false, err + } + pvc = append(pvc, *claim) + case (*api.Service): + svc, err := client.Services(value.Namespace).Get(value.Name) + if err != nil { + return false, err + } + services = append(services, *svc) + } + } + return podsReady(pods) && servicesReady(services) && volumesReady(pvc), nil + }) +} + // waitForJob is a helper that waits for a job to complete. // // This operates on an event returned from a watcher. diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index c137be440..080e9882c 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -105,7 +105,9 @@ func (f *fakeReaperFactory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, e func TestUpdate(t *testing.T) { listA := newPodList("starfish", "otter", "squid") listB := newPodList("starfish", "otter", "dolphin") + listC := newPodList("starfish", "otter", "dolphin") listB.Items[0].Spec.Containers[0].Ports = []api.ContainerPort{{Name: "https", ContainerPort: 443}} + listC.Items[0].Spec.Containers[0].Ports = []api.ContainerPort{{Name: "https", ContainerPort: 443}} actions := make(map[string]string) @@ -148,10 +150,19 @@ func TestUpdate(t *testing.T) { reaper := &fakeReaper{} rf := &fakeReaperFactory{Factory: f, reaper: reaper} c := &Client{Factory: rf} - if err := c.Update(api.NamespaceDefault, objBody(codec, &listA), objBody(codec, &listB), false); err != nil { + if err := c.Update(api.NamespaceDefault, objBody(codec, &listA), objBody(codec, &listB), false, 0, false); err != nil { t.Fatal(err) } - + // TODO: Find a way to test methods that use Client Set + // Test with a wait + // if err := c.Update("test", objBody(codec, &listB), objBody(codec, &listC), false, 300, true); err != nil { + // t.Fatal(err) + // } + // Test with a wait should fail + // TODO: A way to make this not based off of an extremely short timeout? + // if err := c.Update("test", objBody(codec, &listC), objBody(codec, &listA), false, 2, true); err != nil { + // t.Fatal(err) + // } expectedActions := map[string]string{ "/namespaces/default/pods/dolphin": "GET", "/namespaces/default/pods/otter": "GET", @@ -171,7 +182,7 @@ func TestUpdate(t *testing.T) { } -func TestPerform(t *testing.T) { +func TestBuild(t *testing.T) { tests := []struct { name string namespace string @@ -186,12 +197,6 @@ func TestPerform(t *testing.T) { namespace: "test", reader: strings.NewReader(guestbookManifest), count: 6, - }, { - name: "Empty manifests", - namespace: "test", - reader: strings.NewReader(""), - err: true, - errMessage: "no objects visited", }, { name: "Invalid schema", namespace: "test", @@ -202,6 +207,59 @@ func TestPerform(t *testing.T) { }, } + for _, tt := range tests { + f, tf, _, _ := cmdtesting.NewAPIFactory() + c := &Client{Factory: f} + if tt.swaggerFile != "" { + data, err := ioutil.ReadFile(tt.swaggerFile) + if err != nil { + t.Fatalf("could not read swagger spec: %s", err) + } + validator, err := validation.NewSwaggerSchemaFromBytes(data, nil) + if err != nil { + t.Fatalf("could not load swagger spec: %s", err) + } + tf.Validator = validator + } + + // Test for an invalid manifest + infos, err := c.Build(tt.namespace, tt.reader) + if err != nil && err.Error() != tt.errMessage { + t.Errorf("%q. expected error message: %v, got %v", tt.name, tt.errMessage, err) + } else if err != nil && !tt.err { + t.Errorf("%q. Got error message when no error should have occurred: %v, got %v", tt.name, tt.errMessage, err) + } + + if len(infos) != tt.count { + t.Errorf("%q. expected %d result objects, got %d", tt.name, tt.count, len(infos)) + } + } +} + +func TestPerform(t *testing.T) { + tests := []struct { + name string + namespace string + reader io.Reader + count int + swaggerFile string + err bool + errMessage string + }{ + { + name: "Valid input", + namespace: "test", + reader: strings.NewReader(guestbookManifest), + count: 6, + }, { + name: "Empty manifests", + namespace: "test", + reader: strings.NewReader(""), + err: true, + errMessage: "no objects visited", + }, + } + for _, tt := range tests { results := []*resource.Info{} @@ -228,7 +286,12 @@ func TestPerform(t *testing.T) { tf.Validator = validator } - err := perform(c, tt.namespace, tt.reader, fn) + infos, err := c.Build(tt.namespace, tt.reader) + if err != nil && err.Error() != tt.errMessage { + t.Errorf("%q. Error while building manifests: %v", tt.name, err) + } + + err = perform(c, tt.namespace, infos, fn) if (err != nil) != tt.err { t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) } @@ -245,13 +308,13 @@ func TestPerform(t *testing.T) { func TestReal(t *testing.T) { t.Skip("This is a live test, comment this line to run") c := New(nil) - if err := c.Create("test", strings.NewReader(guestbookManifest)); err != nil { + if err := c.Create("test", strings.NewReader(guestbookManifest), 300, false); err != nil { t.Fatal(err) } testSvcEndpointManifest := testServiceManifest + "\n---\n" + testEndpointManifest c = New(nil) - if err := c.Create("test-delete", strings.NewReader(testSvcEndpointManifest)); err != nil { + if err := c.Create("test-delete", strings.NewReader(testSvcEndpointManifest), 300, false); err != nil { t.Fatal(err) } diff --git a/pkg/proto/hapi/services/tiller.pb.go b/pkg/proto/hapi/services/tiller.pb.go index bb773f826..9faca5f8b 100644 --- a/pkg/proto/hapi/services/tiller.pb.go +++ b/pkg/proto/hapi/services/tiller.pb.go @@ -252,6 +252,9 @@ type UpdateReleaseRequest struct { Timeout int64 `protobuf:"varint,7,opt,name=timeout" json:"timeout,omitempty"` // ResetValues will cause Tiller to ignore stored values, resetting to default values. ResetValues bool `protobuf:"varint,8,opt,name=reset_values,json=resetValues" json:"reset_values,omitempty"` + // wait, if true, will wait until all Pods, PVCs, and Services are in a ready state + // before marking the release as successful. It will wait for as long as timeout + Wait bool `protobuf:"varint,9,opt,name=wait" json:"wait,omitempty"` } func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} } @@ -303,6 +306,9 @@ type RollbackReleaseRequest struct { Recreate bool `protobuf:"varint,5,opt,name=recreate" json:"recreate,omitempty"` // timeout specifies the max amount of time any kubernetes client command can run. Timeout int64 `protobuf:"varint,6,opt,name=timeout" json:"timeout,omitempty"` + // wait, if true, will wait until all Pods, PVCs, and Services are in a ready state + // before marking the release as successful. It will wait for as long as timeout + Wait bool `protobuf:"varint,7,opt,name=wait" json:"wait,omitempty"` } func (m *RollbackReleaseRequest) Reset() { *m = RollbackReleaseRequest{} } @@ -349,6 +355,9 @@ type InstallReleaseRequest struct { ReuseName bool `protobuf:"varint,7,opt,name=reuse_name,json=reuseName" json:"reuse_name,omitempty"` // timeout specifies the max amount of time any kubernetes client command can run. Timeout int64 `protobuf:"varint,8,opt,name=timeout" json:"timeout,omitempty"` + // wait, if true, will wait until all Pods, PVCs, and Services are in a ready state + // before marking the release as successful. It will wait for as long as timeout + Wait bool `protobuf:"varint,9,opt,name=wait" json:"wait,omitempty"` } func (m *InstallReleaseRequest) Reset() { *m = InstallReleaseRequest{} } @@ -894,73 +903,74 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("hapi/services/tiller.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 1075 bytes of a gzipped FileDescriptorProto + // 1092 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, 0xfd, 0x71, 0x2d, 0x40, 0xc5, 0x08, 0x36, - 0x2c, 0x6c, 0x0a, 0xe1, 0x0a, 0x09, 0x21, 0x75, 0xb3, 0x51, 0x5a, 0x28, 0x59, 0xc9, 0xa1, 0x8b, - 0xc4, 0x05, 0x91, 0x9b, 0x4c, 0xb6, 0x66, 0x1d, 0x4f, 0xf0, 0x4c, 0xaa, 0xcd, 0x2d, 0x77, 0x3c, - 0x00, 0x3c, 0x00, 0x8f, 0xc1, 0x53, 0xf1, 0x08, 0xc8, 0xf3, 0xe3, 0x66, 0x5c, 0x3b, 0xeb, 0xcd, - 0x4d, 0xec, 0x99, 0x73, 0xe6, 0x3b, 0xe7, 0x7c, 0x73, 0x7e, 0x1c, 0xb0, 0x6f, 0xbd, 0xb9, 0x7f, - 0x46, 0x71, 0x74, 0xe7, 0x8f, 0x31, 0x3d, 0x63, 0x7e, 0x10, 0xe0, 0xa8, 0x3d, 0x8f, 0x08, 0x23, - 0xe8, 0x20, 0x96, 0xb5, 0x95, 0xac, 0x2d, 0x64, 0xf6, 0x11, 0x3f, 0x31, 0xbe, 0xf5, 0x22, 0x26, - 0x7e, 0x85, 0xb6, 0x7d, 0xbc, 0xba, 0x4f, 0xc2, 0xa9, 0xff, 0x4a, 0x0a, 0x84, 0x89, 0x08, 0x07, - 0xd8, 0xa3, 0x58, 0x3d, 0xb5, 0x43, 0x4a, 0xe6, 0x87, 0x53, 0x22, 0x05, 0x27, 0x9a, 0x80, 0x32, - 0x8f, 0x2d, 0xa8, 0x86, 0x77, 0x87, 0x23, 0xea, 0x93, 0x50, 0x3d, 0x85, 0xcc, 0xf9, 0xa7, 0x04, - 0x8f, 0xae, 0x7c, 0xca, 0x5c, 0x71, 0x90, 0xba, 0xf8, 0xf7, 0x05, 0xa6, 0x0c, 0x1d, 0x40, 0x35, - 0xf0, 0x67, 0x3e, 0xb3, 0x8c, 0x53, 0xa3, 0x55, 0x76, 0xc5, 0x02, 0x1d, 0x81, 0x49, 0xa6, 0x53, - 0x8a, 0x99, 0x55, 0x3a, 0x35, 0x5a, 0x0d, 0x57, 0xae, 0xd0, 0x77, 0x50, 0xa3, 0x24, 0x62, 0xa3, - 0x9b, 0xa5, 0x55, 0x3e, 0x35, 0x5a, 0x7b, 0x9d, 0x4f, 0xda, 0x59, 0x54, 0xb4, 0x63, 0x4b, 0x43, - 0x12, 0xb1, 0x76, 0xfc, 0xf3, 0x6c, 0xe9, 0x9a, 0x94, 0x3f, 0x63, 0xdc, 0xa9, 0x1f, 0x30, 0x1c, - 0x59, 0x15, 0x81, 0x2b, 0x56, 0xa8, 0x0f, 0xc0, 0x71, 0x49, 0x34, 0xc1, 0x91, 0x55, 0xe5, 0xd0, - 0xad, 0x02, 0xd0, 0x2f, 0x62, 0x7d, 0xb7, 0x41, 0xd5, 0x2b, 0xfa, 0x16, 0x76, 0x04, 0x25, 0xa3, - 0x31, 0x99, 0x60, 0x6a, 0x99, 0xa7, 0xe5, 0xd6, 0x5e, 0xe7, 0x44, 0x40, 0x29, 0x86, 0x87, 0x82, - 0xb4, 0x2e, 0x99, 0x60, 0x77, 0x5b, 0xa8, 0xc7, 0xef, 0xd4, 0xf9, 0x15, 0xea, 0x0a, 0xde, 0xe9, - 0x80, 0x29, 0x9c, 0x47, 0xdb, 0x50, 0xbb, 0x1e, 0xfc, 0x30, 0x78, 0xf1, 0xf3, 0xa0, 0xb9, 0x85, - 0xea, 0x50, 0x19, 0x9c, 0xff, 0xd8, 0x6b, 0x1a, 0x68, 0x1f, 0x76, 0xaf, 0xce, 0x87, 0x3f, 0x8d, - 0xdc, 0xde, 0x55, 0xef, 0x7c, 0xd8, 0x7b, 0xde, 0x2c, 0x39, 0x1f, 0x42, 0x23, 0xf1, 0x0a, 0xd5, - 0xa0, 0x7c, 0x3e, 0xec, 0x8a, 0x23, 0xcf, 0x7b, 0xc3, 0x6e, 0xd3, 0x70, 0xfe, 0x34, 0xe0, 0x40, - 0xbf, 0x04, 0x3a, 0x27, 0x21, 0xc5, 0xf1, 0x2d, 0x8c, 0xc9, 0x22, 0x4c, 0x6e, 0x81, 0x2f, 0x10, - 0x82, 0x4a, 0x88, 0xdf, 0xa8, 0x3b, 0xe0, 0xef, 0xb1, 0x26, 0x23, 0xcc, 0x0b, 0x38, 0xff, 0x65, - 0x57, 0x2c, 0xd0, 0x57, 0x50, 0x97, 0xc1, 0x51, 0xab, 0x72, 0x5a, 0x6e, 0x6d, 0x77, 0x0e, 0xf5, - 0x90, 0xa5, 0x45, 0x37, 0x51, 0x73, 0xfa, 0x70, 0xdc, 0xc7, 0xca, 0x13, 0xc1, 0x88, 0xca, 0x89, - 0xd8, 0xae, 0x37, 0xc3, 0xdc, 0x99, 0xd8, 0xae, 0x37, 0xc3, 0xc8, 0x82, 0x9a, 0x4c, 0x28, 0xee, - 0x4e, 0xd5, 0x55, 0x4b, 0x87, 0x81, 0xf5, 0x10, 0x48, 0xc6, 0x95, 0x85, 0xf4, 0x29, 0x54, 0xe2, - 0x74, 0xe6, 0x30, 0xdb, 0x1d, 0xa4, 0xfb, 0x79, 0x19, 0x4e, 0x89, 0xcb, 0xe5, 0xe8, 0x7d, 0x68, - 0xc4, 0xfa, 0x74, 0xee, 0x8d, 0x31, 0x8f, 0xb6, 0xe1, 0xde, 0x6f, 0x38, 0x17, 0xab, 0x56, 0xbb, - 0x24, 0x64, 0x38, 0x64, 0x9b, 0xf9, 0x7f, 0x05, 0x27, 0x19, 0x48, 0x32, 0x80, 0x33, 0xa8, 0x49, - 0xd7, 0x38, 0x5a, 0x2e, 0xaf, 0x4a, 0xcb, 0xf9, 0xbb, 0x04, 0x07, 0xd7, 0xf3, 0x89, 0xc7, 0xb0, - 0x12, 0xad, 0x71, 0xea, 0x31, 0x54, 0x79, 0x5b, 0x90, 0x5c, 0xec, 0x0b, 0x6c, 0xd1, 0x3b, 0xba, - 0xf1, 0xaf, 0x2b, 0xe4, 0xe8, 0x09, 0x98, 0x77, 0x5e, 0xb0, 0xc0, 0x94, 0x13, 0x91, 0xb0, 0x26, - 0x35, 0x79, 0x4f, 0x71, 0xa5, 0x06, 0x3a, 0x86, 0xda, 0x24, 0x5a, 0x8e, 0xa2, 0x45, 0xc8, 0x8b, - 0xac, 0xee, 0x9a, 0x93, 0x68, 0xe9, 0x2e, 0x42, 0xf4, 0x31, 0xec, 0x4e, 0x7c, 0xea, 0xdd, 0x04, - 0x78, 0x74, 0x4b, 0xc8, 0x6b, 0xca, 0xeb, 0xac, 0xee, 0xee, 0xc8, 0xcd, 0x8b, 0x78, 0x0f, 0xd9, - 0x71, 0x26, 0x8d, 0x23, 0xec, 0x31, 0x6c, 0x99, 0x5c, 0x9e, 0xac, 0x63, 0x0e, 0x99, 0x3f, 0xc3, - 0x64, 0xc1, 0xac, 0x1a, 0xcf, 0x3e, 0xb5, 0x44, 0x1f, 0xc1, 0x4e, 0x84, 0x29, 0x66, 0x23, 0xe9, - 0x65, 0x9d, 0x9f, 0xdc, 0xe6, 0x7b, 0x2f, 0xf9, 0x96, 0x73, 0x01, 0x87, 0x29, 0x5e, 0x36, 0xa5, - 0xf8, 0x5f, 0x03, 0x8e, 0x5c, 0x12, 0x04, 0x37, 0xde, 0xf8, 0x75, 0x01, 0x92, 0x57, 0xf8, 0x28, - 0xad, 0xe7, 0xa3, 0x9c, 0xc1, 0xc7, 0x4a, 0xde, 0x54, 0xb4, 0xbc, 0xd1, 0x98, 0xaa, 0xe6, 0x33, - 0x65, 0x6a, 0x4c, 0x39, 0xdf, 0xc3, 0xf1, 0x03, 0xdf, 0x37, 0x25, 0xe2, 0xaf, 0x12, 0x1c, 0x5e, - 0x86, 0x94, 0x79, 0x41, 0x90, 0xe2, 0x21, 0x49, 0x2c, 0xa3, 0x70, 0x62, 0x95, 0xde, 0x25, 0xb1, - 0xca, 0x1a, 0x91, 0x8a, 0xf5, 0xca, 0x0a, 0xeb, 0x85, 0x92, 0x4d, 0x2b, 0x71, 0x33, 0x55, 0xe2, - 0xe8, 0x03, 0x80, 0x08, 0x2f, 0x28, 0x1e, 0x71, 0xf0, 0x1a, 0x3f, 0xdf, 0xe0, 0x3b, 0x03, 0x59, - 0xd1, 0x8a, 0xe3, 0xba, 0xce, 0xf1, 0x25, 0x1c, 0xa5, 0x69, 0xd9, 0x94, 0xe2, 0x3f, 0x0c, 0x38, - 0xbe, 0x0e, 0xfd, 0x4c, 0x92, 0xb3, 0x92, 0xed, 0x41, 0xd8, 0xa5, 0x8c, 0xb0, 0x0f, 0xa0, 0x3a, - 0x5f, 0x44, 0xaf, 0xb0, 0xa4, 0x51, 0x2c, 0x56, 0xe3, 0xa9, 0xe8, 0xf1, 0x8c, 0xc0, 0x7a, 0xe8, - 0xc3, 0x86, 0x11, 0xc5, 0x5e, 0x27, 0xed, 0xb7, 0x21, 0x5a, 0xad, 0xf3, 0x08, 0xf6, 0xfb, 0x98, - 0xbd, 0x14, 0x89, 0x2d, 0xc3, 0x73, 0x7a, 0x80, 0x56, 0x37, 0xef, 0xed, 0xc9, 0x2d, 0xdd, 0x9e, - 0xfa, 0xda, 0x50, 0xfa, 0x4a, 0xcb, 0xf9, 0x86, 0x63, 0x5f, 0xf8, 0x94, 0x91, 0x68, 0xb9, 0x8e, - 0xba, 0x26, 0x94, 0x67, 0xde, 0x1b, 0xd9, 0x9d, 0xe3, 0x57, 0xa7, 0xcf, 0x3d, 0x48, 0x8e, 0x4a, - 0x0f, 0x56, 0x67, 0x9d, 0x51, 0x68, 0xd6, 0x75, 0xfe, 0xab, 0xc1, 0x9e, 0x1a, 0x50, 0xe2, 0x73, - 0x02, 0xf9, 0xb0, 0xb3, 0x3a, 0x89, 0xd1, 0x67, 0xf9, 0x5f, 0x1b, 0xa9, 0x4f, 0x26, 0xfb, 0x49, - 0x11, 0x55, 0xe1, 0xac, 0xb3, 0xf5, 0xa5, 0x81, 0x28, 0x34, 0xd3, 0x03, 0x12, 0x3d, 0xcd, 0xc6, - 0xc8, 0x99, 0xc8, 0x76, 0xbb, 0xa8, 0xba, 0x32, 0x8b, 0xee, 0x38, 0xed, 0xfa, 0x54, 0x43, 0x6f, - 0x85, 0xd1, 0x07, 0xa9, 0x7d, 0x56, 0x58, 0x3f, 0xb1, 0xfb, 0x1b, 0xec, 0x6a, 0x6d, 0x1e, 0xe5, - 0xb0, 0x95, 0x35, 0x23, 0xed, 0xcf, 0x0b, 0xe9, 0x26, 0xb6, 0x66, 0xb0, 0xa7, 0xd7, 0x39, 0xca, - 0x01, 0xc8, 0x6c, 0x92, 0xf6, 0x17, 0xc5, 0x94, 0x13, 0x73, 0x14, 0x9a, 0xe9, 0x32, 0xcc, 0xbb, - 0xc7, 0x9c, 0x96, 0x91, 0x77, 0x8f, 0x79, 0xd5, 0xed, 0x6c, 0x21, 0x0f, 0xe0, 0xbe, 0x0a, 0xd1, - 0xe3, 0xdc, 0x0b, 0xd1, 0x8b, 0xd7, 0x6e, 0xbd, 0x5d, 0x31, 0x31, 0x31, 0x87, 0xf7, 0x52, 0x23, - 0x09, 0xe5, 0x50, 0x93, 0x3d, 0x75, 0xed, 0xa7, 0x05, 0xb5, 0x53, 0x41, 0xc9, 0xc2, 0x5e, 0x13, - 0x94, 0xde, 0x35, 0xd6, 0x04, 0x95, 0xea, 0x11, 0xce, 0xd6, 0x33, 0xf8, 0xa5, 0xae, 0xf4, 0x6e, - 0x4c, 0xfe, 0x17, 0xe8, 0xeb, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0x83, 0x62, 0xce, 0x77, 0xd3, - 0x0d, 0x00, 0x00, + 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, } diff --git a/pkg/tiller/environment/environment.go b/pkg/tiller/environment/environment.go index 094ff940d..7e8e94ab6 100644 --- a/pkg/tiller/environment/environment.go +++ b/pkg/tiller/environment/environment.go @@ -99,7 +99,7 @@ type KubeClient interface { // // reader must contain a YAML stream (one or more YAML documents separated // by "\n---\n"). - Create(namespace string, reader io.Reader) error + Create(namespace string, reader io.Reader, timeout int64, shouldWait bool) error // Get gets one or more resources. Returned string hsa the format like kubectl // provides with the column headers separating the resource types. @@ -123,7 +123,7 @@ type KubeClient interface { // For Jobs, "ready" means the job ran to completion (excited without error). // For all other kinds, it means the kind was created or modified without // error. - WatchUntilReady(namespace string, reader io.Reader, timeout int64) error + WatchUntilReady(namespace string, reader io.Reader, timeout int64, shouldWait bool) error // Update updates one or more resources or creates the resource // if it doesn't exist @@ -132,7 +132,7 @@ type KubeClient interface { // // reader must contain a YAML stream (one or more YAML documents separated // by "\n---\n"). - Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool) error + Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error Build(namespace string, reader io.Reader) (kube.Result, error) } @@ -144,7 +144,7 @@ type PrintingKubeClient struct { } // Create prints the values of what would be created with a real KubeClient. -func (p *PrintingKubeClient) Create(ns string, r io.Reader) error { +func (p *PrintingKubeClient) Create(ns string, r io.Reader, timeout int64, shouldWait bool) error { _, err := io.Copy(p.Out, r) return err } @@ -164,13 +164,13 @@ func (p *PrintingKubeClient) Delete(ns string, r io.Reader) error { } // WatchUntilReady implements KubeClient WatchUntilReady. -func (p *PrintingKubeClient) WatchUntilReady(ns string, r io.Reader, t int64) error { +func (p *PrintingKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { _, err := io.Copy(p.Out, r) return err } // Update implements KubeClient Update. -func (p *PrintingKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, recreate bool) error { +func (p *PrintingKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error { _, err := io.Copy(p.Out, modifiedReader) return err } diff --git a/pkg/tiller/environment/environment_test.go b/pkg/tiller/environment/environment_test.go index cae6c5c9e..a6621e5e7 100644 --- a/pkg/tiller/environment/environment_test.go +++ b/pkg/tiller/environment/environment_test.go @@ -37,7 +37,7 @@ func (e *mockEngine) Render(chrt *chart.Chart, v chartutil.Values) (map[string]s type mockKubeClient struct{} -func (k *mockKubeClient) Create(ns string, r io.Reader) error { +func (k *mockKubeClient) Create(ns string, r io.Reader, timeout int64, shouldWait bool) error { return nil } func (k *mockKubeClient) Get(ns string, r io.Reader) (string, error) { @@ -46,10 +46,10 @@ func (k *mockKubeClient) Get(ns string, r io.Reader) (string, error) { func (k *mockKubeClient) Delete(ns string, r io.Reader) error { return nil } -func (k *mockKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, recreate bool) error { +func (k *mockKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error { return nil } -func (k *mockKubeClient) WatchUntilReady(ns string, r io.Reader, t int64) error { +func (k *mockKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { return nil } func (k *mockKubeClient) Build(ns string, reader io.Reader) (kube.Result, error) { @@ -91,7 +91,7 @@ func TestKubeClient(t *testing.T) { b.WriteString(content) } - if err := env.KubeClient.Create("sharry-bobbins", b); err != nil { + if err := env.KubeClient.Create("sharry-bobbins", b, 300, false); err != nil { t.Errorf("Kubeclient failed: %s", err) } } diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index 3cc0bd784..9f91ff8b8 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -299,7 +299,7 @@ func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.R } } - if err := s.performKubeUpdate(originalRelease, updatedRelease, req.Recreate); err != nil { + if err := s.performKubeUpdate(originalRelease, updatedRelease, req.Recreate, req.Timeout, req.Wait); err != nil { log.Printf("warning: Release Upgrade %q failed: %s", updatedRelease.Name, err) originalRelease.Info.Status.Code = release.Status_SUPERSEDED updatedRelease.Info.Status.Code = release.Status_FAILED @@ -453,7 +453,7 @@ func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.R } } - if err := s.performKubeUpdate(currentRelease, targetRelease, req.Recreate); err != nil { + if err := s.performKubeUpdate(currentRelease, targetRelease, req.Recreate, req.Timeout, req.Wait); err != nil { log.Printf("warning: Release Rollback %q failed: %s", targetRelease.Name, err) currentRelease.Info.Status.Code = release.Status_SUPERSEDED targetRelease.Info.Status.Code = release.Status_FAILED @@ -477,11 +477,11 @@ func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.R return res, nil } -func (s *ReleaseServer) performKubeUpdate(currentRelease, targetRelease *release.Release, recreate bool) error { +func (s *ReleaseServer) performKubeUpdate(currentRelease, targetRelease *release.Release, recreate bool, timeout int64, shouldWait bool) error { kubeCli := s.env.KubeClient current := bytes.NewBufferString(currentRelease.Manifest) target := bytes.NewBufferString(targetRelease.Manifest) - return kubeCli.Update(targetRelease.Namespace, current, target, recreate) + return kubeCli.Update(targetRelease.Namespace, current, target, recreate, timeout, shouldWait) } // prepareRollback finds the previous release and prepares a new release object with @@ -820,7 +820,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install // so as to append to the old release's history r.Version = old.Version + 1 - if err := s.performKubeUpdate(old, r, false); err != nil { + if err := s.performKubeUpdate(old, r, false, req.Timeout, req.Wait); err != nil { log.Printf("warning: Release replace %q failed: %s", r.Name, err) old.Info.Status.Code = release.Status_SUPERSEDED r.Info.Status.Code = release.Status_FAILED @@ -833,7 +833,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install // nothing to replace, create as normal // regular manifests b := bytes.NewBufferString(r.Manifest) - if err := s.env.KubeClient.Create(r.Namespace, b); err != nil { + if err := s.env.KubeClient.Create(r.Namespace, b, req.Timeout, req.Wait); err != nil { log.Printf("warning: Release %q failed: %s", r.Name, err) r.Info.Status.Code = release.Status_FAILED s.recordRelease(r, false) @@ -885,14 +885,14 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin } b := bytes.NewBufferString(h.Manifest) - if err := kubeCli.Create(namespace, b); err != nil { + if err := kubeCli.Create(namespace, b, timeout, false); err != nil { log.Printf("warning: Release %q %s %s failed: %s", name, hook, h.Path, err) return err } // No way to rewind a bytes.Buffer()? b.Reset() b.WriteString(h.Manifest) - if err := kubeCli.WatchUntilReady(namespace, b, timeout); err != nil { + if err := kubeCli.WatchUntilReady(namespace, b, timeout, false); err != nil { log.Printf("warning: Release %q %s %s could not complete: %s", name, hook, h.Path, err) return err } diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go index 1a414013f..909119f1b 100644 --- a/pkg/tiller/release_server_test.go +++ b/pkg/tiller/release_server_test.go @@ -1390,7 +1390,7 @@ type updateFailingKubeClient struct { environment.PrintingKubeClient } -func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool) error { +func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error { return errors.New("Failed update in kube client") } @@ -1404,7 +1404,7 @@ type hookFailingKubeClient struct { environment.PrintingKubeClient } -func (h *hookFailingKubeClient) WatchUntilReady(ns string, r io.Reader, t int64) error { +func (h *hookFailingKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { return errors.New("Failed watch") } From 5b116247b892504b16d98b0722ace8f974aa3758 Mon Sep 17 00:00:00 2001 From: Taylor Thomas Date: Fri, 20 Jan 2017 11:06:26 -0800 Subject: [PATCH 2/2] Adds documentation for `--wait` and other new command line flags --- docs/using_helm.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/using_helm.md b/docs/using_helm.md index 93b0ff49b..27ea04adb 100644 --- a/docs/using_helm.md +++ b/docs/using_helm.md @@ -224,7 +224,8 @@ the rest of the defaults for that chart. There are two ways to pass configuration data during install: -- `--values` (or `-f`): Specify a YAML file with overrides. +- `--values` (or `-f`): Specify a YAML file with overrides. This can be specified multiple times + and the rightmost file will take precedence - `--set`: Specify overrides on the command line. If both are used, `--set` values are merged into `--values` with higher precedence. @@ -334,6 +335,24 @@ 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. +## Helpful Options for Install/Upgrade/Rollback +There are several other helpful options you can specify for customizing the +behavior of Helm during an install/upgrade/rollback. Please note that this +is not a full list of cli flags. To see a description of all flags, just run +`helm --help`. + +- `--timeout`: A value in seconds to wait for Kubernetes commands to complete + This defaults to 300 (5 minutes) +- `--wait`: Waits until all Pods are in a ready state, PVCs are bound, and + Services have and IP address (and Ingress if a `LoadBalancer`) before + marking the release as successful. It will wait for as long as the + `--timeout` value. If timeout is reached, the release will be marked as + `FAILED`. +- `--no-hooks`: This skips running hooks for the command +- `--recreate-pods` (only available for `upgrade` and `rollback`): This flag + will cause all pods to be recreated (with the exception of pods belonging to + deployments) + ## 'helm delete': Deleting a Release When it is time to uninstall or delete a release from the cluster, use