Files
warewulf/internal/app/wwctl/overlay/show/main.go
Jonathon Anderson 2bad789f4f Fix newline handling in file, softlink, and ImportLink template functions
Use state-based routing instead of sentinel strings, so whitespace-trimming
syntax (e.g. `{{- file "name" -}}`) correctly creates all named files and
symlinks.

Fixes: #2118

Signed-off-by: Jonathon Anderson <janderson@ciq.com>
Co-authored-by: Christian Goll <cgoll@suse.com>
Signed-off-by: Jonathon Anderson <janderson@ciq.com>
2026-04-17 22:51:10 -06:00

126 lines
3.5 KiB
Go

package show
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/spf13/cobra"
"github.com/warewulf/warewulf/internal/pkg/node"
"github.com/warewulf/warewulf/internal/pkg/overlay"
"github.com/warewulf/warewulf/internal/pkg/util"
"github.com/warewulf/warewulf/internal/pkg/wwlog"
)
func CobraRunE(cmd *cobra.Command, args []string) error {
overlayName := args[0]
fileName := args[1]
overlay_, err := overlay.Get(overlayName)
if err != nil {
return err
}
overlayFile := overlay_.File(fileName)
if NodeName == "" {
// No node specified: show the raw template source without rendering.
if !util.IsFile(overlayFile) {
return fmt.Errorf("%s: %s not found", overlayName, overlayFile)
}
f, err := os.ReadFile(overlayFile)
if err != nil {
return err
}
wwlog.Output("%s", string(f))
} else {
// Node specified: render the template for the given node and show output.
// If the caller gave the output filename (without .ww), find the template.
if !util.IsFile(overlayFile) {
possibleFile := fmt.Sprintf("%s.ww", overlayFile)
if filepath.Ext(overlayFile) != ".ww" && util.IsFile(possibleFile) {
wwlog.Debug("found overlay template: %s", possibleFile)
overlayFile = possibleFile
} else {
return fmt.Errorf("%s: %s not found", overlayName, overlayFile)
}
}
if filepath.Ext(overlayFile) != ".ww" {
wwlog.Warn("%s lacks the '.ww' suffix, will not be rendered in an overlay", fileName)
}
nodeDB, err := node.New()
if err != nil {
return err
}
nodeConf, err := nodeDB.GetNode(NodeName)
if err == node.ErrNotFound {
// Unknown node name: fall back to the local hostname so operators can
// preview templates on the warewulf server itself.
hostName, err := os.Hostname()
if err != nil {
return fmt.Errorf("could not get host name: %s", err)
}
nodeConf = node.NewNode(hostName)
nodeConf.ClusterName = hostName
}
var allNodes []node.Node
allNodes, err = nodeDB.FindAllNodes()
if err != nil {
return err
}
tstruct, err := overlay.InitStruct(overlayName, nodeConf, allNodes)
if err != nil {
return err
}
tstruct.BuildSource = overlayFile
rendered, err := overlay.RenderTemplate(overlayFile, tstruct)
if err != nil {
return err
}
if !rendered.WriteFile {
// abort() was called in the template: nothing would be written to disk.
// Still show any content rendered before the abort() call for debugging.
if !Quiet {
wwlog.Info("backupFile: %v", rendered.BackupFile)
wwlog.Info("writeFile: %v", rendered.WriteFile)
}
wwlog.Output("%s", rendered.Files[0].Buffer.String())
if !Quiet {
wwlog.Info("Aborted")
}
return nil
}
for _, f := range rendered.Files {
// The default slot (Name == "") holds pre-file() content or a symlink.
// When named files are also present, non-symlink content is discarded
// for disk output; skip it here too so that show output matches what
// is actually written to disk. A default symlink is always shown.
if f.Name == "" && len(rendered.Files) > 1 && !f.IsSymlink {
continue
}
// The default slot's display name is the template filename with .ww stripped.
displayName := f.Name
if displayName == "" {
displayName = strings.TrimSuffix(fileName, ".ww")
}
if !Quiet {
wwlog.Info("backupFile: %v", rendered.BackupFile)
wwlog.Info("writeFile: %v", rendered.WriteFile)
wwlog.Info("Filename: %s", displayName)
if f.IsSymlink {
wwlog.Info("Symlink: %s", f.Target)
}
}
if !f.IsSymlink {
wwlog.Output("%s", f.Buffer.String())
}
}
}
return nil
}