Simplify overlay mtime api and add tests
Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
@@ -34,7 +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()))
|
||||
r.Method(http.MethodGet, "/{id}/overlays", nethttp.NewHandler(getNodeOverlayInfo()))
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -42,23 +42,20 @@ func getNodes() usecase.Interactor {
|
||||
return u
|
||||
}
|
||||
|
||||
func getNodeOverlays() usecase.Interactor {
|
||||
func getNodeOverlayInfo() 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 overlayInfo struct {
|
||||
Overlays []string `json:"overlays,omitempty" yaml:"overlays,omitempty"`
|
||||
MTime *time.Time `json:"mtime,omitempty" yaml:"mtime,omitempty"`
|
||||
}
|
||||
type getOverlaysOutput struct {
|
||||
SystemOverlay *overlayEntry `json:"system overlay,omitempty" yaml:"system overlay,omitempty"`
|
||||
RuntimeOverlay *overlayEntry `json:"runtime overlay,omitempty" yaml:"runtime overlay,omitempty"`
|
||||
SystemOverlay *overlayInfo `json:"system overlay,omitempty" yaml:"system overlay,omitempty"`
|
||||
RuntimeOverlay *overlayInfo `json:"runtime overlay,omitempty" yaml:"runtime overlay,omitempty"`
|
||||
}
|
||||
u := usecase.NewInteractor(func(ctx context.Context, input *getOverlaysInput, output *getOverlaysOutput) error {
|
||||
wwlog.Debug("api.getNodeOverlays()")
|
||||
wwlog.Debug("api.getNodeOverlayInfo()")
|
||||
if registry, err := node.New(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
@@ -66,23 +63,23 @@ func getNodeOverlays() usecase.Interactor {
|
||||
return status.Wrap(err, status.NotFound)
|
||||
} else {
|
||||
out := getOverlaysOutput{
|
||||
SystemOverlay: &overlayEntry{
|
||||
SystemOverlay: &overlayInfo{
|
||||
Overlays: node_.SystemOverlay,
|
||||
Build: &buildEntry{},
|
||||
},
|
||||
RuntimeOverlay: &overlayEntry{
|
||||
RuntimeOverlay: &overlayInfo{
|
||||
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)
|
||||
mtime := sysImageStat.ModTime()
|
||||
out.SystemOverlay.MTime = &mtime
|
||||
}
|
||||
|
||||
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)
|
||||
mtime := runtimeImageStat.ModTime()
|
||||
out.RuntimeOverlay.MTime = &mtime
|
||||
}
|
||||
*output = out
|
||||
return nil
|
||||
|
||||
@@ -2,12 +2,15 @@ package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kinbiko/jsonassert"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||
"github.com/warewulf/warewulf/internal/pkg/warewulfd"
|
||||
@@ -16,6 +19,8 @@ import (
|
||||
func TestNodeAPI(t *testing.T) {
|
||||
warewulfd.SetNoDaemon()
|
||||
env := testenv.New(t)
|
||||
env.MkdirAll("/var/lib/warewulf/overlays/so1")
|
||||
env.MkdirAll("/var/lib/warewulf/overlays/ro1")
|
||||
defer env.RemoveAll()
|
||||
|
||||
allowedNets := []net.IPNet{
|
||||
@@ -32,6 +37,8 @@ func TestNodeAPI(t *testing.T) {
|
||||
|
||||
testNode := `{
|
||||
"node":{
|
||||
"system overlay": ["so1"],
|
||||
"runtime overlay": ["ro1"],
|
||||
"kernel": {
|
||||
"version": "v1.0.0",
|
||||
"args": ["kernel-args"]
|
||||
@@ -48,7 +55,7 @@ func TestNodeAPI(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
assert.JSONEq(t, `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("read all nodes", func(t *testing.T) {
|
||||
@@ -64,7 +71,7 @@ func TestNodeAPI(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"node1": {}, "test": {"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`, string(body))
|
||||
assert.JSONEq(t, `{"node1": {}, "test": {"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("get one specific node", func(t *testing.T) {
|
||||
@@ -80,7 +87,31 @@ func TestNodeAPI(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
assert.JSONEq(t, `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("get unbuilt overlay info for the node", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/nodes/test/overlays", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// send request
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// validate the response
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
ja := jsonassert.New(t)
|
||||
ja.Assertf(string(body), `{
|
||||
"system overlay": {
|
||||
"overlays": ["so1"]
|
||||
},
|
||||
"runtime overlay": {
|
||||
"overlays": ["ro1"]
|
||||
}
|
||||
}`)
|
||||
})
|
||||
|
||||
t.Run("update the node", func(t *testing.T) {
|
||||
@@ -101,7 +132,7 @@ func TestNodeAPI(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`, string(body))
|
||||
assert.JSONEq(t, `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("get one specific node (again)", func(t *testing.T) {
|
||||
@@ -115,7 +146,7 @@ func TestNodeAPI(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`, string(body))
|
||||
assert.JSONEq(t, `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("get one specific (raw) node", func(t *testing.T) {
|
||||
@@ -129,7 +160,7 @@ func TestNodeAPI(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`, string(body))
|
||||
assert.JSONEq(t, `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("test build all nodes overlays", func(t *testing.T) {
|
||||
@@ -160,6 +191,43 @@ func TestNodeAPI(t *testing.T) {
|
||||
assert.JSONEq(t, `"test"`, string(body))
|
||||
})
|
||||
|
||||
t.Run("get built overlay info for the node", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/nodes/test/overlays", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// send request
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// validate the response
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
ja := jsonassert.New(t)
|
||||
ja.Assertf(string(body), `{
|
||||
"system overlay": {
|
||||
"overlays": ["so1"],
|
||||
"mtime": "<<PRESENCE>>"
|
||||
},
|
||||
"runtime overlay": {
|
||||
"overlays": ["ro1"],
|
||||
"mtime": "<<PRESENCE>>"
|
||||
}
|
||||
}`)
|
||||
|
||||
data := map[string]any{}
|
||||
assert.NoError(t, json.Unmarshal(body, &data))
|
||||
{
|
||||
_, err := time.Parse(time.RFC3339, data["system overlay"].(map[string]any)["mtime"].(string))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
{
|
||||
_, err := time.Parse(time.RFC3339, data["runtime overlay"].(map[string]any)["mtime"].(string))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("test delete nodes", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodDelete, srv.URL+"/api/nodes/test", nil)
|
||||
assert.NoError(t, err)
|
||||
@@ -171,6 +239,6 @@ func TestNodeAPI(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`, string(body))
|
||||
assert.JSONEq(t, `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user