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/app.go b/core/installer/app.go
index 612f2e5..948e17e 100644
--- a/core/installer/app.go
+++ b/core/installer/app.go
@@ -476,6 +476,31 @@
 	return AppTypeEnv
 }
 
+func merge(d map[string]any, v map[string]any) map[string]any {
+	ret := map[string]any{}
+	for k, val := range d {
+		if vv, ok := v[k]; ok && vv != nil {
+			if mv, ok := val.(map[string]any); ok {
+				// TODO(gio): check that it is actually map
+				fmt.Println(vv)
+				ret[k] = merge(mv, vv.(map[string]any))
+			} else {
+				ret[k] = vv
+			}
+		} else {
+			ret[k] = val
+		}
+	}
+	for k, v := range v {
+		if v != nil {
+			if _, ok := d[k]; !ok {
+				d[k] = v
+			}
+		}
+	}
+	return ret
+}
+
 func (a cueEnvApp) Render(
 	release Release,
 	env EnvConfig,
@@ -485,7 +510,12 @@
 	charts map[string]helmv2.HelmChartTemplateSpec,
 	vpnKeyGen VPNAPIClient,
 ) (EnvAppRendered, error) {
-	derived, err := deriveValues(values, values, a.Schema(), networks, clusters, vpnKeyGen)
+	dv, err := ExtractDefaultValues(a.cueApp.cfg.LookupPath(cue.ParsePath("input")))
+	if err != nil {
+		return EnvAppRendered{}, err
+	}
+	mv := merge(dv.(map[string]any), values)
+	derived, err := deriveValues(mv, values, a.Schema(), networks, clusters, vpnKeyGen)
 	if err != nil {
 		return EnvAppRendered{}, err
 	}