Merge pull request #467 from mslacken/single-Overlay

enable overlay rendering
This commit is contained in:
Christian Goll
2022-06-24 09:50:14 +02:00
committed by GitHub
11 changed files with 326 additions and 143 deletions

View File

@@ -1,6 +1,7 @@
package chmod
import (
"github.com/hpcng/warewulf/internal/pkg/overlay"
"github.com/spf13/cobra"
)
@@ -13,6 +14,13 @@ var (
Example: "wwctl overlay chmod default /etc/hostname.ww 0660",
RunE: CobraRunE,
Args: cobra.ExactArgs(3),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
list, _ := overlay.FindOverlays()
return list, cobra.ShellCompDirectiveNoFileComp
},
}
)

View File

@@ -1,6 +1,9 @@
package chown
import "github.com/spf13/cobra"
import (
"github.com/hpcng/warewulf/internal/pkg/overlay"
"github.com/spf13/cobra"
)
var (
baseCmd = &cobra.Command{
@@ -10,6 +13,13 @@ var (
Long: "This command changes the ownership of a FILE within the system or runtime OVERLAY_NAME\nto the user specified by UID. Optionally, it will also change group ownership to GID.",
RunE: CobraRunE,
Args: cobra.RangeArgs(3, 4),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
list, _ := overlay.FindOverlays()
return list, cobra.ShellCompDirectiveNoFileComp
},
}
)

View File

@@ -1,6 +1,7 @@
package delete
import (
"github.com/hpcng/warewulf/internal/pkg/overlay"
"github.com/spf13/cobra"
)
@@ -13,6 +14,13 @@ var (
RunE: CobraRunE,
Args: cobra.RangeArgs(1, 2),
Aliases: []string{"rm", "del"},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
list, _ := overlay.FindOverlays()
return list, cobra.ShellCompDirectiveNoFileComp
},
}
Force bool
Parents bool

View File

@@ -1,6 +1,7 @@
package edit
import (
"github.com/hpcng/warewulf/internal/pkg/overlay"
"github.com/spf13/cobra"
)
@@ -12,6 +13,13 @@ var (
Long: "This command will open the FILE for editing or create a new file within the\nOVERLAY_NAME. Note: files created with a '.ww' suffix will always be\nparsed as Warewulf template files, and the suffix will be removed automatically.",
RunE: CobraRunE,
Args: cobra.ExactArgs(2),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
list, _ := overlay.FindOverlays()
return list, cobra.ShellCompDirectiveNoFileComp
},
}
ListFiles bool
CreateDirs bool

View File

@@ -1,6 +1,9 @@
package imprt
import "github.com/spf13/cobra"
import (
"github.com/hpcng/warewulf/internal/pkg/overlay"
"github.com/spf13/cobra"
)
var (
baseCmd = &cobra.Command{
@@ -11,6 +14,13 @@ var (
RunE: CobraRunE,
Args: cobra.RangeArgs(2, 3),
Aliases: []string{"cp"},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
list, _ := overlay.FindOverlays()
return list, cobra.ShellCompDirectiveNoFileComp
},
}
PermMode int32
NoOverlayUpdate bool

View File

@@ -1,6 +1,7 @@
package list
import (
"github.com/hpcng/warewulf/internal/pkg/overlay"
"github.com/spf13/cobra"
)
@@ -14,6 +15,13 @@ var (
Args: cobra.MinimumNArgs(0),
Aliases: []string{"ls"},
ValidArgs: []string{"system", "runtime"},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
list, _ := overlay.FindOverlays()
return list, cobra.ShellCompDirectiveNoFileComp
},
}
ListContents bool
ListLong bool

View File

@@ -17,9 +17,9 @@ import (
var (
baseCmd = &cobra.Command{
DisableFlagsInUseLine: true,
Use: "overlay COMMAND [OPTIONS]",
Short: "Warewulf Overlay Management",
Long: "Management interface for Warewulf overlays",
Use: "overlay COMMAND [OPTIONS]",
Short: "Warewulf Overlay Management",
Long: "Management interface for Warewulf overlays",
}
)
@@ -34,6 +34,7 @@ func init() {
baseCmd.AddCommand(imprt.GetCommand())
baseCmd.AddCommand(chmod.GetCommand())
baseCmd.AddCommand(chown.GetCommand())
}
// GetRootCommand returns the root cobra.Command for the application.

View File

@@ -1,11 +1,17 @@
package show
import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/overlay"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
@@ -17,7 +23,6 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
overlayName := args[0]
fileName := args[1]
overlaySourceDir = overlay.OverlaySourceDir(overlayName)
if !util.IsDir(overlaySourceDir) {
@@ -32,13 +37,77 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
os.Exit(1)
}
f, err := ioutil.ReadFile(overlayFile)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not read file: %s\n", err)
os.Exit(1)
if NodeName == "" {
f, err := ioutil.ReadFile(overlayFile)
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not read file: %s\n", err)
os.Exit(1)
}
fmt.Print(string(f))
} else {
var host node.NodeInfo
nodeDB, err := node.New()
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err)
os.Exit(1)
}
nodes, err := nodeDB.FindAllNodes()
if err != nil {
wwlog.Printf(wwlog.ERROR, "Could not get node list: %s\n", err)
os.Exit(1)
}
node := node.FilterByName(nodes, []string{NodeName})
if len(node) != 1 {
wwlog.Printf(wwlog.ERROR, "%v does not identify a single node\n", NodeName)
os.Exit(1)
}
host = node[0]
if !util.IsFile(args[0]) {
wwlog.Printf(wwlog.ERROR, "%s is not a file\n", args[0])
}
tstruct := overlay.InitStruct(host)
tstruct.BuildSource = args[0]
buffer, backupFile, writeFile, err := overlay.RenderTemplateFile(overlayFile, tstruct)
if err != nil {
return err
}
if filepath.Ext(args[0]) != ".ww" {
wwlog.Printf(wwlog.WARN, "%s has not the '.ww' so wont be rendered if in overlay\n", args[0])
}
var outBuffer bytes.Buffer
// search for magic file name comment
bufferScanner := bufio.NewScanner(bytes.NewReader(buffer.Bytes()))
bufferScanner.Split(overlay.ScanLines)
reg := regexp.MustCompile(`.*{{\s*/\*\s*file\s*["'](.*)["']\s*\*/\s*}}.*`)
foundFileComment := false
destFileName := strings.TrimSuffix(args[0], ".ww")
for bufferScanner.Scan() {
line := bufferScanner.Text()
filenameFromTemplate := reg.FindAllStringSubmatch(line, -1)
if len(filenameFromTemplate) != 0 {
wwlog.Printf(wwlog.DEBUG, "Found multifile comment, new filename %s\n", filenameFromTemplate[0][1])
if foundFileComment {
if !Quiet {
wwlog.Printf(wwlog.INFO, "backupFile: %v\nwriteFile: %v\n", backupFile, writeFile)
wwlog.Printf(wwlog.INFO, "Filename: %s\n\n", destFileName)
}
wwlog.Printf(wwlog.INFO, "%s", outBuffer.String())
outBuffer.Reset()
}
destFileName = filenameFromTemplate[0][1]
foundFileComment = true
} else {
_, _ = outBuffer.WriteString(line)
}
}
if !Quiet {
wwlog.Printf(wwlog.INFO, "backupFile: %v\nwriteFile: %v\n", backupFile, writeFile)
wwlog.Printf(wwlog.INFO, "Filename: %s\n\n", destFileName)
}
wwlog.Printf(wwlog.INFO, "%s", outBuffer.String())
}
fmt.Print(string(f))
return nil
}

View File

@@ -1,6 +1,10 @@
package show
import (
"log"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/overlay"
"github.com/spf13/cobra"
)
@@ -13,10 +17,32 @@ var (
RunE: CobraRunE,
Aliases: []string{"cat"},
Args: cobra.ExactArgs(2),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp
}
list, _ := overlay.FindOverlays()
return list, cobra.ShellCompDirectiveNoFileComp
},
}
NodeName string
Quiet bool
)
func init() {
baseCmd.PersistentFlags().StringVarP(&NodeName, "render", "r", "", "node used for the variables in the template")
if err := baseCmd.RegisterFlagCompletionFunc("render", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
nodeDB, _ := node.New()
nodes, _ := nodeDB.FindAllNodes()
var node_names []string
for _, node := range nodes {
node_names = append(node_names, node.Id.Get())
}
return node_names, cobra.ShellCompDirectiveNoFileComp
}); err != nil {
log.Println(err)
}
baseCmd.PersistentFlags().BoolVarP(&Quiet, "quiet", "q", false, "do not print information if multiple, backup files are written")
}
// GetRootCommand returns the root cobra.Command for the application.

View File

@@ -1,8 +1,14 @@
package overlay
import (
"net"
"os"
"strconv"
"time"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
)
/*
@@ -38,3 +44,105 @@ type TemplateStruct struct {
Nfs warewulfconf.NfsConf
Warewulf warewulfconf.WarewulfConf
}
/*
Initialize an TemplateStruct with the given node.NodeInfo
*/
func InitStruct(nodeInfo node.NodeInfo) TemplateStruct {
var tstruct TemplateStruct
controller, err := warewulfconf.New()
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
os.Exit(1)
}
nodeDB, err := node.New()
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
os.Exit(1)
}
allNodes, err := nodeDB.FindAllNodes()
if err != nil {
wwlog.Printf(wwlog.ERROR, "%s\n", err)
os.Exit(1)
}
tstruct.Kernel = new(node.KernelConf)
tstruct.Ipmi = new(node.IpmiConf)
tstruct.Id = nodeInfo.Id.Get()
tstruct.Hostname = nodeInfo.Id.Get()
tstruct.Id = nodeInfo.Id.Get()
tstruct.Hostname = nodeInfo.Id.Get()
tstruct.ClusterName = nodeInfo.ClusterName.Get()
tstruct.Container = nodeInfo.ContainerName.Get()
tstruct.Kernel.Version = nodeInfo.Kernel.Override.Get()
tstruct.Kernel.Override = nodeInfo.Kernel.Override.Get()
tstruct.Kernel.Args = nodeInfo.Kernel.Args.Get()
tstruct.Init = nodeInfo.Init.Get()
tstruct.Root = nodeInfo.Root.Get()
tstruct.Ipmi.Ipaddr = nodeInfo.Ipmi.Ipaddr.Get()
tstruct.Ipmi.Netmask = nodeInfo.Ipmi.Netmask.Get()
tstruct.Ipmi.Port = nodeInfo.Ipmi.Port.Get()
tstruct.Ipmi.Gateway = nodeInfo.Ipmi.Gateway.Get()
tstruct.Ipmi.UserName = nodeInfo.Ipmi.UserName.Get()
tstruct.Ipmi.Password = nodeInfo.Ipmi.Password.Get()
tstruct.Ipmi.Interface = nodeInfo.Ipmi.Interface.Get()
tstruct.Ipmi.Write = nodeInfo.Ipmi.Write.Get()
tstruct.RuntimeOverlay = nodeInfo.RuntimeOverlay.Print()
tstruct.SystemOverlay = nodeInfo.SystemOverlay.Print()
tstruct.NetDevs = make(map[string]*node.NetDevs)
tstruct.Keys = make(map[string]string)
tstruct.Tags = make(map[string]string)
for devname, netdev := range nodeInfo.NetDevs {
var nd node.NetDevs
tstruct.NetDevs[devname] = &nd
tstruct.NetDevs[devname].Device = netdev.Device.Get()
tstruct.NetDevs[devname].Hwaddr = netdev.Hwaddr.Get()
tstruct.NetDevs[devname].Ipaddr = netdev.Ipaddr.Get()
tstruct.NetDevs[devname].Netmask = netdev.Netmask.Get()
tstruct.NetDevs[devname].Gateway = netdev.Gateway.Get()
tstruct.NetDevs[devname].Type = netdev.Type.Get()
tstruct.NetDevs[devname].OnBoot = netdev.OnBoot.Get()
tstruct.NetDevs[devname].Primary = netdev.Primary.Get()
mask := net.IPMask(net.ParseIP(netdev.Netmask.Get()).To4())
ipaddr := net.ParseIP(netdev.Ipaddr.Get()).To4()
netaddr := net.IPNet{IP: ipaddr, Mask: mask}
netPrefix, _ := net.IPMask(net.ParseIP(netdev.Netmask.Get()).To4()).Size()
tstruct.NetDevs[devname].Prefix = strconv.Itoa(netPrefix)
tstruct.NetDevs[devname].IpCIDR = netaddr.String()
tstruct.NetDevs[devname].Ipaddr6 = netdev.Ipaddr6.Get()
tstruct.NetDevs[devname].Tags = make(map[string]string)
for key, value := range netdev.Tags {
tstruct.NetDevs[devname].Tags[key] = value.Get()
}
}
// Backwards compatibility for templates using "Keys"
for keyname, key := range nodeInfo.Tags {
tstruct.Keys[keyname] = key.Get()
}
for keyname, key := range nodeInfo.Tags {
tstruct.Tags[keyname] = key.Get()
}
tstruct.AllNodes = allNodes
tstruct.Nfs = *controller.Nfs
tstruct.Dhcp = *controller.Dhcp
tstruct.Warewulf = *controller.Warewulf
tstruct.Ipaddr = controller.Ipaddr
tstruct.Ipaddr6 = controller.Ipaddr6
tstruct.Netmask = controller.Netmask
tstruct.Network = controller.Network
netaddrStruct := net.IPNet{IP: net.ParseIP(controller.Network), Mask: net.IPMask(net.ParseIP(controller.Netmask))}
tstruct.NetworkCIDR = netaddrStruct.String()
if controller.Ipaddr6 != "" {
tstruct.Ipv6 = true
} else {
tstruct.Ipv6 = false
}
hostname, _ := os.Hostname()
tstruct.BuildHost = hostname
dt := time.Now()
tstruct.BuildTime = dt.Format("01-02-2006 15:04:05 MST")
tstruct.BuildTimeUnix = strconv.FormatInt(dt.Unix(), 10)
return tstruct
}

View File

@@ -6,19 +6,15 @@ import (
"fmt"
"io/fs"
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
"text/template"
"time"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/util"
"github.com/hpcng/warewulf/internal/pkg/warewulfconf"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/pkg/errors"
)
@@ -199,106 +195,17 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
if !util.IsDir(outputDir) {
return errors.Errorf("output must a be a directory: %s", outputDir)
}
controller, err := warewulfconf.New()
if err != nil {
wwlog.ErrorExc(err, "")
os.Exit(1)
}
nodeDB, err := node.New()
if err != nil {
wwlog.ErrorExc(err, "")
os.Exit(1)
}
allNodes, err := nodeDB.FindAllNodes()
if err != nil {
wwlog.ErrorExc(err, "")
os.Exit(1)
}
if !util.ValidString(strings.Join(overlayNames, ""), "^[a-zA-Z0-9-._:]+$") {
return errors.Errorf("overlay names contains illegal characters: %v", overlayNames)
}
wwlog.Verbose("Processing node/overlay: %s/%s", nodeInfo.Id.Get(), strings.Join(overlayNames, "-"))
var tstruct TemplateStruct
tstruct.Kernel = new(node.KernelConf)
tstruct.Ipmi = new(node.IpmiConf)
tstruct.Id = nodeInfo.Id.Get()
tstruct.Hostname = nodeInfo.Id.Get()
tstruct.ClusterName = nodeInfo.ClusterName.Get()
tstruct.Container = nodeInfo.ContainerName.Get()
tstruct.Kernel.Version = nodeInfo.Kernel.Override.Get()
tstruct.Kernel.Override = nodeInfo.Kernel.Override.Get()
tstruct.Kernel.Args = nodeInfo.Kernel.Args.Get()
tstruct.Init = nodeInfo.Init.Get()
tstruct.Root = nodeInfo.Root.Get()
tstruct.Ipmi.Ipaddr = nodeInfo.Ipmi.Ipaddr.Get()
tstruct.Ipmi.Netmask = nodeInfo.Ipmi.Netmask.Get()
tstruct.Ipmi.Port = nodeInfo.Ipmi.Port.Get()
tstruct.Ipmi.Gateway = nodeInfo.Ipmi.Gateway.Get()
tstruct.Ipmi.UserName = nodeInfo.Ipmi.UserName.Get()
tstruct.Ipmi.Password = nodeInfo.Ipmi.Password.Get()
tstruct.Ipmi.Interface = nodeInfo.Ipmi.Interface.Get()
tstruct.Ipmi.Write = nodeInfo.Ipmi.Write.Get()
tstruct.RuntimeOverlay = nodeInfo.RuntimeOverlay.Print()
tstruct.SystemOverlay = nodeInfo.SystemOverlay.Print()
tstruct.NetDevs = make(map[string]*node.NetDevs)
tstruct.Keys = make(map[string]string)
tstruct.Tags = make(map[string]string)
for devname, netdev := range nodeInfo.NetDevs {
var nd node.NetDevs
tstruct.NetDevs[devname] = &nd
tstruct.NetDevs[devname].Device = netdev.Device.Get()
tstruct.NetDevs[devname].Hwaddr = netdev.Hwaddr.Get()
tstruct.NetDevs[devname].Ipaddr = netdev.Ipaddr.Get()
tstruct.NetDevs[devname].Netmask = netdev.Netmask.Get()
tstruct.NetDevs[devname].Gateway = netdev.Gateway.Get()
tstruct.NetDevs[devname].Type = netdev.Type.Get()
tstruct.NetDevs[devname].OnBoot = netdev.OnBoot.Get()
tstruct.NetDevs[devname].Primary = netdev.Primary.Get()
mask := net.IPMask(net.ParseIP(netdev.Netmask.Get()).To4())
ipaddr := net.ParseIP(netdev.Ipaddr.Get()).To4()
netaddr := net.IPNet{IP: ipaddr, Mask: mask}
netPrefix, _ := net.IPMask(net.ParseIP(netdev.Netmask.Get()).To4()).Size()
tstruct.NetDevs[devname].Prefix = strconv.Itoa(netPrefix)
tstruct.NetDevs[devname].IpCIDR = netaddr.String()
tstruct.NetDevs[devname].Ipaddr6 = netdev.Ipaddr6.Get()
tstruct.NetDevs[devname].Tags = make(map[string]string)
for key, value := range netdev.Tags {
tstruct.NetDevs[devname].Tags[key] = value.Get()
}
}
// Backwards compatibility for templates using "Keys"
for keyname, key := range nodeInfo.Tags {
tstruct.Keys[keyname] = key.Get()
}
for keyname, key := range nodeInfo.Tags {
tstruct.Tags[keyname] = key.Get()
}
tstruct.AllNodes = allNodes
tstruct.Nfs = *controller.Nfs
tstruct.Dhcp = *controller.Dhcp
tstruct.Warewulf = *controller.Warewulf
tstruct.Ipaddr = controller.Ipaddr
tstruct.Ipaddr6 = controller.Ipaddr6
tstruct.Netmask = controller.Netmask
tstruct.Network = controller.Network
netaddrStruct := net.IPNet{IP: net.ParseIP(controller.Network), Mask: net.IPMask(net.ParseIP(controller.Netmask))}
tstruct.NetworkCIDR = netaddrStruct.String()
if controller.Ipaddr6 != "" {
tstruct.Ipv6 = true
} else {
tstruct.Ipv6 = false
}
hostname, _ := os.Hostname()
tstruct.BuildHost = hostname
dt := time.Now()
tstruct.BuildTime = dt.Format("01-02-2006 15:04:05 MST")
tstruct.BuildTimeUnix = strconv.FormatInt(dt.Unix(), 10)
wwlog.Printf(wwlog.VERBOSE, "Processing node/overlay: %s/%s\n", nodeInfo.Id.Get(), strings.Join(overlayNames, "-"))
for _, overlayName := range overlayNames {
wwlog.Verbose("Building overlay %s for node %s in %s", overlayName, nodeInfo.Id.Get(), outputDir)
overlaySourceDir := OverlaySourceDir(overlayName)
wwlog.Debug("Starting to build overlay %s\nChanging directory to OverlayDir: %s", overlayName, overlaySourceDir)
err = os.Chdir(overlaySourceDir)
wwlog.Printf(wwlog.DEBUG, "Starting to build overlay %s\nChanging directory to OverlayDir: %s\n", overlayName, overlaySourceDir)
err := os.Chdir(overlaySourceDir)
if err != nil {
return errors.Wrap(err, "could not change directory to overlay dir")
}
@@ -330,48 +237,21 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
wwlog.Debug("Created directory in overlay: %s", location)
} else if filepath.Ext(location) == ".ww" {
tstruct := InitStruct(nodeInfo)
tstruct.BuildSource = path.Join(overlaySourceDir, location)
wwlog.Verbose("Evaluating overlay template file: %s", location)
destFile := strings.TrimSuffix(location, ".ww")
backupFile := true
writeFile := true
tmpl, err := template.New(path.Base(location)).Option("missingkey=default").Funcs(template.FuncMap{
// TODO: Fix for missingkey=zero
"Include": templateFileInclude,
"IncludeFrom": templateContainerFileInclude,
"IncludeBlock": templateFileBlock,
"inc": func(i int) int { return i + 1 },
"dec": func(i int) int { return i - 1 },
"file": func(str string) string { return fmt.Sprintf("{{ /* file \"%s\" */ }}", str) },
"abort": func() string {
wwlog.Debug("abort file called in %s", location)
writeFile = false
return ""
},
"nobackup": func() string {
wwlog.Debug("not backup for %s", location)
backupFile = false
return ""
},
"split": func(s string, d string) []string {
return strings.Split(s, d)
},
// }).ParseGlob(path.Join(OverlayDir, destFile+".ww*"))
}).ParseGlob(location)
buffer, backupFile, writeFile, err := RenderTemplateFile(location, tstruct)
if err != nil {
return errors.Wrap(err, "could not parse template "+location)
}
var buffer bytes.Buffer
err = tmpl.Execute(&buffer, tstruct)
if err != nil {
return errors.Wrap(err, "could not execute template")
return errors.Wrap(err, fmt.Sprintf("Failed to render template %s", location))
}
if writeFile {
destFileName := destFile
var fileBuffer bytes.Buffer
// search for magic file name comment
fileScanner := bufio.NewScanner(bytes.NewReader(buffer.Bytes()))
fileScanner.Split(scanLines)
fileScanner.Split(ScanLines)
reg := regexp.MustCompile(`.*{{\s*/\*\s*file\s*["'](.*)["']\s*\*/\s*}}.*`)
foundFileComment := false
for fileScanner.Scan() {
@@ -475,8 +355,55 @@ func carefulWriteBuffer(destFile string, buffer bytes.Buffer, backupFile bool, p
return err
}
/*
Parses the template with the given filename, variables must be in data. Returns the
parsed template as bytes.Buffer, and the bool variables for backupFile and writeFile.
If something goes wrong an error is returned.
*/
func RenderTemplateFile(fileName string, data TemplateStruct) (
buffer bytes.Buffer,
backupFile bool,
writeFile bool,
err error) {
backupFile = true
writeFile = true
tmpl, err := template.New(path.Base(fileName)).Option("missingkey=default").Funcs(template.FuncMap{
// TODO: Fix for missingkey=zero
"Include": templateFileInclude,
"IncludeFrom": templateContainerFileInclude,
"IncludeBlock": templateFileBlock,
"inc": func(i int) int { return i + 1 },
"dec": func(i int) int { return i - 1 },
"file": func(str string) string { return fmt.Sprintf("{{ /* file \"%s\" */ }}", str) },
"abort": func() string {
wwlog.Printf(wwlog.DEBUG, "abort file called in %s\n", fileName)
writeFile = false
return ""
},
"nobackup": func() string {
wwlog.Printf(wwlog.DEBUG, "not backup for %s\n", fileName)
backupFile = false
return ""
},
"split": func(s string, d string) []string {
return strings.Split(s, d)
},
// }).ParseGlob(path.Join(OverlayDir, destFile+".ww*"))
}).ParseGlob(fileName)
if err != nil {
err = errors.Wrap(err, "could not parse template "+fileName)
return
}
err = tmpl.Execute(&buffer, data)
if err != nil {
err = errors.Wrap(err, "could not execute template")
return
}
return
}
// Simple version of ScanLines, but include the line break
func scanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}