Rebuild overlays for discovered nodes
- Fixes: #1468 Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `warewulfd` overlay autobuild rebuilds overlays after node discovery. #1468
|
||||||
|
|
||||||
## v4.6.2, 2025-07-09
|
## v4.6.2, 2025-07-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -105,3 +105,19 @@ func OverlayImage(nodeName string, context string, overlayNames []string) string
|
|||||||
|
|
||||||
return path.Join(config.Get().Paths.OverlayProvisiondir(), nodeName, name)
|
return path.Join(config.Get().Paths.OverlayProvisiondir(), nodeName, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ClearOverlayImage(nodeName string, context string, overlayNames []string) error {
|
||||||
|
imagePath := OverlayImage(nodeName, context, overlayNames)
|
||||||
|
if util.IsFile(imagePath) {
|
||||||
|
if err := os.Remove(imagePath); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove overlay image: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compressedImagePath := imagePath + ".gz"
|
||||||
|
if util.IsFile(compressedImagePath) {
|
||||||
|
if err := os.Remove(compressedImagePath); err != nil {
|
||||||
|
return fmt.Errorf("failed to remove compressed overlay image: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||||
|
"github.com/warewulf/warewulf/internal/pkg/overlay"
|
||||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ func loadNodeDB() (err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetNodeOrSetDiscoverable(hwaddr string) (node.Node, error) {
|
func GetNodeOrSetDiscoverable(hwaddr string, autobuildOverlays bool) (node.Node, error) {
|
||||||
db.lock.RLock()
|
db.lock.RLock()
|
||||||
defer db.lock.RUnlock()
|
defer db.lock.RUnlock()
|
||||||
// NOTE: since discoverable nodes will write an updated DB to file and then
|
// NOTE: since discoverable nodes will write an updated DB to file and then
|
||||||
@@ -97,9 +98,14 @@ func GetNodeOrSetDiscoverable(hwaddr string) (node.Node, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nodeFound, fmt.Errorf("%s (failed to reload configuration) %w", hwaddr, err)
|
return nodeFound, fmt.Errorf("%s (failed to reload configuration) %w", hwaddr, err)
|
||||||
}
|
}
|
||||||
// NOTE: previously all overlays were built here, but that will also
|
if autobuildOverlays {
|
||||||
// be done automatically when attempting to serve an overlay that
|
if err := overlay.ClearOverlayImage(nodeFound.Id(), "system", []string{}); err != nil {
|
||||||
// hasn't been built (without blocking the database).
|
wwlog.Warn("Failed to clear system overlay image: %s: %s", nodeFound.Id(), err)
|
||||||
|
}
|
||||||
|
if err := overlay.ClearOverlayImage(nodeFound.Id(), "runtime", []string{}); err != nil {
|
||||||
|
wwlog.Warn("Failed to clear runtime overlay image: %s: %s", nodeFound.Id(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wwlog.Serv("%s (node %s automatically configured)", hwaddr, nodeFound.Id())
|
wwlog.Serv("%s (node %s automatically configured)", hwaddr, nodeFound.Id())
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ import (
|
|||||||
|
|
||||||
func Test_GetNodeOrSetDiscoverable(t *testing.T) {
|
func Test_GetNodeOrSetDiscoverable(t *testing.T) {
|
||||||
var tests = map[string]struct {
|
var tests = map[string]struct {
|
||||||
nodesConf string
|
nodesConf string
|
||||||
hwaddr string
|
hwaddr string
|
||||||
node string
|
node string
|
||||||
err bool
|
err bool
|
||||||
|
initFiles []string
|
||||||
|
removedFiles []string
|
||||||
}{
|
}{
|
||||||
"empty": {
|
"empty": {
|
||||||
nodesConf: `
|
nodesConf: `
|
||||||
@@ -40,6 +42,29 @@ nodes:
|
|||||||
discoverable: true
|
discoverable: true
|
||||||
network devices:
|
network devices:
|
||||||
default: {}
|
default: {}
|
||||||
|
`,
|
||||||
|
hwaddr: "00:00:00:00:00:01",
|
||||||
|
node: "n1",
|
||||||
|
},
|
||||||
|
"discoverable autobuild": {
|
||||||
|
initFiles: []string{
|
||||||
|
"/srv/warewulf/overlays/n1/__SYSTEM__.img",
|
||||||
|
"/srv/warewulf/overlays/n1/__SYSTEM__.img.gz",
|
||||||
|
"/srv/warewulf/overlays/n1/__RUNTIME__.img",
|
||||||
|
"/srv/warewulf/overlays/n1/__RUNTIME__.img.gz",
|
||||||
|
},
|
||||||
|
removedFiles: []string{
|
||||||
|
"/srv/warewulf/overlays/n1/__SYSTEM__.img",
|
||||||
|
"/srv/warewulf/overlays/n1/__SYSTEM__.img.gz",
|
||||||
|
"/srv/warewulf/overlays/n1/__RUNTIME__.img",
|
||||||
|
"/srv/warewulf/overlays/n1/__RUNTIME__.img.gz",
|
||||||
|
},
|
||||||
|
nodesConf: `
|
||||||
|
nodes:
|
||||||
|
n1:
|
||||||
|
discoverable: true
|
||||||
|
network devices:
|
||||||
|
default: {}
|
||||||
`,
|
`,
|
||||||
hwaddr: "00:00:00:00:00:01",
|
hwaddr: "00:00:00:00:00:01",
|
||||||
node: "n1",
|
node: "n1",
|
||||||
@@ -77,20 +102,25 @@ nodes:
|
|||||||
for name, tt := range tests {
|
for name, tt := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
env := testenv.New(t)
|
env := testenv.New(t)
|
||||||
|
for _, file := range tt.initFiles {
|
||||||
|
env.CreateFile(file)
|
||||||
|
}
|
||||||
defer env.RemoveAll()
|
defer env.RemoveAll()
|
||||||
env.WriteFile("/etc/warewulf/nodes.conf", tt.nodesConf)
|
env.WriteFile("/etc/warewulf/nodes.conf", tt.nodesConf)
|
||||||
|
|
||||||
err := LoadNodeDB()
|
err := LoadNodeDB()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
node, err := GetNodeOrSetDiscoverable(tt.hwaddr)
|
node, err := GetNodeOrSetDiscoverable(tt.hwaddr, true)
|
||||||
if tt.err {
|
if tt.err {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
} else {
|
} else {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, tt.node, node.Id())
|
assert.Equal(t, tt.node, node.Id())
|
||||||
}
|
}
|
||||||
|
for _, file := range tt.removedFiles {
|
||||||
|
assert.NoFileExists(t, env.GetPath(file), "File should not exist: %s", file)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func ProvisionSend(w http.ResponseWriter, req *http.Request) {
|
|||||||
// TODO: when module version is upgraded to go1.18, should be 'any' type
|
// TODO: when module version is upgraded to go1.18, should be 'any' type
|
||||||
var tmpl_data *templateVars
|
var tmpl_data *templateVars
|
||||||
|
|
||||||
remoteNode, err := GetNodeOrSetDiscoverable(rinfo.hwaddr)
|
remoteNode, err := GetNodeOrSetDiscoverable(rinfo.hwaddr, conf.Warewulf.AutobuildOverlays())
|
||||||
if err != nil && err != node.ErrNoUnconfigured {
|
if err != nil && err != node.ErrNoUnconfigured {
|
||||||
wwlog.ErrorExc(err, "")
|
wwlog.ErrorExc(err, "")
|
||||||
w.WriteHeader(http.StatusServiceUnavailable)
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
|||||||
Reference in New Issue
Block a user