(feat) Add support for expanding templates in values

This is enabled via a new expandValues flag in Chart.yaml
pull/3252/head
Morgan Parry 8 years ago
parent 48e7039970
commit bba555aa3f

@ -89,5 +89,8 @@ message Metadata {
map<string,string> annotations = 16;
// KubeVersion is a SemVer constraint specifying the version of Kubernetes required.
string kubeVersion = 17;
string kubeVersion = 17;
// Whether or not any templates found in values should be recursively expanded
bool expandValues = 18;
}

@ -214,6 +214,7 @@ message UpdateReleaseRequest {
// UpdateReleaseResponse is the response to an update request.
message UpdateReleaseResponse {
hapi.release.Release release = 1;
hapi.chart.Config final_values = 2;
}
message RollbackReleaseRequest {
@ -276,6 +277,7 @@ message InstallReleaseRequest {
// InstallReleaseResponse is the response from a release installation.
message InstallReleaseResponse {
hapi.release.Release release = 1;
hapi.chart.Config final_values = 2;
}
// UninstallReleaseRequest represents a request to uninstall a named release.

@ -85,5 +85,5 @@ func (g *getCmd) run() error {
if err != nil {
return prettyError(err)
}
return printRelease(g.out, res.Release)
return printRelease(g.out, res.Release, nil)
}

@ -283,7 +283,7 @@ func (i *installCmd) run() error {
if rel == nil {
return nil
}
i.printRelease(rel)
i.printRelease(rel, res.GetFinalValues())
// If this is a dry run, we can't display status.
if i.dryRun {
@ -377,14 +377,14 @@ func vals(valueFiles valueFiles, values []string, stringValues []string) ([]byte
}
// printRelease prints info about a release if the Debug is true.
func (i *installCmd) printRelease(rel *release.Release) {
func (i *installCmd) printRelease(rel *release.Release, finalVals *chart.Config) {
if rel == nil {
return
}
// TODO: Switch to text/template like everything else.
fmt.Fprintf(i.out, "NAME: %s\n", rel.Name)
if settings.Debug {
printRelease(i.out, rel)
printRelease(i.out, rel, finalVals)
}
}
@ -516,7 +516,6 @@ func readFile(filePath string) ([]byte, error) {
// FIXME: maybe someone handle other protocols like ftp.
getterConstructor, err := p.ByScheme(u.Scheme)
if err != nil {
return ioutil.ReadFile(filePath)
}

@ -23,6 +23,7 @@ import (
"time"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/timeconv"
)
@ -44,23 +45,28 @@ MANIFEST:
{{.Release.Manifest}}
`
func printRelease(out io.Writer, rel *release.Release) error {
func printRelease(out io.Writer, rel *release.Release, finalVals *chart.Config) error {
if rel == nil {
return nil
}
cfg, err := chartutil.CoalesceValues(rel.Chart, rel.Config)
if err != nil {
return err
}
cfgStr, err := cfg.YAML()
if err != nil {
return err
var valsStr string
if finalVals == nil {
vals, err := chartutil.CoalesceValues(rel.Chart, rel.Config)
if err != nil {
return err
}
valsStr, err = vals.YAML()
if err != nil {
return err
}
} else {
valsStr = finalVals.Raw
}
data := map[string]interface{}{
"Release": rel,
"ComputedValues": cfgStr,
"ComputedValues": valsStr,
"ReleaseDate": timeconv.Format(rel.Info.LastDeployed, time.ANSIC),
}
return tpl(printReleaseTemplate, data, out)

@ -216,6 +216,12 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
return err
}
if c.Metadata.ExpandValues {
if vals, err = renderer.ExpandValues(vals); err != nil {
return err
}
}
out, err := renderer.Render(c, vals)
listManifests := []tiller.Manifest{}
if err != nil {
@ -254,7 +260,11 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
Namespace: t.namespace,
Info: &release.Info{LastDeployed: timeconv.Timestamp(time.Now())},
}
printRelease(os.Stdout, rel)
valsYaml, err := vals.YAML()
if err != nil {
return err
}
printRelease(os.Stdout, rel, &chart.Config{Raw: valsYaml})
}
for _, m := range tiller.SortByKind(listManifests) {

@ -230,7 +230,7 @@ func (u *upgradeCmd) run() error {
}
if settings.Debug {
printRelease(u.out, resp.Release)
printRelease(u.out, resp.Release, resp.GetFinalValues())
}
fmt.Fprintf(u.out, "Release %q has been upgraded. Happy Helming!\n", u.release)

@ -57,6 +57,7 @@ maintainers: # (optional)
email: The maintainer's email (optional for each maintainer)
url: A URL for the maintainer (optional for each maintainer)
engine: gotpl # The name of the template engine (optional, defaults to gotpl)
expandValues: Whether or not any templates found in values should be recursively expanded (optional, boolean)
icon: A URL to an SVG or PNG image to be used as an icon (optional).
appVersion: The version of the app that this contains (optional). This needn't be SemVer.
deprecated: Whether this chart is deprecated (optional, boolean)
@ -776,6 +777,95 @@ parent chart.
Also, global variables of parent charts take precedence over the global variables from subcharts.
### Templated Values
If the `expandValues` flag in `Chart.yaml` is set to `true` then values can be composed
using other values, via a custom `tval` function. Since all files must be comprised of
valid YAML, all such values are composed within YAML strings. For example:
```yaml
a: expanded
b: 'this will be {{ tval "a" }}' # => 'this will be expanded'
```
Expansion is performed recursively and multiple templates can be referenced:
```yaml
a: expanded
b:
1: this
2: will
3: be
4: '{{ tval "a" }}'
c: '{{ tval "b.1" }} {{ tval "b.2" }} {{ tval "b.3" }} {{ tval "b.4" }}' # => 'this will be expanded'
```
You can also use all of the usual templating functions:
```yaml
connection: '1.2.3.4:56'
port: '{{ index (splitList ":" (tval "connection")) 1 }}' # => 56
```
As you can see in the above examples, the `.Values` prefix that you would need to write
elsewhere is implicit here. You can still explicitly reference values that way if you
wish but then there will be no recursive expansion. For example, for a release called
`zealous-zebu`:
```yaml
a: 'My release is called {{ .Release.Name }}'
b: '{{ .Values.a }}' # Will not recursively expand; you will get the literal string 'My release is called {{ .Release.Name }}'
c: '{{ tval "a" }}' # Will recursively expand; you will get 'My release is called zealous-zebu'
```
One immediate application of this feature is to create extra values that are derived
from existing ones. For example, suppose that you wish to specify some database names
automatically based upon the release name:
```yaml
databases:
cities: 'db-{{ .Release.Name }}-cities'
cities-dev: '{{ tval "databases.cities" }}-dev'
```
Another use case is for passing values down to child charts, without having to duplicate
the configuration in question. Suppose that in `requirements.yaml` you have configured
the following:
```yaml
dependencies:
- name: child
...
```
You can then pass through values from the parent chart by writing something like this in
the parent's `values.yaml`:
```yaml
a: abc
b: def
child:
c: '{{ tval "a" }}'
d: '{{ tval "b" }}ghi'
e: jkl
```
That is, you can pass values through unchanged (`c`), pass them through modified (`d`),
and of course inject additional values (`e`). As usual, the child chart will see these
as `.Values.c` etc.
When this feature is enabled, if you wish to embed the literal string `{{` in a value
then you will need to write something like this:
```yaml
a: '{{`{{ actual braces }}`}}'
```
Note that the `tval` function is not available in any other context. Also,
values used for [requirement conditions](#tags-and-condition-fields-in-requirementsyaml)
cannot be templated.
### References
When it comes to writing templates and values files, there are several

@ -14,9 +14,11 @@ First, we added almost all of the functions in the
for security reasons: `env` and `expandenv` (which would have given chart authors
access to Tiller's environment).
We also added two special template functions: `include` and `required`. The `include`
function allows you to bring in another template, and then pass the results to other
template functions.
We also added four special template functions: `include`, `required`, `tpl`, and
`tval`.
The `include` function allows you to bring in another template, and then pass the
results to other template functions.
For example, this template snippet includes a template called `mytpl`, then
lowercases the result, then wraps that in double quotes.
@ -36,6 +38,9 @@ is required, and will print an error message when that entry is missing:
value: {{required "A valid .Values.who entry required!" .Values.who }}
```
The `tval` function enables templated values to be used. There are further
details [here](https://github.com/kubernetes/helm/blob/master/docs/charts.md#templated-values).
## Quote Strings, Don't Quote Integers
When you are working with string data, you are always safer quoting the

@ -166,49 +166,53 @@ func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap {
// Add the 'tpl' function here
funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) {
basePath, err := vals.PathValue("Template.BasePath")
if err != nil {
return "", fmt.Errorf("Cannot retrieve Template.Basepath from values inside tpl function: %s (%s)", tpl, err.Error())
}
r := renderable{
tpl: tpl,
vals: vals,
basePath: basePath.(string),
dummyName := "___tpl_template"
spec := renderSpec{
tpls: map[string]renderable{dummyName: {tpl: tpl, vals: vals}},
}
templates := map[string]renderable{}
templateName, err := vals.PathValue("Template.Name")
if err != nil {
return "", fmt.Errorf("Cannot retrieve Template.Name from values inside tpl function: %s (%s)", tpl, err.Error())
}
templates[templateName.(string)] = r
result, err := e.render(templates)
result, err := e.renderFromSpec(&spec)
if err != nil {
return "", fmt.Errorf("Error during tpl function execution for %q: %s", tpl, err.Error())
}
return result[templateName.(string)], nil
return result[dummyName], nil
}
return funcMap
}
// render takes a map of templates/values and renders them.
func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string, err error) {
func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) {
spec := renderSpec{
tpls: tpls,
// We want to parse the templates in a predictable order. The order favors
// higher-level (in file system) templates over deeply nested templates.
order: sortTemplates(tpls),
// Don't render partials. We don't care about the direct output of partials.
// They are only included from other templates.
filter: func(name string) bool { return !strings.HasPrefix(path.Base(name), "_") },
addTplMeta: true,
}
return e.renderFromSpec(&spec)
}
type renderSpec struct {
tpls map[string]renderable
order []string
filter func(string) bool
addTplMeta bool
}
type preparedTemplate struct {
tpl *template.Template
funcMap template.FuncMap
}
func (e *Engine) renderPrepare() (*preparedTemplate, error) {
// Basically, what we do here is start with an empty parent template and then
// build up a list of templates -- one for each file. Once all of the templates
// have been parsed, we loop through again and execute every template.
//
// The idea with this process is to make it possible for more complex templates
// to share common blocks, but to make the entire thing feel like a file-based
// template engine.
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("rendering template failed: %v", r)
}
}()
t := template.New("gotpl")
if e.Strict {
t.Option("missingkey=error")
@ -220,52 +224,78 @@ func (e *Engine) render(tpls map[string]renderable) (rendered map[string]string,
funcMap := e.alterFuncMap(t)
// We want to parse the templates in a predictable order. The order favors
// higher-level (in file system) templates over deeply nested templates.
keys := sortTemplates(tpls)
// Add the engine's currentTemplates to the template context
// so they can be referenced in the tpl function
for name, r := range e.CurrentTemplates {
if t.Lookup(name) == nil {
t = t.New(name).Funcs(funcMap)
if _, err := t.Parse(r.tpl); err != nil {
return nil, fmt.Errorf("parse error in %q: %s", name, err)
}
}
}
files := []string{}
return &preparedTemplate{tpl: t, funcMap: funcMap}, nil
}
for _, fname := range keys {
r := tpls[fname]
t = t.New(fname).Funcs(funcMap)
if _, err := t.Parse(r.tpl); err != nil {
return map[string]string{}, fmt.Errorf("parse error in %q: %s", fname, err)
func (e *Engine) renderSingle(t *template.Template, name string, vals chartutil.Values, buf *bytes.Buffer) (result string, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("rendering template failed: %v", r)
}
files = append(files, fname)
}()
buf.Reset()
if err := t.ExecuteTemplate(buf, name, vals); err != nil {
return "", fmt.Errorf("render error in %q: %s", name, err)
}
// Work around the issue where Go will emit "<no value>" even if Options(missing=zero)
// is set. Since missing=error will never get here, we do not need to handle
// the Strict case.
result = strings.Replace(buf.String(), "<no value>", "", -1)
return result, nil
}
// Adding the engine's currentTemplates to the template context
// so they can be referenced in the tpl function
for fname, r := range e.CurrentTemplates {
if t.Lookup(fname) == nil {
t = t.New(fname).Funcs(funcMap)
if _, err := t.Parse(r.tpl); err != nil {
return map[string]string{}, fmt.Errorf("parse error in %q: %s", fname, err)
}
func (e *Engine) renderFromSpec(spec *renderSpec) (rendered map[string]string, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("rendering template failed: %v", r)
}
}()
prep, err := e.renderPrepare()
if err != nil {
return nil, err
}
rendered = make(map[string]string, len(files))
var buf bytes.Buffer
for _, file := range files {
// Don't render partials. We don't care out the direct output of partials.
// They are only included from other templates.
if strings.HasPrefix(path.Base(file), "_") {
continue
if spec.order == nil {
spec.order = make([]string, 0, len(spec.tpls))
for key := range spec.tpls {
spec.order = append(spec.order, key)
}
// At render time, add information about the template that is being rendered.
vals := tpls[file].vals
vals["Template"] = map[string]interface{}{"Name": file, "BasePath": tpls[file].basePath}
if err := t.ExecuteTemplate(&buf, file, vals); err != nil {
return map[string]string{}, fmt.Errorf("render error in %q: %s", file, err)
sort.Strings(spec.order)
}
t := prep.tpl
for _, name := range spec.order {
t = t.New(name).Funcs(prep.funcMap)
if _, err := t.Parse(spec.tpls[name].tpl); err != nil {
return nil, fmt.Errorf("parse error in %q: %s", name, err)
}
}
// Work around the issue where Go will emit "<no value>" even if Options(missing=zero)
// is set. Since missing=error will never get here, we do not need to handle
// the Strict case.
rendered[file] = strings.Replace(buf.String(), "<no value>", "", -1)
buf.Reset()
rendered = make(map[string]string, len(spec.order))
var buf bytes.Buffer
for _, name := range spec.order {
if spec.filter == nil || spec.filter(name) {
if spec.addTplMeta {
// At render time, add information about the template that is being rendered.
spec.tpls[name].vals["Template"] = map[string]interface{}{"Name": name, "BasePath": spec.tpls[name].basePath}
}
if rendered[name], err = e.renderSingle(t, name, spec.tpls[name].vals, &buf); err != nil {
return nil, err
}
}
}
return rendered, nil

@ -0,0 +1,176 @@
/*
Copyright 2017 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package engine
import (
"bytes"
"fmt"
"strconv"
"strings"
"k8s.io/helm/pkg/chartutil"
)
type expansionState struct {
origTop chartutil.Values
origVals chartutil.Values
engine *Engine
prepTpl *preparedTemplate
locks map[string]bool
valCache map[string]interface{}
rendBufs []*bytes.Buffer
rendDepth int
}
// ExpandValues will expand all templates found in .Values. The usual .Values structure will be available
// via a new `tval` function instead; eg. {{ .Values.foo.bar }} can be accessed via {{ tval foo.bar }}.
// This permits recursive expansion; eg. a.b='{{a.c}}', a.c='{{a.d}}', a.d='d '=> a.b='d'. If .Values is
// used directly, no recursive expansion will occur. Also, all string values are expanded, so if literal
// {{ }} characters are required, they must be escaped; eg. {{ "{{ }}" }}. Values of any other type (eg.
// numeric) will remain unchanged.
func (engine *Engine) ExpandValues(top chartutil.Values) (chartutil.Values, error) {
vals, err := top.Table("Values")
if err != nil {
return top, nil
}
prepTpl, err := engine.renderPrepare()
if err != nil {
return nil, fmt.Errorf("Error during templated value expansion: %s", err.Error())
}
state := expansionState{
origTop: top, origVals: vals, engine: engine, prepTpl: prepTpl, locks: map[string]bool{},
valCache: map[string]interface{}{}, rendBufs: []*bytes.Buffer{}, rendDepth: 0,
}
prepTpl.funcMap["tval"] = state.tvalImpl
var expVals chartutil.Values
expVals, err = state.expandMapVal(vals, "")
if err != nil {
return nil, fmt.Errorf("Error during templated value expansion: %s", err.Error())
}
newTop := make(chartutil.Values, len(top))
for k, v := range top {
newTop[k] = v
}
newTop["Values"] = expVals
return newTop, nil
}
func (state *expansionState) tvalImpl(path string) (interface{}, error) {
// Lock state is only checked here. This is the only place in which we can jump to some
// other subtree of .Values, and if it's unlocked then it's fine to recurse into it. The
// only time we need to check for another lock is on any further jump (ie. tval call).
if _, locked := state.locks[path]; locked {
return "", fmt.Errorf("Cyclic reference to %q", path)
}
val, err := state.origVals.PathValue(path)
if err != nil {
if val, err = state.origVals.Table(path); err != nil { // PathValue() will not return maps
return "", fmt.Errorf("Value %q does not exist", path)
}
}
return state.expandVal(val, path)
}
func joinPath(parent string, child string) string {
if len(parent) == 0 {
return child
}
return parent + "." + child
}
func (state *expansionState) expandMapVal(vals map[string]interface{}, path string) (map[string]interface{}, error) {
state.locks[path] = true
defer delete(state.locks, path)
newVals := make(map[string]interface{}, len(vals))
for key, val := range vals {
newVal, err := state.expandVal(val, joinPath(path, key))
if err != nil {
return nil, err
}
newVals[key] = newVal
}
return newVals, nil
}
func (state *expansionState) expandArrayVal(vals []interface{}, path string) ([]interface{}, error) {
state.locks[path] = true
defer delete(state.locks, path)
newVals := make([]interface{}, len(vals))
for i, val := range vals {
newVal, err := state.expandVal(val, joinPath(path, strconv.Itoa(i)))
if err != nil {
return nil, err
}
newVals[i] = newVal
}
return newVals, nil
}
func (state *expansionState) expandVal(val interface{}, path string) (interface{}, error) {
state.locks[path] = true
defer delete(state.locks, path)
switch typedVal := val.(type) {
case chartutil.Values:
return state.expandMapVal(typedVal, path)
case map[string]interface{}:
return state.expandMapVal(typedVal, path)
case []interface{}:
return state.expandArrayVal(typedVal, path)
case string:
return state.renderVal(path, typedVal)
default:
return val, nil
}
}
func (state *expansionState) renderVal(path string, val string) (interface{}, error) {
if !strings.Contains(val, "{{") {
return val, nil
}
// We only cache string values that we have actually resolved. It's probably not worthwhile
// for anything else as either the value is trivial to retrieve (if there are no template
// expressions) or it is some structure (eg. a list) that will not usually be used directly
// (and is straightforward to reconstruct from cached values).
if precalc, found := state.valCache[path]; found {
return precalc, nil
}
tplName := joinPath("__expand", path)
tpl := state.prepTpl.tpl.New(tplName).Funcs(state.prepTpl.funcMap)
if _, err := tpl.Parse(val); err != nil {
return nil, fmt.Errorf("Parse error in value %q: %s", path, err)
}
if state.rendDepth == len(state.rendBufs) {
state.rendBufs = append(state.rendBufs, new(bytes.Buffer))
}
state.rendDepth++
rendered, err := state.engine.renderSingle(tpl, tplName, state.origTop, state.rendBufs[state.rendDepth-1])
state.rendDepth--
if err != nil {
return nil, fmt.Errorf("Error expanding value %q: %s", path, err.Error())
}
state.valCache[path] = rendered
return rendered, nil
}

@ -0,0 +1,189 @@
/*
Copyright 2017 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package engine
import (
"fmt"
"regexp"
"testing"
"k8s.io/helm/pkg/chartutil"
)
func expandValuesHelper(input string) (chartutil.Values, error) {
var vals chartutil.Values
vals = chartutil.FromYaml(input)
if err, found := vals["Error"]; found {
return nil, fmt.Errorf("Unexpected YAML parse failure: %s", err.(string))
}
expanded, err := New().ExpandValues(chartutil.Values{"Values": vals})
if err != nil {
return nil, fmt.Errorf("Expansion failed: %s", err.Error())
}
return expanded, nil
}
func checkFullExpansionHelper(input string, expected string) error {
expanded, err := expandValuesHelper(input)
if err != nil {
return err
}
output := chartutil.ToYaml(expanded["Values"])
if "\n"+output != expected {
return fmt.Errorf("Unexpected result from ExpandValues().\nGot: %s\nExpected: %s", output, expected)
}
return nil
}
func checkSingleValue(input string, path string, expected string) error {
expanded, err := expandValuesHelper(input)
if err != nil {
return err
}
val, err := expanded.PathValue("Values." + path)
if err != nil {
return err
}
if val != expected {
return fmt.Errorf("Unexpected expansion result for %q.\nGot: %s\nExpected: %s", path, val, expected)
}
return nil
}
func TestTemplateValueExpansion(t *testing.T) {
input := `
a:
aa: '{{ tval "b.ba.baa" }}2' # 12
ab: '{{ tval "a.aa" }}3' # 123
b:
ba:
baa: 1
bb: '0{{ tval "a.ab" -}} 4 {{- tval "b.bc" }}' # 012345
bc: 5
bd: '{{ index (tval "d") 1 }}' # L1
c:
ca: '{{ substr 2 5 (tval "b.bb") }}' # 234
cb: '{{ .Values.b.ba.baa }}' # Can still access things this way (and get 1 here)
cc: '{{ .Values.a.aa }}' # ... but there will be no recursion, so this will just be '{{ tval.b.ba }}2'
d:
- L0
- 'L{{ tval "b.ba.baa" }}' # L1
e:
bool: true # Will just be left alone
float: 1.234 # "
`
expected := `
a:
aa: "12"
ab: "123"
b:
ba:
baa: 1
bb: "012345"
bc: 5
bd: L1
c:
ca: "234"
cb: "1"
cc: '{{ tval "b.ba.baa" }}2'
d:
- L0
- L1
e:
bool: true
float: 1.234
`
if err := checkFullExpansionHelper(input, expected); err != nil {
t.Error(err)
}
input = `
a:
- aa:
aaa: '{{ (tval "b").ba.baa }}'
aab: '{{ tval "b.ba.bac" }}'
b:
ba:
baa: 1
bab:
- unused
- left
- right
bac: 2
c:
ca: >-
{{
print
(index (tval "b.ba").bab (atoi (index (tval "a") 0).aa.aaa))
(index (tval "d") (atoi (index (tval "a") 0).aa.aaa)).db.dba.dbaa
}}
d:
- da:
- unused
- db:
dba:
dbaa: '{{ index (tval "b.ba.bab") (atoi (index (tval "a") 0).aa.aab) }}'
`
if err := checkSingleValue(input, "c.ca", "leftright"); err != nil {
t.Error(err)
}
input = `
a:
aa: cc
b:
bb: '{{ (index (tval "c") (tval "a.aa")).x.xx }}'
c:
cc:
x:
xx: '{{ tval "c.cd.x.xx" }}'
cd:
x:
xx: good
`
if err := checkSingleValue(input, "b.bb", "good"); err != nil {
t.Error(err)
}
}
func TestTemplateValueExpansionErrors(t *testing.T) {
checkExpError := func(input string, errRegex string) {
if _, err := expandValuesHelper(input); err == nil {
t.Errorf("Expected error matching %q but expansion succeeded", errRegex)
} else if !regexp.MustCompile("(?i)" + errRegex).MatchString(err.Error()) {
t.Errorf("Expected error matching %q but got %q", errRegex, err.Error())
}
}
inputYaml := "a: { b: '{{ tval \"a.b\" }}' }"
checkExpError(inputYaml, `cyclic reference to "a.b"`)
inputYaml = "a: { b: '{{ tval \"c.d\" }}' }\nc: { d: '{{ tval \"a.b\" }}' }"
checkExpError(inputYaml, `cyclic reference`)
inputYaml = "a: { b: '{{ tval \"c.d\" }}' }\nc: { d: '{{ tval \"e.f\" }}' }\ne: { f: '{{ tval \"g.h\" }}' }\ng: { h: '{{ tval \"a.b\" }}' }"
checkExpError(inputYaml, `cyclic reference`)
inputYaml = "a: { b: '{{ tval \"c.d\" }}' }"
checkExpError(inputYaml, `value "c.d" does not exist`)
inputYaml = "a: { b: '{{ tval \"c.d\" }}' }\nc: { d: '{{ invalid }}' }"
checkExpError(inputYaml, `function "invalid" not defined`) // This one happens during parse, so we don't catch it so specifically
checkExpError(inputYaml, `parse error in value "c.d"`) // The (decorated) template will still be reported though
inputYaml = "a: { b: '{{ tval \"c.d\" }}' }\nc: { d: '{{ substr \"bad arg\" 0 0 }}' }"
checkExpError(inputYaml, `error expanding value "c.d"`)
}

@ -109,6 +109,8 @@ type Metadata struct {
Annotations map[string]string `protobuf:"bytes,16,rep,name=annotations" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
// KubeVersion is a SemVer constraint specifying the version of Kubernetes required.
KubeVersion string `protobuf:"bytes,17,opt,name=kubeVersion" json:"kubeVersion,omitempty"`
// Whether or not any templates found in values should be recursively expanded
ExpandValues bool `protobuf:"varint,18,opt,name=expandValues" json:"expandValues,omitempty"`
}
func (m *Metadata) Reset() { *m = Metadata{} }
@ -235,6 +237,13 @@ func (m *Metadata) GetKubeVersion() string {
return ""
}
func (m *Metadata) GetExpandValues() bool {
if m != nil {
return m.ExpandValues
}
return false
}
func init() {
proto.RegisterType((*Maintainer)(nil), "hapi.chart.Maintainer")
proto.RegisterType((*Metadata)(nil), "hapi.chart.Metadata")
@ -244,33 +253,34 @@ func init() {
func init() { proto.RegisterFile("hapi/chart/metadata.proto", fileDescriptor2) }
var fileDescriptor2 = []byte{
// 435 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x52, 0x5d, 0x6b, 0xd4, 0x40,
0x14, 0x35, 0xcd, 0x66, 0x77, 0x73, 0x63, 0x35, 0x0e, 0x52, 0xc6, 0x22, 0x12, 0x16, 0x85, 0x7d,
0xda, 0x82, 0xbe, 0x14, 0x1f, 0x04, 0x85, 0x52, 0x41, 0xbb, 0x95, 0xe0, 0x07, 0xf8, 0x36, 0x4d,
0x2e, 0xdd, 0x61, 0x93, 0x99, 0x30, 0x99, 0xad, 0xec, 0xaf, 0xf0, 0x2f, 0xcb, 0xdc, 0x64, 0x9a,
0xac, 0xf4, 0xed, 0x9e, 0x73, 0x66, 0xce, 0xcc, 0xbd, 0xf7, 0xc0, 0x8b, 0x8d, 0x68, 0xe4, 0x59,
0xb1, 0x11, 0xc6, 0x9e, 0xd5, 0x68, 0x45, 0x29, 0xac, 0x58, 0x35, 0x46, 0x5b, 0xcd, 0xc0, 0x49,
0x2b, 0x92, 0x16, 0x9f, 0x01, 0xae, 0x84, 0x54, 0x56, 0x48, 0x85, 0x86, 0x31, 0x98, 0x28, 0x51,
0x23, 0x0f, 0xb2, 0x60, 0x19, 0xe7, 0x54, 0xb3, 0xe7, 0x10, 0x61, 0x2d, 0x64, 0xc5, 0x8f, 0x88,
0xec, 0x00, 0x4b, 0x21, 0xdc, 0x99, 0x8a, 0x87, 0xc4, 0xb9, 0x72, 0xf1, 0x37, 0x82, 0xf9, 0x55,
0xff, 0xd0, 0x83, 0x46, 0x0c, 0x26, 0x1b, 0x5d, 0x63, 0xef, 0x43, 0x35, 0xe3, 0x30, 0x6b, 0xf5,
0xce, 0x14, 0xd8, 0xf2, 0x30, 0x0b, 0x97, 0x71, 0xee, 0xa1, 0x53, 0xee, 0xd0, 0xb4, 0x52, 0x2b,
0x3e, 0xa1, 0x0b, 0x1e, 0xb2, 0x0c, 0x92, 0x12, 0xdb, 0xc2, 0xc8, 0xc6, 0x3a, 0x35, 0x22, 0x75,
0x4c, 0xb1, 0x53, 0x98, 0x6f, 0x71, 0xff, 0x47, 0x9b, 0xb2, 0xe5, 0x53, 0xb2, 0xbd, 0xc7, 0xec,
0x1c, 0x92, 0xfa, 0xbe, 0xe1, 0x96, 0xcf, 0xb2, 0x70, 0x99, 0xbc, 0x3d, 0x59, 0x0d, 0x23, 0x59,
0x0d, 0xf3, 0xc8, 0xc7, 0x47, 0xd9, 0x09, 0x4c, 0x51, 0xdd, 0x4a, 0x85, 0x7c, 0x4e, 0x4f, 0xf6,
0xc8, 0xf5, 0x25, 0x0b, 0xad, 0x78, 0xdc, 0xf5, 0xe5, 0x6a, 0xf6, 0x0a, 0x40, 0x34, 0xf2, 0x67,
0xdf, 0x00, 0x90, 0x32, 0x62, 0xd8, 0x4b, 0x88, 0x0b, 0xad, 0x4a, 0x49, 0x1d, 0x24, 0x24, 0x0f,
0x84, 0x73, 0xb4, 0xe2, 0xb6, 0xe5, 0x8f, 0x3b, 0x47, 0x57, 0x77, 0x8e, 0x8d, 0x77, 0x3c, 0xf6,
0x8e, 0x9e, 0x71, 0x7a, 0x89, 0x8d, 0xc1, 0x42, 0x58, 0x2c, 0xf9, 0x93, 0x2c, 0x58, 0xce, 0xf3,
0x11, 0xc3, 0x5e, 0xc3, 0xb1, 0x95, 0x55, 0x85, 0xc6, 0x5b, 0x3c, 0x25, 0x8b, 0x43, 0x92, 0x5d,
0x42, 0x22, 0x94, 0xd2, 0x56, 0xb8, 0x7f, 0xb4, 0x3c, 0xa5, 0xe9, 0xbc, 0x39, 0x98, 0x8e, 0xcf,
0xd2, 0xc7, 0xe1, 0xdc, 0x85, 0xb2, 0x66, 0x9f, 0x8f, 0x6f, 0xba, 0x25, 0x6d, 0x77, 0x37, 0xe8,
0x1f, 0x7b, 0xd6, 0x2d, 0x69, 0x44, 0x9d, 0x7e, 0x80, 0xf4, 0x7f, 0x0b, 0x97, 0xaa, 0x2d, 0xee,
0xfb, 0xd4, 0xb8, 0xd2, 0xa5, 0xef, 0x4e, 0x54, 0x3b, 0x9f, 0x9a, 0x0e, 0xbc, 0x3f, 0x3a, 0x0f,
0x16, 0x19, 0x4c, 0x2f, 0xba, 0x05, 0x24, 0x30, 0xfb, 0xb1, 0xfe, 0xb2, 0xbe, 0xfe, 0xb5, 0x4e,
0x1f, 0xb1, 0x18, 0xa2, 0xcb, 0xeb, 0xef, 0xdf, 0xbe, 0xa6, 0xc1, 0xa7, 0xd9, 0xef, 0x88, 0xfe,
0x7c, 0x33, 0xa5, 0xdc, 0xbf, 0xfb, 0x17, 0x00, 0x00, 0xff, 0xff, 0x36, 0xf9, 0x0d, 0xa6, 0x14,
0x03, 0x00, 0x00,
// 452 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x53, 0x51, 0x6b, 0xd4, 0x40,
0x10, 0x36, 0xcd, 0xe5, 0xee, 0x32, 0x69, 0x35, 0x0e, 0x52, 0xd6, 0x22, 0x12, 0x0e, 0x85, 0x7b,
0xba, 0x82, 0xbe, 0x14, 0x1f, 0x04, 0x85, 0x52, 0x41, 0x7b, 0x95, 0xa0, 0x15, 0x7c, 0xdb, 0x26,
0x43, 0x6f, 0xb9, 0x64, 0x13, 0x36, 0x7b, 0xd5, 0xfb, 0x5d, 0xfe, 0x41, 0xd9, 0x49, 0x72, 0x97,
0x13, 0xdf, 0xe6, 0xfb, 0xbe, 0xcd, 0x37, 0xfb, 0xcd, 0x4e, 0xe0, 0xf9, 0x4a, 0xd6, 0xea, 0x3c,
0x5b, 0x49, 0x63, 0xcf, 0x4b, 0xb2, 0x32, 0x97, 0x56, 0x2e, 0x6a, 0x53, 0xd9, 0x0a, 0xc1, 0x49,
0x0b, 0x96, 0x66, 0x9f, 0x00, 0xae, 0xa5, 0xd2, 0x56, 0x2a, 0x4d, 0x06, 0x11, 0x46, 0x5a, 0x96,
0x24, 0xbc, 0xc4, 0x9b, 0x87, 0x29, 0xd7, 0xf8, 0x0c, 0x02, 0x2a, 0xa5, 0x2a, 0xc4, 0x11, 0x93,
0x2d, 0xc0, 0x18, 0xfc, 0x8d, 0x29, 0x84, 0xcf, 0x9c, 0x2b, 0x67, 0x7f, 0x02, 0x98, 0x5e, 0x77,
0x8d, 0xfe, 0x6b, 0x84, 0x30, 0x5a, 0x55, 0x25, 0x75, 0x3e, 0x5c, 0xa3, 0x80, 0x49, 0x53, 0x6d,
0x4c, 0x46, 0x8d, 0xf0, 0x13, 0x7f, 0x1e, 0xa6, 0x3d, 0x74, 0xca, 0x03, 0x99, 0x46, 0x55, 0x5a,
0x8c, 0xf8, 0x83, 0x1e, 0x62, 0x02, 0x51, 0x4e, 0x4d, 0x66, 0x54, 0x6d, 0x9d, 0x1a, 0xb0, 0x3a,
0xa4, 0xf0, 0x0c, 0xa6, 0x6b, 0xda, 0xfe, 0xaa, 0x4c, 0xde, 0x88, 0x31, 0xdb, 0xee, 0x30, 0x5e,
0x40, 0x54, 0xee, 0x02, 0x37, 0x62, 0x92, 0xf8, 0xf3, 0xe8, 0xcd, 0xe9, 0x62, 0x3f, 0x92, 0xc5,
0x7e, 0x1e, 0xe9, 0xf0, 0x28, 0x9e, 0xc2, 0x98, 0xf4, 0xbd, 0xd2, 0x24, 0xa6, 0xdc, 0xb2, 0x43,
0x2e, 0x97, 0xca, 0x2a, 0x2d, 0xc2, 0x36, 0x97, 0xab, 0xf1, 0x25, 0x80, 0xac, 0xd5, 0x6d, 0x17,
0x00, 0x58, 0x19, 0x30, 0xf8, 0x02, 0xc2, 0xac, 0xd2, 0xb9, 0xe2, 0x04, 0x11, 0xcb, 0x7b, 0xc2,
0x39, 0x5a, 0x79, 0xdf, 0x88, 0xe3, 0xd6, 0xd1, 0xd5, 0xad, 0x63, 0xdd, 0x3b, 0x9e, 0xf4, 0x8e,
0x3d, 0xe3, 0xf4, 0x9c, 0x6a, 0x43, 0x99, 0xb4, 0x94, 0x8b, 0xc7, 0x89, 0x37, 0x9f, 0xa6, 0x03,
0x06, 0x5f, 0xc1, 0x89, 0x55, 0x45, 0x41, 0xa6, 0xb7, 0x78, 0xc2, 0x16, 0x87, 0x24, 0x5e, 0x41,
0x24, 0xb5, 0xae, 0xac, 0x74, 0xf7, 0x68, 0x44, 0xcc, 0xd3, 0x79, 0x7d, 0x30, 0x9d, 0x7e, 0x97,
0x3e, 0xec, 0xcf, 0x5d, 0x6a, 0x6b, 0xb6, 0xe9, 0xf0, 0x4b, 0xf7, 0x48, 0xeb, 0xcd, 0x1d, 0xf5,
0xcd, 0x9e, 0xb6, 0x8f, 0x34, 0xa0, 0x70, 0x06, 0xc7, 0xf4, 0xbb, 0x96, 0x3a, 0xbf, 0x95, 0xc5,
0x86, 0x1a, 0x81, 0x7c, 0xe5, 0x03, 0xee, 0xec, 0x3d, 0xc4, 0xff, 0xb6, 0x71, 0x9b, 0xb7, 0xa6,
0x6d, 0xb7, 0x59, 0xae, 0x74, 0x1b, 0xfa, 0xe0, 0xce, 0xf7, 0x1b, 0xca, 0xe0, 0xdd, 0xd1, 0x85,
0x37, 0x4b, 0x60, 0x7c, 0xd9, 0x3e, 0x52, 0x04, 0x93, 0xef, 0xcb, 0xcf, 0xcb, 0x9b, 0x1f, 0xcb,
0xf8, 0x11, 0x86, 0x10, 0x5c, 0xdd, 0x7c, 0xfb, 0xfa, 0x25, 0xf6, 0x3e, 0x4e, 0x7e, 0x06, 0x9c,
0xeb, 0x6e, 0xcc, 0xff, 0xc6, 0xdb, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xfa, 0x67, 0xed, 0x9b,
0x38, 0x03, 0x00, 0x00,
}

@ -462,7 +462,8 @@ func (m *UpdateReleaseRequest) GetForce() bool {
// UpdateReleaseResponse is the response to an update request.
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"`
FinalValues *hapi_chart.Config `protobuf:"bytes,2,opt,name=final_values,json=finalValues" json:"final_values,omitempty"`
}
func (m *UpdateReleaseResponse) Reset() { *m = UpdateReleaseResponse{} }
@ -477,6 +478,13 @@ func (m *UpdateReleaseResponse) GetRelease() *hapi_release5.Release {
return nil
}
func (m *UpdateReleaseResponse) GetFinalValues() *hapi_chart.Config {
if m != nil {
return m.FinalValues
}
return nil
}
type RollbackReleaseRequest struct {
// The name of the release
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
@ -672,7 +680,8 @@ func (m *InstallReleaseRequest) GetWait() bool {
// InstallReleaseResponse is the response from a release installation.
type InstallReleaseResponse 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"`
FinalValues *hapi_chart.Config `protobuf:"bytes,2,opt,name=final_values,json=finalValues" json:"final_values,omitempty"`
}
func (m *InstallReleaseResponse) Reset() { *m = InstallReleaseResponse{} }
@ -687,6 +696,13 @@ func (m *InstallReleaseResponse) GetRelease() *hapi_release5.Release {
return nil
}
func (m *InstallReleaseResponse) GetFinalValues() *hapi_chart.Config {
if m != nil {
return m.FinalValues
}
return nil
}
// UninstallReleaseRequest represents a request to uninstall a named release.
type UninstallReleaseRequest struct {
// Name is the name of the release to delete.
@ -1368,82 +1384,83 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("hapi/services/tiller.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 1217 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xdd, 0x6e, 0xe3, 0xc4,
0x17, 0xaf, 0xf3, 0x9d, 0x93, 0x36, 0xff, 0x74, 0x9a, 0xb6, 0xae, 0xff, 0x0b, 0x2a, 0x46, 0xb0,
0xd9, 0x85, 0x4d, 0x21, 0x70, 0x83, 0x84, 0x90, 0xba, 0xdd, 0xa8, 0x2d, 0x94, 0xae, 0xe4, 0x6c,
0x17, 0x09, 0x01, 0x91, 0x9b, 0x4c, 0x5a, 0xb3, 0x8e, 0x27, 0x78, 0xc6, 0x65, 0x7b, 0xcb, 0x1d,
0x8f, 0xc2, 0x5b, 0xf0, 0x1e, 0x5c, 0xc2, 0x83, 0x20, 0xcf, 0x87, 0xeb, 0x49, 0xed, 0xd6, 0xf4,
0x26, 0x9e, 0x99, 0xf3, 0xfd, 0x3b, 0x67, 0xce, 0x9c, 0x80, 0x75, 0xe9, 0x2e, 0xbc, 0x3d, 0x8a,
0xc3, 0x2b, 0x6f, 0x82, 0xe9, 0x1e, 0xf3, 0x7c, 0x1f, 0x87, 0xfd, 0x45, 0x48, 0x18, 0x41, 0xdd,
0x98, 0xd6, 0x57, 0xb4, 0xbe, 0xa0, 0x59, 0x5b, 0x5c, 0x62, 0x72, 0xe9, 0x86, 0x4c, 0xfc, 0x0a,
0x6e, 0x6b, 0x3b, 0x7d, 0x4e, 0x82, 0x99, 0x77, 0x21, 0x09, 0xc2, 0x44, 0x88, 0x7d, 0xec, 0x52,
0xac, 0xbe, 0x9a, 0x90, 0xa2, 0x79, 0xc1, 0x8c, 0x48, 0xc2, 0xff, 0x35, 0x02, 0xc3, 0x94, 0x8d,
0xc3, 0x28, 0x90, 0xc4, 0x1d, 0x8d, 0x48, 0x99, 0xcb, 0x22, 0xaa, 0x19, 0xbb, 0xc2, 0x21, 0xf5,
0x48, 0xa0, 0xbe, 0x82, 0x66, 0xff, 0x59, 0x82, 0x8d, 0x13, 0x8f, 0x32, 0x47, 0x08, 0x52, 0x07,
0xff, 0x12, 0x61, 0xca, 0x50, 0x17, 0xaa, 0xbe, 0x37, 0xf7, 0x98, 0x69, 0xec, 0x1a, 0xbd, 0xb2,
0x23, 0x36, 0x68, 0x0b, 0x6a, 0x64, 0x36, 0xa3, 0x98, 0x99, 0xa5, 0x5d, 0xa3, 0xd7, 0x74, 0xe4,
0x0e, 0x7d, 0x05, 0x75, 0x4a, 0x42, 0x36, 0x3e, 0xbf, 0x36, 0xcb, 0xbb, 0x46, 0xaf, 0x3d, 0xf8,
0xa0, 0x9f, 0x85, 0x53, 0x3f, 0xb6, 0x34, 0x22, 0x21, 0xeb, 0xc7, 0x3f, 0xcf, 0xaf, 0x9d, 0x1a,
0xe5, 0xdf, 0x58, 0xef, 0xcc, 0xf3, 0x19, 0x0e, 0xcd, 0x8a, 0xd0, 0x2b, 0x76, 0xe8, 0x10, 0x80,
0xeb, 0x25, 0xe1, 0x14, 0x87, 0x66, 0x95, 0xab, 0xee, 0x15, 0x50, 0xfd, 0x32, 0xe6, 0x77, 0x9a,
0x54, 0x2d, 0xd1, 0x97, 0xb0, 0x2a, 0x20, 0x19, 0x4f, 0xc8, 0x14, 0x53, 0xb3, 0xb6, 0x5b, 0xee,
0xb5, 0x07, 0x3b, 0x42, 0x95, 0x82, 0x7f, 0x24, 0x40, 0x3b, 0x20, 0x53, 0xec, 0xb4, 0x04, 0x7b,
0xbc, 0xa6, 0xe8, 0x11, 0x34, 0x03, 0x77, 0x8e, 0xe9, 0xc2, 0x9d, 0x60, 0xb3, 0xce, 0x3d, 0xbc,
0x39, 0xb0, 0x7f, 0x82, 0x86, 0x32, 0x6e, 0x0f, 0xa0, 0x26, 0x42, 0x43, 0x2d, 0xa8, 0x9f, 0x9d,
0x7e, 0x73, 0xfa, 0xf2, 0xbb, 0xd3, 0xce, 0x0a, 0x6a, 0x40, 0xe5, 0x74, 0xff, 0xdb, 0x61, 0xc7,
0x40, 0xeb, 0xb0, 0x76, 0xb2, 0x3f, 0x7a, 0x35, 0x76, 0x86, 0x27, 0xc3, 0xfd, 0xd1, 0xf0, 0x45,
0xa7, 0x64, 0xbf, 0x0b, 0xcd, 0xc4, 0x67, 0x54, 0x87, 0xf2, 0xfe, 0xe8, 0x40, 0x88, 0xbc, 0x18,
0x8e, 0x0e, 0x3a, 0x86, 0xfd, 0xbb, 0x01, 0x5d, 0x3d, 0x45, 0x74, 0x41, 0x02, 0x8a, 0xe3, 0x1c,
0x4d, 0x48, 0x14, 0x24, 0x39, 0xe2, 0x1b, 0x84, 0xa0, 0x12, 0xe0, 0xb7, 0x2a, 0x43, 0x7c, 0x1d,
0x73, 0x32, 0xc2, 0x5c, 0x9f, 0x67, 0xa7, 0xec, 0x88, 0x0d, 0xfa, 0x14, 0x1a, 0x32, 0x74, 0x6a,
0x56, 0x76, 0xcb, 0xbd, 0xd6, 0x60, 0x53, 0x07, 0x44, 0x5a, 0x74, 0x12, 0x36, 0xfb, 0x10, 0xb6,
0x0f, 0xb1, 0xf2, 0x44, 0xe0, 0xa5, 0x2a, 0x26, 0xb6, 0xeb, 0xce, 0x31, 0x77, 0x26, 0xb6, 0xeb,
0xce, 0x31, 0x32, 0xa1, 0x2e, 0xcb, 0x8d, 0xbb, 0x53, 0x75, 0xd4, 0xd6, 0x66, 0x60, 0xde, 0x56,
0x24, 0xe3, 0xca, 0xd2, 0xf4, 0x21, 0x54, 0xe2, 0x9b, 0xc0, 0xd5, 0xb4, 0x06, 0x48, 0xf7, 0xf3,
0x38, 0x98, 0x11, 0x87, 0xd3, 0xf5, 0x54, 0x95, 0x97, 0x53, 0x75, 0x94, 0xb6, 0x7a, 0x40, 0x02,
0x86, 0x03, 0xf6, 0x30, 0xff, 0x4f, 0x60, 0x27, 0x43, 0x93, 0x0c, 0x60, 0x0f, 0xea, 0xd2, 0x35,
0xae, 0x2d, 0x17, 0x57, 0xc5, 0x65, 0xff, 0x5d, 0x82, 0xee, 0xd9, 0x62, 0xea, 0x32, 0xac, 0x48,
0x77, 0x38, 0xf5, 0x18, 0xaa, 0xbc, 0xa3, 0x48, 0x2c, 0xd6, 0x85, 0x6e, 0xd1, 0x76, 0x0e, 0xe2,
0x5f, 0x47, 0xd0, 0xd1, 0x53, 0xa8, 0x5d, 0xb9, 0x7e, 0x84, 0x29, 0x07, 0x22, 0x41, 0x4d, 0x72,
0xf2, 0x76, 0xe4, 0x48, 0x0e, 0xb4, 0x0d, 0xf5, 0x69, 0x78, 0x1d, 0xf7, 0x13, 0x7e, 0x05, 0x1b,
0x4e, 0x6d, 0x1a, 0x5e, 0x3b, 0x51, 0x80, 0xde, 0x87, 0xb5, 0xa9, 0x47, 0xdd, 0x73, 0x1f, 0x8f,
0x2f, 0x09, 0x79, 0x43, 0xf9, 0x2d, 0x6c, 0x38, 0xab, 0xf2, 0xf0, 0x28, 0x3e, 0x43, 0x56, 0x5c,
0x49, 0x93, 0x10, 0xbb, 0x0c, 0x9b, 0x35, 0x4e, 0x4f, 0xf6, 0x31, 0x86, 0xcc, 0x9b, 0x63, 0x12,
0x31, 0x7e, 0x75, 0xca, 0x8e, 0xda, 0xa2, 0xf7, 0x60, 0x35, 0xc4, 0x14, 0xb3, 0xb1, 0xf4, 0xb2,
0xc1, 0x25, 0x5b, 0xfc, 0xec, 0xb5, 0x70, 0x0b, 0x41, 0xe5, 0x57, 0xd7, 0x63, 0x66, 0x93, 0x93,
0xf8, 0x5a, 0x88, 0x45, 0x14, 0x2b, 0x31, 0x50, 0x62, 0x11, 0xc5, 0x52, 0xac, 0x0b, 0xd5, 0x19,
0x09, 0x27, 0xd8, 0x6c, 0x71, 0x9a, 0xd8, 0xd8, 0x47, 0xb0, 0xb9, 0x04, 0xf2, 0x43, 0xf3, 0xf5,
0x8f, 0x01, 0x5b, 0x0e, 0xf1, 0xfd, 0x73, 0x77, 0xf2, 0xa6, 0x40, 0xc6, 0x52, 0xe0, 0x96, 0xee,
0x06, 0xb7, 0x9c, 0x01, 0x6e, 0xaa, 0x08, 0x2b, 0x5a, 0x11, 0x6a, 0xb0, 0x57, 0xf3, 0x61, 0xaf,
0xe9, 0xb0, 0x2b, 0x4c, 0xeb, 0x29, 0x4c, 0x13, 0xc0, 0x1a, 0x69, 0xc0, 0xbe, 0x86, 0xed, 0x5b,
0x51, 0x3e, 0x14, 0xb2, 0x3f, 0x4a, 0xb0, 0x79, 0x1c, 0x50, 0xe6, 0xfa, 0xfe, 0x12, 0x62, 0x49,
0x3d, 0x1b, 0x85, 0xeb, 0xb9, 0xf4, 0x5f, 0xea, 0xb9, 0xac, 0x41, 0xae, 0xf2, 0x53, 0x49, 0xe5,
0xa7, 0x50, 0x8d, 0x6b, 0x9d, 0xa5, 0xb6, 0xd4, 0x59, 0xd0, 0x3b, 0x00, 0xa2, 0x28, 0xb9, 0x72,
0x01, 0x6d, 0x93, 0x9f, 0x9c, 0xca, 0x46, 0xa2, 0xb2, 0xd1, 0xc8, 0xce, 0x46, 0xaa, 0xc2, 0xed,
0x63, 0xd8, 0x5a, 0x86, 0xea, 0xa1, 0xb0, 0xff, 0x66, 0xc0, 0xf6, 0x59, 0xe0, 0x65, 0x02, 0x9f,
0x55, 0xaa, 0xb7, 0xa0, 0x28, 0x65, 0x40, 0xd1, 0x85, 0xea, 0x22, 0x0a, 0x2f, 0xb0, 0x84, 0x56,
0x6c, 0xd2, 0x31, 0x56, 0xb4, 0x18, 0xed, 0x31, 0x98, 0xb7, 0x7d, 0x78, 0x60, 0x44, 0xb1, 0xd7,
0xc9, 0x4b, 0xd0, 0x14, 0x5d, 0xdf, 0xde, 0x80, 0xf5, 0x43, 0xcc, 0x5e, 0x8b, 0x6b, 0x21, 0xc3,
0xb3, 0x87, 0x80, 0xd2, 0x87, 0x37, 0xf6, 0xe4, 0x91, 0x6e, 0x4f, 0x8d, 0x45, 0x8a, 0x5f, 0x71,
0xd9, 0x5f, 0x70, 0xdd, 0x47, 0x1e, 0x65, 0x24, 0xbc, 0xbe, 0x0b, 0xba, 0x0e, 0x94, 0xe7, 0xee,
0x5b, 0xf9, 0x50, 0xc4, 0x4b, 0xfb, 0x90, 0x7b, 0x90, 0x88, 0x4a, 0x0f, 0xd2, 0xcf, 0xae, 0x51,
0xec, 0xd9, 0xfd, 0x01, 0xd0, 0x2b, 0x9c, 0x4c, 0x00, 0xf7, 0xbc, 0x58, 0x2a, 0x09, 0x25, 0xbd,
0xd0, 0x4c, 0xa8, 0x4f, 0x7c, 0xec, 0x06, 0xd1, 0x42, 0xa6, 0x4d, 0x6d, 0xed, 0x1f, 0x61, 0x43,
0xd3, 0x2e, 0xfd, 0x8c, 0xe3, 0xa1, 0x17, 0x52, 0x7b, 0xbc, 0x44, 0x9f, 0x43, 0x4d, 0x8c, 0x45,
0x5c, 0x77, 0x7b, 0xf0, 0x48, 0xf7, 0x9b, 0x2b, 0x89, 0x02, 0x39, 0x47, 0x39, 0x92, 0x77, 0xf0,
0x57, 0x03, 0xda, 0xea, 0xa1, 0x17, 0x43, 0x1b, 0xf2, 0x60, 0x35, 0x3d, 0xd1, 0xa0, 0x27, 0xf9,
0x33, 0xdd, 0xd2, 0x60, 0x6a, 0x3d, 0x2d, 0xc2, 0x2a, 0x22, 0xb0, 0x57, 0x3e, 0x31, 0x10, 0x85,
0xce, 0xf2, 0xa0, 0x81, 0x9e, 0x65, 0xeb, 0xc8, 0x99, 0x6c, 0xac, 0x7e, 0x51, 0x76, 0x65, 0x16,
0x5d, 0xf1, 0x9a, 0xd1, 0xa7, 0x03, 0x74, 0xaf, 0x1a, 0x7d, 0x20, 0xb1, 0xf6, 0x0a, 0xf3, 0x27,
0x76, 0x7f, 0x86, 0x35, 0xed, 0x85, 0x43, 0x39, 0x68, 0x65, 0xcd, 0x1a, 0xd6, 0x47, 0x85, 0x78,
0x13, 0x5b, 0x73, 0x68, 0xeb, 0x4d, 0x0a, 0xe5, 0x28, 0xc8, 0xec, 0xfa, 0xd6, 0xc7, 0xc5, 0x98,
0x13, 0x73, 0x14, 0x3a, 0xcb, 0x3d, 0x24, 0x2f, 0x8f, 0x39, 0xfd, 0x2e, 0x2f, 0x8f, 0x79, 0xad,
0xc9, 0x5e, 0x41, 0x2e, 0xc0, 0x4d, 0x0b, 0x41, 0x8f, 0x73, 0x13, 0xa2, 0x77, 0x1e, 0xab, 0x77,
0x3f, 0x63, 0x62, 0x62, 0x01, 0xff, 0x5b, 0x7a, 0x63, 0x51, 0x0e, 0x34, 0xd9, 0x03, 0x87, 0xf5,
0xac, 0x20, 0xf7, 0x52, 0x50, 0xb2, 0x2b, 0xdd, 0x11, 0x94, 0xde, 0xf2, 0xee, 0x08, 0x6a, 0xa9,
0xc1, 0xd9, 0x2b, 0xc8, 0x83, 0xb6, 0x13, 0x05, 0xd2, 0x74, 0xdc, 0x16, 0x50, 0x8e, 0xf4, 0xed,
0xae, 0x66, 0x3d, 0x29, 0xc0, 0x79, 0x73, 0xbf, 0x9f, 0xc3, 0xf7, 0x0d, 0xc5, 0x7a, 0x5e, 0xe3,
0xff, 0x69, 0x3f, 0xfb, 0x37, 0x00, 0x00, 0xff, 0xff, 0xf3, 0x7c, 0x9c, 0x49, 0xc1, 0x0f, 0x00,
0x00,
// 1243 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xdd, 0x6e, 0xe3, 0xc4,
0x17, 0xaf, 0xf3, 0x9d, 0x93, 0x6e, 0xfe, 0xe9, 0x34, 0x6d, 0x5d, 0xff, 0x17, 0x54, 0x8c, 0x60,
0xb3, 0x0b, 0x9b, 0x42, 0x80, 0x0b, 0x24, 0x84, 0xd4, 0xed, 0x46, 0xed, 0x42, 0xe9, 0x4a, 0xce,
0x76, 0x91, 0x10, 0x10, 0xb9, 0xc9, 0xa4, 0x35, 0xeb, 0x78, 0x82, 0x67, 0x5c, 0xb6, 0x57, 0x20,
0xee, 0x78, 0x14, 0xde, 0x82, 0xf7, 0xe0, 0x12, 0x1e, 0x04, 0x79, 0x3e, 0x5c, 0x4f, 0x6a, 0xb7,
0xa6, 0x17, 0xdc, 0xc4, 0x33, 0x73, 0xce, 0x9c, 0x73, 0xe6, 0xf7, 0x9b, 0x73, 0xe6, 0x04, 0xac,
0x73, 0x77, 0xe1, 0xed, 0x52, 0x1c, 0x5e, 0x78, 0x13, 0x4c, 0x77, 0x99, 0xe7, 0xfb, 0x38, 0xec,
0x2f, 0x42, 0xc2, 0x08, 0xea, 0xc6, 0xb2, 0xbe, 0x92, 0xf5, 0x85, 0xcc, 0xda, 0xe4, 0x3b, 0x26,
0xe7, 0x6e, 0xc8, 0xc4, 0xaf, 0xd0, 0xb6, 0xb6, 0xd2, 0xeb, 0x24, 0x98, 0x79, 0x67, 0x52, 0x20,
0x5c, 0x84, 0xd8, 0xc7, 0x2e, 0xc5, 0xea, 0xab, 0x6d, 0x52, 0x32, 0x2f, 0x98, 0x11, 0x29, 0xf8,
0xbf, 0x26, 0x60, 0x98, 0xb2, 0x71, 0x18, 0x05, 0x52, 0xb8, 0xad, 0x09, 0x29, 0x73, 0x59, 0x44,
0x35, 0x67, 0x17, 0x38, 0xa4, 0x1e, 0x09, 0xd4, 0x57, 0xc8, 0xec, 0x3f, 0x4a, 0xb0, 0x7e, 0xe4,
0x51, 0xe6, 0x88, 0x8d, 0xd4, 0xc1, 0x3f, 0x46, 0x98, 0x32, 0xd4, 0x85, 0xaa, 0xef, 0xcd, 0x3d,
0x66, 0x1a, 0x3b, 0x46, 0xaf, 0xec, 0x88, 0x09, 0xda, 0x84, 0x1a, 0x99, 0xcd, 0x28, 0x66, 0x66,
0x69, 0xc7, 0xe8, 0x35, 0x1d, 0x39, 0x43, 0x9f, 0x43, 0x9d, 0x92, 0x90, 0x8d, 0x4f, 0x2f, 0xcd,
0xf2, 0x8e, 0xd1, 0x6b, 0x0f, 0xde, 0xe9, 0x67, 0xe1, 0xd4, 0x8f, 0x3d, 0x8d, 0x48, 0xc8, 0xfa,
0xf1, 0xcf, 0x93, 0x4b, 0xa7, 0x46, 0xf9, 0x37, 0xb6, 0x3b, 0xf3, 0x7c, 0x86, 0x43, 0xb3, 0x22,
0xec, 0x8a, 0x19, 0x3a, 0x00, 0xe0, 0x76, 0x49, 0x38, 0xc5, 0xa1, 0x59, 0xe5, 0xa6, 0x7b, 0x05,
0x4c, 0x3f, 0x8f, 0xf5, 0x9d, 0x26, 0x55, 0x43, 0xf4, 0x19, 0xac, 0x0a, 0x48, 0xc6, 0x13, 0x32,
0xc5, 0xd4, 0xac, 0xed, 0x94, 0x7b, 0xed, 0xc1, 0xb6, 0x30, 0xa5, 0xe0, 0x1f, 0x09, 0xd0, 0xf6,
0xc9, 0x14, 0x3b, 0x2d, 0xa1, 0x1e, 0x8f, 0x29, 0xba, 0x0f, 0xcd, 0xc0, 0x9d, 0x63, 0xba, 0x70,
0x27, 0xd8, 0xac, 0xf3, 0x08, 0xaf, 0x16, 0xec, 0xef, 0xa1, 0xa1, 0x9c, 0xdb, 0x03, 0xa8, 0x89,
0xa3, 0xa1, 0x16, 0xd4, 0x4f, 0x8e, 0xbf, 0x3c, 0x7e, 0xfe, 0xf5, 0x71, 0x67, 0x05, 0x35, 0xa0,
0x72, 0xbc, 0xf7, 0xd5, 0xb0, 0x63, 0xa0, 0x35, 0xb8, 0x77, 0xb4, 0x37, 0x7a, 0x31, 0x76, 0x86,
0x47, 0xc3, 0xbd, 0xd1, 0xf0, 0x69, 0xa7, 0x64, 0xbf, 0x09, 0xcd, 0x24, 0x66, 0x54, 0x87, 0xf2,
0xde, 0x68, 0x5f, 0x6c, 0x79, 0x3a, 0x1c, 0xed, 0x77, 0x0c, 0xfb, 0x37, 0x03, 0xba, 0x3a, 0x45,
0x74, 0x41, 0x02, 0x8a, 0x63, 0x8e, 0x26, 0x24, 0x0a, 0x12, 0x8e, 0xf8, 0x04, 0x21, 0xa8, 0x04,
0xf8, 0xb5, 0x62, 0x88, 0x8f, 0x63, 0x4d, 0x46, 0x98, 0xeb, 0x73, 0x76, 0xca, 0x8e, 0x98, 0xa0,
0x0f, 0xa1, 0x21, 0x8f, 0x4e, 0xcd, 0xca, 0x4e, 0xb9, 0xd7, 0x1a, 0x6c, 0xe8, 0x80, 0x48, 0x8f,
0x4e, 0xa2, 0x66, 0x1f, 0xc0, 0xd6, 0x01, 0x56, 0x91, 0x08, 0xbc, 0xd4, 0x8d, 0x89, 0xfd, 0xba,
0x73, 0xcc, 0x83, 0x89, 0xfd, 0xba, 0x73, 0x8c, 0x4c, 0xa8, 0xcb, 0xeb, 0xc6, 0xc3, 0xa9, 0x3a,
0x6a, 0x6a, 0x33, 0x30, 0xaf, 0x1b, 0x92, 0xe7, 0xca, 0xb2, 0xf4, 0x2e, 0x54, 0xe2, 0x4c, 0xe0,
0x66, 0x5a, 0x03, 0xa4, 0xc7, 0xf9, 0x2c, 0x98, 0x11, 0x87, 0xcb, 0x75, 0xaa, 0xca, 0xcb, 0x54,
0x1d, 0xa6, 0xbd, 0xee, 0x93, 0x80, 0xe1, 0x80, 0xdd, 0x2d, 0xfe, 0x23, 0xd8, 0xce, 0xb0, 0x24,
0x0f, 0xb0, 0x0b, 0x75, 0x19, 0x1a, 0xb7, 0x96, 0x8b, 0xab, 0xd2, 0xb2, 0xff, 0x2a, 0x41, 0xf7,
0x64, 0x31, 0x75, 0x19, 0x56, 0xa2, 0x1b, 0x82, 0x7a, 0x00, 0x55, 0x5e, 0x51, 0x24, 0x16, 0x6b,
0xc2, 0xb6, 0x28, 0x3b, 0xfb, 0xf1, 0xaf, 0x23, 0xe4, 0xe8, 0x11, 0xd4, 0x2e, 0x5c, 0x3f, 0xc2,
0x94, 0x03, 0x91, 0xa0, 0x26, 0x35, 0x79, 0x39, 0x72, 0xa4, 0x06, 0xda, 0x82, 0xfa, 0x34, 0xbc,
0x8c, 0xeb, 0x09, 0x4f, 0xc1, 0x86, 0x53, 0x9b, 0x86, 0x97, 0x4e, 0x14, 0xa0, 0xb7, 0xe1, 0xde,
0xd4, 0xa3, 0xee, 0xa9, 0x8f, 0xc7, 0xe7, 0x84, 0xbc, 0xa2, 0x3c, 0x0b, 0x1b, 0xce, 0xaa, 0x5c,
0x3c, 0x8c, 0xd7, 0x90, 0x15, 0xdf, 0xa4, 0x49, 0x88, 0x5d, 0x86, 0xcd, 0x1a, 0x97, 0x27, 0xf3,
0x18, 0x43, 0xe6, 0xcd, 0x31, 0x89, 0x18, 0x4f, 0x9d, 0xb2, 0xa3, 0xa6, 0xe8, 0x2d, 0x58, 0x0d,
0x31, 0xc5, 0x6c, 0x2c, 0xa3, 0x6c, 0xf0, 0x9d, 0x2d, 0xbe, 0xf6, 0x52, 0x84, 0x85, 0xa0, 0xf2,
0x93, 0xeb, 0x31, 0xb3, 0xc9, 0x45, 0x7c, 0x2c, 0xb6, 0x45, 0x14, 0xab, 0x6d, 0xa0, 0xb6, 0x45,
0x14, 0xcb, 0x6d, 0x5d, 0xa8, 0xce, 0x48, 0x38, 0xc1, 0x66, 0x8b, 0xcb, 0xc4, 0xc4, 0xfe, 0x19,
0x36, 0x96, 0x40, 0xbe, 0x23, 0x5f, 0xe8, 0x13, 0x58, 0x9d, 0x79, 0x81, 0xeb, 0xab, 0x10, 0x4a,
0xb9, 0xf8, 0xb6, 0xb8, 0x9e, 0x08, 0xcb, 0xfe, 0xdb, 0x80, 0x4d, 0x87, 0xf8, 0xfe, 0xa9, 0x3b,
0x79, 0x55, 0x80, 0xe8, 0x14, 0x27, 0xa5, 0x9b, 0x39, 0x29, 0x67, 0x70, 0x92, 0xba, 0xbb, 0x15,
0xed, 0xee, 0x6a, 0x6c, 0x55, 0xf3, 0xd9, 0xaa, 0xe9, 0x6c, 0x29, 0x2a, 0xea, 0x29, 0x2a, 0x12,
0x9c, 0x1b, 0x69, 0x9c, 0xbf, 0x80, 0xad, 0x6b, 0xa7, 0xbc, 0x6b, 0x66, 0xfc, 0x5e, 0x82, 0x8d,
0x67, 0x01, 0x65, 0xae, 0xef, 0x2f, 0x21, 0x96, 0xa4, 0x81, 0x51, 0x38, 0x0d, 0x4a, 0xff, 0x26,
0x0d, 0xca, 0x1a, 0xe4, 0x8a, 0x9f, 0x4a, 0x8a, 0x9f, 0x42, 0xa9, 0xa1, 0x15, 0xa4, 0xda, 0x52,
0x41, 0x42, 0x6f, 0x00, 0x88, 0xbb, 0xcc, 0x8d, 0x0b, 0x68, 0x9b, 0x7c, 0xe5, 0x58, 0xd6, 0x1f,
0xc5, 0x46, 0x23, 0x9b, 0x8d, 0x54, 0x62, 0xd8, 0xbf, 0x18, 0xb0, 0xb9, 0x8c, 0xd5, 0x7f, 0x7c,
0xc3, 0x7f, 0x35, 0x60, 0xeb, 0x24, 0xf0, 0x32, 0x09, 0xcb, 0xba, 0xe2, 0xd7, 0x20, 0x2c, 0x65,
0x40, 0xd8, 0x85, 0xea, 0x22, 0x0a, 0xcf, 0xb0, 0xa4, 0x44, 0x4c, 0xd2, 0xd8, 0x54, 0x34, 0x6c,
0xec, 0x31, 0x98, 0xd7, 0x63, 0xb8, 0x2b, 0x10, 0x28, 0xf5, 0xf0, 0x34, 0xc5, 0x23, 0x63, 0xaf,
0xc3, 0xda, 0x01, 0x66, 0x2f, 0x45, 0x3a, 0xc9, 0xe3, 0xd9, 0x43, 0x40, 0xe9, 0xc5, 0x2b, 0x7f,
0x72, 0x49, 0xf7, 0xa7, 0xba, 0x30, 0xa5, 0xaf, 0xb4, 0xec, 0x4f, 0xb9, 0xed, 0x43, 0x8f, 0x32,
0x12, 0x5e, 0xde, 0x04, 0x5d, 0x07, 0xca, 0x73, 0xf7, 0xb5, 0x7c, 0x97, 0xe2, 0xa1, 0x7d, 0xc0,
0x23, 0x48, 0xb6, 0xca, 0x08, 0xd2, 0xaf, 0xbc, 0x51, 0xec, 0x95, 0xff, 0x16, 0xd0, 0x0b, 0x9c,
0x34, 0x1c, 0xb7, 0x3c, 0x90, 0x8a, 0x84, 0x92, 0x7e, 0x41, 0x4d, 0xa8, 0x4f, 0x7c, 0xec, 0x06,
0xd1, 0x42, 0xd2, 0xa6, 0xa6, 0xf6, 0x77, 0xb0, 0xae, 0x59, 0x97, 0x71, 0xc6, 0xe7, 0xa1, 0x67,
0xd2, 0x7a, 0x3c, 0x44, 0x1f, 0x43, 0x4d, 0x74, 0x61, 0xdc, 0x76, 0x7b, 0x70, 0x5f, 0x8f, 0x9b,
0x1b, 0x89, 0x02, 0xd9, 0xb6, 0x39, 0x52, 0x77, 0xf0, 0x67, 0x03, 0xda, 0xaa, 0xaf, 0x10, 0x3d,
0x22, 0xf2, 0x60, 0x35, 0xdd, 0x40, 0xa1, 0x87, 0xf9, 0x2d, 0xe4, 0x52, 0x1f, 0x6c, 0x3d, 0x2a,
0xa2, 0x2a, 0x4e, 0x60, 0xaf, 0x7c, 0x60, 0x20, 0x0a, 0x9d, 0xe5, 0xbe, 0x06, 0x3d, 0xce, 0xb6,
0x91, 0xd3, 0x48, 0x59, 0xfd, 0xa2, 0xea, 0xca, 0x2d, 0xba, 0xe0, 0x77, 0x46, 0x6f, 0x46, 0xd0,
0xad, 0x66, 0xf4, 0xfe, 0xc7, 0xda, 0x2d, 0xac, 0x9f, 0xf8, 0xfd, 0x01, 0xee, 0x69, 0x0f, 0x2a,
0xca, 0x41, 0x2b, 0xab, 0xb5, 0xb1, 0xde, 0x2b, 0xa4, 0x9b, 0xf8, 0x9a, 0x43, 0x5b, 0xaf, 0x6d,
0x28, 0xc7, 0x40, 0xe6, 0x6b, 0x61, 0xbd, 0x5f, 0x4c, 0x39, 0x71, 0x47, 0xa1, 0xb3, 0x5c, 0x43,
0xf2, 0x78, 0xcc, 0xa9, 0x77, 0x79, 0x3c, 0xe6, 0x95, 0x26, 0x7b, 0x05, 0xb9, 0x00, 0x57, 0x25,
0x04, 0x3d, 0xc8, 0x25, 0x44, 0xaf, 0x3c, 0x56, 0xef, 0x76, 0xc5, 0xc4, 0xc5, 0x02, 0xfe, 0xb7,
0xf4, 0x36, 0xa3, 0x1c, 0x68, 0xb2, 0x1b, 0x15, 0xeb, 0x71, 0x41, 0xed, 0xa5, 0x43, 0xc9, 0xaa,
0x74, 0xc3, 0xa1, 0xf4, 0x92, 0x77, 0xc3, 0xa1, 0x96, 0x0a, 0x9c, 0xbd, 0x82, 0x3c, 0x68, 0x3b,
0x51, 0x20, 0x5d, 0xc7, 0x65, 0x01, 0xe5, 0xec, 0xbe, 0x5e, 0xd5, 0xac, 0x87, 0x05, 0x34, 0xaf,
0xf2, 0xfb, 0x09, 0x7c, 0xd3, 0x50, 0xaa, 0xa7, 0x35, 0xfe, 0x17, 0xfa, 0xa3, 0x7f, 0x02, 0x00,
0x00, 0xff, 0xff, 0x8a, 0xf6, 0x81, 0x03, 0x30, 0x10, 0x00, 0x00,
}

@ -90,6 +90,8 @@ type Engine interface {
// It receives a chart, a config, and a map of overrides to the config.
// Overrides are assumed to be passed from the system, not the user.
Render(*chart.Chart, chartutil.Values) (map[string]string, error)
// ExpandValues will expand all templates found in vals
ExpandValues(chartutil.Values) (chartutil.Values, error)
}
// KubeClient represents a client capable of communicating with the Kubernetes API.

@ -18,6 +18,7 @@ package environment
import (
"bytes"
"fmt"
"io"
"testing"
"time"
@ -38,6 +39,10 @@ func (e *mockEngine) Render(chrt *chart.Chart, v chartutil.Values) (map[string]s
return e.out, nil
}
func (e *mockEngine) ExpandValues(chartutil.Values) (chartutil.Values, error) {
return nil, fmt.Errorf("not implemented")
}
type mockKubeClient struct{}
func (k *mockKubeClient) Create(ns string, r io.Reader, timeout int64, shouldWait bool) error {

@ -24,6 +24,7 @@ import (
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/hooks"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/proto/hapi/services"
relutil "k8s.io/helm/pkg/releaseutil"
@ -33,7 +34,7 @@ import (
// InstallRelease installs a release and stores the release record.
func (s *ReleaseServer) InstallRelease(c ctx.Context, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) {
s.Log("preparing install for %s", req.Name)
rel, err := s.prepareRelease(req)
rel, finalVals, err := s.prepareRelease(req)
if err != nil {
s.Log("failed install prepare step: %s", err)
res := &services.InstallReleaseResponse{Release: rel}
@ -47,27 +48,34 @@ func (s *ReleaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea
}
s.Log("performing install for %s", req.Name)
res, err := s.performRelease(rel, req)
if err != nil {
s.Log("failed install perform step: %s", err)
res := &services.InstallReleaseResponse{Release: rel, FinalValues: finalVals}
if req.DryRun {
s.Log("dry run for %s", rel.Name)
res.Release.Info.Description = "Dry run complete"
} else {
if err := s.performRelease(rel, req); err != nil {
s.Log("failed install perform step: %s", err)
return res, err
}
}
return res, err
return res, nil
}
// prepareRelease builds a release for an install operation.
func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*release.Release, error) {
func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*release.Release, *chart.Config, error) {
if req.Chart == nil {
return nil, errMissingChart
return nil, nil, errMissingChart
}
name, err := s.uniqName(req.Name, req.ReuseName)
if err != nil {
return nil, err
return nil, nil, err
}
caps, err := capabilities(s.clientset.Discovery())
if err != nil {
return nil, err
return nil, nil, err
}
revision := 1
@ -81,10 +89,10 @@ func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*re
}
valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps)
if err != nil {
return nil, err
return nil, nil, err
}
hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender, caps.APIVersions)
hooks, manifestDoc, notesTxt, finalVals, err := s.renderResources(req.Chart, valuesToRender, caps.APIVersions)
if err != nil {
// Return a release with partial data so that client can show debugging
// information.
@ -104,7 +112,7 @@ func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*re
if manifestDoc != nil {
rel.Manifest = manifestDoc.String()
}
return rel, err
return rel, finalVals, err
}
// Store a release.
@ -128,23 +136,15 @@ func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*re
}
err = validateManifest(s.env.KubeClient, req.Namespace, manifestDoc.Bytes())
return rel, err
return rel, finalVals, err
}
// performRelease runs a release.
func (s *ReleaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) {
res := &services.InstallReleaseResponse{Release: r}
if req.DryRun {
s.Log("dry run for %s", r.Name)
res.Release.Info.Description = "Dry run complete"
return res, nil
}
func (s *ReleaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) error {
// pre-install hooks
if !req.DisableHooks {
if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PreInstall, req.Timeout); err != nil {
return res, err
return err
}
} else {
s.Log("install hooks disabled for %s", req.Name)
@ -181,7 +181,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install
r.Info.Description = msg
s.recordRelease(old, true)
s.recordRelease(r, true)
return res, err
return err
}
default:
@ -194,7 +194,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install
r.Info.Status.Code = release.Status_FAILED
r.Info.Description = msg
s.recordRelease(r, true)
return res, fmt.Errorf("release %s failed: %s", r.Name, err)
return fmt.Errorf("release %s failed: %s", r.Name, err)
}
}
@ -206,7 +206,7 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install
r.Info.Status.Code = release.Status_FAILED
r.Info.Description = msg
s.recordRelease(r, true)
return res, err
return err
}
}
@ -221,5 +221,5 @@ func (s *ReleaseServer) performRelease(r *release.Release, req *services.Install
// this stored in the future.
s.recordRelease(r, true)
return res, nil
return nil
}

@ -244,12 +244,12 @@ func GetVersionSet(client discovery.ServerGroupsInterface) (chartutil.VersionSet
return chartutil.NewVersionSet(versions...), nil
}
func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values, vs chartutil.VersionSet) ([]*release.Hook, *bytes.Buffer, string, error) {
func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values, vs chartutil.VersionSet) ([]*release.Hook, *bytes.Buffer, string, *chart.Config, error) {
// Guard to make sure Tiller is at the right version to handle this chart.
sver := version.GetVersion()
if ch.Metadata.TillerVersion != "" &&
!version.IsCompatibleRange(ch.Metadata.TillerVersion, sver) {
return nil, nil, "", fmt.Errorf("Chart incompatible with Tiller %s", sver)
return nil, nil, "", nil, fmt.Errorf("Chart incompatible with Tiller %s", sver)
}
if ch.Metadata.KubeVersion != "" {
@ -257,15 +257,22 @@ func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values
gitVersion := cap.KubeVersion.String()
k8sVersion := strings.Split(gitVersion, "+")[0]
if !version.IsCompatibleRange(ch.Metadata.KubeVersion, k8sVersion) {
return nil, nil, "", fmt.Errorf("Chart requires kubernetesVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, k8sVersion)
return nil, nil, "", nil, fmt.Errorf("Chart requires kubernetesVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, k8sVersion)
}
}
s.Log("rendering %s chart using values", ch.GetMetadata().Name)
renderer := s.engine(ch)
if ch.Metadata.ExpandValues {
newVals, err := renderer.ExpandValues(values)
if err != nil {
return nil, nil, "", nil, err
}
values = newVals
}
files, err := renderer.Render(ch, values)
if err != nil {
return nil, nil, "", err
return nil, nil, "", nil, err
}
// NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource,
@ -303,7 +310,7 @@ func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values
b.WriteString("\n---\n# Source: " + name + "\n")
b.WriteString(content)
}
return nil, b, "", err
return nil, b, "", nil, err
}
// Aggregate all valid manifests into one big doc.
@ -313,7 +320,18 @@ func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values
b.WriteString(m.Content)
}
return hooks, b, notes, nil
if !ch.Metadata.ExpandValues {
return hooks, b, notes, nil, nil
}
var finalValsStr string
finalVals, err := values.Table("Values")
if err == nil {
finalValsStr, err = finalVals.YAML()
}
if err != nil {
return nil, b, "", nil, err
}
return hooks, b, notes, &chart.Config{Raw: finalValsStr}, nil
}
// recordRelease with an update operation in case reuse has been set.

@ -24,6 +24,7 @@ import (
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/hooks"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/proto/hapi/services"
"k8s.io/helm/pkg/timeconv"
@ -36,7 +37,7 @@ func (s *ReleaseServer) UpdateRelease(c ctx.Context, req *services.UpdateRelease
return nil, err
}
s.Log("preparing update for %s", req.Name)
currentRelease, updatedRelease, err := s.prepareUpdate(req)
currentRelease, updatedRelease, finalVals, err := s.prepareUpdate(req)
if err != nil {
if req.Force {
// Use the --force, Luke.
@ -53,12 +54,14 @@ func (s *ReleaseServer) UpdateRelease(c ctx.Context, req *services.UpdateRelease
}
s.Log("performing update for %s", req.Name)
res, err := s.performUpdate(currentRelease, updatedRelease, req)
if err != nil {
return res, err
}
if !req.DryRun {
res := &services.UpdateReleaseResponse{Release: updatedRelease, FinalValues: finalVals}
if req.DryRun {
s.Log("dry run for %s", updatedRelease.Name)
res.Release.Info.Description = "Dry run complete"
} else {
if err := s.performUpdate(currentRelease, updatedRelease, req); err != nil {
return res, err
}
s.Log("updating status for updated release for %s", req.Name)
if err := s.env.Releases.Update(updatedRelease); err != nil {
return res, err
@ -69,26 +72,26 @@ func (s *ReleaseServer) UpdateRelease(c ctx.Context, req *services.UpdateRelease
}
// prepareUpdate builds an updated release for an update operation.
func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*release.Release, *release.Release, error) {
func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*release.Release, *release.Release, *chart.Config, error) {
if req.Chart == nil {
return nil, nil, errMissingChart
return nil, nil, nil, errMissingChart
}
// finds the deployed release with the given name
currentRelease, err := s.env.Releases.Deployed(req.Name)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
// If new values were not supplied in the upgrade, re-use the existing values.
if err := s.reuseValues(req, currentRelease); err != nil {
return nil, nil, err
return nil, nil, nil, err
}
// finds the non-deleted release with the given name
lastRelease, err := s.env.Releases.Last(req.Name)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
// Increment revision count. This is passed to templates, and also stored on
@ -106,16 +109,16 @@ func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele
caps, err := capabilities(s.clientset.Discovery())
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender, caps.APIVersions)
hooks, manifestDoc, notesTxt, finalVals, err := s.renderResources(req.Chart, valuesToRender, caps.APIVersions)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
// Store an updated release.
@ -139,7 +142,7 @@ func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele
updatedRelease.Info.Status.Notes = notesTxt
}
err = validateManifest(s.env.KubeClient, currentRelease.Namespace, manifestDoc.Bytes())
return currentRelease, updatedRelease, err
return currentRelease, updatedRelease, finalVals, err
}
// performUpdateForce performs the same action as a `helm delete && helm install --replace`.
@ -150,7 +153,7 @@ func (s *ReleaseServer) performUpdateForce(req *services.UpdateReleaseRequest) (
return nil, err
}
newRelease, err := s.prepareRelease(&services.InstallReleaseRequest{
newRelease, finalVals, err := s.prepareRelease(&services.InstallReleaseRequest{
Chart: req.Chart,
Values: req.Values,
DryRun: req.DryRun,
@ -161,7 +164,7 @@ func (s *ReleaseServer) performUpdateForce(req *services.UpdateReleaseRequest) (
Timeout: req.Timeout,
Wait: req.Wait,
})
res := &services.UpdateReleaseResponse{Release: newRelease}
res := &services.UpdateReleaseResponse{Release: newRelease, FinalValues: finalVals}
if err != nil {
s.Log("failed update prepare step: %s", err)
// On dry run, append the manifest contents to a failed release. This is
@ -249,19 +252,11 @@ func (s *ReleaseServer) performUpdateForce(req *services.UpdateReleaseRequest) (
return res, nil
}
func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) {
res := &services.UpdateReleaseResponse{Release: updatedRelease}
if req.DryRun {
s.Log("dry run for %s", updatedRelease.Name)
res.Release.Info.Description = "Dry run complete"
return res, nil
}
func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest) error {
// pre-upgrade hooks
if !req.DisableHooks {
if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PreUpgrade, req.Timeout); err != nil {
return res, err
return err
}
} else {
s.Log("update hooks disabled for %s", req.Name)
@ -273,13 +268,13 @@ func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.R
updatedRelease.Info.Description = msg
s.recordRelease(originalRelease, true)
s.recordRelease(updatedRelease, true)
return res, err
return err
}
// post-upgrade hooks
if !req.DisableHooks {
if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PostUpgrade, req.Timeout); err != nil {
return res, err
return err
}
}
@ -289,5 +284,5 @@ func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.R
updatedRelease.Info.Status.Code = release.Status_DEPLOYED
updatedRelease.Info.Description = "Upgrade complete"
return res, nil
return nil
}

Loading…
Cancel
Save