From 9e5b2d1aa3c203d9b4055dfd97ec2c1446169d63 Mon Sep 17 00:00:00 2001 From: Christian Goll Date: Wed, 25 May 2022 11:41:33 +0200 Subject: [PATCH] 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. --- internal/app/wwctl/overlay/parse/main.go | 83 ++++++++++++++++++++++++ internal/app/wwctl/overlay/parse/root.go | 41 ++++++++++++ internal/app/wwctl/overlay/root.go | 9 ++- internal/pkg/overlay/overlay.go | 13 +++- 4 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 internal/app/wwctl/overlay/parse/main.go create mode 100644 internal/app/wwctl/overlay/parse/root.go diff --git a/internal/app/wwctl/overlay/parse/main.go b/internal/app/wwctl/overlay/parse/main.go new file mode 100644 index 00000000..7d7384b7 --- /dev/null +++ b/internal/app/wwctl/overlay/parse/main.go @@ -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 +} diff --git a/internal/app/wwctl/overlay/parse/root.go b/internal/app/wwctl/overlay/parse/root.go new file mode 100644 index 00000000..e4db0f81 --- /dev/null +++ b/internal/app/wwctl/overlay/parse/root.go @@ -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 +} diff --git a/internal/app/wwctl/overlay/root.go b/internal/app/wwctl/overlay/root.go index 4bb1cd95..00d931c0 100644 --- a/internal/app/wwctl/overlay/root.go +++ b/internal/app/wwctl/overlay/root.go @@ -10,6 +10,7 @@ import ( "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/mkdir" + "github.com/hpcng/warewulf/internal/app/wwctl/overlay/parse" "github.com/hpcng/warewulf/internal/app/wwctl/overlay/show" "github.com/spf13/cobra" ) @@ -17,9 +18,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 +35,8 @@ func init() { baseCmd.AddCommand(imprt.GetCommand()) baseCmd.AddCommand(chmod.GetCommand()) baseCmd.AddCommand(chown.GetCommand()) + baseCmd.AddCommand(parse.GetCommand()) + } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/pkg/overlay/overlay.go b/internal/pkg/overlay/overlay.go index 6a8a15b7..0f55563b 100644 --- a/internal/pkg/overlay/overlay.go +++ b/internal/pkg/overlay/overlay.go @@ -243,12 +243,15 @@ func BuildOverlayIndir(nodeInfo node.NodeInfo, overlayNames []string, outputDir destFile := strings.TrimSuffix(location, ".ww") buffer, backupFile, writeFile, err := RenderTemplateFile(location, tstruct) + if err != nil { + 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() { @@ -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. 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 writeFile = true 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 -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 }