f-34: metadata: add metadata in dispatcher #34
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
parent
883d628bf6
commit
ae81158b2d
5 changed files with 221 additions and 15 deletions
|
|
@ -29,6 +29,7 @@ type StartVMCommand struct {
|
||||||
UEFI bool
|
UEFI bool
|
||||||
Password string
|
Password string
|
||||||
SSHKey string
|
SSHKey string
|
||||||
|
Documents map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c StartVMCommand) Key() string { return "vm/" + c.Name }
|
func (c StartVMCommand) Key() string { return "vm/" + c.Name }
|
||||||
|
|
@ -66,6 +67,11 @@ func (c StartVMCommand) Prepare(db *badger.DB, _ *configuration.Config) error {
|
||||||
if c.SSHKey != "" {
|
if c.SSHKey != "" {
|
||||||
kv.AddInDB(db, "vm/"+c.Name+"/sshkey", c.SSHKey)
|
kv.AddInDB(db, "vm/"+c.Name+"/sshkey", c.SSHKey)
|
||||||
}
|
}
|
||||||
|
for doc, content := range c.Documents {
|
||||||
|
if err := kv.AddInDB(db, "vm/"+c.Name+"/metadata/"+doc, content); err != nil {
|
||||||
|
return fmt.Errorf("store metadata %s: %w", doc, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.g3e.fr/syonad/two/pkg/db/kv"
|
"git.g3e.fr/syonad/two/pkg/db/kv"
|
||||||
|
"github.com/dgraph-io/badger/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// --- StartVMCommand.Prepare : écriture des disques en DB ---
|
// --- StartVMCommand.Prepare : écriture des disques en DB ---
|
||||||
|
|
@ -173,3 +174,98 @@ func TestStartVMCommand_Prepare_Duplicate(t *testing.T) {
|
||||||
t.Error("Prepare devrait échouer si la VM existe déjà")
|
t.Error("Prepare devrait échouer si la VM existe déjà")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- StartVMCommand.Prepare : documents cloud-init ---
|
||||||
|
|
||||||
|
func prepareWithDocuments(t *testing.T, docs map[string]string) *badger.DB {
|
||||||
|
t.Helper()
|
||||||
|
_, db := newTestDispatcher(t)
|
||||||
|
kv.AddInDB(db, "subnet/sn-1/state", "running")
|
||||||
|
kv.AddInDB(db, "subnet/sn-1/vpc", "vpc-1")
|
||||||
|
|
||||||
|
cmd := StartVMCommand{
|
||||||
|
Name: "vm-doc",
|
||||||
|
Subnet: "sn-1",
|
||||||
|
IP: "10.0.0.5",
|
||||||
|
Disks: []VMDisk{{Path: "/data/root.qcow2", Dev: "vda"}},
|
||||||
|
Documents: docs,
|
||||||
|
}
|
||||||
|
if err := cmd.Prepare(db, nil); err != nil {
|
||||||
|
t.Fatalf("Prepare a échoué : %v", err)
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStartVMCommand_Prepare_StoresDocuments(t *testing.T) {
|
||||||
|
userData := "#cloud-config\npackages:\n - nginx\n"
|
||||||
|
db := prepareWithDocuments(t, map[string]string{"user-data": userData})
|
||||||
|
|
||||||
|
got, err := kv.GetFromDB(db, "vm/vm-doc/metadata/user-data")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("clé metadata/user-data absente : %v", err)
|
||||||
|
}
|
||||||
|
if got != userData {
|
||||||
|
t.Errorf("user-data attendu %q, obtenu %q", userData, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStartVMCommand_Prepare_NoDocumentsWritesNoKey(t *testing.T) {
|
||||||
|
db := prepareWithDocuments(t, nil)
|
||||||
|
|
||||||
|
entries, err := kv.ListByPrefix(db, "vm/vm-doc/metadata/")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ListByPrefix : %v", err)
|
||||||
|
}
|
||||||
|
if len(entries) != 0 {
|
||||||
|
t.Errorf("aucun document fourni, aucune clé ne doit être écrite : %v", entries)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStartVMCommand_Prepare_EmptyDocumentIsStored(t *testing.T) {
|
||||||
|
db := prepareWithDocuments(t, map[string]string{"vendor-data": ""})
|
||||||
|
|
||||||
|
entries, err := kv.ListByPrefix(db, "vm/vm-doc/metadata/")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ListByPrefix : %v", err)
|
||||||
|
}
|
||||||
|
if _, ok := entries["vm/vm-doc/metadata/vendor-data"]; !ok {
|
||||||
|
t.Error("un document explicitement vide doit être stocké : c'est une demande de ne rien servir, pas une absence de demande")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStartVMCommand_Prepare_AllDocumentKinds(t *testing.T) {
|
||||||
|
docs := map[string]string{
|
||||||
|
"user-data": "u",
|
||||||
|
"meta-data": "m",
|
||||||
|
"network-config": "n",
|
||||||
|
"vendor-data": "v",
|
||||||
|
}
|
||||||
|
db := prepareWithDocuments(t, docs)
|
||||||
|
|
||||||
|
for doc, want := range docs {
|
||||||
|
got, err := kv.GetFromDB(db, "vm/vm-doc/metadata/"+doc)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("clé metadata/%s absente : %v", doc, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("%s attendu %q, obtenu %q", doc, want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteInDB_RemovesMetadataDocuments(t *testing.T) {
|
||||||
|
db := prepareWithDocuments(t, map[string]string{"user-data": "u", "vendor-data": "v"})
|
||||||
|
|
||||||
|
if err := kv.DeleteInDB(db, "vm/vm-doc"); err != nil {
|
||||||
|
t.Fatalf("DeleteInDB : %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := kv.ListByPrefix(db, "vm/vm-doc/")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ListByPrefix : %v", err)
|
||||||
|
}
|
||||||
|
if len(entries) != 0 {
|
||||||
|
t.Errorf("la suppression de la VM doit emporter ses documents : %v", entries)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ func StartVM(db *badger.DB, name string, cfg *configuration.Config) error {
|
||||||
BindPort: d.metadataPort,
|
BindPort: d.metadataPort,
|
||||||
Password: d.password,
|
Password: d.password,
|
||||||
SSHKEY: d.sshkey,
|
SSHKEY: d.sshkey,
|
||||||
|
Documents: d.documents,
|
||||||
}, cfg, false); err != nil {
|
}, cfg, false); err != nil {
|
||||||
return fmt.Errorf("start metadata: %w", err)
|
return fmt.Errorf("start metadata: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ type vmData struct {
|
||||||
uefi bool
|
uefi bool
|
||||||
password string
|
password string
|
||||||
sshkey string
|
sshkey string
|
||||||
|
documents map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadVM(db *badger.DB, name string) (vmData, error) {
|
func loadVM(db *badger.DB, name string) (vmData, error) {
|
||||||
|
|
@ -124,5 +125,17 @@ func loadVM(db *badger.DB, name string) (vmData, error) {
|
||||||
d.password, _ = kv.GetFromDB(db, "vm/"+name+"/password")
|
d.password, _ = kv.GetFromDB(db, "vm/"+name+"/password")
|
||||||
d.sshkey, _ = kv.GetFromDB(db, "vm/"+name+"/sshkey")
|
d.sshkey, _ = kv.GetFromDB(db, "vm/"+name+"/sshkey")
|
||||||
|
|
||||||
|
docPrefix := "vm/" + name + "/metadata/"
|
||||||
|
docEntries, err := kv.ListByPrefix(db, docPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return d, fmt.Errorf("list metadata documents: %w", err)
|
||||||
|
}
|
||||||
|
if len(docEntries) > 0 {
|
||||||
|
d.documents = make(map[string]string, len(docEntries))
|
||||||
|
for key, content := range docEntries {
|
||||||
|
d.documents[strings.TrimPrefix(key, docPrefix)] = content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
90
internal/vm/data_test.go
Normal file
90
internal/vm/data_test.go
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.g3e.fr/syonad/two/pkg/db/kv"
|
||||||
|
"github.com/dgraph-io/badger/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newVMInDB(t *testing.T) *badger.DB {
|
||||||
|
t.Helper()
|
||||||
|
db := kv.InitDB(kv.Config{Path: t.TempDir()}, false)
|
||||||
|
t.Cleanup(func() { db.Close() })
|
||||||
|
|
||||||
|
kv.AddInDB(db, "vm/vm-1/subnet", "sn-000001")
|
||||||
|
kv.AddInDB(db, "subnet/sn-000001/vpc", "vp-admin")
|
||||||
|
kv.AddInDB(db, "subnet/sn-000001/interface_ip", "10.1.1.1")
|
||||||
|
kv.AddInDB(db, "subnet/sn-000001/dhcp/10.1.1.2", "00:22:33:00:01:02")
|
||||||
|
kv.AddInDB(db, "vm/vm-1/ip", "10.1.1.2")
|
||||||
|
kv.AddInDB(db, "vm/vm-1/metadata_port", "8081")
|
||||||
|
kv.AddInDB(db, "vm/vm-1/disk/vda", "/data/root.qcow2")
|
||||||
|
kv.AddInDB(db, "vm/vm-1/memory", "2048")
|
||||||
|
kv.AddInDB(db, "vm/vm-1/cpus", "2")
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadVM_NoDocuments(t *testing.T) {
|
||||||
|
db := newVMInDB(t)
|
||||||
|
|
||||||
|
d, err := loadVM(db, "vm-1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("loadVM : %v", err)
|
||||||
|
}
|
||||||
|
if d.documents != nil {
|
||||||
|
t.Errorf("aucun document en base, la map doit rester nil : %v", d.documents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadVM_ReadsDocuments(t *testing.T) {
|
||||||
|
db := newVMInDB(t)
|
||||||
|
userData := "#cloud-config\npackages:\n - nginx\n"
|
||||||
|
kv.AddInDB(db, "vm/vm-1/metadata/user-data", userData)
|
||||||
|
kv.AddInDB(db, "vm/vm-1/metadata/network-config", "version: 2\n")
|
||||||
|
|
||||||
|
d, err := loadVM(db, "vm-1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("loadVM : %v", err)
|
||||||
|
}
|
||||||
|
if len(d.documents) != 2 {
|
||||||
|
t.Fatalf("2 documents attendus, obtenu %d : %v", len(d.documents), d.documents)
|
||||||
|
}
|
||||||
|
if d.documents["user-data"] != userData {
|
||||||
|
t.Errorf("user-data altéré au passage en base :\nattendu %q\nobtenu %q", userData, d.documents["user-data"])
|
||||||
|
}
|
||||||
|
if d.documents["network-config"] != "version: 2\n" {
|
||||||
|
t.Errorf("network-config inattendu : %q", d.documents["network-config"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadVM_DocumentKeysAreStrippedOfPrefix(t *testing.T) {
|
||||||
|
db := newVMInDB(t)
|
||||||
|
kv.AddInDB(db, "vm/vm-1/metadata/vendor-data", "v")
|
||||||
|
|
||||||
|
d, err := loadVM(db, "vm-1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("loadVM : %v", err)
|
||||||
|
}
|
||||||
|
for key := range d.documents {
|
||||||
|
if key != "vendor-data" {
|
||||||
|
t.Errorf("clé de document attendue %q, obtenue %q — le préfixe doit être retiré", "vendor-data", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadVM_EmptyDocumentIsPreserved(t *testing.T) {
|
||||||
|
db := newVMInDB(t)
|
||||||
|
kv.AddInDB(db, "vm/vm-1/metadata/user-data", "")
|
||||||
|
|
||||||
|
d, err := loadVM(db, "vm-1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("loadVM : %v", err)
|
||||||
|
}
|
||||||
|
content, ok := d.documents["user-data"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("un document vide doit survivre au passage en base : c'est une demande de ne rien servir")
|
||||||
|
}
|
||||||
|
if content != "" {
|
||||||
|
t.Errorf("contenu attendu vide, obtenu %q", content)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue