github.com/cloudposse/helm@v2.2.3+incompatible/pkg/chartutil/files.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors All rights reserved.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package chartutil
    17  
    18  import (
    19  	"encoding/base64"
    20  	"path"
    21  	"strings"
    22  
    23  	yaml "gopkg.in/yaml.v2"
    24  
    25  	"github.com/gobwas/glob"
    26  	"github.com/golang/protobuf/ptypes/any"
    27  )
    28  
    29  // Files is a map of files in a chart that can be accessed from a template.
    30  type Files map[string][]byte
    31  
    32  // NewFiles creates a new Files from chart files.
    33  // Given an []*any.Any (the format for files in a chart.Chart), extract a map of files.
    34  func NewFiles(from []*any.Any) Files {
    35  	files := map[string][]byte{}
    36  	if from != nil {
    37  		for _, f := range from {
    38  			files[f.TypeUrl] = f.Value
    39  		}
    40  	}
    41  	return files
    42  }
    43  
    44  // GetBytes gets a file by path.
    45  //
    46  // The returned data is raw. In a template context, this is identical to calling
    47  // {{index .Files $path}}.
    48  //
    49  // This is intended to be accessed from within a template, so a missed key returns
    50  // an empty []byte.
    51  func (f Files) GetBytes(name string) []byte {
    52  	v, ok := f[name]
    53  	if !ok {
    54  		return []byte{}
    55  	}
    56  	return v
    57  }
    58  
    59  // Get returns a string representation of the given file.
    60  //
    61  // Fetch the contents of a file as a string. It is designed to be called in a
    62  // template.
    63  //
    64  //	{{.Files.Get "foo"}}
    65  func (f Files) Get(name string) string {
    66  	return string(f.GetBytes(name))
    67  }
    68  
    69  // Glob takes a glob pattern and returns another files object only containing
    70  // matched  files.
    71  //
    72  // This is designed to be called from a template.
    73  //
    74  // {{ range $name, $content := .Files.Glob("foo/**") }}
    75  // {{ $name }}: |
    76  // {{ .Files.Get($name) | indent 4 }}{{ end }}
    77  func (f Files) Glob(pattern string) Files {
    78  	g, err := glob.Compile(pattern, '/')
    79  	if err != nil {
    80  		g, _ = glob.Compile("**")
    81  	}
    82  
    83  	nf := NewFiles(nil)
    84  	for name, contents := range f {
    85  		if g.Match(name) {
    86  			nf[name] = contents
    87  		}
    88  	}
    89  
    90  	return nf
    91  }
    92  
    93  // AsConfig turns a Files group and flattens it to a YAML map suitable for
    94  // including in the `data` section of a kubernetes ConfigMap definition.
    95  // Duplicate keys will be overwritten, so be aware that your filenames
    96  // (regardless of path) should be unique.
    97  //
    98  // This is designed to be called from a template, and will return empty string
    99  // (via ToYaml function) if it cannot be serialized to YAML, or if the Files
   100  // object is nil.
   101  //
   102  // The output will not be indented, so you will want to pipe this to the
   103  // `indent` template function.
   104  //
   105  //   data:
   106  // {{ .Files.Glob("config/**").AsConfig() | indent 4 }}
   107  func (f Files) AsConfig() string {
   108  	if f == nil {
   109  		return ""
   110  	}
   111  
   112  	m := map[string]string{}
   113  
   114  	// Explicitly convert to strings, and file names
   115  	for k, v := range f {
   116  		m[path.Base(k)] = string(v)
   117  	}
   118  
   119  	return ToYaml(m)
   120  }
   121  
   122  // AsSecrets returns the value of a Files object as base64 suitable for
   123  // including in the `data` section of a kubernetes Secret definition.
   124  // Duplicate keys will be overwritten, so be aware that your filenames
   125  // (regardless of path) should be unique.
   126  //
   127  // This is designed to be called from a template, and will return empty string
   128  // (via ToYaml function) if it cannot be serialized to YAML, or if the Files
   129  // object is nil.
   130  //
   131  // The output will not be indented, so you will want to pipe this to the
   132  // `indent` template function.
   133  //
   134  //   data:
   135  // {{ .Files.Glob("secrets/*").AsSecrets() }}
   136  func (f Files) AsSecrets() string {
   137  	if f == nil {
   138  		return ""
   139  	}
   140  
   141  	m := map[string]string{}
   142  
   143  	for k, v := range f {
   144  		m[path.Base(k)] = base64.StdEncoding.EncodeToString(v)
   145  	}
   146  
   147  	return ToYaml(m)
   148  }
   149  
   150  // Lines returns each line of a named file (split by "\n") as a slice, so it can
   151  // be ranged over in your templates.
   152  //
   153  // This is designed to be called from a template.
   154  //
   155  // {{ range .Files.Lines "foo/bar.html" }}
   156  // {{ . }}{{ end }}
   157  func (f Files) Lines(path string) []string {
   158  	if f == nil || f[path] == nil {
   159  		return []string{}
   160  	}
   161  
   162  	return strings.Split(string(f[path]), "\n")
   163  }
   164  
   165  // ToYaml takes an interface, marshals it to yaml, and returns a string. It will
   166  // always return a string, even on marshal error (empty string).
   167  //
   168  // This is designed to be called from a template.
   169  func ToYaml(v interface{}) string {
   170  	data, err := yaml.Marshal(v)
   171  	if err != nil {
   172  		// Swallow errors inside of a template.
   173  		return ""
   174  	}
   175  	return string(data)
   176  }
   177  
   178  // FromYaml converts a YAML document into a map[string]interface{}.
   179  //
   180  // This is not a general-purpose YAML parser, and will not parse all valid
   181  // YAML documents. Additionally, because its intended use is within templates
   182  // it tolerates errors. It will insert the returned error message string into
   183  // m["error"] in the returned map.
   184  func FromYaml(str string) map[string]interface{} {
   185  	m := map[string]interface{}{}
   186  
   187  	if err := yaml.Unmarshal([]byte(str), &m); err != nil {
   188  		m["Error"] = err.Error()
   189  	}
   190  	return m
   191  }