adds some code
This commit is contained in:
16
Makefile
Normal file
16
Makefile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
APP=node-agent
|
||||||
|
PKG=./...
|
||||||
|
|
||||||
|
.PHONY: build fmt tidy test
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build -o bin/$(APP) ./cmd/node-agent
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
go fmt $(PKG)
|
||||||
|
|
||||||
|
tidy:
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test $(PKG)
|
||||||
@@ -140,6 +140,29 @@ func handleDeleteVM(cfg config.Config, svc Services) http.HandlerFunc {
|
|||||||
func lifecycleVM(cfg config.Config, svc Services, action string) http.HandlerFunc {
|
func lifecycleVM(cfg config.Config, svc Services, action string) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
id := chi.URLParam(r, "id")
|
id := chi.URLParam(r, "id")
|
||||||
|
// Ensure VM exists either in store or runtime
|
||||||
|
spec, specErr := svc.Store.LoadVM(id)
|
||||||
|
runtimeExists := false
|
||||||
|
if specErr != nil {
|
||||||
|
if vms, err := svc.Libvirt.ListVMs(); err == nil {
|
||||||
|
for _, vm := range vms {
|
||||||
|
if vm.ID == id || vm.Name == id {
|
||||||
|
runtimeExists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if specErr != nil && !runtimeExists {
|
||||||
|
writeJSON(w, http.StatusNotFound, map[string]string{"error": "vm not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if specErr == nil {
|
||||||
|
if err := validators.CheckStoragePoolsVM(spec.Disks, cfg); err != nil {
|
||||||
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
taskID := enqueueWork(svc.Tasks, "vm."+action, func(ctx context.Context) (interface{}, error) {
|
taskID := enqueueWork(svc.Tasks, "vm."+action, func(ctx context.Context) (interface{}, error) {
|
||||||
unlock := svc.StoreLock(id)
|
unlock := svc.StoreLock(id)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
@@ -259,6 +282,28 @@ func handleDeleteCT(cfg config.Config, svc Services) http.HandlerFunc {
|
|||||||
func lifecycleCT(cfg config.Config, svc Services, action string) http.HandlerFunc {
|
func lifecycleCT(cfg config.Config, svc Services, action string) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
id := chi.URLParam(r, "id")
|
id := chi.URLParam(r, "id")
|
||||||
|
spec, specErr := svc.Store.LoadCT(id)
|
||||||
|
runtimeExists := false
|
||||||
|
if specErr != nil {
|
||||||
|
if cts, err := svc.LXC.List(); err == nil {
|
||||||
|
for _, ct := range cts {
|
||||||
|
if ct.ID == id || ct.Name == id {
|
||||||
|
runtimeExists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if specErr != nil && !runtimeExists {
|
||||||
|
writeJSON(w, http.StatusNotFound, map[string]string{"error": "ct not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if specErr == nil {
|
||||||
|
if err := validators.CheckStoragePoolsCT(spec, cfg); err != nil {
|
||||||
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
taskID := enqueueWork(svc.Tasks, "ct."+action, func(ctx context.Context) (interface{}, error) {
|
taskID := enqueueWork(svc.Tasks, "ct."+action, func(ctx context.Context) (interface{}, error) {
|
||||||
unlock := svc.StoreLock(id)
|
unlock := svc.StoreLock(id)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
@@ -293,7 +292,7 @@ func buildIsoPureGo(outPath, userDataPath, metaDataPath string) error {
|
|||||||
dirRec := buildDirRecord(0, 0, 0, true) // current dir
|
dirRec := buildDirRecord(0, 0, 0, true) // current dir
|
||||||
dirRec = append(dirRec, buildDirRecord(0, 0, 0, true)...) // parent (same)
|
dirRec = append(dirRec, buildDirRecord(0, 0, 0, true)...) // parent (same)
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
dirRec = append(dirRec, buildDirRecord(byteLen(e.name), e.start, e.size, false, e.name)...)
|
dirRec = append(dirRec, buildDirRecord(byte(len(e.name)), e.start, e.size, false, e.name)...)
|
||||||
}
|
}
|
||||||
dirRec = padTo(dirRec, sectorSize)
|
dirRec = padTo(dirRec, sectorSize)
|
||||||
if err := writeAt(f, rootSector*int64(sectorSize), dirRec); err != nil {
|
if err := writeAt(f, rootSector*int64(sectorSize), dirRec); err != nil {
|
||||||
|
|||||||
10
pkg/state/store_test.go
Normal file
10
pkg/state/store_test.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package state
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestTrimExt(t *testing.T) {
|
||||||
|
got := trimExt("foo.yaml")
|
||||||
|
if got != "foo" {
|
||||||
|
t.Fatalf("expected foo, got %s", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
20
pkg/validators/validators_test.go
Normal file
20
pkg/validators/validators_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package validators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"jagacloud/node-agent/pkg/compute/libvirt"
|
||||||
|
"jagacloud/node-agent/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPoolExists(t *testing.T) {
|
||||||
|
cfg := config.Config{StoragePools: []config.StoragePool{{Name: "local"}}}
|
||||||
|
err := CheckStoragePoolsVM([]libvirt.DiskSpec{{Pool: "local"}}, cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected pool to be valid: %v", err)
|
||||||
|
}
|
||||||
|
err = CheckStoragePoolsVM([]libvirt.DiskSpec{{Pool: "missing"}}, cfg)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error for missing pool")
|
||||||
|
}
|
||||||
|
}
|
||||||
35
scripts/smoke.sh
Executable file
35
scripts/smoke.sh
Executable file
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
API="${API:-http://127.0.0.1:8000/api/v1}"
|
||||||
|
TOKEN="${TOKEN:-changeme}"
|
||||||
|
HDR=(-H "Authorization: Bearer ${TOKEN}")
|
||||||
|
|
||||||
|
vm_id=${VM_ID:-smoke-vm}
|
||||||
|
ct_id=${CT_ID:-smoke-ct}
|
||||||
|
|
||||||
|
create_vm() {
|
||||||
|
curl -sfS "${HDR[@]}" -X POST "${API}/vms" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "{\"id\":\"${vm_id}\",\"name\":\"${vm_id}\",\"cpus\":1,\"memory_mb\":512,\"disks\":[{\"name\":\"root\",\"size_gb\":1,\"pool\":\"local\"}],\"nics\":[{\"bridge\":\"vmbr0\"}]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
create_ct() {
|
||||||
|
curl -sfS "${HDR[@]}" -X POST "${API}/containers" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "{\"id\":\"${ct_id}\",\"name\":\"${ct_id}\",\"template\":\"debian-bookworm\",\"rootfs_pool\":\"local\",\"rootfs_size_g\":1,\"nics\":[{\"bridge\":\"vmbr0\"}],\"limits\":{\"cpus\":1,\"memory_mb\":256},\"unprivileged\":true}"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd=${1:-}
|
||||||
|
case "$cmd" in
|
||||||
|
create-vm) create_vm ;;
|
||||||
|
create-ct) create_ct ;;
|
||||||
|
list)
|
||||||
|
curl -sfS "${HDR[@]}" "${API}/vms" | jq .
|
||||||
|
curl -sfS "${HDR[@]}" "${API}/containers" | jq .
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: API=... TOKEN=... $0 {create-vm|create-ct|list}" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Reference in New Issue
Block a user