Refactor wwapi tests for proper isolation
Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
@@ -2,143 +2,173 @@ package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/kinbiko/jsonassert"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/warewulf/warewulf/internal/pkg/config"
|
||||
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||
"github.com/warewulf/warewulf/internal/pkg/warewulfd"
|
||||
)
|
||||
|
||||
func TestImageAPI(t *testing.T) {
|
||||
env := testenv.New(t)
|
||||
defer env.RemoveAll()
|
||||
var imageTests = map[string]struct {
|
||||
initFiles []string
|
||||
request func(serverURL string) (*http.Request, error)
|
||||
response string
|
||||
status int
|
||||
resultFiles []string
|
||||
resultAbsentFiles []string
|
||||
authenticate bool
|
||||
}{
|
||||
"test no authentication": {
|
||||
initFiles: []string{
|
||||
"/var/lib/warewulf/chroots/test-image/rootfs/file",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/images", nil)
|
||||
},
|
||||
response: fmt.Sprintln("Unauthorized"),
|
||||
status: http.StatusUnauthorized,
|
||||
authenticate: false,
|
||||
},
|
||||
|
||||
"test get all images": {
|
||||
initFiles: []string{
|
||||
"/var/lib/warewulf/chroots/test-image/rootfs/file",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/images", nil)
|
||||
},
|
||||
response: `{"test-image": {"kernels":[], "size":0, "buildtime":0, "writable":true}}`,
|
||||
authenticate: true,
|
||||
},
|
||||
|
||||
"test get single image": {
|
||||
initFiles: []string{
|
||||
"/var/lib/warewulf/chroots/test-image/rootfs/file",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/images/test-image", nil)
|
||||
},
|
||||
response: `{"kernels":[], "size":0, "buildtime":0, "writable":true}`,
|
||||
authenticate: true,
|
||||
},
|
||||
|
||||
"test build image": {
|
||||
initFiles: []string{
|
||||
"/var/lib/warewulf/chroots/test-image/rootfs/file",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodPost, serverURL+"/api/images/test-image/build?force=true&default=true", nil)
|
||||
},
|
||||
response: `{"kernels":[], "size":512, "buildtime":"<<PRESENCE>>", "writable":true}`,
|
||||
resultFiles: []string{
|
||||
"/srv/warewulf/images/test-image.img",
|
||||
"/srv/warewulf/images/test-image.img.gz",
|
||||
},
|
||||
authenticate: true,
|
||||
},
|
||||
|
||||
"test rename image": {
|
||||
initFiles: []string{
|
||||
"/var/lib/warewulf/chroots/test-image/rootfs/file",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodPatch, serverURL+"/api/images/test-image?build=true", bytes.NewBuffer([]byte(`{"name": "new-image"}`)))
|
||||
},
|
||||
response: `{"kernels":[], "size":512, "buildtime":"<<PRESENCE>>", "writable":true}`,
|
||||
authenticate: true,
|
||||
},
|
||||
|
||||
"test delete image": {
|
||||
initFiles: []string{
|
||||
"/var/lib/warewulf/chroots/new-image/rootfs/file",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodDelete, serverURL+"/api/images/new-image", nil)
|
||||
},
|
||||
response: `{"kernels":[], "size":0, "buildtime":"<<PRESENCE>>", "writable":true}`,
|
||||
resultAbsentFiles: []string{
|
||||
"/var/lib/warewulf/chroots/new-image",
|
||||
"/srv/warewulf/images/new-image.img",
|
||||
"/srv/warewulf/images/new-image.img.gz",
|
||||
},
|
||||
authenticate: true,
|
||||
},
|
||||
}
|
||||
|
||||
func TestImageAPI(t *testing.T) {
|
||||
authData := `
|
||||
users:
|
||||
- name: admin
|
||||
password hash: $2b$05$5QVWDpiWE7L4SDL9CYdi3O/l6HnbNOLoXgY2sa1bQQ7aSBKdSqvsC
|
||||
`
|
||||
auth := config.NewAuthentication()
|
||||
err := auth.ParseFromRaw([]byte(authData))
|
||||
assert.NoError(t, err)
|
||||
|
||||
allowedNets := []net.IPNet{
|
||||
{
|
||||
IP: net.IPv4(127, 0, 0, 0),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
},
|
||||
for name, tt := range imageTests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
warewulfd.SetNoDaemon()
|
||||
env := testenv.New(t)
|
||||
defer env.RemoveAll()
|
||||
|
||||
// Create test files
|
||||
for _, fileName := range tt.initFiles {
|
||||
env.CreateFile(fileName)
|
||||
}
|
||||
|
||||
auth := config.NewAuthentication()
|
||||
err := auth.ParseFromRaw([]byte(authData))
|
||||
assert.NoError(t, err)
|
||||
|
||||
allowedNets := []net.IPNet{
|
||||
{
|
||||
IP: net.IPv4(127, 0, 0, 0),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
},
|
||||
}
|
||||
srv := httptest.NewServer(Handler(auth, allowedNets))
|
||||
defer srv.Close()
|
||||
|
||||
req, err := tt.request(srv.URL)
|
||||
assert.NoError(t, err)
|
||||
|
||||
if tt.authenticate {
|
||||
req.SetBasicAuth("admin", "admin")
|
||||
}
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedStatus := tt.status
|
||||
if expectedStatus == 0 {
|
||||
expectedStatus = http.StatusOK
|
||||
}
|
||||
assert.Equal(t, expectedStatus, resp.StatusCode)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
if expectedStatus == http.StatusUnauthorized {
|
||||
// For plain text responses like Unauthorized
|
||||
assert.Equal(t, tt.response, string(body))
|
||||
} else {
|
||||
// For JSON responses
|
||||
ja := jsonassert.New(t)
|
||||
ja.Assertf(string(body), tt.response) //nolint:govet
|
||||
}
|
||||
|
||||
for _, fileName := range tt.resultFiles {
|
||||
assert.FileExists(t, env.GetPath(fileName))
|
||||
}
|
||||
|
||||
for _, fileName := range tt.resultAbsentFiles {
|
||||
assert.NoFileExists(t, env.GetPath(fileName))
|
||||
}
|
||||
})
|
||||
}
|
||||
srv := httptest.NewServer(Handler(auth, allowedNets))
|
||||
defer srv.Close()
|
||||
env.WriteFile(path.Join(testenv.WWChrootdir, "test-image/rootfs/file"), `test`)
|
||||
|
||||
t.Run("test no authentication", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/images", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.Equal(t, resp.StatusCode, http.StatusUnauthorized)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Unauthorized\n", string(body))
|
||||
})
|
||||
|
||||
t.Run("test get all images", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/images", nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth("admin", "admin")
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, `{"test-image": {"kernels":[], "size":0, "buildtime":0, "writable":true}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("test get single image", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/images/test-image", nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth("admin", "admin")
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, `{"kernels":[] ,"size":0, "buildtime":0, "writable":true}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("test build image", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPost, srv.URL+"/api/images/test-image/build?force=true&default=true", nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth("admin", "admin")
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
|
||||
var bodyData map[string]interface{}
|
||||
assert.NoError(t, json.Unmarshal([]byte(body), &bodyData))
|
||||
assert.True(t, bodyData["buildtime"].(float64) > 0.0)
|
||||
|
||||
bodyData["buildtime"] = 0.0
|
||||
assert.Equal(t, map[string]interface{}{"kernels": []interface{}{}, "size": 512.0, "buildtime": 0.0, "writable": true}, bodyData)
|
||||
})
|
||||
|
||||
t.Run("test rename image", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPatch, srv.URL+"/api/images/test-image?build=true", bytes.NewBuffer([]byte(`{"name": "new-image"}`)))
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth("admin", "admin")
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
|
||||
var bodyData map[string]interface{}
|
||||
assert.NoError(t, json.Unmarshal([]byte(body), &bodyData))
|
||||
assert.True(t, bodyData["buildtime"].(float64) > 0.0)
|
||||
|
||||
bodyData["buildtime"] = 0.0
|
||||
assert.Equal(t, map[string]interface{}{"kernels": []interface{}{}, "size": 512.0, "buildtime": 0.0, "writable": true}, bodyData)
|
||||
})
|
||||
|
||||
t.Run("test delete image", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodDelete, srv.URL+"/api/images/new-image", nil)
|
||||
assert.NoError(t, err)
|
||||
req.SetBasicAuth("admin", "admin")
|
||||
|
||||
// send request
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// validate the resp
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.Equal(t, resp.StatusCode, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var bodyData map[string]interface{}
|
||||
assert.NoError(t, json.Unmarshal([]byte(body), &bodyData))
|
||||
assert.True(t, bodyData["buildtime"].(float64) > 0.0)
|
||||
|
||||
bodyData["buildtime"] = 0.0
|
||||
assert.Equal(t, map[string]interface{}{"kernels": []interface{}{}, "size": 512.0, "buildtime": 0.0, "writable": true}, bodyData)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,13 +2,11 @@ package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kinbiko/jsonassert"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -16,26 +14,23 @@ import (
|
||||
"github.com/warewulf/warewulf/internal/pkg/warewulfd"
|
||||
)
|
||||
|
||||
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{
|
||||
{
|
||||
IP: net.IPv4(127, 0, 0, 0),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
var nodeTests = map[string]struct {
|
||||
initConf string
|
||||
initFiles []string
|
||||
request func(serverURL string) (*http.Request, error)
|
||||
response string
|
||||
status int
|
||||
resultConf string
|
||||
resultFiles []string
|
||||
}{
|
||||
"add a node": {
|
||||
initConf: "",
|
||||
initFiles: []string{
|
||||
"/var/lib/warewulf/overlays/so1/rootfs/file",
|
||||
"/var/lib/warewulf/overlays/ro1/rootfs/file",
|
||||
},
|
||||
}
|
||||
srv := httptest.NewServer(Handler(nil, allowedNets))
|
||||
defer srv.Close()
|
||||
|
||||
t.Run("add a node", func(t *testing.T) {
|
||||
// prepareration
|
||||
|
||||
testNode := `{
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
testNode := `{
|
||||
"node":{
|
||||
"system overlay": ["so1"],
|
||||
"runtime overlay": ["ro1"],
|
||||
@@ -45,221 +40,251 @@ func TestNodeAPI(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}`
|
||||
req, err := http.NewRequest(http.MethodPut, srv.URL+"/api/nodes/test", bytes.NewBuffer([]byte(testNode)))
|
||||
assert.NoError(t, err)
|
||||
return http.NewRequest(http.MethodPut, serverURL+"/api/nodes/n1", bytes.NewBuffer([]byte(testNode)))
|
||||
},
|
||||
response: `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`,
|
||||
resultConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
},
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
"read all nodes": {
|
||||
initConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/nodes", nil)
|
||||
},
|
||||
response: `{"n1": {"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`,
|
||||
},
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
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) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/nodes", 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())
|
||||
|
||||
assert.JSONEq(t, `{"node1": {}, "test": {"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("test idempotency (put same node again)", func(t *testing.T) {
|
||||
// prepareration
|
||||
|
||||
testNode := `{
|
||||
"test idempotency (replacing existing node)": {
|
||||
initConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
initFiles: []string{
|
||||
"/var/lib/warewulf/overlays/so2/rootfs/file",
|
||||
"/var/lib/warewulf/overlays/ro2/rootfs/file",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
testNode := `{
|
||||
"node":{
|
||||
"system overlay": ["so1"],
|
||||
"runtime overlay": ["ro1"],
|
||||
"system overlay": ["so2"],
|
||||
"runtime overlay": ["ro2"],
|
||||
"kernel": {
|
||||
"version": "v1.0.0",
|
||||
"args": ["kernel-args"]
|
||||
}
|
||||
}
|
||||
}`
|
||||
req, err := http.NewRequest(http.MethodPut, srv.URL+"/api/nodes/test", bytes.NewBuffer([]byte(testNode)))
|
||||
assert.NoError(t, err)
|
||||
return http.NewRequest(http.MethodPut, serverURL+"/api/nodes/n1", bytes.NewBuffer([]byte(testNode)))
|
||||
},
|
||||
response: `{"system overlay": ["so2"], "runtime overlay": ["ro2"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`,
|
||||
resultConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so2
|
||||
runtime overlay:
|
||||
- ro2
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
},
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("fail if node already exists (given appropriate header)", func(t *testing.T) {
|
||||
// prepareration
|
||||
|
||||
testNode := `{
|
||||
"test preventing replacing a node": {
|
||||
initConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
initFiles: []string{
|
||||
"/var/lib/warewulf/overlays/so2/rootfs/file",
|
||||
"/var/lib/warewulf/overlays/ro2/rootfs/file",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
testNode := `{
|
||||
"node":{
|
||||
"system overlay": ["so1"],
|
||||
"runtime overlay": ["ro1"],
|
||||
"system overlay": ["so2"],
|
||||
"runtime overlay": ["ro2"],
|
||||
"kernel": {
|
||||
"version": "v1.0.0",
|
||||
"args": ["kernel-args"]
|
||||
}
|
||||
}
|
||||
}`
|
||||
req, err := http.NewRequest(http.MethodPut, srv.URL+"/api/nodes/test", bytes.NewBuffer([]byte(testNode)))
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("If-None-Match", "*")
|
||||
req, err := http.NewRequest(http.MethodPut, serverURL+"/api/nodes/n1", bytes.NewBuffer([]byte(testNode)))
|
||||
req.Header.Set("If-None-Match", "*")
|
||||
return req, err
|
||||
},
|
||||
response: `{"error": "invalid argument: node 'n1' already exists", "status": "INVALID_ARGUMENT"}`,
|
||||
status: http.StatusBadRequest,
|
||||
resultConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
},
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
"get one specific node": {
|
||||
initConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/nodes/n1", nil)
|
||||
},
|
||||
response: `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`,
|
||||
},
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
"get one specific node with a profile": {
|
||||
initConf: `
|
||||
nodeprofiles:
|
||||
default:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
nodes:
|
||||
n1:
|
||||
profiles:
|
||||
- default
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/nodes/n1", nil)
|
||||
},
|
||||
response: `{"profiles": ["default"], "system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`,
|
||||
},
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||
assert.Contains(t, string(body), "node 'test' already exists")
|
||||
})
|
||||
"get one specific raw node with a profile": {
|
||||
initConf: `
|
||||
nodeprofiles:
|
||||
default:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
nodes:
|
||||
n1:
|
||||
profiles:
|
||||
- default
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/nodes/n1/raw", nil)
|
||||
},
|
||||
response: `{"profiles": ["default"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`,
|
||||
},
|
||||
|
||||
t.Run("get one specific node", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/nodes/test", nil)
|
||||
assert.NoError(t, err)
|
||||
"get unbuilt overlay info for the node": {
|
||||
initConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/nodes/n1/overlays", nil)
|
||||
},
|
||||
response: `{"system overlay": { "overlays": ["so1"] }, "runtime overlay": { "overlays": ["ro1"] }}`,
|
||||
},
|
||||
|
||||
// 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())
|
||||
|
||||
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) {
|
||||
updateNode := `{
|
||||
"node":{
|
||||
"kernel": {
|
||||
"version": "v1.0.1-newversion"
|
||||
}
|
||||
}
|
||||
}`
|
||||
req, err := http.NewRequest(http.MethodPatch, srv.URL+"/api/nodes/test", bytes.NewBuffer([]byte(updateNode)))
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
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) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/nodes/test", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
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) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/nodes/test/raw", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
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) {
|
||||
req, err := http.NewRequest(http.MethodPost, srv.URL+"/api/nodes/overlays/build", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `["node1", "test"]`, string(body))
|
||||
})
|
||||
|
||||
t.Run("test build one node's overlays", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodPost, srv.URL+"/api/nodes/test/overlays/build", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
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), `{
|
||||
"get built overlay info for the node": {
|
||||
initConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
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",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/nodes/n1/overlays", nil)
|
||||
},
|
||||
response: `{
|
||||
"system overlay": {
|
||||
"overlays": ["so1"],
|
||||
"mtime": "<<PRESENCE>>"
|
||||
@@ -268,57 +293,187 @@ func TestNodeAPI(t *testing.T) {
|
||||
"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("replace a node", func(t *testing.T) {
|
||||
// prepareration
|
||||
|
||||
testNode := `{
|
||||
"update a node": {
|
||||
initConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
updateNode := `{
|
||||
"node":{
|
||||
"system overlay": ["so1"],
|
||||
"runtime overlay": ["ro1"],
|
||||
"kernel": {
|
||||
"version": "v1.0.0",
|
||||
"args": ["kernel-args"]
|
||||
"version": "v1.0.1-newversion"
|
||||
}
|
||||
}
|
||||
}`
|
||||
req, err := http.NewRequest(http.MethodPut, srv.URL+"/api/nodes/test", bytes.NewBuffer([]byte(testNode)))
|
||||
assert.NoError(t, err)
|
||||
return http.NewRequest(http.MethodPatch, serverURL+"/api/nodes/n1", bytes.NewBuffer([]byte(updateNode)))
|
||||
},
|
||||
response: `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`,
|
||||
resultConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.1-newversion"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
},
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
"test build all nodes overlays": {
|
||||
initConf: `
|
||||
nodeprofiles:
|
||||
default:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
nodes:
|
||||
n1:
|
||||
profiles:
|
||||
- default
|
||||
n2:
|
||||
profiles:
|
||||
- default
|
||||
`,
|
||||
initFiles: []string{
|
||||
"/var/lib/warewulf/overlays/so1/rootfs/file",
|
||||
"/var/lib/warewulf/overlays/ro1/rootfs/file",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodPost, serverURL+"/api/nodes/overlays/build", nil)
|
||||
},
|
||||
response: `["n1", "n2"]`,
|
||||
resultFiles: []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",
|
||||
"/srv/warewulf/overlays/n2/__SYSTEM__.img",
|
||||
"/srv/warewulf/overlays/n2/__SYSTEM__.img.gz",
|
||||
"/srv/warewulf/overlays/n2/__RUNTIME__.img",
|
||||
"/srv/warewulf/overlays/n2/__RUNTIME__.img.gz",
|
||||
},
|
||||
},
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
"test build one node's overlays": {
|
||||
initConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
initFiles: []string{
|
||||
"/var/lib/warewulf/overlays/so1/rootfs/file",
|
||||
"/var/lib/warewulf/overlays/ro1/rootfs/file",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodPost, serverURL+"/api/nodes/n1/overlays/build", nil)
|
||||
},
|
||||
response: `"n1"`,
|
||||
resultFiles: []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",
|
||||
},
|
||||
},
|
||||
|
||||
assert.JSONEq(t, `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
"test delete nodes": {
|
||||
initConf: `
|
||||
nodeprofiles: {}
|
||||
nodes:
|
||||
n1:
|
||||
system overlay:
|
||||
- so1
|
||||
runtime overlay:
|
||||
- ro1
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodDelete, serverURL+"/api/nodes/n1", nil)
|
||||
},
|
||||
response: `{"system overlay": ["so1"], "runtime overlay": ["ro1"], "kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`,
|
||||
resultConf: `
|
||||
nodeprofiles: {}
|
||||
nodes: {}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestNodeAPI(t *testing.T) {
|
||||
for name, tt := range nodeTests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
warewulfd.SetNoDaemon()
|
||||
env := testenv.New(t)
|
||||
defer env.RemoveAll()
|
||||
|
||||
env.WriteFile("/etc/warewulf/nodes.conf", tt.initConf)
|
||||
for _, fileName := range tt.initFiles {
|
||||
env.CreateFile(fileName)
|
||||
}
|
||||
|
||||
allowedNets := []net.IPNet{
|
||||
{
|
||||
IP: net.IPv4(127, 0, 0, 0),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
},
|
||||
}
|
||||
srv := httptest.NewServer(Handler(nil, allowedNets))
|
||||
defer srv.Close()
|
||||
|
||||
req, err := tt.request(srv.URL)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedStatus := tt.status
|
||||
if expectedStatus == 0 {
|
||||
expectedStatus = http.StatusOK
|
||||
}
|
||||
assert.Equal(t, expectedStatus, resp.StatusCode)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
ja := jsonassert.New(t)
|
||||
ja.Assertf(string(body), tt.response) //nolint:govet
|
||||
|
||||
if tt.resultConf != "" {
|
||||
assert.YAMLEq(t, tt.resultConf, env.ReadFile("/etc/warewulf/nodes.conf"))
|
||||
}
|
||||
|
||||
for _, fileName := range tt.resultFiles {
|
||||
assert.FileExists(t, env.GetPath(fileName))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kinbiko/jsonassert"
|
||||
@@ -14,175 +15,162 @@ import (
|
||||
"github.com/warewulf/warewulf/internal/pkg/warewulfd"
|
||||
)
|
||||
|
||||
func TestOverlayAPI(t *testing.T) {
|
||||
warewulfd.SetNoDaemon()
|
||||
env := testenv.New(t)
|
||||
defer env.RemoveAll()
|
||||
env.WriteFile("usr/share/warewulf/overlays/testoverlay/email.ww", `
|
||||
{{ if .Tags.email }}eMail: {{ .Tags.email }}{{else}} noMail{{- end }}
|
||||
`)
|
||||
const sampleTemplate = `{{ if .Tags.email }}eMail: {{ .Tags.email }}{{else}} noMail{{- end }}
|
||||
`
|
||||
|
||||
allowedNets := []net.IPNet{
|
||||
{
|
||||
IP: net.IPv4(127, 0, 0, 0),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
var overlayTests = map[string]struct {
|
||||
initFiles map[string]string
|
||||
request func(serverURL string) (*http.Request, error)
|
||||
response string
|
||||
status int
|
||||
resultFiles []string
|
||||
validateFiles map[string]string // file path -> expected content
|
||||
}{
|
||||
"get all overlays": {
|
||||
initFiles: map[string]string{
|
||||
"/usr/share/warewulf/overlays/testoverlay/email.ww": sampleTemplate,
|
||||
},
|
||||
}
|
||||
srv := httptest.NewServer(Handler(nil, allowedNets))
|
||||
defer srv.Close()
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/overlays", nil)
|
||||
},
|
||||
response: `{"testoverlay":{"files":["/email.ww"], "site":false}}`,
|
||||
},
|
||||
|
||||
t.Run("get all overlays", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/overlays", nil)
|
||||
assert.NoError(t, err)
|
||||
"get one specific overlay": {
|
||||
initFiles: map[string]string{
|
||||
"/usr/share/warewulf/overlays/testoverlay/email.ww": sampleTemplate,
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/overlays/testoverlay", nil)
|
||||
},
|
||||
response: `{"files":["/email.ww"], "site":false}`,
|
||||
},
|
||||
|
||||
// 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())
|
||||
|
||||
assert.JSONEq(t, `{"testoverlay":{"files":["/email.ww"], "site":false}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("get one specific overlay", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/overlays/testoverlay", 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())
|
||||
|
||||
assert.JSONEq(t, `{"files":["/email.ww"], "site":false}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("get overlay file", func(t *testing.T) {
|
||||
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())
|
||||
|
||||
// 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.
|
||||
ja := jsonassert.New(t)
|
||||
ja.Assert(string(body), `
|
||||
{
|
||||
"get overlay file": {
|
||||
initFiles: map[string]string{
|
||||
"/usr/share/warewulf/overlays/testoverlay/email.ww": sampleTemplate,
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/overlays/testoverlay/file?path=email.ww", nil)
|
||||
},
|
||||
response: `{
|
||||
"overlay": "testoverlay",
|
||||
"path": "email.ww",
|
||||
"contents": "\n{{ if .Tags.email }}eMail: {{ .Tags.email }}{{else}} noMail{{- end }}\n",
|
||||
"contents": "{{ if .Tags.email }}eMail: {{ .Tags.email }}{{else}} noMail{{- end }}\n",
|
||||
"perms": "<<PRESENCE>>",
|
||||
"uid": "<<PRESENCE>>",
|
||||
"gid": "<<PRESENCE>>"
|
||||
}`)
|
||||
})
|
||||
}`,
|
||||
},
|
||||
|
||||
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)
|
||||
"update overlay file": {
|
||||
initFiles: map[string]string{
|
||||
"/usr/share/warewulf/overlays/testoverlay/email.ww": sampleTemplate,
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodPut, serverURL+"/api/overlays/testoverlay/file?path=email.ww", bytes.NewReader([]byte("{\"content\":\"hello world\"}")))
|
||||
},
|
||||
response: `{"files":["/email.ww"], "site":true}`,
|
||||
validateFiles: map[string]string{
|
||||
"/var/lib/warewulf/overlays/testoverlay/email.ww": "hello world",
|
||||
},
|
||||
},
|
||||
|
||||
// set request
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
"create an overlay": {
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodPut, serverURL+"/api/overlays/test", nil)
|
||||
},
|
||||
response: `{"files":null, "site":true}`,
|
||||
resultFiles: []string{
|
||||
"/var/lib/warewulf/overlays/test",
|
||||
},
|
||||
},
|
||||
|
||||
// validate the resp
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
"get all overlays after creation": {
|
||||
initFiles: map[string]string{
|
||||
"/usr/share/warewulf/overlays/testoverlay/email.ww": sampleTemplate,
|
||||
"/var/lib/warewulf/overlays/test/rootfs/": "",
|
||||
"/var/lib/warewulf/overlays/testoverlay/email.ww": sampleTemplate,
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/overlays", nil)
|
||||
},
|
||||
response: `{"test":{"files":null, "site":true},"testoverlay":{"files":["/email.ww"], "site":true}}`,
|
||||
},
|
||||
|
||||
assert.JSONEq(t, `{"files":["/email.ww"], "site":true}`, string(body))
|
||||
"delete overlay file": {
|
||||
initFiles: map[string]string{
|
||||
"/var/lib/warewulf/overlays/testoverlay/email.ww": sampleTemplate,
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodDelete, serverURL+"/api/overlays/testoverlay/file?path=email.ww&force=true", nil)
|
||||
},
|
||||
response: `{"files":null, "site":true}`,
|
||||
},
|
||||
|
||||
// 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)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"files":null, "site":true}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("get all overlays", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/overlays", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"test":{"files":null, "site":true},"testoverlay":{"files":["/email.ww"], "site":true}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("test delete overlay file", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodDelete, srv.URL+"/api/overlays/testoverlay/file?path=email.ww&force=true", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"files":null, "site":true}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("test delete overlays", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodDelete, srv.URL+"/api/overlays/test?force=true", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
assert.JSONEq(t, `{"files":[], "site":true}`, string(body))
|
||||
})
|
||||
"delete overlay": {
|
||||
initFiles: map[string]string{
|
||||
"/var/lib/warewulf/overlays/test/": "",
|
||||
},
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodDelete, serverURL+"/api/overlays/test?force=true", nil)
|
||||
},
|
||||
response: `{"files":[], "site":true}`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestOverlayAPI(t *testing.T) {
|
||||
for name, tt := range overlayTests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
warewulfd.SetNoDaemon()
|
||||
env := testenv.New(t)
|
||||
defer env.RemoveAll()
|
||||
|
||||
// Create test files
|
||||
for fileName, fileContent := range tt.initFiles {
|
||||
if strings.HasSuffix(fileName, "/") {
|
||||
env.MkdirAll(fileName)
|
||||
} else {
|
||||
env.WriteFile(fileName, fileContent)
|
||||
}
|
||||
}
|
||||
|
||||
allowedNets := []net.IPNet{
|
||||
{
|
||||
IP: net.IPv4(127, 0, 0, 0),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
},
|
||||
}
|
||||
srv := httptest.NewServer(Handler(nil, allowedNets))
|
||||
defer srv.Close()
|
||||
|
||||
req, err := tt.request(srv.URL)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedStatus := tt.status
|
||||
if expectedStatus == 0 {
|
||||
expectedStatus = http.StatusOK
|
||||
}
|
||||
assert.Equal(t, expectedStatus, resp.StatusCode)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
ja := jsonassert.New(t)
|
||||
ja.Assertf(string(body), tt.response) //nolint:govet // tt.response is used as a format string with special tokens
|
||||
|
||||
for _, fileName := range tt.resultFiles {
|
||||
assert.DirExists(t, env.GetPath(fileName))
|
||||
}
|
||||
|
||||
for filePath, expectedContent := range tt.validateFiles {
|
||||
actualContent := env.ReadFile(filePath)
|
||||
assert.Equal(t, expectedContent, actualContent)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,160 +8,225 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestProfileAPI(t *testing.T) {
|
||||
warewulfd.SetNoDaemon()
|
||||
env := testenv.New(t)
|
||||
defer env.RemoveAll()
|
||||
|
||||
allowedNets := []net.IPNet{
|
||||
{
|
||||
IP: net.IPv4(127, 0, 0, 0),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
var profileTests = map[string]struct {
|
||||
initConf string
|
||||
initFiles []string
|
||||
request func(serverURL string) (*http.Request, error)
|
||||
response string
|
||||
status int
|
||||
resultConf string
|
||||
}{
|
||||
"get all profiles": {
|
||||
initConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
nodes: {}
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/profiles", nil)
|
||||
},
|
||||
}
|
||||
srv := httptest.NewServer(Handler(nil, allowedNets))
|
||||
defer srv.Close()
|
||||
response: `{"default": {}}`,
|
||||
},
|
||||
|
||||
t.Run("get all profiles", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/profiles", nil)
|
||||
assert.NoError(t, err)
|
||||
"add a new profile": {
|
||||
initConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
nodes: {}
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
testProfile := `{"profile": {"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`
|
||||
return http.NewRequest(http.MethodPut, serverURL+"/api/profiles/p1", bytes.NewBuffer([]byte(testProfile)))
|
||||
},
|
||||
response: `{"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`,
|
||||
resultConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
p1:
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
nodes: {}
|
||||
`,
|
||||
},
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
"test idempotency (replacing existing profile)": {
|
||||
initConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
p1:
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
nodes: {}
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
testProfile := `{"profile": {"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`
|
||||
return http.NewRequest(http.MethodPut, serverURL+"/api/profiles/p1", bytes.NewBuffer([]byte(testProfile)))
|
||||
},
|
||||
response: `{"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`,
|
||||
resultConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
p1:
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
nodes: {}
|
||||
`,
|
||||
},
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, `{"default": {}}`, string(body))
|
||||
})
|
||||
"test preventing replacing a profile": {
|
||||
initConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
p1:
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
nodes: {}
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
testProfile := `{"profile": {"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`
|
||||
req, err := http.NewRequest(http.MethodPut, serverURL+"/api/profiles/p1", bytes.NewBuffer([]byte(testProfile)))
|
||||
req.Header.Set("If-None-Match", "*")
|
||||
return req, err
|
||||
},
|
||||
response: `{"error": "invalid argument: profile 'p1' already exists", "status": "INVALID_ARGUMENT"}`,
|
||||
status: http.StatusBadRequest,
|
||||
resultConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
p1:
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
nodes: {}
|
||||
`,
|
||||
},
|
||||
|
||||
t.Run("add a new profile", func(t *testing.T) {
|
||||
testProfile := `{"profile": {"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`
|
||||
req, err := http.NewRequest(http.MethodPut, srv.URL+"/api/profiles/test", bytes.NewBuffer([]byte(testProfile)))
|
||||
assert.NoError(t, err)
|
||||
"get one specific profile": {
|
||||
initConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
p1:
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
nodes: {}
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodGet, serverURL+"/api/profiles/p1", nil)
|
||||
},
|
||||
response: `{"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`,
|
||||
},
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
"update a profile": {
|
||||
initConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
p1:
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
nodes: {}
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
updateProfile := `{"profile": {"kernel": {"version": "v1.0.1-newversion"}}}`
|
||||
return http.NewRequest(http.MethodPatch, serverURL+"/api/profiles/p1", bytes.NewBuffer([]byte(updateProfile)))
|
||||
},
|
||||
response: `{"kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`,
|
||||
resultConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
p1:
|
||||
kernel:
|
||||
version: "v1.0.1-newversion"
|
||||
args:
|
||||
- "kernel-args"
|
||||
nodes: {}
|
||||
`,
|
||||
},
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("test idempotency", func(t *testing.T) {
|
||||
testProfile := `{"profile": {"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`
|
||||
req, err := http.NewRequest(http.MethodPut, srv.URL+"/api/profiles/test", bytes.NewBuffer([]byte(testProfile)))
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("fail if profile already exists (given appropriate header)", func(t *testing.T) {
|
||||
testProfile := `{"profile": {"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`
|
||||
req, err := http.NewRequest(http.MethodPut, srv.URL+"/api/profiles/test", bytes.NewBuffer([]byte(testProfile)))
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("If-None-Match", "*")
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
||||
assert.Contains(t, string(body), "profile 'test' already exists")
|
||||
})
|
||||
|
||||
t.Run("re-read all profiles", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/profiles", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, `{"default": {}, "test": {"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("get one specific profile (that was just added)", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/profiles/test", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("update the profile", func(t *testing.T) {
|
||||
updateProfile := `{"profile": {"kernel": {"version": "v1.0.1-newversion"}}}`
|
||||
req, err := http.NewRequest(http.MethodPatch, srv.URL+"/api/profiles/test", bytes.NewBuffer([]byte(updateProfile)))
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("get one specific profile (that was just updated)", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, srv.URL+"/api/profiles/test", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.1-newversion", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("test overwrite", func(t *testing.T) {
|
||||
testProfile := `{"profile": {"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}}`
|
||||
req, err := http.NewRequest(http.MethodPut, srv.URL+"/api/profiles/test", bytes.NewBuffer([]byte(testProfile)))
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
|
||||
t.Run("test delete a profile", func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodDelete, srv.URL+"/api/profiles/test", nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
assert.NoError(t, err)
|
||||
assert.JSONEq(t, `{"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`, string(body))
|
||||
})
|
||||
"test delete a profile": {
|
||||
initConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
p1:
|
||||
kernel:
|
||||
version: "v1.0.0"
|
||||
args:
|
||||
- "kernel-args"
|
||||
nodes: {}
|
||||
`,
|
||||
request: func(serverURL string) (*http.Request, error) {
|
||||
return http.NewRequest(http.MethodDelete, serverURL+"/api/profiles/p1", nil)
|
||||
},
|
||||
response: `{"kernel": {"version": "v1.0.0", "args": ["kernel-args"]}}`,
|
||||
resultConf: `
|
||||
nodeprofiles:
|
||||
default: {}
|
||||
nodes: {}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
func TestProfileAPI(t *testing.T) {
|
||||
for name, tt := range profileTests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
warewulfd.SetNoDaemon()
|
||||
env := testenv.New(t)
|
||||
defer env.RemoveAll()
|
||||
|
||||
env.WriteFile("/etc/warewulf/nodes.conf", tt.initConf)
|
||||
for _, fileName := range tt.initFiles {
|
||||
env.CreateFile(fileName)
|
||||
}
|
||||
|
||||
allowedNets := []net.IPNet{
|
||||
{
|
||||
IP: net.IPv4(127, 0, 0, 0),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
},
|
||||
}
|
||||
srv := httptest.NewServer(Handler(nil, allowedNets))
|
||||
defer srv.Close()
|
||||
|
||||
req, err := tt.request(srv.URL)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resp, err := http.DefaultTransport.RoundTrip(req)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedStatus := tt.status
|
||||
if expectedStatus == 0 {
|
||||
expectedStatus = http.StatusOK
|
||||
}
|
||||
assert.Equal(t, expectedStatus, resp.StatusCode)
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, resp.Body.Close())
|
||||
|
||||
ja := jsonassert.New(t)
|
||||
ja.Assertf(string(body), tt.response) //nolint:govet
|
||||
|
||||
if tt.resultConf != "" {
|
||||
assert.YAMLEq(t, tt.resultConf, env.ReadFile("/etc/warewulf/nodes.conf"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user