/
chart.go
118 lines (101 loc) · 3.31 KB
/
chart.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package chart
import (
"bytes"
"fmt"
"path"
"strings"
simontype "github.com/alibaba/open-simulator/pkg/type"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/engine"
"helm.sh/helm/v3/pkg/releaseutil"
)
// ProcessChart parses chart to /tmp/charts
func ProcessChart(name string, chartPath string) ([]string, error) {
chartRequested, err := loader.Load(chartPath)
if err != nil {
return nil, err
}
chartRequested.Metadata.Name = name
if err := checkIfInstallable(chartRequested); err != nil {
return nil, err
}
// TODO
var vals map[string]interface{}
if err := chartutil.ProcessDependencies(chartRequested, vals); err != nil {
return nil, err
}
valuesToRender, err := ToRenderValues(chartRequested, vals)
if err != nil {
return nil, err
}
return renderResources(chartRequested, valuesToRender, true)
}
// checkIfInstallable validates if a chart can be installed
// Application chart type is only installable
func checkIfInstallable(ch *chart.Chart) error {
switch ch.Metadata.Type {
case "", "application":
return nil
}
return fmt.Errorf("%s charts are not installable", ch.Metadata.Type)
}
// ToRenderValues composes the struct from the data coming from the Releases, Charts and Values files
func ToRenderValues(chrt *chart.Chart, chrtVals map[string]interface{}) (chartutil.Values, error) {
top := map[string]interface{}{
"Chart": chrt.Metadata,
"Release": map[string]interface{}{
"Name": chrt.Name(),
"Namespace": "default",
"Revision": 1,
"Service": "Helm",
},
}
vals, err := chartutil.CoalesceValues(chrt, chrtVals)
if err != nil {
return top, err
}
if err := chartutil.ValidateAgainstSchema(chrt, vals); err != nil {
errFmt := "values don't meet the specifications of the schema(s) in the following chart(s):\n%s"
return top, fmt.Errorf(errFmt, err.Error())
}
top["Values"] = vals
return top, nil
}
func renderResources(ch *chart.Chart, values chartutil.Values, subNotes bool) ([]string, error) {
files, err := engine.Render(ch, values)
if err != nil {
return nil, err
}
// NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource,
// pull it out of here into a separate file so that we can actually use the output of the rendered
// text file. We have to spin through this map because the file contains path information, so we
// look for terminating NOTES.txt. We also remove it from the files so that we don't have to skip
// it in the sortHooks.
var notesBuffer bytes.Buffer
for k, v := range files {
if strings.HasSuffix(k, simontype.NotesFileSuffix) {
if subNotes || (k == path.Join(ch.Name(), "templates", simontype.NotesFileSuffix)) {
// If buffer contains data, add newline before adding more
if notesBuffer.Len() > 0 {
notesBuffer.WriteString("\n")
}
notesBuffer.WriteString(v)
}
delete(files, k)
}
}
// Sort hooks, manifests, and partials. Only hooks and manifests are returned,
// as partials are not used after renderer.Render. Empty manifests are also
// removed here.
var yamlStr []string
_, manifests, err := releaseutil.SortManifests(files, []string{}, releaseutil.InstallOrder)
if err != nil {
return nil, err
}
for _, item := range manifests {
yamlStr = append(yamlStr, item.Content)
}
return yamlStr, nil
}