Add AsSecrets, AsConfig methods for Files object. Move ToYaml to chartutil
This commit is contained in:
parent
935779157b
commit
9771973888
|
@ -9,6 +9,17 @@ Helm provides access to files through the `.Files` object. Before we get going w
|
|||
- Files in `templates/` cannot be accessed.
|
||||
- Charts to not preserve UNIX mode information, so file-level permissions will have no impact on the availability of a file when it comes to the `.Files` object.
|
||||
|
||||
<!-- (see https://github.com/jonschlinkert/markdown-toc) -->
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
- [Basic example](#basic-example)
|
||||
- [Glob patterns](#glob-patterns)
|
||||
- [ConfigMap and Secrets utility functions](#configmap-and-secrets-utility-functions)
|
||||
- [Secrets](#secrets)
|
||||
|
||||
<!-- tocstop -->
|
||||
|
||||
## Basic example
|
||||
|
||||
With those caveats behind, let's write a template that reads three files into our ConfigMap. To get started, we will add three files to the chart, putting all three directly inside of the `mychart/` directory.
|
||||
|
@ -73,6 +84,9 @@ As your chart grows, you may find you have a greater need to organize your
|
|||
files more, and so we provide a `Files.Glob(pattern string)` method to assist
|
||||
in extracting certain files with all the flexibility of [glob patterns](//godoc.org/github.com/gobwas/glob).
|
||||
|
||||
`.Glob` returns a `Files` type, so you may call any of the `Files` methods on
|
||||
the returned object.
|
||||
|
||||
For example, imagine the directory structure:
|
||||
|
||||
```
|
||||
|
@ -101,6 +115,36 @@ Or
|
|||
{{ end }}
|
||||
```
|
||||
|
||||
## ConfigMap and Secrets utility functions
|
||||
|
||||
(Not present in version 2.0.2 or prior)
|
||||
|
||||
It is very common to want to place file content into both configmaps and
|
||||
secrets, for mounting into your pods at run time. To help with this, we provide a
|
||||
couple utility methods on the `Files` type.
|
||||
|
||||
For further organization, it is especially useful to use these methods in
|
||||
conjunction with the `Glob` method.
|
||||
|
||||
Given the directory structure from the [Glob][Glob patterns] example above:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: conf
|
||||
data:
|
||||
{{ (.Files.Glob "foo/*").AsConfig | indent 2 }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: very-secret
|
||||
type: Opaque
|
||||
data:
|
||||
{{ (.Files.Glob "bar/*").AsSecrets | indent 2 }}
|
||||
```
|
||||
|
||||
## Secrets
|
||||
|
||||
When working with a Secret resource, you can import a file and have the template base-64 encode it for you:
|
||||
|
|
|
@ -16,6 +16,11 @@ limitations under the License.
|
|||
package chartutil
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"path"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/golang/protobuf/ptypes/any"
|
||||
)
|
||||
|
@ -83,3 +88,69 @@ func (f Files) Glob(pattern string) Files {
|
|||
|
||||
return nf
|
||||
}
|
||||
|
||||
// AsConfig turns a Files group and flattens it to a YAML map suitable for
|
||||
// including in the `data` section of a kubernetes ConfigMap definition.
|
||||
// Duplicate keys will be overwritten, so be aware that your filenames
|
||||
// (regardless of path) should be unique.
|
||||
//
|
||||
// This is designed to be called from a template, and will return empty string
|
||||
// (via ToYaml function) if it cannot be serialized to YAML, or if the Files
|
||||
// object is nil.
|
||||
//
|
||||
// The output will not be indented, so you will want to pipe this to the
|
||||
// `indent` template function.
|
||||
//
|
||||
// data:
|
||||
// {{ .Files.Glob("config/**").AsConfig() | indent 4 }}
|
||||
func (f Files) AsConfig() string {
|
||||
if f == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
m := map[string]string{}
|
||||
|
||||
// Explicitly convert to strings, and file names
|
||||
for k, v := range f {
|
||||
m[path.Base(k)] = string(v)
|
||||
}
|
||||
|
||||
return ToYaml(m)
|
||||
}
|
||||
|
||||
// AsSecrets returns the value of a Files object as base64 suitable for
|
||||
// including in the `data` section of a kubernetes Secret definition.
|
||||
// Duplicate keys will be overwritten, so be aware that your filenames
|
||||
// (regardless of path) should be unique.
|
||||
//
|
||||
// This is designed to be called from a template, and will return empty string
|
||||
// (via ToYaml function) if it cannot be serialized to YAML, or if the Files
|
||||
// object is nil.
|
||||
//
|
||||
// The output will not be indented, so you will want to pipe this to the
|
||||
// `indent` template function.
|
||||
//
|
||||
// data:
|
||||
// {{ .Files.Glob("secrets/*").AsSecrets() }}
|
||||
func (f Files) AsSecrets() string {
|
||||
if f == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
m := map[string]string{}
|
||||
|
||||
for k, v := range f {
|
||||
m[path.Base(k)] = base64.StdEncoding.EncodeToString(v)
|
||||
}
|
||||
|
||||
return ToYaml(m)
|
||||
}
|
||||
|
||||
func ToYaml(v interface{}) string {
|
||||
data, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
// Swallow errors inside of a template.
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/any"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var cases = []struct {
|
||||
|
@ -55,16 +56,45 @@ func TestNewFiles(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFileGlob(t *testing.T) {
|
||||
as := assert.New(t)
|
||||
|
||||
f := NewFiles(getTestFiles())
|
||||
|
||||
matched := f.Glob("story/**")
|
||||
|
||||
if len(matched) != 2 {
|
||||
t.Errorf("Expected two files in glob story/**, got %d", len(matched))
|
||||
as.Len(matched, 2, "Should be two files in glob story/**")
|
||||
as.Equal("Joseph Conrad", matched.Get("story/author.txt"))
|
||||
}
|
||||
|
||||
func TestToConfig(t *testing.T) {
|
||||
as := assert.New(t)
|
||||
|
||||
f := NewFiles(getTestFiles())
|
||||
out := f.Glob("**/captain.txt").AsConfig()
|
||||
as.Equal("captain.txt: The Captain\n", out)
|
||||
|
||||
out = f.Glob("ship/**").AsConfig()
|
||||
as.Equal("captain.txt: The Captain\nstowaway.txt: Legatt\n", out)
|
||||
}
|
||||
|
||||
func TestToSecret(t *testing.T) {
|
||||
as := assert.New(t)
|
||||
|
||||
f := NewFiles(getTestFiles())
|
||||
|
||||
out := f.Glob("ship/**").AsSecrets()
|
||||
as.Equal("captain.txt: VGhlIENhcHRhaW4=\nstowaway.txt: TGVnYXR0\n", out)
|
||||
}
|
||||
|
||||
func TestToYaml(t *testing.T) {
|
||||
expect := "foo: bar\n"
|
||||
v := struct {
|
||||
Foo string `json:"foo"`
|
||||
}{
|
||||
Foo: "bar",
|
||||
}
|
||||
|
||||
m, expect := matched.Get("story/author.txt"), "Joseph Conrad"
|
||||
if m != expect {
|
||||
t.Errorf("Wrong globbed file content. Expected %s, got %s", expect, m)
|
||||
if got := ToYaml(v); got != expect {
|
||||
t.Errorf("Expected %q, got %q", expect, got)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import (
|
|||
"text/template"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"k8s.io/helm/pkg/chartutil"
|
||||
"k8s.io/helm/pkg/proto/hapi/chart"
|
||||
|
@ -69,26 +68,28 @@ func FuncMap() template.FuncMap {
|
|||
delete(f, "env")
|
||||
delete(f, "expandenv")
|
||||
|
||||
// Add a function to convert to YAML:
|
||||
f["toYaml"] = toYaml
|
||||
// Add some extra functionality
|
||||
extra := template.FuncMap{
|
||||
"toYaml": files.ToYaml,
|
||||
"base": path.Base,
|
||||
"dir": path.Dir,
|
||||
"ext": path.Ext,
|
||||
"isAbs": path.IsAbs,
|
||||
"clean": path.Clean,
|
||||
|
||||
// This is a placeholder for the "include" function, which is
|
||||
// late-bound to a template. By declaring it here, we preserve the
|
||||
// integrity of the linter.
|
||||
f["include"] = func(string, interface{}) string { return "not implemented" }
|
||||
// This is a placeholder for the "include" function, which is
|
||||
// late-bound to a template. By declaring it here, we preserve the
|
||||
// integrity of the linter.
|
||||
"include": func(string, interface{}) string { return "not implemented" },
|
||||
}
|
||||
|
||||
for k, v := range extra {
|
||||
f[k] = v
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
func toYaml(v interface{}) string {
|
||||
data, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
// Swallow errors inside of a template.
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.
|
||||
//
|
||||
// Render can be called repeatedly on the same engine.
|
||||
|
|
|
@ -27,19 +27,6 @@ import (
|
|||
"github.com/golang/protobuf/ptypes/any"
|
||||
)
|
||||
|
||||
func TestToYaml(t *testing.T) {
|
||||
expect := "foo: bar\n"
|
||||
v := struct {
|
||||
Foo string `json:"foo"`
|
||||
}{
|
||||
Foo: "bar",
|
||||
}
|
||||
|
||||
if got := toYaml(v); got != expect {
|
||||
t.Errorf("Expected %q, got %q", expect, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngine(t *testing.T) {
|
||||
e := New()
|
||||
|
||||
|
|
Loading…
Reference in New Issue