Support for creating and updating overlay file in wwapi
Signed-off-by: jason yang <jasonyangshadow@gmail.com>
This commit is contained in:
committed by
Jonathon Anderson
parent
7b2c0901ed
commit
62d1aa654d
@@ -114,6 +114,25 @@ func (overlay Overlay) IsDistributionOverlay() bool {
|
||||
return path.Dir(overlay.Path()) == config.Get().Paths.DistributionOverlaydir()
|
||||
}
|
||||
|
||||
func (overlay Overlay) CreateOverlayFile(filePath string, content []byte, force bool) error {
|
||||
wwlog.Info("Creating file %s in overlay %s, force: %v", filePath, overlay.Name(), force)
|
||||
fullPath := overlay.File(filePath)
|
||||
// create necessary parent directories
|
||||
if err := os.MkdirAll(path.Dir(fullPath), 0o755); err != nil {
|
||||
return fmt.Errorf("failed to create parent directories for %s: %w", fullPath, err)
|
||||
}
|
||||
|
||||
// if the file already exists and force is false, return an error
|
||||
if util.IsFile(fullPath) {
|
||||
if force {
|
||||
return os.WriteFile(fullPath, content, 0o644)
|
||||
}
|
||||
return fmt.Errorf("file %s already exists in overlay %s", filePath, overlay.Name())
|
||||
}
|
||||
|
||||
return os.WriteFile(fullPath, content, 0o644)
|
||||
}
|
||||
|
||||
// DeleteFile deletes a file or the entire overlay directory.
|
||||
// If the file belongs to a distribution overlay, it will be cloned to a site overlay
|
||||
// before deletion.
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
)
|
||||
|
||||
func Test_FindOverlays(t *testing.T) {
|
||||
var tests = map[string]struct {
|
||||
tests := map[string]struct {
|
||||
distOverlays []string
|
||||
siteOverlays []string
|
||||
overlayList []string
|
||||
@@ -75,7 +75,7 @@ func Test_OverlayMethods(t *testing.T) {
|
||||
env.WriteFile(path.Join(sitedir, "both/rootfs/testfile"), "the site version")
|
||||
env.WriteFile(path.Join(distdir, "both/rootfs/testfile"), "the distribution version")
|
||||
|
||||
var tests = map[string]struct {
|
||||
tests := map[string]struct {
|
||||
name string
|
||||
path string
|
||||
rootfs string
|
||||
@@ -157,7 +157,7 @@ func Test_OverlayMethods(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_BuildOverlayIndir(t *testing.T) {
|
||||
var tests = map[string]struct {
|
||||
tests := map[string]struct {
|
||||
node node.Node
|
||||
overlays []string
|
||||
overlayFiles map[string]string
|
||||
@@ -336,7 +336,7 @@ Tags:map[]
|
||||
}
|
||||
|
||||
func Test_BuildOverlay(t *testing.T) {
|
||||
var tests = []struct {
|
||||
tests := []struct {
|
||||
description string
|
||||
nodeName string
|
||||
context string
|
||||
@@ -377,7 +377,7 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
overlays: []string{"o1"},
|
||||
image: "o1.img",
|
||||
contents: []string{"o1.txt"},
|
||||
perms: []int{0644},
|
||||
perms: []int{0o644},
|
||||
},
|
||||
{
|
||||
description: "if multiple overlays are specified without a node, then the combined overlay is built directly in the overlay directory",
|
||||
@@ -386,7 +386,7 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
overlays: []string{"o1", "o2"},
|
||||
image: "o1-o2.img",
|
||||
contents: []string{"o1.txt", "o2.txt"},
|
||||
perms: []int{0644, 0644},
|
||||
perms: []int{0o644, 0o644},
|
||||
},
|
||||
{
|
||||
description: "if a single node overlay is specified, then the overlay is built in a node overlay directory",
|
||||
@@ -395,7 +395,7 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
overlays: []string{"o1"},
|
||||
image: "node1/o1.img",
|
||||
contents: []string{"o1.txt"},
|
||||
perms: []int{0644},
|
||||
perms: []int{0o644},
|
||||
},
|
||||
{
|
||||
description: "if multiple node overlays are specified, then the combined overlay is built in a node overlay directory",
|
||||
@@ -404,7 +404,7 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
overlays: []string{"o1", "o2"},
|
||||
image: "node1/o1-o2.img",
|
||||
contents: []string{"o1.txt", "o2.txt"},
|
||||
perms: []int{0644, 0644},
|
||||
perms: []int{0o644, 0o644},
|
||||
},
|
||||
{
|
||||
description: "if no node system overlays are specified, then context pointed overlay is generated",
|
||||
@@ -429,7 +429,7 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
overlays: []string{"o1"},
|
||||
image: "node1/__SYSTEM__.img",
|
||||
contents: []string{"o1.txt"},
|
||||
perms: []int{0644},
|
||||
perms: []int{0o644},
|
||||
},
|
||||
{
|
||||
description: "if a single node runtime overlay is specified, then a runtime overlay image is generated in a node overlay directory",
|
||||
@@ -438,7 +438,7 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
overlays: []string{"o1"},
|
||||
image: "node1/__RUNTIME__.img",
|
||||
contents: []string{"o1.txt"},
|
||||
perms: []int{0644},
|
||||
perms: []int{0o644},
|
||||
},
|
||||
{
|
||||
description: "if multiple node system overlays are specified, then a system overlay image is generated with the contents of both overlays",
|
||||
@@ -447,7 +447,7 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
overlays: []string{"o1", "o2"},
|
||||
image: "node1/__SYSTEM__.img",
|
||||
contents: []string{"o1.txt", "o2.txt"},
|
||||
perms: []int{0644, 0644},
|
||||
perms: []int{0o644, 0o644},
|
||||
},
|
||||
{
|
||||
description: "if multiple node runtime overlays are specified, then a runtime overlay image is generated with the contents of both overlays",
|
||||
@@ -456,7 +456,7 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
overlays: []string{"o1", "o2"},
|
||||
image: "node1/__RUNTIME__.img",
|
||||
contents: []string{"o1.txt", "o2.txt"},
|
||||
perms: []int{0644, 0644},
|
||||
perms: []int{0o644, 0o644},
|
||||
},
|
||||
{
|
||||
description: "validating altered permissions are retained",
|
||||
@@ -465,7 +465,7 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
overlays: []string{"o3"},
|
||||
image: "node1/__RUNTIME__.img",
|
||||
contents: []string{"subdir", "subdir/o3.txt"},
|
||||
perms: []int{0700, 0600},
|
||||
perms: []int{0o700, 0o600},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -473,12 +473,12 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
defer env.RemoveAll()
|
||||
|
||||
env.CreateFile("var/lib/warewulf/overlays/o1/rootfs/o1.txt")
|
||||
env.Chmod("var/lib/warewulf/overlays/o1/rootfs/o1.txt", 0644)
|
||||
env.Chmod("var/lib/warewulf/overlays/o1/rootfs/o1.txt", 0o644)
|
||||
env.CreateFile("var/lib/warewulf/overlays/o2/rootfs/o2.txt")
|
||||
env.Chmod("var/lib/warewulf/overlays/o2/rootfs/o2.txt", 0644)
|
||||
env.Chmod("var/lib/warewulf/overlays/o2/rootfs/o2.txt", 0o644)
|
||||
env.CreateFile("var/lib/warewulf/overlays/o3/rootfs/subdir/o3.txt.ww")
|
||||
env.Chmod("var/lib/warewulf/overlays/o3/rootfs/subdir", 0700)
|
||||
env.Chmod("var/lib/warewulf/overlays/o3/rootfs/subdir/o3.txt.ww", 0600)
|
||||
env.Chmod("var/lib/warewulf/overlays/o3/rootfs/subdir", 0o700)
|
||||
env.Chmod("var/lib/warewulf/overlays/o3/rootfs/subdir/o3.txt.ww", 0o600)
|
||||
|
||||
for _, tt := range tests {
|
||||
nodeInfo := node.NewNode(tt.nodeName)
|
||||
@@ -508,7 +508,7 @@ func Test_BuildOverlay(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_BuildAllOverlays(t *testing.T) {
|
||||
var tests = []struct {
|
||||
tests := []struct {
|
||||
description string
|
||||
nodes []string
|
||||
systemOverlays [][]string
|
||||
@@ -585,8 +585,8 @@ func Test_BuildAllOverlays(t *testing.T) {
|
||||
assert.NoError(t, overlayDirErr)
|
||||
defer os.RemoveAll(overlayDir)
|
||||
conf.Paths.WWOverlaydir = overlayDir
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o1"), 0700))
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o2"), 0700))
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o1"), 0o700))
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o2"), 0o700))
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
@@ -620,7 +620,7 @@ func Test_BuildAllOverlays(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_BuildSpecificOverlays(t *testing.T) {
|
||||
var tests = []struct {
|
||||
tests := []struct {
|
||||
description string
|
||||
nodes []string
|
||||
overlays []string
|
||||
@@ -683,8 +683,8 @@ func Test_BuildSpecificOverlays(t *testing.T) {
|
||||
assert.NoError(t, overlayDirErr)
|
||||
defer os.RemoveAll(overlayDir)
|
||||
conf.Paths.WWOverlaydir = overlayDir
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o1"), 0700))
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o2"), 0700))
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o1"), 0o700))
|
||||
assert.NoError(t, os.Mkdir(path.Join(overlayDir, "o2"), 0o700))
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
@@ -711,6 +711,39 @@ func Test_BuildSpecificOverlays(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_CreateOverlayFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
overlayName string
|
||||
filePath string
|
||||
content []byte
|
||||
force bool
|
||||
}{
|
||||
{"create file", "test", "newfile.ww", []byte("new file"), false},
|
||||
{"overwrite existing file", "test", "existingfile.ww", []byte("overwrite file"), true},
|
||||
}
|
||||
|
||||
conf := warewulfconf.Get()
|
||||
overlayDir, overlayDirErr := os.MkdirTemp(os.TempDir(), "ww-test-overlay-*")
|
||||
assert.NoError(t, overlayDirErr)
|
||||
defer os.RemoveAll(overlayDir)
|
||||
conf.Paths.WWOverlaydir = overlayDir
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
newOverlay := GetSiteOverlay(tt.overlayName)
|
||||
err := newOverlay.CreateOverlayFile(tt.filePath, tt.content, tt.force)
|
||||
assert.NoError(t, err)
|
||||
|
||||
newFile := newOverlay.File(tt.filePath)
|
||||
assert.FileExists(t, newFile)
|
||||
readContent, err := os.ReadFile(newFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.content, readContent)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func dirIsEmpty(t *testing.T, name string) bool {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
|
||||
@@ -36,7 +36,6 @@ func FirstError(errs ...error) (err error) {
|
||||
}
|
||||
|
||||
func DirModTime(path string) (time.Time, error) {
|
||||
|
||||
var lastTime time.Time
|
||||
err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
@@ -186,7 +185,8 @@ func FindFilterFiles(
|
||||
path string,
|
||||
includePattern []string,
|
||||
ignorePattern []string,
|
||||
ignore_xdev bool) (ofiles []string, err error) {
|
||||
ignore_xdev bool,
|
||||
) (ofiles []string, err error) {
|
||||
wwlog.Debug("Finding files: %s include: %s ignore: %s", path, includePattern, ignorePattern)
|
||||
|
||||
// Preprocess patterns to remove leading (and trailing) /, as we are handling relative paths
|
||||
@@ -347,7 +347,7 @@ Appending the lines to the given file
|
||||
*/
|
||||
func AppendLines(fileName string, lines []string) error {
|
||||
wwlog.Verbose("appending %v lines to %s", len(lines), fileName)
|
||||
file, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0644)
|
||||
file, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't open file: %s: %w", fileName, err)
|
||||
}
|
||||
@@ -362,6 +362,21 @@ func AppendLines(fileName string, lines []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func OverwriteFile(fileName string, content []byte) error {
|
||||
wwlog.Verbose("overwrite file %s", fileName)
|
||||
file, err := os.OpenFile(fileName, os.O_RDWR|os.O_TRUNC, 0o644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file: %s, err: %w", fileName, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write(content)
|
||||
if err != nil {
|
||||
return fmt.Errorf("while writing file: %s, err: %w", fileName, err)
|
||||
}
|
||||
return file.Sync()
|
||||
}
|
||||
|
||||
/*
|
||||
******************************************************************************
|
||||
|
||||
@@ -372,14 +387,15 @@ func CpioCreate(
|
||||
ifiles []string,
|
||||
ofile string,
|
||||
format string,
|
||||
cpio_args ...string) (err error) {
|
||||
|
||||
cpio_args ...string,
|
||||
) (err error) {
|
||||
args := []string{
|
||||
"--quiet",
|
||||
"--create",
|
||||
"--directory", rootdir,
|
||||
"--format", format,
|
||||
"--file=" + ofile}
|
||||
"--file=" + ofile,
|
||||
}
|
||||
|
||||
args = append(args, cpio_args...)
|
||||
|
||||
@@ -413,13 +429,12 @@ func CpioCreate(
|
||||
Compress a file using gzip or pigz
|
||||
*/
|
||||
func FileGz(
|
||||
file string) (err error) {
|
||||
|
||||
file string,
|
||||
) (err error) {
|
||||
file_gz := file + ".gz"
|
||||
|
||||
if IsFile(file_gz) {
|
||||
err := os.Remove(file_gz)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not remove existing file: %s: %w", file_gz, err)
|
||||
}
|
||||
@@ -509,9 +524,9 @@ func BuildFsImage(
|
||||
ignore []string,
|
||||
ignore_xdev bool,
|
||||
format string,
|
||||
cpio_args ...string) (err error) {
|
||||
|
||||
err = os.MkdirAll(path.Dir(imagePath), 0755)
|
||||
cpio_args ...string,
|
||||
) (err error) {
|
||||
err = os.MkdirAll(path.Dir(imagePath), 0o755)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create image directory for %s: %s: %w", name, imagePath, err)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func Test_FindFiles(t *testing.T) {
|
||||
var tests = map[string]struct {
|
||||
tests := map[string]struct {
|
||||
createFiles []string
|
||||
findFiles []string
|
||||
}{
|
||||
@@ -43,7 +43,7 @@ func Test_FindFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_FindFilterFiles(t *testing.T) {
|
||||
var tests = map[string]struct {
|
||||
tests := map[string]struct {
|
||||
createFiles []string
|
||||
include []string
|
||||
exclude []string
|
||||
@@ -92,3 +92,19 @@ func Test_FindFilterFiles(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Overwrite(t *testing.T) {
|
||||
t.Run("overwrite a file", func(t *testing.T) {
|
||||
env := testenv.New(t)
|
||||
defer env.RemoveAll()
|
||||
env.CreateFile("file")
|
||||
env.WriteFile("file", "hello world")
|
||||
|
||||
assert.Equal(t, "hello world", env.ReadFile("file"))
|
||||
|
||||
err := OverwriteFile(env.GetPath("file"), []byte("hello warewulf"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "hello warewulf", env.ReadFile("file"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -71,8 +71,10 @@ func Handler(auth *config.Authentication, allowedNets []net.IPNet) *web.Service
|
||||
r.Method(http.MethodGet, "/{name}", nethttp.NewHandler(getOverlayByName()))
|
||||
r.Method(http.MethodGet, "/{name}/file", nethttp.NewHandler(getOverlayFile()))
|
||||
r.Method(http.MethodPut, "/{name}", nethttp.NewHandler(createOverlay()))
|
||||
r.Method(http.MethodPut, "/{name}/file", nethttp.NewHandler(createOverlayFile()))
|
||||
r.Method(http.MethodDelete, "/{name}", nethttp.NewHandler(deleteOverlay()))
|
||||
r.Method(http.MethodDelete, "/{name}/file", nethttp.NewHandler(deleteOverlayFile()))
|
||||
r.Method(http.MethodPut, "/{name}/file", nethttp.NewHandler(updateOverlayFile()))
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -215,6 +215,39 @@ func createOverlay() usecase.Interactor {
|
||||
return u
|
||||
}
|
||||
|
||||
func createOverlayFile() usecase.Interactor {
|
||||
type createOverlayFileInput struct {
|
||||
Name string `path:"name" required:"true" description:"Name of overlay to create a file in"`
|
||||
Path string `query:"path" required:"true" description:"Path to file to create in an overlay"`
|
||||
Force bool `query:"force" default:"false" description:"Whether to forcefully create an overlay file, default:'false'"`
|
||||
Content string `json:"content" required:"true" description:"Content of the file to create"`
|
||||
}
|
||||
|
||||
u := usecase.NewInteractor(func(ctx context.Context, input createOverlayFileInput, output *OverlayResponse) error {
|
||||
wwlog.Debug("api.createOverlayFile(Name:%v, Path:%v, Force: %v)", input.Name, input.Path, input.Force)
|
||||
if input.Path == "" {
|
||||
return status.Wrap(fmt.Errorf("must specify a path"), status.InvalidArgument)
|
||||
}
|
||||
if input.Content == "" {
|
||||
return status.Wrap(fmt.Errorf("content should not be empty"), status.InvalidArgument)
|
||||
}
|
||||
if relPath, err := url.QueryUnescape(input.Path); err != nil {
|
||||
return fmt.Errorf("failed to decode path: %v: %w", input.Path, err)
|
||||
} else {
|
||||
newOverlay := overlay.GetOverlay(input.Name)
|
||||
if err := newOverlay.CreateOverlayFile(relPath, []byte(input.Content), input.Force); err != nil {
|
||||
return fmt.Errorf("unable to create overlay file %v: %v: %w", input.Name, relPath, err)
|
||||
}
|
||||
*output = *NewOverlayResponse(input.Name)
|
||||
return nil
|
||||
}
|
||||
})
|
||||
u.SetTitle("Create a file in an overlay")
|
||||
u.SetDescription("Create a file in an overlay from the overlay name and file path.")
|
||||
u.SetTags("Overlay")
|
||||
return u
|
||||
}
|
||||
|
||||
func deleteOverlay() usecase.Interactor {
|
||||
type deleteOverlayInput struct {
|
||||
Name string `path:"name" required:"true" description:"Name of overlay to delete"`
|
||||
@@ -288,3 +321,37 @@ func deleteOverlayFile() usecase.Interactor {
|
||||
u.SetTags("Overlay")
|
||||
return u
|
||||
}
|
||||
|
||||
func updateOverlayFile() usecase.Interactor {
|
||||
type updateOverlayInput struct {
|
||||
Name string `path:"name" required:"true" description:"Name of overlay to update"`
|
||||
Path string `query:"path" required:"true" description:"Path to file to get from an overlay"`
|
||||
Content string `json:"content" required:"true" description:"New content"`
|
||||
}
|
||||
|
||||
u := usecase.NewInteractor(func(ctx context.Context, input updateOverlayInput, output *OverlayResponse) error {
|
||||
wwlog.Debug("api.updateOverlay(Name: %s)", input.Name)
|
||||
if input.Path == "" {
|
||||
return status.Wrap(fmt.Errorf("must specify a path"), status.InvalidArgument)
|
||||
}
|
||||
if input.Content == "" {
|
||||
return status.Wrap(fmt.Errorf("new content should not be empty"), status.InvalidArgument)
|
||||
}
|
||||
*output = *NewOverlayResponse(input.Name)
|
||||
|
||||
if relPath, err := url.QueryUnescape(input.Path); err != nil {
|
||||
return status.Wrap(fmt.Errorf("failed to decode path"), status.InvalidArgument)
|
||||
} else {
|
||||
overlay_ := overlay.GetOverlay(input.Name)
|
||||
file := overlay_.File(relPath)
|
||||
if err := util.OverwriteFile(file, []byte(input.Content)); err != nil {
|
||||
return fmt.Errorf("failed to overwrite file: %s, err: %w", file, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
u.SetTitle("Update an overlay")
|
||||
u.SetDescription("Update an overlay")
|
||||
u.SetTags("Overlay")
|
||||
return u
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -89,6 +90,46 @@ func TestOverlayAPI(t *testing.T) {
|
||||
}`)
|
||||
})
|
||||
|
||||
t.Run("update overlay file", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPut, srv.URL+"/api/overlays/testoverlay/file?path=email.ww", bytes.NewReader([]byte("{\"content\":\"hello world\"}")))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// set request
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// validate the resp
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"files":["/email.ww"], "site":false}`, string(body))
|
||||
|
||||
// get again
|
||||
req, err = http.NewRequest(http.MethodGet, srv.URL+"/api/overlays/testoverlay/file?path=email.ww", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// send request
|
||||
resp, err = http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// validate the resp
|
||||
body, err = io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
ja := jsonassert.New(t)
|
||||
ja.Assert(string(body), `
|
||||
{
|
||||
"overlay": "testoverlay",
|
||||
"path": "email.ww",
|
||||
"contents": "hello world",
|
||||
"perms": "<<PRESENCE>>",
|
||||
"uid": "<<PRESENCE>>",
|
||||
"gid": "<<PRESENCE>>"
|
||||
}`)
|
||||
})
|
||||
|
||||
t.Run("create an overlay", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPut, srv.URL+"/api/overlays/test", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user