feat(*): Adds support for timeout flag

Installs, rollback, upgrade, and delete now accept a `--timeout` flag
that allows the user to specify the maximum number of seconds that
any kubernetes command can take.

Closes #1678
pull/1693/head
Taylor Thomas 8 years ago
parent 5fc020f0a1
commit 774789c79a

@ -190,6 +190,8 @@ message UpdateReleaseRequest {
bool disable_hooks = 5; bool disable_hooks = 5;
// Performs pods restart for resources if applicable // Performs pods restart for resources if applicable
bool recreate = 6; bool recreate = 6;
// timeout specifies the max amount of time any kubernetes client command can run.
int64 timeout = 7;
} }
// UpdateReleaseResponse is the response to an update request. // UpdateReleaseResponse is the response to an update request.
@ -208,6 +210,8 @@ message RollbackReleaseRequest {
int32 version = 4; int32 version = 4;
// Performs pods restart for resources if applicable // Performs pods restart for resources if applicable
bool recreate = 5; bool recreate = 5;
// timeout specifies the max amount of time any kubernetes client command can run.
int64 timeout = 6;
} }
// RollbackReleaseResponse is the response to an update request. // RollbackReleaseResponse is the response to an update request.
@ -239,6 +243,9 @@ message InstallReleaseRequest {
// ReuseName requests that Tiller re-uses a name, instead of erroring out. // ReuseName requests that Tiller re-uses a name, instead of erroring out.
bool reuse_name = 7; bool reuse_name = 7;
// timeout specifies the max amount of time any kubernetes client command can run.
int64 timeout = 8;
} }
// InstallReleaseResponse is the response from a release installation. // InstallReleaseResponse is the response from a release installation.
@ -254,6 +261,8 @@ message UninstallReleaseRequest {
bool disable_hooks = 2; bool disable_hooks = 2;
// Purge removes the release from the store and make its name free for later use. // Purge removes the release from the store and make its name free for later use.
bool purge = 3; bool purge = 3;
// timeout specifies the max amount of time any kubernetes client command can run.
int64 timeout = 4;
} }
// UninstallReleaseResponse represents a successful response to an uninstall request. // UninstallReleaseResponse represents a successful response to an uninstall request.

@ -39,6 +39,7 @@ type deleteCmd struct {
dryRun bool dryRun bool
disableHooks bool disableHooks bool
purge bool purge bool
timeout int64
out io.Writer out io.Writer
client helm.Interface client helm.Interface
@ -77,6 +78,7 @@ func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&del.dryRun, "dry-run", false, "simulate a delete") f.BoolVar(&del.dryRun, "dry-run", false, "simulate a delete")
f.BoolVar(&del.disableHooks, "no-hooks", false, "prevent hooks from running during deletion") f.BoolVar(&del.disableHooks, "no-hooks", false, "prevent hooks from running during deletion")
f.BoolVar(&del.purge, "purge", false, "remove the release from the store and make its name free for later use") f.BoolVar(&del.purge, "purge", false, "remove the release from the store and make its name free for later use")
f.Int64Var(&del.timeout, "timeout", 300, "time in seconds to wait for any individual kubernetes operation (like Jobs for hooks)")
return cmd return cmd
} }
@ -86,6 +88,7 @@ func (d *deleteCmd) run() error {
helm.DeleteDryRun(d.dryRun), helm.DeleteDryRun(d.dryRun),
helm.DeleteDisableHooks(d.disableHooks), helm.DeleteDisableHooks(d.disableHooks),
helm.DeletePurge(d.purge), helm.DeletePurge(d.purge),
helm.DeleteTimeout(d.timeout),
} }
res, err := d.client.DeleteRelease(d.name, opts...) res, err := d.client.DeleteRelease(d.name, opts...)
if res != nil && res.Info != "" { if res != nil && res.Info != "" {

@ -33,6 +33,13 @@ func TestDelete(t *testing.T) {
expected: "", // Output of a delete is an empty string and exit 0. expected: "", // Output of a delete is an empty string and exit 0.
resp: releaseMock(&releaseOptions{name: "aeneas"}), resp: releaseMock(&releaseOptions{name: "aeneas"}),
}, },
{
name: "delete with timeout",
args: []string{"aeneas"},
flags: []string{"--timeout", "120"},
expected: "",
resp: releaseMock(&releaseOptions{name: "aeneas"}),
},
{ {
name: "delete without hooks", name: "delete without hooks",
args: []string{"aeneas"}, args: []string{"aeneas"},

@ -104,6 +104,7 @@ type installCmd struct {
values string values string
nameTemplate string nameTemplate string
version string version string
timeout int64
} }
type valueFiles []string type valueFiles []string
@ -160,6 +161,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&inst.verify, "verify", false, "verify the package before installing it") f.BoolVar(&inst.verify, "verify", false, "verify the package before installing it")
f.StringVar(&inst.keyring, "keyring", defaultKeyring(), "location of public keys used for verification") 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.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)")
return cmd return cmd
} }
@ -195,7 +197,8 @@ func (i *installCmd) run() error {
helm.ReleaseName(i.name), helm.ReleaseName(i.name),
helm.InstallDryRun(i.dryRun), helm.InstallDryRun(i.dryRun),
helm.InstallReuseName(i.replace), helm.InstallReuseName(i.replace),
helm.InstallDisableHooks(i.disableHooks)) helm.InstallDisableHooks(i.disableHooks),
helm.InstallTimeout(i.timeout))
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)
} }

@ -82,6 +82,14 @@ func TestInstall(t *testing.T) {
expected: "aeneas", expected: "aeneas",
resp: releaseMock(&releaseOptions{name: "aeneas"}), resp: releaseMock(&releaseOptions{name: "aeneas"}),
}, },
// Install, with timeout
{
name: "install with a timeout",
args: []string{"testdata/testcharts/alpine"},
flags: strings.Split("--timeout 120", " "),
expected: "foobar",
resp: releaseMock(&releaseOptions{name: "foobar"}),
},
// Install, using the name-template // Install, using the name-template
{ {
name: "install with name-template", name: "install with name-template",

@ -39,6 +39,7 @@ type rollbackCmd struct {
disableHooks bool disableHooks bool
out io.Writer out io.Writer
client helm.Interface client helm.Interface
timeout int64
} }
func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command { func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
@ -74,6 +75,7 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&rollback.dryRun, "dry-run", false, "simulate a rollback") f.BoolVar(&rollback.dryRun, "dry-run", false, "simulate a rollback")
f.BoolVar(&rollback.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") 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.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)")
return cmd return cmd
} }
@ -85,7 +87,7 @@ func (r *rollbackCmd) run() error {
helm.RollbackRecreate(r.recreate), helm.RollbackRecreate(r.recreate),
helm.RollbackDisableHooks(r.disableHooks), helm.RollbackDisableHooks(r.disableHooks),
helm.RollbackVersion(r.revision), helm.RollbackVersion(r.revision),
) helm.RollbackTimeout(r.timeout))
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)
} }

@ -31,6 +31,12 @@ func TestRollbackCmd(t *testing.T) {
args: []string{"funny-honey", "1"}, args: []string{"funny-honey", "1"},
expected: "Rollback was a success! Happy Helming!", expected: "Rollback was a success! Happy Helming!",
}, },
{
name: "rollback a release with timeout",
args: []string{"funny-honey", "1"},
flags: []string{"--timeout", "120"},
expected: "Rollback was a success! Happy Helming!",
},
{ {
name: "rollback a release without revision", name: "rollback a release without revision",
args: []string{"funny-honey"}, args: []string{"funny-honey"},

@ -63,6 +63,7 @@ type upgradeCmd struct {
install bool install bool
namespace string namespace string
version string version string
timeout int64
} }
func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
@ -102,6 +103,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.BoolVarP(&upgrade.install, "install", "i", false, "if a release by this name doesn't already exist, run an install") f.BoolVarP(&upgrade.install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
f.StringVar(&upgrade.namespace, "namespace", "default", "namespace to install the release into (only used if --install is set)") f.StringVar(&upgrade.namespace, "namespace", "default", "namespace to install the release into (only used if --install is set)")
f.StringVar(&upgrade.version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used") 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.MarkDeprecated("disable-hooks", "use --no-hooks instead") f.MarkDeprecated("disable-hooks", "use --no-hooks instead")
@ -136,6 +138,7 @@ func (u *upgradeCmd) run() error {
keyring: u.keyring, keyring: u.keyring,
values: u.values, values: u.values,
namespace: u.namespace, namespace: u.namespace,
timeout: u.timeout,
} }
return ic.run() return ic.run()
} }
@ -152,7 +155,8 @@ func (u *upgradeCmd) run() error {
helm.UpdateValueOverrides(rawVals), helm.UpdateValueOverrides(rawVals),
helm.UpgradeDryRun(u.dryRun), helm.UpgradeDryRun(u.dryRun),
helm.UpgradeRecreate(u.recreate), helm.UpgradeRecreate(u.recreate),
helm.UpgradeDisableHooks(u.disableHooks)) helm.UpgradeDisableHooks(u.disableHooks),
helm.UpgradeTimeout(u.timeout))
if err != nil { if err != nil {
return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err)) return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err))
} }

@ -62,6 +62,23 @@ func TestUpgradeCmd(t *testing.T) {
t.Errorf("Error loading updated chart: %v", err) t.Errorf("Error loading updated chart: %v", err)
} }
// update chart version again
cfile = &chart.Metadata{
Name: "testUpgradeChart",
Description: "A Helm chart for Kubernetes",
Version: "0.1.3",
}
chartPath, err = chartutil.Create(cfile, tmpChart)
if err != nil {
t.Errorf("Error creating chart: %v", err)
}
var ch2 *chart.Chart
ch2, err = chartutil.Load(chartPath)
if err != nil {
t.Errorf("Error loading updated chart: %v", err)
}
tests := []releaseCase{ tests := []releaseCase{
{ {
name: "upgrade a release", name: "upgrade a release",
@ -69,6 +86,13 @@ func TestUpgradeCmd(t *testing.T) {
resp: releaseMock(&releaseOptions{name: "funny-bunny", version: 2, chart: ch}), resp: releaseMock(&releaseOptions{name: "funny-bunny", version: 2, chart: ch}),
expected: "funny-bunny has been upgraded. Happy Helming!\n", expected: "funny-bunny has been upgraded. Happy Helming!\n",
}, },
{
name: "upgrade a release with timeout",
args: []string{"funny-bunny", chartPath},
flags: []string{"--timeout", "120"},
resp: releaseMock(&releaseOptions{name: "funny-bunny", version: 3, chart: ch2}),
expected: "funny-bunny has been upgraded. Happy Helming!\n",
},
{ {
name: "install a release with 'upgrade --install'", name: "install a release with 'upgrade --install'",
args: []string{"zany-bunny", chartPath}, args: []string{"zany-bunny", chartPath},
@ -76,6 +100,13 @@ func TestUpgradeCmd(t *testing.T) {
resp: releaseMock(&releaseOptions{name: "zany-bunny", version: 1, chart: ch}), resp: releaseMock(&releaseOptions{name: "zany-bunny", version: 1, chart: ch}),
expected: "zany-bunny has been upgraded. Happy Helming!\n", expected: "zany-bunny has been upgraded. Happy Helming!\n",
}, },
{
name: "install a release with 'upgrade --install' and timeout",
args: []string{"crazy-bunny", chartPath},
flags: []string{"-i", "--timeout", "120"},
resp: releaseMock(&releaseOptions{name: "crazy-bunny", version: 1, chart: ch}),
expected: "crazy-bunny has been upgraded. Happy Helming!\n",
},
} }
cmd := func(c *fakeReleaseClient, out io.Writer) *cobra.Command { cmd := func(c *fakeReleaseClient, out io.Writer) *cobra.Command {

@ -151,6 +151,34 @@ func ReleaseName(name string) InstallOption {
} }
} }
// InstallTimeout specifies the number of seconds before kubernetes calls timeout
func InstallTimeout(timeout int64) InstallOption {
return func(opts *options) {
opts.instReq.Timeout = timeout
}
}
// UpgradeTimeout specifies the number of seconds before kubernetes calls timeout
func UpgradeTimeout(timeout int64) UpdateOption {
return func(opts *options) {
opts.updateReq.Timeout = timeout
}
}
// DeleteTimeout specifies the number of seconds before kubernetes calls timeout
func DeleteTimeout(timeout int64) DeleteOption {
return func(opts *options) {
opts.uninstallReq.Timeout = timeout
}
}
// RollbackTimeout specifies the number of seconds before kubernetes calls timeout
func RollbackTimeout(timeout int64) RollbackOption {
return func(opts *options) {
opts.rollbackReq.Timeout = timeout
}
}
// UpdateValueOverrides specifies a list of values to include when upgrading // UpdateValueOverrides specifies a list of values to include when upgrading
func UpdateValueOverrides(raw []byte) UpdateOption { func UpdateValueOverrides(raw []byte) UpdateOption {
return func(opts *options) { return func(opts *options) {

@ -258,6 +258,12 @@ func skipIfNotFound(err error) error {
return err return err
} }
func watchTimeout(t time.Duration) ResourceActorFunc {
return func(info *resource.Info) error {
return watchUntilReady(t, info)
}
}
// WatchUntilReady watches the resource given in the reader, and waits until it is ready. // WatchUntilReady watches the resource given in the reader, and waits until it is ready.
// //
// This function is mainly for hook implementations. It watches for a resource to // This function is mainly for hook implementations. It watches for a resource to
@ -270,10 +276,10 @@ func skipIfNotFound(err error) error {
// ascertained by watching the Status fields in a job's output. // ascertained by watching the Status fields in a job's output.
// //
// Handling for other kinds will be added as necessary. // Handling for other kinds will be added as necessary.
func (c *Client) WatchUntilReady(namespace string, reader io.Reader) error { func (c *Client) WatchUntilReady(namespace string, reader io.Reader, timeout int64) error {
// For jobs, there's also the option to do poll c.Jobs(namespace).Get(): // 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 // https://github.com/adamreese/kubernetes/blob/master/test/e2e/job.go#L291-L300
return perform(c, namespace, reader, watchUntilReady) return perform(c, namespace, reader, watchTimeout(time.Duration(timeout)*time.Second))
} }
func perform(c *Client, namespace string, reader io.Reader, fn ResourceActorFunc) error { func perform(c *Client, namespace string, reader io.Reader, fn ResourceActorFunc) error {
@ -382,15 +388,14 @@ func recreatePods(client *internalclientset.Clientset, namespace string, selecto
return nil return nil
} }
func watchUntilReady(info *resource.Info) error { func watchUntilReady(timeout time.Duration, info *resource.Info) error {
w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion) w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion)
if err != nil { if err != nil {
return err return err
} }
kind := info.Mapping.GroupVersionKind.Kind kind := info.Mapping.GroupVersionKind.Kind
log.Printf("Watching for changes to %s %s", kind, info.Name) log.Printf("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout)
timeout := time.Minute * 5
// What we watch for depends on the Kind. // What we watch for depends on the Kind.
// - For a Job, we watch for completion. // - For a Job, we watch for completion.

@ -248,6 +248,8 @@ type UpdateReleaseRequest struct {
DisableHooks bool `protobuf:"varint,5,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"` DisableHooks bool `protobuf:"varint,5,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"`
// Performs pods restart for resources if applicable // Performs pods restart for resources if applicable
Recreate bool `protobuf:"varint,6,opt,name=recreate" json:"recreate,omitempty"` Recreate bool `protobuf:"varint,6,opt,name=recreate" json:"recreate,omitempty"`
// timeout specifies the max amount of time any kubernetes client command can run.
Timeout int64 `protobuf:"varint,7,opt,name=timeout" json:"timeout,omitempty"`
} }
func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} } func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} }
@ -297,6 +299,8 @@ type RollbackReleaseRequest struct {
Version int32 `protobuf:"varint,4,opt,name=version" json:"version,omitempty"` Version int32 `protobuf:"varint,4,opt,name=version" json:"version,omitempty"`
// Performs pods restart for resources if applicable // Performs pods restart for resources if applicable
Recreate bool `protobuf:"varint,5,opt,name=recreate" json:"recreate,omitempty"` 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"`
} }
func (m *RollbackReleaseRequest) Reset() { *m = RollbackReleaseRequest{} } func (m *RollbackReleaseRequest) Reset() { *m = RollbackReleaseRequest{} }
@ -341,6 +345,8 @@ type InstallReleaseRequest struct {
Namespace string `protobuf:"bytes,6,opt,name=namespace" json:"namespace,omitempty"` Namespace string `protobuf:"bytes,6,opt,name=namespace" json:"namespace,omitempty"`
// ReuseName requests that Tiller re-uses a name, instead of erroring out. // ReuseName requests that Tiller re-uses a name, instead of erroring out.
ReuseName bool `protobuf:"varint,7,opt,name=reuse_name,json=reuseName" json:"reuse_name,omitempty"` 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"`
} }
func (m *InstallReleaseRequest) Reset() { *m = InstallReleaseRequest{} } func (m *InstallReleaseRequest) Reset() { *m = InstallReleaseRequest{} }
@ -387,6 +393,8 @@ type UninstallReleaseRequest struct {
DisableHooks bool `protobuf:"varint,2,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"` DisableHooks bool `protobuf:"varint,2,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"`
// Purge removes the release from the store and make its name free for later use. // Purge removes the release from the store and make its name free for later use.
Purge bool `protobuf:"varint,3,opt,name=purge" json:"purge,omitempty"` Purge bool `protobuf:"varint,3,opt,name=purge" json:"purge,omitempty"`
// timeout specifies the max amount of time any kubernetes client command can run.
Timeout int64 `protobuf:"varint,4,opt,name=timeout" json:"timeout,omitempty"`
} }
func (m *UninstallReleaseRequest) Reset() { *m = UninstallReleaseRequest{} } func (m *UninstallReleaseRequest) Reset() { *m = UninstallReleaseRequest{} }
@ -884,70 +892,72 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("hapi/services/tiller.proto", fileDescriptor0) } func init() { proto.RegisterFile("hapi/services/tiller.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 1027 bytes of a gzipped FileDescriptorProto // 1058 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x57, 0xdd, 0x6e, 0xe3, 0x44, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x57, 0xdd, 0x6e, 0xe3, 0xc4,
0x14, 0xae, 0xf3, 0x9f, 0xd3, 0x1f, 0xd2, 0xd9, 0xb4, 0x71, 0x2d, 0x40, 0x91, 0x11, 0x6c, 0x58, 0x17, 0xaf, 0xf3, 0xe1, 0x24, 0xa7, 0x1f, 0xff, 0x74, 0xb6, 0x1f, 0xae, 0xf5, 0x07, 0x45, 0x46,
0xd8, 0x14, 0xc2, 0x15, 0x12, 0x42, 0xea, 0x66, 0xa3, 0xb4, 0x50, 0xb2, 0xd2, 0x84, 0x82, 0xc4, 0xb0, 0x61, 0x61, 0x53, 0x08, 0x57, 0x48, 0x08, 0xa9, 0x9b, 0x8d, 0xd2, 0x42, 0xc9, 0x4a, 0x0e,
0x05, 0x91, 0x9b, 0x4c, 0xb6, 0x66, 0x1d, 0x4f, 0xf0, 0x4c, 0xaa, 0xcd, 0x23, 0xf0, 0x16, 0x88, 0x05, 0x89, 0x0b, 0x22, 0x37, 0x99, 0x6c, 0xcd, 0x3a, 0x9e, 0xe0, 0x99, 0x54, 0x9b, 0x5b, 0xee,
0xb7, 0xe2, 0x2d, 0xb8, 0xe7, 0x06, 0x79, 0x7e, 0xdc, 0xd8, 0xb5, 0xb3, 0x26, 0x37, 0xb6, 0x67, 0x78, 0x00, 0x5e, 0x80, 0xc7, 0xe0, 0xa9, 0x90, 0x78, 0x01, 0xe4, 0xf9, 0x70, 0x33, 0xae, 0x9d,
0xce, 0x99, 0x73, 0xce, 0xf7, 0xcd, 0xf9, 0x49, 0xc0, 0xba, 0x73, 0x96, 0xee, 0x39, 0x23, 0xc1, 0x35, 0xb9, 0x89, 0x3d, 0x73, 0xce, 0xfc, 0xce, 0x39, 0xbf, 0x39, 0x1f, 0x0e, 0xd8, 0x77, 0xde,
0xbd, 0x3b, 0x25, 0xec, 0x9c, 0xbb, 0x9e, 0x47, 0x82, 0xee, 0x32, 0xa0, 0x9c, 0xa2, 0x66, 0x28, 0xc2, 0x3f, 0xa7, 0x38, 0xba, 0xf7, 0x27, 0x98, 0x9e, 0x33, 0x3f, 0x08, 0x70, 0xd4, 0x59, 0x44,
0xeb, 0x6a, 0x59, 0x57, 0xca, 0xac, 0x53, 0x71, 0x62, 0x7a, 0xe7, 0x04, 0x5c, 0x3e, 0xa5, 0xb6, 0x84, 0x11, 0x74, 0x14, 0xcb, 0x3a, 0x4a, 0xd6, 0x11, 0x32, 0xfb, 0x84, 0x9f, 0x98, 0xdc, 0x79,
0xd5, 0xda, 0xdc, 0xa7, 0xfe, 0xdc, 0x7d, 0xad, 0x04, 0xd2, 0x45, 0x40, 0x3c, 0xe2, 0x30, 0xa2, 0x11, 0x13, 0xbf, 0x42, 0xdb, 0x3e, 0x5d, 0xdf, 0x27, 0xe1, 0xcc, 0x7f, 0x2d, 0x05, 0xc2, 0x44,
0xdf, 0xb1, 0x43, 0x5a, 0xe6, 0xfa, 0x73, 0xaa, 0x04, 0x67, 0x31, 0x01, 0xe3, 0x0e, 0x5f, 0xb1, 0x84, 0x03, 0xec, 0x51, 0xac, 0x9e, 0xda, 0x21, 0x25, 0xf3, 0xc3, 0x19, 0x91, 0x82, 0x33, 0x4d,
0x98, 0xbd, 0x7b, 0x12, 0x30, 0x97, 0xfa, 0xfa, 0x2d, 0x65, 0xf6, 0x5f, 0x05, 0x78, 0x72, 0xed, 0x40, 0x99, 0xc7, 0x96, 0x54, 0xc3, 0xbb, 0xc7, 0x11, 0xf5, 0x49, 0xa8, 0x9e, 0x42, 0xe6, 0xfc,
0x32, 0x8e, 0xe5, 0x41, 0x86, 0xc9, 0xef, 0x2b, 0xc2, 0x38, 0x6a, 0x42, 0xd9, 0x73, 0x17, 0x2e, 0x59, 0x82, 0x27, 0xd7, 0x3e, 0x65, 0xae, 0x38, 0x48, 0x5d, 0xfc, 0xeb, 0x12, 0x53, 0x86, 0x8e,
0x37, 0x8d, 0xb6, 0xd1, 0x29, 0x62, 0xb9, 0x40, 0xa7, 0x50, 0xa1, 0xf3, 0x39, 0x23, 0xdc, 0x2c, 0xa0, 0x1a, 0xf8, 0x73, 0x9f, 0x59, 0x46, 0xcb, 0x68, 0x97, 0x5d, 0xb1, 0x40, 0x27, 0x60, 0x92,
0xb4, 0x8d, 0x4e, 0x1d, 0xab, 0x15, 0xfa, 0x16, 0xaa, 0x8c, 0x06, 0x7c, 0x72, 0xbb, 0x36, 0x8b, 0xd9, 0x8c, 0x62, 0x66, 0x95, 0x5a, 0x46, 0xbb, 0xe1, 0xca, 0x15, 0xfa, 0x1a, 0x6a, 0x94, 0x44,
0x6d, 0xa3, 0x73, 0xd4, 0xfb, 0xb8, 0x9b, 0x46, 0x45, 0x37, 0xf4, 0x34, 0xa6, 0x01, 0xef, 0x86, 0x6c, 0x7c, 0xbb, 0xb2, 0xca, 0x2d, 0xa3, 0x7d, 0xd0, 0xfd, 0xb0, 0x93, 0x45, 0x45, 0x27, 0xb6,
0x8f, 0x17, 0x6b, 0x5c, 0x61, 0xe2, 0x1d, 0xda, 0x9d, 0xbb, 0x1e, 0x27, 0x81, 0x59, 0x92, 0x76, 0x34, 0x22, 0x11, 0xeb, 0xc4, 0x3f, 0x2f, 0x56, 0xae, 0x49, 0xf9, 0x33, 0xc6, 0x9d, 0xf9, 0x01,
0xe5, 0x0a, 0x0d, 0x01, 0x84, 0x5d, 0x1a, 0xcc, 0x48, 0x60, 0x96, 0x85, 0xe9, 0x4e, 0x0e, 0xd3, 0xc3, 0x91, 0x55, 0x11, 0xb8, 0x62, 0x85, 0x06, 0x00, 0x1c, 0x97, 0x44, 0x53, 0x1c, 0x59, 0x55,
0xaf, 0x42, 0x7d, 0x5c, 0x67, 0xfa, 0x13, 0x7d, 0x03, 0x07, 0x92, 0x92, 0xc9, 0x94, 0xce, 0x08, 0x0e, 0xdd, 0x2e, 0x00, 0xfd, 0x2a, 0xd6, 0x77, 0x1b, 0x54, 0xbd, 0xa2, 0xaf, 0x60, 0x4f, 0x50,
0x33, 0x2b, 0xed, 0x62, 0xe7, 0xa8, 0x77, 0x26, 0x4d, 0x69, 0x86, 0xc7, 0x92, 0xb4, 0x3e, 0x9d, 0x32, 0x9e, 0x90, 0x29, 0xa6, 0x96, 0xd9, 0x2a, 0xb7, 0x0f, 0xba, 0x67, 0x02, 0x4a, 0x31, 0x3c,
0x11, 0xbc, 0x2f, 0xd5, 0xc3, 0x6f, 0x66, 0xff, 0x0a, 0x35, 0x6d, 0xde, 0xee, 0x41, 0x45, 0x06, 0x12, 0xa4, 0xf5, 0xc8, 0x14, 0xbb, 0xbb, 0x42, 0x3d, 0x7e, 0xa7, 0xce, 0xcf, 0x50, 0x57, 0xf0,
0x8f, 0xf6, 0xa1, 0x7a, 0x33, 0xfa, 0x7e, 0xf4, 0xea, 0xe7, 0x51, 0x63, 0x0f, 0xd5, 0xa0, 0x34, 0x4e, 0x17, 0x4c, 0xe1, 0x3c, 0xda, 0x85, 0xda, 0xcd, 0xf0, 0xdb, 0xe1, 0xab, 0x1f, 0x87, 0xcd,
0xba, 0xf8, 0x61, 0xd0, 0x30, 0xd0, 0x31, 0x1c, 0x5e, 0x5f, 0x8c, 0x7f, 0x9c, 0xe0, 0xc1, 0xf5, 0x1d, 0x54, 0x87, 0xca, 0xf0, 0xe2, 0xbb, 0x7e, 0xd3, 0x40, 0x87, 0xb0, 0x7f, 0x7d, 0x31, 0xfa,
0xe0, 0x62, 0x3c, 0x78, 0xd9, 0x28, 0xd8, 0x1f, 0x42, 0x3d, 0x8a, 0x0a, 0x55, 0xa1, 0x78, 0x31, 0x7e, 0xec, 0xf6, 0xaf, 0xfb, 0x17, 0xa3, 0xfe, 0xcb, 0x66, 0xc9, 0x79, 0x1f, 0x1a, 0x89, 0x57,
0xee, 0xcb, 0x23, 0x2f, 0x07, 0xe3, 0x7e, 0xc3, 0xb0, 0xff, 0x30, 0xa0, 0x19, 0xbf, 0x04, 0xb6, 0xa8, 0x06, 0xe5, 0x8b, 0x51, 0x4f, 0x1c, 0x79, 0xd9, 0x1f, 0xf5, 0x9a, 0x86, 0xf3, 0xbb, 0x01,
0xa4, 0x3e, 0x23, 0xe1, 0x2d, 0x4c, 0xe9, 0xca, 0x8f, 0x6e, 0x41, 0x2c, 0x10, 0x82, 0x92, 0x4f, 0x47, 0xfa, 0x25, 0xd0, 0x05, 0x09, 0x29, 0x8e, 0x6f, 0x61, 0x42, 0x96, 0x61, 0x72, 0x0b, 0x7c,
0xde, 0xea, 0x3b, 0x10, 0xdf, 0xa1, 0x26, 0xa7, 0xdc, 0xf1, 0x04, 0xff, 0x45, 0x2c, 0x17, 0xe8, 0x81, 0x10, 0x54, 0x42, 0xfc, 0x56, 0xdd, 0x01, 0x7f, 0x8f, 0x35, 0x19, 0x61, 0x5e, 0xc0, 0xf9,
0x4b, 0xa8, 0x29, 0x70, 0xcc, 0x2c, 0xb5, 0x8b, 0x9d, 0xfd, 0xde, 0x49, 0x1c, 0xb2, 0xf2, 0x88, 0x2f, 0xbb, 0x62, 0x81, 0x3e, 0x87, 0xba, 0x0c, 0x8e, 0x5a, 0x95, 0x56, 0xb9, 0xbd, 0xdb, 0x3d,
0x23, 0x35, 0x7b, 0x08, 0xad, 0x21, 0xd1, 0x91, 0x48, 0x46, 0x74, 0x4e, 0x84, 0x7e, 0x9d, 0x05, 0xd6, 0x43, 0x96, 0x16, 0xdd, 0x44, 0xcd, 0x19, 0xc0, 0xe9, 0x00, 0x2b, 0x4f, 0x04, 0x23, 0x2a,
0x11, 0xc1, 0x84, 0x7e, 0x9d, 0x05, 0x41, 0x26, 0x54, 0x55, 0x42, 0x89, 0x70, 0xca, 0x58, 0x2f, 0x27, 0x62, 0xbb, 0xde, 0x1c, 0x73, 0x67, 0x62, 0xbb, 0xde, 0x1c, 0x23, 0x0b, 0x6a, 0x32, 0xa1,
0x6d, 0x0e, 0xe6, 0x63, 0x43, 0x0a, 0x57, 0x9a, 0xa5, 0x4f, 0xa0, 0x14, 0xa6, 0xb3, 0x30, 0xb3, 0xb8, 0x3b, 0x55, 0x57, 0x2d, 0x1d, 0x06, 0xd6, 0x63, 0x20, 0x19, 0x57, 0x16, 0xd2, 0x47, 0x50,
0xdf, 0x43, 0xf1, 0x38, 0xaf, 0xfc, 0x39, 0xc5, 0x42, 0x8e, 0xde, 0x87, 0x7a, 0xa8, 0xcf, 0x96, 0x89, 0xd3, 0x99, 0xc3, 0xec, 0x76, 0x91, 0xee, 0xe7, 0x55, 0x38, 0x23, 0x2e, 0x97, 0xa3, 0xff,
0xce, 0x94, 0x08, 0xb4, 0x75, 0xfc, 0xb0, 0x61, 0x5f, 0x6e, 0x7a, 0xed, 0x53, 0x9f, 0x13, 0x9f, 0x43, 0x23, 0xd6, 0xa7, 0x0b, 0x6f, 0x82, 0x79, 0xb4, 0x0d, 0xf7, 0x61, 0xc3, 0xb9, 0x5c, 0xb7,
0xef, 0x16, 0xff, 0x35, 0x9c, 0xa5, 0x58, 0x52, 0x00, 0xce, 0xa1, 0xaa, 0x42, 0x13, 0xd6, 0x32, 0xda, 0x23, 0x21, 0xc3, 0x21, 0xdb, 0xce, 0xff, 0x6b, 0x38, 0xcb, 0x40, 0x92, 0x01, 0x9c, 0x43,
0x79, 0xd5, 0x5a, 0xf6, 0xdf, 0x06, 0x34, 0x6f, 0x96, 0x33, 0x87, 0x13, 0x2d, 0xda, 0x12, 0xd4, 0x4d, 0xba, 0xc6, 0xd1, 0x72, 0x79, 0x55, 0x5a, 0xce, 0x3f, 0x06, 0x1c, 0xdd, 0x2c, 0xa6, 0x1e,
0x53, 0x28, 0x8b, 0xb6, 0xa0, 0xb8, 0x38, 0x96, 0xb6, 0x65, 0xef, 0xe8, 0x87, 0x4f, 0x2c, 0xe5, 0xc3, 0x4a, 0xb4, 0xc1, 0xa9, 0xa7, 0x50, 0xe5, 0x6d, 0x41, 0x72, 0x71, 0x28, 0xb0, 0x45, 0xef,
0xe8, 0x19, 0x54, 0xee, 0x1d, 0x6f, 0x45, 0x98, 0x20, 0x22, 0x62, 0x4d, 0x69, 0x8a, 0x9e, 0x82, 0xe8, 0xc5, 0xbf, 0xae, 0x90, 0xa3, 0x67, 0x60, 0xde, 0x7b, 0xc1, 0x12, 0x53, 0x4e, 0x44, 0xc2,
0x95, 0x06, 0x6a, 0x41, 0x75, 0x16, 0xac, 0x27, 0xc1, 0xca, 0x17, 0x45, 0x56, 0xc3, 0x95, 0x59, 0x9a, 0xd4, 0xe4, 0x3d, 0xc5, 0x95, 0x1a, 0xe8, 0x14, 0x6a, 0xd3, 0x68, 0x35, 0x8e, 0x96, 0x21,
0xb0, 0xc6, 0x2b, 0x1f, 0x7d, 0x04, 0x87, 0x33, 0x97, 0x39, 0xb7, 0x1e, 0x99, 0xdc, 0x51, 0xfa, 0x2f, 0xb2, 0xba, 0x6b, 0x4e, 0xa3, 0x95, 0xbb, 0x0c, 0xd1, 0x07, 0xb0, 0x3f, 0xf5, 0xa9, 0x77,
0x86, 0x89, 0x3a, 0xab, 0xe1, 0x03, 0xb5, 0x79, 0x19, 0xee, 0x21, 0x2b, 0xcc, 0xa4, 0x69, 0x40, 0x1b, 0xe0, 0xf1, 0x1d, 0x21, 0x6f, 0x28, 0xaf, 0xb3, 0xba, 0xbb, 0x27, 0x37, 0x2f, 0xe3, 0x3d,
0x1c, 0x4e, 0xcc, 0x8a, 0x90, 0x47, 0x6b, 0xfb, 0x12, 0x4e, 0x12, 0xd0, 0x76, 0x65, 0xe9, 0x4f, 0x64, 0xc7, 0x99, 0x34, 0x89, 0xb0, 0xc7, 0xb0, 0x65, 0x72, 0x79, 0xb2, 0x8e, 0x39, 0x64, 0xfe,
0x03, 0x4e, 0x31, 0xf5, 0xbc, 0x5b, 0x67, 0xfa, 0x26, 0x07, 0x4f, 0x1b, 0x90, 0x0a, 0xdb, 0x21, 0x1c, 0x93, 0x25, 0xb3, 0x6a, 0x3c, 0xfb, 0xd4, 0xd2, 0xb9, 0x84, 0xe3, 0x54, 0xd0, 0xdb, 0xf2,
0x15, 0x53, 0x20, 0x6d, 0x5c, 0x7d, 0x29, 0x76, 0xf5, 0x31, 0xb0, 0xe5, 0x04, 0xd8, 0xef, 0xa0, 0xf7, 0x97, 0x01, 0x27, 0x2e, 0x09, 0x82, 0x5b, 0x6f, 0xf2, 0xa6, 0x00, 0x83, 0x6b, 0xc1, 0x96,
0xf5, 0x28, 0xc2, 0x5d, 0xe1, 0xfe, 0x6b, 0xc0, 0xc9, 0x95, 0xcf, 0xb8, 0xe3, 0x79, 0x09, 0xb4, 0x36, 0x07, 0x5b, 0xce, 0x08, 0x76, 0x2d, 0x29, 0x2a, 0x5a, 0x52, 0x68, 0x34, 0x54, 0xf3, 0x69,
0x51, 0x06, 0x18, 0xb9, 0x33, 0xa0, 0xf0, 0x7f, 0x32, 0xa0, 0x18, 0xa3, 0x4b, 0x73, 0x5b, 0xda, 0x30, 0x75, 0x1a, 0xbe, 0x81, 0xd3, 0x47, 0xbe, 0x6f, 0x4b, 0xc4, 0x1f, 0x25, 0x38, 0xbe, 0x0a,
0xe0, 0x36, 0x57, 0x56, 0xc4, 0x6a, 0xb1, 0x92, 0xa8, 0x45, 0xf4, 0x01, 0x40, 0x40, 0x56, 0x8c, 0x29, 0xf3, 0x82, 0x20, 0xc5, 0x43, 0x92, 0x35, 0x46, 0xe1, 0xac, 0x29, 0xfd, 0x97, 0xac, 0x29,
0x4c, 0x84, 0xf1, 0xaa, 0x38, 0x5f, 0x17, 0x3b, 0x23, 0x67, 0x41, 0xec, 0x2b, 0x38, 0x4d, 0x82, 0x6b, 0x44, 0x2a, 0xd6, 0x2b, 0x6b, 0xac, 0x17, 0xca, 0x24, 0xad, 0x7e, 0xcd, 0x54, 0xfd, 0xa2,
0xdf, 0x95, 0xc8, 0x3b, 0x68, 0xdd, 0xf8, 0x6e, 0x2a, 0x93, 0x69, 0x79, 0xf3, 0x08, 0x5b, 0x21, 0xf7, 0x00, 0x22, 0xbc, 0xa4, 0x78, 0xcc, 0xc1, 0x6b, 0xfc, 0x7c, 0x83, 0xef, 0x0c, 0x65, 0xb9,
0x05, 0x5b, 0x13, 0xca, 0xcb, 0x55, 0xf0, 0x9a, 0x28, 0xae, 0xe4, 0xc2, 0x9e, 0x80, 0xf9, 0xd8, 0x2a, 0x8e, 0xeb, 0x3a, 0xc7, 0x57, 0x70, 0x92, 0xa6, 0x65, 0x5b, 0x8a, 0x7f, 0x33, 0xe0, 0xf4,
0xd3, 0x8e, 0x61, 0x87, 0xb1, 0x45, 0x2d, 0xaf, 0x2e, 0xdb, 0x9b, 0xfd, 0x04, 0x8e, 0x87, 0x84, 0x26, 0xf4, 0x33, 0x49, 0xce, 0x4a, 0xb6, 0x47, 0x61, 0x97, 0x32, 0xc2, 0x3e, 0x82, 0xea, 0x62,
0xff, 0x24, 0x33, 0x51, 0x81, 0xb0, 0x07, 0x80, 0x36, 0x37, 0x1f, 0xfc, 0xa9, 0xad, 0xb8, 0x3f, 0x19, 0xbd, 0xc6, 0x92, 0x46, 0xb1, 0x58, 0x8f, 0xa7, 0xa2, 0xc7, 0x33, 0x06, 0xeb, 0xb1, 0x0f,
0x3d, 0xe1, 0xb5, 0xbe, 0xd6, 0xb2, 0xbf, 0x16, 0xb6, 0x2f, 0x5d, 0xc6, 0x69, 0xb0, 0xde, 0x46, 0x5b, 0x46, 0x14, 0x7b, 0x9d, 0xf4, 0xd6, 0x86, 0xe8, 0xa3, 0xce, 0x13, 0x38, 0x1c, 0x60, 0xf6,
0x50, 0x03, 0x8a, 0x0b, 0xe7, 0xad, 0xea, 0x88, 0xe1, 0xa7, 0x3d, 0x14, 0x11, 0x44, 0x47, 0x55, 0x83, 0x48, 0x6c, 0x19, 0x9e, 0xd3, 0x07, 0xb4, 0xbe, 0xf9, 0x60, 0x4f, 0x6e, 0xe9, 0xf6, 0xd4,
0x04, 0x9b, 0xf3, 0xc5, 0xc8, 0x35, 0x5f, 0x7a, 0xff, 0x54, 0xe1, 0x48, 0x0f, 0x05, 0x39, 0xc2, 0xa7, 0x84, 0xd2, 0x57, 0x5a, 0xce, 0x97, 0x1c, 0xfb, 0xd2, 0xa7, 0x8c, 0x44, 0xab, 0x4d, 0xd4,
0x91, 0x0b, 0x07, 0x9b, 0xd3, 0x0f, 0x7d, 0x9a, 0x3d, 0xe1, 0x13, 0x3f, 0x53, 0xac, 0x67, 0x79, 0x35, 0xa1, 0x3c, 0xf7, 0xde, 0xca, 0xd6, 0x1b, 0xbf, 0x3a, 0x03, 0xee, 0x41, 0x72, 0x54, 0x7a,
0x54, 0x65, 0xb0, 0xf6, 0xde, 0x17, 0x06, 0x62, 0xd0, 0x48, 0x0e, 0x25, 0xf4, 0x3c, 0xdd, 0x46, 0xb0, 0x3e, 0xc8, 0x8c, 0x42, 0x83, 0xac, 0xfb, 0x77, 0x0d, 0x0e, 0xd4, 0xf4, 0x11, 0xdf, 0x0a,
0xc6, 0x14, 0xb4, 0xba, 0x79, 0xd5, 0xb5, 0x5b, 0x74, 0x2f, 0x68, 0x8f, 0x4f, 0x12, 0xf4, 0x4e, 0xc8, 0x87, 0xbd, 0xf5, 0x31, 0x8b, 0x3e, 0xce, 0xff, 0x94, 0x48, 0x7d, 0x0f, 0xd9, 0xcf, 0x8a,
0x33, 0xf1, 0xe1, 0x65, 0x9d, 0xe7, 0xd6, 0x8f, 0xfc, 0xfe, 0x06, 0x87, 0xb1, 0xbe, 0x8c, 0x32, 0xa8, 0x0a, 0x67, 0x9d, 0x9d, 0xcf, 0x0c, 0x44, 0xa1, 0x99, 0x9e, 0x7e, 0xe8, 0x79, 0x36, 0x46,
0xd8, 0x4a, 0x9b, 0x4b, 0xd6, 0x67, 0xb9, 0x74, 0x23, 0x5f, 0x0b, 0x38, 0x8a, 0x17, 0x33, 0xca, 0xce, 0xb8, 0xb5, 0x3b, 0x45, 0xd5, 0x95, 0x59, 0x74, 0xcf, 0x69, 0xd7, 0x47, 0x16, 0x7a, 0x27,
0x30, 0x90, 0xda, 0xef, 0xac, 0xcf, 0xf3, 0x29, 0x47, 0xee, 0x18, 0x34, 0x92, 0x65, 0x98, 0x75, 0x8c, 0x3e, 0x25, 0xed, 0xf3, 0xc2, 0xfa, 0x89, 0xdd, 0x5f, 0x60, 0x5f, 0x6b, 0xf3, 0x28, 0x87,
0x8f, 0x19, 0x8d, 0x21, 0xeb, 0x1e, 0xb3, 0xaa, 0xdb, 0xde, 0x43, 0x0e, 0xc0, 0x43, 0x15, 0xa2, 0xad, 0xac, 0x01, 0x68, 0x7f, 0x52, 0x48, 0x37, 0xb1, 0x35, 0x87, 0x03, 0xbd, 0xce, 0x51, 0x0e,
0xa7, 0x99, 0x17, 0x12, 0x2f, 0x5e, 0xab, 0xf3, 0x6e, 0xc5, 0xc8, 0xc5, 0x12, 0xde, 0x4b, 0x4c, 0x40, 0x66, 0x93, 0xb4, 0x3f, 0x2d, 0xa6, 0x9c, 0x98, 0xa3, 0xd0, 0x4c, 0x97, 0x61, 0xde, 0x3d,
0x17, 0x94, 0x41, 0x4d, 0xfa, 0x98, 0xb4, 0x9e, 0xe7, 0xd4, 0x4e, 0x80, 0x52, 0x85, 0xbd, 0x05, 0xe6, 0xb4, 0x8c, 0xbc, 0x7b, 0xcc, 0xab, 0x6e, 0x67, 0x07, 0x79, 0x00, 0x0f, 0x55, 0x88, 0x9e,
0x54, 0xbc, 0x6b, 0x6c, 0x01, 0x95, 0xe8, 0x11, 0xf6, 0xde, 0x0b, 0xf8, 0xa5, 0xa6, 0xf5, 0x6e, 0xe6, 0x5e, 0x88, 0x5e, 0xbc, 0x76, 0xfb, 0xdd, 0x8a, 0x89, 0x89, 0x05, 0xfc, 0x2f, 0x35, 0x92,
0x2b, 0xe2, 0x6f, 0xc7, 0x57, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x95, 0x81, 0x46, 0x8e, 0x47, 0x50, 0x0e, 0x35, 0xd9, 0x53, 0xd7, 0x7e, 0x5e, 0x50, 0x3b, 0x15, 0x94, 0x2c, 0xec, 0x0d, 0x41,
0x0d, 0x00, 0x00, 0xe9, 0x5d, 0x63, 0x43, 0x50, 0xa9, 0x1e, 0xe1, 0xec, 0xbc, 0x80, 0x9f, 0xea, 0x4a, 0xef, 0xd6,
0xe4, 0xff, 0x6f, 0xbe, 0xf8, 0x37, 0x00, 0x00, 0xff, 0xff, 0xae, 0xe0, 0xb8, 0xc3, 0xb0, 0x0d,
0x00, 0x00,
} }

@ -122,7 +122,7 @@ type KubeClient interface {
// For Jobs, "ready" means the job ran to completion (excited without error). // 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 // For all other kinds, it means the kind was created or modified without
// error. // error.
WatchUntilReady(namespace string, reader io.Reader) error WatchUntilReady(namespace string, reader io.Reader, timeout int64) error
// Update updates one or more resources or creates the resource // Update updates one or more resources or creates the resource
// if it doesn't exist // if it doesn't exist
@ -161,7 +161,7 @@ func (p *PrintingKubeClient) Delete(ns string, r io.Reader) error {
} }
// WatchUntilReady implements KubeClient WatchUntilReady. // WatchUntilReady implements KubeClient WatchUntilReady.
func (p *PrintingKubeClient) WatchUntilReady(ns string, r io.Reader) error { func (p *PrintingKubeClient) WatchUntilReady(ns string, r io.Reader, t int64) error {
_, err := io.Copy(p.Out, r) _, err := io.Copy(p.Out, r)
return err return err
} }

@ -47,7 +47,7 @@ func (k *mockKubeClient) Delete(ns string, r io.Reader) error {
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) error {
return nil return nil
} }
func (k *mockKubeClient) WatchUntilReady(ns string, r io.Reader) error { func (k *mockKubeClient) WatchUntilReady(ns string, r io.Reader, t int64) error {
return nil return nil
} }

@ -333,9 +333,9 @@ func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.R
return res, nil return res, nil
} }
// pre-ugrade hooks // pre-upgrade hooks
if !req.DisableHooks { if !req.DisableHooks {
if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, preUpgrade); err != nil { if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, preUpgrade, req.Timeout); err != nil {
return res, err return res, err
} }
} }
@ -351,7 +351,7 @@ func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.R
// post-upgrade hooks // post-upgrade hooks
if !req.DisableHooks { if !req.DisableHooks {
if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, postUpgrade); err != nil { if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, postUpgrade, req.Timeout); err != nil {
return res, err return res, err
} }
} }
@ -473,7 +473,7 @@ func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.R
// pre-rollback hooks // pre-rollback hooks
if !req.DisableHooks { if !req.DisableHooks {
if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, preRollback); err != nil { if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, preRollback, req.Timeout); err != nil {
return res, err return res, err
} }
} }
@ -489,7 +489,7 @@ func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.R
// post-rollback hooks // post-rollback hooks
if !req.DisableHooks { if !req.DisableHooks {
if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, postRollback); err != nil { if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, postRollback, req.Timeout); err != nil {
return res, err return res, err
} }
} }
@ -809,7 +809,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install
// pre-install hooks // pre-install hooks
if !req.DisableHooks { if !req.DisableHooks {
if err := s.execHook(r.Hooks, r.Name, r.Namespace, preInstall); err != nil { if err := s.execHook(r.Hooks, r.Name, r.Namespace, preInstall, req.Timeout); err != nil {
return res, err return res, err
} }
} }
@ -854,7 +854,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install
// post-install hooks // post-install hooks
if !req.DisableHooks { if !req.DisableHooks {
if err := s.execHook(r.Hooks, r.Name, r.Namespace, postInstall); err != nil { if err := s.execHook(r.Hooks, r.Name, r.Namespace, postInstall, req.Timeout); err != nil {
log.Printf("warning: Release %q failed post-install: %s", r.Name, err) log.Printf("warning: Release %q failed post-install: %s", r.Name, err)
r.Info.Status.Code = release.Status_FAILED r.Info.Status.Code = release.Status_FAILED
s.recordRelease(r, false) s.recordRelease(r, false)
@ -875,7 +875,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install
return res, nil return res, nil
} }
func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook string) error { func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook string, timeout int64) error {
kubeCli := s.env.KubeClient kubeCli := s.env.KubeClient
code, ok := events[hook] code, ok := events[hook]
if !ok { if !ok {
@ -903,7 +903,7 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin
// No way to rewind a bytes.Buffer()? // No way to rewind a bytes.Buffer()?
b.Reset() b.Reset()
b.WriteString(h.Manifest) b.WriteString(h.Manifest)
if err := kubeCli.WatchUntilReady(namespace, b); err != nil { if err := kubeCli.WatchUntilReady(namespace, b, timeout); err != nil {
log.Printf("warning: Release %q pre-install %s could not complete: %s", name, h.Path, err) log.Printf("warning: Release %q pre-install %s could not complete: %s", name, h.Path, err)
return err return err
} }
@ -964,7 +964,7 @@ func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
res := &services.UninstallReleaseResponse{Release: rel} res := &services.UninstallReleaseResponse{Release: rel}
if !req.DisableHooks { if !req.DisableHooks {
if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, preDelete); err != nil { if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, preDelete, req.Timeout); err != nil {
return res, err return res, err
} }
} }
@ -1010,7 +1010,7 @@ func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
} }
if !req.DisableHooks { if !req.DisableHooks {
if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, postDelete); err != nil { if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, postDelete, req.Timeout); err != nil {
es = append(es, err.Error()) es = append(es, err.Error())
} }
} }

@ -1375,7 +1375,7 @@ type hookFailingKubeClient struct {
environment.PrintingKubeClient environment.PrintingKubeClient
} }
func (h *hookFailingKubeClient) WatchUntilReady(ns string, r io.Reader) error { func (h *hookFailingKubeClient) WatchUntilReady(ns string, r io.Reader, t int64) error {
return errors.New("Failed watch") return errors.New("Failed watch")
} }

Loading…
Cancel
Save