VirtualMachine: Implement virtual machines using KubeVirt

Auto adds new VM into given user's Tailscale network

Change-Id: I16847a0b9eacc17b0e794d3b4913eb1d80a93f0a
diff --git a/core/installer/schema.go b/core/installer/schema.go
index 8b49905..b02f3b7 100644
--- a/core/installer/schema.go
+++ b/core/installer/schema.go
@@ -22,6 +22,7 @@
 	KindNumber            = 4
 	KindArrayString       = 8
 	KindPort              = 9
+	KindVPNAuthKey        = 11
 )
 
 type Field struct {
@@ -34,13 +35,14 @@
 	Kind() Kind
 	Fields() []Field
 	Advanced() bool
+	Meta() map[string]string
 }
 
 var AuthSchema Schema = structSchema{
 	name: "Auth",
 	fields: []Field{
-		Field{"enabled", basicSchema{"Enabled", KindBoolean, false}},
-		Field{"groups", basicSchema{"Groups", KindString, false}},
+		Field{"enabled", basicSchema{"Enabled", KindBoolean, false, nil}},
+		Field{"groups", basicSchema{"Groups", KindString, false, nil}},
 	},
 	advanced: false,
 }
@@ -48,8 +50,8 @@
 var SSHKeySchema Schema = structSchema{
 	name: "SSH Key",
 	fields: []Field{
-		Field{"public", basicSchema{"Public Key", KindString, false}},
-		Field{"private", basicSchema{"Private Key", KindString, false}},
+		Field{"public", basicSchema{"Public Key", KindString, false, nil}},
+		Field{"private", basicSchema{"Private Key", KindString, false, nil}},
 	},
 	advanced: true,
 }
@@ -166,6 +168,7 @@
 	name     string
 	kind     Kind
 	advanced bool
+	meta     map[string]string
 }
 
 func (s basicSchema) Name() string {
@@ -184,6 +187,10 @@
 	return s.advanced
 }
 
+func (s basicSchema) Meta() map[string]string {
+	return s.meta
+}
+
 type structSchema struct {
 	name     string
 	fields   []Field
@@ -206,6 +213,10 @@
 	return s.advanced
 }
 
+func (s structSchema) Meta() map[string]string {
+	return map[string]string{}
+}
+
 func NewCueSchema(name string, v cue.Value) (Schema, error) {
 	nameAttr := v.Attribute("name")
 	if nameAttr.Err() == nil {
@@ -218,29 +229,36 @@
 	}
 	switch v.IncompleteKind() {
 	case cue.StringKind:
-		return basicSchema{name, KindString, false}, nil
+		if role == "vpnauthkey" {
+			meta := map[string]string{}
+			usernameAttr := v.Attribute("usernameField")
+			meta["usernameField"] = strings.ToLower(usernameAttr.Contents())
+			return basicSchema{name, KindVPNAuthKey, true, meta}, nil
+		} else {
+			return basicSchema{name, KindString, false, nil}, nil
+		}
 	case cue.BoolKind:
-		return basicSchema{name, KindBoolean, false}, nil
+		return basicSchema{name, KindBoolean, false, nil}, nil
 	case cue.NumberKind:
-		return basicSchema{name, KindNumber, false}, nil
+		return basicSchema{name, KindNumber, false, nil}, nil
 	case cue.IntKind:
 		if role == "port" {
-			return basicSchema{name, KindPort, true}, nil
+			return basicSchema{name, KindPort, true, nil}, nil
 		} else {
-			return basicSchema{name, KindInt, false}, nil
+			return basicSchema{name, KindInt, false, nil}, nil
 		}
 	case cue.ListKind:
 		if isMultiNetwork(v) {
-			return basicSchema{name, KindMultiNetwork, false}, nil
+			return basicSchema{name, KindMultiNetwork, false, nil}, nil
 		}
-		return basicSchema{name, KindArrayString, false}, nil
+		return basicSchema{name, KindArrayString, false, nil}, nil
 	case cue.StructKind:
 		if isNetwork(v) {
-			return basicSchema{name, KindNetwork, false}, nil
+			return basicSchema{name, KindNetwork, false, nil}, nil
 		} else if isAuth(v) {
-			return basicSchema{name, KindAuth, false}, nil
+			return basicSchema{name, KindAuth, false, nil}, nil
 		} else if isSSHKey(v) {
-			return basicSchema{name, KindSSHKey, true}, nil
+			return basicSchema{name, KindSSHKey, true, nil}, nil
 		}
 		s := structSchema{name, make([]Field, 0), false}
 		f, err := v.Fields(cue.Schema())