VirtualMachine: Implement virtual machines using KubeVirt

Auto adds new VM into given user's Tailscale network

Change-Id: I16847a0b9eacc17b0e794d3b4913eb1d80a93f0a
diff --git a/core/installer/derived.go b/core/installer/derived.go
index 7a5e5f3..aabda98 100644
--- a/core/installer/derived.go
+++ b/core/installer/derived.go
@@ -3,6 +3,7 @@
 import (
 	"fmt"
 	"html/template"
+	"strings"
 )
 
 type Release struct {
@@ -55,7 +56,21 @@
 	return ret
 }
 
-func deriveValues(values any, schema Schema, networks []Network) (map[string]any, error) {
+func getField(v any, f string) any {
+	for _, i := range strings.Split(f, ".") {
+		vm := v.(map[string]any)
+		v = vm[i]
+	}
+	return v
+}
+
+func deriveValues(
+	root any,
+	values any,
+	schema Schema,
+	networks []Network,
+	vpnKeyGen VPNAuthKeyGenerator,
+) (map[string]any, error) {
 	ret := make(map[string]any)
 	for _, f := range schema.Fields() {
 		k := f.Name
@@ -74,6 +89,16 @@
 					"private": string(key.RawPrivateKey()),
 				}
 			}
+			if def.Kind() == KindVPNAuthKey {
+				usernameField := def.Meta()["usernameField"]
+				// TODO(gio): Improve getField
+				username := getField(root, usernameField)
+				authKey, err := vpnKeyGen.Generate(username.(string))
+				if err != nil {
+					return nil, err
+				}
+				ret[k] = authKey
+			}
 			continue
 		}
 		switch def.Kind() {
@@ -85,6 +110,8 @@
 			ret[k] = v
 		case KindPort:
 			ret[k] = v
+		case KindVPNAuthKey:
+			ret[k] = v
 		case KindArrayString:
 			a, ok := v.([]string)
 			if !ok {
@@ -120,19 +147,19 @@
 			}
 			ret[k] = picked
 		case KindAuth:
-			r, err := deriveValues(v, AuthSchema, networks)
+			r, err := deriveValues(v, v, AuthSchema, networks, vpnKeyGen)
 			if err != nil {
 				return nil, err
 			}
 			ret[k] = r
 		case KindSSHKey:
-			r, err := deriveValues(v, SSHKeySchema, networks)
+			r, err := deriveValues(v, v, SSHKeySchema, networks, vpnKeyGen)
 			if err != nil {
 				return nil, err
 			}
 			ret[k] = r
 		case KindStruct:
-			r, err := deriveValues(v, def, networks)
+			r, err := deriveValues(v, v, def, networks, vpnKeyGen)
 			if err != nil {
 				return nil, err
 			}
@@ -163,6 +190,8 @@
 			ret[k] = v
 		case KindPort:
 			ret[k] = v
+		case KindVPNAuthKey:
+			ret[k] = v
 		case KindArrayString:
 			a, ok := v.([]string)
 			if !ok {