pkg/safeguards/preprocessing/preprocessing.go (81 lines of code) (raw):
package preprocessing
import (
"fmt"
"path/filepath"
sgTypes "github.com/Azure/draft/pkg/safeguards/types"
log "github.com/sirupsen/logrus"
"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"
"sigs.k8s.io/kustomize/api/krusty"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
// Given a Helm chart directory or file, renders all templates and writes them to the specified directory
func RenderHelmChart(isFile bool, mainChartPath string, opt chartutil.ReleaseOptions) ([]sgTypes.ManifestFile, error) {
if isFile { // Get the directory that the Chart.yaml lives in
mainChartPath = filepath.Dir(mainChartPath)
}
mainChart, err := loader.Load(mainChartPath)
if err != nil {
return nil, fmt.Errorf("failed to load main chart: %s", err)
}
loadedCharts := make(map[string]*chart.Chart) // map of chart path to chart object
loadedCharts[mainChartPath] = mainChart
// Load subcharts and dependencies
for _, dep := range mainChart.Metadata.Dependencies {
// Resolve the chart path based on the main chart's directory
chartPath := filepath.Join(mainChartPath, dep.Repository[len("file://"):])
chartPath = filepath.Clean(chartPath)
subChart, err := loader.Load(chartPath)
if err != nil {
return nil, fmt.Errorf("failed to load chart: %s", err)
}
loadedCharts[chartPath] = subChart
}
var manifestFiles []sgTypes.ManifestFile
for chartPath, chart := range loadedCharts {
valuesPath := filepath.Join(chartPath, "values.yaml") // Enforce that values.yaml must be at same level as Chart.yaml
mergedValues, err := getValues(chart, valuesPath, opt, filepath.Base(mainChartPath))
if err != nil {
return nil, fmt.Errorf("failed to load values: %s", err)
}
e := engine.Engine{Strict: true}
renderedFiles, err := e.Render(chart, mergedValues)
if err != nil {
return nil, fmt.Errorf("failed to render chart: %s", err)
}
// Convert renderd files to []byte
for renderedPath, content := range renderedFiles {
byteContent := []byte(content)
manifestFiles = append(manifestFiles, sgTypes.ManifestFile{Name: filepath.Base(renderedPath), ManifestContent: byteContent})
}
}
return manifestFiles, nil
}
// Given a kustomization manifest file within kustomizationPath, RenderKustomizeManifest will return render templates
func RenderKustomizeManifest(kustomizationPath string) ([]sgTypes.ManifestFile, error) {
log.Debugf("Rendering kustomization.yaml...")
if IsYAML(kustomizationPath) {
kustomizationPath = filepath.Dir(kustomizationPath)
}
options := &krusty.Options{
Reorder: "none",
LoadRestrictions: types.LoadRestrictionsRootOnly,
PluginConfig: &types.PluginConfig{},
}
k := krusty.MakeKustomizer(options)
// Run the build to generate the manifests
kustomizeFS := filesys.MakeFsOnDisk()
resMap, err := k.Run(kustomizeFS, kustomizationPath)
if err != nil {
return nil, fmt.Errorf("error building manifests: %s", err.Error())
}
// Output the manifests
var manifestFiles []sgTypes.ManifestFile
for _, res := range resMap.Resources() {
yamlRes, err := res.AsYAML()
if err != nil {
return nil, fmt.Errorf("error converting resource to YAML: %s", err.Error())
}
// write yamlRes to dir
manifestFiles = append(manifestFiles, sgTypes.ManifestFile{
Name: res.GetName(),
ManifestContent: yamlRes,
})
}
return manifestFiles, nil
}