pull/31762/merge
Dima Aratin 3 days ago committed by GitHub
commit b0958b9c3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -213,13 +213,52 @@ func splitAndDeannotate(postrendered string) (map[string]string, error) {
return reconstructed, nil
}
// TransformManifestPath modifies the manifest path based on the skipChartNameDir and skipTemplatesDir flags.
// The input path is typically in the format "chart-name/templates/file.yaml" or "chart-name/charts/subchart/templates/file.yaml"
// - skipChartNameDir: removes the root chart name directory
// - skipTemplatesDir: removes all "templates" directories from the path
func TransformManifestPath(name string, skipChartNameDir, skipTemplatesDir bool) string {
if !skipChartNameDir && !skipTemplatesDir {
return name
}
parts := strings.Split(name, "/")
if len(parts) == 0 {
return name
}
var result []string
for i, part := range parts {
// Skip empty parts (e.g., from leading slash or double slashes)
if part == "" {
continue
}
// Skip the first part (chart name) if skipChartNameDir is true
if i == 0 && skipChartNameDir {
continue
}
// Skip "templates" directories if skipTemplatesDir is true
if skipTemplatesDir && part == "templates" {
continue
}
result = append(result, part)
}
if len(result) == 0 {
return name
}
return strings.Join(result, "/")
}
// renderResources renders the templates in a chart
//
// TODO: This function is badly in need of a refactor.
// TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed
//
// This code has to do with writing files to disk.
func (cfg *Configuration) renderResources(ch *chart.Chart, values common.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrenderer.PostRenderer, interactWithRemote, enableDNS, hideSecret bool) ([]*release.Hook, *bytes.Buffer, string, error) {
func (cfg *Configuration) renderResources(ch *chart.Chart, values common.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrenderer.PostRenderer, interactWithRemote, enableDNS, hideSecret, skipChartNameDir, skipTemplatesDir bool) ([]*release.Hook, *bytes.Buffer, string, error) {
var hs []*release.Hook
b := bytes.NewBuffer(nil)
@ -336,11 +375,12 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values common.Values,
if outputDir == "" {
fmt.Fprintf(b, "---\n# Source: %s\n%s\n", crd.Filename, string(crd.File.Data[:]))
} else {
err = writeToFile(outputDir, crd.Filename, string(crd.File.Data[:]), fileWritten[crd.Filename])
transformedName := TransformManifestPath(crd.Filename, skipChartNameDir, skipTemplatesDir)
err = writeToFile(outputDir, transformedName, string(crd.File.Data[:]), fileWritten[transformedName])
if err != nil {
return hs, b, "", err
}
fileWritten[crd.Filename] = true
fileWritten[transformedName] = true
}
}
}
@ -361,11 +401,12 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values common.Values,
// output dir is only used by `helm template`. In the next major
// release, we should move this logic to template only as it is not
// used by install or upgrade
err = writeToFile(newDir, m.Name, m.Content, fileWritten[m.Name])
transformedName := TransformManifestPath(m.Name, skipChartNameDir, skipTemplatesDir)
err = writeToFile(newDir, transformedName, m.Content, fileWritten[transformedName])
if err != nil {
return hs, b, "", err
}
fileWritten[m.Name] = true
fileWritten[transformedName] = true
}
}

@ -799,7 +799,7 @@ func TestRenderResources_PostRenderer_Success(t *testing.T) {
hooks, buf, notes, err := cfg.renderResources(
ch, values, "test-release", "", false, false, false,
mockPR, false, false, false,
mockPR, false, false, false, false, false,
)
assert.NoError(t, err)
@ -842,7 +842,7 @@ func TestRenderResources_PostRenderer_Error(t *testing.T) {
_, _, _, err := cfg.renderResources(
ch, values, "test-release", "", false, false, false,
mockPR, false, false, false,
mockPR, false, false, false, false, false,
)
assert.Error(t, err)
@ -870,7 +870,7 @@ func TestRenderResources_PostRenderer_MergeError(t *testing.T) {
_, _, _, err := cfg.renderResources(
ch, values, "test-release", "", false, false, false,
mockPR, false, false, false,
mockPR, false, false, false, false, false,
)
assert.Error(t, err)
@ -892,7 +892,7 @@ func TestRenderResources_PostRenderer_SplitError(t *testing.T) {
_, _, _, err := cfg.renderResources(
ch, values, "test-release", "", false, false, false,
mockPR, false, false, false,
mockPR, false, false, false, false, false,
)
assert.Error(t, err)
@ -913,7 +913,7 @@ func TestRenderResources_PostRenderer_Integration(t *testing.T) {
hooks, buf, notes, err := cfg.renderResources(
ch, values, "test-release", "", false, false, false,
mockPR, false, false, false,
mockPR, false, false, false, false, false,
)
assert.NoError(t, err)
@ -949,7 +949,7 @@ func TestRenderResources_NoPostRenderer(t *testing.T) {
hooks, buf, notes, err := cfg.renderResources(
ch, values, "test-release", "", false, false, false,
nil, false, false, false,
nil, false, false, false, false, false,
)
assert.NoError(t, err)
@ -974,3 +974,98 @@ func TestInteractWithServer(t *testing.T) {
assert.False(t, interactWithServer(DryRunClient))
assert.True(t, interactWithServer(DryRunServer))
}
func TestTransformManifestPath(t *testing.T) {
tests := []struct {
name string
input string
skipChartNameDir bool
skipTemplatesDir bool
expected string
}{
{
name: "no transformation",
input: "mychart/templates/deployment.yaml",
skipChartNameDir: false,
skipTemplatesDir: false,
expected: "mychart/templates/deployment.yaml",
},
{
name: "skip chart name only",
input: "mychart/templates/deployment.yaml",
skipChartNameDir: true,
skipTemplatesDir: false,
expected: "templates/deployment.yaml",
},
{
name: "skip templates dir only",
input: "mychart/templates/deployment.yaml",
skipChartNameDir: false,
skipTemplatesDir: true,
expected: "mychart/deployment.yaml",
},
{
name: "skip both chart name and templates dir",
input: "mychart/templates/deployment.yaml",
skipChartNameDir: true,
skipTemplatesDir: true,
expected: "deployment.yaml",
},
{
name: "subchart path - skip chart name",
input: "mychart/charts/subchart/templates/deployment.yaml",
skipChartNameDir: true,
skipTemplatesDir: false,
expected: "charts/subchart/templates/deployment.yaml",
},
{
name: "subchart path - skip templates",
input: "mychart/charts/subchart/templates/deployment.yaml",
skipChartNameDir: false,
skipTemplatesDir: true,
expected: "mychart/charts/subchart/deployment.yaml",
},
{
name: "subchart path - skip both",
input: "mychart/charts/subchart/templates/deployment.yaml",
skipChartNameDir: true,
skipTemplatesDir: true,
expected: "charts/subchart/deployment.yaml",
},
{
name: "crds path - skip chart name",
input: "mychart/crds/crd.yaml",
skipChartNameDir: true,
skipTemplatesDir: false,
expected: "crds/crd.yaml",
},
{
name: "crds path - skip templates (no effect)",
input: "mychart/crds/crd.yaml",
skipChartNameDir: false,
skipTemplatesDir: true,
expected: "mychart/crds/crd.yaml",
},
{
name: "single filename - skip both returns original",
input: "deployment.yaml",
skipChartNameDir: true,
skipTemplatesDir: true,
expected: "deployment.yaml",
},
{
name: "empty string",
input: "",
skipChartNameDir: true,
skipTemplatesDir: true,
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := TransformManifestPath(tt.input, tt.skipChartNameDir, tt.skipTemplatesDir)
assert.Equal(t, tt.expected, result)
})
}
}

@ -127,6 +127,12 @@ type Install struct {
// Used by helm template to add the release as part of OutputDir path
// OutputDir/<ReleaseName>
UseReleaseName bool
// SkipChartNameDir skips adding the chart name directory when writing to OutputDir
// When true: OutputDir/templates/file.yaml instead of OutputDir/chart-name/templates/file.yaml
SkipChartNameDir bool
// SkipTemplatesDir skips adding the "templates" subdirectory when writing to OutputDir
// When true: OutputDir/chart-name/file.yaml instead of OutputDir/chart-name/templates/file.yaml
SkipTemplatesDir bool
// TakeOwnership will ignore the check for helm annotations and take ownership of the resources.
TakeOwnership bool
PostRenderer postrenderer.PostRenderer
@ -355,7 +361,7 @@ func (i *Install) RunWithContext(ctx context.Context, ch ci.Charter, vals map[st
rel := i.createRelease(chrt, vals, i.Labels)
var manifestDoc *bytes.Buffer
rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, interactWithServer(i.DryRunStrategy), i.EnableDNS, i.HideSecret)
rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, interactWithServer(i.DryRunStrategy), i.EnableDNS, i.HideSecret, i.SkipChartNameDir, i.SkipTemplatesDir)
// Even for errors, attach this if available
if manifestDoc != nil {
rel.Manifest = manifestDoc.String()

@ -296,7 +296,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chartv2.Chart, vals map[str
return nil, nil, false, err
}
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, interactWithServer(u.DryRunStrategy), u.EnableDNS, u.HideSecret)
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, interactWithServer(u.DryRunStrategy), u.EnableDNS, u.HideSecret, false, false)
if err != nil {
return nil, nil, false, err
}

@ -132,12 +132,13 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
if client.UseReleaseName {
newDir = filepath.Join(client.OutputDir, client.ReleaseName)
}
_, err := os.Stat(filepath.Join(newDir, m.Path))
transformedPath := action.TransformManifestPath(m.Path, client.SkipChartNameDir, client.SkipTemplatesDir)
_, err := os.Stat(filepath.Join(newDir, transformedPath))
if err == nil {
fileWritten[m.Path] = true
fileWritten[transformedPath] = true
}
err = writeToFile(newDir, m.Path, m.Manifest, fileWritten[m.Path])
err = writeToFile(newDir, transformedPath, m.Manifest, fileWritten[transformedPath])
if err != nil {
return err
}
@ -214,6 +215,8 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.StringVar(&kubeVersion, "kube-version", "", "Kubernetes version used for Capabilities.KubeVersion")
f.StringSliceVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions (multiple can be specified)")
f.BoolVar(&client.UseReleaseName, "release-name", false, "use release name in the output-dir path.")
f.BoolVar(&client.SkipChartNameDir, "skip-chart-dir", false, "skip adding the chart name directory when writing to output-dir")
f.BoolVar(&client.SkipTemplatesDir, "skip-templates-dir", false, "skip adding the templates subdirectory when writing to output-dir")
f.String(
"dry-run",
"client",
@ -274,4 +277,4 @@ func ensureDirectoryForFile(file string) error {
}
return os.MkdirAll(baseDir, 0755)
}
}
Loading…
Cancel
Save