DodoApp: Support volume requests

Users can define volumes section under app, and for each volume
corresponding env variable will be passed down to the application.

Change-Id: I8da9d5e1ca652cdb2f0196fcba1b6242064d057f
diff --git a/apps/app-runner/main.go b/apps/app-runner/main.go
index ca99831..10d74ae 100644
--- a/apps/app-runner/main.go
+++ b/apps/app-runner/main.go
@@ -25,6 +25,7 @@
 type Command struct {
 	Bin  string   `json:"bin"`
 	Args []string `json:"args"`
+	Env  []string `json:"env"`
 }
 
 func CloneRepository(addr string, signer ssh.Signer, path string) error {
diff --git a/apps/app-runner/server.go b/apps/app-runner/server.go
index 665f493..865f724 100644
--- a/apps/app-runner/server.go
+++ b/apps/app-runner/server.go
@@ -98,6 +98,7 @@
 			Dir:    *appDir,
 			Path:   c.Bin,
 			Args:   args,
+			Env:    c.Env,
 			Stdout: os.Stdout,
 			Stderr: os.Stderr,
 		}
diff --git a/charts/app-runner/templates/install.yaml b/charts/app-runner/templates/install.yaml
index 5bdf72e..d2b7f70 100644
--- a/charts/app-runner/templates/install.yaml
+++ b/charts/app-runner/templates/install.yaml
@@ -68,6 +68,11 @@
       - name: run-cfg
         configMap:
           name: app-run-cfg
+      {{- range .Values.volumes }}
+      - name: volume-{{ .name }}
+        persistentVolumeClaim:
+          claimName: {{ .name }}
+      {{- end }}
       containers:
       - name: app
         image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
@@ -99,3 +104,7 @@
         - name: run-cfg
           readOnly: true
           mountPath: /pcloud/config
+        {{- range .Values.volumes }}
+        - name: volume-{{ .name }}
+          mountPath: {{ .mountPath }}
+        {{- end }}
diff --git a/charts/app-runner/values.yaml b/charts/app-runner/values.yaml
index 9ec9b55..97e793f 100644
--- a/charts/app-runner/values.yaml
+++ b/charts/app-runner/values.yaml
@@ -8,3 +8,4 @@
 appDir: /dodo-app
 appPort: 8080
 manager: ""
+volumes: []
diff --git a/core/installer/app_configs/dodo_app.cue b/core/installer/app_configs/dodo_app.cue
index b4878b3..c721863 100644
--- a/core/installer/app_configs/dodo_app.cue
+++ b/core/installer/app_configs/dodo_app.cue
@@ -18,16 +18,24 @@
 	baseURL: "https://\(subdomain).\(_network.domain)"
 }
 
-#AppTmpl: {
-	type: string
-	ingress: #AppIngress
-	runConfiguration: [...#Command]
+#Volumes: {
 	...
 }
 
+app: {
+	volumes: {
+		for key, value in volumes {
+			"\(key)": #volume & value & {
+				name: key
+			}
+		}
+	}
+}
+
 #Command: {
 	bin: string
 	args: [...string] | *[]
+	env: [...string] | *[]
 }
 
 // Go app
@@ -39,6 +47,7 @@
 	type: _goVer1220 | _goVer1200
 	run: string
 	ingress: #AppIngress
+	volumes: #Volumes
 
 	runConfiguration: [{
 		bin: "/usr/local/go/bin/go",
@@ -48,7 +57,12 @@
 		args: ["build", "-o", ".app", run]
 	}, {
 		bin: ".app",
-		args: []
+		args: [],
+		env: [
+			for k, v in volumes {
+				"DODO_VOLUME_\(strings.ToUpper(k))=/dodo-volume/\(v.name)"
+			}
+	    ]
 	}]
 }
 
@@ -69,6 +83,7 @@
 #HugoAppTmpl: {
 	type: _hugoLatest
 	ingress: #AppIngress
+	volumes: {}
 
 	runConfiguration: [{
 		bin: "/usr/bin/hugo",
@@ -125,6 +140,8 @@
 	}
 }
 
+volumes: app.volumes
+
 helm: {
 	app: {
 		chart: charts.app
@@ -140,6 +157,12 @@
 			sshPrivateKey: base64.Encode(null, input.sshPrivateKey)
 			runCfg: base64.Encode(null, json.Marshal(_app.runConfiguration))
 			manager: "http://dodo-app.\(release.namespace).svc.cluster.local/register-worker"
+			volumes: [
+				for key, value in _app.volumes {
+					name: value.name
+					mountPath: "/dodo-volume/\(key)"
+				}
+            ]
 		}
 	}
 }
diff --git a/core/installer/app_configs/testapp.cue b/core/installer/app_configs/testapp.cue
index 2573c5c..8201ff6 100644
--- a/core/installer/app_configs/testapp.cue
+++ b/core/installer/app_configs/testapp.cue
@@ -6,6 +6,7 @@
 		subdomain: "testapp"
 		auth: enabled: false
 	}
+	volumes: data: size: "1Gi"
 }
 
 // do create app --type=go[1.22.0] [--run-cmd=(*default main.go)]
diff --git a/core/installer/app_test.go b/core/installer/app_test.go
index ef5a8ef..559de31 100644
--- a/core/installer/app_test.go
+++ b/core/installer/app_test.go
@@ -304,7 +304,20 @@
 var testAppCue []byte
 
 func TestPCloudApp(t *testing.T) {
-	_, err := NewDodoApp(testAppCue)
+	app, err := NewDodoApp(testAppCue)
+	if err != nil {
+		t.Fatal(err)
+	}
+	release := Release{
+		Namespace:     "foo",
+		AppInstanceId: "foo-bar",
+		RepoAddr:      "ssh://192.168.100.210:22/config",
+		AppDir:        "/foo/bar",
+	}
+	_, err = app.Render(release, env, map[string]any{
+		"repoAddr":      "",
+		"sshPrivateKey": "",
+	}, nil)
 	if err != nil {
 		t.Fatal(err)
 	}