From a3dbf6aeee7a70e34b1db6ef0f56c21c335b818b Mon Sep 17 00:00:00 2001 From: jason yang Date: Thu, 10 Jul 2025 03:26:13 +0000 Subject: [PATCH] add node overlay buildtime Signed-off-by: jason yang --- CHANGELOG.md | 4 +++ internal/pkg/warewulfd/api/api.go | 1 + internal/pkg/warewulfd/api/node.go | 56 ++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59281c08..a3b288d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased +### Added + +- Added system and runtime overlay built time to REST API. + ### Changed - `warewulfd` overlay autobuild rebuilds overlays after node discovery. #1468 diff --git a/internal/pkg/warewulfd/api/api.go b/internal/pkg/warewulfd/api/api.go index 9e214bdc..7f4d9229 100644 --- a/internal/pkg/warewulfd/api/api.go +++ b/internal/pkg/warewulfd/api/api.go @@ -34,6 +34,7 @@ func Handler(auth *config.Authentication, allowedNets []net.IPNet) *web.Service r.Method(http.MethodGet, "/{id}/fields", nethttp.NewHandler(getNodeFields())) r.Method(http.MethodPost, "/overlays/build", nethttp.NewHandler(buildAllOverlays())) r.Method(http.MethodPost, "/{id}/overlays/build", nethttp.NewHandler(buildOverlays())) + r.Method(http.MethodGet, "/{id}/overlays", nethttp.NewHandler(getNodeOverlays())) }) }) diff --git a/internal/pkg/warewulfd/api/node.go b/internal/pkg/warewulfd/api/node.go index 955ba33d..faa4588c 100644 --- a/internal/pkg/warewulfd/api/node.go +++ b/internal/pkg/warewulfd/api/node.go @@ -3,8 +3,10 @@ package api import ( "context" "fmt" + "os" "runtime" "sort" + "time" "dario.cat/mergo" "github.com/swaggest/usecase" @@ -40,6 +42,60 @@ func getNodes() usecase.Interactor { return u } +func getNodeOverlays() usecase.Interactor { + type getOverlaysInput struct { + ID string `path:"id" description:"ID of node to retrieve overlays for"` + } + type buildEntry struct { + MTime string `json:"mtime,omitempty" yaml:"mtime,omitempty"` + } + type overlayEntry struct { + Overlays []string `json:"overlays,omitempty" yaml:"overlays,omitempty"` + Build *buildEntry `json:"build,omitempty" yaml:"build,omitempty"` + } + type getOverlaysOutput struct { + SystemOverlay *overlayEntry `json:"system overlay,omitempty" yaml:"system overlay,omitempty"` + RuntimeOverlay *overlayEntry `json:"runtime overlay,omitempty" yaml:"runtime overlay,omitempty"` + } + u := usecase.NewInteractor(func(ctx context.Context, input *getOverlaysInput, output *getOverlaysOutput) error { + wwlog.Debug("api.getNodeOverlays()") + if registry, err := node.New(); err != nil { + return err + } else { + if node_, err := registry.GetNode(input.ID); err != nil { + return status.Wrap(err, status.NotFound) + } else { + out := getOverlaysOutput{ + SystemOverlay: &overlayEntry{ + Overlays: node_.SystemOverlay, + Build: &buildEntry{}, + }, + RuntimeOverlay: &overlayEntry{ + Overlays: node_.RuntimeOverlay, + Build: &buildEntry{}, + }, + } + sysImagePath := overlay.OverlayImage(input.ID, "system", node_.SystemOverlay) + if sysImageStat, err := os.Stat(sysImagePath); err == nil { + out.SystemOverlay.Build.MTime = sysImageStat.ModTime().Format(time.RFC3339) + } + + runtimeImagePath := overlay.OverlayImage(input.ID, "runtime", node_.RuntimeOverlay) + if runtimeImageStat, err := os.Stat(runtimeImagePath); err == nil { + out.RuntimeOverlay.Build.MTime = runtimeImageStat.ModTime().Format(time.RFC3339) + } + *output = out + return nil + } + } + }) + u.SetTitle("Get overlay info for a node") + u.SetDescription("Get system and runtime overlay info for a node.") + u.SetTags("Node") + + return u +} + func getNodeByID() usecase.Interactor { type getNodeByIDInput struct { ID string `path:"id" required:"true" description:"ID of node to get"`