From b6c33d956742fede24359c7a340f1953c716b5b5 Mon Sep 17 00:00:00 2001 From: MatthewHink Date: Thu, 12 Jun 2025 10:50:15 -0700 Subject: [PATCH 1/2] Add uid, gid, and permissions to OverlayFile struct There is an existing test for this. uid, gid, and permissions seem consistent across test boxes. --- CHANGELOG.md | 1 + internal/pkg/warewulfd/api/overlay.go | 28 ++++++++++++++++++---- internal/pkg/warewulfd/api/overlay_test.go | 18 +++++++++++++- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b45ae406..fe9243cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Added override.conf for nm-wait-online-initrd.service with dracut. - Added userdocs for `wwctl node import` from yaml/csv. +- Added uid, gid, and permissions to OverlayFile in REST API. #1925 ### Fixed diff --git a/internal/pkg/warewulfd/api/overlay.go b/internal/pkg/warewulfd/api/overlay.go index f18f534d..a6152df6 100644 --- a/internal/pkg/warewulfd/api/overlay.go +++ b/internal/pkg/warewulfd/api/overlay.go @@ -6,6 +6,7 @@ import ( "net/url" "os" "path" + "syscall" "github.com/swaggest/usecase" "github.com/swaggest/usecase/status" @@ -68,9 +69,12 @@ func getOverlayByName() usecase.Interactor { } type OverlayFile struct { - Overlay string `json:"overlay"` - Path string `json:"path"` - Contents string `json:"contents"` + Overlay string `json:"overlay"` + Path string `json:"path"` + Contents string `json:"contents"` + Perms os.FileMode `json:"perms"` + Uid uint32 `json:"uid"` + Gid uint32 `json:"gid"` rendered bool } @@ -83,7 +87,23 @@ func (of *OverlayFile) Exists() bool { } func (of *OverlayFile) readContents() (string, error) { - f, err := os.ReadFile(of.FullPath()) + fullPath := of.FullPath() + f, err := os.ReadFile(fullPath) + if err != nil { + wwlog.Warn("os.ReadFile err %w", err) + return "", err + } + // Populate the permissions, uid, and gid. + s, err := os.Stat(fullPath) + if err != nil { + wwlog.Warn("os.Stat err %w", err) + return "", err + } + fileMode := s.Mode() + of.Perms = fileMode & os.ModePerm + sys := s.Sys() + of.Uid = sys.(*syscall.Stat_t).Uid + of.Gid = sys.(*syscall.Stat_t).Gid return string(f), err } diff --git a/internal/pkg/warewulfd/api/overlay_test.go b/internal/pkg/warewulfd/api/overlay_test.go index 2ddf98e5..ba4bd6c1 100644 --- a/internal/pkg/warewulfd/api/overlay_test.go +++ b/internal/pkg/warewulfd/api/overlay_test.go @@ -1,6 +1,7 @@ package api import ( + "encoding/json" "io" "net" "net/http" @@ -74,7 +75,22 @@ func TestOverlayAPI(t *testing.T) { assert.NoError(t, err) assert.NoError(t, resp.Body.Close()) - assert.JSONEq(t, `{"overlay":"testoverlay","path":"email.ww","contents":"\n{{ if .Tags.email }}eMail: {{ .Tags.email }}{{else}} noMail{{- end }}\n"}`, string(body)) + // gid and uid values may vary depending on where this test is run. (local box, github, etc) + // Assert the keys exist, but ignore the values. + var data map[string]interface{} + err = json.Unmarshal([]byte(body), &data) + assert.NoError(t, err) + assert.Contains(t, data, "gid") + assert.Contains(t, data, "uid") + + // delete gid and uid from the map for comparison + delete(data, "gid") + delete(data, "uid") + + body2, err := json.Marshal(data) + assert.NoError(t, err) + + assert.JSONEq(t, `{"perms":420, "overlay":"testoverlay","path":"email.ww","contents":"\n{{ if .Tags.email }}eMail: {{ .Tags.email }}{{else}} noMail{{- end }}\n"}`, string(body2)) }) t.Run("create an overlay", func(t *testing.T) { From f61dfdde8718a657e9fd309c920cbab0fabba11e Mon Sep 17 00:00:00 2001 From: Jonathon Anderson Date: Mon, 16 Jun 2025 17:05:56 -0600 Subject: [PATCH 2/2] Use jsonassert in test Signed-off-by: Jonathon Anderson --- go.mod | 1 + go.sum | 2 ++ internal/pkg/warewulfd/api/overlay_test.go | 26 +++++++++------------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index cb11d7f3..9f79d7f5 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/google/uuid v1.6.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 github.com/hashicorp/go-version v1.7.0 + github.com/kinbiko/jsonassert v1.2.0 github.com/manifoldco/promptui v0.9.0 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/opencontainers/image-spec v1.1.0 diff --git a/go.sum b/go.sum index 7284a028..c5a19894 100644 --- a/go.sum +++ b/go.sum @@ -207,6 +207,8 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kinbiko/jsonassert v1.2.0 h1:+/JthIVXdIrThrOtSN9ry0mNtWKXMWuvxR0nU7gQ+tI= +github.com/kinbiko/jsonassert v1.2.0/go.mod h1:pCc3uudOt+lVAbkji9O0uw8MSVt4s+1ZJ0y8Ux2F1Og= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= diff --git a/internal/pkg/warewulfd/api/overlay_test.go b/internal/pkg/warewulfd/api/overlay_test.go index ba4bd6c1..d2e981f8 100644 --- a/internal/pkg/warewulfd/api/overlay_test.go +++ b/internal/pkg/warewulfd/api/overlay_test.go @@ -1,13 +1,13 @@ package api import ( - "encoding/json" "io" "net" "net/http" "net/http/httptest" "testing" + "github.com/kinbiko/jsonassert" "github.com/stretchr/testify/assert" "github.com/warewulf/warewulf/internal/pkg/testenv" "github.com/warewulf/warewulf/internal/pkg/warewulfd" @@ -77,20 +77,16 @@ func TestOverlayAPI(t *testing.T) { // gid and uid values may vary depending on where this test is run. (local box, github, etc) // Assert the keys exist, but ignore the values. - var data map[string]interface{} - err = json.Unmarshal([]byte(body), &data) - assert.NoError(t, err) - assert.Contains(t, data, "gid") - assert.Contains(t, data, "uid") - - // delete gid and uid from the map for comparison - delete(data, "gid") - delete(data, "uid") - - body2, err := json.Marshal(data) - assert.NoError(t, err) - - assert.JSONEq(t, `{"perms":420, "overlay":"testoverlay","path":"email.ww","contents":"\n{{ if .Tags.email }}eMail: {{ .Tags.email }}{{else}} noMail{{- end }}\n"}`, string(body2)) + ja := jsonassert.New(t) + ja.Assert(string(body), ` + { + "overlay": "testoverlay", + "path": "email.ww", + "contents": "\n{{ if .Tags.email }}eMail: {{ .Tags.email }}{{else}} noMail{{- end }}\n", + "perms": "<>", + "uid": "<>", + "gid": "<>" + }`) }) t.Run("create an overlay", func(t *testing.T) {