AppManager: Add API endpoint to install dodo app

Refactors cue definitions.

Next steps:
* Needs some cleanup, namespace is hard coded ...
* Maybe merge with regular install API
* Support exposing ports across clusters

Change-Id: Ibfc3c3f742b61f2c5874012fe6c77b958eae81d9
diff --git a/core/installer/schema.go b/core/installer/schema.go
index 7f8410f..fb5d305 100644
--- a/core/installer/schema.go
+++ b/core/installer/schema.go
@@ -369,6 +369,56 @@
 	}
 }
 
+func ExtractDefaultValues(v cue.Value) (any, error) {
+	switch v.IncompleteKind() {
+	case cue.StringKind:
+		if d, ok := v.Default(); ok {
+			return d.String()
+		}
+	case cue.BoolKind:
+		if d, ok := v.Default(); ok {
+			return d.Bool()
+		}
+	case cue.NumberKind:
+		// TODO(gio): handle numbers
+		return nil, fmt.Errorf("implement: %s", v)
+	case cue.IntKind:
+		if d, ok := v.Default(); ok {
+			return d.Int64()
+		}
+	case cue.ListKind:
+		// TODO(gio): handle lists
+		return nil, nil
+	case cue.StructKind:
+		// TODO(gio): Such fields might have default values as well?
+		if isNetwork(v) {
+			return nil, nil
+		} else if isAuth(v) {
+			return nil, nil
+		} else if isSSHKey(v) {
+			return nil, nil
+		} else if isCluster(v) {
+			return nil, nil
+		}
+		ret := map[string]any{}
+		f, err := v.Fields(cue.All())
+		if err != nil {
+			return nil, err
+		}
+		for f.Next() {
+			fv, err := ExtractDefaultValues(f.Value())
+			if err != nil {
+				return nil, err
+			}
+			ret[f.Selector().String()] = fv
+		}
+		return ret, nil
+	default:
+		return nil, fmt.Errorf("SHOULD NOT REACH! value: %s", v)
+	}
+	return nil, nil
+}
+
 func cleanFieldName(name string) string {
 	return strings.ReplaceAll(strings.ReplaceAll(name, "?", ""), "!", "")
 }