diff --git a/CHANGELOG.md b/CHANGELOG.md index 44fdc00a..0640f122 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Document "known issues." - Add `wwctl --kernelversion` to specify the desired kernel version or path. #1556 - Add `wwctl container kernels` to list discovered kernels from containers. #1556 +- Add possibility to define a softlink target with an overlay template +- Support defining a symlink with an overlay template. #1303 +- New "localtime" overlay to define the system time zone. #1303 ### Changed diff --git a/internal/pkg/overlay/funcmap.go b/internal/pkg/overlay/funcmap.go index a9758d76..0a2e99fb 100644 --- a/internal/pkg/overlay/funcmap.go +++ b/internal/pkg/overlay/funcmap.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path" + "path/filepath" "strings" warewulfconf "github.com/warewulf/warewulf/internal/pkg/config" @@ -122,3 +123,16 @@ func createIgnitionJson(node *node.Node) string { tmpYaml, _ := json.Marshal(&conf) return string(tmpYaml) } + +func importSoftlink(lnk string) string { + target, err := filepath.EvalSymlinks(lnk) + if err != nil { + return "abort" + } + wwlog.Debug("importing softlink pointing to: %s", target) + return softlink(target) +} + +func softlink(target string) string { + return fmt.Sprintf("{{ /* softlink \"%s\" */ }}", target) +} diff --git a/internal/pkg/overlay/overlay.go b/internal/pkg/overlay/overlay.go index 8542b346..bef2edc3 100644 --- a/internal/pkg/overlay/overlay.go +++ b/internal/pkg/overlay/overlay.go @@ -238,13 +238,18 @@ func BuildOverlayIndir(nodeData node.Node, overlayNames []string, outputDir stri // search for magic file name comment fileScanner := bufio.NewScanner(bytes.NewReader(buffer.Bytes())) fileScanner.Split(ScanLines) - reg := regexp.MustCompile(`.*{{\s*/\*\s*file\s*["'](.*)["']\s*\*/\s*}}.*`) + regFile := regexp.MustCompile(`.*{{\s*/\*\s*file\s*["'](.*)["']\s*\*/\s*}}.*`) + regLink := regexp.MustCompile(`.*{{\s*/\*\s*softlink\s*["'](.*)["']\s*\*/\s*}}.*`) foundFileComment := false for fileScanner.Scan() { line := fileScanner.Text() - filenameFromTemplate := reg.FindAllStringSubmatch(line, -1) - if len(filenameFromTemplate) != 0 { - wwlog.Debug("Found multiple comment, new filename %s", filenameFromTemplate[0][1]) + filenameFromTemplate := regFile.FindAllStringSubmatch(line, -1) + softlinkFromTemplate := regLink.FindAllStringSubmatch(line, -1) + if len(softlinkFromTemplate) != 0 { + wwlog.Debug("Creating soft link %s -> %s", destFileName, softlinkFromTemplate[0][1]) + return os.Symlink(softlinkFromTemplate[0][1], path.Join(outputDir, destFileName)) + } else if len(filenameFromTemplate) != 0 { + wwlog.Debug("Writing file %s", filenameFromTemplate[0][1]) if foundFileComment { err = CarefulWriteBuffer(path.Join(outputDir, destFileName), fileBuffer, backupFile, info.Mode()) @@ -371,10 +376,13 @@ func RenderTemplateFile(fileName string, data TemplateStruct) ( "Include": templateFileInclude, "IncludeFrom": templateContainerFileInclude, "IncludeBlock": templateFileBlock, + "ImportLink": importSoftlink, "basename": path.Base, "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) }, + "softlink": softlink, + "readlink": filepath.EvalSymlinks, "IgnitionJson": func() string { str := createIgnitionJson(data.ThisNode) if str != "" { diff --git a/overlays/localtime/internal/localtime_test.go b/overlays/localtime/internal/localtime_test.go new file mode 100644 index 00000000..36557553 --- /dev/null +++ b/overlays/localtime/internal/localtime_test.go @@ -0,0 +1,56 @@ +package localtime + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/warewulf/warewulf/internal/app/wwctl/overlay/show" + "github.com/warewulf/warewulf/internal/pkg/config" + "github.com/warewulf/warewulf/internal/pkg/testenv" + "github.com/warewulf/warewulf/internal/pkg/wwlog" +) + +func Test_localtimeOverlay(t *testing.T) { + env := testenv.New(t) + defer env.RemoveAll(t) + env.ImportFile(t, "etc/warewulf/nodes.conf", "nodes.conf") + assert.NoError(t, config.Get().Read(env.GetPath("etc/warewulf/warewulf.conf"))) + env.ImportFile(t, "var/lib/warewulf/overlays/localtime/rootfs/etc/localtime.ww", "../rootfs/etc/localtime.ww") + + tests := []struct { + name string + args []string + log string + }{ + { + name: "/etc/localtime", + args: []string{"--render", "node1", "localtime", "etc/localtime.ww"}, + log: localtime, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cmd := show.GetCommand() + cmd.SetArgs(tt.args) + stdout := bytes.NewBufferString("") + stderr := bytes.NewBufferString("") + logbuf := bytes.NewBufferString("") + cmd.SetOut(stdout) + cmd.SetErr(stderr) + wwlog.SetLogWriter(logbuf) + err := cmd.Execute() + assert.NoError(t, err) + assert.Empty(t, stdout.String()) + assert.Empty(t, stderr.String()) + assert.Equal(t, tt.log, logbuf.String()) + }) + } +} + +const localtime string = `backupFile: true +writeFile: true +Filename: etc/localtime +{{ /* softlink "/usr/share/zoneinfo/GMT" */ }} +` diff --git a/overlays/localtime/internal/nodes.conf b/overlays/localtime/internal/nodes.conf new file mode 100644 index 00000000..e89e9c56 --- /dev/null +++ b/overlays/localtime/internal/nodes.conf @@ -0,0 +1,4 @@ +nodes: + node1: + tags: + localtime: "GMT" diff --git a/overlays/localtime/rootfs/etc/localtime.ww b/overlays/localtime/rootfs/etc/localtime.ww new file mode 100644 index 00000000..ccba4480 --- /dev/null +++ b/overlays/localtime/rootfs/etc/localtime.ww @@ -0,0 +1 @@ +{{ if .Tags.localtime }}{{ printf "%s/%s" "/usr/share/zoneinfo" .Tags.localtime | softlink }}{{ else }}{{ ImportLink "/etc/localtime" }}{{ end }} \ No newline at end of file diff --git a/userdocs/contents/templating.rst b/userdocs/contents/templating.rst index 90420acd..c2164905 100644 --- a/userdocs/contents/templating.rst +++ b/userdocs/contents/templating.rst @@ -212,6 +212,22 @@ A given string can be split into substrings. {{ $y := (split $x ":") -}} {{ range $y }} {{.}} {{ end }} +softlink +^^^^^^^^ + +Creates a soft link to the given string for the template. + +ImportLink +^^^^^^^^^^ + +Evaluates the soft link on the Warewulf server and +then create the soft link to it in the overlay. + +readlink +^^^^^^^^ + +Evaluates the soft link on the Warewulf server and returns the target. + Node specific files ------------------- diff --git a/warewulf.spec.in b/warewulf.spec.in index 92972428..86dec4f7 100644 --- a/warewulf.spec.in +++ b/warewulf.spec.in @@ -212,6 +212,7 @@ getent group %{wwgroup} >/dev/null || groupadd -r %{wwgroup} %attr(-, root, root) %{_sharedstatedir}/warewulf/overlays/wicked/rootfs/* %attr(-, root, root) %{_sharedstatedir}/warewulf/overlays/wwclient/rootfs/* %attr(-, root, root) %{_sharedstatedir}/warewulf/overlays/wwinit/rootfs/* +%attr(-, root, root) %{_sharedstatedir}/warewulf/overlays/localtime/rootfs/* %attr(-, root, root) %{_bindir}/wwctl %attr(-, root, root) %{_prefix}/lib/firewalld/services/warewulf.xml