added the wwctl overlay parse command
The `wwctl overlay parse -n NODE FILE` will interpret the given overlay file and print it stdout, via wwlog.PRINTF. The overlay code had to be refactored for this, but hopefully this makes the code a bit more readable. Also the tstruct has not its own initilization code.
This commit is contained in:
83
internal/app/wwctl/overlay/parse/main.go
Normal file
83
internal/app/wwctl/overlay/parse/main.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package parse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||||
|
var host node.NodeInfo
|
||||||
|
if NodeName == "" {
|
||||||
|
host.Kernel = new(node.KernelEntry)
|
||||||
|
host.Ipmi = new(node.IpmiEntry)
|
||||||
|
var idEntry node.Entry
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
idEntry.Set(hostname)
|
||||||
|
host.Id = idEntry
|
||||||
|
} else {
|
||||||
|
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)
|
||||||
|
buffer, backupFile, writeFile, err := overlay.RenderTemplateFile(args[0], 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])
|
||||||
|
}
|
||||||
|
wwlog.Printf(wwlog.INFO, "backupFile: %v\nwriteFile: %v\n", backupFile, writeFile)
|
||||||
|
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 := args[0]
|
||||||
|
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 {
|
||||||
|
wwlog.Printf(wwlog.INFO, "%s", outBuffer.String())
|
||||||
|
outBuffer.Reset()
|
||||||
|
}
|
||||||
|
destFileName = filenameFromTemplate[0][1]
|
||||||
|
wwlog.Printf(wwlog.INFO, "Filename: %s\n\n", destFileName)
|
||||||
|
wwlog.Printf(wwlog.INFO, "%s", outBuffer.String())
|
||||||
|
foundFileComment = true
|
||||||
|
} else {
|
||||||
|
_, _ = outBuffer.WriteString(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wwlog.Printf(wwlog.INFO, "Filename: %s\n\n", destFileName)
|
||||||
|
wwlog.Printf(wwlog.INFO, "%s", outBuffer.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
41
internal/app/wwctl/overlay/parse/root.go
Normal file
41
internal/app/wwctl/overlay/parse/root.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package parse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/hpcng/warewulf/internal/pkg/node"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
baseCmd = &cobra.Command{
|
||||||
|
DisableFlagsInUseLine: true,
|
||||||
|
Use: "parse [OPTIONS] FILE_NAME",
|
||||||
|
Short: "parses interprets the given file as warewulf template",
|
||||||
|
Long: "This command tries to parse a given file as warewulf template, it needs not to be in overlay folder.",
|
||||||
|
RunE: CobraRunE,
|
||||||
|
Aliases: []string{"parse"},
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
}
|
||||||
|
NodeName string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
baseCmd.PersistentFlags().StringVarP(&NodeName, "node", "n", "", "node used for the variables in the template")
|
||||||
|
if err := baseCmd.RegisterFlagCompletionFunc("node", 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRootCommand returns the root cobra.Command for the application.
|
||||||
|
func GetCommand() *cobra.Command {
|
||||||
|
return baseCmd
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/hpcng/warewulf/internal/app/wwctl/overlay/imprt"
|
"github.com/hpcng/warewulf/internal/app/wwctl/overlay/imprt"
|
||||||
"github.com/hpcng/warewulf/internal/app/wwctl/overlay/list"
|
"github.com/hpcng/warewulf/internal/app/wwctl/overlay/list"
|
||||||
"github.com/hpcng/warewulf/internal/app/wwctl/overlay/mkdir"
|
"github.com/hpcng/warewulf/internal/app/wwctl/overlay/mkdir"
|
||||||
|
"github.com/hpcng/warewulf/internal/app/wwctl/overlay/parse"
|
||||||
"github.com/hpcng/warewulf/internal/app/wwctl/overlay/show"
|
"github.com/hpcng/warewulf/internal/app/wwctl/overlay/show"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -17,9 +18,9 @@ import (
|
|||||||
var (
|
var (
|
||||||
baseCmd = &cobra.Command{
|
baseCmd = &cobra.Command{
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
Use: "overlay COMMAND [OPTIONS]",
|
Use: "overlay COMMAND [OPTIONS]",
|
||||||
Short: "Warewulf Overlay Management",
|
Short: "Warewulf Overlay Management",
|
||||||
Long: "Management interface for Warewulf overlays",
|
Long: "Management interface for Warewulf overlays",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,6 +35,8 @@ func init() {
|
|||||||
baseCmd.AddCommand(imprt.GetCommand())
|
baseCmd.AddCommand(imprt.GetCommand())
|
||||||
baseCmd.AddCommand(chmod.GetCommand())
|
baseCmd.AddCommand(chmod.GetCommand())
|
||||||
baseCmd.AddCommand(chown.GetCommand())
|
baseCmd.AddCommand(chown.GetCommand())
|
||||||
|
baseCmd.AddCommand(parse.GetCommand())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRootCommand returns the root cobra.Command for the application.
|
// GetRootCommand returns the root cobra.Command for the application.
|
||||||
|
|||||||
@@ -243,12 +243,15 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir
|
|||||||
destFile := strings.TrimSuffix(location, ".ww")
|
destFile := strings.TrimSuffix(location, ".ww")
|
||||||
|
|
||||||
buffer, backupFile, writeFile, err := RenderTemplateFile(location, tstruct)
|
buffer, backupFile, writeFile, err := RenderTemplateFile(location, tstruct)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, fmt.Sprintf("Failed to render template %s", location))
|
||||||
|
}
|
||||||
if writeFile {
|
if writeFile {
|
||||||
destFileName := destFile
|
destFileName := destFile
|
||||||
var fileBuffer bytes.Buffer
|
var fileBuffer bytes.Buffer
|
||||||
// search for magic file name comment
|
// search for magic file name comment
|
||||||
fileScanner := bufio.NewScanner(bytes.NewReader(buffer.Bytes()))
|
fileScanner := bufio.NewScanner(bytes.NewReader(buffer.Bytes()))
|
||||||
fileScanner.Split(scanLines)
|
fileScanner.Split(ScanLines)
|
||||||
reg := regexp.MustCompile(`.*{{\s*/\*\s*file\s*["'](.*)["']\s*\*/\s*}}.*`)
|
reg := regexp.MustCompile(`.*{{\s*/\*\s*file\s*["'](.*)["']\s*\*/\s*}}.*`)
|
||||||
foundFileComment := false
|
foundFileComment := false
|
||||||
for fileScanner.Scan() {
|
for fileScanner.Scan() {
|
||||||
@@ -346,7 +349,11 @@ Parses the template with the given filename, variables must be in data. Returns
|
|||||||
parsed template as bytes.Buffer, and the bool variables for backupFile and writeFile.
|
parsed template as bytes.Buffer, and the bool variables for backupFile and writeFile.
|
||||||
If something goes wrong an error is returned.
|
If something goes wrong an error is returned.
|
||||||
*/
|
*/
|
||||||
func RenderTemplateFile(fileName string, data TemplateStruct) (buffer bytes.Buffer, backupFile bool, writeFile bool, err error) {
|
func RenderTemplateFile(fileName string, data TemplateStruct) (
|
||||||
|
buffer bytes.Buffer,
|
||||||
|
backupFile bool,
|
||||||
|
writeFile bool,
|
||||||
|
err error) {
|
||||||
backupFile = true
|
backupFile = true
|
||||||
writeFile = true
|
writeFile = true
|
||||||
tmpl, err := template.New(path.Base(fileName)).Option("missingkey=default").Funcs(template.FuncMap{
|
tmpl, err := template.New(path.Base(fileName)).Option("missingkey=default").Funcs(template.FuncMap{
|
||||||
@@ -385,7 +392,7 @@ func RenderTemplateFile(fileName string, data TemplateStruct) (buffer bytes.Buff
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Simple version of ScanLines, but include the line break
|
// 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 {
|
if atEOF && len(data) == 0 {
|
||||||
return 0, nil, nil
|
return 0, nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user