122 lines
3.3 KiB
Go
122 lines
3.3 KiB
Go
package chart
|
|
|
|
// Author: Weisen Pan
|
|
// Date: 2023-10-24
|
|
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"path"
|
|
"strings"
|
|
|
|
simontype "github.com/hkust-adsl/kubernetes-scheduler-simulator/knets_pkg/type"
|
|
"helm.sh/helm/v3/knets_pkg/chart"
|
|
"helm.sh/helm/v3/knets_pkg/chart/loader"
|
|
"helm.sh/helm/v3/knets_pkg/chartutil"
|
|
"helm.sh/helm/v3/knets_pkg/engine"
|
|
"helm.sh/helm/v3/knets_pkg/releaseutil"
|
|
)
|
|
|
|
// ProcessChart parses a chart and returns rendered resources.
|
|
func ProcessChart(name string, chartPath string) ([]string, error) {
|
|
// Load the requested chart from the provided path.
|
|
chartRequested, err := loader.Load(chartPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
chartRequested.Metadata.Name = name
|
|
|
|
// Check if the chart is installable.
|
|
if err := checkIfInstallable(chartRequested); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// TODO: Define values to be processed.
|
|
var vals map[string]interface{}
|
|
|
|
// Process chart dependencies.
|
|
if err := chartutil.ProcessDependencies(chartRequested, vals); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Convert values and render resources.
|
|
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 the only type considered 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
|
|
}
|
|
|
|
// renderResources renders the resources from the chart.
|
|
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 is treated separately.
|
|
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 the buffer contains data, add a newline before adding more.
|
|
if notesBuffer.Len() > 0 {
|
|
notesBuffer.WriteString("\n")
|
|
}
|
|
notesBuffer.WriteString(v)
|
|
}
|
|
delete(files, k)
|
|
}
|
|
}
|
|
|
|
// Sort hooks and manifests and return only manifests.
|
|
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
|
|
}
|