Merge pull request #1303 from mslacken/softlink

create soft links from overlays
This commit is contained in:
Jonathon Anderson
2024-12-18 15:17:41 -07:00
committed by GitHub
8 changed files with 107 additions and 4 deletions

View File

@@ -29,6 +29,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Document "known issues." - Document "known issues."
- Add `wwctl <node|profile> <add|set> --kernelversion` to specify the desired kernel version or path. #1556 - Add `wwctl <node|profile> <add|set> --kernelversion` to specify the desired kernel version or path. #1556
- Add `wwctl container kernels` to list discovered kernels from containers. #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 ### Changed

View File

@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"os" "os"
"path" "path"
"path/filepath"
"strings" "strings"
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config" warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
@@ -122,3 +123,16 @@ func createIgnitionJson(node *node.Node) string {
tmpYaml, _ := json.Marshal(&conf) tmpYaml, _ := json.Marshal(&conf)
return string(tmpYaml) 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)
}

View File

@@ -238,13 +238,18 @@ func BuildOverlayIndir(nodeData node.Node, overlayNames []string, outputDir stri
// 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*}}.*`) regFile := regexp.MustCompile(`.*{{\s*/\*\s*file\s*["'](.*)["']\s*\*/\s*}}.*`)
regLink := regexp.MustCompile(`.*{{\s*/\*\s*softlink\s*["'](.*)["']\s*\*/\s*}}.*`)
foundFileComment := false foundFileComment := false
for fileScanner.Scan() { for fileScanner.Scan() {
line := fileScanner.Text() line := fileScanner.Text()
filenameFromTemplate := reg.FindAllStringSubmatch(line, -1) filenameFromTemplate := regFile.FindAllStringSubmatch(line, -1)
if len(filenameFromTemplate) != 0 { softlinkFromTemplate := regLink.FindAllStringSubmatch(line, -1)
wwlog.Debug("Found multiple comment, new filename %s", filenameFromTemplate[0][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 { if foundFileComment {
err = CarefulWriteBuffer(path.Join(outputDir, destFileName), err = CarefulWriteBuffer(path.Join(outputDir, destFileName),
fileBuffer, backupFile, info.Mode()) fileBuffer, backupFile, info.Mode())
@@ -371,10 +376,13 @@ func RenderTemplateFile(fileName string, data TemplateStruct) (
"Include": templateFileInclude, "Include": templateFileInclude,
"IncludeFrom": templateContainerFileInclude, "IncludeFrom": templateContainerFileInclude,
"IncludeBlock": templateFileBlock, "IncludeBlock": templateFileBlock,
"ImportLink": importSoftlink,
"basename": path.Base, "basename": path.Base,
"inc": func(i int) int { return i + 1 }, "inc": func(i int) int { return i + 1 },
"dec": 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) }, "file": func(str string) string { return fmt.Sprintf("{{ /* file \"%s\" */ }}", str) },
"softlink": softlink,
"readlink": filepath.EvalSymlinks,
"IgnitionJson": func() string { "IgnitionJson": func() string {
str := createIgnitionJson(data.ThisNode) str := createIgnitionJson(data.ThisNode)
if str != "" { if str != "" {

View File

@@ -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" */ }}
`

View File

@@ -0,0 +1,4 @@
nodes:
node1:
tags:
localtime: "GMT"

View File

@@ -0,0 +1 @@
{{ if .Tags.localtime }}{{ printf "%s/%s" "/usr/share/zoneinfo" .Tags.localtime | softlink }}{{ else }}{{ ImportLink "/etc/localtime" }}{{ end }}

View File

@@ -212,6 +212,22 @@ A given string can be split into substrings.
{{ $y := (split $x ":") -}} {{ $y := (split $x ":") -}}
{{ range $y }} {{.}} {{ end }} {{ 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 Node specific files
------------------- -------------------

View File

@@ -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/wicked/rootfs/*
%attr(-, root, root) %{_sharedstatedir}/warewulf/overlays/wwclient/rootfs/* %attr(-, root, root) %{_sharedstatedir}/warewulf/overlays/wwclient/rootfs/*
%attr(-, root, root) %{_sharedstatedir}/warewulf/overlays/wwinit/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) %{_bindir}/wwctl
%attr(-, root, root) %{_prefix}/lib/firewalld/services/warewulf.xml %attr(-, root, root) %{_prefix}/lib/firewalld/services/warewulf.xml