Merge branch 'master' into feat/app-version

Signed-off-by: Kevin Labesse <kevin@labesse.me>
pull/5492/head
Kevin Labesse 7 years ago
commit 0548cd04b6

@ -4,7 +4,7 @@ jobs:
working_directory: /go/src/k8s.io/helm working_directory: /go/src/k8s.io/helm
parallelism: 3 parallelism: 3
docker: docker:
- image: golang:1.12.1 - image: golang:1.12.2
environment: environment:
PROJECT_NAME: "kubernetes-helm" PROJECT_NAME: "kubernetes-helm"
steps: steps:

@ -1,6 +1,6 @@
DOCKER_REGISTRY ?= gcr.io DOCKER_REGISTRY ?= gcr.io
IMAGE_PREFIX ?= kubernetes-helm IMAGE_PREFIX ?= kubernetes-helm
DEV_IMAGE ?= golang:1.12.1 DEV_IMAGE ?= golang:1.12.2
SHORT_NAME ?= tiller SHORT_NAME ?= tiller
SHORT_NAME_RUDDER ?= rudder SHORT_NAME_RUDDER ?= rudder
TARGETS ?= darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64 TARGETS ?= darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64

@ -92,6 +92,7 @@ message UpgradeReleaseRequest{
bool Wait = 4; bool Wait = 4;
bool Recreate = 5; bool Recreate = 5;
bool Force = 6; bool Force = 6;
bool CleanupOnFail = 7;
} }
message UpgradeReleaseResponse{ message UpgradeReleaseResponse{
hapi.release.Release release = 1; hapi.release.Release release = 1;
@ -105,6 +106,7 @@ message RollbackReleaseRequest{
bool Wait = 4; bool Wait = 4;
bool Recreate = 5; bool Recreate = 5;
bool Force = 6; bool Force = 6;
bool CleanupOnFail = 7;
} }
message RollbackReleaseResponse{ message RollbackReleaseResponse{
hapi.release.Release release = 1; hapi.release.Release release = 1;

@ -212,8 +212,10 @@ message UpdateReleaseRequest {
bool force = 11; bool force = 11;
// Description, if set, will set the description for the updated release // Description, if set, will set the description for the updated release
string description = 12; string description = 12;
// Render subchart notes if enabled // Render subchart notes if enabled
bool subNotes = 13; bool subNotes = 13;
// Allow deletion of new resources created in this update when update failed
bool cleanup_on_fail = 14;
} }
// UpdateReleaseResponse is the response to an update request. // UpdateReleaseResponse is the response to an update request.
@ -241,6 +243,8 @@ message RollbackReleaseRequest {
bool force = 8; bool force = 8;
// Description, if set, will set the description for the rollback // Description, if set, will set the description for the rollback
string description = 9; string description = 9;
// Allow deletion of new resources created in this rollback when rollback failed
bool cleanup_on_fail = 10;
} }
// RollbackReleaseResponse is the response to an update request. // RollbackReleaseResponse is the response to an update request.
@ -283,8 +287,8 @@ message InstallReleaseRequest {
// Description, if set, will set the description for the installed release // Description, if set, will set the description for the installed release
string description = 11; string description = 11;
bool subNotes = 12; bool subNotes = 12;
} }

@ -73,7 +73,7 @@ the dependency charts stored locally. The path should start with a prefix of
repository: "file://../dependency_chart/nginx" repository: "file://../dependency_chart/nginx"
If the dependency chart is retrieved locally, it is not required to have the If the dependency chart is retrieved locally, it is not required to have the
repository added to helm by "helm add repo". Version matching is also supported repository added to helm by "helm repo add". Version matching is also supported
for this case. for this case.
` `

@ -27,27 +27,26 @@ import (
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
) )
const inspectDesc = ` const (
inspectDesc = `
This command inspects a chart and displays information. It takes a chart reference This command inspects a chart and displays information. It takes a chart reference
('stable/drupal'), a full path to a directory or packaged chart, or a URL. ('stable/drupal'), a full path to a directory or packaged chart, or a URL.
Inspect prints the contents of the Chart.yaml file and the values.yaml file. Inspect prints the contents of the Chart.yaml file and the values.yaml file.
` `
inspectValuesDesc = `
const inspectValuesDesc = `
This command inspects a chart (directory, file, or URL) and displays the contents This command inspects a chart (directory, file, or URL) and displays the contents
of the values.yaml file of the values.yaml file
` `
inspectChartDesc = `
const inspectChartDesc = `
This command inspects a chart (directory, file, or URL) and displays the contents This command inspects a chart (directory, file, or URL) and displays the contents
of the Charts.yaml file of the Charts.yaml file
` `
readmeChartDesc = `
const readmeChartDesc = `
This command inspects a chart (directory, file, or URL) and displays the contents This command inspects a chart (directory, file, or URL) and displays the contents
of the README file of the README file
` `
)
type inspectCmd struct { type inspectCmd struct {
chartpath string chartpath string

@ -301,7 +301,7 @@ func (i *installCmd) run() error {
if i.appVersion != "" { if i.appVersion != "" {
if !chartRequested.Metadata.OverrideMetadata { if !chartRequested.Metadata.OverrideMetadata {
return fmt.Errorf("The chart maintainer has disallowed overriding the chart's package metadata. Please contact the maintainer to set `overrideChartMeta` in their Chart.yaml to `true`.") return fmt.Errorf("The chart maintainer has disallowed overriding the chart's package metadata. Please contact the maintainer to set `overrideMetadata` in their Chart.yaml to `true`.")
} }
chartRequested.Metadata.AppVersion = i.appVersion chartRequested.Metadata.AppVersion = i.appVersion

@ -24,7 +24,12 @@ import (
"k8s.io/helm/pkg/version" "k8s.io/helm/pkg/version"
) )
const defaultImage = "gcr.io/kubernetes-helm/tiller" const (
defaultImage = "gcr.io/kubernetes-helm/tiller"
fmtJSON OutputFormat = "json"
fmtYAML OutputFormat = "yaml"
)
// Options control how to install Tiller into a cluster, upgrade, and uninstall Tiller from a cluster. // Options control how to install Tiller into a cluster, upgrade, and uninstall Tiller from a cluster.
type Options struct { type Options struct {
@ -154,11 +159,6 @@ func (f *OutputFormat) Type() string {
return "OutputFormat" return "OutputFormat"
} }
const (
fmtJSON OutputFormat = "json"
fmtYAML OutputFormat = "yaml"
)
// Set validates and sets the value of the OutputFormat // Set validates and sets the value of the OutputFormat
func (f *OutputFormat) Set(s string) error { func (f *OutputFormat) Set(s string) error {
for _, of := range []OutputFormat{fmtJSON, fmtYAML} { for _, of := range []OutputFormat{fmtJSON, fmtYAML} {

@ -36,17 +36,18 @@ second is a revision (version) number. To see revision numbers, run
` `
type rollbackCmd struct { type rollbackCmd struct {
name string name string
revision int32 revision int32
dryRun bool dryRun bool
recreate bool recreate bool
force bool force bool
disableHooks bool disableHooks bool
out io.Writer out io.Writer
client helm.Interface client helm.Interface
timeout int64 timeout int64
wait bool wait bool
description string description string
cleanupOnFail bool
} }
func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command { func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
@ -87,6 +88,7 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
f.Int64Var(&rollback.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") 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, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.BoolVar(&rollback.wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout")
f.StringVar(&rollback.description, "description", "", "specify a description for the release") f.StringVar(&rollback.description, "description", "", "specify a description for the release")
f.BoolVar(&rollback.cleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this rollback when rollback failed")
// set defaults from environment // set defaults from environment
settings.InitTLS(f) settings.InitTLS(f)
@ -104,7 +106,8 @@ func (r *rollbackCmd) run() error {
helm.RollbackVersion(r.revision), helm.RollbackVersion(r.revision),
helm.RollbackTimeout(r.timeout), helm.RollbackTimeout(r.timeout),
helm.RollbackWait(r.wait), helm.RollbackWait(r.wait),
helm.RollbackDescription(r.description)) helm.RollbackDescription(r.description),
helm.RollbackCleanupOnFail(r.cleanupOnFail))
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)
} }

@ -33,6 +33,12 @@ import (
"k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/repo"
) )
const (
sep = "\v"
// verSep is a separator for version fields in map keys.
verSep = "$$"
)
// Result is a search result. // Result is a search result.
// //
// Score indicates how close it is to match. The higher the score, the longer // Score indicates how close it is to match. The higher the score, the longer
@ -49,16 +55,11 @@ type Index struct {
charts map[string]*repo.ChartVersion charts map[string]*repo.ChartVersion
} }
const sep = "\v"
// NewIndex creats a new Index. // NewIndex creats a new Index.
func NewIndex() *Index { func NewIndex() *Index {
return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}} return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}}
} }
// verSep is a separator for version fields in map keys.
const verSep = "$$"
// AddRepo adds a repository index to the search index. // AddRepo adds a repository index to the search index.
func (i *Index) AddRepo(rname string, ind *repo.IndexFile, all bool) { func (i *Index) AddRepo(rname string, ind *repo.IndexFile, all bool) {
ind.SortEntries() ind.SortEntries()

@ -84,39 +84,39 @@ which results in "pwd: 3jk$o2z=f\30with'quote".
` `
type upgradeCmd struct { type upgradeCmd struct {
release string release string
chart string chart string
out io.Writer out io.Writer
client helm.Interface client helm.Interface
dryRun bool dryRun bool
recreate bool recreate bool
force bool force bool
disableHooks bool disableHooks bool
valueFiles valueFiles valueFiles valueFiles
values []string values []string
stringValues []string stringValues []string
fileValues []string fileValues []string
verify bool verify bool
keyring string keyring string
install bool install bool
namespace string namespace string
version string version string
appVersion string appVersion string
timeout int64 timeout int64
resetValues bool resetValues bool
reuseValues bool reuseValues bool
wait bool wait bool
atomic bool atomic bool
repoURL string repoURL string
username string username string
password string password string
devel bool devel bool
subNotes bool subNotes bool
description string description string
cleanupOnFail bool
certFile string certFile string
keyFile string keyFile string
caFile string caFile string
} }
func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
@ -181,6 +181,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&upgrade.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.") f.BoolVar(&upgrade.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
f.BoolVar(&upgrade.subNotes, "render-subchart-notes", false, "render subchart notes along with parent") f.BoolVar(&upgrade.subNotes, "render-subchart-notes", false, "render subchart notes along with parent")
f.StringVar(&upgrade.description, "description", "", "specify the description to use for the upgrade, rather than the default") f.StringVar(&upgrade.description, "description", "", "specify the description to use for the upgrade, rather than the default")
f.BoolVar(&upgrade.cleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade failed")
f.MarkDeprecated("disable-hooks", "use --no-hooks instead") f.MarkDeprecated("disable-hooks", "use --no-hooks instead")
@ -266,7 +267,7 @@ func (u *upgradeCmd) run() error {
if u.appVersion != "" { if u.appVersion != "" {
if !chart.Metadata.OverrideMetadata { if !chart.Metadata.OverrideMetadata {
return fmt.Errorf("The chart maintainer has disallowed overriding the chart's package metadata. Please contact the maintainer to set `overrideChartMeta` in their Chart.yaml to `true`.") return fmt.Errorf("The chart maintainer has disallowed overriding the chart's package metadata. Please contact the maintainer to set `overrideMetadata` in their Chart.yaml to `true`.")
} }
chart.Metadata.AppVersion = u.appVersion chart.Metadata.AppVersion = u.appVersion
@ -285,22 +286,25 @@ func (u *upgradeCmd) run() error {
helm.ReuseValues(u.reuseValues), helm.ReuseValues(u.reuseValues),
helm.UpgradeSubNotes(u.subNotes), helm.UpgradeSubNotes(u.subNotes),
helm.UpgradeWait(u.wait), helm.UpgradeWait(u.wait),
helm.UpgradeDescription(u.description)) helm.UpgradeDescription(u.description),
helm.UpgradeCleanupOnFail(u.cleanupOnFail))
if err != nil { if err != nil {
fmt.Fprintf(u.out, "UPGRADE FAILED\nROLLING BACK\nError: %v\n", prettyError(err)) fmt.Fprintf(u.out, "UPGRADE FAILED\nError: %v\n", prettyError(err))
if u.atomic { if u.atomic {
fmt.Fprint(u.out, "ROLLING BACK")
rollback := &rollbackCmd{ rollback := &rollbackCmd{
out: u.out, out: u.out,
client: u.client, client: u.client,
name: u.release, name: u.release,
dryRun: u.dryRun, dryRun: u.dryRun,
recreate: u.recreate, recreate: u.recreate,
force: u.force, force: u.force,
timeout: u.timeout, timeout: u.timeout,
wait: u.wait, wait: u.wait,
description: "", description: "",
revision: releaseHistory.Releases[0].Version, revision: releaseHistory.Releases[0].Version,
disableHooks: u.disableHooks, disableHooks: u.disableHooks,
cleanupOnFail: u.cleanupOnFail,
} }
if err := rollback.run(); err != nil { if err := rollback.run(); err != nil {
return err return err

@ -131,7 +131,13 @@ func (r *ReleaseModuleServiceServer) RollbackRelease(ctx context.Context, in *ru
grpclog.Print("rollback") grpclog.Print("rollback")
c := bytes.NewBufferString(in.Current.Manifest) c := bytes.NewBufferString(in.Current.Manifest)
t := bytes.NewBufferString(in.Target.Manifest) t := bytes.NewBufferString(in.Target.Manifest)
err := kubeClient.Update(in.Target.Namespace, c, t, in.Force, in.Recreate, in.Timeout, in.Wait) err := kubeClient.UpdateWithOptions(in.Target.Namespace, c, t, kube.UpdateOptions{
Force: in.Force,
Recreate: in.Recreate,
Timeout: in.Timeout,
ShouldWait: in.Wait,
CleanupOnFail: in.CleanupOnFail,
})
return &rudderAPI.RollbackReleaseResponse{}, err return &rudderAPI.RollbackReleaseResponse{}, err
} }
@ -140,7 +146,13 @@ func (r *ReleaseModuleServiceServer) UpgradeRelease(ctx context.Context, in *rud
grpclog.Print("upgrade") grpclog.Print("upgrade")
c := bytes.NewBufferString(in.Current.Manifest) c := bytes.NewBufferString(in.Current.Manifest)
t := bytes.NewBufferString(in.Target.Manifest) t := bytes.NewBufferString(in.Target.Manifest)
err := kubeClient.Update(in.Target.Namespace, c, t, in.Force, in.Recreate, in.Timeout, in.Wait) err := kubeClient.UpdateWithOptions(in.Target.Namespace, c, t, kube.UpdateOptions{
Force: in.Force,
Recreate: in.Recreate,
Timeout: in.Timeout,
ShouldWait: in.Wait,
CleanupOnFail: in.CleanupOnFail,
})
// upgrade response object should be changed to include status // upgrade response object should be changed to include status
return &rudderAPI.UpgradeReleaseResponse{}, err return &rudderAPI.UpgradeReleaseResponse{}, err
} }

@ -67,7 +67,6 @@ const (
storageConfigMap = "configmap" storageConfigMap = "configmap"
storageSecret = "secret" storageSecret = "secret"
probeAddr = ":44135"
traceAddr = ":44136" traceAddr = ":44136"
// defaultMaxHistory sets the maximum number of releases to 0: unlimited // defaultMaxHistory sets the maximum number of releases to 0: unlimited
@ -76,6 +75,7 @@ const (
var ( var (
grpcAddr = flag.String("listen", ":44134", "address:port to listen on") grpcAddr = flag.String("listen", ":44134", "address:port to listen on")
probeAddr = flag.String("probe-listen", ":44135", "address:port to listen on for probes")
enableTracing = flag.Bool("trace", false, "enable rpc tracing") enableTracing = flag.Bool("trace", false, "enable rpc tracing")
store = flag.String("storage", storageConfigMap, "storage driver to use. One of 'configmap', 'memory', or 'secret'") store = flag.String("storage", storageConfigMap, "storage driver to use. One of 'configmap', 'memory', or 'secret'")
remoteReleaseModules = flag.Bool("experimental-release", false, "enable experimental release modules") remoteReleaseModules = flag.Bool("experimental-release", false, "enable experimental release modules")
@ -187,7 +187,7 @@ func start() {
logger.Printf("Starting Tiller %s (tls=%t)", version.GetVersion(), *tlsEnable || *tlsVerify) logger.Printf("Starting Tiller %s (tls=%t)", version.GetVersion(), *tlsEnable || *tlsVerify)
logger.Printf("GRPC listening on %s", *grpcAddr) logger.Printf("GRPC listening on %s", *grpcAddr)
logger.Printf("Probes listening on %s", probeAddr) logger.Printf("Probes listening on %s", *probeAddr)
logger.Printf("Storage driver is %s", env.Releases.Name()) logger.Printf("Storage driver is %s", env.Releases.Name())
logger.Printf("Max history per release is %d", *maxHistory) logger.Printf("Max history per release is %d", *maxHistory)
@ -213,7 +213,7 @@ func start() {
goprom.Register(rootServer) goprom.Register(rootServer)
addPrometheusHandler(mux) addPrometheusHandler(mux)
if err := http.ListenAndServe(probeAddr, mux); err != nil { if err := http.ListenAndServe(*probeAddr, mux); err != nil {
probeErrCh <- err probeErrCh <- err
} }
}() }()

@ -215,7 +215,7 @@ data:
myvalue: "Hello World" myvalue: "Hello World"
drink: "coffee" drink: "coffee"
food: "pizza" food: "pizza"
app_name: mychart app_name: mychart
app_version: "0.1.0+1478129847" app_version: "0.1.0+1478129847"
``` ```

@ -49,7 +49,7 @@ the dependency charts stored locally. The path should start with a prefix of
repository: "file://../dependency_chart/nginx" repository: "file://../dependency_chart/nginx"
If the dependency chart is retrieved locally, it is not required to have the If the dependency chart is retrieved locally, it is not required to have the
repository added to helm by "helm add repo". Version matching is also supported repository added to helm by "helm repo add". Version matching is also supported
for this case. for this case.
@ -78,4 +78,4 @@ for this case.
* [helm dependency list](helm_dependency_list.md) - list the dependencies for the given chart * [helm dependency list](helm_dependency_list.md) - list the dependencies for the given chart
* [helm dependency update](helm_dependency_update.md) - update charts/ based on the contents of requirements.yaml * [helm dependency update](helm_dependency_update.md) - update charts/ based on the contents of requirements.yaml
###### Auto generated by spf13/cobra on 1-Aug-2018 ###### Auto generated by spf13/cobra on 26-Mar-2019

@ -20,6 +20,7 @@ helm rollback [flags] [RELEASE] [REVISION]
### Options ### Options
``` ```
--cleanup-on-fail allow deletion of new resources created in this rollback when rollback failed
--description string specify a description for the release --description string specify a description for the release
--dry-run simulate a rollback --dry-run simulate a rollback
--force force resource update through delete/recreate if needed --force force resource update through delete/recreate if needed
@ -52,4 +53,4 @@ helm rollback [flags] [RELEASE] [REVISION]
* [helm](helm.md) - The Helm package manager for Kubernetes. * [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 29-Jan-2019 ###### Auto generated by spf13/cobra on 5-Feb-2019

@ -69,6 +69,7 @@ helm upgrade [RELEASE] [CHART] [flags]
--atomic if set, upgrade process rolls back changes made in case of failed upgrade, also sets --wait flag --atomic if set, upgrade process rolls back changes made in case of failed upgrade, also sets --wait flag
--ca-file string verify certificates of HTTPS-enabled servers using this CA bundle --ca-file string verify certificates of HTTPS-enabled servers using this CA bundle
--cert-file string identify HTTPS client using this SSL certificate file --cert-file string identify HTTPS client using this SSL certificate file
--cleanup-on-fail allow deletion of new resources created in this upgrade when upgrade failed
--description string specify the description to use for the upgrade, rather than the default --description string specify the description to use for the upgrade, rather than the default
--devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored. --devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.
--dry-run simulate an upgrade --dry-run simulate an upgrade

@ -297,6 +297,20 @@ func DeleteDescription(description string) DeleteOption {
} }
} }
// UpgradeCleanupOnFail allows deletion of new resources created in this upgrade when upgrade failed
func UpgradeCleanupOnFail(cleanupOnFail bool) UpdateOption {
return func(opts *options) {
opts.updateReq.CleanupOnFail = cleanupOnFail
}
}
// RollbackCleanupOnFail allows deletion of new resources created in this rollback when rollback failed
func RollbackCleanupOnFail(cleanupOnFail bool) RollbackOption {
return func(opts *options) {
opts.rollbackReq.CleanupOnFail = cleanupOnFail
}
}
// DeleteDisableHooks will disable hooks for a deletion operation. // DeleteDisableHooks will disable hooks for a deletion operation.
func DeleteDisableHooks(disable bool) DeleteOption { func DeleteDisableHooks(disable bool) DeleteOption {
return func(opts *options) { return func(opts *options) {

@ -20,14 +20,14 @@ import (
"k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/release"
) )
// HookAnno is the label name for a hook const (
const HookAnno = "helm.sh/hook" // HookAnno is the label name for a hook
HookAnno = "helm.sh/hook"
// HookWeightAnno is the label name for a hook weight // HookWeightAnno is the label name for a hook weight
const HookWeightAnno = "helm.sh/hook-weight" HookWeightAnno = "helm.sh/hook-weight"
// HookDeleteAnno is the label name for the delete policy for a hook
// HookDeleteAnno is the label name for the delete policy for a hook HookDeleteAnno = "helm.sh/hook-delete-policy"
const HookDeleteAnno = "helm.sh/hook-delete-policy" )
// Types of hooks // Types of hooks
const ( const (

@ -35,8 +35,9 @@ import (
appsv1beta1 "k8s.io/api/apps/v1beta1" appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2" appsv1beta2 "k8s.io/api/apps/v1beta2"
batch "k8s.io/api/batch/v1" batch "k8s.io/api/batch/v1"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
extv1beta1 "k8s.io/api/extensions/v1beta1" extv1beta1 "k8s.io/api/extensions/v1beta1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -45,6 +46,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/resource" "k8s.io/cli-runtime/pkg/resource"
@ -76,6 +78,12 @@ func New(getter genericclioptions.RESTClientGetter) *Client {
if getter == nil { if getter == nil {
getter = genericclioptions.NewConfigFlags(true) getter = genericclioptions.NewConfigFlags(true)
} }
err := apiextv1beta1.AddToScheme(scheme.Scheme)
if err != nil {
panic(err)
}
return &Client{ return &Client{
Factory: cmdutil.NewFactory(getter), Factory: cmdutil.NewFactory(getter),
Log: nopLogger, Log: nopLogger,
@ -142,8 +150,8 @@ func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result,
ContinueOnError(). ContinueOnError().
NamespaceParam(namespace). NamespaceParam(namespace).
DefaultNamespace(). DefaultNamespace().
Stream(reader, "").
Schema(c.validator()). Schema(c.validator()).
Stream(reader, "").
Flatten(). Flatten().
Do().Infos() Do().Infos()
return result, scrubValidationError(err) return result, scrubValidationError(err)
@ -290,13 +298,33 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
return buf.String(), nil return buf.String(), nil
} }
// Update reads in the current configuration and a target configuration from io.reader // Deprecated; use UpdateWithOptions instead
func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error {
return c.UpdateWithOptions(namespace, originalReader, targetReader, UpdateOptions{
Force: force,
Recreate: recreate,
Timeout: timeout,
ShouldWait: shouldWait,
})
}
// UpdateOptions provides options to control update behavior
type UpdateOptions struct {
Force bool
Recreate bool
Timeout int64
ShouldWait bool
// Allow deletion of new resources created in this update when update failed
CleanupOnFail bool
}
// UpdateWithOptions reads in the current configuration and a target configuration from io.reader
// and creates resources that don't already exists, updates resources that have been modified // and creates resources that don't already exists, updates resources that have been modified
// in the target configuration and deletes resources from the current configuration that are // in the target configuration and deletes resources from the current configuration that are
// not present in the target configuration. // not present in the target configuration.
// //
// Namespace will set the namespaces. // Namespace will set the namespaces.
func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { func (c *Client) UpdateWithOptions(namespace string, originalReader, targetReader io.Reader, opts UpdateOptions) error {
original, err := c.BuildUnstructured(namespace, originalReader) original, err := c.BuildUnstructured(namespace, originalReader)
if err != nil { if err != nil {
return fmt.Errorf("failed decoding reader into objects: %s", err) return fmt.Errorf("failed decoding reader into objects: %s", err)
@ -308,6 +336,7 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
return fmt.Errorf("failed decoding reader into objects: %s", err) return fmt.Errorf("failed decoding reader into objects: %s", err)
} }
newlyCreatedResources := []*resource.Info{}
updateErrors := []string{} updateErrors := []string{}
c.Log("checking %d resources for changes", len(target)) c.Log("checking %d resources for changes", len(target))
@ -326,6 +355,7 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
if err := createResource(info); err != nil { if err := createResource(info); err != nil {
return fmt.Errorf("failed to create resource: %s", err) return fmt.Errorf("failed to create resource: %s", err)
} }
newlyCreatedResources = append(newlyCreatedResources, info)
kind := info.Mapping.GroupVersionKind.Kind kind := info.Mapping.GroupVersionKind.Kind
c.Log("Created a new %s called %q\n", kind, info.Name) c.Log("Created a new %s called %q\n", kind, info.Name)
@ -347,7 +377,7 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
) )
} }
if err := updateResource(c, info, originalInfo.Object, force, recreate); err != nil { if err := updateResource(c, info, originalInfo.Object, opts.Force, opts.Recreate); err != nil {
c.Log("error updating the resource %q:\n\t %v", info.Name, err) c.Log("error updating the resource %q:\n\t %v", info.Name, err)
updateErrors = append(updateErrors, err.Error()) updateErrors = append(updateErrors, err.Error())
} }
@ -355,11 +385,18 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
return nil return nil
}) })
cleanupErrors := []string{}
if opts.CleanupOnFail && (err != nil || len(updateErrors) != 0) {
c.Log("Cleanup on fail enabled: cleaning up newly created resources due to update manifests failures")
cleanupErrors = c.cleanup(newlyCreatedResources)
}
switch { switch {
case err != nil: case err != nil:
return err return fmt.Errorf(strings.Join(append([]string{err.Error()}, cleanupErrors...), " && "))
case len(updateErrors) != 0: case len(updateErrors) != 0:
return fmt.Errorf(strings.Join(updateErrors, " && ")) return fmt.Errorf(strings.Join(append(updateErrors, cleanupErrors...), " && "))
} }
for _, info := range original.Difference(target) { for _, info := range original.Difference(target) {
@ -382,12 +419,32 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
c.Log("Failed to delete %q, err: %s", info.Name, err) c.Log("Failed to delete %q, err: %s", info.Name, err)
} }
} }
if shouldWait { if opts.ShouldWait {
return c.waitForResources(time.Duration(timeout)*time.Second, target) err := c.waitForResources(time.Duration(opts.Timeout)*time.Second, target)
if opts.CleanupOnFail && err != nil {
c.Log("Cleanup on fail enabled: cleaning up newly created resources due to wait failure during update")
cleanupErrors = c.cleanup(newlyCreatedResources)
return fmt.Errorf(strings.Join(append([]string{err.Error()}, cleanupErrors...), " && "))
}
return err
} }
return nil return nil
} }
func (c *Client) cleanup(newlyCreatedResources []*resource.Info) (cleanupErrors []string) {
for _, info := range newlyCreatedResources {
kind := info.Mapping.GroupVersionKind.Kind
c.Log("Deleting newly created %s with the name %q in %s...", kind, info.Name, info.Namespace)
if err := deleteResource(info); err != nil {
c.Log("Error deleting newly created %s with the name %q in %s: %s", kind, info.Name, info.Namespace, err)
cleanupErrors = append(cleanupErrors, err.Error())
}
}
return
}
// Delete deletes Kubernetes resources from an io.reader. // Delete deletes Kubernetes resources from an io.reader.
// //
// Namespace will set the namespace. // Namespace will set the namespace.
@ -439,6 +496,55 @@ func (c *Client) WatchUntilReady(namespace string, reader io.Reader, timeout int
return perform(infos, c.watchTimeout(time.Duration(timeout)*time.Second)) return perform(infos, c.watchTimeout(time.Duration(timeout)*time.Second))
} }
// WatchUntilCRDEstablished polls the given CRD until it reaches the established
// state. A CRD needs to reach the established state before CRs can be created.
//
// If a naming conflict condition is found, this function will return an error.
func (c *Client) WaitUntilCRDEstablished(reader io.Reader, timeout time.Duration) error {
infos, err := c.BuildUnstructured(metav1.NamespaceAll, reader)
if err != nil {
return err
}
return perform(infos, c.pollCRDEstablished(timeout))
}
func (c *Client) pollCRDEstablished(t time.Duration) ResourceActorFunc {
return func(info *resource.Info) error {
return c.pollCRDUntilEstablished(t, info)
}
}
func (c *Client) pollCRDUntilEstablished(timeout time.Duration, info *resource.Info) error {
return wait.PollImmediate(time.Second, timeout, func() (bool, error) {
err := info.Get()
if err != nil {
return false, fmt.Errorf("unable to get CRD: %v", err)
}
crd := &apiextv1beta1.CustomResourceDefinition{}
err = scheme.Scheme.Convert(info.Object, crd, nil)
if err != nil {
return false, fmt.Errorf("unable to convert to CRD type: %v", err)
}
for _, cond := range crd.Status.Conditions {
switch cond.Type {
case apiextv1beta1.Established:
if cond.Status == apiextv1beta1.ConditionTrue {
return true, nil
}
case apiextv1beta1.NamesAccepted:
if cond.Status == apiextv1beta1.ConditionFalse {
return false, fmt.Errorf("naming conflict detected for CRD %s", crd.GetName())
}
}
}
return false, nil
})
}
func perform(infos Result, fn ResourceActorFunc) error { func perform(infos Result, fn ResourceActorFunc) error {
if len(infos) == 0 { if len(infos) == 0 {
return ErrNoObjectsVisited return ErrNoObjectsVisited

@ -24,8 +24,10 @@ import (
"sort" "sort"
"strings" "strings"
"testing" "testing"
"time"
v1 "k8s.io/api/core/v1" "k8s.io/api/core/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
@ -33,15 +35,35 @@ import (
"k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest/fake" "k8s.io/client-go/rest/fake"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
kubectlscheme "k8s.io/kubernetes/pkg/kubectl/scheme"
) )
func init() {
err := apiextv1beta1.AddToScheme(scheme.Scheme)
if err != nil {
panic(err)
}
// Tiller use the scheme from go-client, but the cmdtesting
// package used here is hardcoded to use the scheme from
// kubectl. So for testing, we need to add the CustomResourceDefinition
// type to both schemes.
err = apiextv1beta1.AddToScheme(kubectlscheme.Scheme)
if err != nil {
panic(err)
}
}
var ( var (
codec = scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
unstructuredSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer unstructuredSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer
) )
func getCodec() runtime.Codec {
return scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
}
func objBody(obj runtime.Object) io.ReadCloser { func objBody(obj runtime.Object) io.ReadCloser {
return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(getCodec(), obj))))
} }
func newPod(name string) v1.Pod { func newPod(name string) v1.Pod {
@ -103,7 +125,7 @@ func notFoundBody() *metav1.Status {
func newResponse(code int, obj runtime.Object) (*http.Response, error) { func newResponse(code int, obj runtime.Object) (*http.Response, error) {
header := http.Header{} header := http.Header{}
header.Set("Content-Type", runtime.ContentTypeJSON) header.Set("Content-Type", runtime.ContentTypeJSON)
body := ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) body := ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(getCodec(), obj))))
return &http.Response{StatusCode: code, Header: header, Body: body}, nil return &http.Response{StatusCode: code, Header: header, Body: body}, nil
} }
@ -434,6 +456,88 @@ func TestResourceSortOrder(t *testing.T) {
} }
} }
func TestWaitUntilCRDEstablished(t *testing.T) {
testCases := map[string]struct {
conditions []apiextv1beta1.CustomResourceDefinitionCondition
returnConditionsAfter int
success bool
}{
"crd reaches established state after 2 requests": {
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{
{
Type: apiextv1beta1.Established,
Status: apiextv1beta1.ConditionTrue,
},
},
returnConditionsAfter: 2,
success: true,
},
"crd does not reach established state before timeout": {
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{},
returnConditionsAfter: 100,
success: false,
},
"crd name is not accepted": {
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{
{
Type: apiextv1beta1.NamesAccepted,
Status: apiextv1beta1.ConditionFalse,
},
},
returnConditionsAfter: 1,
success: false,
},
}
for tn, tc := range testCases {
func(name string) {
c := newTestClient()
defer c.Cleanup()
crdWithoutConditions := newCrdWithStatus("name", apiextv1beta1.CustomResourceDefinitionStatus{})
crdWithConditions := newCrdWithStatus("name", apiextv1beta1.CustomResourceDefinitionStatus{
Conditions: tc.conditions,
})
requestCount := 0
c.TestFactory.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
var crd apiextv1beta1.CustomResourceDefinition
if requestCount < tc.returnConditionsAfter {
crd = crdWithoutConditions
} else {
crd = crdWithConditions
}
requestCount += 1
return newResponse(200, &crd)
}),
}
err := c.WaitUntilCRDEstablished(strings.NewReader(crdManifest), 5*time.Second)
if err != nil && tc.success {
t.Errorf("%s: expected no error, but got %v", name, err)
}
if err == nil && !tc.success {
t.Errorf("%s: expected error, but didn't get one", name)
}
}(tn)
}
}
func newCrdWithStatus(name string, status apiextv1beta1.CustomResourceDefinitionStatus) apiextv1beta1.CustomResourceDefinition {
crd := apiextv1beta1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: metav1.NamespaceDefault,
},
Spec: apiextv1beta1.CustomResourceDefinitionSpec{},
Status: status,
}
return crd
}
func TestPerform(t *testing.T) { func TestPerform(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
@ -701,3 +805,41 @@ spec:
ports: ports:
- containerPort: 80 - containerPort: 80
` `
const crdManifest = `
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
labels:
controller-tools.k8s.io: "1.0"
name: applications.app.k8s.io
spec:
group: app.k8s.io
names:
kind: Application
plural: applications
scope: Namespaced
validation:
openAPIV3Schema:
properties:
apiVersion:
description: 'Description'
type: string
kind:
description: 'Kind'
type: string
metadata:
type: object
spec:
type: object
status:
type: object
version: v1beta1
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
`

@ -26,13 +26,14 @@ import (
var values = []byte{} var values = []byte{}
const namespace = "testNamespace" const (
const strict = false namespace = "testNamespace"
strict = false
const badChartDir = "rules/testdata/badchartfile" badChartDir = "rules/testdata/badchartfile"
const badValuesFileDir = "rules/testdata/badvaluesfile" badValuesFileDir = "rules/testdata/badvaluesfile"
const badYamlFileDir = "rules/testdata/albatross" badYamlFileDir = "rules/testdata/albatross"
const goodChartDir = "rules/testdata/goodone" goodChartDir = "rules/testdata/goodone"
)
func TestBadChart(t *testing.T) { func TestBadChart(t *testing.T) {
m := All(badChartDir, values, namespace, strict).Messages m := All(badChartDir, values, namespace, strict).Messages

@ -25,7 +25,11 @@ import (
"k8s.io/helm/pkg/lint/support" "k8s.io/helm/pkg/lint/support"
) )
const templateTestBasedir = "./testdata/albatross" const (
strict = false
namespace = "testNamespace"
templateTestBasedir = "./testdata/albatross"
)
func TestValidateAllowedExtension(t *testing.T) { func TestValidateAllowedExtension(t *testing.T) {
var failTest = []string{"/foo", "/test.toml"} var failTest = []string{"/foo", "/test.toml"}
@ -46,9 +50,6 @@ func TestValidateAllowedExtension(t *testing.T) {
var values = []byte("nameOverride: ''\nhttpPort: 80") var values = []byte("nameOverride: ''\nhttpPort: 80")
const namespace = "testNamespace"
const strict = false
func TestTemplateParsing(t *testing.T) { func TestTemplateParsing(t *testing.T) {
linter := support.Linter{ChartDir: templateTestBasedir} linter := support.Linter{ChartDir: templateTestBasedir}
Templates(&linter, values, namespace, strict) Templates(&linter, values, namespace, strict)

@ -20,7 +20,7 @@ const (
Status_UNKNOWN Status_Code = 0 Status_UNKNOWN Status_Code = 0
// Status_DEPLOYED indicates that the release has been pushed to Kubernetes. // Status_DEPLOYED indicates that the release has been pushed to Kubernetes.
Status_DEPLOYED Status_Code = 1 Status_DEPLOYED Status_Code = 1
// Status_DELETED indicates that a release has been deleted from Kubermetes. // Status_DELETED indicates that a release has been deleted from Kubernetes.
Status_DELETED Status_Code = 2 Status_DELETED Status_Code = 2
// Status_SUPERSEDED indicates that this release object is outdated and a newer one exists. // Status_SUPERSEDED indicates that this release object is outdated and a newer one exists.
Status_SUPERSEDED Status_Code = 3 Status_SUPERSEDED Status_Code = 3

@ -214,12 +214,13 @@ func (m *DeleteReleaseResponse) GetResult() *Result {
} }
type UpgradeReleaseRequest struct { type UpgradeReleaseRequest struct {
Current *hapi_release5.Release `protobuf:"bytes,1,opt,name=current" json:"current,omitempty"` Current *hapi_release5.Release `protobuf:"bytes,1,opt,name=current" json:"current,omitempty"`
Target *hapi_release5.Release `protobuf:"bytes,2,opt,name=target" json:"target,omitempty"` Target *hapi_release5.Release `protobuf:"bytes,2,opt,name=target" json:"target,omitempty"`
Timeout int64 `protobuf:"varint,3,opt,name=Timeout" json:"Timeout,omitempty"` Timeout int64 `protobuf:"varint,3,opt,name=Timeout" json:"Timeout,omitempty"`
Wait bool `protobuf:"varint,4,opt,name=Wait" json:"Wait,omitempty"` Wait bool `protobuf:"varint,4,opt,name=Wait" json:"Wait,omitempty"`
Recreate bool `protobuf:"varint,5,opt,name=Recreate" json:"Recreate,omitempty"` Recreate bool `protobuf:"varint,5,opt,name=Recreate" json:"Recreate,omitempty"`
Force bool `protobuf:"varint,6,opt,name=Force" json:"Force,omitempty"` Force bool `protobuf:"varint,6,opt,name=Force" json:"Force,omitempty"`
CleanupOnFail bool `protobuf:"varint,7,opt,name=CleanupOnFail" json:"CleanupOnFail,omitempty"`
} }
func (m *UpgradeReleaseRequest) Reset() { *m = UpgradeReleaseRequest{} } func (m *UpgradeReleaseRequest) Reset() { *m = UpgradeReleaseRequest{} }
@ -269,6 +270,13 @@ func (m *UpgradeReleaseRequest) GetForce() bool {
return false return false
} }
func (m *UpgradeReleaseRequest) GetCleanupOnFail() bool {
if m != nil {
return m.CleanupOnFail
}
return false
}
type UpgradeReleaseResponse struct { type UpgradeReleaseResponse struct {
Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
Result *Result `protobuf:"bytes,2,opt,name=result" json:"result,omitempty"` Result *Result `protobuf:"bytes,2,opt,name=result" json:"result,omitempty"`
@ -294,12 +302,13 @@ func (m *UpgradeReleaseResponse) GetResult() *Result {
} }
type RollbackReleaseRequest struct { type RollbackReleaseRequest struct {
Current *hapi_release5.Release `protobuf:"bytes,1,opt,name=current" json:"current,omitempty"` Current *hapi_release5.Release `protobuf:"bytes,1,opt,name=current" json:"current,omitempty"`
Target *hapi_release5.Release `protobuf:"bytes,2,opt,name=target" json:"target,omitempty"` Target *hapi_release5.Release `protobuf:"bytes,2,opt,name=target" json:"target,omitempty"`
Timeout int64 `protobuf:"varint,3,opt,name=Timeout" json:"Timeout,omitempty"` Timeout int64 `protobuf:"varint,3,opt,name=Timeout" json:"Timeout,omitempty"`
Wait bool `protobuf:"varint,4,opt,name=Wait" json:"Wait,omitempty"` Wait bool `protobuf:"varint,4,opt,name=Wait" json:"Wait,omitempty"`
Recreate bool `protobuf:"varint,5,opt,name=Recreate" json:"Recreate,omitempty"` Recreate bool `protobuf:"varint,5,opt,name=Recreate" json:"Recreate,omitempty"`
Force bool `protobuf:"varint,6,opt,name=Force" json:"Force,omitempty"` Force bool `protobuf:"varint,6,opt,name=Force" json:"Force,omitempty"`
CleanupOnFail bool `protobuf:"varint,7,opt,name=CleanupOnFail" json:"CleanupOnFail,omitempty"`
} }
func (m *RollbackReleaseRequest) Reset() { *m = RollbackReleaseRequest{} } func (m *RollbackReleaseRequest) Reset() { *m = RollbackReleaseRequest{} }
@ -349,6 +358,13 @@ func (m *RollbackReleaseRequest) GetForce() bool {
return false return false
} }
func (m *RollbackReleaseRequest) GetCleanupOnFail() bool {
if m != nil {
return m.CleanupOnFail
}
return false
}
type RollbackReleaseResponse struct { type RollbackReleaseResponse struct {
Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
Result *Result `protobuf:"bytes,2,opt,name=result" json:"result,omitempty"` Result *Result `protobuf:"bytes,2,opt,name=result" json:"result,omitempty"`
@ -680,43 +696,44 @@ var _ReleaseModuleService_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("hapi/rudder/rudder.proto", fileDescriptor0) } func init() { proto.RegisterFile("hapi/rudder/rudder.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 597 bytes of a gzipped FileDescriptorProto // 615 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x56, 0x5f, 0x8f, 0xd2, 0x4e, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x56, 0x4d, 0x6f, 0xd3, 0x40,
0x14, 0xa5, 0xb0, 0x14, 0xb8, 0x64, 0x7f, 0x3f, 0x32, 0xa1, 0xd0, 0x34, 0x3e, 0x90, 0x3e, 0x18, 0x10, 0x8d, 0x9b, 0xc6, 0x69, 0xa6, 0x2a, 0x44, 0xab, 0xba, 0xb5, 0x2c, 0x0e, 0x91, 0x85, 0x50,
0xe2, 0xba, 0x25, 0x41, 0x1f, 0x7d, 0x51, 0x96, 0xfd, 0x13, 0x23, 0x9b, 0x0c, 0xe2, 0x26, 0xbe, 0x44, 0xa9, 0x2b, 0x15, 0x8e, 0x5c, 0x20, 0xfd, 0x14, 0x22, 0x95, 0x36, 0x84, 0x4a, 0xdc, 0xb6,
0x75, 0xe1, 0x82, 0xd5, 0xd2, 0xd6, 0xe9, 0x74, 0x1f, 0xd5, 0x4f, 0xe3, 0x57, 0xd2, 0x8f, 0x63, 0xc9, 0x24, 0x18, 0x36, 0xb6, 0x59, 0xaf, 0x7b, 0x04, 0x7e, 0x0d, 0xff, 0x12, 0x84, 0xec, 0xb5,
0xda, 0x69, 0x89, 0xad, 0xd3, 0x88, 0x6b, 0xc2, 0x83, 0x4f, 0x9d, 0xe9, 0x3d, 0xdc, 0x39, 0xe7, 0x23, 0x6c, 0xd6, 0x22, 0x14, 0x29, 0x17, 0x4e, 0xde, 0xd9, 0x79, 0x9d, 0x9d, 0xf7, 0x76, 0xf6,
0xf4, 0xce, 0x09, 0xa0, 0xbf, 0xb3, 0x03, 0x67, 0xc4, 0xa2, 0xd5, 0x0a, 0x59, 0xfa, 0xb0, 0x02, 0x35, 0x60, 0xbf, 0x67, 0x91, 0x7f, 0x24, 0x92, 0xe9, 0x14, 0x45, 0xfe, 0xf1, 0x22, 0x11, 0xca,
0xe6, 0x73, 0x9f, 0x74, 0xe3, 0x8a, 0x15, 0x22, 0xbb, 0x73, 0x96, 0x18, 0x5a, 0xa2, 0x66, 0xf4, 0x90, 0xec, 0xa6, 0x19, 0x2f, 0x46, 0x71, 0xeb, 0x4f, 0x30, 0xf6, 0x54, 0xce, 0xd9, 0x57, 0x78,
0x05, 0x1e, 0x5d, 0xb4, 0x43, 0x1c, 0x39, 0xde, 0xda, 0x17, 0x70, 0xc3, 0xc8, 0x15, 0xd2, 0xa7, 0xe4, 0xc8, 0x62, 0x3c, 0xf2, 0x83, 0x59, 0xa8, 0xe0, 0x8e, 0x53, 0x4a, 0xe4, 0x5f, 0x95, 0x73,
0xa8, 0x99, 0x2e, 0xa8, 0x14, 0xc3, 0xc8, 0xe5, 0x84, 0xc0, 0x51, 0xfc, 0x1b, 0x5d, 0x19, 0x28, 0x39, 0x98, 0x14, 0xe3, 0x84, 0x4b, 0x42, 0x60, 0x33, 0xfd, 0x1b, 0xdb, 0xe8, 0x19, 0xfd, 0x0e,
0xc3, 0x16, 0x4d, 0xd6, 0xa4, 0x03, 0x35, 0xd7, 0xdf, 0xe8, 0xd5, 0x41, 0x6d, 0xd8, 0xa2, 0xf1, 0xcd, 0xd6, 0xa4, 0x0b, 0x4d, 0x1e, 0xce, 0xed, 0x8d, 0x5e, 0xb3, 0xdf, 0xa1, 0xe9, 0xd2, 0x7d,
0xd2, 0x7c, 0x06, 0xea, 0x9c, 0xdb, 0x3c, 0x0a, 0x49, 0x1b, 0x1a, 0x8b, 0xd9, 0xcb, 0xd9, 0xf5, 0x0e, 0xe6, 0x48, 0x32, 0x99, 0xc4, 0x64, 0x1b, 0xda, 0xe3, 0xe1, 0xab, 0xe1, 0xd5, 0xf5, 0xb0,
0xcd, 0xac, 0x53, 0x89, 0x37, 0xf3, 0xc5, 0x64, 0x32, 0x9d, 0xcf, 0x3b, 0x0a, 0x39, 0x86, 0xd6, 0xdb, 0x48, 0x83, 0xd1, 0x78, 0x30, 0x38, 0x1d, 0x8d, 0xba, 0x06, 0xd9, 0x81, 0xce, 0x78, 0x38,
0x62, 0x36, 0xb9, 0x7c, 0x3e, 0xbb, 0x98, 0x9e, 0x75, 0xaa, 0xa4, 0x05, 0xf5, 0x29, 0xa5, 0xd7, 0xb8, 0x78, 0x31, 0x3c, 0x3f, 0x3d, 0xe9, 0x6e, 0x90, 0x0e, 0xb4, 0x4e, 0x29, 0xbd, 0xa2, 0xdd,
0xb4, 0x53, 0x33, 0xfb, 0xa0, 0xbd, 0x41, 0x16, 0x3a, 0xbe, 0x47, 0x05, 0x0b, 0x8a, 0x1f, 0x23, 0xa6, 0xbb, 0x0f, 0xd6, 0x5b, 0x14, 0xb1, 0x1f, 0x06, 0x54, 0x75, 0x41, 0xf1, 0x53, 0x82, 0xb1,
0x0c, 0xb9, 0x79, 0x0e, 0xbd, 0x62, 0x21, 0x0c, 0x7c, 0x2f, 0xc4, 0x98, 0x96, 0x67, 0x6f, 0x31, 0x74, 0xcf, 0x60, 0xaf, 0x9a, 0x88, 0xa3, 0x30, 0x88, 0x31, 0x6d, 0x2b, 0x60, 0x0b, 0x2c, 0xda,
0xa3, 0x15, 0xaf, 0x89, 0x0e, 0x8d, 0x3b, 0x81, 0xd6, 0xab, 0xc9, 0xeb, 0x6c, 0x6b, 0x5e, 0x82, 0x4a, 0xd7, 0xc4, 0x86, 0xf6, 0xad, 0x42, 0xdb, 0x1b, 0xd9, 0x76, 0x11, 0xba, 0x17, 0x60, 0x5d,
0x76, 0xe5, 0x85, 0xdc, 0x76, 0xdd, 0xfc, 0x01, 0x64, 0x04, 0x8d, 0x54, 0x78, 0xd2, 0xa9, 0x3d, 0x06, 0xb1, 0x64, 0x9c, 0x97, 0x0f, 0x20, 0x47, 0xd0, 0xce, 0x89, 0x67, 0x95, 0xb6, 0x8f, 0x2d,
0xd6, 0xac, 0xc4, 0xc4, 0xcc, 0x8d, 0x0c, 0x9e, 0xa1, 0xcc, 0xcf, 0xd0, 0x2b, 0x76, 0x4a, 0x19, 0x2f, 0x13, 0xb1, 0x50, 0xa3, 0x80, 0x17, 0x28, 0xf7, 0x0b, 0xec, 0x55, 0x2b, 0xe5, 0x1d, 0xfd,
0xfd, 0x69, 0x2b, 0xf2, 0x14, 0x54, 0x96, 0x78, 0x9c, 0xb0, 0x6d, 0x8f, 0x1f, 0x58, 0xb2, 0xef, 0x6d, 0x29, 0xf2, 0x0c, 0x4c, 0x91, 0x69, 0x9c, 0x75, 0xbb, 0x7d, 0xfc, 0xc0, 0xd3, 0xdd, 0x9f,
0x67, 0x89, 0xef, 0x40, 0x53, 0xac, 0x79, 0x01, 0xdd, 0x33, 0x74, 0x91, 0xe3, 0xdf, 0x2a, 0xf9, 0xa7, 0xee, 0x81, 0xe6, 0x58, 0xf7, 0x1c, 0x76, 0x4f, 0x90, 0xa3, 0xc4, 0x7f, 0x65, 0xf2, 0x19,
0x04, 0x5a, 0xa1, 0xd1, 0x61, 0x85, 0x7c, 0x53, 0x40, 0x5b, 0x04, 0x1b, 0x66, 0xaf, 0x24, 0x52, 0xac, 0x4a, 0xa1, 0xf5, 0x12, 0xf9, 0x6e, 0x80, 0x35, 0x8e, 0xe6, 0x82, 0x4d, 0x35, 0x54, 0x26,
0x96, 0x11, 0x63, 0xe8, 0xf1, 0xdf, 0x10, 0x48, 0x51, 0xe4, 0x14, 0x54, 0x6e, 0xb3, 0x0d, 0x66, 0x89, 0x10, 0x18, 0xc8, 0x3f, 0x34, 0x90, 0xa3, 0xc8, 0x21, 0x98, 0x92, 0x89, 0x39, 0x16, 0x0d,
0x04, 0x4a, 0xf0, 0x29, 0x28, 0x9e, 0x93, 0xd7, 0xce, 0x16, 0xfd, 0x88, 0xeb, 0xb5, 0x81, 0x32, 0xd4, 0xe0, 0x73, 0x50, 0x3a, 0x27, 0x6f, 0xfc, 0x05, 0x86, 0x89, 0xb4, 0x9b, 0x3d, 0xa3, 0xdf,
0xac, 0xd1, 0x6c, 0x1b, 0x4f, 0xd5, 0x8d, 0xed, 0x70, 0xfd, 0x68, 0xa0, 0x0c, 0x9b, 0x34, 0x59, 0xa4, 0x45, 0x98, 0x4e, 0xd5, 0x35, 0xf3, 0xa5, 0xbd, 0xd9, 0x33, 0xfa, 0x5b, 0x34, 0x5b, 0x13,
0x13, 0x03, 0x9a, 0x14, 0x97, 0x0c, 0x6d, 0x8e, 0x7a, 0x3d, 0x79, 0xbf, 0xdb, 0x93, 0x2e, 0xd4, 0x07, 0xb6, 0x28, 0x4e, 0x04, 0x32, 0x89, 0x76, 0x2b, 0xdb, 0x5f, 0xc6, 0x64, 0x17, 0x5a, 0x67,
0xcf, 0x7d, 0xb6, 0x44, 0x5d, 0x4d, 0x0a, 0x62, 0x13, 0xcf, 0x48, 0x51, 0xd8, 0x61, 0xad, 0xfd, 0xa1, 0x98, 0xa0, 0x6d, 0x66, 0x09, 0x15, 0x90, 0x87, 0xb0, 0x33, 0xe0, 0xc8, 0x82, 0x24, 0xba,
0xae, 0x40, 0x8f, 0xfa, 0xae, 0x7b, 0x6b, 0x2f, 0x3f, 0xfc, 0x63, 0xde, 0x7e, 0x51, 0xa0, 0xff, 0x0a, 0xce, 0x98, 0xcf, 0xed, 0x76, 0x96, 0x2d, 0x6f, 0xa6, 0x93, 0x54, 0xa5, 0xbf, 0xde, 0x0b,
0x8b, 0xb4, 0x83, 0xdf, 0xc0, 0xb4, 0x93, 0x88, 0xbc, 0x7b, 0xdf, 0xc0, 0x00, 0xb4, 0x42, 0xa3, 0xf8, 0x61, 0xc0, 0x1e, 0x0d, 0x39, 0xbf, 0x61, 0x93, 0x8f, 0xff, 0xe5, 0x0d, 0x7c, 0x35, 0x60,
0xfb, 0x0a, 0x79, 0x98, 0x86, 0xb4, 0x90, 0x41, 0xf2, 0xe8, 0x2b, 0x6f, 0xed, 0x8b, 0xe0, 0x1e, 0xff, 0x37, 0x01, 0xd6, 0xfe, 0x9a, 0xf3, 0x4a, 0xca, 0x3e, 0xef, 0xfc, 0x9a, 0x23, 0xb0, 0x2a,
0x7f, 0xad, 0xef, 0xb8, 0xbf, 0xf2, 0x57, 0x91, 0x8b, 0x73, 0x21, 0x95, 0xac, 0xa1, 0x91, 0x06, 0x85, 0xee, 0x4a, 0xe4, 0x51, 0x6e, 0xf8, 0x8a, 0x06, 0x29, 0xa3, 0x2f, 0x83, 0x59, 0xa8, 0xfe,
0x2d, 0x39, 0x91, 0x9b, 0x20, 0x0d, 0x68, 0xe3, 0xf1, 0x7e, 0x60, 0xa1, 0xcb, 0xac, 0x90, 0x2d, 0x09, 0x1c, 0x7f, 0x6b, 0x2d, 0x7b, 0x7f, 0x1d, 0x4e, 0x13, 0x8e, 0x23, 0x45, 0x95, 0xcc, 0xa0,
0xfc, 0x97, 0x8f, 0xcf, 0xb2, 0xe3, 0xa4, 0x71, 0x5d, 0x76, 0x9c, 0x3c, 0x91, 0xcd, 0x0a, 0x79, 0x9d, 0x9b, 0x36, 0x39, 0xd0, 0x8b, 0xa0, 0x35, 0x7b, 0xe7, 0xc9, 0x6a, 0x60, 0xc5, 0xcb, 0x6d,
0x0f, 0xc7, 0xb9, 0x8c, 0x23, 0x8f, 0xe4, 0x0d, 0x64, 0x89, 0x6a, 0x9c, 0xec, 0x85, 0xdd, 0x9d, 0x90, 0x05, 0xdc, 0x2b, 0x5b, 0x71, 0xdd, 0x71, 0x5a, 0xeb, 0xaf, 0x3b, 0x4e, 0xef, 0xee, 0x6e,
0x15, 0xc0, 0xff, 0x85, 0xc1, 0x24, 0x25, 0x74, 0xe5, 0x57, 0xd3, 0x38, 0xdd, 0x13, 0xfd, 0xb3, 0x83, 0x7c, 0x80, 0x9d, 0x92, 0x5f, 0x92, 0xc7, 0xfa, 0x02, 0x3a, 0x77, 0x76, 0x0e, 0x56, 0xc2,
0x99, 0xf9, 0x9c, 0x29, 0x33, 0x53, 0x1a, 0xb3, 0x65, 0x66, 0xca, 0xa3, 0x4b, 0x98, 0x99, 0x1b, 0x2e, 0xcf, 0x8a, 0xe0, 0x7e, 0x65, 0x30, 0x49, 0x4d, 0xbb, 0xfa, 0x07, 0xec, 0x1c, 0xae, 0x88,
0xd7, 0x32, 0x33, 0x65, 0x97, 0xa3, 0xcc, 0x4c, 0xe9, 0xfc, 0x9b, 0x95, 0x17, 0xcd, 0xb7, 0xaa, 0xfe, 0x55, 0xcc, 0xb2, 0x1b, 0xd5, 0x89, 0xa9, 0xb5, 0xec, 0x3a, 0x31, 0xf5, 0x06, 0xa7, 0xc4,
0x40, 0xdc, 0xaa, 0xc9, 0x1f, 0x92, 0x27, 0x3f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xb6, 0xa5, 0x37, 0x2c, 0x8d, 0x6b, 0x9d, 0x98, 0xba, 0xc7, 0x51, 0x27, 0xa6, 0x76, 0xfe, 0xdd, 0xc6, 0xcb, 0xad,
0x75, 0xf7, 0x08, 0x00, 0x00, 0x77, 0xa6, 0x42, 0xdc, 0x98, 0xd9, 0x8f, 0x9b, 0xa7, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe2,
0x9e, 0x21, 0x0d, 0x43, 0x09, 0x00, 0x00,
} }

@ -383,6 +383,8 @@ type UpdateReleaseRequest struct {
Description string `protobuf:"bytes,12,opt,name=description" json:"description,omitempty"` Description string `protobuf:"bytes,12,opt,name=description" json:"description,omitempty"`
// Render subchart notes if enabled // Render subchart notes if enabled
SubNotes bool `protobuf:"varint,13,opt,name=subNotes" json:"subNotes,omitempty"` SubNotes bool `protobuf:"varint,13,opt,name=subNotes" json:"subNotes,omitempty"`
// Allow deletion of new resources created in this update when update failed
CleanupOnFail bool `protobuf:"varint,14,opt,name=cleanup_on_fail,json=cleanupOnFail" json:"cleanup_on_fail,omitempty"`
} }
func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} } func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} }
@ -481,6 +483,13 @@ func (m *UpdateReleaseRequest) GetSubNotes() bool {
return false return false
} }
func (m *UpdateReleaseRequest) GetCleanupOnFail() bool {
if m != nil {
return m.CleanupOnFail
}
return false
}
// UpdateReleaseResponse is the response to an update request. // UpdateReleaseResponse is the response to an update request.
type UpdateReleaseResponse struct { type UpdateReleaseResponse struct {
Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
@ -518,6 +527,8 @@ type RollbackReleaseRequest struct {
Force bool `protobuf:"varint,8,opt,name=force" json:"force,omitempty"` Force bool `protobuf:"varint,8,opt,name=force" json:"force,omitempty"`
// Description, if set, will set the description for the rollback // Description, if set, will set the description for the rollback
Description string `protobuf:"bytes,9,opt,name=description" json:"description,omitempty"` Description string `protobuf:"bytes,9,opt,name=description" json:"description,omitempty"`
// Allow deletion of new resources created in this rollback when rollback failed
CleanupOnFail bool `protobuf:"varint,10,opt,name=cleanup_on_fail,json=cleanupOnFail" json:"cleanup_on_fail,omitempty"`
} }
func (m *RollbackReleaseRequest) Reset() { *m = RollbackReleaseRequest{} } func (m *RollbackReleaseRequest) Reset() { *m = RollbackReleaseRequest{} }
@ -588,6 +599,13 @@ func (m *RollbackReleaseRequest) GetDescription() string {
return "" return ""
} }
func (m *RollbackReleaseRequest) GetCleanupOnFail() bool {
if m != nil {
return m.CleanupOnFail
}
return false
}
// RollbackReleaseResponse is the response to an update request. // RollbackReleaseResponse is the response to an update request.
type RollbackReleaseResponse struct { type RollbackReleaseResponse struct {
Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"`
@ -1441,87 +1459,89 @@ 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{
// 1308 bytes of a gzipped FileDescriptorProto // 1337 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xed, 0x6e, 0x1b, 0x45, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0xdd, 0x72, 0xdb, 0x44,
0x17, 0x8e, 0xbd, 0xfe, 0x3c, 0x4e, 0xfc, 0xba, 0xd3, 0x34, 0xd9, 0xee, 0x5b, 0x50, 0x58, 0x04, 0x14, 0x8e, 0x2d, 0xff, 0x1e, 0x27, 0xae, 0xbb, 0x4d, 0x13, 0xd5, 0x14, 0x26, 0x88, 0xa1, 0x75,
0x75, 0x0b, 0x75, 0xc0, 0xf0, 0x07, 0x09, 0x21, 0xa5, 0xae, 0x95, 0x14, 0x82, 0x2b, 0xad, 0xdb, 0x0b, 0x75, 0x20, 0x70, 0xc3, 0x0c, 0xc3, 0x4c, 0xea, 0x86, 0xa4, 0x10, 0xd2, 0x19, 0xb9, 0x2d,
0x22, 0x21, 0x21, 0x6b, 0x63, 0x8f, 0xdb, 0xa5, 0xeb, 0x5d, 0xb3, 0x33, 0x1b, 0x9a, 0x1b, 0x40, 0x33, 0xcc, 0x30, 0x1e, 0xc5, 0x5e, 0xb7, 0xa2, 0xb2, 0xd6, 0x68, 0x57, 0xa1, 0x79, 0x04, 0x2e,
0xe2, 0x27, 0x97, 0x80, 0xc4, 0x85, 0x70, 0x1f, 0xdc, 0x0c, 0x9a, 0xaf, 0xcd, 0xce, 0x7a, 0xd7, 0x79, 0x07, 0xae, 0x79, 0x06, 0x6e, 0x79, 0x06, 0x5e, 0x86, 0xd9, 0x3f, 0x45, 0x2b, 0x4b, 0xae,
0x5d, 0xf2, 0x27, 0xde, 0x99, 0x73, 0xe6, 0x7c, 0x3c, 0xcf, 0x9c, 0x33, 0x27, 0x60, 0xbd, 0x76, 0xc8, 0x4d, 0xac, 0xdd, 0x73, 0xf6, 0xfc, 0x7c, 0xdf, 0x9e, 0xb3, 0x67, 0x02, 0xfd, 0xd7, 0xde,
0xd7, 0xde, 0x31, 0xc1, 0xd1, 0xa5, 0x37, 0xc7, 0xe4, 0x98, 0x7a, 0xbe, 0x8f, 0xa3, 0xc1, 0x3a, 0xd2, 0xdf, 0xa7, 0x38, 0xba, 0xf0, 0xa7, 0x98, 0xee, 0x33, 0x3f, 0x08, 0x70, 0x34, 0x5c, 0x46,
0x0a, 0x69, 0x88, 0xf6, 0x99, 0x6c, 0xa0, 0x64, 0x03, 0x21, 0xb3, 0x0e, 0xf8, 0x89, 0xf9, 0x6b, 0x84, 0x11, 0xb4, 0xcd, 0x65, 0x43, 0x2d, 0x1b, 0x4a, 0x59, 0x7f, 0x47, 0x9c, 0x98, 0xbe, 0xf6,
0x37, 0xa2, 0xe2, 0xaf, 0xd0, 0xb6, 0x0e, 0xd3, 0xfb, 0x61, 0xb0, 0xf4, 0x5e, 0x49, 0x81, 0x70, 0x22, 0x26, 0xff, 0x4a, 0xed, 0xfe, 0x6e, 0x7a, 0x9f, 0x84, 0x73, 0xff, 0x95, 0x12, 0x48, 0x17,
0x11, 0x61, 0x1f, 0xbb, 0x04, 0xab, 0x5f, 0xed, 0x90, 0x92, 0x79, 0xc1, 0x32, 0x94, 0x82, 0xff, 0x11, 0x0e, 0xb0, 0x47, 0xb1, 0xfe, 0x35, 0x0e, 0x69, 0x99, 0x1f, 0xce, 0x89, 0x12, 0xbc, 0x67,
0x6b, 0x02, 0x8a, 0x09, 0x9d, 0x45, 0x71, 0x20, 0x85, 0x77, 0x35, 0x21, 0xa1, 0x2e, 0x8d, 0x89, 0x08, 0x18, 0xa6, 0x6c, 0x12, 0xc5, 0xa1, 0x12, 0xde, 0x31, 0x84, 0x94, 0x79, 0x2c, 0xa6, 0x86,
0xe6, 0xec, 0x12, 0x47, 0xc4, 0x0b, 0x03, 0xf5, 0x2b, 0x64, 0xf6, 0xdf, 0x55, 0xb8, 0x7d, 0xee, 0xb3, 0x0b, 0x1c, 0x51, 0x9f, 0x84, 0xfa, 0x57, 0xca, 0x9c, 0xbf, 0xab, 0x70, 0xeb, 0xd4, 0xa7,
0x11, 0xea, 0x88, 0x83, 0xc4, 0xc1, 0xbf, 0xc4, 0x98, 0x50, 0xb4, 0x0f, 0x75, 0xdf, 0x5b, 0x79, 0xcc, 0x95, 0x07, 0xa9, 0x8b, 0x7f, 0x8d, 0x31, 0x65, 0x68, 0x1b, 0xea, 0x81, 0xbf, 0xf0, 0x99,
0xd4, 0xac, 0x1c, 0x55, 0xfa, 0x86, 0x23, 0x16, 0xe8, 0x00, 0x1a, 0xe1, 0x72, 0x49, 0x30, 0x35, 0x5d, 0xd9, 0xab, 0x0c, 0x2c, 0x57, 0x2e, 0xd0, 0x0e, 0x34, 0xc8, 0x7c, 0x4e, 0x31, 0xb3, 0xab,
0xab, 0x47, 0x95, 0x7e, 0xdb, 0x91, 0x2b, 0xf4, 0x0d, 0x34, 0x49, 0x18, 0xd1, 0xd9, 0xc5, 0x95, 0x7b, 0x95, 0x41, 0xdb, 0x55, 0x2b, 0xf4, 0x0d, 0x34, 0x29, 0x89, 0xd8, 0xe4, 0xfc, 0xd2, 0xb6,
0x69, 0x1c, 0x55, 0xfa, 0xdd, 0xe1, 0x47, 0x83, 0x3c, 0x9c, 0x06, 0xcc, 0xd3, 0x34, 0x8c, 0xe8, 0xf6, 0x2a, 0x83, 0xee, 0xc1, 0xc7, 0xc3, 0x3c, 0x9c, 0x86, 0xdc, 0xd3, 0x98, 0x44, 0x6c, 0xc8,
0x80, 0xfd, 0x79, 0x7c, 0xe5, 0x34, 0x08, 0xff, 0x65, 0x76, 0x97, 0x9e, 0x4f, 0x71, 0x64, 0xd6, 0xff, 0x3c, 0xbe, 0x74, 0x1b, 0x54, 0xfc, 0x72, 0xbb, 0x73, 0x3f, 0x60, 0x38, 0xb2, 0x6b, 0xd2,
0x84, 0x5d, 0xb1, 0x42, 0xa7, 0x00, 0xdc, 0x6e, 0x18, 0x2d, 0x70, 0x64, 0xd6, 0xb9, 0xe9, 0x7e, 0xae, 0x5c, 0xa1, 0x63, 0x00, 0x61, 0x97, 0x44, 0x33, 0x1c, 0xd9, 0x75, 0x61, 0x7a, 0x50, 0xc2,
0x09, 0xd3, 0xcf, 0x98, 0xbe, 0xd3, 0x26, 0xea, 0x13, 0x7d, 0x0d, 0xbb, 0x02, 0x92, 0xd9, 0x3c, 0xf4, 0x33, 0xae, 0xef, 0xb6, 0xa9, 0xfe, 0x44, 0x5f, 0xc3, 0xa6, 0x84, 0x64, 0x32, 0x25, 0x33,
0x5c, 0x60, 0x62, 0x36, 0x8e, 0x8c, 0x7e, 0x77, 0x78, 0x57, 0x98, 0x52, 0xf0, 0x4f, 0x05, 0x68, 0x4c, 0xed, 0xc6, 0x9e, 0x35, 0xe8, 0x1e, 0xdc, 0x91, 0xa6, 0x34, 0xfc, 0x63, 0x09, 0xda, 0x88,
0xa3, 0x70, 0x81, 0x9d, 0x8e, 0x50, 0x67, 0xdf, 0x04, 0xdd, 0x83, 0x76, 0xe0, 0xae, 0x30, 0x59, 0xcc, 0xb0, 0xdb, 0x91, 0xea, 0xfc, 0x9b, 0xa2, 0xbb, 0xd0, 0x0e, 0xbd, 0x05, 0xa6, 0x4b, 0x6f,
0xbb, 0x73, 0x6c, 0x36, 0x79, 0x84, 0xd7, 0x1b, 0x76, 0x00, 0x2d, 0xe5, 0xdc, 0x7e, 0x0c, 0x0d, 0x8a, 0xed, 0xa6, 0x88, 0xf0, 0x6a, 0xc3, 0x09, 0xa1, 0xa5, 0x9d, 0x3b, 0x8f, 0xa1, 0x21, 0x53,
0x91, 0x1a, 0xea, 0x40, 0xf3, 0xc5, 0xe4, 0xbb, 0xc9, 0xb3, 0x1f, 0x26, 0xbd, 0x1d, 0xd4, 0x82, 0x43, 0x1d, 0x68, 0xbe, 0x38, 0xfb, 0xfe, 0xec, 0xd9, 0x8f, 0x67, 0xbd, 0x0d, 0xd4, 0x82, 0xda,
0xda, 0xe4, 0xe4, 0xfb, 0x71, 0xaf, 0x82, 0x6e, 0xc1, 0xde, 0xf9, 0xc9, 0xf4, 0xf9, 0xcc, 0x19, 0xd9, 0xe1, 0x0f, 0x47, 0xbd, 0x0a, 0xba, 0x09, 0x5b, 0xa7, 0x87, 0xe3, 0xe7, 0x13, 0xf7, 0xe8,
0x9f, 0x8f, 0x4f, 0xa6, 0xe3, 0x27, 0xbd, 0x2a, 0xea, 0x02, 0x8c, 0xce, 0x4e, 0x9c, 0xe7, 0x33, 0xf4, 0xe8, 0x70, 0x7c, 0xf4, 0xa4, 0x57, 0x45, 0x5d, 0x80, 0xd1, 0xc9, 0xa1, 0xfb, 0x7c, 0x22,
0xae, 0x62, 0xd8, 0xef, 0x43, 0x3b, 0xc9, 0x01, 0x35, 0xc1, 0x38, 0x99, 0x8e, 0x84, 0x89, 0x27, 0x54, 0x2c, 0xe7, 0x03, 0x68, 0x27, 0x39, 0xa0, 0x26, 0x58, 0x87, 0xe3, 0x91, 0x34, 0xf1, 0xe4,
0xe3, 0xe9, 0xa8, 0x57, 0xb1, 0x7f, 0xaf, 0xc0, 0xbe, 0x4e, 0x19, 0x59, 0x87, 0x01, 0xc1, 0x8c, 0x68, 0x3c, 0xea, 0x55, 0x9c, 0xdf, 0x2b, 0xb0, 0x6d, 0x52, 0x46, 0x97, 0x24, 0xa4, 0x98, 0x73,
0xb3, 0x79, 0x18, 0x07, 0x09, 0x67, 0x7c, 0x81, 0x10, 0xd4, 0x02, 0xfc, 0x56, 0x31, 0xc6, 0xbf, 0x36, 0x25, 0x71, 0x98, 0x70, 0x26, 0x16, 0x08, 0x41, 0x2d, 0xc4, 0x6f, 0x35, 0x63, 0xe2, 0x9b,
0x99, 0x26, 0x0d, 0xa9, 0xeb, 0x73, 0xb6, 0x0c, 0x47, 0x2c, 0xd0, 0xe7, 0xd0, 0x92, 0x50, 0x10, 0x6b, 0x32, 0xc2, 0xbc, 0x40, 0xb0, 0x65, 0xb9, 0x72, 0x81, 0x3e, 0x87, 0x96, 0x82, 0x82, 0xda,
0xb3, 0x76, 0x64, 0xf4, 0x3b, 0xc3, 0x3b, 0x3a, 0x40, 0xd2, 0xa3, 0x93, 0xa8, 0xd9, 0xa7, 0x70, 0xb5, 0x3d, 0x6b, 0xd0, 0x39, 0xb8, 0x6d, 0x02, 0xa4, 0x3c, 0xba, 0x89, 0x9a, 0x73, 0x0c, 0xbb,
0x78, 0x8a, 0x55, 0x24, 0x02, 0x3f, 0x75, 0x83, 0x98, 0x5f, 0x77, 0x85, 0x79, 0x30, 0xcc, 0xaf, 0xc7, 0x58, 0x47, 0x22, 0xf1, 0xd3, 0x37, 0x88, 0xfb, 0xf5, 0x16, 0x58, 0x04, 0xc3, 0xfd, 0x7a,
0xbb, 0xc2, 0xc8, 0x84, 0xa6, 0xbc, 0x7e, 0x3c, 0x9c, 0xba, 0xa3, 0x96, 0x36, 0x05, 0x73, 0xd3, 0x0b, 0x8c, 0x6c, 0x68, 0xaa, 0xeb, 0x27, 0xc2, 0xa9, 0xbb, 0x7a, 0xe9, 0x30, 0xb0, 0x57, 0x0d,
0x90, 0xcc, 0x2b, 0xcf, 0xd2, 0xc7, 0x50, 0x63, 0x95, 0xc1, 0xcd, 0x74, 0x86, 0x48, 0x8f, 0xf3, 0xa9, 0xbc, 0xf2, 0x2c, 0xdd, 0x83, 0x1a, 0xaf, 0x0c, 0x61, 0xa6, 0x73, 0x80, 0xcc, 0x38, 0x9f,
0x69, 0xb0, 0x0c, 0x1d, 0x2e, 0xd7, 0xa9, 0x33, 0xb2, 0xd4, 0x9d, 0xa5, 0xbd, 0x8e, 0xc2, 0x80, 0x86, 0x73, 0xe2, 0x0a, 0xb9, 0x49, 0x9d, 0x95, 0xa5, 0xee, 0x24, 0xed, 0x75, 0x44, 0x42, 0x86,
0xe2, 0x80, 0xde, 0x2c, 0xfe, 0x73, 0xb8, 0x9b, 0x63, 0x49, 0x26, 0x70, 0x0c, 0x4d, 0x19, 0x1a, 0x43, 0x76, 0xbd, 0xf8, 0x4f, 0xe1, 0x4e, 0x8e, 0x25, 0x95, 0xc0, 0x3e, 0x34, 0x55, 0x68, 0xc2,
0xb7, 0x56, 0x88, 0xab, 0xd2, 0xb2, 0xff, 0x34, 0x60, 0xff, 0xc5, 0x7a, 0xe1, 0x52, 0xac, 0x44, 0x5a, 0x21, 0xae, 0x5a, 0xcb, 0xf9, 0xc7, 0x82, 0xed, 0x17, 0xcb, 0x99, 0xc7, 0xb0, 0x16, 0xad,
0x5b, 0x82, 0xba, 0x0f, 0x75, 0xde, 0x61, 0x24, 0x16, 0xb7, 0x84, 0x6d, 0xd1, 0x86, 0x46, 0xec, 0x09, 0xea, 0x3e, 0xd4, 0x45, 0x87, 0x51, 0x58, 0xdc, 0x94, 0xb6, 0x65, 0x1b, 0x1a, 0xf1, 0xbf,
0xaf, 0x23, 0xe4, 0xe8, 0x21, 0x34, 0x2e, 0x5d, 0x3f, 0xc6, 0x84, 0x03, 0x91, 0xa0, 0x26, 0x35, 0xae, 0x94, 0xa3, 0x87, 0xd0, 0xb8, 0xf0, 0x82, 0x18, 0x53, 0x01, 0x44, 0x82, 0x9a, 0xd2, 0x14,
0x79, 0x7b, 0x72, 0xa4, 0x06, 0x3a, 0x84, 0xe6, 0x22, 0xba, 0x62, 0xfd, 0x85, 0x97, 0x64, 0xcb, 0xed, 0xc9, 0x55, 0x1a, 0x68, 0x17, 0x9a, 0xb3, 0xe8, 0x92, 0xf7, 0x17, 0x51, 0x92, 0x2d, 0xb7,
0x69, 0x2c, 0xa2, 0x2b, 0x27, 0x0e, 0xd0, 0x87, 0xb0, 0xb7, 0xf0, 0x88, 0x7b, 0xe1, 0xe3, 0xd9, 0x31, 0x8b, 0x2e, 0xdd, 0x38, 0x44, 0x1f, 0xc1, 0xd6, 0xcc, 0xa7, 0xde, 0x79, 0x80, 0x27, 0xaf,
0xeb, 0x30, 0x7c, 0x43, 0x78, 0x55, 0xb6, 0x9c, 0x5d, 0xb9, 0x79, 0xc6, 0xf6, 0x90, 0xc5, 0x6e, 0x09, 0x79, 0x43, 0x45, 0x55, 0xb6, 0xdc, 0x4d, 0xb5, 0x79, 0xc2, 0xf7, 0x50, 0x9f, 0xdf, 0xa4,
0xd2, 0x3c, 0xc2, 0x2e, 0xc5, 0x66, 0x83, 0xcb, 0x93, 0x35, 0xc3, 0x90, 0x7a, 0x2b, 0x1c, 0xc6, 0x69, 0x84, 0x3d, 0x86, 0xed, 0x86, 0x90, 0x27, 0x6b, 0x8e, 0x21, 0xf3, 0x17, 0x98, 0xc4, 0x4c,
0x94, 0x97, 0x92, 0xe1, 0xa8, 0x25, 0xfa, 0x00, 0x76, 0x23, 0x4c, 0x30, 0x9d, 0xc9, 0x28, 0x5b, 0x94, 0x92, 0xe5, 0xea, 0x25, 0xfa, 0x10, 0x36, 0x23, 0x4c, 0x31, 0x9b, 0xa8, 0x28, 0x5b, 0xe2,
0xfc, 0x64, 0x87, 0xef, 0xbd, 0x14, 0x61, 0x21, 0xa8, 0xfd, 0xea, 0x7a, 0xd4, 0x6c, 0x73, 0x11, 0x64, 0x47, 0xec, 0xbd, 0x94, 0x61, 0x21, 0xa8, 0xfd, 0xe6, 0xf9, 0xcc, 0x6e, 0x0b, 0x91, 0xf8,
0xff, 0x16, 0xc7, 0x62, 0x82, 0xd5, 0x31, 0x50, 0xc7, 0x62, 0x82, 0xe5, 0xb1, 0x7d, 0xa8, 0x2f, 0x96, 0xc7, 0x62, 0x8a, 0xf5, 0x31, 0xd0, 0xc7, 0x62, 0x8a, 0xd5, 0xb1, 0x6d, 0xa8, 0xcf, 0x49,
0xc3, 0x68, 0x8e, 0xcd, 0x0e, 0x97, 0x89, 0x05, 0x3a, 0x82, 0xce, 0x02, 0x93, 0x79, 0xe4, 0xad, 0x34, 0xc5, 0x76, 0x47, 0xc8, 0xe4, 0x02, 0xed, 0x41, 0x67, 0x86, 0xe9, 0x34, 0xf2, 0x97, 0x8c,
0x29, 0x63, 0x74, 0x97, 0x63, 0x9a, 0xde, 0x62, 0x79, 0x90, 0xf8, 0x62, 0x12, 0x52, 0x4c, 0xcc, 0x33, 0xba, 0x29, 0x30, 0x4d, 0x6f, 0xf1, 0x3c, 0x68, 0x7c, 0x7e, 0x46, 0x18, 0xa6, 0xf6, 0x96,
0x3d, 0x91, 0x87, 0x5a, 0xdb, 0x67, 0x70, 0x27, 0x43, 0xd1, 0x4d, 0xd9, 0xfe, 0xad, 0x0a, 0x07, 0xcc, 0x43, 0xaf, 0xd1, 0x3d, 0xb8, 0x31, 0x0d, 0xb0, 0x17, 0xc6, 0xcb, 0x09, 0x09, 0x27, 0x73,
0x4e, 0xe8, 0xfb, 0x17, 0xee, 0xfc, 0x4d, 0x09, 0xbe, 0x53, 0xd4, 0x54, 0xb7, 0x53, 0x63, 0xe4, 0xcf, 0x0f, 0xec, 0xae, 0x50, 0xd9, 0x52, 0xdb, 0xcf, 0xc2, 0x6f, 0x3d, 0x3f, 0x70, 0x4e, 0xe0,
0x50, 0x93, 0xba, 0xc2, 0x35, 0xed, 0x0a, 0x6b, 0xa4, 0xd5, 0x8b, 0x49, 0x6b, 0xe8, 0xa4, 0x29, 0x76, 0x86, 0xca, 0xeb, 0xde, 0x8a, 0xbf, 0xaa, 0xb0, 0xe3, 0x92, 0x20, 0x38, 0xf7, 0xa6, 0x6f,
0x46, 0x9a, 0x29, 0x46, 0x12, 0xb8, 0x5b, 0x5b, 0xe0, 0x6e, 0x6f, 0xc0, 0x6d, 0x7f, 0x0b, 0x87, 0x4a, 0xdc, 0x8b, 0x14, 0x85, 0xd5, 0xf5, 0x14, 0x5a, 0x39, 0x14, 0xa6, 0xae, 0x7a, 0xcd, 0xb8,
0x1b, 0x38, 0xdc, 0x14, 0xd4, 0x3f, 0x0c, 0xb8, 0xf3, 0x34, 0x20, 0xd4, 0xf5, 0xfd, 0x0c, 0xa6, 0xea, 0x06, 0xb9, 0xf5, 0x62, 0x72, 0x1b, 0x26, 0xb9, 0x9a, 0xb9, 0x66, 0x8a, 0xb9, 0x84, 0x96,
0x49, 0xbd, 0x54, 0x4a, 0xd7, 0x4b, 0xf5, 0xbf, 0xd4, 0x8b, 0xa1, 0x91, 0xa2, 0x18, 0xac, 0xa5, 0xd6, 0x1a, 0x5a, 0xda, 0xab, 0xb4, 0xe4, 0x40, 0x0f, 0x79, 0xd0, 0x7f, 0x07, 0xbb, 0x2b, 0x78,
0x18, 0x2c, 0x55, 0x43, 0x5a, 0xe7, 0x6a, 0x64, 0x3a, 0x17, 0x7a, 0x0f, 0x40, 0x5c, 0x7a, 0x6e, 0x5d, 0x17, 0xfc, 0x3f, 0x2c, 0xb8, 0xfd, 0x34, 0xa4, 0xcc, 0x0b, 0x82, 0x0c, 0xf6, 0x49, 0xfd,
0x5c, 0x80, 0xdf, 0xe6, 0x3b, 0x13, 0xd9, 0xa8, 0x14, 0x5f, 0xad, 0x7c, 0xbe, 0xd2, 0x15, 0xd4, 0x55, 0x4a, 0xd7, 0x5f, 0xf5, 0xff, 0xd4, 0x9f, 0x65, 0x90, 0xa7, 0x99, 0xae, 0xa5, 0x98, 0x2e,
0x87, 0x9e, 0x8a, 0x67, 0x1e, 0x2d, 0x78, 0x4c, 0xb2, 0x8a, 0xba, 0x72, 0x7f, 0x14, 0x2d, 0x58, 0x55, 0x93, 0x46, 0x27, 0x6c, 0x64, 0x3a, 0x21, 0x7a, 0x1f, 0x40, 0x16, 0x91, 0x30, 0x2e, 0x49,
0x54, 0x59, 0x0e, 0x3b, 0xdb, 0x4b, 0x66, 0x37, 0x53, 0x32, 0x4f, 0xe1, 0x20, 0x4b, 0xc9, 0x4d, 0x6a, 0x8b, 0x9d, 0x33, 0xd5, 0xf8, 0x34, 0xaf, 0xad, 0x7c, 0x5e, 0xd3, 0x15, 0x39, 0x80, 0x9e,
0xe9, 0xfd, 0xab, 0x02, 0x87, 0x2f, 0x02, 0x2f, 0x97, 0xe0, 0xbc, 0xa2, 0xd9, 0x80, 0xbc, 0x9a, 0x8e, 0x67, 0x1a, 0xcd, 0x44, 0x4c, 0x8a, 0xa0, 0xae, 0xda, 0x1f, 0x45, 0x33, 0x1e, 0x55, 0x96,
0x03, 0xf9, 0x3e, 0xd4, 0xd7, 0x71, 0xf4, 0x0a, 0x4b, 0x0a, 0xc5, 0x22, 0x8d, 0x65, 0x4d, 0xc7, 0xeb, 0xce, 0xfa, 0x12, 0xdc, 0x34, 0x4b, 0xd0, 0x79, 0x0a, 0x3b, 0x59, 0x4a, 0xae, 0x4b, 0xef,
0x32, 0x83, 0x46, 0x7d, 0xf3, 0x46, 0xcf, 0xc0, 0xdc, 0x8c, 0xf2, 0x86, 0x39, 0xb3, 0xbc, 0x92, 0x9f, 0x15, 0xd8, 0x7d, 0x11, 0xfa, 0xb9, 0x04, 0xe7, 0x15, 0xd7, 0x0a, 0xe4, 0xd5, 0x1c, 0xc8,
0x37, 0xaf, 0x2d, 0xde, 0x37, 0xfb, 0x36, 0xdc, 0x3a, 0xc5, 0xf4, 0xa5, 0x28, 0x61, 0x09, 0x80, 0xb7, 0xa1, 0xbe, 0x8c, 0xa3, 0x57, 0x58, 0x51, 0x28, 0x17, 0x69, 0x2c, 0x6b, 0x26, 0x96, 0x19,
0x3d, 0x06, 0x94, 0xde, 0xbc, 0xf6, 0x27, 0xb7, 0x74, 0x7f, 0x6a, 0x20, 0x54, 0xfa, 0x4a, 0xcb, 0x34, 0xea, 0x2b, 0x68, 0x38, 0x13, 0xb0, 0x57, 0xa3, 0xbc, 0x66, 0xce, 0x3c, 0xaf, 0xe4, 0x0d,
0xfe, 0x8a, 0xdb, 0x3e, 0xf3, 0x08, 0x0d, 0xa3, 0xab, 0x6d, 0xe0, 0xf6, 0xc0, 0x58, 0xb9, 0x6f, 0x6d, 0xcb, 0xf7, 0xd2, 0xb9, 0x05, 0x37, 0x8f, 0x31, 0x7b, 0x29, 0x4b, 0x5d, 0x01, 0xe0, 0x1c,
0xe5, 0x93, 0xc8, 0x3e, 0xed, 0x53, 0x1e, 0x41, 0x72, 0x54, 0x46, 0x90, 0x1e, 0x30, 0x2a, 0xe5, 0x01, 0x4a, 0x6f, 0x5e, 0xf9, 0x53, 0x5b, 0xa6, 0x3f, 0x3d, 0x60, 0x6a, 0x7d, 0xad, 0xe5, 0x7c,
0x06, 0x8c, 0xb7, 0x80, 0x9e, 0xe3, 0x64, 0xd6, 0x79, 0xc7, 0xdb, 0xac, 0x68, 0xaa, 0xea, 0x34, 0x25, 0x6c, 0x9f, 0xf8, 0x94, 0x91, 0xe8, 0x72, 0x1d, 0xb8, 0x3d, 0xb0, 0x16, 0xde, 0x5b, 0xf5,
0x99, 0xd0, 0x9c, 0xfb, 0xd8, 0x0d, 0xe2, 0xb5, 0x24, 0x56, 0x2d, 0xd9, 0x65, 0x5d, 0xbb, 0x91, 0xc4, 0xf2, 0x4f, 0xe7, 0x58, 0x44, 0x90, 0x1c, 0x55, 0x11, 0xa4, 0x07, 0x96, 0x4a, 0xb9, 0x81,
0xeb, 0xfb, 0xd8, 0x97, 0xcf, 0x5c, 0xb2, 0xb6, 0x7f, 0x82, 0xdb, 0x9a, 0x67, 0x99, 0x03, 0xcb, 0xe5, 0x2d, 0xa0, 0xe7, 0x38, 0x99, 0x9d, 0xde, 0xf1, 0xd6, 0x6b, 0x9a, 0xaa, 0x26, 0x4d, 0x36,
0x95, 0xbc, 0x92, 0x9e, 0xd9, 0x27, 0xfa, 0x12, 0x1a, 0x62, 0x58, 0xe4, 0x7e, 0xbb, 0xc3, 0x7b, 0x34, 0x55, 0x9f, 0x51, 0xc4, 0xea, 0x25, 0xbf, 0xac, 0x4b, 0x2f, 0xf2, 0x82, 0x00, 0x07, 0xea,
0x7a, 0x4e, 0xdc, 0x48, 0x1c, 0xc8, 0xe9, 0xd2, 0x91, 0xba, 0xc3, 0x7f, 0x5a, 0xd0, 0x55, 0xe3, 0xd9, 0x4c, 0xd6, 0xce, 0xcf, 0x70, 0xcb, 0xf0, 0xac, 0x72, 0xe0, 0xb9, 0xd2, 0x57, 0xca, 0x33,
0x8e, 0x18, 0x65, 0x91, 0x07, 0xbb, 0xe9, 0xb9, 0x0e, 0x3d, 0x28, 0x9e, 0x74, 0x33, 0xe3, 0xba, 0xff, 0x44, 0x5f, 0x42, 0x43, 0x0e, 0x9f, 0xc2, 0x6f, 0xf7, 0xe0, 0xae, 0x99, 0x93, 0x30, 0x12,
0xf5, 0xb0, 0x8c, 0xaa, 0xc8, 0xc0, 0xde, 0xf9, 0xac, 0x82, 0x08, 0xf4, 0xb2, 0xe3, 0x16, 0x7a, 0x87, 0x6a, 0x5a, 0x75, 0x95, 0xee, 0xc1, 0xbf, 0x2d, 0xe8, 0xea, 0xf1, 0x49, 0x8e, 0xc6, 0xc8,
0x94, 0x6f, 0xa3, 0x60, 0xbe, 0xb3, 0x06, 0x65, 0xd5, 0x95, 0x5b, 0x74, 0xc9, 0xef, 0x93, 0x3e, 0x87, 0xcd, 0xf4, 0x9c, 0x88, 0x1e, 0x14, 0x4f, 0xce, 0x99, 0xf1, 0xbf, 0xff, 0xb0, 0x8c, 0xaa,
0x23, 0xa1, 0x77, 0x9a, 0xd1, 0xc7, 0x32, 0xeb, 0xb8, 0xb4, 0x7e, 0xe2, 0xf7, 0x67, 0xd8, 0xd3, 0xcc, 0xc0, 0xd9, 0xf8, 0xac, 0x82, 0x28, 0xf4, 0xb2, 0xe3, 0x1b, 0x7a, 0x94, 0x6f, 0xa3, 0x60,
0x5e, 0x6a, 0x54, 0x80, 0x56, 0xde, 0xc4, 0x65, 0x7d, 0x52, 0x4a, 0x37, 0xf1, 0xb5, 0x82, 0xae, 0x5e, 0xec, 0x0f, 0xcb, 0xaa, 0x6b, 0xb7, 0xe8, 0x42, 0xdc, 0x27, 0x73, 0xe6, 0x42, 0xef, 0x34,
0xde, 0xe2, 0x50, 0x81, 0x81, 0xdc, 0xb7, 0xc9, 0xfa, 0xb4, 0x9c, 0x72, 0xe2, 0x8e, 0x40, 0x2f, 0x63, 0x8e, 0x79, 0xfd, 0xfd, 0xd2, 0xfa, 0x89, 0xdf, 0x5f, 0x60, 0xcb, 0x78, 0xd1, 0x51, 0x01,
0xdb, 0x5f, 0x8a, 0x78, 0x2c, 0xe8, 0x96, 0x45, 0x3c, 0x16, 0xb5, 0x2d, 0x7b, 0x07, 0xb9, 0x00, 0x5a, 0x79, 0x13, 0x5c, 0xff, 0x93, 0x52, 0xba, 0x89, 0xaf, 0x05, 0x74, 0xcd, 0x16, 0x87, 0x0a,
0xd7, 0xed, 0x05, 0xdd, 0x2f, 0x24, 0x44, 0xef, 0x4a, 0x56, 0xff, 0xdd, 0x8a, 0x89, 0x8b, 0x35, 0x0c, 0xe4, 0xbe, 0x4d, 0xfd, 0x4f, 0xcb, 0x29, 0x27, 0xee, 0x28, 0xf4, 0xb2, 0xfd, 0xa5, 0x88,
0xfc, 0x2f, 0x33, 0x09, 0xa0, 0x02, 0x68, 0xf2, 0x07, 0x27, 0xeb, 0x51, 0x49, 0xed, 0x4c, 0x52, 0xc7, 0x82, 0x6e, 0x59, 0xc4, 0x63, 0x51, 0xdb, 0x72, 0x36, 0x90, 0x07, 0x70, 0xd5, 0x5e, 0xd0,
0xb2, 0x63, 0x6d, 0x49, 0x4a, 0x6f, 0x87, 0x5b, 0x92, 0xca, 0x34, 0x3f, 0x7b, 0x07, 0x79, 0xd0, 0xfd, 0x42, 0x42, 0xcc, 0xae, 0xd4, 0x1f, 0xbc, 0x5b, 0x31, 0x71, 0xb1, 0x84, 0x1b, 0x99, 0x49,
0x75, 0xe2, 0x40, 0xba, 0x66, 0x6d, 0x01, 0x15, 0x9c, 0xde, 0xec, 0x78, 0xd6, 0x83, 0x12, 0x9a, 0x00, 0x15, 0x40, 0x93, 0x3f, 0x60, 0xf5, 0x1f, 0x95, 0xd4, 0xce, 0x24, 0xa5, 0x3a, 0xd6, 0x9a,
0xd7, 0xf5, 0xfd, 0x18, 0x7e, 0x6c, 0x29, 0xd5, 0x8b, 0x06, 0xff, 0x4f, 0xff, 0x8b, 0x7f, 0x03, 0xa4, 0xcc, 0x76, 0xb8, 0x26, 0xa9, 0x4c, 0xf3, 0x73, 0x36, 0x90, 0x0f, 0x5d, 0x37, 0x0e, 0x95,
0x00, 0x00, 0xff, 0xff, 0x30, 0x18, 0x95, 0x9c, 0xd7, 0x10, 0x00, 0x00, 0x6b, 0xde, 0x16, 0x50, 0xc1, 0xe9, 0xd5, 0x8e, 0xd7, 0x7f, 0x50, 0x42, 0xf3, 0xaa, 0xbe, 0x1f,
0xc3, 0x4f, 0x2d, 0xad, 0x7a, 0xde, 0x10, 0xff, 0x39, 0xf8, 0xe2, 0xbf, 0x00, 0x00, 0x00, 0xff,
0xff, 0x20, 0xcd, 0x9e, 0x3a, 0x27, 0x11, 0x00, 0x00,
} }

@ -126,14 +126,17 @@ type KubeClient interface {
// error. // error.
WatchUntilReady(namespace string, reader io.Reader, timeout int64, shouldWait bool) error WatchUntilReady(namespace string, reader io.Reader, timeout int64, shouldWait bool) error
// Update updates one or more resources or creates the resource // Deprecated; use UpdateWithOptions instead
Update(namespace string, originalReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error
// UpdateWithOptions updates one or more resources or creates the resource
// if it doesn't exist. // if it doesn't exist.
// //
// namespace must contain a valid existing namespace. // namespace must contain a valid existing namespace.
// //
// reader must contain a YAML stream (one or more YAML documents separated // reader must contain a YAML stream (one or more YAML documents separated
// by "\n---\n"). // by "\n---\n").
Update(namespace string, originalReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error UpdateWithOptions(namespace string, originalReader, modifiedReader io.Reader, opts kube.UpdateOptions) error
Build(namespace string, reader io.Reader) (kube.Result, error) Build(namespace string, reader io.Reader) (kube.Result, error)
BuildUnstructured(namespace string, reader io.Reader) (kube.Result, error) BuildUnstructured(namespace string, reader io.Reader) (kube.Result, error)
@ -141,6 +144,8 @@ type KubeClient interface {
// WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase
// and returns said phase (PodSucceeded or PodFailed qualify). // and returns said phase (PodSucceeded or PodFailed qualify).
WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error)
WaitUntilCRDEstablished(reader io.Reader, timeout time.Duration) error
} }
// PrintingKubeClient implements KubeClient, but simply prints the reader to // PrintingKubeClient implements KubeClient, but simply prints the reader to
@ -177,6 +182,16 @@ func (p *PrintingKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int
// Update implements KubeClient Update. // Update implements KubeClient Update.
func (p *PrintingKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { func (p *PrintingKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error {
return p.UpdateWithOptions(ns, currentReader, modifiedReader, kube.UpdateOptions{
Force: force,
Recreate: recreate,
Timeout: timeout,
ShouldWait: shouldWait,
})
}
// UpdateWithOptions implements KubeClient UpdateWithOptions.
func (p *PrintingKubeClient) UpdateWithOptions(ns string, currentReader, modifiedReader io.Reader, opts kube.UpdateOptions) error {
_, err := io.Copy(p.Out, modifiedReader) _, err := io.Copy(p.Out, modifiedReader)
return err return err
} }
@ -197,6 +212,11 @@ func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(namespace string, reade
return v1.PodUnknown, err return v1.PodUnknown, err
} }
func (p *PrintingKubeClient) WaitUntilCRDEstablished(reader io.Reader, timeout time.Duration) error {
_, err := io.Copy(p.Out, reader)
return err
}
// Environment provides the context for executing a client request. // Environment provides the context for executing a client request.
// //
// All services in a context are concurrency safe. // All services in a context are concurrency safe.

@ -52,6 +52,9 @@ func (k *mockKubeClient) Delete(ns string, r io.Reader) error {
func (k *mockKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { func (k *mockKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error {
return nil return nil
} }
func (k *mockKubeClient) UpdateWithOptions(ns string, currentReader, modifiedReader io.Reader, opts kube.UpdateOptions) error {
return nil
}
func (k *mockKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { func (k *mockKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error {
return nil return nil
} }
@ -69,6 +72,10 @@ func (k *mockKubeClient) WaitAndGetCompletedPodStatus(namespace string, reader i
return "", nil return "", nil
} }
func (k *mockKubeClient) WaitUntilCRDEstablished(reader io.Reader, timeout time.Duration) error {
return nil
}
var _ Engine = &mockEngine{} var _ Engine = &mockEngine{}
var _ KubeClient = &mockKubeClient{} var _ KubeClient = &mockKubeClient{}
var _ KubeClient = &PrintingKubeClient{} var _ KubeClient = &PrintingKubeClient{}

@ -58,14 +58,26 @@ func (m *LocalReleaseModule) Create(r *release.Release, req *services.InstallRel
func (m *LocalReleaseModule) Update(current, target *release.Release, req *services.UpdateReleaseRequest, env *environment.Environment) error { func (m *LocalReleaseModule) Update(current, target *release.Release, req *services.UpdateReleaseRequest, env *environment.Environment) error {
c := bytes.NewBufferString(current.Manifest) c := bytes.NewBufferString(current.Manifest)
t := bytes.NewBufferString(target.Manifest) t := bytes.NewBufferString(target.Manifest)
return env.KubeClient.Update(target.Namespace, c, t, req.Force, req.Recreate, req.Timeout, req.Wait) return env.KubeClient.UpdateWithOptions(target.Namespace, c, t, kube.UpdateOptions{
Force: req.Force,
Recreate: req.Recreate,
Timeout: req.Timeout,
ShouldWait: req.Wait,
CleanupOnFail: req.CleanupOnFail,
})
} }
// Rollback performs a rollback from current to target release // Rollback performs a rollback from current to target release
func (m *LocalReleaseModule) Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, env *environment.Environment) error { func (m *LocalReleaseModule) Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, env *environment.Environment) error {
c := bytes.NewBufferString(current.Manifest) c := bytes.NewBufferString(current.Manifest)
t := bytes.NewBufferString(target.Manifest) t := bytes.NewBufferString(target.Manifest)
return env.KubeClient.Update(target.Namespace, c, t, req.Force, req.Recreate, req.Timeout, req.Wait) return env.KubeClient.UpdateWithOptions(target.Namespace, c, t, kube.UpdateOptions{
Force: req.Force,
Recreate: req.Recreate,
Timeout: req.Timeout,
ShouldWait: req.Wait,
CleanupOnFail: req.CleanupOnFail,
})
} }
// Status returns kubectl-like formatted status of release objects // Status returns kubectl-like formatted status of release objects

@ -23,6 +23,7 @@ import (
"path" "path"
"regexp" "regexp"
"strings" "strings"
"time"
"github.com/technosophos/moniker" "github.com/technosophos/moniker"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -399,7 +400,7 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin
b.Reset() b.Reset()
b.WriteString(h.Manifest) b.WriteString(h.Manifest)
// We can't watch CRDs // We can't watch CRDs, but need to wait until they reach the established state before continuing
if hook != hooks.CRDInstall { if hook != hooks.CRDInstall {
if err := kubeCli.WatchUntilReady(namespace, b, timeout, false); err != nil { if err := kubeCli.WatchUntilReady(namespace, b, timeout, false); err != nil {
s.Log("warning: Release %s %s %s could not complete: %s", name, hook, h.Path, err) s.Log("warning: Release %s %s %s could not complete: %s", name, hook, h.Path, err)
@ -410,6 +411,11 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin
} }
return err return err
} }
} else {
if err := kubeCli.WaitUntilCRDEstablished(b, time.Duration(timeout)*time.Second); err != nil {
s.Log("warning: Release %s %s %s could not complete: %s", name, hook, h.Path, err)
return err
}
} }
} }

@ -500,6 +500,15 @@ type updateFailingKubeClient struct {
} }
func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error {
return u.UpdateWithOptions(namespace, originalReader, modifiedReader, kube.UpdateOptions{
Force: force,
Recreate: recreate,
Timeout: timeout,
ShouldWait: shouldWait,
})
}
func (u *updateFailingKubeClient) UpdateWithOptions(namespace string, originalReader, modifiedReader io.Reader, opts kube.UpdateOptions) error {
return errors.New("Failed update in kube client") return errors.New("Failed update in kube client")
} }
@ -632,6 +641,9 @@ func (kc *mockHooksKubeClient) WatchUntilReady(ns string, r io.Reader, timeout i
func (kc *mockHooksKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { func (kc *mockHooksKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error {
return nil return nil
} }
func (kc *mockHooksKubeClient) UpdateWithOptions(ns string, currentReader, modifiedReader io.Reader, opts kube.UpdateOptions) error {
return nil
}
func (kc *mockHooksKubeClient) Build(ns string, reader io.Reader) (kube.Result, error) { func (kc *mockHooksKubeClient) Build(ns string, reader io.Reader) (kube.Result, error) {
return []*resource.Info{}, nil return []*resource.Info{}, nil
} }
@ -642,6 +654,10 @@ func (kc *mockHooksKubeClient) WaitAndGetCompletedPodPhase(namespace string, rea
return v1.PodUnknown, nil return v1.PodUnknown, nil
} }
func (kc *mockHooksKubeClient) WaitUntilCRDEstablished(reader io.Reader, timeout time.Duration) error {
return nil
}
func deletePolicyStub(kubeClient *mockHooksKubeClient) *ReleaseServer { func deletePolicyStub(kubeClient *mockHooksKubeClient) *ReleaseServer {
e := environment.New() e := environment.New()
e.Releases = storage.Init(driver.NewMemory()) e.Releases = storage.Init(driver.NewMemory())

Loading…
Cancel
Save